mirror of
https://github.com/janet-lang/janet
synced 2024-11-18 06:34:48 +00:00
More work on x64 backend, especially branching.
Needs changes to IR to allow encoding immediates in all instructions where possible. This makes the IR denser, means we don't need `constant` and `callk`, and allows certain optimizations like comparing to zero, using `inc` and `dec`, etc which are specializations of more general instructions with constants.
This commit is contained in:
parent
3995fa86e2
commit
af10c1d4b5
@ -367,5 +367,5 @@
|
|||||||
####
|
####
|
||||||
|
|
||||||
(compile1 myprog)
|
(compile1 myprog)
|
||||||
#(dump)
|
(dump)
|
||||||
(dumpx64)
|
(dumpx64)
|
||||||
|
@ -2226,8 +2226,8 @@
|
|||||||
|
|
||||||
(defn thaw
|
(defn thaw
|
||||||
`Thaw an object (make it mutable) and do a deep copy, making
|
`Thaw an object (make it mutable) and do a deep copy, making
|
||||||
child value also mutable. Closures, fibers, and abstract
|
child values also mutable. Closures, fibers, and abstract
|
||||||
types will not be recursively thawed, but all other types will`
|
types will not be recursively thawed, but all other types will.`
|
||||||
[ds]
|
[ds]
|
||||||
(case (type ds)
|
(case (type ds)
|
||||||
:array (walk-ind thaw ds)
|
:array (walk-ind thaw ds)
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* TODO
|
/* TODO
|
||||||
* [ ] named fields (for debugging mostly)
|
* [ ] encode constants directly in 3 address codes - makes codegen easier
|
||||||
* [x] named registers and types
|
* [x] named registers and types
|
||||||
* [x] better type errors (perhaps mostly for compiler debugging - full type system goes on top)
|
* [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
|
||||||
@ -87,7 +87,7 @@ static const JanetPrimName prim_names[] = {
|
|||||||
{"void", JANET_PRIM_VOID},
|
{"void", JANET_PRIM_VOID},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *prim_to_prim_name[] = {
|
const char *prim_to_prim_name[] = {
|
||||||
"u8",
|
"u8",
|
||||||
"s8",
|
"s8",
|
||||||
"u16",
|
"u16",
|
||||||
@ -1757,6 +1757,9 @@ static int sysir_gcmark(void *p, size_t s) {
|
|||||||
if (ir->link_name != NULL) {
|
if (ir->link_name != NULL) {
|
||||||
janet_mark(janet_wrap_string(ir->link_name));
|
janet_mark(janet_wrap_string(ir->link_name));
|
||||||
}
|
}
|
||||||
|
janet_mark(janet_wrap_table(ir->labels));
|
||||||
|
janet_mark(janet_wrap_table(ir->register_name_lookup));
|
||||||
|
janet_mark(janet_wrap_abstract(ir->linkage));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,11 @@ typedef struct {
|
|||||||
uint32_t type;
|
uint32_t type;
|
||||||
} JanetSysTypeField;
|
} JanetSysTypeField;
|
||||||
|
|
||||||
|
/* Allow read arguments to be constants to allow
|
||||||
|
* encoding immediates. This makes codegen easier. */
|
||||||
|
#define JANET_SYS_MAX_OPERAND 0x7FFFFFFFU
|
||||||
|
#define JANET_SYS_CONSTANT_PREFIX 0x80000000U
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
JanetSysOp opcode;
|
JanetSysOp opcode;
|
||||||
union {
|
union {
|
||||||
@ -251,10 +256,19 @@ typedef struct {
|
|||||||
JANET_SYS_SPILL_BOTH
|
JANET_SYS_SPILL_BOTH
|
||||||
} spills[3];
|
} spills[3];
|
||||||
uint32_t regs[3];
|
uint32_t regs[3];
|
||||||
|
uint32_t stack_offsets[3];
|
||||||
|
uint32_t stack_sizes[3];
|
||||||
} JanetSysSpill;
|
} JanetSysSpill;
|
||||||
|
|
||||||
|
/* Delay alignment info for the most part to the lowering phase */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t alignment;
|
||||||
|
} JanetSysTypeLayout;
|
||||||
|
|
||||||
/* Keep track of names for each instruction */
|
/* Keep track of names for each instruction */
|
||||||
extern const char *janet_sysop_names[];
|
extern const char *janet_sysop_names[];
|
||||||
|
extern const char *prim_to_prim_name[];
|
||||||
|
|
||||||
/* Lowering */
|
/* Lowering */
|
||||||
void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into);
|
void janet_sys_ir_lower_to_ir(JanetSysIRLinkage *linkage, JanetArray *into);
|
||||||
|
@ -25,17 +25,64 @@
|
|||||||
#include <janet.h>
|
#include <janet.h>
|
||||||
#include "sysir.h"
|
#include "sysir.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
#include "util.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
static const char *register_names[] = {
|
||||||
* Wrap stuff up in a context struct
|
"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||||
*/
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
|
||||||
|
};
|
||||||
|
|
||||||
static uint32_t v2reg(JanetTable *assignments, uint32_t var) {
|
/* Get the layout for types */
|
||||||
return (uint32_t) janet_unwrap_number(janet_table_get(assignments, janet_wrap_number(var)));
|
JanetSysTypeLayout get_x64layout(JanetSysTypeInfo info) {
|
||||||
|
JanetSysTypeLayout layout;
|
||||||
|
switch (info.prim) {
|
||||||
|
default:
|
||||||
|
layout.size = 1;
|
||||||
|
layout.alignment = 1;
|
||||||
|
break;
|
||||||
|
case JANET_PRIM_S8:
|
||||||
|
case JANET_PRIM_U8:
|
||||||
|
case JANET_PRIM_BOOLEAN:
|
||||||
|
layout.size = 1;
|
||||||
|
layout.alignment = 1;
|
||||||
|
break;
|
||||||
|
case JANET_PRIM_S16:
|
||||||
|
case JANET_PRIM_U16:
|
||||||
|
layout.size = 2;
|
||||||
|
layout.alignment = 2;
|
||||||
|
break;
|
||||||
|
case JANET_PRIM_S32:
|
||||||
|
case JANET_PRIM_U32:
|
||||||
|
layout.size = 4;
|
||||||
|
layout.alignment = 4;
|
||||||
|
break;
|
||||||
|
case JANET_PRIM_U64:
|
||||||
|
case JANET_PRIM_S64:
|
||||||
|
case JANET_PRIM_POINTER:
|
||||||
|
layout.size = 8;
|
||||||
|
layout.alignment = 8;
|
||||||
|
break;
|
||||||
|
case JANET_PRIM_F32:
|
||||||
|
case JANET_PRIM_F64:
|
||||||
|
layout.size = 8;
|
||||||
|
layout.alignment = 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
JanetSysSpill *assign_registers(JanetSysIR *ir, JanetTable *assignments,
|
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) {
|
uint32_t max_reg) {
|
||||||
|
|
||||||
/* simplest register assignment algorithm - first n variables
|
/* simplest register assignment algorithm - first n variables
|
||||||
@ -48,13 +95,19 @@ JanetSysSpill *assign_registers(JanetSysIR *ir, JanetTable *assignments,
|
|||||||
for (uint32_t i = 0; i < ir->register_count; i++) {
|
for (uint32_t i = 0; i < ir->register_count; i++) {
|
||||||
if (i < max_reg) {
|
if (i < max_reg) {
|
||||||
janet_table_put(assignments, janet_wrap_number(i), janet_wrap_number(i));
|
janet_table_put(assignments, janet_wrap_number(i), janet_wrap_number(i));
|
||||||
} else {
|
|
||||||
janet_table_put(assignments, janet_wrap_number(i), janet_wrap_number(max_reg));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - keep track of where we spill to. Simple idea would be to assign each variable
|
/* Assign all slots a stack location */
|
||||||
// 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) */
|
/* Generate spills. Spills occur iff using the temporary register (max_reg) */
|
||||||
JanetSysSpill *spills = NULL;
|
JanetSysSpill *spills = NULL;
|
||||||
@ -89,20 +142,26 @@ JanetSysSpill *assign_registers(JanetSysIR *ir, JanetTable *assignments,
|
|||||||
case JANET_SYSOP_GTE:
|
case JANET_SYSOP_GTE:
|
||||||
case JANET_SYSOP_POINTER_ADD:
|
case JANET_SYSOP_POINTER_ADD:
|
||||||
case JANET_SYSOP_POINTER_SUBTRACT:
|
case JANET_SYSOP_POINTER_SUBTRACT:
|
||||||
rega = v2reg(assignments, instruction.three.dest);
|
rega = v2reg_dflt(assignments, instruction.three.dest, max_reg);
|
||||||
regb = v2reg(assignments, instruction.three.lhs);
|
regb = v2reg_dflt(assignments, instruction.three.lhs, max_reg + 1);
|
||||||
regc = v2reg(assignments, instruction.three.rhs);
|
regc = v2reg_dflt(assignments, instruction.three.rhs, max_reg + 2);
|
||||||
if (rega == max_reg) {
|
spill.regs[0] = rega;
|
||||||
|
spill.regs[1] = regb;
|
||||||
|
spill.regs[2] = regc;
|
||||||
|
if (rega >= max_reg) {
|
||||||
spill.spills[0] = JANET_SYS_SPILL_WRITE;
|
spill.spills[0] = JANET_SYS_SPILL_WRITE;
|
||||||
spill.regs[0] = instruction.three.dest;
|
spill.stack_offsets[0] = stack_locations[instruction.three.dest];
|
||||||
|
spill.stack_sizes[0] = layouts[instruction.three.dest].size;
|
||||||
}
|
}
|
||||||
if (regb == max_reg) {
|
if (regb >= max_reg) {
|
||||||
spill.spills[1] = JANET_SYS_SPILL_READ;
|
spill.spills[1] = JANET_SYS_SPILL_READ;
|
||||||
spill.regs[1] = instruction.three.lhs;
|
spill.stack_offsets[1] = stack_locations[instruction.three.lhs];
|
||||||
|
spill.stack_sizes[1] = layouts[instruction.three.lhs].size;
|
||||||
}
|
}
|
||||||
if (regc == max_reg) {
|
if (regc >= max_reg) {
|
||||||
spill.spills[2] = JANET_SYS_SPILL_READ;
|
spill.spills[2] = JANET_SYS_SPILL_READ;
|
||||||
spill.regs[2] = instruction.three.rhs;
|
spill.stack_offsets[2] = stack_locations[instruction.three.rhs];
|
||||||
|
spill.stack_sizes[2] = layouts[instruction.three.rhs].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -110,41 +169,51 @@ JanetSysSpill *assign_registers(JanetSysIR *ir, JanetTable *assignments,
|
|||||||
case JANET_SYSOP_MOVE:
|
case JANET_SYSOP_MOVE:
|
||||||
case JANET_SYSOP_CAST:
|
case JANET_SYSOP_CAST:
|
||||||
case JANET_SYSOP_BNOT:
|
case JANET_SYSOP_BNOT:
|
||||||
rega = v2reg(assignments, instruction.two.dest);
|
rega = v2reg_dflt(assignments, instruction.two.dest, max_reg);
|
||||||
regb = v2reg(assignments, instruction.two.src);
|
regb = v2reg_dflt(assignments, instruction.two.src, max_reg + 1);
|
||||||
if (rega == max_reg) {
|
spill.regs[0] = rega;
|
||||||
|
spill.regs[1] = regb;
|
||||||
|
if (rega >= max_reg) {
|
||||||
spill.spills[0] = JANET_SYS_SPILL_WRITE;
|
spill.spills[0] = JANET_SYS_SPILL_WRITE;
|
||||||
spill.regs[0] = instruction.two.dest;
|
spill.stack_offsets[0] = stack_locations[instruction.two.dest];
|
||||||
|
spill.stack_sizes[0] = layouts[instruction.two.dest].size;
|
||||||
}
|
}
|
||||||
if (regb == max_reg) {
|
if (regb >= max_reg) {
|
||||||
spill.spills[1] = JANET_SYS_SPILL_READ;
|
spill.spills[1] = JANET_SYS_SPILL_READ;
|
||||||
spill.regs[1] = instruction.two.src;
|
spill.stack_offsets[1] = stack_locations[instruction.two.src];
|
||||||
|
spill.stack_sizes[1] = layouts[instruction.two.src].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* branch COND */
|
/* branch COND */
|
||||||
case JANET_SYSOP_BRANCH:
|
case JANET_SYSOP_BRANCH:
|
||||||
case JANET_SYSOP_BRANCH_NOT:
|
case JANET_SYSOP_BRANCH_NOT:
|
||||||
rega = v2reg(assignments, instruction.branch.cond);
|
rega = v2reg_dflt(assignments, instruction.branch.cond, max_reg);
|
||||||
if (rega == max_reg) {
|
spill.regs[0] = rega;
|
||||||
|
if (rega >= max_reg) {
|
||||||
spill.spills[0] = JANET_SYS_SPILL_READ;
|
spill.spills[0] = JANET_SYS_SPILL_READ;
|
||||||
spill.regs[0] = instruction.branch.cond;
|
spill.stack_offsets[0] = stack_locations[instruction.branch.cond];
|
||||||
|
spill.stack_sizes[0] = layouts[instruction.branch.cond].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JANET_SYSOP_CONSTANT:
|
case JANET_SYSOP_CONSTANT:
|
||||||
rega = v2reg(assignments, instruction.constant.dest);
|
rega = v2reg_dflt(assignments, instruction.constant.dest, max_reg);
|
||||||
if (rega == max_reg) {
|
spill.regs[0] = rega;
|
||||||
|
if (rega >= max_reg) {
|
||||||
spill.spills[0] = JANET_SYS_SPILL_WRITE;
|
spill.spills[0] = JANET_SYS_SPILL_WRITE;
|
||||||
spill.regs[0] = instruction.constant.dest;
|
spill.stack_offsets[0] = stack_locations[instruction.constant.dest];
|
||||||
|
spill.stack_sizes[0] = layouts[instruction.constant.dest].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JANET_SYSOP_RETURN:
|
case JANET_SYSOP_RETURN:
|
||||||
rega = v2reg(assignments, instruction.one.src);
|
rega = v2reg_dflt(assignments, instruction.one.src, max_reg);
|
||||||
if (rega == max_reg) {
|
spill.regs[0] = rega;
|
||||||
|
if (rega >= max_reg) {
|
||||||
spill.spills[0] = JANET_SYS_SPILL_READ;
|
spill.spills[0] = JANET_SYS_SPILL_READ;
|
||||||
spill.regs[0] = instruction.one.src;
|
spill.stack_offsets[0] = stack_locations[instruction.one.src];
|
||||||
|
spill.stack_sizes[0] = layouts[instruction.one.src].size;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -152,10 +221,12 @@ JanetSysSpill *assign_registers(JanetSysIR *ir, JanetTable *assignments,
|
|||||||
case JANET_SYSOP_ARG:
|
case JANET_SYSOP_ARG:
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
uint32_t var = instruction.arg.args[j];
|
uint32_t var = instruction.arg.args[j];
|
||||||
rega = v2reg(assignments, var);
|
rega = v2reg_dflt(assignments, var, 0);
|
||||||
if (rega == max_reg) {
|
spill.regs[j] = rega;
|
||||||
|
if (rega >= max_reg) { /* Unused elements must be 0 */
|
||||||
spill.spills[j] = JANET_SYS_SPILL_READ;
|
spill.spills[j] = JANET_SYS_SPILL_READ;
|
||||||
spill.regs[j] = var;
|
spill.stack_offsets[j] = stack_locations[instruction.arg.args[j]];
|
||||||
|
spill.stack_sizes[j] = layouts[instruction.arg.args[j]].size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -163,46 +234,88 @@ JanetSysSpill *assign_registers(JanetSysIR *ir, JanetTable *assignments,
|
|||||||
/* Variable arg */
|
/* Variable arg */
|
||||||
case JANET_SYSOP_CALL:
|
case JANET_SYSOP_CALL:
|
||||||
case JANET_SYSOP_CALLK:
|
case JANET_SYSOP_CALLK:
|
||||||
|
/* TODO */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
janet_v_push(spills, spill);
|
janet_v_push(spills, spill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
janet_sfree(layouts);
|
||||||
|
janet_sfree(stack_locations);
|
||||||
|
|
||||||
return spills;
|
return spills;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_spills(JanetBuffer *buffer, JanetSysSpill *spills, uint32_t index) {
|
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 void do_spills_write(JanetBuffer *buffer, JanetSysSpill *spills, uint32_t index) {
|
||||||
JanetSysSpill spill = spills[index];
|
JanetSysSpill spill = spills[index];
|
||||||
for (int spi = 0; spi < 3; spi++) {
|
for (int spi = 0; spi < 3; spi++) {
|
||||||
if (spill.spills[spi] == JANET_SYS_SPILL_WRITE || spill.spills[spi] == JANET_SYS_SPILL_BOTH) {
|
if (spill.spills[spi] == JANET_SYS_SPILL_WRITE || spill.spills[spi] == JANET_SYS_SPILL_BOTH) {
|
||||||
// emit store
|
// emit store
|
||||||
uint32_t reg = spill.regs[spi];
|
uint32_t reg = spill.regs[spi];
|
||||||
void *x = (void *) 0x123456;
|
uint32_t x = spill.stack_offsets[spi];
|
||||||
janet_formatb(buffer, "store r%u to %v ; SPILL\n", reg, janet_wrap_pointer(x));
|
uint32_t s = spill.stack_sizes[spi];
|
||||||
}
|
janet_formatb(buffer, "store%u r%u to stack[%u] ; SPILL\n", s, reg, x);
|
||||||
if (spill.spills[spi] == JANET_SYS_SPILL_READ || spill.spills[spi] == JANET_SYS_SPILL_BOTH) {
|
|
||||||
// emit load
|
|
||||||
uint32_t reg = spill.regs[spi];
|
|
||||||
void *x = (void *) 0x123456;
|
|
||||||
janet_formatb(buffer, "load r%u from %v ; SPILL\n", reg, janet_wrap_pointer(x));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer) {
|
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));
|
||||||
|
for (uint32_t i = 0; i < linkage->type_def_count; i++) {
|
||||||
|
all_layouts[i] = get_x64layout(linkage->type_defs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Emit prelude */
|
||||||
|
janet_formatb(buffer, "bits 64\ndefault rel\n\n");
|
||||||
|
janet_formatb(buffer, "segment .text\n");
|
||||||
|
|
||||||
/* Do register allocation */
|
/* Do register allocation */
|
||||||
for (int32_t i = 0; i < linkage->ir_ordered->count; i++) {
|
for (int32_t i = 0; i < linkage->ir_ordered->count; i++) {
|
||||||
JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]);
|
JanetSysIR *ir = janet_unwrap_pointer(linkage->ir_ordered->data[i]);
|
||||||
JanetTable *assignments = janet_table(0);
|
JanetTable *assignments = janet_table(0);
|
||||||
/* 16 total 64 bit registers - 3 temp */
|
JanetTempRegs temps;
|
||||||
JanetSysSpill *spills = assign_registers(ir, assignments, 113);
|
/* 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;
|
||||||
|
|
||||||
|
/* 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]);
|
||||||
|
}
|
||||||
|
|
||||||
/* Emit prelude */
|
/* Emit prelude */
|
||||||
if (ir->link_name != NULL) {
|
if (ir->link_name != NULL) {
|
||||||
janet_formatb(buffer, ".%s\n", ir->link_name);
|
janet_formatb(buffer, "\n%s:\n", ir->link_name);
|
||||||
} else {
|
} else {
|
||||||
janet_formatb(buffer, "._section_%d\n", i);
|
janet_formatb(buffer, "\n_section_%d:\n", i);
|
||||||
}
|
}
|
||||||
for (uint32_t j = 0; j < ir->instruction_count; j++) {
|
for (uint32_t j = 0; j < ir->instruction_count; j++) {
|
||||||
JanetSysInstruction instruction = ir->instructions[j];
|
JanetSysInstruction instruction = ir->instructions[j];
|
||||||
@ -225,34 +338,34 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
|
|||||||
case JANET_SYSOP_SUBTRACT:
|
case JANET_SYSOP_SUBTRACT:
|
||||||
case JANET_SYSOP_MULTIPLY:
|
case JANET_SYSOP_MULTIPLY:
|
||||||
case JANET_SYSOP_DIVIDE:
|
case JANET_SYSOP_DIVIDE:
|
||||||
do_spills(buffer, spills, j);
|
temps = do_spills_read(buffer, spills, j);
|
||||||
janet_formatb(buffer, "r%u = %s r%u, r%u\n",
|
janet_formatb(buffer, "r%u = %s r%u, r%u\n",
|
||||||
v2reg(assignments, instruction.three.dest),
|
temps.temps[0],
|
||||||
janet_sysop_names[instruction.opcode],
|
janet_sysop_names[instruction.opcode],
|
||||||
v2reg(assignments, instruction.three.lhs),
|
temps.temps[1],
|
||||||
v2reg(assignments, instruction.three.rhs));
|
temps.temps[2]);
|
||||||
|
do_spills_write(buffer, spills, j);
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_MOVE:
|
case JANET_SYSOP_MOVE:
|
||||||
do_spills(buffer, spills, j);
|
temps = do_spills_read(buffer, spills, j);
|
||||||
janet_formatb(buffer, "r%u = r%u\n",
|
//janet_formatb(buffer, "r%u = r%u\n", temps.temps[0], temps.temps[1]);
|
||||||
v2reg(assignments, instruction.two.dest),
|
janet_formatb(buffer, "mov %s, %s\n",
|
||||||
v2reg(assignments, instruction.two.src));
|
register_names[temps.temps[0]],
|
||||||
|
register_names[temps.temps[1]]);
|
||||||
|
do_spills_write(buffer, spills, j);
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_RETURN:
|
case JANET_SYSOP_RETURN:
|
||||||
do_spills(buffer, spills, j);
|
temps = do_spills_read(buffer, spills, j);
|
||||||
janet_formatb(buffer, "return r%u\n",
|
//janet_formatb(buffer, "return r%u\n", temps.temps[0]);
|
||||||
v2reg(assignments, instruction.one.src));
|
janet_formatb(buffer, "leave\n mov %s, rax\nret\n", register_names[temps.temps[0]]);
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_CONSTANT:
|
case JANET_SYSOP_CONSTANT:
|
||||||
do_spills(buffer, spills, j);
|
temps = do_spills_read(buffer, spills, j);
|
||||||
janet_formatb(buffer, "r%u = constant $%v\n",
|
janet_formatb(buffer, "r%u = constant $%v\n", temps.temps[0], ir->constants[instruction.constant.constant]);
|
||||||
v2reg(assignments, instruction.constant.dest),
|
do_spills_write(buffer, spills, j);
|
||||||
ir->constants[instruction.constant.constant]);
|
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_LABEL:
|
case JANET_SYSOP_LABEL:
|
||||||
do_spills(buffer, spills, j);
|
janet_formatb(buffer, "label_%u:\n", instruction.label.id);
|
||||||
janet_formatb(buffer, "label_%u:\n",
|
|
||||||
v2reg(assignments, instruction.label.id));
|
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_EQ:
|
case JANET_SYSOP_EQ:
|
||||||
case JANET_SYSOP_NEQ:
|
case JANET_SYSOP_NEQ:
|
||||||
@ -260,19 +373,74 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
|
|||||||
case JANET_SYSOP_LTE:
|
case JANET_SYSOP_LTE:
|
||||||
case JANET_SYSOP_GT:
|
case JANET_SYSOP_GT:
|
||||||
case JANET_SYSOP_GTE:
|
case JANET_SYSOP_GTE:
|
||||||
do_spills(buffer, spills, j);
|
temps = do_spills_read(buffer, spills, j);
|
||||||
janet_formatb(buffer, "r%u = %s r%u, r%u\n",
|
JanetSysInstruction nexti = ir->instructions[j + 1];
|
||||||
v2reg(assignments, instruction.three.dest),
|
/* Combine compare and branch into one instruction */
|
||||||
janet_sysop_names[instruction.opcode],
|
/* TODO - handle when lhs or rhs is 0 */
|
||||||
v2reg(assignments, instruction.three.lhs),
|
/* TODO - handle floats */
|
||||||
v2reg(assignments, instruction.three.rhs));
|
janet_formatb(buffer, "cmp %s, %s\n", register_names[temps.temps[1]], register_names[temps.temps[2]]);
|
||||||
|
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);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
do_spills_write(buffer, spills, j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Fallback to set* instructions */
|
||||||
|
if (instruction.opcode == JANET_SYSOP_EQ) {
|
||||||
|
janet_formatb(buffer, "sete %s\n", register_names[temps.temps[0]]);
|
||||||
|
} else if (instruction.opcode == JANET_SYSOP_NEQ) {
|
||||||
|
janet_formatb(buffer, "setne %s\n", register_names[temps.temps[0]]);
|
||||||
|
} else if (instruction.opcode == JANET_SYSOP_GT) {
|
||||||
|
janet_formatb(buffer, "setg %s\n", register_names[temps.temps[0]]);
|
||||||
|
} else if (instruction.opcode == JANET_SYSOP_GTE) {
|
||||||
|
janet_formatb(buffer, "setge %s\n", register_names[temps.temps[0]]);
|
||||||
|
} else if (instruction.opcode == JANET_SYSOP_LT) {
|
||||||
|
janet_formatb(buffer, "setl %s\n", register_names[temps.temps[0]]);
|
||||||
|
} else if (instruction.opcode == JANET_SYSOP_LTE) {
|
||||||
|
janet_formatb(buffer, "setle %s\n", register_names[temps.temps[0]]);
|
||||||
|
} else {
|
||||||
|
janet_panic("unreachable");
|
||||||
|
}
|
||||||
|
do_spills_write(buffer, spills, j);
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_BRANCH:
|
case JANET_SYSOP_BRANCH:
|
||||||
case JANET_SYSOP_BRANCH_NOT:
|
case JANET_SYSOP_BRANCH_NOT:
|
||||||
do_spills(buffer, spills, j);
|
;
|
||||||
janet_formatb(buffer, "branch label_%u if r%u\n",
|
if (skip_branch) {
|
||||||
instruction.branch.to,
|
skip_branch = 0;
|
||||||
v2reg(assignments, instruction.branch.cond));
|
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);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JANET_SYSOP_JUMP:
|
||||||
|
janet_formatb(buffer, "jmp label_%u\n",
|
||||||
|
instruction.jump.to);
|
||||||
break;
|
break;
|
||||||
case JANET_SYSOP_CALLK:
|
case JANET_SYSOP_CALLK:
|
||||||
case JANET_SYSOP_CALL:
|
case JANET_SYSOP_CALL:
|
||||||
@ -284,15 +452,15 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
|
|||||||
while (offset * 3 + sub_count >= (int32_t) instruction.call.arg_count) {
|
while (offset * 3 + sub_count >= (int32_t) instruction.call.arg_count) {
|
||||||
sub_count--;
|
sub_count--;
|
||||||
}
|
}
|
||||||
JanetSysInstruction arg_instruction = ir->instructions[j + offset];
|
temps = do_spills_read(buffer, spills, j + offset);
|
||||||
do_spills(buffer, spills, j + offset);
|
|
||||||
for (int x = sub_count; x >= 0; x--) {
|
for (int x = sub_count; x >= 0; x--) {
|
||||||
janet_formatb(buffer, "push r%u\n", v2reg(assignments, arg_instruction.arg.args[x]));
|
janet_formatb(buffer, "push r%u\n", temps.temps[x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
temps = do_spills_read(buffer, spills, j);
|
||||||
if (instruction.opcode == JANET_SYSOP_CALLK) {
|
if (instruction.opcode == JANET_SYSOP_CALLK) {
|
||||||
if (instruction.callk.has_dest) {
|
if (instruction.callk.has_dest) {
|
||||||
janet_formatb(buffer, "r%u = call %p\n", v2reg(assignments, instruction.callk.dest),
|
janet_formatb(buffer, "r%u = call %p\n", temps.temps[0],
|
||||||
ir->constants[instruction.callk.constant]);
|
ir->constants[instruction.callk.constant]);
|
||||||
} else {
|
} else {
|
||||||
janet_formatb(buffer, "call %p\n",
|
janet_formatb(buffer, "call %p\n",
|
||||||
@ -300,13 +468,12 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (instruction.call.has_dest) {
|
if (instruction.call.has_dest) {
|
||||||
janet_formatb(buffer, "r%u = call r%u\n", v2reg(assignments, instruction.call.dest),
|
janet_formatb(buffer, "r%u = call r%u\n", temps.temps[0], temps.temps[1]);
|
||||||
v2reg(assignments, instruction.call.callee));
|
|
||||||
} else {
|
} else {
|
||||||
janet_formatb(buffer, "call r%u\n",
|
janet_formatb(buffer, "call r%u\n", temps.temps[0]);
|
||||||
v2reg(assignments, instruction.call.callee));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
do_spills_write(buffer, spills, j);
|
||||||
break;
|
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.
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user