mirror of
https://github.com/janet-lang/janet
synced 2024-11-16 05:34:48 +00:00
Lots of work on calling conventions and x86 backend.
We need the ability to represent multiple calling conventions in IR. All backends need to support a :default CC, but can also support more for interop with system libraries, code from other compilers, syscalls, etc. Also allow void returns.
This commit is contained in:
parent
af10c1d4b5
commit
5b3c5a5969
@ -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)))
|
||||
|
@ -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)
|
||||
|
531
src/core/sysir.c
531
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),
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user