From b6fb7ae69c8b588c3dd946cef3061affa8642b1d Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Mon, 17 Jun 2024 23:01:11 -0500 Subject: [PATCH] x64 allow dynamically switching between windows and sysv target. --- Makefile | 2 +- examples/sysir/run_samples.bat | 2 +- examples/sysir/run_samples.sh | 2 +- examples/sysir/samples.janet | 21 ++----- examples/sysir/windows_samples.janet | 14 +++++ src/core/sysir.c | 18 +++++- src/core/sysir.h | 7 ++- src/core/sysir_x86.c | 82 +++++++++++++++------------- 8 files changed, 89 insertions(+), 59 deletions(-) create mode 100644 examples/sysir/windows_samples.janet diff --git a/Makefile b/Makefile index 43c2a59b..3b1f4a78 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/examples/sysir/run_samples.bat b/examples/sysir/run_samples.bat index b65536e5..3dc76def 100644 --- a/examples/sysir/run_samples.bat +++ b/examples/sysir/run_samples.bat @@ -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 diff --git a/examples/sysir/run_samples.sh b/examples/sysir/run_samples.sh index 46b174d8..2cfffef8 100755 --- a/examples/sysir/run_samples.sh +++ b/examples/sysir/run_samples.sh @@ -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 diff --git a/examples/sysir/samples.janet b/examples/sysir/samples.janet index ec5e09e5..d3b7ce56 100644 --- a/examples/sysir/samples.janet +++ b/examples/sysir/samples.janet @@ -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 diff --git a/examples/sysir/windows_samples.janet b/examples/sysir/windows_samples.janet new file mode 100644 index 00000000..47aa86ab --- /dev/null +++ b/examples/sysir/windows_samples.janet @@ -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) diff --git a/src/core/sysir.c b/src/core/sysir.c index 9880db12..e92467ad 100644 --- a/src/core/sysir.c +++ b/src/core/sysir.c @@ -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); } diff --git a/src/core/sysir.h b/src/core/sysir.h index 9bceda37..16ab5205 100644 --- a/src/core/sysir.h +++ b/src/core/sysir.h @@ -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 diff --git a/src/core/sysir_x86.c b/src/core/sysir_x86.c index 59d94dde..60644c88 100644 --- a/src/core/sysir_x86.c +++ b/src/core/sysir_x86.c @@ -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. } }