1
0
mirror of https://github.com/janet-lang/janet synced 2025-01-07 22:20:26 +00:00

x64 allow dynamically switching between windows and sysv target.

This commit is contained in:
Calvin Rose 2024-06-17 23:01:11 -05:00
parent e5765b26d4
commit b6fb7ae69c
8 changed files with 89 additions and 59 deletions

View File

@ -51,7 +51,7 @@ SONAME_SETTER=-Wl,-soname,
HOSTCC?=$(CC)
HOSTAR?=$(AR)
# Symbols are (optionally) removed later, keep -g as default!
CFLAGS?=-O2 -g
CFLAGS?=-O0 -g
LDFLAGS?=-rdynamic
LIBJANET_LDFLAGS?=$(LD_FLAGS)
RUN:=$(RUN)

View File

@ -1,4 +1,4 @@
janet.exe examples/sysir/samples.janet > temp.nasm
janet.exe examples/sysir/windows_samples.janet > temp.nasm
nasm -fwin64 temp.nasm -l temp.lst -o temp.o
link /entry:Start /subsystem:windows kernel32.lib user32.lib temp.o /out:temp.exe
temp.exe

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash
build/janet examples/sysir/samples.janet > temp.nasm
valgrind build/janet examples/sysir/samples.janet > temp.nasm
nasm -felf64 temp.nasm -l temp.lst -o temp.o
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
valgrind ./temp.bin

View File

@ -38,24 +38,13 @@
(exit (the int 0))
(return)))
(def winmain
'(defn Start:void []
(MessageBoxExA (the pointer 0) "Hello, world!" "Test" 0 (the s16 0))
(ExitProcess (the int 0))
(return)))
####
#(compile1 square)
#(compile1 simple)
#(compile1 myprog)
#(compile1 doloop)
#(compile1 main-fn)
(compile1 winmain)
(compile1 square)
(compile1 simple)
(compile1 myprog)
(compile1 doloop)
(compile1 main-fn)
#(dump)
#(dumpc)
(dumpx64)
# Run with
# janet examples/sysir/scratch.janet > temp.nasm && nasm -felf64 temp.nasm -l temp.lst && ld temp.o && ./a.out

View File

@ -0,0 +1,14 @@
(use ./frontend)
(def winmain
'(defn Start:void []
(MessageBoxExA (the pointer 0) "Hello, world!" "Test" 0 (the s16 0))
(ExitProcess (the int 0))
(return)))
####
(compile1 winmain)
#(dump)
#(dumpc)
(dumpx64)

View File

@ -1879,12 +1879,26 @@ JANET_CORE_FN(cfun_sysir_toir,
}
JANET_CORE_FN(cfun_sysir_tox64,
"(sysir/to-x64 context &opt buffer options)",
"(sysir/to-x64 context &opt buffer target)",
"Lower IR to x64 machine code.") {
janet_arity(argc, 1, 3);
JanetSysIRLinkage *ir = janet_getabstract(argv, 0, &janet_sysir_context_type);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 1, 0);
janet_sys_ir_lower_to_x64(ir, buffer);
JanetSysTarget target;
if (argc < 3 || janet_checktype(argv[2], JANET_NIL) || janet_keyeq(argv[2], "native")) {
#ifdef JANET_WINDOWS
target = JANET_SYS_TARGET_X64_WINDOWS;
#else
target = JANET_SYS_TARGET_X64_LINUX;
#endif
} else if (janet_keyeq(argv[1], "windows")) {
target = JANET_SYS_TARGET_X64_WINDOWS;
} else if (janet_keyeq(argv[1], "linux")) {
target = JANET_SYS_TARGET_X64_LINUX;
} else {
janet_panicf("unknown target %v", argv[1]);
}
janet_sys_ir_lower_to_x64(ir, target, buffer);
return janet_wrap_buffer(buffer);
}

View File

@ -185,6 +185,11 @@ typedef enum {
JANET_SYS_CC_X64_WINDOWS,
} JanetSysCallingConvention;
typedef enum {
JANET_SYS_TARGET_X64_WINDOWS, /* 64 bit, modern windows */
JANET_SYS_TARGET_X64_LINUX, /* x64 linux with recent kernel */
} JanetSysTarget;
typedef struct {
JanetSysOp opcode;
union {
@ -328,6 +333,6 @@ uint32_t *janet_sys_callargs(JanetSysInstruction *instr, uint32_t *count);
void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into);
void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer);
void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer);
void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetSysTarget target, JanetBuffer *buffer);
#endif

