mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	x64 allow dynamically switching between windows and sysv target.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -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) | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								examples/sysir/windows_samples.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								examples/sysir/windows_samples.janet
									
									
									
									
									
										Normal 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) | ||||||
| @@ -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); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose