1
0
mirror of https://github.com/janet-lang/janet synced 2025-01-09 07:00:27 +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) HOSTCC?=$(CC)
HOSTAR?=$(AR) HOSTAR?=$(AR)
# Symbols are (optionally) removed later, keep -g as default! # Symbols are (optionally) removed later, keep -g as default!
CFLAGS?=-O2 -g CFLAGS?=-O0 -g
LDFLAGS?=-rdynamic LDFLAGS?=-rdynamic
LIBJANET_LDFLAGS?=$(LD_FLAGS) LIBJANET_LDFLAGS?=$(LD_FLAGS)
RUN:=$(RUN) 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 nasm -fwin64 temp.nasm -l temp.lst -o temp.o
link /entry:Start /subsystem:windows kernel32.lib user32.lib temp.o /out:temp.exe link /entry:Start /subsystem:windows kernel32.lib user32.lib temp.o /out:temp.exe
temp.exe temp.exe

View File

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/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 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 ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
valgrind ./temp.bin valgrind ./temp.bin

View File

@ -38,24 +38,13 @@
(exit (the int 0)) (exit (the int 0))
(return))) (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 square)
#(compile1 simple) (compile1 simple)
#(compile1 myprog) (compile1 myprog)
#(compile1 doloop) (compile1 doloop)
#(compile1 main-fn) (compile1 main-fn)
(compile1 winmain)
#(dump) #(dump)
#(dumpc) #(dumpc)
(dumpx64) (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, 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.") { "Lower IR to x64 machine code.") {
janet_arity(argc, 1, 3); janet_arity(argc, 1, 3);
JanetSysIRLinkage *ir = janet_getabstract(argv, 0, &janet_sysir_context_type); JanetSysIRLinkage *ir = janet_getabstract(argv, 0, &janet_sysir_context_type);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 1, 0); 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); return janet_wrap_buffer(buffer);
} }

View File