View File

@ -180,6 +180,7 @@ void assign_registers(JanetSysx64Context *ctx) {
ctx->regs[i].kind = get_slot_regkind(ctx, i);
if (i < ctx->ir->parameter_count) {
/* Assign to rdi, rsi, etc. according to ABI */
ctx->regs[i].storage = JANET_SYSREG_REGISTER;
if (cc == JANET_SYS_CC_X64_SYSV) {
if (i == 0) ctx->regs[i].index = RDI;
if (i == 1) ctx->regs[i].index = RSI;
@ -188,8 +189,9 @@ void assign_registers(JanetSysx64Context *ctx) {
if (i == 4) ctx->regs[i].index = 8;
if (i == 5) ctx->regs[i].index = 9;
if (i >= 6) {
janet_panic("nyi parameter count > 6");
/* TODO check sizing and alignment */
ctx->regs[i].storage = JANET_SYSREG_STACK_PARAMETER;
ctx->regs[i].index = (i - 6) * 8 + 16;
}
} else if (cc == JANET_SYS_CC_X64_WINDOWS) {
if (i == 0) ctx->regs[i].index = RCX;
@ -197,6 +199,7 @@ void assign_registers(JanetSysx64Context *ctx) {
if (i == 2) ctx->regs[i].index = 8;
if (i == 3) ctx->regs[i].index = 9;
if (i >= 4) {
/* TODO check sizing and alignment */
ctx->regs[i].storage = JANET_SYSREG_STACK_PARAMETER;
ctx->regs[i].index = (i - 4) * 8 + 16;
}
@ -222,28 +225,29 @@ void assign_registers(JanetSysx64Context *ctx) {
}
next_loc = (next_loc + 15) / 16 * 16;
ctx->frame_size = next_loc + 16;
ctx->frame_size = next_loc;
ctx->occupied_registers = occupied;
/* Mark which registers need restoration before returning */
uint32_t non_volatile_mask = 0;
if (cc == JANET_SYS_CC_X64_SYSV) {
non_volatile_mask = (1 << RBX)
| (1 << 12)
| (1 << 13)
| (1 << 14)
| (1 << 15);
| (1 << 12)
| (1 << 13)
| (1 << 14)
| (1 << 15);
}
if (cc == JANET_SYS_CC_X64_WINDOWS) {
ctx->frame_size += 16; /* For shadow stack */
non_volatile_mask = (1 << RBX)
| (RDI << 12)
| (RSI << 12)
| (1 << 12)
| (1 << 13)
| (1 << 14)
| (1 << 15);
| (RDI << 12)
| (RSI << 12)
| (1 << 12)
| (1 << 13)
| (1 << 14)
| (1 << 15);
}
ctx->clobbered_registers = assigned | non_volatile_mask;
ctx->clobbered_registers = assigned & non_volatile_mask;
}
static int operand_isstack(JanetSysx64Context *ctx, uint32_t o) {
@ -330,6 +334,7 @@ static void sysemit_movreg(JanetSysx64Context *ctx, uint32_t regdest, uint32_t s
if (operand_isreg(ctx, src, regdest)) return;
x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, src);
tempreg.storage = JANET_SYSREG_REGISTER;
tempreg.index = regdest;
janet_formatb(ctx->buffer, "mov ");
sysemit_reg(ctx, tempreg, ", ");
@ -340,6 +345,7 @@ static void sysemit_movfromreg(JanetSysx64Context *ctx, uint32_t dest, uint32_t
if (operand_isreg(ctx, dest, srcreg)) return;
x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, dest);
tempreg.storage = JANET_SYSREG_REGISTER;
tempreg.index = srcreg;
janet_formatb(ctx->buffer, "mov ");
sysemit_operand(ctx, dest, ", ");
@ -371,6 +377,7 @@ static void sysemit_threeop_nodeststack(JanetSysx64Context *ctx, const char *op,
sysemit_movreg(ctx, RAX, lhs);
x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, dest);
tempreg.storage = JANET_SYSREG_REGISTER;
tempreg.index = RAX;
janet_formatb(ctx->buffer, "%s ", op);
sysemit_reg(ctx, tempreg, ", ");
@ -456,7 +463,6 @@ static void sysemit_cast(JanetSysx64Context *ctx, JanetSysInstruction instructio
static void sysemit_sysv_call(JanetSysx64Context *ctx, JanetSysInstruction instruction, uint32_t *args, uint32_t argcount) {
/* Push first 6 arguments to particular registers */
JanetSysIR *ir = ctx->ir;
JanetBuffer *buffer = ctx->buffer;
int save_rdi = argcount >= 1 || (ctx->occupied_registers & (1 << RDI));
int save_rsi = argcount >= 2 || (ctx->occupied_registers & (1 << RSI));
@ -507,7 +513,6 @@ static void sysemit_sysv_call(JanetSysx64Context *ctx, JanetSysInstruction instr
static void sysemit_win64_call(JanetSysx64Context *ctx, JanetSysInstruction instruction, uint32_t *args, uint32_t argcount) {
/* Push first 6 arguments to particular registers */
JanetSysIR *ir = ctx->ir;
JanetBuffer *buffer = ctx->buffer;
int save_rcx = argcount >= 1 || (ctx->occupied_registers & (1 << RCX));
int save_rdx = argcount >= 2 || (ctx->occupied_registers & (1 << RDX));
@ -548,7 +553,7 @@ static void sysemit_win64_call(JanetSysx64Context *ctx, JanetSysInstruction inst
if (save_rcx) sysemit_popreg(ctx, RCX);
}
void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetSysTarget target, JanetBuffer *buffer) {
/* Partially setup context */
JanetSysx64Context ctx;
@ -581,19 +586,27 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
}
}
}
janet_formatb(buffer, "section .text\n");
janet_formatb(buffer, "\nsection .text\n");
/* Do register allocation */
/* For Top level IR group, emit a function body or other data */
for (int32_t i = 0; i < linkage->ir_ordered->count; i++) {
JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]);
ctx.ir_index = i;
if (ir->link_name == NULL) {
/* Unnamed IR sections contain just type definitions and can be discarded during lowering. */
continue;
}
ctx.calling_convention = ir->calling_convention;
if (ctx.calling_convention == JANET_SYS_CC_DEFAULT) {
/* Pick default calling convention */
ctx.calling_convention = JANET_SYS_CC_X64_WINDOWS;
switch (target) {
default:
ctx.calling_convention = JANET_SYS_CC_X64_SYSV;
break;
case JANET_SYS_TARGET_X64_WINDOWS:
ctx.calling_convention = JANET_SYS_CC_X64_WINDOWS;
break;
}
}
/* Setup context */
@ -708,25 +721,20 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
janet_formatb(buffer, "jmp label_%d_%u\n", i, instruction.jump.to);
break;
case JANET_SYSOP_SYSCALL:
case JANET_SYSOP_CALL:
{
uint32_t argcount = 0;
uint32_t *args = janet_sys_callargs(ir->instructions + j, &argcount);
JanetSysCallingConvention cc = instruction.call.calling_convention;
/* Set default calling convention based on context */
if (cc == JANET_SYS_CC_DEFAULT) {
cc = JANET_SYS_CC_X64_WINDOWS;
}
if (cc == JANET_SYS_CC_X64_SYSV) {
sysemit_sysv_call(&ctx, instruction, args, argcount);
} else if (cc == JANET_SYS_CC_X64_WINDOWS) {
sysemit_win64_call(&ctx, instruction, args, argcount);
}
janet_sfree(args);
break;
case JANET_SYSOP_CALL: {
uint32_t argcount = 0;
uint32_t *args = janet_sys_callargs(ir->instructions + j, &argcount);
JanetSysCallingConvention cc = instruction.call.calling_convention;
// TODO better way of chosing default calling convention
if (cc == JANET_SYS_CC_DEFAULT) cc = ctx.calling_convention;
if (cc == JANET_SYS_CC_X64_SYSV) {
sysemit_sysv_call(&ctx, instruction, args, argcount);
} else if (cc == JANET_SYS_CC_X64_WINDOWS) {
sysemit_win64_call(&ctx, instruction, args, argcount);
}
janet_sfree(args);
break;
}
// On a comparison, if next instruction is branch that reads from dest, combine into a single op.
}
}