diff --git a/examples/sysir/basic1.janet b/examples/sysir/basic1.janet index 57cbda5c..73193a80 100644 --- a/examples/sysir/basic1.janet +++ b/examples/sysir/basic1.janet @@ -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))) diff --git a/examples/sysir/frontend.janet b/examples/sysir/frontend.janet index 0b1a3349..ea94b7c0 100644 --- a/examples/sysir/frontend.janet +++ b/examples/sysir/frontend.janet @@ -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) diff --git a/src/core/sysir.c b/src/core/sysir.c index ed50bb4b..2a4fade1 100644 --- a/src/core/sysir.c +++ b/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), diff --git a/src/core/sysir.h b/src/core/sysir.h index 524c9c22..c9a4beb5 100644 --- a/src/core/sysir.h +++ b/src/core/sysir.h @@ -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; diff --git a/src/core/sysir_x86.c b/src/core/sysir_x86.c index b2f46c0a..b0131565 100644 --- a/src/core/sysir_x86.c +++ b/src/core/sysir_x86.c @@ -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