mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	Lots of work on calling conventions and x86 backend.
We need the ability to represent multiple calling conventions in IR. All backends need to support a :default CC, but can also support more for interop with system libraries, code from other compilers, syscalls, etc. Also allow void returns.
This commit is contained in:
		| @@ -25,7 +25,7 @@ | ||||
|     (constant 3 1.77) | ||||
|     (call 3 sin 3) | ||||
|     (cast bob 2) | ||||
|     (call bob test_function) | ||||
|     (call :default bob test_function) | ||||
|     (add 5 bob 3) | ||||
|     (jump :location) | ||||
|     (return 5))) | ||||
|   | ||||
| @@ -79,33 +79,13 @@ | ||||
|   (cond | ||||
|  | ||||
|     # Compile a constant | ||||
|     (number? code) | ||||
|     (let [slot (get-slot) | ||||
|           slottype 'int] | ||||
|       (array/push into ~(bind ,slot ,slottype)) | ||||
|       (array/push into ~(constant ,slot ,code)) | ||||
|       slot) | ||||
|  | ||||
|     # Booleans | ||||
|     (boolean? code) | ||||
|     (let [slot (get-slot) | ||||
|           slottype 'boolean] | ||||
|       (array/push into ~(bind ,slot ,slottype)) | ||||
|       (array/push into ~(constant ,slot ,(if code -1 0))) | ||||
|       slot) | ||||
|     (or (string? code) (number? code) (boolean? code)) | ||||
|     ~(,code) | ||||
|  | ||||
|     # Binding | ||||
|     (symbol? code) | ||||
|     (named-slot code) | ||||
|  | ||||
|     # String literals | ||||
|     (string? code) | ||||
|     (let [slot (get-slot) | ||||
|           slottype 'pointer] | ||||
|       (array/push into ~(bind ,slot ,slottype)) | ||||
|       (array/push into ~(constant ,slot ,code)) | ||||
|       slot) | ||||
|  | ||||
|     # Compile forms | ||||
|     (and (tuple? code) (= :parens (tuple/type code))) | ||||
|     (do | ||||
| @@ -135,8 +115,14 @@ | ||||
|           (assert (= 2 (length args))) | ||||
|           (def [xtype x] args) | ||||
|           (def result (visit1 x into)) | ||||
|           (array/push into ~(bind ,result ,xtype)) | ||||
|           result) | ||||
|           (if (tuple? result) # constant | ||||
|             (let [r (get-slot)]  | ||||
|               (array/push into ~(bind ,r ,xtype)) | ||||
|               (array/push into ~(move ,r ,result)) | ||||
|               r) | ||||
|             (do | ||||
|               (array/push into ~(bind ,result ,xtype)) | ||||
|               result))) | ||||
|  | ||||
|         # Named bindings | ||||
|         # TODO - type inference | ||||
| @@ -235,7 +221,7 @@ | ||||
|           (def ret (if no-return nil (get-slot))) | ||||
|           (each arg args | ||||
|             (array/push slots (visit1 arg into))) | ||||
|           (array/push into ~(call ,ret ,op ,;slots)) | ||||
|           (array/push into ~(call :default ,ret [,op] ,;slots)) | ||||
|           ret))) | ||||
|  | ||||
|     (errorf "cannot compile %q" code))) | ||||
| @@ -311,6 +297,7 @@ | ||||
|         (array/push ir-asm ~(bind ,slot ,tp))) | ||||
|       (each part body | ||||
|         (visit1 part ir-asm true)) | ||||
|       #(eprintf "%.99M" ir-asm) | ||||
|       (sysir/asm ctx ir-asm)) | ||||
|  | ||||
|     (errorf "unknown form %v" form))) | ||||
| @@ -319,6 +306,11 @@ | ||||
| ### | ||||
| ### | ||||
|  | ||||
| (def simple | ||||
|   '(defn simple [x:int] | ||||
|      (def xyz:int (+ 1 2 3)) | ||||
|      (return (* x 2 x)))) | ||||
|  | ||||
| (def myprog | ||||
|   '(defn myprog [] | ||||
|      (def xyz:int (+ 1 2 3)) | ||||
| @@ -327,8 +319,8 @@ | ||||
|      (var i:int 0) | ||||
|      (while (< i 10) | ||||
|        (set i (+ 1 i)) | ||||
|        (printf "i = %d\n" (the int i))) | ||||
|      (printf "hello, world!\n%d\n" (the int (if x abc xyz))) | ||||
|        (printf (the pointer "i = %d\n") (the int i))) | ||||
|      (printf (the pointer "hello, world!\n%d\n") (the int (if x abc xyz))) | ||||
|      (return (/ abc xyz)))) | ||||
|  | ||||
| (def doloop | ||||
| @@ -340,10 +332,10 @@ | ||||
|      (return x))) | ||||
|  | ||||
| (def main-fn | ||||
|   '(defn main:int [] | ||||
|   '(defn main:void [] | ||||
|      (doloop 10 20) | ||||
|      (printf "done!\n") | ||||
|      (return (the int 0)))) | ||||
|      (return))) | ||||
|  | ||||
| (def ctx (sysir/context)) | ||||
| (setup-default-types ctx) | ||||
| @@ -367,5 +359,9 @@ | ||||
| #### | ||||
|  | ||||
| (compile1 myprog) | ||||
| (compile1 doloop) | ||||
| (compile1 main-fn) | ||||
| (print "compiled!") | ||||
| (dump) | ||||
| (dumpc) | ||||
| (dumpx64) | ||||
|   | ||||
							
								
								
									
										531
									
								
								src/core/sysir.c
									
									
									
									
									
								
							
							
						
						
									
										531
									
								
								src/core/sysir.c
									
									
									
									
									
								
							| @@ -27,7 +27,8 @@ | ||||
