1
0
mirror of https://github.com/janet-lang/janet synced 2025-07-06 12:02:53 +00:00

More work on x64 backend.

This commit is contained in:
Calvin Rose 2024-06-14 16:57:32 -05:00
parent 232a8faa35
commit 314e684097
4 changed files with 77 additions and 34 deletions

View File

@ -2,4 +2,4 @@
build/janet examples/sysir/samples.janet > temp.nasm build/janet examples/sysir/samples.janet > temp.nasm
nasm -felf64 temp.nasm -l temp.lst -o temp.o nasm -felf64 temp.nasm -l temp.lst -o temp.o
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
./temp.bin valgrind ./temp.bin

View File

@ -1,9 +1,13 @@
(use ./frontend) (use ./frontend)
(def square
'(defn square:int [num:int]
(return (* num num))))
(def simple (def simple
'(defn simple [x:int] '(defn simple:int [x:int]
(def xyz:int (+ 1 2 3)) (def xyz:int (+ 1 2 3))
(return (* x 2 x)))) (return (the int (* x 2 x)))))
(def myprog (def myprog
'(defn myprog:int [] '(defn myprog:int []
@ -16,6 +20,7 @@
(printf "i = %d\n" i)) (printf "i = %d\n" i))
(printf "hello, world!\n%d\n" (the int (if x abc xyz))) (printf "hello, world!\n%d\n" (the int (if x abc xyz)))
(return (* abc xyz)))) (return (* abc xyz))))
#(return (the int (simple (* abc xyz))))))
(def doloop (def doloop
'(defn doloop [x:int y:int] '(defn doloop [x:int y:int]
@ -35,6 +40,8 @@
#### ####
#(compile1 square)
(compile1 simple)
(compile1 myprog) (compile1 myprog)
(compile1 doloop) (compile1 doloop)
(compile1 main-fn) (compile1 main-fn)

View File

@ -3050,9 +3050,10 @@
``Merge a module source into the `target` environment with a `prefix`, as with the `import` macro. ``Merge a module source into the `target` environment with a `prefix`, as with the `import` macro.
This lets users emulate the behavior of `import` with a custom module table. This lets users emulate the behavior of `import` with a custom module table.
If `export` is truthy, then merged functions are not marked as private. Returns If `export` is truthy, then merged functions are not marked as private. Returns
the modified target environment.`` the modified target environment. If an array `only` is passed, only merge keys in `only`.``
[target source &opt prefix export] [target source &opt prefix export only]
(loop [[k v] :pairs source :when (symbol? k) :when (not (v :private))] (def only-set (if only (invert only)))
(loop [[k v] :pairs source :when (symbol? k) :when (not (v :private)) :when (or (not only) (in only-set k))]
(def newv (table/setproto @{:private (not export)} v)) (def newv (table/setproto @{:private (not export)} v))
(put target (symbol prefix k) newv)) (put target (symbol prefix k) newv))
target) target)
@ -3065,13 +3066,14 @@
(def kargs (table ;args)) (def kargs (table ;args))
(def {:as as (def {:as as
:prefix prefix :prefix prefix
:export ep} kargs) :export ep
:only only} kargs)
(def newenv (require-1 path args kargs)) (def newenv (require-1 path args kargs))
(def prefix (or (def prefix (or
(and as (string as "/")) (and as (string as "/"))
prefix prefix
(string (last (string/split "/" path)) "/"))) (string (last (string/split "/" path)) "/")))
(merge-module env newenv prefix ep)) (merge-module env newenv prefix ep only))
(defmacro import (defmacro import
``Import a module. First requires the module, and then merges its ``Import a module. First requires the module, and then merges its
@ -3084,7 +3086,7 @@
module cache.`` module cache.``
[path & args] [path & args]
(def ps (partition 2 args)) (def ps (partition 2 args))
(def argm (mapcat (fn [[k v]] [k (if (= k :as) (string v) v)]) ps)) (def argm (mapcat (fn [[k v]] [k (case k :as (string v) :only ~(quote ,v) v)]) ps))
(tuple import* (string path) ;argm)) (tuple import* (string path) ;argm))
(defmacro use (defmacro use

View File

@ -63,7 +63,6 @@ static const char *register_names_xmm[] = {
}; };
typedef enum { typedef enum {
JANET_SYSREG_STACK, // TODO - one of these is not like the others
JANET_SYSREG_8, JANET_SYSREG_8,
JANET_SYSREG_16, JANET_SYSREG_16,
JANET_SYSREG_32, JANET_SYSREG_32,
@ -72,8 +71,15 @@ typedef enum {
JANET_SYSREG_XMM JANET_SYSREG_XMM
} x64RegKind; } x64RegKind;
typedef enum {
JANET_SYSREG_REGISTER,
JANET_SYSREG_STACK, /* Indexed from base pointer */
JANET_SYSREG_STACK_PARAMETER, /* Index from base pointer in other direction */
} x64Storage;
typedef struct { typedef struct {
x64RegKind kind; x64RegKind kind;
x64Storage storage;
uint32_t index; uint32_t index;
} x64Reg; } x64Reg;
@ -161,15 +167,6 @@ void assign_registers(JanetSysx64Context *ctx) {
/* TODO - avoid spills inside loops if possible i.e. not all spills are equal */ /* TODO - avoid spills inside loops if possible i.e. not all spills are equal */
/* TODO - move into sysir.c and allow reuse for multiple targets */ /* TODO - move into sysir.c and allow reuse for multiple targets */
if (ctx->ir->register_count == 0) {
ctx->regs = NULL;
ctx->frame_size = 0;
ctx->occupied_registers = 0;
ctx->restore_count = 0;
return;
}
/* Make trivial assigments */ /* Make trivial assigments */
uint32_t next_loc = 0; uint32_t next_loc = 0;
ctx->regs = janet_smalloc(ctx->ir->register_count * sizeof(x64Reg)); ctx->regs = janet_smalloc(ctx->ir->register_count * sizeof(x64Reg));
@ -179,9 +176,9 @@ void assign_registers(JanetSysx64Context *ctx) {
assigned |= 1 << RBP; assigned |= 1 << RBP;
assigned |= 1 << RAX; // return reg, div, etc. assigned |= 1 << RAX; // return reg, div, etc.
for (uint32_t i = 0; i < ctx->ir->register_count; i++) { for (uint32_t i = 0; i < ctx->ir->register_count; i++) {
ctx->regs[i].kind = get_slot_regkind(ctx, i);
if (i < ctx->ir->parameter_count) { if (i < ctx->ir->parameter_count) {
/* Assign to rdi, rsi, etc. according to ABI */ /* Assign to rdi, rsi, etc. according to ABI */
ctx->regs[i].kind = get_slot_regkind(ctx, i);
if (i == 0) ctx->regs[i].index = RDI; if (i == 0) ctx->regs[i].index = RDI;
if (i == 1) ctx->regs[i].index = RSI; if (i == 1) ctx->regs[i].index = RSI;
if (i == 2) ctx->regs[i].index = RDX; if (i == 2) ctx->regs[i].index = RDX;
@ -189,24 +186,27 @@ void assign_registers(JanetSysx64Context *ctx) {
if (i == 4) ctx->regs[i].index = 8; if (i == 4) ctx->regs[i].index = 8;
if (i == 5) ctx->regs[i].index = 9; if (i == 5) ctx->regs[i].index = 9;
if (i >= 6) { if (i >= 6) {
janet_assert(0, "more than 6 parameters nyi"); janet_panic("nyi");
ctx->regs[i].storage = JANET_SYSREG_STACK_PARAMETER;
} else {
ctx->regs[i].storage = JANET_SYSREG_REGISTER;
assigned |= 1 << ctx->regs[i].index;
occupied |= 1 << ctx->regs[i].index;
} }
assigned |= 1 << ctx->regs[i].index;
occupied |= 1 << ctx->regs[i].index;
} else if (assigned < 0xFFFF) { /* skip r15 so we have some temporary registers if needed */ } else if (assigned < 0xFFFF) { /* skip r15 so we have some temporary registers if needed */
/* Assign to register */ /* Assign to register */
uint32_t to = 0; uint32_t to = 0;
while ((1 << to) & assigned) to++; while ((1 << to) & assigned) to++;
ctx->regs[i].kind = get_slot_regkind(ctx, i);
ctx->regs[i].index = to; ctx->regs[i].index = to;
ctx->regs[i].storage = JANET_SYSREG_REGISTER;
assigned |= 1 << ctx->regs[i].index; assigned |= 1 << ctx->regs[i].index;
occupied |= 1 << ctx->regs[i].index; occupied |= 1 << ctx->regs[i].index;
} else { // TODO - also assign stack location if src of address IR instruction } else { // TODO - also assign stack location if src of address IR instruction
/* Assign to stack location */ /* Assign to stack location */
ctx->regs[i].kind = JANET_SYSREG_STACK;
JanetSysTypeLayout layout = ctx->ir_layouts[i]; JanetSysTypeLayout layout = ctx->ir_layouts[i];
next_loc = (next_loc + layout.alignment - 1) / layout.alignment * layout.alignment; next_loc = (next_loc + layout.alignment - 1) / layout.alignment * layout.alignment;
ctx->regs[i].index = next_loc; ctx->regs[i].index = next_loc;
ctx->regs[i].storage = JANET_SYSREG_STACK;
next_loc += layout.size; next_loc += layout.size;
} }
} }
@ -221,7 +221,7 @@ void assign_registers(JanetSysx64Context *ctx) {
unsigned char tokeep[] = {3, 6, 7, 12, 13, 14, 15}; unsigned char tokeep[] = {3, 6, 7, 12, 13, 14, 15};
for (uint32_t i = 0; i < ctx->ir->register_count; i++) { for (uint32_t i = 0; i < ctx->ir->register_count; i++) {
x64Reg reg = ctx->regs[i]; x64Reg reg = ctx->regs[i];
if (reg.kind == JANET_SYSREG_STACK) continue; if (reg.storage != JANET_SYSREG_REGISTER) continue;
for (unsigned int j = 0; j < sizeof(tokeep); j++) { for (unsigned int j = 0; j < sizeof(tokeep); j++) {
if (!seen[j] && reg.index == tokeep[j]) { if (!seen[j] && reg.index == tokeep[j]) {
ctx->to_restore[ctx->restore_count++] = reg.index; ctx->to_restore[ctx->restore_count++] = reg.index;
@ -234,19 +234,23 @@ void assign_registers(JanetSysx64Context *ctx) {
static int operand_isstack(JanetSysx64Context *ctx, uint32_t o) { static int operand_isstack(JanetSysx64Context *ctx, uint32_t o) {
if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */ if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */
x64Reg reg = ctx->regs[o]; x64Reg reg = ctx->regs[o];
return reg.kind == JANET_SYSREG_STACK; return reg.storage != JANET_SYSREG_REGISTER;
} }
static int operand_isreg(JanetSysx64Context *ctx, uint32_t o, uint32_t regindex) { static int operand_isreg(JanetSysx64Context *ctx, uint32_t o, uint32_t regindex) {
if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */ if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */
x64Reg reg = ctx->regs[o]; x64Reg reg = ctx->regs[o];
if (reg.kind == JANET_SYSREG_STACK) return 0; if (reg.storage != JANET_SYSREG_REGISTER) return 0;
return reg.index == regindex; return reg.index == regindex;
} }
static void sysemit_reg(JanetSysx64Context *ctx, x64Reg reg, const char *after) { static void sysemit_reg(JanetSysx64Context *ctx, x64Reg reg, const char *after) {
if (reg.kind == JANET_SYSREG_STACK) { if (reg.storage == JANET_SYSREG_STACK) {
janet_formatb(ctx->buffer, "[rbp - %u]", reg.index); // TODO - use LEA for parameters larger than a qword
janet_formatb(ctx->buffer, "[rbp-%u]", reg.index);
} else if (reg.storage == JANET_SYSREG_STACK_PARAMETER) {
// TODO - use LEA for parameters larger than a qword
janet_formatb(ctx->buffer, "[rbp+%u]", reg.index);
} else if (reg.kind == JANET_SYSREG_64) { } else if (reg.kind == JANET_SYSREG_64) {
janet_formatb(ctx->buffer, "%s", register_names[reg.index]); janet_formatb(ctx->buffer, "%s", register_names[reg.index]);
} else if (reg.kind == JANET_SYSREG_32) { } else if (reg.kind == JANET_SYSREG_32) {
@ -317,6 +321,16 @@ static void sysemit_movreg(JanetSysx64Context *ctx, uint32_t regdest, uint32_t s
sysemit_operand(ctx, src, "\n"); sysemit_operand(ctx, src, "\n");
} }
static void sysemit_movfromreg(JanetSysx64Context *ctx, uint32_t dest, uint32_t srcreg) {
if (operand_isreg(ctx, dest, srcreg)) return;
x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, dest);
tempreg.index = srcreg;
janet_formatb(ctx->buffer, "mov ");
sysemit_operand(ctx, dest, ", ");
sysemit_reg(ctx, tempreg, "\n");
}
static void sysemit_pushreg(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]); janet_formatb(ctx->buffer, "push %s\n", register_names[dest_reg]);
} }
@ -336,6 +350,22 @@ static void sysemit_threeop(JanetSysx64Context *ctx, const char *op, uint32_t de
sysemit_binop(ctx, op, dest, rhs); sysemit_binop(ctx, op, dest, rhs);
} }
static void sysemit_threeop_nodeststack(JanetSysx64Context *ctx, const char *op, uint32_t dest, uint32_t lhs,
uint32_t rhs) {
if (operand_isstack(ctx, dest)) {
sysemit_movreg(ctx, RAX, lhs);
x64Reg tempreg;
tempreg.kind = get_slot_regkind(ctx, dest);
tempreg.index = RAX;
janet_formatb(ctx->buffer, "%s ", op);
sysemit_reg(ctx, tempreg, ", ");
sysemit_operand(ctx, lhs, "\n");
sysemit_movfromreg(ctx, dest, RAX);
} else {
sysemit_threeop(ctx, op, dest, lhs, rhs);
}
}
static void sysemit_three_inst(JanetSysx64Context *ctx, const char *op, JanetSysInstruction instruction) { 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); sysemit_threeop(ctx, op, instruction.three.dest, instruction.three.lhs, instruction.three.rhs);
} }
@ -379,9 +409,12 @@ static int sysemit_comp(JanetSysx64Context *ctx, uint32_t index,
return 1; return 1;
} else { } else {
/* Set register instead */ /* Set register instead */
janet_formatb(ctx->buffer, "%s ", set); x64RegKind kind = get_slot_regkind(ctx, instruction.three.dest);
/* Set only byte register */ if (kind != JANET_SYSREG_8) {
sysemit_operand(ctx, instruction.three.dest, "\n"); /* Zero the upper bits */
sysemit_binop(ctx, "xor", instruction.three.dest, instruction.three.dest);
}
janet_formatb(ctx->buffer, "%s %s\n", set, register_names_8[instruction.three.dest]);
return 0; return 0;
} }
} }
@ -495,7 +528,8 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
sysemit_three_inst(&ctx, "sub", instruction); sysemit_three_inst(&ctx, "sub", instruction);
break; break;
case JANET_SYSOP_MULTIPLY: case JANET_SYSOP_MULTIPLY:
sysemit_three_inst(&ctx, "imul", instruction); sysemit_threeop_nodeststack(&ctx, "imul", instruction.three.dest,
instruction.three.lhs, instruction.three.rhs);
break; break;
case JANET_SYSOP_DIVIDE: case JANET_SYSOP_DIVIDE:
sysemit_three_inst(&ctx, "idiv", instruction); sysemit_three_inst(&ctx, "idiv", instruction);