From f0395763b757713403167261d229cc20664f2558 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Mon, 10 Jun 2024 20:14:41 -0500 Subject: [PATCH] More work on x86 target. Also remove all (limited) type inference from the sysir. Type inference is better done in frontend, limited inference in backend just covers compilers issues. Simple hello world with nasm working. --- examples/sysir/frontend.janet | 30 +++-- src/core/sysir.c | 197 ++++++++++----------------- src/core/sysir.h | 8 ++ src/core/sysir_x86.c | 245 ++++++++++++++++++---------------- 4 files changed, 230 insertions(+), 250 deletions(-) diff --git a/examples/sysir/frontend.janet b/examples/sysir/frontend.janet index ea94b7c0..f30d32db 100644 --- a/examples/sysir/frontend.janet +++ b/examples/sysir/frontend.janet @@ -215,6 +215,20 @@ (array/push into lab-end) ret) + # Insert IR + 'ir + (do (array/push into ;args) nil) + + # Syscall + 'syscall + (do + (def slots @[]) + (def ret (if no-return nil (get-slot))) + (each arg args + (array/push slots (visit1 arg into))) + (array/push into ~(syscall :default ,ret ,;slots)) + ret) + # Assume function call (do (def slots @[]) @@ -332,9 +346,10 @@ (return x))) (def main-fn - '(defn main:void [] - (doloop 10 20) - (printf "done!\n") + '(defn _start:void [] + (syscall 1 1 "Hello, world!\n" 14) + (syscall 60 0) + #(write 1 "Hello, world!\n" 14) (return))) (def ctx (sysir/context)) @@ -358,10 +373,9 @@ #### -(compile1 myprog) -(compile1 doloop) +#(compile1 myprog) +#(compile1 doloop) (compile1 main-fn) -(print "compiled!") -(dump) -(dumpc) +#(dump) +#(dumpc) (dumpx64) diff --git a/src/core/sysir.c b/src/core/sysir.c index 2a4fade1..ac91dcce 100644 --- a/src/core/sysir.c +++ b/src/core/sysir.c @@ -31,7 +31,7 @@ * [ ] 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 + * [-] x86/x64 machine code target - in progress * [ ] LLVM target * [ ] target specific extensions - custom instructions and custom primitives * [ ] better casting semantics @@ -55,7 +55,6 @@ * [ ] a few built-in array combinators (maybe?) * [ ] multiple error messages in one pass * [ ] better verification of constants - * [ ] forward type inference * [x] don't allow redefining types */ @@ -131,6 +130,7 @@ const char *janet_sysop_names[] = { "gte", /* JANET_SYSOP_GTE */ "lte", /* JANET_SYSOP_LTE */ "call", /* JANET_SYSOP_CALL */ + "syscall", /* JANET_SYSOP_SYSCALL */ "return", /* JANET_SYSOP_RETURN */ "jump", /* JANET_SYSOP_JUMP */ "branch", /* JANET_SYSOP_BRANCH */ @@ -219,6 +219,7 @@ static const JanetSysInstrName sys_op_names[] = { {"shr", JANET_SYSOP_SHR}, {"store", JANET_SYSOP_STORE}, {"subtract", JANET_SYSOP_SUBTRACT}, + {"syscall", JANET_SYSOP_SYSCALL}, {"type-array", JANET_SYSOP_TYPE_ARRAY}, {"type-pointer", JANET_SYSOP_TYPE_POINTER}, {"type-prim", JANET_SYSOP_TYPE_PRIMITIVE}, @@ -481,7 +482,8 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction janet_v_push(ir, instruction); break; case JANET_SYSOP_CALL: - instr_assert_min_length(tuple, 3, opvalue); + case JANET_SYSOP_SYSCALL: + instr_assert_min_length(tuple, 4, opvalue); instruction.call.calling_convention = instr_read_cc(tuple[1]); if (janet_checktype(tuple[2], JANET_NIL)) { instruction.call.dest = 0; @@ -492,16 +494,15 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction } 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 = 4; j < janet_tuple_length(tuple); j += 3) { JanetSysInstruction arginstr; arginstr.opcode = JANET_SYSOP_ARG; arginstr.line = line; arginstr.column = column; - arginstr.arg.args[0] = 0; - arginstr.arg.args[1] = 0; - arginstr.arg.args[2] = 0; + arginstr.arg.args[0] = 0xcafe; + arginstr.arg.args[1] = 0xcafe; + arginstr.arg.args[2] = 0xcafe; int32_t remaining = janet_tuple_length(tuple) - j; if (remaining > 3) remaining = 3; for (int32_t k = 0; k < remaining; k++) { @@ -664,6 +665,29 @@ static void janet_sysir_init_instructions(JanetSysIR *out, JanetView instruction out->constants = janet_v_flatten(out->constants); } +uint32_t *janet_sys_callargs(JanetSysInstruction *instr, uint32_t *count) { + uint32_t arg_count = 0; + if (instr->opcode == JANET_SYSOP_CALL) { + arg_count = instr->call.arg_count; + } else if (instr->opcode == JANET_SYSOP_SYSCALL) { + arg_count = instr->call.arg_count; + } else if (instr->opcode == JANET_SYSOP_TYPE_UNION) { + arg_count = instr->type_types.arg_count; + } else if (instr->opcode == JANET_SYSOP_TYPE_STRUCT) { + arg_count = instr->type_types.arg_count; + } else { + janet_assert(0, "bad callargs"); + } + uint32_t *args = janet_smalloc(sizeof(uint32_t) * arg_count); + for (uint32_t i = 0; i < arg_count; i++) { + uint32_t instr_index = 1 + i / 3; + uint32_t sub_index = i % 3; + args[i] = instr[instr_index].arg.args[sub_index]; + } + if (count != NULL) *count = arg_count; + return args; +} + /* Get a printable representation of a type on type failure */ static Janet tname(JanetSysIR *ir, uint32_t typeid) { JanetSysIRLinkage *linkage = ir->linkage; @@ -718,15 +742,14 @@ static void janet_sysir_init_types(JanetSysIR *ir) { : JANET_PRIM_UNION; type_defs[type_def].st.field_count = instruction.type_types.arg_count; type_defs[type_def].st.field_start = field_offset + (uint32_t) janet_v_count(fields); - for (uint32_t j = 0; j < instruction.type_types.arg_count; j++) { - uint32_t offset = j / 3 + 1; - uint32_t index = j % 3; - JanetSysInstruction arg_instruction = ir->instructions[i + offset]; - uint32_t arg = arg_instruction.arg.args[index]; + uint32_t count = 0; + uint32_t *args = janet_sys_callargs(ir->instructions + i, &count); + for (uint32_t j = 0; j < count; j++) { JanetSysTypeField field; - field.type = arg; + field.type = args[j]; janet_v_push(fields, field); } + janet_sfree(args); break; } case JANET_SYSOP_TYPE_POINTER: { @@ -842,6 +865,12 @@ static void tcheck_integer(JanetSysIR *sysir, uint32_t t) { } } +static void rcheck_integer(JanetSysIR *sysir, uint32_t reg) { + if (reg <= JANET_SYS_MAX_OPERAND) { + tcheck_integer(sysir, sysir->types[reg]); + } +} + static void tcheck_pointer(JanetSysIR *sysir, uint32_t t) { JanetSysIRLinkage *linkage = sysir->linkage; if (linkage->type_defs[t].prim != JANET_PRIM_POINTER) { @@ -962,9 +991,7 @@ static void rcheck_array_getp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, ui static void rcheck_array_pgetp(JanetSysIR *sysir, uint32_t dest, uint32_t lhs, uint32_t rhs) { tcheck_pointer(sysir, sysir->types[lhs]); - if (rhs <= JANET_SYS_MAX_OPERAND) { - tcheck_integer(sysir, sysir->types[rhs]); - } + rcheck_integer(sysir, rhs); tcheck_pointer(sysir, sysir->types[dest]); JanetSysIRLinkage *linkage = sysir->linkage; uint32_t aptype = linkage->type_defs[sysir->types[lhs]].pointer.type; @@ -1000,9 +1027,7 @@ static void rcheck_fgetp(JanetSysIR *sysir, uint32_t dest, uint32_t st, uint32_t /* Unlike C, only allow pointer on lhs for addition and subtraction */ 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]); - } + rcheck_integer(sysir, rhs); } static JanetString rname(JanetSysIR *sysir, uint32_t regid) { @@ -1026,95 +1051,6 @@ static int reg_is_unknown_type(JanetSysIR *sysir, uint32_t reg) { static void janet_sysir_type_check(JanetSysIR *sysir) { - /* Simple forward type inference */ - for (uint32_t i = 0; i < sysir->instruction_count; i++) { - JanetSysInstruction instruction = sysir->instructions[i]; - switch (instruction.opcode) { - default: - break; - case JANET_SYSOP_MOVE: - 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: - rcheck_cast(sysir, instruction.two.dest, instruction.two.src); - break; - case JANET_SYSOP_POINTER_ADD: - case JANET_SYSOP_POINTER_SUBTRACT: - 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])); - 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])); - 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.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.dest])); - rcheck_equal(sysir, instruction.three.lhs, instruction.three.rhs); - rcheck_equal(sysir, instruction.three.dest, instruction.three.lhs); - break; - case JANET_SYSOP_LOAD: - rcheck_pointer_equals(sysir, instruction.two.src, instruction.two.dest); - break; - case JANET_SYSOP_STORE: - rcheck_pointer_equals(sysir, instruction.two.dest, instruction.two.src); - break; - case JANET_SYSOP_GT: - case JANET_SYSOP_LT: - case JANET_SYSOP_EQ: - case JANET_SYSOP_NEQ: - case JANET_SYSOP_GTE: - case JANET_SYSOP_LTE: - 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: - rcheck_pointer(sysir, instruction.two.dest); - break; - case JANET_SYSOP_BRANCH: - case JANET_SYSOP_BRANCH_NOT: - rcheck_boolean(sysir, instruction.branch.cond); - break; - case JANET_SYSOP_CALL: - rcheck_pointer(sysir, instruction.call.callee); - break; - case JANET_SYSOP_ARRAY_GETP: - rcheck_array_getp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); - break; - case JANET_SYSOP_ARRAY_PGETP: - rcheck_array_pgetp(sysir, instruction.three.dest, instruction.three.lhs, instruction.three.lhs); - break; - case JANET_SYSOP_FIELD_GETP: - rcheck_fgetp(sysir, instruction.field.r, instruction.field.st, instruction.field.field); - break; - } - /* Write back possibly modified instruction */ - sysir->instructions[i] = instruction; - } - /* Assert no unknown types */ JanetSysIRLinkage *linkage = sysir->linkage; for (uint32_t i = 0; i < sysir->register_count; i++) { @@ -1225,6 +1161,9 @@ static void janet_sysir_type_check(JanetSysIR *sysir) { case JANET_SYSOP_BRANCH_NOT: rcheck_boolean(sysir, instruction.branch.cond); break; + case JANET_SYSOP_SYSCALL: + rcheck_integer(sysir, instruction.call.callee); + break; case JANET_SYSOP_CALL: rcheck_pointer(sysir, instruction.call.callee); break; @@ -1375,7 +1314,7 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { #define EMITBINOP(OP) emit_binop(ir, buffer, tempbuf, instruction, OP) /* Prelude */ - janet_formatb(buffer, "#include \n#include \n#include \n#include \n\n"); + janet_formatb(buffer, "#include \n#include \n#include \n#include \n#include \n\n"); /* Emit type defs */ for (uint32_t j = 0; j < (uint32_t) linkage->ir_ordered->count; j++) { @@ -1530,20 +1469,25 @@ void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { case JANET_SYSOP_SHR: EMITBINOP(">>"); break; + case JANET_SYSOP_SYSCALL: case JANET_SYSOP_CALL: { if (instruction.call.has_dest) { janet_formatb(buffer, " _r%u = ", instruction.call.dest); } else { 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]; - if (j) janet_formatb(buffer, ", "); - op_or_const(ir, buffer, arg_instruction.arg.args[index]); + if (instruction.opcode == JANET_SYSOP_SYSCALL) { + janet_formatb(buffer, "syscall("); + op_or_const(ir, buffer, instruction.call.callee); + } else { + op_or_const(ir, buffer, instruction.call.callee); + janet_formatb(buffer, "("); + } + uint32_t count; + uint32_t *args = janet_sys_callargs(ir->instructions + i, &count); + for (uint32_t j = 0; j < count; j++) { + if (j || instruction.opcode == JANET_SYSOP_SYSCALL) janet_formatb(buffer, ", "); + op_or_const(ir, buffer, args[j]); } janet_formatb(buffer, ");\n"); break; @@ -1676,12 +1620,10 @@ void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into) { build_tuple[1] = janet_wrap_string(ir->link_name); janet_array_push(linkage_ir, janet_wrap_tuple(janet_tuple_end(build_tuple))); } - if (ir->parameter_count > 0) { - Janet *build_tuple = janet_tuple_begin(2); - build_tuple[0] = janet_csymbolv("parameter-count"); - build_tuple[1] = janet_wrap_number(ir->parameter_count); - janet_array_push(linkage_ir, janet_wrap_tuple(janet_tuple_end(build_tuple))); - } + Janet *build_tuple = janet_tuple_begin(2); + build_tuple[0] = janet_csymbolv("parameter-count"); + build_tuple[1] = janet_wrap_number(ir->parameter_count); + janet_array_push(linkage_ir, janet_wrap_tuple(janet_tuple_end(build_tuple))); /* Lift type bindings to top of IR */ for (uint32_t i = 0; i < ir->instruction_count; i++) { @@ -1779,16 +1721,17 @@ void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into) { build_tuple[3] = janet_wrap_number(instruction.field.field); break; case JANET_SYSOP_CALL: + case JANET_SYSOP_SYSCALL: 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]); + uint32_t *args = janet_sys_callargs(ir->instructions + i, NULL); + for (uint32_t k = 0; k < instruction.call.arg_count; k++) { + build_tuple[4 + k] = wrap_op(ir, args[k]); } + janet_sfree(args); break; } if (instruction.line > 0) { diff --git a/src/core/sysir.h b/src/core/sysir.h index c9a4beb5..793a3a43 100644 --- a/src/core/sysir.h +++ b/src/core/sysir.h @@ -83,6 +83,7 @@ typedef enum { JANET_SYSOP_GTE, JANET_SYSOP_LTE, JANET_SYSOP_CALL, + JANET_SYSOP_SYSCALL, JANET_SYSOP_RETURN, JANET_SYSOP_JUMP, JANET_SYSOP_BRANCH, @@ -260,6 +261,13 @@ typedef struct { extern const char *janet_sysop_names[]; extern const char *prim_to_prim_name[]; +/* Utilities */ + +/* Get list of uint32_t instruction arguments from a call or other variable length instruction. + Needs to be free with janet_sfree (or you can leak it and the garbage collector will eventually clean + * it up). */ +uint32_t *janet_sys_callargs(JanetSysInstruction *instr, uint32_t *count); + /* Lowering */ void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into); void janet_sys_ir_lower_to_c(JanetSysIRLinkage *linkage, JanetBuffer *buffer); diff --git a/src/core/sysir_x86.c b/src/core/sysir_x86.c index b0131565..34dd0c47 100644 --- a/src/core/sysir_x86.c +++ b/src/core/sysir_x86.c @@ -135,8 +135,8 @@ void assign_registers(JanetSysx64Context *ctx) { 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) { + uint32_t to = i + 1; /* skip rax */ + if (i > 5) { to += 2; /* skip rsp and rbp */ } ctx->regs[i].kind = JANET_SYSREG_64; @@ -161,7 +161,7 @@ void assign_registers(JanetSysx64Context *ctx) { 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++) { + for (unsigned 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; @@ -197,7 +197,7 @@ static void sysemit_operand(JanetSysx64Context *ctx, uint32_t o, const char *aft 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); + janet_formatb(ctx->buffer, "CONST%u", index); } else { janet_formatb(ctx->buffer, "%V", c); } @@ -225,16 +225,23 @@ static void sysemit_mov(JanetSysx64Context *ctx, uint32_t dest, uint32_t src) { 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_movreg(JanetSysx64Context *ctx, uint32_t regdest, uint32_t src) { + if (operand_isreg(ctx, src, regdest)) return; + janet_formatb(ctx->buffer, "mov %s, ", register_names[regdest]); + sysemit_operand(ctx, src, "\n"); } -static void sysemit_mov_restore(JanetSysx64Context *ctx, uint32_t dest_reg) { +static void sysemit_pushreg(JanetSysx64Context *ctx, uint32_t dest_reg) { + janet_formatb(ctx->buffer, "push %s\n", register_names[dest_reg]); +} + +/* 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) { + sysemit_pushreg(ctx, dest_reg); + sysemit_movreg(ctx, dest_reg, src); +} + +static void sysemit_popreg(JanetSysx64Context *ctx, uint32_t dest_reg) { janet_formatb(ctx->buffer, "pop %s\n", register_names[dest_reg]); } @@ -243,11 +250,12 @@ static void sysemit_threeop(JanetSysx64Context *ctx, const char *op, uint32_t de sysemit_binop(ctx, op, dest, rhs); } +static void sysemit_three_inst(JanetSysx64Context *ctx, const char *op, JanetSysInstruction instruction) { + sysemit_threeop(ctx, op, instruction.three.dest, instruction.three.lhs, instruction.three.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"); - } + if (has_return) sysemit_movreg(ctx, RAX, arg); 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 */ @@ -257,6 +265,31 @@ static void sysemit_ret(JanetSysx64Context *ctx, uint32_t arg, int has_return) { janet_formatb(ctx->buffer, "ret\n"); } +static int sysemit_comp(JanetSysx64Context *ctx, uint32_t index, + const char *branch, const char *branch_invert, + const char *set) { + JanetSysInstruction instruction = ctx->ir->instructions[index]; + sysemit_binop(ctx, "cmp", instruction.three.lhs, instruction.three.rhs); + int has_next = index < ctx->ir->instruction_count - 1; + JanetSysInstruction nexti; + if (has_next) nexti = ctx->ir->instructions[index + 1]; + if (has_next && + (nexti.opcode == JANET_SYSOP_BRANCH || + nexti.opcode == JANET_SYSOP_BRANCH_NOT) && + nexti.branch.cond == instruction.three.dest) { + /* Combine compare and branch */ + int invert = nexti.opcode == JANET_SYSOP_BRANCH_NOT; + janet_formatb(ctx->buffer, "%s label_%u\n", invert ? branch_invert : branch, nexti.branch.to); + /* Skip next branch IR instruction */ + return 1; + } else { + /* Set register instead */ + janet_formatb(ctx->buffer, "%s ", set); + sysemit_operand(ctx, instruction.three.dest, "\n"); + return 0; + } +} + void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) { /* Partially setup context */ @@ -270,11 +303,20 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) /* Emit prelude */ janet_formatb(buffer, "bits 64\ndefault rel\n\n"); - janet_formatb(buffer, "segment .text\n"); + for (int32_t i = 0; i < linkage->ir_ordered->count; i++) { + JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]); + if (ir->link_name != NULL) { + janet_formatb(buffer, "global %s\n", ir->link_name); + } + } + janet_formatb(buffer, "section .text\n"); /* Do register allocation */ for (int32_t i = 0; i < linkage->ir_ordered->count; i++) { JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]); + if (ir->link_name == NULL) { + continue; + } /* Setup conttext */ ctx.ir_layouts = janet_smalloc(ir->register_count * sizeof(JanetSysTypeLayout)); @@ -287,7 +329,32 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) /* Emit constant strings */ 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]); + JanetString str = janet_unwrap_string(ir->constants[j]); + janet_formatb(buffer, "\nCONST%u: db ", j); + /* Nasm syntax */ + int in_string = 0; + for (int32_t ci = 0; ci < janet_string_length(str); ci++) { + int c = str[ci]; + if (c < 32) { + if (in_string) { + janet_formatb(buffer, "\", %d", c); + } else { + janet_formatb(buffer, ci ? ", %d" : "%d", c); + } + in_string = 0; + } else { + if (!in_string) { + janet_buffer_push_cstring(buffer, ci ? ", \"" : "\""); + } + janet_buffer_push_u8(buffer, c); + in_string = 1; + } + } + if (!in_string) { + janet_buffer_push_cstring(buffer, "\n"); + } else { + janet_buffer_push_cstring(buffer, "\"\n"); + } } } @@ -322,50 +389,32 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) break; case JANET_SYSOP_POINTER_ADD: case JANET_SYSOP_ADD: - sysemit_threeop(&ctx, "add", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "add", instruction); break; case JANET_SYSOP_POINTER_SUBTRACT: case JANET_SYSOP_SUBTRACT: - sysemit_threeop(&ctx, "sub", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "sub", instruction); break; case JANET_SYSOP_MULTIPLY: - sysemit_threeop(&ctx, "mul", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "mul", instruction); break; case JANET_SYSOP_DIVIDE: - sysemit_threeop(&ctx, "div", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "div", instruction); break; case JANET_SYSOP_BAND: - sysemit_threeop(&ctx, "and", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "and", instruction); break; case JANET_SYSOP_BOR: - sysemit_threeop(&ctx, "or", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "or", instruction); break; case JANET_SYSOP_BXOR: - sysemit_threeop(&ctx, "xor", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "xor", instruction); break; case JANET_SYSOP_SHL: - sysemit_threeop(&ctx, "shl", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "shl", instruction); break; case JANET_SYSOP_SHR: - sysemit_threeop(&ctx, "shr", instruction.three.dest, - instruction.three.lhs, - instruction.three.rhs); + sysemit_three_inst(&ctx, "shr", instruction); break; case JANET_SYSOP_MOVE: sysemit_mov(&ctx, instruction.two.dest, instruction.two.src); @@ -377,56 +426,22 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) janet_formatb(buffer, "label_%u:\n", instruction.label.id); break; case JANET_SYSOP_EQ: + j += sysemit_comp(&ctx, j, "je", "jne", "sete"); + break; case JANET_SYSOP_NEQ: + j += sysemit_comp(&ctx, j, "jne", "je", "setne"); + break; case JANET_SYSOP_LT: + j += sysemit_comp(&ctx, j, "jl", "jge", "setl"); + break; case JANET_SYSOP_LTE: + j += sysemit_comp(&ctx, j, "jle", "jg", "setle"); + break; case JANET_SYSOP_GT: + j += sysemit_comp(&ctx, j, "jg", "jle", "setg"); + break; case JANET_SYSOP_GTE: - ; - JanetSysInstruction nexti = ir->instructions[j + 1]; - /* Combine compare and branch into one instruction */ - /* TODO - specialize when lhs or rhs is 0 */ - /* TODO - handle floats */ - 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) { - 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); - } else if (instruction.opcode == JANET_SYSOP_NEQ) { - janet_formatb(buffer, "%s label_%u\n", invert ? "je" : "jne", nexti.branch.to); - } else if (instruction.opcode == JANET_SYSOP_GT) { - janet_formatb(buffer, "%s label_%u\n", invert ? "jle" : "jg", nexti.branch.to); - } else if (instruction.opcode == JANET_SYSOP_GTE) { - janet_formatb(buffer, "%s label_%u\n", invert ? "jl" : "jge", nexti.branch.to); - } else if (instruction.opcode == JANET_SYSOP_LT) { - janet_formatb(buffer, "%s label_%u\n", invert ? "jge" : "jl", nexti.branch.to); - } else if (instruction.opcode == JANET_SYSOP_LTE) { - janet_formatb(buffer, "%s label_%u\n", invert ? "jg" : "jle", nexti.branch.to); - } else { - janet_panic("unreachable"); - } - j++; /* Skip next branch instruction */ - break; - } - /* Fallback to set* instructions */ - if (instruction.opcode == JANET_SYSOP_EQ) { - janet_formatb(buffer, "sete "); - } else if (instruction.opcode == JANET_SYSOP_NEQ) { - janet_formatb(buffer, "setne "); - } else if (instruction.opcode == JANET_SYSOP_GT) { - janet_formatb(buffer, "setg "); - } else if (instruction.opcode == JANET_SYSOP_GTE) { - janet_formatb(buffer, "setge "); - } else if (instruction.opcode == JANET_SYSOP_LT) { - janet_formatb(buffer, "setl "); - } else if (instruction.opcode == JANET_SYSOP_LTE) { - janet_formatb(buffer, "setle "); - } else { - janet_panic("unreachable"); - } - sysemit_operand(&ctx, instruction.three.dest, "\n"); + j += sysemit_comp(&ctx, j, "jge", "jl", "setge"); break; case JANET_SYSOP_BRANCH: case JANET_SYSOP_BRANCH_NOT: @@ -437,37 +452,37 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) case JANET_SYSOP_JUMP: janet_formatb(buffer, "jmp label_%u\n", instruction.jump.to); break; + case JANET_SYSOP_SYSCALL: 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 >= 5; argo--) { - int32_t offset = argo / 3; - int32_t x = argo % 3; + uint32_t argcount = 0; + uint32_t *args = janet_sys_callargs(ir->instructions + j, &argcount); + if (argcount >= 1) sysemit_mov_save(&ctx, RDI, args[0]); + if (argcount >= 2) sysemit_mov_save(&ctx, RSI, args[1]); + if (argcount >= 3) sysemit_mov_save(&ctx, RDX, args[2]); + if (argcount >= 4) sysemit_mov_save(&ctx, RCX, args[3]); + if (argcount >= 5) sysemit_mov_save(&ctx, 8, args[4]); + if (argcount >= 6) sysemit_mov_save(&ctx, 9, args[5]); + for (int32_t argo = argcount - 1; argo >= 5; argo--) { janet_formatb(buffer, "push "); - sysemit_operand(&ctx, ir->instructions[j + offset + 1].arg.args[x], "\n"); + sysemit_operand(&ctx, args[argo], "\n"); } - 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"); + janet_sfree(args); + if (instruction.opcode == JANET_SYSOP_SYSCALL) { + sysemit_movreg(&ctx, RAX, instruction.call.callee); + janet_formatb(buffer, "syscall\n"); + } else { + janet_formatb(buffer, "call "); + sysemit_operand(&ctx, instruction.call.callee, "\n"); } - 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); + if (instruction.call.has_dest) sysemit_movreg(&ctx, instruction.call.dest, RAX); + if (argcount >= 6) sysemit_popreg(&ctx, 9); + if (argcount >= 5) sysemit_popreg(&ctx, 8); + if (argcount >= 4) sysemit_popreg(&ctx, RCX); + if (argcount >= 3) sysemit_popreg(&ctx, RDX); + if (argcount >= 2) sysemit_popreg(&ctx, RSI); + if (argcount >= 1) sysemit_popreg(&ctx, RDI); break; // On a comparison, if next instruction is branch that reads from dest, combine into a single op. }