@ -185,6 +185,11 @@ typedef enum {
JANET_SYS_CC_X64_WINDOWS, JANET_SYS_CC_X64_WINDOWS,
} JanetSysCallingConvention; } 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 { typedef struct {
JanetSysOp opcode; JanetSysOp opcode;
union { 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_ir(JanetSysIRLinkage *linkage, JanetArray *into);
void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer); 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 #endif

View File

@ -180,6 +180,7 @@ void assign_registers(JanetSysx64Context *ctx) {
ctx->regs[i].kind = get_slot_regkind(ctx, i); ctx->regs[i].kind = get_slot_regkind(ctx, i);
if (i < ctx->ir->parameter_count) { if (i < ctx->ir->parameter_count) {
/* Assign to rdi, rsi, etc. according to ABI */ /* Assign to rdi, rsi, etc. according to ABI */
ctx->regs[i].storage = JANET_SYSREG_REGISTER;
if (cc == JANET_SYS_CC_X64_SYSV) { if (cc == JANET_SYS_CC_X64_SYSV) {
if (i == 0) ctx->regs[i].index = RDI; if (i == 0) ctx->regs[i].index = RDI;
if (i == 1) ctx->regs[i].index = RSI; 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 == 4) ctx->regs[i].index = 8;
if (i == 5) ctx->regs[i].index = 9; if (i == 5) ctx->regs[i].index = 9;
if (i >= 6) { 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].storage = JANET_SYSREG_STACK_PARAMETER;
ctx->regs[i].index = (i - 6) * 8 + 16;
} }
} else if (cc == JANET_SYS_CC_X64_WINDOWS) { } else if (cc == JANET_SYS_CC_X64_WINDOWS) {
if (i == 0) ctx->regs[i].index = RCX; 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 == 2) ctx->regs[i].index = 8;
if (i == 3) ctx->regs[i].index = 9; if (i == 3) ctx->regs[i].index = 9;
if (i >= 4) { if (i >= 4) {
/* TODO check sizing and alignment */
ctx->regs[i].storage = JANET_SYSREG_STACK_PARAMETER; ctx->regs[i].storage = JANET_SYSREG_STACK_PARAMETER;
ctx->regs[i].index = (i - 4) * 8 + 16; ctx->regs[i].index = (i - 4) * 8 + 16;
} }
@ -222,7 +225,7 @@ void assign_registers(JanetSysx64Context *ctx) {
} }
next_loc = (next_loc + 15) / 16 * 16; next_loc = (next_loc + 15) / 16 * 16;
ctx->frame_size = next_loc + 16; ctx->frame_size = next_loc;
ctx->occupied_registers = occupied; ctx->occupied_registers = occupied;
/* Mark which registers need restoration before returning */ /* Mark which registers need restoration before returning */
@ -235,6 +238,7 @@ void assign_registers(JanetSysx64Context *ctx) {
| (1 << 15); | (1 << 15);
} }
if (cc == JANET_SYS_CC_X64_WINDOWS) { if (cc == JANET_SYS_CC_X64_WINDOWS) {
ctx->frame_size += 16; /* For shadow stack */
non_volatile_mask = (1 << RBX) non_volatile_mask = (1 << RBX)
| (RDI << 12) | (RDI << 12)
| (RSI << 12) | (RSI << 12)
@ -243,7 +247,7 @@ void assign_registers(JanetSysx64Context *ctx) {
| (1 << 14) | (1 << 14)
| (1 << 15); | (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) { 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; if (operand_isreg(ctx, src, regdest)) return;
x64Reg tempreg; x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, src); tempreg.kind = get_slot_regkind(ctx, src);
tempreg.storage = JANET_SYSREG_REGISTER;
tempreg.index = regdest; tempreg.index = regdest;
janet_formatb(ctx->buffer, "mov "); janet_formatb(ctx->buffer, "mov ");
sysemit_reg(ctx, tempreg, ", "); 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; if (operand_isreg(ctx, dest, srcreg)) return;
x64Reg tempreg; x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, dest); tempreg.kind = get_slot_regkind(ctx, dest);
tempreg.storage = JANET_SYSREG_REGISTER;
tempreg.index = srcreg; tempreg.index = srcreg;
janet_formatb(ctx->buffer, "mov "); janet_formatb(ctx->buffer, "mov ");
sysemit_operand(ctx, dest, ", "); sysemit_operand(ctx, dest, ", ");
@ -371,6 +377,7 @@ static void sysemit_threeop_nodeststack(JanetSysx64Context *ctx, const char *op,
sysemit_movreg(ctx, RAX, lhs); sysemit_movreg(ctx, RAX, lhs);
x64Reg tempreg; x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, dest); tempreg.kind = get_slot_regkind(ctx, dest);
tempreg.storage = JANET_SYSREG_REGISTER;
tempreg.index = RAX; tempreg.index = RAX;
janet_formatb(ctx->buffer, "%s ", op); janet_formatb(ctx->buffer, "%s ", op);
sysemit_reg(ctx, tempreg, ", "); 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) { static void sysemit_sysv_call(JanetSysx64Context *ctx, JanetSysInstruction instruction, uint32_t *args, uint32_t argcount) {
/* Push first 6 arguments to particular registers */ /* Push first 6 arguments to particular registers */
JanetSysIR *ir = ctx->ir;
JanetBuffer *buffer = ctx->buffer; JanetBuffer *buffer = ctx->buffer;
int save_rdi = argcount >= 1 || (ctx->occupied_registers & (1 << RDI)); int save_rdi = argcount >= 1 || (ctx->occupied_registers & (1 << RDI));
int save_rsi = argcount >= 2 || (ctx->occupied_registers & (1 << RSI)); 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) { static void sysemit_win64_call(JanetSysx64Context *ctx, JanetSysInstruction instruction, uint32_t *args, uint32_t argcount) {
/* Push first 6 arguments to particular registers */ /* Push first 6 arguments to particular registers */
JanetSysIR *ir = ctx->ir;
JanetBuffer *buffer = ctx->buffer; JanetBuffer *buffer = ctx->buffer;
int save_rcx = argcount >= 1 || (ctx->occupied_registers & (1 << RCX)); int save_rcx = argcount >= 1 || (ctx->occupied_registers & (1 << RCX));
int save_rdx = argcount >= 2 || (ctx->occupied_registers & (1 << RDX)); 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); 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 */ /* Partially setup context */
JanetSysx64Context ctx; 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++) { for (int32_t i = 0; i < linkage->ir_ordered->count; i++) {
JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]); JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]);
ctx.ir_index = i; ctx.ir_index = i;
if (ir->link_name == NULL) { if (ir->link_name == NULL) {
/* Unnamed IR sections contain just type definitions and can be discarded during lowering. */
continue; continue;
} }
ctx.calling_convention = ir->calling_convention; ctx.calling_convention = ir->calling_convention;
if (ctx.calling_convention == JANET_SYS_CC_DEFAULT) { if (ctx.calling_convention == JANET_SYS_CC_DEFAULT) {
/* Pick default calling convention */ /* Pick default calling convention */
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; ctx.calling_convention = JANET_SYS_CC_X64_WINDOWS;
break;
}
} }
/* Setup context */ /* Setup context */
@ -708,17 +721,12 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
janet_formatb(buffer, "jmp label_%d_%u\n", i, instruction.jump.to); janet_formatb(buffer, "jmp label_%d_%u\n", i, instruction.jump.to);
break; break;
case JANET_SYSOP_SYSCALL: case JANET_SYSOP_SYSCALL:
case JANET_SYSOP_CALL: case JANET_SYSOP_CALL: {
{
uint32_t argcount = 0; uint32_t argcount = 0;
uint32_t *args = janet_sys_callargs(ir->instructions + j, &argcount); uint32_t *args = janet_sys_callargs(ir->instructions + j, &argcount);
JanetSysCallingConvention cc = instruction.call.calling_convention; JanetSysCallingConvention cc = instruction.call.calling_convention;
// TODO better way of chosing default calling convention
/* Set default calling convention based on context */ if (cc == JANET_SYS_CC_DEFAULT) cc = ctx.calling_convention;
if (cc == JANET_SYS_CC_DEFAULT) {
cc = JANET_SYS_CC_X64_WINDOWS;
}
if (cc == JANET_SYS_CC_X64_SYSV) { if (cc == JANET_SYS_CC_X64_SYSV) {
sysemit_sysv_call(&ctx, instruction, args, argcount); sysemit_sysv_call(&ctx, instruction, args, argcount);
} else if (cc == JANET_SYS_CC_X64_WINDOWS) { } else if (cc == JANET_SYS_CC_X64_WINDOWS) {