mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-30 23:23:07 +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) | ||||
| 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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										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, | ||||
|               "(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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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. | ||||
|             } | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose