1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-16 05:34:48 +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
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
./temp.bin
valgrind ./temp.bin

View File

@ -1,9 +1,13 @@
(use ./frontend)
(def square
'(defn square:int [num:int]
(return (* num num))))
(def simple
'(defn simple [x:int]
'(defn simple:int [x:int]
(def xyz:int (+ 1 2 3))
(return (* x 2 x))))
(return (the int (* x 2 x)))))
(def myprog
'(defn myprog:int []
@ -16,6 +20,7 @@
(printf "i = %d\n" i))
(printf "hello, world!\n%d\n" (the int (if x abc xyz)))
(return (* abc xyz))))
#(return (the int (simple (* abc xyz))))))
(def doloop
'(defn doloop [x:int y:int]
@ -35,6 +40,8 @@
####
#(compile1 square)
(compile1 simple)
(compile1 myprog)
(compile1 doloop)
(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.
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
the modified target environment.``
[target source &opt prefix export]
(loop [[k v] :pairs source :when (symbol? k) :when (not (v :private))]
the modified target environment. If an array `only` is passed, only merge keys in `only`.``
[target source &opt prefix export only]
(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))
(put target (symbol prefix k) newv))
target)
@ -3065,13 +3066,14 @@
(def kargs (table ;args))
(def {:as as
:prefix prefix
:export ep} kargs)
:export ep
:only only} kargs)
(def newenv (require-1 path args kargs))
(def prefix (or
(and as (string as "/"))
prefix
(string (last (string/split "/" path)) "/")))
(merge-module env newenv prefix ep))
(merge-module env newenv prefix ep only))
(defmacro import
``Import a module. First requires the module, and then merges its
@ -3084,7 +3086,7 @@
module cache.``
[path & 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))
(defmacro use

View File

@ -63,7 +63,6 @@ static const char *register_names_xmm[] = {
};
typedef enum {
JANET_SYSREG_STACK, // TODO - one of these is not like the others
JANET_SYSREG_8,
JANET_SYSREG_16,
JANET_SYSREG_32,
@ -72,8 +71,15 @@ typedef enum {
JANET_SYSREG_XMM
} 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 {
x64RegKind kind;
x64Storage storage;
uint32_t index;
} 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 - 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 */
uint32_t next_loc = 0;
ctx->regs = janet_smalloc(ctx->ir->register_count * sizeof(x64Reg));
@ -179,9 +176,9 @@ void assign_registers(JanetSysx64Context *ctx) {
assigned |= 1 << RBP;
assigned |= 1 << RAX; // return reg, div, etc.
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) {
/* 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 == 1) ctx->regs[i].index = RSI;
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 == 5) ctx->regs[i].index = 9;
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 */
/* Assign to register */
uint32_t to = 0;
while ((1 << to) & assigned) to++;
ctx->regs[i].kind = get_slot_regkind(ctx, i);
ctx->regs[i].index = to;
ctx->regs[i].storage = JANET_SYSREG_REGISTER;
assigned |= 1 << ctx->regs[i].index;
occupied |= 1 << ctx->regs[i].index;
} 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;
ctx->regs[i].storage = JANET_SYSREG_STACK;
next_loc += layout.size;
}
}
@ -221,7 +221,7 @@ void assign_registers(JanetSysx64Context *ctx) {
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;
if (reg.storage != JANET_SYSREG_REGISTER) continue;
for (unsigned int j = 0; j < sizeof(tokeep); j++) {
if (!seen[j] && reg.index == tokeep[j]) {
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) {
if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */
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) {
if (o > JANET_SYS_MAX_OPERAND) return 0; /* constant */
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;
}
static void sysemit_reg(JanetSysx64Context *ctx, x64Reg reg, const char *after) {
if (reg.kind == JANET_SYSREG_STACK) {
janet_formatb(ctx->buffer, "[rbp - %u]", reg.index);
if (reg.storage == JANET_SYSREG_STACK) {
// 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) {
janet_formatb(ctx->buffer, "%s", register_names[reg.index]);
} 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");
}
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) {
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);
}
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) {
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;
} else {
/* Set register instead */
janet_formatb(ctx->buffer, "%s ", set);
/* Set only byte register */
sysemit_operand(ctx, instruction.three.dest, "\n");
x64RegKind kind = get_slot_regkind(ctx, instruction.three.dest);
if (kind != JANET_SYSREG_8) {
/* 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;
}
}
@ -495,7 +528,8 @@ void janet_sys_ir_lower_to_x64(JanetSysIRLinkage *linkage, JanetBuffer *buffer)
sysemit_three_inst(&ctx, "sub", instruction);
break;
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;
case JANET_SYSOP_DIVIDE:
sysemit_three_inst(&ctx, "idiv", instruction);