|  */ | ||||
|  | ||||
| /* TODO | ||||
|  * [ ] encode constants directly in 3 address codes - makes codegen easier | ||||
|  * [x] encode constants directly in 3 address codes - makes codegen easier | ||||
|  * [ ] typed constants | ||||
|  * [x] named registers and types | ||||
|  * [x] better type errors (perhaps mostly for compiler debugging - full type system goes on top) | ||||
|  * [ ] x86/x64 machine code target | ||||
| @@ -41,9 +42,7 @@ | ||||
|  * [x] union types? | ||||
|  * [x] incremental compilation - save type definitions for later | ||||
|  * [ ] Extension to C target for interfacing with Janet | ||||
|  * [ ] malloc/alloca exposure (only some targets) | ||||
|  * [x] pointer math, pointer types | ||||
|  * [x] callk - allow linking to other named functions | ||||
|  * [x] composite types - support for load, store, move, and function args. | ||||
|  * [x] Have some mechanism for field access (dest = src.offset) | ||||
|  * [x] Related, move type creation as opcodes like in SPIRV - have separate virtual "type slots" and value slots for this. | ||||
| @@ -131,14 +130,12 @@ const char *janet_sysop_names[] = { | ||||
|     "neq", /* JANET_SYSOP_NEQ */ | ||||
|     "gte", /* JANET_SYSOP_GTE */ | ||||
|     "lte", /* JANET_SYSOP_LTE */ | ||||
|     "constant", /* JANET_SYSOP_CONSTANT */ | ||||
|     "call", /* JANET_SYSOP_CALL */ | ||||
|     "return", /* JANET_SYSOP_RETURN */ | ||||
|     "jump", /* JANET_SYSOP_JUMP */ | ||||
|     "branch", /* JANET_SYSOP_BRANCH */ | ||||
|     "branch_not", /* JANET_SYSOP_BRANCH_NOT */ | ||||
|     "address", /* JANET_SYSOP_ADDRESS */ | ||||
|     "callk", /* JANET_SYSOP_CALLK */ | ||||
|     "type-primitive", /* JANET_SYSOP_TYPE_PRIMITIVE */ | ||||
|     "type-struct", /* JANET_SYSOP_TYPE_STRUCT */ | ||||
|     "type-bind", /* JANET_SYSOP_TYPE_BIND */ | ||||
| @@ -154,6 +151,33 @@ const char *janet_sysop_names[] = { | ||||
|     "label", /* JANET_SYSOP_LABEL */ | ||||
| }; | ||||
|  | ||||
| static const char *calling_convention_names[] = { | ||||
|     "default", /* JANET_SYS_CC_DEFAULT */ | ||||
|     "syscall", /* JANET_SYS_CC_SYSCALL */ | ||||
|     "cdecl", /* JANET_SYS_CC_X86_CDECL */ | ||||
|     "stdcall", /* JANET_SYS_CC_X86_STDCALL */ | ||||
|     "fastcall", /* JANET_SYS_CC_X86_FASTCALL */ | ||||
|     "sysv", /* JANET_SYS_CC_X64_SYSV */ | ||||
|     "sysv-syscall", /* JANET_SYS_CC_X64_SYSV_SYSCALL */ | ||||
|     "windows-x64", /* JANET_SYS_CC_X64_WINDOWS */ | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     const char *name; | ||||
|     JanetSysCallingConvention cc; | ||||
| } JanetSysCCName; | ||||
|  | ||||
| static JanetSysCCName sys_calling_convention_names[] = { | ||||
|     {"cdecl", JANET_SYS_CC_X86_CDECL}, | ||||
|     {"default", JANET_SYS_CC_DEFAULT}, | ||||
|     {"fastcall", JANET_SYS_CC_X86_FASTCALL}, | ||||
|     {"stdcall", JANET_SYS_CC_X86_STDCALL}, | ||||
|     {"syscall", JANET_SYS_CC_SYSCALL}, | ||||
|     {"sysv", JANET_SYS_CC_X64_SYSV}, | ||||
|     {"sysv-syscall", JANET_SYS_CC_X64_SYSV_SYSCALL}, | ||||
|     {"windows-x64", JANET_SYS_CC_X64_WINDOWS}, | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     const char *name; | ||||
|     JanetSysOp op; | ||||
| @@ -173,7 +197,6 @@ static const JanetSysInstrName sys_op_names[] = { | ||||
|     {"bxor", JANET_SYSOP_BXOR}, | ||||
|     {"call", JANET_SYSOP_CALL}, | ||||
|     {"cast", JANET_SYSOP_CAST}, | ||||
|     {"constant", JANET_SYSOP_CONSTANT}, | ||||
|     {"divide", JANET_SYSOP_DIVIDE}, | ||||
|     {"eq", JANET_SYSOP_EQ}, | ||||
|     {"fgetp", JANET_SYSOP_FIELD_GETP}, | ||||
| @@ -239,6 +262,12 @@ static void instr_assert_min_length(JanetTuple tup, int32_t minlen, Janet x) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void instr_assert_max_length(JanetTuple tup, int32_t maxlen, Janet x) { | ||||
|     if (janet_tuple_length(tup) > maxlen) { | ||||
|         janet_panicf("expected instruction of at most length %d, got %v", maxlen, x); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static uint32_t instr_read_operand(Janet x, JanetSysIR *ir) { | ||||
|     if (janet_checktype(x, JANET_SYMBOL)) { | ||||
|         Janet check = janet_table_get(ir->register_name_lookup, x); | ||||
| @@ -258,6 +287,26 @@ static uint32_t instr_read_operand(Janet x, JanetSysIR *ir) { | ||||
|     return operand; | ||||
| } | ||||
|  | ||||
| static uint32_t instr_read_operand_or_const(Janet x, JanetSysIR *ir) { | ||||
|     JanetTable *constant_cache = ir->constants_lookup; | ||||
|     if (janet_checktype(x, JANET_TUPLE)) { | ||||
|         const Janet *tup = janet_unwrap_tuple(x); | ||||
|         if (janet_tuple_length(tup) != 1) janet_panicf("expected constant wrapped in tuple, got %v", x); | ||||
|         Janet c = tup[0]; | ||||
|         int32_t index; | ||||
|         Janet check = janet_table_get(constant_cache, c); | ||||
|         if (janet_checktype(check, JANET_NUMBER)) { | ||||
|             index = (int32_t) janet_unwrap_number(check); | ||||
|         } else { | ||||
|             index = constant_cache->count; | ||||
|             janet_table_put(constant_cache, c, janet_wrap_number(index)); | ||||
|             janet_v_push(ir->constants, c); | ||||
|         } | ||||
|         return JANET_SYS_CONSTANT_PREFIX + (uint32_t) index; | ||||
|     } | ||||
|     return instr_read_operand(x, ir); | ||||
| } | ||||
|  | ||||
| static uint32_t instr_read_field(Janet x, JanetSysIR *ir) { | ||||
|     if (!janet_checkuint(x)) janet_panicf("expected non-negative field index, got %v", x); | ||||
|     (void) ir; /* Perhaps support syntax for named fields instead of numbered */ | ||||
| @@ -309,6 +358,20 @@ static JanetPrim instr_read_prim(Janet x) { | ||||
|     return namedata->prim; | ||||
| } | ||||
|  | ||||
| static JanetSysCallingConvention instr_read_cc(Janet x) { | ||||
|     if (!janet_checktype(x, JANET_KEYWORD)) { | ||||
|         janet_panicf("expected calling convention keyword, got %v", x); | ||||
|     } | ||||
|     JanetKeyword cc_name = janet_unwrap_keyword(x); | ||||
|     const JanetSysCCName *namedata = janet_strbinsearch(sys_calling_convention_names, | ||||
|                                      sizeof(sys_calling_convention_names) / sizeof(sys_calling_convention_names[0]), | ||||
|                                      sizeof(sys_calling_convention_names[0]), cc_name); | ||||
|     if (NULL == namedata) { | ||||
|         janet_panicf("unknown calling convention %v", x); | ||||
|     } | ||||
|     return namedata->cc; | ||||
| } | ||||
|  | ||||
| static uint32_t instr_read_label(JanetSysIR *sysir, Janet x) { | ||||
|     uint32_t ret = 0; | ||||
|     Janet check = janet_table_get(sysir->labels, x); | ||||
| @@ -325,8 +388,6 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|  | ||||
|     JanetSysInstruction *ir = NULL; | ||||
|     JanetTable *labels = out->labels; | ||||
|     JanetTable *constant_cache = janet_table(0); | ||||
|     uint32_t next_constant = 0; | ||||
|     int found_parameter_count = 0; | ||||
|  | ||||
|     /* Parse instructions */ | ||||
| @@ -376,7 +437,6 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|         instruction.line = line; | ||||
|         instruction.column = column; | ||||
|         switch (opcode) { | ||||
|             case JANET_SYSOP_CALLK: | ||||
|             case JANET_SYSOP_ARG: | ||||
|                 janet_assert(0, "not reachable"); | ||||
|                 break; | ||||
| @@ -416,38 +476,25 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|             case JANET_SYSOP_POINTER_SUBTRACT: | ||||
|                 instr_assert_length(tuple, 4, opvalue); | ||||
|                 instruction.three.dest = instr_read_operand(tuple[1], out); | ||||
|                 instruction.three.lhs = instr_read_operand(tuple[2], out); | ||||
|                 instruction.three.rhs = instr_read_operand(tuple[3], out); | ||||
|                 instruction.three.lhs = instr_read_operand_or_const(tuple[2], out); | ||||
|                 instruction.three.rhs = instr_read_operand_or_const(tuple[3], out); | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CALL: | ||||
|                 instr_assert_min_length(tuple, 2, opvalue); | ||||
|                 if (janet_checktype(tuple[1], JANET_NIL)) { | ||||
|                 instr_assert_min_length(tuple, 3, opvalue); | ||||
|                 instruction.call.calling_convention = instr_read_cc(tuple[1]); | ||||
|                 if (janet_checktype(tuple[2], JANET_NIL)) { | ||||
|                     instruction.call.dest = 0; | ||||
|                     instruction.call.has_dest = 0; | ||||
|                 } else { | ||||
|                     instruction.call.dest = instr_read_operand(tuple[1], out); | ||||
|                     instruction.call.dest = instr_read_operand(tuple[2], out); | ||||
|                     instruction.call.has_dest = 1; | ||||
|                 } | ||||
|                 Janet c = tuple[2]; | ||||
|                 if (janet_checktype(c, JANET_SYMBOL)) { | ||||
|                     instruction.callk.arg_count = janet_tuple_length(tuple) - 3; | ||||
|                     Janet check = janet_table_get(constant_cache, c); | ||||
|                     if (janet_checktype(check, JANET_NUMBER)) { | ||||
|                         instruction.callk.constant = (uint32_t) janet_unwrap_number(check); | ||||
|                     } else { | ||||
|                         instruction.callk.constant = next_constant; | ||||
|                         janet_table_put(constant_cache, c, janet_wrap_integer(next_constant)); | ||||
|                         next_constant++; | ||||
|                     } | ||||
|                     opcode = JANET_SYSOP_CALLK; | ||||
|                     instruction.opcode = opcode; | ||||
|                 } else { | ||||
|                     instruction.call.arg_count = janet_tuple_length(tuple) - 3; | ||||
|                     instruction.call.callee = instr_read_operand(tuple[2], out); | ||||
|                 } | ||||
|                 instruction.call.arg_count = janet_tuple_length(tuple) - 4; | ||||
|                 instruction.call.callee = instr_read_operand_or_const(tuple[3], out); | ||||
|                 instruction.call.calling_convention = JANET_SYS_CC_DEFAULT; | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 for (int32_t j = 3; j < janet_tuple_length(tuple); j += 3) { | ||||
|                 for (int32_t j = 4; j < janet_tuple_length(tuple); j += 3) { | ||||
|                     JanetSysInstruction arginstr; | ||||
|                     arginstr.opcode = JANET_SYSOP_ARG; | ||||
|                     arginstr.line = line; | ||||
| @@ -458,7 +505,7 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|                     int32_t remaining = janet_tuple_length(tuple) - j; | ||||
|                     if (remaining > 3) remaining = 3; | ||||
|                     for (int32_t k = 0; k < remaining; k++) { | ||||
|                         arginstr.arg.args[k] = instr_read_operand(tuple[j + k], out); | ||||
|                         arginstr.arg.args[k] = instr_read_operand_or_const(tuple[j + k], out); | ||||
|                     } | ||||
|                     janet_v_push(ir, arginstr); | ||||
|                 } | ||||
| @@ -471,7 +518,7 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|             case JANET_SYSOP_ADDRESS: | ||||
|                 instr_assert_length(tuple, 3, opvalue); | ||||
|                 instruction.two.dest = instr_read_operand(tuple[1], out); | ||||
|                 instruction.two.src = instr_read_operand(tuple[2], out); | ||||
|                 instruction.two.src = instr_read_operand_or_const(tuple[2], out); | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
|             case JANET_SYSOP_FIELD_GETP: | ||||
| @@ -482,14 +529,19 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
|             case JANET_SYSOP_RETURN: | ||||
|                 instr_assert_length(tuple, 2, opvalue); | ||||
|                 instruction.one.src = instr_read_operand(tuple[1], out); | ||||
|                 instr_assert_max_length(tuple, 2, opvalue); | ||||
|                 if (janet_tuple_length(tuple) == 2) { | ||||
|                     instruction.ret.value = instr_read_operand_or_const(tuple[1], out); | ||||
|                     instruction.ret.has_value = 1; | ||||
|                 } else { | ||||
|                     instruction.ret.has_value = 0; | ||||
|                 } | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BRANCH: | ||||
|             case JANET_SYSOP_BRANCH_NOT: | ||||
|                 instr_assert_length(tuple, 3, opvalue); | ||||
|                 instruction.branch.cond = instr_read_operand(tuple[1], out); | ||||
|                 instruction.branch.cond = instr_read_operand_or_const(tuple[1], out); | ||||
|                 instruction.branch.to = instr_read_label(out, tuple[2]); | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
| @@ -498,21 +550,6 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|                 instruction.jump.to = instr_read_label(out, tuple[1]); | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CONSTANT: { | ||||
|                 instr_assert_length(tuple, 3, opvalue); | ||||
|                 instruction.constant.dest = instr_read_operand(tuple[1], out); | ||||
|                 Janet c = tuple[2]; | ||||
|                 Janet check = janet_table_get(constant_cache, c); | ||||
|                 if (janet_checktype(check, JANET_NUMBER)) { | ||||
|                     instruction.constant.constant = (uint32_t) janet_unwrap_number(check); | ||||
|                 } else { | ||||
|                     instruction.constant.constant = next_constant; | ||||
|                     janet_table_put(constant_cache, c, janet_wrap_number(next_constant)); | ||||
|                     next_constant++; | ||||
|                 } | ||||
|                 janet_v_push(ir, instruction); | ||||
|                 break; | ||||
|             } | ||||
|             case JANET_SYSOP_TYPE_PRIMITIVE: { | ||||
|                 instr_assert_length(tuple, 3, opvalue); | ||||
|                 instruction.type_prim.dest_type = instr_read_type_operand(tuple[1], out, 1); | ||||
| @@ -623,15 +660,8 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction | ||||
|     } | ||||
|  | ||||
|     /* Build constants */ | ||||
|     out->constant_count = next_constant; | ||||
|     out->constants = next_constant ? janet_malloc(sizeof(Janet) * out->constant_count) : NULL; | ||||
|     for (int32_t i = 0; i < constant_cache->capacity; i++) { | ||||
|         JanetKV kv = constant_cache->data[i]; | ||||
|         if (!janet_checktype(kv.key, JANET_NIL)) { | ||||
|             uint32_t index = (uint32_t) janet_unwrap_number(kv.value); | ||||
|             out->constants[index] = kv.key; | ||||
|         } | ||||
|     } | ||||
|     out->constant_count = janet_v_count(out->constants); | ||||
|     out->constants = janet_v_flatten(out->constants); | ||||
| } | ||||
|  | ||||
| /* Get a printable representation of a type on type failure */ | ||||
| @@ -754,6 +784,12 @@ static void tcheck_boolean(JanetSysIR *sysir, uint32_t t) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void rcheck_boolean(JanetSysIR *sysir, uint32_t reg) { | ||||
|     if (reg <= JANET_SYS_MAX_OPERAND) { | ||||
|         tcheck_boolean(sysir, sysir->types[reg]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tcheck_array(JanetSysIR *sysir, uint32_t t) { | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     if (linkage->type_defs[t].prim != JANET_PRIM_ARRAY) { | ||||
| @@ -784,6 +820,13 @@ static void tcheck_number_or_pointer(JanetSysIR *sysir, uint32_t t) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void rcheck_number_or_pointer(JanetSysIR *sysir, uint32_t reg) { | ||||
|     if (reg <= JANET_SYS_MAX_OPERAND) { | ||||
|         tcheck_number_or_pointer(sysir, sysir->types[reg]); | ||||
|     } | ||||
|     // TODO - check constants | ||||
| } | ||||
|  | ||||
| static void tcheck_integer(JanetSysIR *sysir, uint32_t t) { | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     JanetPrim t1 = linkage->type_defs[t].prim; | ||||
| @@ -806,7 +849,13 @@ static void tcheck_pointer(JanetSysIR *sysir, uint32_t t) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tcheck_pointer_equals(JanetSysIR *sysir, uint32_t preg, uint32_t elreg) { | ||||
| static void rcheck_pointer(JanetSysIR *sysir, uint32_t reg) { | ||||
|     if (reg <= JANET_SYS_MAX_OPERAND) { | ||||
|         tcheck_pointer(sysir, sysir->types[reg]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void rcheck_pointer_equals(JanetSysIR *sysir, uint32_t preg, uint32_t elreg) { | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     uint32_t t1 = sysir->types[preg]; | ||||
|     if (linkage->type_defs[t1].prim != JANET_PRIM_POINTER) { | ||||
| @@ -829,7 +878,9 @@ static void tcheck_struct_or_union(JanetSysIR *sysir, uint32_t t) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tcheck_equal(JanetSysIR *sysir, uint32_t reg1, uint32_t reg2) { | ||||
| static void rcheck_equal(JanetSysIR *sysir, uint32_t reg1, uint32_t reg2) { | ||||
|     if (reg1 == reg2) return; | ||||
|     if (reg1 > JANET_SYS_MAX_OPERAND || reg2 > JANET_SYS_MAX_OPERAND) return; | ||||
|     uint32_t t1 = sysir->types[reg1]; | ||||
|     uint32_t t2 = sysir->types[reg2]; | ||||
|     if (t1 != t2) { | ||||
| @@ -839,7 +890,7 @@ static void tcheck_equal(JanetSysIR *sysir, uint32_t reg1, uint32_t reg2) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int tcheck_cast_type(JanetSysIR *sysir, uint32_t td, uint32_t ts) { | ||||
| static int tcheck_cast(JanetSysIR *sysir, uint32_t td, uint32_t ts) { | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     if (td == ts) return 0; /* trivial case */ | ||||
|     JanetPrim primd = linkage->type_defs[td].prim; | ||||
| @@ -855,9 +906,9 @@ static int tcheck_cast_type(JanetSysIR *sysir, uint32_t td, uint32_t ts) { | ||||
|             default: | ||||
|                 return -1; | ||||
|             case JANET_PRIM_ARRAY: | ||||
|                 return tcheck_cast_type(sysir, linkage->type_defs[td].array.type, linkage->type_defs[ts].array.type); | ||||
|                 return tcheck_cast(sysir, linkage->type_defs[td].array.type, linkage->type_defs[ts].array.type); | ||||
|             case JANET_PRIM_POINTER: | ||||
|                 return tcheck_cast_type(sysir, linkage->type_defs[td].pointer.type, linkage->type_defs[ts].pointer.type); | ||||
|                 return tcheck_cast(sysir, linkage->type_defs[td].pointer.type, linkage->type_defs[ts].pointer.type); | ||||
|         } | ||||
|         return 0; | ||||
|         */ | ||||
| @@ -881,13 +932,13 @@ static int tcheck_cast_type(JanetSysIR *sysir, uint32_t td, uint32_t ts) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void tcheck_cast(JanetSysIR *sysir, uint32_t dest, uint32_t src) { | ||||
| static void rcheck_cast(JanetSysIR *sysir, uint32_t dest, uint32_t src) { | ||||
|     (void) sysir; | ||||
|     (void) dest; | ||||
|     (void) src; | ||||
|     uint32_t td = sysir->types[dest]; | ||||
|     uint32_t ts = sysir->types[src]; | ||||
|     int notok = tcheck_cast_type(sysir, td, ts); | ||||
|     int notok = tcheck_cast(sysir, td, ts); | ||||
|     if (notok) { | ||||
|         janet_panicf("type failure, %V cannot be cast to %V", | ||||
|                      tname(sysir, ts), | ||||
| @@ -895,16 +946,11 @@ static void tcheck_cast(JanetSysIR *sysir, uint32_t dest, uint32_t src) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tcheck_constant(JanetSysIR *sysir, uint32_t dest, Janet c) { | ||||
|     (void) sysir; | ||||
|     (void) dest; | ||||
|     (void) c; | ||||
|     /* TODO - validate the the constant C can be represented as dest */ | ||||
| } | ||||
|  | ||||
| static void tcheck_array_getp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
| static void rcheck_array_getp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
|     tcheck_array(sysir, sysir->types[lhs]); | ||||
|     tcheck_integer(sysir, sysir->types[rhs]); | ||||
|     if (rhs <= JANET_SYS_MAX_OPERAND) { | ||||
|         tcheck_integer(sysir, sysir->types[rhs]); | ||||
|     } | ||||
|     tcheck_pointer(sysir, sysir->types[dest]); | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     uint32_t dtype = linkage->type_defs[sysir->types[dest]].pointer.type; | ||||
| @@ -914,9 +960,11 @@ static void tcheck_array_getp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, ui | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tcheck_array_pgetp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
| static void rcheck_array_pgetp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
|     tcheck_pointer(sysir, sysir->types[lhs]); | ||||
|     tcheck_integer(sysir, sysir->types[rhs]); | ||||
|     if (rhs <= JANET_SYS_MAX_OPERAND) { | ||||
|         tcheck_integer(sysir, sysir->types[rhs]); | ||||
|     } | ||||
|     tcheck_pointer(sysir, sysir->types[dest]); | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     uint32_t aptype = linkage->type_defs[sysir->types[lhs]].pointer.type; | ||||
| @@ -930,7 +978,7 @@ static void tcheck_array_pgetp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, u | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void tcheck_fgetp(JanetSysIR *sysir, uint32_t dest, uint32_t st, uint32_t field) { | ||||
| static void rcheck_fgetp(JanetSysIR *sysir, uint32_t dest, uint32_t st, uint32_t field) { | ||||
|     tcheck_pointer(sysir, sysir->types[dest]); | ||||
|     tcheck_struct_or_union(sysir, sysir->types[st]); | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
| @@ -950,12 +998,18 @@ static void tcheck_fgetp(JanetSysIR *sysir, uint32_t dest, uint32_t st, uint32_t | ||||
| } | ||||
|  | ||||
| /* Unlike C, only allow pointer on lhs for addition and subtraction */ | ||||
| static void tcheck_pointer_math(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
|     tcheck_pointer_equals(sysir, dest, lhs); | ||||
|     tcheck_integer(sysir, sysir->types[rhs]); | ||||
| static void rcheck_pointer_math(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
|     rcheck_pointer_equals(sysir, dest, lhs); | ||||
|     if (rhs <= JANET_SYS_MAX_OPERAND) { | ||||
|         tcheck_integer(sysir, sysir->types[rhs]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static JanetString rname(JanetSysIR *sysir, uint32_t regid) { | ||||
|     if (regid > JANET_SYS_MAX_OPERAND) { | ||||
|         uint32_t id = regid - JANET_SYS_CONSTANT_PREFIX; | ||||
|         return janet_formatc("constant:%u[%v]", id, sysir->constants[id]); | ||||
|     } | ||||
|     JanetString name = sysir->register_names[regid]; | ||||
|     if (NULL == name) { | ||||
|         return janet_formatc("value[%u]", regid); | ||||
| @@ -965,6 +1019,7 @@ static JanetString rname(JanetSysIR *sysir, uint32_t regid) { | ||||
|  | ||||
| static int reg_is_unknown_type(JanetSysIR *sysir, uint32_t reg) { | ||||
|     JanetSysIRLinkage *linkage = sysir->linkage; | ||||
|     if (reg > JANET_SYS_MAX_OPERAND) return -1; | ||||
|     uint32_t t = sysir->types[reg]; | ||||
|     return (linkage->type_defs[t].prim == JANET_PRIM_UNKNOWN); | ||||
| } | ||||
| @@ -978,50 +1033,52 @@ static void janet_sysir_type_check(JanetSysIR *sysir) { | ||||
|             default: | ||||
|                 break; | ||||
|             case JANET_SYSOP_MOVE: | ||||
|                 if (reg_is_unknown_type(sysir, instruction.two.dest)) { | ||||
|                     sysir->types[instruction.two.dest] = sysir->types[instruction.two.src]; | ||||
|                 } | ||||
|                 if (reg_is_unknown_type(sysir, instruction.two.src)) { | ||||
|                     sysir->types[instruction.two.src] = sysir->types[instruction.two.dest]; | ||||
|                 if (instruction.two.src <= JANET_SYS_MAX_OPERAND) { | ||||
|                     if (reg_is_unknown_type(sysir, instruction.two.dest)) { | ||||
|                         sysir->types[instruction.two.dest] = sysir->types[instruction.two.src]; | ||||
|                     } | ||||
|                     if (reg_is_unknown_type(sysir, instruction.two.src)) { | ||||
|                         sysir->types[instruction.two.src] = sysir->types[instruction.two.dest]; | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             case JANET_SYSOP_CAST: | ||||
|                 tcheck_cast(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 rcheck_cast(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_POINTER_ADD: | ||||
|             case JANET_SYSOP_POINTER_SUBTRACT: | ||||
|                 tcheck_pointer_math(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_pointer_math(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ADD: | ||||
|             case JANET_SYSOP_SUBTRACT: | ||||
|             case JANET_SYSOP_MULTIPLY: | ||||
|             case JANET_SYSOP_DIVIDE: | ||||
|                 tcheck_number(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.dest])); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BAND: | ||||
|             case JANET_SYSOP_BOR: | ||||
|             case JANET_SYSOP_BXOR: | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.dest])); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BNOT: | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.two.src])); | ||||
|                 tcheck_equal(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.two.dest])); | ||||
|                 rcheck_equal(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_SHL: | ||||
|             case JANET_SYSOP_SHR: | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.lhs])); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.dest])); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_LOAD: | ||||
|                 tcheck_pointer_equals(sysir, instruction.two.src, instruction.two.dest); | ||||
|                 rcheck_pointer_equals(sysir, instruction.two.src, instruction.two.dest); | ||||
|                 break; | ||||
|             case JANET_SYSOP_STORE: | ||||
|                 tcheck_pointer_equals(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 rcheck_pointer_equals(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_GT: | ||||
|             case JANET_SYSOP_LT: | ||||
| @@ -1029,39 +1086,29 @@ static void janet_sysir_type_check(JanetSysIR *sysir) { | ||||
|             case JANET_SYSOP_NEQ: | ||||
|             case JANET_SYSOP_GTE: | ||||
|             case JANET_SYSOP_LTE: | ||||
|                 /* TODO - allow arrays */ | ||||
|                 tcheck_number_or_pointer(sysir, sysir->types[instruction.three.lhs]); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 //tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 tcheck_boolean(sysir, sysir->types[instruction.three.dest]); | ||||
|                 rcheck_number_or_pointer(sysir, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 //rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_boolean(sysir, instruction.three.dest); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ADDRESS: | ||||
|                 tcheck_pointer(sysir, sysir->types[instruction.two.dest]); | ||||
|                 rcheck_pointer(sysir, instruction.two.dest); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BRANCH: | ||||
|             case JANET_SYSOP_BRANCH_NOT: | ||||
|                 tcheck_boolean(sysir, sysir->types[instruction.branch.cond]); | ||||
|                 if (instruction.branch.to >= sysir->instruction_count) { | ||||
|                     janet_panicf("label outside of range [0, %u), got %u", sysir->instruction_count, instruction.branch.to); | ||||
|                 } | ||||
|                 break; | ||||
|             case JANET_SYSOP_CONSTANT: | ||||
|                 tcheck_constant(sysir, instruction.constant.dest, sysir->constants[instruction.constant.constant]); | ||||
|                 rcheck_boolean(sysir, instruction.branch.cond); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CALL: | ||||
|                 tcheck_pointer(sysir, sysir->types[instruction.call.callee]); | ||||
|                 rcheck_pointer(sysir, instruction.call.callee); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ARRAY_GETP: | ||||
|                 tcheck_array_getp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 rcheck_array_getp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ARRAY_PGETP: | ||||
|                 tcheck_array_pgetp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 rcheck_array_pgetp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_FIELD_GETP: | ||||
|                 tcheck_fgetp(sysir, instruction.field.r, instruction.field.st, instruction.field.field); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CALLK: | ||||
|                 /* TODO - check function return type */ | ||||
|                 rcheck_fgetp(sysir, instruction.field.r, instruction.field.st, instruction.field.field); | ||||
|                 break; | ||||
|         } | ||||
|         /* Write back possibly modified instruction */ | ||||
| @@ -1095,9 +1142,18 @@ static void janet_sysir_type_check(JanetSysIR *sysir) { | ||||
|             case JANET_SYSOP_LABEL: | ||||
|                 break; | ||||
|             case JANET_SYSOP_RETURN: { | ||||
|                 uint32_t ret_type = sysir->types[instruction.one.src]; | ||||
|                 uint32_t ret_type = 0; | ||||
|                 if (instruction.ret.has_value) { | ||||
|                     ret_type = sysir->types[instruction.ret.value]; | ||||
|                 } | ||||
|                 if (found_return) { | ||||
|                     if (sysir->return_type != ret_type) { | ||||
|                     if (instruction.ret.has_value && !sysir->has_return_type) { | ||||
|                         janet_panic("void return type not compatible with non-void return type"); | ||||
|                     } | ||||
|                     if ((!instruction.ret.has_value) && sysir->has_return_type) { | ||||
|                         janet_panic("void return type not compatible with non-void return type"); | ||||
|                     } | ||||
|                     if (sysir->has_return_type && sysir->return_type != ret_type) { | ||||
|                         janet_panicf("multiple return types are not allowed: %V and %V", | ||||
|                                      tname(sysir, ret_type), | ||||
|                                      tname(sysir, sysir->return_type)); | ||||
| @@ -1105,49 +1161,50 @@ static void janet_sysir_type_check(JanetSysIR *sysir) { | ||||
|                 } else { | ||||
|                     sysir->return_type = ret_type; | ||||
|                 } | ||||
|                 sysir->has_return_type = instruction.ret.has_value; | ||||
|                 found_return = 1; | ||||
|                 break; | ||||
|             } | ||||
|             case JANET_SYSOP_MOVE: | ||||
|                 tcheck_equal(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 rcheck_equal(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CAST: | ||||
|                 tcheck_cast(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 rcheck_cast(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_POINTER_ADD: | ||||
|             case JANET_SYSOP_POINTER_SUBTRACT: | ||||
|                 tcheck_pointer_math(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_pointer_math(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ADD: | ||||
|             case JANET_SYSOP_SUBTRACT: | ||||
|             case JANET_SYSOP_MULTIPLY: | ||||
|             case JANET_SYSOP_DIVIDE: | ||||
|                 tcheck_number(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.dest])); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BAND: | ||||
|             case JANET_SYSOP_BOR: | ||||
|             case JANET_SYSOP_BXOR: | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.dest])); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BNOT: | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.two.src])); | ||||
|                 tcheck_equal(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 rcheck_equal(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_SHL: | ||||
|             case JANET_SYSOP_SHR: | ||||
|                 tcheck_integer(sysir, tcheck_array_element(sysir, sysir->types[instruction.three.lhs])); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_LOAD: | ||||
|                 tcheck_pointer_equals(sysir, instruction.two.src, instruction.two.dest); | ||||
|                 rcheck_pointer_equals(sysir, instruction.two.src, instruction.two.dest); | ||||
|                 break; | ||||
|             case JANET_SYSOP_STORE: | ||||
|                 tcheck_pointer_equals(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 rcheck_pointer_equals(sysir, instruction.two.dest, instruction.two.src); | ||||
|                 break; | ||||
|             case JANET_SYSOP_GT: | ||||
|             case JANET_SYSOP_LT: | ||||
| @@ -1156,35 +1213,29 @@ static void janet_sysir_type_check(JanetSysIR *sysir) { | ||||
|             case JANET_SYSOP_GTE: | ||||
|             case JANET_SYSOP_LTE: | ||||
|                 /* TODO - allow arrays */ | ||||
|                 tcheck_number_or_pointer(sysir, sysir->types[instruction.three.lhs]); | ||||
|                 tcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 //tcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 rcheck_number_or_pointer(sysir, instruction.three.lhs); | ||||
|                 rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); | ||||
|                 //rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); | ||||
|                 tcheck_boolean(sysir, sysir->types[instruction.three.dest]); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ADDRESS: | ||||
|                 tcheck_pointer(sysir, sysir->types[instruction.two.dest]); | ||||
|                 rcheck_pointer(sysir, sysir->types[instruction.two.dest]); | ||||
|                 break; | ||||
|             case JANET_SYSOP_BRANCH: | ||||
|             case JANET_SYSOP_BRANCH_NOT: | ||||
|                 tcheck_boolean(sysir, sysir->types[instruction.branch.cond]); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CONSTANT: | ||||
|                 tcheck_constant(sysir, instruction.constant.dest, sysir->constants[instruction.constant.constant]); | ||||
|                 rcheck_boolean(sysir, instruction.branch.cond); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CALL: | ||||
|                 tcheck_pointer(sysir, sysir->types[instruction.call.callee]); | ||||
|                 rcheck_pointer(sysir, instruction.call.callee); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ARRAY_GETP: | ||||
|                 tcheck_array_getp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 rcheck_array_getp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_ARRAY_PGETP: | ||||
|                 tcheck_array_pgetp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 rcheck_array_pgetp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); | ||||
|                 break; | ||||
|             case JANET_SYSOP_FIELD_GETP: | ||||
|                 tcheck_fgetp(sysir, instruction.field.r, instruction.field.st, instruction.field.field); | ||||
|                 break; | ||||
|             case JANET_SYSOP_CALLK: | ||||
|                 /* TODO - check function return type */ | ||||
|                 rcheck_fgetp(sysir, instruction.field.r, instruction.field.st, instruction.field.field); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| @@ -1216,6 +1267,7 @@ static void janet_sys_ir_init(JanetSysIR *out, JanetView instructions, JanetSysI | ||||
|     ir.return_type = 0; | ||||
|     ir.parameter_count = 0; | ||||
|     ir.register_name_lookup = janet_table(0); | ||||
|     ir.constants_lookup = janet_table(0); | ||||
|     ir.labels = janet_table(0); | ||||
|     ir.register_names = NULL; | ||||
|     ir.linkage = linkage; | ||||
| @@ -1257,6 +1309,15 @@ static const char *c_prim_names[] = { | ||||
|     "bool" | ||||
| }; | ||||
|  | ||||
| static void op_or_const(JanetSysIR *ir, JanetBuffer *buf, uint32_t reg) { | ||||
|     if (reg < JANET_SYS_MAX_OPERAND) { | ||||
|         janet_formatb(buf, "_r%u", reg); | ||||
|     } else { | ||||
|         uint32_t constant_id = reg - JANET_SYS_CONSTANT_PREFIX; | ||||
|         janet_formatb(buf, "%v", ir->constants[constant_id]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void emit_binop(JanetSysIR *ir, JanetBuffer *buffer, JanetBuffer *tempbuf, JanetSysInstruction instruction, const char *op) { | ||||
|     uint32_t operand_type = ir->types[instruction.three.dest]; | ||||
|     tempbuf->count = 0; | ||||
| @@ -1292,13 +1353,18 @@ static void emit_binop(JanetSysIR *ir, JanetBuffer *buffer, JanetBuffer *tempbuf | ||||
|                       instruction.three.lhs, | ||||
|                       op, | ||||
|                       instruction.three.rhs); | ||||
|         janet_formatb(buffer, "  *_r%u = *", instruction.three.dest); | ||||
|         op_or_const(ir, buffer, instruction.three.lhs); | ||||
|         janet_formatb(buffer, " %s ", op); | ||||
|         op_or_const(ir, buffer, instruction.three.rhs); | ||||
|         janet_formatb(buffer, ";\n"); | ||||
|     } else { | ||||
|         Janet index_part = janet_wrap_buffer(tempbuf); | ||||
|         janet_formatb(buffer, "  _r%u%V = _r%u%V %s _r%u%V;\n", | ||||
|                       instruction.three.dest, index_part, | ||||
|                       instruction.three.lhs, index_part, | ||||
|                       op, | ||||
|                       instruction.three.rhs, index_part); | ||||
|         janet_formatb(buffer, "  _r%u%V = ", instruction.three.dest, index_part); | ||||
|         op_or_const(ir, buffer, instruction.three.lhs); | ||||
|         janet_formatb(buffer, "%V %s ", index_part, op); | ||||
|         op_or_const(ir, buffer, instruction.three.rhs); | ||||
|         janet_formatb(buffer, "%V;\n", index_part); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1362,7 +1428,7 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { | ||||
|         if (ir->link_name == NULL) { | ||||
|             continue; | ||||
|         } | ||||
|         janet_formatb(buffer, "_t%u %s(", ir->return_type, (ir->link_name != NULL) ? ir->link_name : janet_cstring("_thunk")); | ||||
|         janet_formatb(buffer, "\n\n_t%u %s(", ir->return_type, (ir->link_name != NULL) ? ir->link_name : janet_cstring("_thunk")); | ||||
|         for (uint32_t i = 0; i < ir->parameter_count; i++) { | ||||
|             if (i) janet_buffer_push_cstring(buffer, ", "); | ||||
|             janet_formatb(buffer, "_t%u _r%u", ir->types[i], i); | ||||
| @@ -1377,7 +1443,7 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { | ||||
|         for (uint32_t i = 0; i < ir->instruction_count; i++) { | ||||
|             JanetSysInstruction instruction = ir->instructions[i]; | ||||
|             if (instruction.line > 0) { | ||||
|                 janet_formatb(buffer, "#line %d\n  ", instruction.line); | ||||
|                 janet_formatb(buffer, "#line %d\n", instruction.line); | ||||
|             } | ||||
|             switch (instruction.opcode) { | ||||
|                 case JANET_SYSOP_TYPE_PRIMITIVE: | ||||
| @@ -1394,25 +1460,28 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { | ||||
|                     janet_formatb(buffer, "\n_label_%u:\n", instruction.label.id); | ||||
|                     break; | ||||
|                 } | ||||
|                 case JANET_SYSOP_CONSTANT: { | ||||
|                     uint32_t cast = ir->types[instruction.two.dest]; | ||||
|                     janet_formatb(buffer, "  _r%u = (_t%u) %j;\n", instruction.two.dest, cast, ir->constants[instruction.two.src]); | ||||
|                     break; | ||||
|                 } | ||||
|                 case JANET_SYSOP_ADDRESS: | ||||
|                     janet_formatb(buffer, "  _r%u = (char *) &_r%u;\n", instruction.two.dest, instruction.two.src); | ||||
|                     janet_formatb(buffer, "  _r%u = (char *) &", instruction.two.dest); | ||||
|                     op_or_const(ir, buffer, instruction.two.src); | ||||
|                     janet_formatb(buffer, ";\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_JUMP: | ||||
|                     janet_formatb(buffer, "  goto _label_%u;\n", instruction.jump.to); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BRANCH: | ||||
|                     janet_formatb(buffer, "  if (_r%u) goto _label_%u;\n", instruction.branch.cond, instruction.branch.to); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BRANCH_NOT: | ||||
|                     janet_formatb(buffer, "  if (!_r%u) goto _label_%u;\n", instruction.branch.cond, instruction.branch.to); | ||||
|                     janet_formatb(buffer, instruction.opcode == JANET_SYSOP_BRANCH ? "  if (" : "  if (!"); | ||||
|                     op_or_const(ir, buffer, instruction.branch.cond); | ||||
|                     janet_formatb(buffer, ") goto _label_%u;\n", instruction.branch.to); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_RETURN: | ||||
|                     janet_formatb(buffer, "  return _r%u;\n", instruction.one.src); | ||||
|                     if (instruction.ret.has_value) { | ||||
|                         janet_buffer_push_cstring(buffer, "  return "); | ||||
|                         op_or_const(ir, buffer, instruction.ret.value); | ||||
|                         janet_buffer_push_cstring(buffer, ";\n"); | ||||
|                     } else { | ||||
|                         janet_buffer_push_cstring(buffer, "  return;\n"); | ||||
|                     } | ||||
|                     break; | ||||
|                 case JANET_SYSOP_ADD: | ||||
|                 case JANET_SYSOP_POINTER_ADD: | ||||
| @@ -1463,58 +1532,59 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CALL: { | ||||
|                     if (instruction.call.has_dest) { | ||||
|                         janet_formatb(buffer, "  _r%u = _r%u(", instruction.call.dest, instruction.call.callee); | ||||
|                         janet_formatb(buffer, "  _r%u = ", instruction.call.dest); | ||||
|                     } else { | ||||
|                         janet_formatb(buffer, "  _r%u(", instruction.call.callee); | ||||
|                         janet_formatb(buffer, "  "); | ||||
|                     } | ||||
|                     op_or_const(ir, buffer, instruction.call.callee); | ||||
|                     janet_formatb(buffer, "("); | ||||
|                     for (uint32_t j = 0; j < instruction.call.arg_count; j++) { | ||||
|                         uint32_t offset = j / 3 + 1; | ||||
|                         uint32_t index = j % 3; | ||||
|                         JanetSysInstruction arg_instruction = ir->instructions[i + offset]; | ||||
|                         janet_formatb(buffer, j ? ", _r%u" : "_r%u", arg_instruction.arg.args[index]); | ||||
|                         if (j) janet_formatb(buffer, ", "); | ||||
|                         op_or_const(ir, buffer, arg_instruction.arg.args[index]); | ||||
|                     } | ||||
|                     janet_formatb(buffer, "); // CALL\n"); | ||||
|                     janet_formatb(buffer, ");\n"); | ||||
|                     break; | ||||
|                 } | ||||
|                 case JANET_SYSOP_CALLK: | ||||
|                     if (instruction.callk.has_dest) { | ||||
|                         janet_formatb(buffer, "  _r%u = %v(", instruction.callk.dest, ir->constants[instruction.callk.constant]); | ||||
|                     } else { | ||||
|                         janet_formatb(buffer, "  %v(", ir->constants[instruction.callk.constant]); | ||||
|                     } | ||||
|                     for (uint32_t j = 0; j < instruction.callk.arg_count; j++) { | ||||
|                         uint32_t offset = j / 3 + 1; | ||||
|                         uint32_t index = j % 3; | ||||
|                         JanetSysInstruction arg_instruction = ir->instructions[i + offset]; | ||||
|                         janet_formatb(buffer, j ? ", _r%u" : "_r%u", arg_instruction.arg.args[index]); | ||||
|                     } | ||||
|                     janet_formatb(buffer, "); // CALLK\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CAST: { | ||||
|                     uint32_t to = ir->types[instruction.two.dest]; | ||||
|                     janet_formatb(buffer, "  _r%u = (_t%u) (_r%u);\n", instruction.two.dest, to, instruction.two.src); | ||||
|                     break; | ||||
|                 } | ||||
|                 case JANET_SYSOP_MOVE: | ||||
|                     janet_formatb(buffer, "  _r%u = _r%u;\n", instruction.two.dest, instruction.two.src); | ||||
|                     janet_formatb(buffer, "  _r%u = ", instruction.two.dest); | ||||
|                     op_or_const(ir, buffer, instruction.two.src); | ||||
|                     janet_formatb(buffer, ";\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BNOT: | ||||
|                     janet_formatb(buffer, "  _r%u = ~_r%u;\n", instruction.two.dest, instruction.two.src); | ||||
|                     janet_formatb(buffer, "  _r%u = ~", instruction.two.dest); | ||||
|                     op_or_const(ir, buffer, instruction.two.src); | ||||
|                     janet_formatb(buffer, ";\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_LOAD: | ||||
|                     janet_formatb(buffer, "  _r%u = *(_r%u);\n", instruction.two.dest, instruction.two.src); | ||||
|                     janet_formatb(buffer, "  _r%u = *(", instruction.two.dest); | ||||
|                     op_or_const(ir, buffer, instruction.two.src); | ||||
|                     janet_formatb(buffer, ");\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_STORE: | ||||
|                     janet_formatb(buffer, "  *(_r%u) = _r%u;\n", instruction.two.dest, instruction.two.src); | ||||
|                     janet_formatb(buffer, "  *(_r%u) = ", instruction.two.dest); | ||||
|                     op_or_const(ir, buffer, instruction.two.src); | ||||
|                     janet_formatb(buffer, ";\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_FIELD_GETP: | ||||
|                     janet_formatb(buffer, "  _r%u = &(_r%u._f%u);\n", instruction.field.r, instruction.field.st, instruction.field.field); | ||||
|                     janet_formatb(buffer, "  _r%u = &(", instruction.field.r); | ||||
|                     janet_formatb(buffer, "._f%u);\n", instruction.field.field); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_ARRAY_GETP: | ||||
|                     janet_formatb(buffer, "  _r%u = &(_r%u.els[_r%u]);\n", instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                     // TODO | ||||
|                     //janet_formatb(buffer, "  _r%u = &(_r%u.els[_r%u]);\n", instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_ARRAY_PGETP: | ||||
|                     janet_formatb(buffer, "  _r%u = &(_r%u->els[_r%u]);\n", instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                     // TODO | ||||
|                     //janet_formatb(buffer, "  _r%u = &(_r%u->els[_r%u]);\n", instruction.three.dest, instruction.three.lhs, instruction.three.rhs); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
| @@ -1527,6 +1597,20 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { | ||||
|  | ||||
| /* Convert IR linkage back to Janet ASM */ | ||||
|  | ||||
| static Janet wrap_op(JanetSysIR *ir, uint32_t reg) { | ||||
|     if (reg <= JANET_SYS_MAX_OPERAND) { | ||||
|         return janet_wrap_number(reg); | ||||
|     } | ||||
|     Janet *tuple = janet_tuple_begin(1); | ||||
|     tuple[0] = ir->constants[reg - JANET_SYS_CONSTANT_PREFIX]; | ||||
|     janet_tuple_flag(tuple) |= JANET_TUPLE_FLAG_BRACKETCTOR; | ||||
|     return janet_wrap_tuple(janet_tuple_end(tuple)); | ||||
| } | ||||
|  | ||||
| static Janet wrap_dest(uint32_t reg) { | ||||
|     return janet_wrap_number(reg); | ||||
| } | ||||
|  | ||||
| void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into) { | ||||
|  | ||||
|     /* Emit type defs */ | ||||
| @@ -1637,8 +1721,8 @@ void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into) { | ||||
|                 case JANET_SYSOP_ADDRESS: | ||||
|                     build_tuple = janet_tuple_begin(3); | ||||
|                     build_tuple[0] = janet_csymbolv(janet_sysop_names[instruction.opcode]); | ||||
|                     build_tuple[1] = janet_wrap_number(instruction.two.dest); | ||||
|                     build_tuple[2] = janet_wrap_number(instruction.two.src); | ||||
|                     build_tuple[1] = wrap_dest(instruction.two.dest); | ||||
|                     build_tuple[2] = wrap_op(ir, instruction.two.src); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_ADD: | ||||
|                 case JANET_SYSOP_SUBTRACT: | ||||
| @@ -1661,47 +1745,25 @@ void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into) { | ||||
|                 case JANET_SYSOP_ARRAY_PGETP: | ||||
|                     build_tuple = janet_tuple_begin(4); | ||||
|                     build_tuple[0] = janet_csymbolv(janet_sysop_names[instruction.opcode]); | ||||
|                     build_tuple[1] = janet_wrap_number(instruction.three.dest); | ||||
|                     build_tuple[2] = janet_wrap_number(instruction.three.lhs); | ||||
|                     build_tuple[3] = janet_wrap_number(instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CALLK: | ||||
|                     build_tuple = janet_tuple_begin(3 + instruction.callk.arg_count); | ||||
|                     build_tuple[0] = janet_csymbolv("call"); | ||||
|                     if (instruction.callk.has_dest) { | ||||
|                         build_tuple[1] = janet_wrap_number(instruction.callk.dest); | ||||
|                     } else { | ||||
|                         build_tuple[1] = janet_wrap_nil(); | ||||
|                     } | ||||
|                     build_tuple[2] = ir->constants[instruction.callk.constant]; | ||||
|                     for (uint32_t j = 0; j < instruction.callk.arg_count; j++) { | ||||
|                         uint32_t offset = j / 3 + 1; | ||||
|                         uint32_t index = j % 3; | ||||
|                         JanetSysInstruction arg_instruction = ir->instructions[i + offset]; | ||||
|                         build_tuple[j + 3] = janet_wrap_number(arg_instruction.arg.args[index]); | ||||
|                     } | ||||
|                     build_tuple[1] = wrap_dest(instruction.three.dest); | ||||
|                     build_tuple[2] = wrap_op(ir, instruction.three.lhs); | ||||
|                     build_tuple[3] = wrap_op(ir, instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_LABEL: | ||||
|                     build_tuple = janet_tuple_begin(2); | ||||
|                     build_tuple[0] = janet_csymbolv("label"); | ||||
|                     build_tuple[1] = janet_table_get(ir->labels, janet_wrap_number(instruction.label.id)); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CONSTANT: | ||||
|                     build_tuple = janet_tuple_begin(3); | ||||
|                     build_tuple[0] = janet_csymbolv("constant"); | ||||
|                     build_tuple[1] = janet_wrap_number(instruction.constant.dest); | ||||
|                     build_tuple[2] = ir->constants[instruction.constant.constant]; | ||||
|                     break; | ||||
|                 case JANET_SYSOP_RETURN: | ||||
|                     build_tuple = janet_tuple_begin(2); | ||||
|                     build_tuple = janet_tuple_begin(instruction.ret.has_value ? 2 : 1); | ||||
|                     build_tuple[0] = janet_csymbolv("return"); | ||||
|                     build_tuple[1] = janet_wrap_number(instruction.one.src); | ||||
|                     if (instruction.ret.has_value) build_tuple[1] = wrap_op(ir, instruction.ret.value); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BRANCH: | ||||
|                 case JANET_SYSOP_BRANCH_NOT: | ||||
|                     build_tuple = janet_tuple_begin(3); | ||||
|                     build_tuple[0] = janet_csymbolv(janet_sysop_names[instruction.opcode]); | ||||
|                     build_tuple[1] = janet_wrap_number(instruction.branch.cond); | ||||
|                     build_tuple[1] = wrap_op(ir, instruction.branch.cond); | ||||
|                     build_tuple[2] = janet_table_get(ir->labels, janet_wrap_number(instruction.branch.to)); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_JUMP: | ||||
| @@ -1712,10 +1774,22 @@ void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into) { | ||||
|                 case JANET_SYSOP_FIELD_GETP: | ||||
|                     build_tuple = janet_tuple_begin(4); | ||||
|                     build_tuple[0] = janet_csymbolv(janet_sysop_names[instruction.opcode]); | ||||
|                     build_tuple[1] = janet_wrap_number(instruction.field.r); | ||||
|                     build_tuple[2] = janet_wrap_number(instruction.field.st); | ||||
|                     build_tuple[1] = wrap_dest(instruction.field.r); | ||||
|                     build_tuple[2] = wrap_op(ir, instruction.field.st); | ||||
|                     build_tuple[3] = janet_wrap_number(instruction.field.field); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CALL: | ||||
|                     build_tuple = janet_tuple_begin(4 + instruction.call.arg_count); | ||||
|                     build_tuple[0] = janet_csymbolv(janet_sysop_names[instruction.opcode]); | ||||
|                     build_tuple[1] = janet_ckeywordv(calling_convention_names[instruction.call.calling_convention]); | ||||
|                     build_tuple[2] = instruction.call.has_dest ? wrap_dest(instruction.call.dest) : janet_wrap_nil(); | ||||
|                     build_tuple[3] = wrap_op(ir, instruction.call.callee); | ||||
|                     for (uint32_t j = 0; j < instruction.call.arg_count; j++) { | ||||
|                         uint32_t offset = j / 3; | ||||
|                         uint32_t sub_offset = j - 3 * offset; | ||||
|                         build_tuple[4 + j] = wrap_op(ir, ir->instructions[offset + 1].arg.args[sub_offset]); | ||||
|                     } | ||||
|                     break; | ||||
|             } | ||||
|             if (instruction.line > 0) { | ||||
|                 janet_tuple_sm_line(build_tuple) = instruction.line; | ||||
| @@ -1759,6 +1833,7 @@ static int sysir_gcmark(void *p, size_t s) { | ||||
|     } | ||||
|     janet_mark(janet_wrap_table(ir->labels)); | ||||
|     janet_mark(janet_wrap_table(ir->register_name_lookup)); | ||||
|     janet_mark(janet_wrap_table(ir->constants_lookup)); | ||||
|     janet_mark(janet_wrap_abstract(ir->linkage)); | ||||
|     return 0; | ||||
| } | ||||
| @@ -1833,8 +1908,8 @@ JANET_CORE_FN(cfun_sysir_toc, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_sysir_toir, | ||||
|         "(sysir/to-ir context &opt array)", | ||||
|         "Raise IR back to IR after running other optimizations on it. Returns an array with various linkages.") { | ||||
|               "(sysir/to-ir context &opt array)", | ||||
|               "Raise IR back to IR after running other optimizations on it. Returns an array with various linkages.") { | ||||
|     janet_arity(argc, 1, 2); | ||||
|     JanetSysIRLinkage *ir = janet_getabstract(argv, 0, &janet_sysir_context_type); | ||||
|     JanetArray *array = janet_optarray(argv, argc, 1, 0); | ||||
| @@ -1843,15 +1918,15 @@ JANET_CORE_FN(cfun_sysir_toir, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_sysir_tox64, | ||||
|         "(sysir/to-x64 context &opt buffer options)", | ||||
|         "Lower IR to x64 machine code.") { | ||||
|               "(sysir/to-x64 context &opt buffer options)", | ||||
|               "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); | ||||
|     return janet_wrap_buffer(buffer); | ||||
| } | ||||
|          | ||||
|  | ||||
| void janet_lib_sysir(JanetTable *env) { | ||||
|     JanetRegExt cfuns[] = { | ||||
|         JANET_CORE_REG("sysir/context", cfun_sysir_context), | ||||
|   | ||||
| @@ -82,14 +82,12 @@ typedef enum { | ||||
|     JANET_SYSOP_NEQ, | ||||
|     JANET_SYSOP_GTE, | ||||
|     JANET_SYSOP_LTE, | ||||
|     JANET_SYSOP_CONSTANT, | ||||
|     JANET_SYSOP_CALL, | ||||
|     JANET_SYSOP_RETURN, | ||||
|     JANET_SYSOP_JUMP, | ||||
|     JANET_SYSOP_BRANCH, | ||||
|     JANET_SYSOP_BRANCH_NOT, | ||||
|     JANET_SYSOP_ADDRESS, | ||||
|     JANET_SYSOP_CALLK, | ||||
|     JANET_SYSOP_TYPE_PRIMITIVE, | ||||
|     JANET_SYSOP_TYPE_STRUCT, | ||||
|     JANET_SYSOP_TYPE_BIND, | ||||
| @@ -131,6 +129,17 @@ typedef struct { | ||||
| #define JANET_SYS_MAX_OPERAND      0x7FFFFFFFU | ||||
| #define JANET_SYS_CONSTANT_PREFIX  0x80000000U | ||||
|  | ||||
| typedef enum { | ||||
|     JANET_SYS_CC_DEFAULT, /* Reasonable default - maps to a specific cc based on target */ | ||||
|     JANET_SYS_CC_SYSCALL, /* Reasonable default for platform syscalls - maps to a specific cc based on target */ | ||||
|     JANET_SYS_CC_X86_CDECL, | ||||
|     JANET_SYS_CC_X86_STDCALL, | ||||
|     JANET_SYS_CC_X86_FASTCALL, | ||||
|     JANET_SYS_CC_X64_SYSV, | ||||
|     JANET_SYS_CC_X64_SYSV_SYSCALL, | ||||
|     JANET_SYS_CC_X64_WINDOWS, | ||||
| } JanetSysCallingConvention; | ||||
|  | ||||
| typedef struct { | ||||
|     JanetSysOp opcode; | ||||
|     union { | ||||
| @@ -143,14 +152,9 @@ typedef struct { | ||||
|             uint32_t dest; | ||||
|             uint32_t callee; | ||||
|             uint32_t arg_count; | ||||
|             uint32_t has_dest; | ||||
|             uint8_t has_dest; | ||||
|             JanetSysCallingConvention calling_convention; | ||||
|         } call; | ||||
|         struct { | ||||
|             uint32_t dest; | ||||
|             uint32_t constant; | ||||
|             uint32_t arg_count; | ||||
|             uint32_t has_dest; | ||||
|         } callk; | ||||
|         struct { | ||||
|             uint32_t dest; | ||||
|             uint32_t src; | ||||
| @@ -165,10 +169,6 @@ typedef struct { | ||||
|             uint32_t cond; | ||||
|             uint32_t to; | ||||
|         } branch; | ||||
|         struct { | ||||
|             uint32_t dest; | ||||
|             uint32_t constant; | ||||
|         } constant; | ||||
|         struct { | ||||
|             uint32_t dest_type; | ||||
|             uint32_t prim; | ||||
| @@ -201,6 +201,10 @@ typedef struct { | ||||
|         struct { | ||||
|             uint32_t id; | ||||
|         } label; | ||||
|         struct { | ||||
|             uint32_t value; | ||||
|             uint32_t has_value; | ||||
|         } ret; | ||||
|     }; | ||||
|     int32_t line; | ||||
|     int32_t column; | ||||
| @@ -234,32 +238,18 @@ typedef struct { | ||||
|     uint32_t register_count; | ||||
|     uint32_t constant_count; | ||||
|     uint32_t return_type; | ||||
|     uint32_t has_return_type; | ||||
|     uint32_t parameter_count; | ||||
|     uint32_t label_count; | ||||
|     uint32_t *types; | ||||
|     JanetSysInstruction *instructions; | ||||
|     JanetString *register_names; | ||||
|     Janet *constants; | ||||
|     JanetTable *constants_lookup; | ||||
|     JanetTable *register_name_lookup; | ||||
|     JanetTable *labels; | ||||
| } JanetSysIR; | ||||
|  | ||||
| /* Represent register spills after doing register allocation. Before lowering | ||||
|  * individual instructions, check if any spills occur and possibly insert extra | ||||
|  * reads and writes from/to the stack. Up to 6 spills per instruction (3 registers | ||||
|  * a load and store each) */ | ||||
| typedef struct { | ||||
|     enum { | ||||
|         JANET_SYS_SPILL_NONE, | ||||
|         JANET_SYS_SPILL_READ, | ||||
|         JANET_SYS_SPILL_WRITE, | ||||
|         JANET_SYS_SPILL_BOTH | ||||
|     } spills[3]; | ||||
|     uint32_t regs[3]; | ||||
|     uint32_t stack_offsets[3]; | ||||
|     uint32_t stack_sizes[3]; | ||||
| } JanetSysSpill; | ||||
|  | ||||
| /* Delay alignment info for the most part to the lowering phase */ | ||||
| typedef struct { | ||||
|     uint32_t size; | ||||
|   | ||||
| @@ -28,11 +28,60 @@ | ||||
| #include "util.h" | ||||
| #endif | ||||
|  | ||||
| #define RAX 0 | ||||
| #define RCX 1 | ||||
| #define RDX 2 | ||||
| #define RBX 3 | ||||
| #define RSI 4 | ||||
| #define RDI 5 | ||||
| #define RSP 6 | ||||
| #define RBP 7 | ||||
|  | ||||
| static const char *register_names[] = { | ||||
|     "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", | ||||
|     "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "rsp", "rbp", | ||||
|     "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" | ||||
| }; | ||||
|  | ||||
| static const char *register_names_32[] = { | ||||
|     "eax", "ecx", "edx", "ebx", "esi", "edi", "esp", "ebp", | ||||
|     "r8d", "r9d", "r10d", "rlld", "r12d", "r13d", "r14d", "r15d" | ||||
| }; | ||||
|  | ||||
| static const char *register_names_16[] = { | ||||
|     "ax", "cx", "dx", "bx", "si", "di", "sp", "bp", | ||||
|     "r8w", "r9w", "r10w", "rllw", "r12w", "r13w", "r14w", "r15w" | ||||
| }; | ||||
|  | ||||
| static const char *register_names_8[] = { | ||||
|     "al", "cl", "dl", "bl", "sil", "dil", "spl", "bpl", | ||||
|     "r8b", "r9b", "r10b", "rllb", "r12b", "r13b", "r14b", "r15b" | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|     enum { | ||||
|         JANET_SYSREG_STACK, | ||||
|         JANET_SYSREG_8, | ||||
|         JANET_SYSREG_16, | ||||
|         JANET_SYSREG_32, | ||||
|         JANET_SYSREG_64, | ||||
|         JANET_SYSREG_XMM | ||||
|     } kind; | ||||
|     uint32_t index; | ||||
| } x64Reg; | ||||
|  | ||||
| typedef struct { | ||||
|     JanetSysIRLinkage *linkage; | ||||
|     JanetSysIR *ir; | ||||
|     JanetBuffer *buffer; | ||||
|     x64Reg *regs; /* Map IR virtual registers to hardware register or stack offset */ | ||||
|     JanetSysTypeLayout *layouts; | ||||
|     JanetSysTypeLayout *ir_layouts; | ||||
|     uint32_t frame_size; | ||||
|     uint32_t restore_count; | ||||
|     uint32_t to_restore[128]; | ||||
|     JanetSysCallingConvention calling_convention; | ||||
| } JanetSysx64Context; | ||||
|  | ||||
| /* Get the layout for types */ | ||||
| JanetSysTypeLayout get_x64layout(JanetSysTypeInfo info) { | ||||
|     JanetSysTypeLayout layout; | ||||
| @@ -72,18 +121,7 @@ JanetSysTypeLayout get_x64layout(JanetSysTypeInfo info) { | ||||
|     return layout; | ||||
| } | ||||
|  | ||||
| static uint32_t v2reg_dflt(JanetTable *assignments, uint32_t var, uint32_t dflt) { | ||||
|     Janet check = janet_table_get(assignments, janet_wrap_number(var)); | ||||
|     if (janet_checktype(check, JANET_NUMBER)) { | ||||
|         return (uint32_t) janet_unwrap_number(check); | ||||
|     } | ||||
|     return dflt; | ||||
| } | ||||
|  | ||||
| JanetSysSpill *assign_registers(JanetSysIR *ir, | ||||
|         JanetSysTypeLayout *layouts, | ||||
|         JanetTable *assignments, | ||||
|         uint32_t max_reg) { | ||||
| void assign_registers(JanetSysx64Context *ctx) { | ||||
|  | ||||
|     /* simplest register assignment algorithm - first n variables | ||||
|      * get registers, rest get assigned temporary registers and spill on every use. */ | ||||
| @@ -92,199 +130,142 @@ JanetSysSpill *assign_registers(JanetSysIR *ir, | ||||
|     /* TODO - move into sysir.c and allow reuse for multiple targets */ | ||||
|  | ||||
|     /* Make trivial assigments */ | ||||
|     for (uint32_t i = 0; i < ir->register_count; i++) { | ||||
|         if (i < max_reg) { | ||||
|             janet_table_put(assignments, janet_wrap_number(i), janet_wrap_number(i)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Assign all slots a stack location */ | ||||
|     /* TODO - be smarter about this */ | ||||
|     uint32_t *stack_locations = janet_smalloc(ir->register_count * sizeof(uint32_t)); | ||||
|     uint32_t next_loc = 0; | ||||
|     for (uint32_t i = 0; i < ir->register_count; i++) { | ||||
|         JanetSysTypeLayout layout = layouts[i]; | ||||
|         next_loc = (next_loc + layout.alignment - 1) / layout.alignment * layout.alignment; | ||||
|         stack_locations[i] = next_loc; | ||||
|         next_loc += layout.size; | ||||
|     } | ||||
|  | ||||
|     /* Generate spills. Spills occur iff using the temporary register (max_reg) */ | ||||
|     JanetSysSpill *spills = NULL; | ||||
|     for (uint32_t i = 0; i < ir->instruction_count; i++) { | ||||
|         JanetSysInstruction instruction = ir->instructions[i]; | ||||
|         JanetSysSpill spill; | ||||
|         spill.spills[0] = JANET_SYS_SPILL_NONE; | ||||
|         spill.spills[1] = JANET_SYS_SPILL_NONE; | ||||
|         spill.spills[2] = JANET_SYS_SPILL_NONE; | ||||
|         uint32_t rega; | ||||
|         uint32_t regb; | ||||
|         uint32_t regc; | ||||
|         switch (instruction.opcode) { | ||||
|             default: | ||||
|                 break; | ||||
|  | ||||
|             /* DEST = LHS op RHS */ | ||||
|             case JANET_SYSOP_ADD: | ||||
|             case JANET_SYSOP_SUBTRACT: | ||||
|             case JANET_SYSOP_MULTIPLY: | ||||
|             case JANET_SYSOP_DIVIDE: | ||||
|             case JANET_SYSOP_BAND: | ||||
|             case JANET_SYSOP_BOR: | ||||
|             case JANET_SYSOP_BXOR: | ||||
|             case JANET_SYSOP_SHL: | ||||
|             case JANET_SYSOP_SHR: | ||||
|             case JANET_SYSOP_EQ: | ||||
|             case JANET_SYSOP_NEQ: | ||||
|             case JANET_SYSOP_LT: | ||||
|             case JANET_SYSOP_LTE: | ||||
|             case JANET_SYSOP_GT: | ||||
|             case JANET_SYSOP_GTE: | ||||
|             case JANET_SYSOP_POINTER_ADD: | ||||
|             case JANET_SYSOP_POINTER_SUBTRACT: | ||||
|                 rega = v2reg_dflt(assignments, instruction.three.dest, max_reg); | ||||
|                 regb = v2reg_dflt(assignments, instruction.three.lhs, max_reg + 1); | ||||
|                 regc = v2reg_dflt(assignments, instruction.three.rhs, max_reg + 2); | ||||
|                 spill.regs[0] = rega; | ||||
|                 spill.regs[1] = regb; | ||||
|                 spill.regs[2] = regc; | ||||
|                 if (rega >= max_reg) { | ||||
|                     spill.spills[0] = JANET_SYS_SPILL_WRITE; | ||||
|                     spill.stack_offsets[0] = stack_locations[instruction.three.dest]; | ||||
|                     spill.stack_sizes[0] = layouts[instruction.three.dest].size; | ||||
|                 } | ||||
|                 if (regb >= max_reg) { | ||||
|                     spill.spills[1] = JANET_SYS_SPILL_READ; | ||||
|                     spill.stack_offsets[1] = stack_locations[instruction.three.lhs]; | ||||
|                     spill.stack_sizes[1] = layouts[instruction.three.lhs].size; | ||||
|                 } | ||||
|                 if (regc >= max_reg) { | ||||
|                     spill.spills[2] = JANET_SYS_SPILL_READ; | ||||
|                     spill.stack_offsets[2] = stack_locations[instruction.three.rhs]; | ||||
|                     spill.stack_sizes[2] = layouts[instruction.three.rhs].size; | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             /* DEST = op SRC */ | ||||
|             case JANET_SYSOP_MOVE: | ||||
|             case JANET_SYSOP_CAST: | ||||
|             case JANET_SYSOP_BNOT: | ||||
|                 rega = v2reg_dflt(assignments, instruction.two.dest, max_reg); | ||||
|                 regb = v2reg_dflt(assignments, instruction.two.src, max_reg + 1); | ||||
|                 spill.regs[0] = rega; | ||||
|                 spill.regs[1] = regb; | ||||
|                 if (rega >= max_reg) { | ||||
|                     spill.spills[0] = JANET_SYS_SPILL_WRITE; | ||||
|                     spill.stack_offsets[0] = stack_locations[instruction.two.dest]; | ||||
|                     spill.stack_sizes[0] = layouts[instruction.two.dest].size; | ||||
|                 } | ||||
|                 if (regb >= max_reg) { | ||||
|                     spill.spills[1] = JANET_SYS_SPILL_READ; | ||||
|                     spill.stack_offsets[1] = stack_locations[instruction.two.src]; | ||||
|                     spill.stack_sizes[1] = layouts[instruction.two.src].size; | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             /* branch COND */ | ||||
|             case JANET_SYSOP_BRANCH: | ||||
|             case JANET_SYSOP_BRANCH_NOT: | ||||
|                 rega = v2reg_dflt(assignments, instruction.branch.cond, max_reg); | ||||
|                 spill.regs[0] = rega; | ||||
|                 if (rega >= max_reg) { | ||||
|                     spill.spills[0] = JANET_SYS_SPILL_READ; | ||||
|                     spill.stack_offsets[0] = stack_locations[instruction.branch.cond]; | ||||
|                     spill.stack_sizes[0] = layouts[instruction.branch.cond].size; | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             case JANET_SYSOP_CONSTANT: | ||||
|                 rega = v2reg_dflt(assignments, instruction.constant.dest, max_reg); | ||||
|                 spill.regs[0] = rega; | ||||
|                 if (rega >= max_reg) { | ||||
|                     spill.spills[0] = JANET_SYS_SPILL_WRITE; | ||||
|                     spill.stack_offsets[0] = stack_locations[instruction.constant.dest]; | ||||
|                     spill.stack_sizes[0] = layouts[instruction.constant.dest].size; | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             case JANET_SYSOP_RETURN: | ||||
|                 rega = v2reg_dflt(assignments, instruction.one.src, max_reg); | ||||
|                 spill.regs[0] = rega; | ||||
|                 if (rega >= max_reg) { | ||||
|                     spill.spills[0] = JANET_SYS_SPILL_READ; | ||||
|                     spill.stack_offsets[0] = stack_locations[instruction.one.src]; | ||||
|                     spill.stack_sizes[0] = layouts[instruction.one.src].size; | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             /* Should we handle here or per call? */ | ||||
|             case JANET_SYSOP_ARG: | ||||
|                 for (int j = 0; j < 3; j++) { | ||||
|                     uint32_t var = instruction.arg.args[j]; | ||||
|                     rega = v2reg_dflt(assignments, var, 0); | ||||
|                     spill.regs[j] = rega; | ||||
|                     if (rega >= max_reg) { /* Unused elements must be 0 */ | ||||
|                         spill.spills[j] = JANET_SYS_SPILL_READ; | ||||
|                         spill.stack_offsets[j] = stack_locations[instruction.arg.args[j]]; | ||||
|                         spill.stack_sizes[j] = layouts[instruction.arg.args[j]].size; | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|  | ||||
|             /* Variable arg */ | ||||
|             case JANET_SYSOP_CALL: | ||||
|             case JANET_SYSOP_CALLK: | ||||
|                 /* TODO */ | ||||
|                 break; | ||||
|     ctx->regs = janet_smalloc(ctx->ir->register_count * sizeof(x64Reg)); | ||||
|     for (uint32_t i = 0; i < ctx->ir->register_count; i++) { | ||||
|         if (i < 13) { /* skip r15 so we have some temporary registers if needed */ | ||||
|             /* Assign to register */ | ||||
|             uint32_t to = i; | ||||
|             if (to > 5) { | ||||
|                 to += 2; /* skip rsp and rbp */ | ||||
|             } | ||||
|             ctx->regs[i].kind = JANET_SYSREG_64; | ||||
|             ctx->regs[i].index = to; | ||||
|         } else { // TODO - also assign stack location if src of address IR instruction | ||||
|             /* Assign to stack location */ | ||||
|             ctx->regs[i].kind = JANET_SYSREG_STACK; | ||||
|             JanetSysTypeLayout layout = ctx->ir_layouts[i]; | ||||
|             next_loc = (next_loc + layout.alignment - 1) / layout.alignment * layout.alignment; | ||||
|             ctx->regs[i].index = next_loc; | ||||
|             next_loc += layout.size; | ||||
|         } | ||||
|         janet_v_push(spills, spill); | ||||
|     } | ||||
|  | ||||
|     janet_sfree(layouts); | ||||
|     janet_sfree(stack_locations); | ||||
|     next_loc = (next_loc + 15) / 16 * 16; | ||||
|     ctx->frame_size = next_loc + 16; | ||||
|  | ||||
|     return spills; | ||||
|     /* Mark which registers need restoration before returning */ | ||||
|     ctx->restore_count = 0; | ||||
|     unsigned char seen[16] = {0}; | ||||
|     unsigned char tokeep[] = {3, 6, 7, 12, 13, 14, 15}; | ||||
|     for (uint32_t i = 0; i < ctx->ir->register_count; i++) { | ||||
|         x64Reg reg = ctx->regs[i]; | ||||
|         if (reg.kind == JANET_SYSREG_STACK) continue; | ||||
|         for (int j = 0; j < sizeof(tokeep); j++) { | ||||
|             if (!seen[j] && reg.index == tokeep[j]) { | ||||
|                 ctx->to_restore[ctx->restore_count++] = reg.index; | ||||
|                 seen[j] = 1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
|     uint32_t temps[3]; | ||||
| } JanetTempRegs; | ||||
|  | ||||
| static JanetTempRegs do_spills_read(JanetBuffer *buffer, JanetSysSpill *spills, uint32_t index) { | ||||
|     JanetSysSpill spill = spills[index]; | ||||
|     JanetTempRegs temps; | ||||
|     for (int spi = 0; spi < 3; spi++) { | ||||
|         uint32_t reg = spill.regs[spi]; | ||||
|         temps.temps[spi] = reg; | ||||
|         if (spill.spills[spi] == JANET_SYS_SPILL_READ || spill.spills[spi] == JANET_SYS_SPILL_BOTH) { | ||||
|             // emit load | ||||
|             uint32_t x = spill.stack_offsets[spi]; | ||||
|             uint32_t s = spill.stack_sizes[spi]; | ||||
|             janet_formatb(buffer, "load%u r%u from stack[%u] ; SPILL\n", s, reg, x); | ||||
|         } | ||||
|     } | ||||
|     return temps; | ||||
| static int operand_isstack(JanetSysx64Context *ctx, uint32_t o) { | ||||
|     if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */ | ||||
|     x64Reg reg = ctx->regs[o]; | ||||
|     return reg.kind == JANET_SYSREG_STACK; | ||||
| } | ||||
|  | ||||
| static void do_spills_write(JanetBuffer *buffer, JanetSysSpill *spills, uint32_t index) { | ||||
|     JanetSysSpill spill = spills[index]; | ||||
|     for (int spi = 0; spi < 3; spi++) { | ||||
|         if (spill.spills[spi] == JANET_SYS_SPILL_WRITE || spill.spills[spi] == JANET_SYS_SPILL_BOTH) { | ||||
|             // emit store | ||||
|             uint32_t reg = spill.regs[spi]; | ||||
|             uint32_t x = spill.stack_offsets[spi]; | ||||
|             uint32_t s = spill.stack_sizes[spi]; | ||||
|             janet_formatb(buffer, "store%u r%u to stack[%u] ; SPILL\n", s, reg, x); | ||||
| static int operand_isreg(JanetSysx64Context *ctx, uint32_t o, uint32_t regindex) { | ||||
|     if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */ | ||||
|     x64Reg reg = ctx->regs[o]; | ||||
|     if (reg.kind == JANET_SYSREG_STACK) return 0; | ||||
|     return reg.index == regindex; | ||||
| } | ||||
|  | ||||
| static void sysemit_operand(JanetSysx64Context *ctx, uint32_t o, const char *after) { | ||||
|     if (o <= JANET_SYS_MAX_OPERAND) { | ||||
|         /* Virtual register */ | ||||
|         x64Reg reg = ctx->regs[o]; | ||||
|         if (reg.kind == JANET_SYSREG_STACK) { | ||||
|             janet_formatb(ctx->buffer, "[rbp - %u]", reg.index); | ||||
|         } else if (reg.kind == JANET_SYSREG_64) { | ||||
|             janet_formatb(ctx->buffer, "%s", register_names[reg.index]); | ||||
|         } | ||||
|     } else { | ||||
|         /* Constant */ | ||||
|         uint32_t index = o - JANET_SYS_CONSTANT_PREFIX; | ||||
|         Janet c = ctx->ir->constants[index]; | ||||
|         if (janet_checktype(c, JANET_STRING)) { | ||||
|             janet_formatb(ctx->buffer, "[rel CONST%u]", index); | ||||
|         } else { | ||||
|             janet_formatb(ctx->buffer, "%V", c); | ||||
|         } | ||||
|     } | ||||
|     janet_buffer_push_cstring(ctx->buffer, after); | ||||
| } | ||||
|  | ||||
| /* A = A op B */ | ||||
| static void sysemit_binop(JanetSysx64Context *ctx, const char *op, uint32_t dest, uint32_t src) { | ||||
|     if (operand_isstack(ctx, dest) && operand_isstack(ctx, src)) { | ||||
|         /* Use a temporary register for src */ | ||||
|         janet_formatb(ctx->buffer, "mov r15, "); | ||||
|         sysemit_operand(ctx, src, "\n"); | ||||
|         janet_formatb(ctx->buffer, "%s ", op); | ||||
|         sysemit_operand(ctx, dest, ", r15\n"); | ||||
|     } else { | ||||
|         janet_formatb(ctx->buffer, "%s ", op); | ||||
|         sysemit_operand(ctx, dest, ", "); | ||||
|         sysemit_operand(ctx, src, "\n"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void sysemit_mov(JanetSysx64Context *ctx, uint32_t dest, uint32_t src) { | ||||
|     if (dest == src) return; | ||||
|     sysemit_binop(ctx, "mov", dest, src); | ||||
| } | ||||
|  | ||||
| /* Move a value to a register, and save the contents of the old register on the stack */ | ||||
| static void sysemit_mov_save(JanetSysx64Context *ctx, uint32_t dest_reg, uint32_t src) { | ||||
|     janet_formatb(ctx->buffer, "push %s\n", register_names[dest_reg]); | ||||
|     if (!operand_isreg(ctx, src, dest_reg)) { | ||||
|         janet_formatb(ctx->buffer, "mov %s, ", register_names[dest_reg]); | ||||
|         sysemit_operand(ctx, src, "\n"); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void sysemit_mov_restore(JanetSysx64Context *ctx, uint32_t dest_reg) { | ||||
|     janet_formatb(ctx->buffer, "pop %s\n", register_names[dest_reg]); | ||||
| } | ||||
|  | ||||
| static void sysemit_threeop(JanetSysx64Context *ctx, const char *op, uint32_t dest, uint32_t lhs, uint32_t rhs) { | ||||
|     sysemit_mov(ctx, dest, lhs); | ||||
|     sysemit_binop(ctx, op, dest, rhs); | ||||
| } | ||||
|  | ||||
| static void sysemit_ret(JanetSysx64Context *ctx, uint32_t arg, int has_return) { | ||||
|     if (has_return && !operand_isreg(ctx, arg, RAX)) { | ||||
|         janet_formatb(ctx->buffer, "mov rax, "); | ||||
|         sysemit_operand(ctx, arg, "\n"); | ||||
|     } | ||||
|     janet_formatb(ctx->buffer, "add rsp, %u\n", ctx->frame_size); | ||||
|     for (uint32_t k = 0; k < ctx->restore_count; k++) { | ||||
|         /* Pop in reverse order */ | ||||
|         janet_formatb(ctx->buffer, "pop %s\n", register_names[ctx->to_restore[ctx->restore_count - k - 1]]); | ||||
|     } | ||||
|     janet_formatb(ctx->buffer, "leave\n"); | ||||
|     janet_formatb(ctx->buffer, "ret\n"); | ||||
| } | ||||
|  | ||||
| void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { | ||||
|     /* For now, emit assembly for nasm. Eventually an assembler for use with Janet would be good. */ | ||||
|  | ||||
|     JanetSysTypeLayout *all_layouts = janet_smalloc(linkage->type_def_count * sizeof(JanetSysTypeLayout)); | ||||
|     /* Partially setup context */ | ||||
|     JanetSysx64Context ctx; | ||||
|     ctx.linkage = linkage; | ||||
|     ctx.buffer = buffer; | ||||
|     ctx.layouts = janet_smalloc(linkage->type_def_count * sizeof(JanetSysTypeLayout)); | ||||
|     for (uint32_t i = 0; i < linkage->type_def_count; i++) { | ||||
|         all_layouts[i] = get_x64layout(linkage->type_defs[i]); | ||||
|         ctx.layouts[i] = get_x64layout(linkage->type_defs[i]); | ||||
|     } | ||||
|  | ||||
|     /* Emit prelude */ | ||||
| @@ -294,21 +275,20 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) | ||||
|     /* Do register allocation  */ | ||||
|     for (int32_t i = 0; i < linkage->ir_ordered->count; i++) { | ||||
|         JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]); | ||||
|         JanetTable *assignments = janet_table(0); | ||||
|         JanetTempRegs temps; | ||||
|         /* Get type layouts */ | ||||
|         JanetSysTypeLayout *layouts = janet_smalloc(ir->register_count * sizeof(JanetSysTypeLayout)); | ||||
|         for (uint32_t i = 0; i < ir->register_count; i++) { | ||||
|             layouts[i] = all_layouts[ir->types[i]]; | ||||
|         } | ||||
|         JanetSysSpill *spills = assign_registers(ir, layouts, assignments, 13); | ||||
|  | ||||
|         /* Allow combining compare + branch instructions more easily */ | ||||
|         int skip_branch = 0; | ||||
|         /* Setup conttext */ | ||||
|         ctx.ir_layouts = janet_smalloc(ir->register_count * sizeof(JanetSysTypeLayout)); | ||||
|         for (uint32_t i = 0; i < ir->register_count; i++) { | ||||
|             ctx.ir_layouts[i] = ctx.layouts[ir->types[i]]; | ||||
|         } | ||||
|         ctx.ir = ir; | ||||
|         assign_registers(&ctx); | ||||
|  | ||||
|         /* Emit constant strings */ | ||||
|         for (int32_t j = 0; j < ir->constant_count; j++) { | ||||
|             janet_formatb(buffer, ".CONST%u:\n  .string %p\n", j, ir->constants[j]); | ||||
|         for (uint32_t j = 0; j < ir->constant_count; j++) { | ||||
|             if (janet_checktype(ir->constants[j], JANET_STRING)) { | ||||
|                 janet_formatb(buffer, "\nCONST%u: db %p\n", j, ir->constants[j]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Emit prelude */ | ||||
| @@ -317,6 +297,14 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) | ||||
|         } else { | ||||
|             janet_formatb(buffer, "\n_section_%d:\n", i); | ||||
|         } | ||||
|         janet_formatb(buffer, "push rbp\nmov rbp, rsp\nsub rsp, %u\n", ctx.frame_size); | ||||
|  | ||||
|         for (uint32_t k = 0; k < ctx.restore_count; k++) { | ||||
|             /* Pop in reverse order */ | ||||
|             janet_formatb(buffer, "push %s\n", register_names[ctx.to_restore[k]]); | ||||
|         } | ||||
|  | ||||
|         /* Function body */ | ||||
|         for (uint32_t j = 0; j < ir->instruction_count; j++) { | ||||
|             JanetSysInstruction instruction = ir->instructions[j]; | ||||
|             switch (instruction.opcode) { | ||||
| @@ -333,36 +321,57 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) | ||||
|                     /* Non synthesized instructions */ | ||||
|                     break; | ||||
|                 case JANET_SYSOP_POINTER_ADD: | ||||
|                 case JANET_SYSOP_POINTER_SUBTRACT: | ||||
|                 case JANET_SYSOP_ADD: | ||||
|                     sysemit_threeop(&ctx, "add", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_POINTER_SUBTRACT: | ||||
|                 case JANET_SYSOP_SUBTRACT: | ||||
|                     sysemit_threeop(&ctx, "sub", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_MULTIPLY: | ||||
|                     sysemit_threeop(&ctx, "mul", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_DIVIDE: | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     janet_formatb(buffer, "r%u = %s r%u, r%u\n", | ||||
|                             temps.temps[0], | ||||
|                             janet_sysop_names[instruction.opcode], | ||||
|                             temps.temps[1], | ||||
|                             temps.temps[2]); | ||||
|                     do_spills_write(buffer, spills, j); | ||||
|                     sysemit_threeop(&ctx, "div", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BAND: | ||||
|                     sysemit_threeop(&ctx, "and", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BOR: | ||||
|                     sysemit_threeop(&ctx, "or", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BXOR: | ||||
|                     sysemit_threeop(&ctx, "xor", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_SHL: | ||||
|                     sysemit_threeop(&ctx, "shl", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_SHR: | ||||
|                     sysemit_threeop(&ctx, "shr", instruction.three.dest, | ||||
|                                     instruction.three.lhs, | ||||
|                                     instruction.three.rhs); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_MOVE: | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     //janet_formatb(buffer, "r%u = r%u\n", temps.temps[0], temps.temps[1]); | ||||
|                     janet_formatb(buffer, "mov %s, %s\n", | ||||
|                             register_names[temps.temps[0]], | ||||
|                             register_names[temps.temps[1]]); | ||||
|                     do_spills_write(buffer, spills, j); | ||||
|                     sysemit_mov(&ctx, instruction.two.dest, instruction.two.src); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_RETURN: | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     //janet_formatb(buffer, "return r%u\n", temps.temps[0]); | ||||
|                     janet_formatb(buffer, "leave\n mov %s, rax\nret\n", register_names[temps.temps[0]]); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CONSTANT: | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     janet_formatb(buffer, "r%u = constant $%v\n", temps.temps[0], ir->constants[instruction.constant.constant]); | ||||
|                     do_spills_write(buffer, spills, j); | ||||
|                     sysemit_ret(&ctx, instruction.ret.value, instruction.ret.has_value); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_LABEL: | ||||
|                     janet_formatb(buffer, "label_%u:\n", instruction.label.id); | ||||
| @@ -373,16 +382,15 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) | ||||
|                 case JANET_SYSOP_LTE: | ||||
|                 case JANET_SYSOP_GT: | ||||
|                 case JANET_SYSOP_GTE: | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     ; | ||||
|                     JanetSysInstruction nexti = ir->instructions[j + 1]; | ||||
|                     /* Combine compare and branch into one instruction */ | ||||
|                     /* TODO - handle when lhs or rhs is 0 */ | ||||
|                     /* TODO - specialize when lhs or rhs is 0 */ | ||||
|                     /* TODO - handle floats */ | ||||
|                     janet_formatb(buffer, "cmp %s, %s\n", register_names[temps.temps[1]], register_names[temps.temps[2]]); | ||||
|                     sysemit_binop(&ctx, "cmp", instruction.three.lhs, instruction.three.rhs); | ||||
|                     if ((nexti.opcode == JANET_SYSOP_BRANCH || | ||||
|                             nexti.opcode == JANET_SYSOP_BRANCH_NOT) | ||||
|                             && nexti.branch.cond == instruction.three.dest) { | ||||
|                         skip_branch = 1; | ||||
|                         int invert = nexti.opcode == JANET_SYSOP_BRANCH_NOT; | ||||
|                         if (instruction.opcode == JANET_SYSOP_EQ) { | ||||
|                             janet_formatb(buffer, "%s label_%u\n", invert ? "jne" : "je", nexti.branch.to); | ||||
| @@ -399,84 +407,79 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) | ||||
|                         } else { | ||||
|                             janet_panic("unreachable"); | ||||
|                         } | ||||
|                         do_spills_write(buffer, spills, j); | ||||
|                         j++; /* Skip next branch instruction */ | ||||
|                         break; | ||||
|                     } | ||||
|                     /* Fallback to set* instructions */ | ||||
|                     if (instruction.opcode == JANET_SYSOP_EQ) { | ||||
|                         janet_formatb(buffer, "sete %s\n", register_names[temps.temps[0]]); | ||||
|                         janet_formatb(buffer, "sete "); | ||||
|                     } else if (instruction.opcode == JANET_SYSOP_NEQ) { | ||||
|                         janet_formatb(buffer, "setne %s\n", register_names[temps.temps[0]]); | ||||
|                         janet_formatb(buffer, "setne "); | ||||
|                     } else if (instruction.opcode == JANET_SYSOP_GT) { | ||||
|                         janet_formatb(buffer, "setg %s\n", register_names[temps.temps[0]]); | ||||
|                         janet_formatb(buffer, "setg "); | ||||
|                     } else if (instruction.opcode == JANET_SYSOP_GTE) { | ||||
|                         janet_formatb(buffer, "setge %s\n", register_names[temps.temps[0]]); | ||||
|                         janet_formatb(buffer, "setge "); | ||||
|                     } else if (instruction.opcode == JANET_SYSOP_LT) { | ||||
|                         janet_formatb(buffer, "setl %s\n", register_names[temps.temps[0]]); | ||||
|                         janet_formatb(buffer, "setl "); | ||||
|                     } else if (instruction.opcode == JANET_SYSOP_LTE) { | ||||
|                         janet_formatb(buffer, "setle %s\n", register_names[temps.temps[0]]); | ||||
|                         janet_formatb(buffer, "setle "); | ||||
|                     } else { | ||||
|                         janet_panic("unreachable"); | ||||
|                     } | ||||
|                     do_spills_write(buffer, spills, j); | ||||
|                     sysemit_operand(&ctx, instruction.three.dest, "\n"); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_BRANCH: | ||||
|                 case JANET_SYSOP_BRANCH_NOT: | ||||
|                     ; | ||||
|                     if (skip_branch) { | ||||
|                         skip_branch = 0; | ||||
|                         break; | ||||
|                     } | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     if (instruction.opcode == JANET_SYSOP_BRANCH) { | ||||
|                         janet_formatb(buffer, "jnz %s label_%u\n", | ||||
|                                 register_names[temps.temps[0]], | ||||
|                                 instruction.branch.to); | ||||
|                     } else { | ||||
|                         janet_formatb(buffer, "jz %s label_%u\n", | ||||
|                                 register_names[temps.temps[0]], | ||||
|                                 instruction.branch.to); | ||||
|                     } | ||||
|                     janet_formatb(buffer, instruction.opcode == JANET_SYSOP_BRANCH ? "jnz " : "jz "); | ||||
|                     sysemit_operand(&ctx, instruction.branch.cond, " "); | ||||
|                     janet_formatb(buffer, "label_%u\n", instruction.branch.to); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_JUMP: | ||||
|                     janet_formatb(buffer, "jmp label_%u\n", | ||||
|                             instruction.jump.to); | ||||
|                     janet_formatb(buffer, "jmp label_%u\n", instruction.jump.to); | ||||
|                     break; | ||||
|                 case JANET_SYSOP_CALLK: | ||||
|                 case JANET_SYSOP_CALL: | ||||
|                     ; | ||||
|                     /* Push first 6 arguments to particular registers */ | ||||
|                     JanetSysInstruction args1 = ir->instructions[j + 1]; | ||||
|                     JanetSysInstruction args2 = ir->instructions[j + 2]; | ||||
|                     if (instruction.call.arg_count >= 1) sysemit_mov_save(&ctx, RDI, args1.arg.args[0]); | ||||
|                     if (instruction.call.arg_count >= 2) sysemit_mov_save(&ctx, RSI, args1.arg.args[1]); | ||||
|                     if (instruction.call.arg_count >= 3) sysemit_mov_save(&ctx, RDX, args1.arg.args[2]); | ||||
|                     if (instruction.call.arg_count >= 4) sysemit_mov_save(&ctx, RCX, args2.arg.args[0]); | ||||
|                     if (instruction.call.arg_count >= 5) sysemit_mov_save(&ctx, 8,   args2.arg.args[1]); | ||||
|                     if (instruction.call.arg_count >= 6) sysemit_mov_save(&ctx, 9,   args2.arg.args[2]); | ||||
|                     /* Strange iteration is to iterate the arguments in reverse order */ | ||||
|                     for (int32_t argo = instruction.call.arg_count - 1; argo >= 0; argo -= 3) { | ||||
|                     for (int32_t argo = instruction.call.arg_count - 1; argo >= 5; argo--) { | ||||
|                         int32_t offset = argo / 3; | ||||
|                         int32_t sub_count = 3; | ||||
|                         while (offset * 3 + sub_count >= (int32_t) instruction.call.arg_count) { | ||||
|                             sub_count--; | ||||
|                         } | ||||
|                         temps = do_spills_read(buffer, spills, j + offset); | ||||
|                         for (int x = sub_count; x >= 0; x--) { | ||||
|                             janet_formatb(buffer, "push r%u\n", temps.temps[x]); | ||||
|                         } | ||||
|                         int32_t x = argo % 3; | ||||
|                         janet_formatb(buffer, "push "); | ||||
|                         sysemit_operand(&ctx, ir->instructions[j + offset + 1].arg.args[x], "\n"); | ||||
|                     } | ||||
|                     temps = do_spills_read(buffer, spills, j); | ||||
|                     if (instruction.opcode == JANET_SYSOP_CALLK) { | ||||
|                         if (instruction.callk.has_dest) { | ||||
|                             janet_formatb(buffer, "r%u = call %p\n", temps.temps[0], | ||||
|                                     ir->constants[instruction.callk.constant]); | ||||
|                         } else { | ||||
|                             janet_formatb(buffer, "call %p\n", | ||||
|                                     ir->constants[instruction.callk.constant]); | ||||
|                         } | ||||
|                     } else { | ||||
|                         if (instruction.call.has_dest) { | ||||
|                             janet_formatb(buffer, "r%u = call r%u\n", temps.temps[0], temps.temps[1]); | ||||
|                         } else { | ||||
|                             janet_formatb(buffer, "call r%u\n", temps.temps[0]); | ||||
|                         } | ||||
|                     janet_formatb(buffer, "call "); | ||||
|                     sysemit_operand(&ctx, instruction.call.callee, "\n"); | ||||
|                     if (instruction.call.has_dest) { | ||||
|                         /* Get result from rax */ | ||||
|                         janet_formatb(buffer, "mov "); | ||||
|                         sysemit_operand(&ctx, instruction.call.dest, ", rax\n"); | ||||
|                     } | ||||
|                     do_spills_write(buffer, spills, j); | ||||
|                     if (instruction.call.arg_count >= 6) sysemit_mov_restore(&ctx, 9); | ||||
|                     if (instruction.call.arg_count >= 5) sysemit_mov_restore(&ctx, 8); | ||||
|                     if (instruction.call.arg_count >= 4) sysemit_mov_restore(&ctx, RCX); | ||||
|                     if (instruction.call.arg_count >= 3) sysemit_mov_restore(&ctx, RDX); | ||||
|                     if (instruction.call.arg_count >= 2) sysemit_mov_restore(&ctx, RSI); | ||||
|                     if (instruction.call.arg_count >= 1) sysemit_mov_restore(&ctx, RDI); | ||||
|                     break; | ||||
|                 // On a comparison, if next instruction is branch that reads from dest, combine into a single op. | ||||
|                     // On a comparison, if next instruction is branch that reads from dest, combine into a single op. | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #undef RAX | ||||
| #undef RCX | ||||
| #undef RDX | ||||
| #undef RBX | ||||
| #undef RSI | ||||
| #undef RDI | ||||
| #undef RSP | ||||
| #undef RBP | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose