1016 lines
31 KiB
C
1016 lines
31 KiB
C
#include "spl_ir2vm.h"
|
|
#include "spl_layout.h"
|
|
|
|
static usize ir_type_size(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
|
if (!tn)
|
|
return 8;
|
|
switch (tn->kind) {
|
|
case SPL_TYPE_UNIT:
|
|
return 0;
|
|
case SPL_TYPE_BOOL:
|
|
return 1;
|
|
case SPL_TYPE_INT:
|
|
return (tn->int_type.bits + 7) / 8;
|
|
case SPL_TYPE_FLOAT:
|
|
return (tn->float_type.bits + 7) / 8;
|
|
case SPL_TYPE_PTR:
|
|
return sizeof(void *);
|
|
case SPL_TYPE_FN:
|
|
return sizeof(void *);
|
|
case SPL_TYPE_ARRAY:
|
|
return ir_type_size(ctx, tn->array_type.element) * tn->array_type.len;
|
|
case SPL_TYPE_STRUCT:
|
|
case SPL_TYPE_UNION: {
|
|
usize s = 0;
|
|
for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i)
|
|
s += ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id);
|
|
return s;
|
|
}
|
|
default:
|
|
return 8;
|
|
}
|
|
}
|
|
|
|
static bool ir_is_aggregate(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
|
if (!tn)
|
|
return false;
|
|
return tn->kind == SPL_TYPE_ARRAY || tn->kind == SPL_TYPE_STRUCT ||
|
|
tn->kind == SPL_TYPE_UNION || tn->kind == SPL_TYPE_ENUM || tn->kind == SPL_TYPE_SHAPE;
|
|
}
|
|
|
|
static spl_type_id_t ir_agg_field_tid(spl_ir2vm_t *ctx, spl_type_id_t tid, usize idx) {
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
|
if (!tn)
|
|
return 0;
|
|
if (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_UNION) {
|
|
if (idx < vec_size(tn->agg_type.variants))
|
|
return vec_at(tn->agg_type.variants, idx).type_id;
|
|
} else if (tn->kind == SPL_TYPE_ARRAY) {
|
|
return tn->array_type.element;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static usize ir_agg_field_off(spl_ir2vm_t *ctx, spl_type_id_t tid, usize idx) {
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
|
if (!tn)
|
|
return 0;
|
|
if (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_UNION) {
|
|
usize off = 0;
|
|
for (usize i = 0; i < idx && i < vec_size(tn->agg_type.variants); ++i)
|
|
off += ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id);
|
|
return off;
|
|
}
|
|
if (tn->kind == SPL_TYPE_ARRAY)
|
|
return idx * ir_type_size(ctx, tn->array_type.element);
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t vm_kind_of(spl_ir2vm_t *ctx, spl_type_id_t tid) {
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
|
if (!tn)
|
|
return SPL_VOID;
|
|
switch (tn->kind) {
|
|
case SPL_TYPE_UNIT:
|
|
return SPL_VOID;
|
|
case SPL_TYPE_BOOL:
|
|
return SPL_BOOL;
|
|
case SPL_TYPE_INT:
|
|
if (tn->int_type.bits == 0)
|
|
return SPL_I32;
|
|
switch (tn->int_type.bits) {
|
|
case 8:
|
|
return tn->int_type.is_signed ? SPL_I8 : SPL_U8;
|
|
case 16:
|
|
return tn->int_type.is_signed ? SPL_I16 : SPL_U16;
|
|
case 32:
|
|
return tn->int_type.is_signed ? SPL_I32 : SPL_U32;
|
|
default:
|
|
return tn->int_type.is_signed ? SPL_I64 : SPL_U64;
|
|
}
|
|
case SPL_TYPE_FLOAT:
|
|
return tn->float_type.bits == 32 ? SPL_F32 : SPL_F64;
|
|
default:
|
|
return SPL_PTR;
|
|
}
|
|
}
|
|
|
|
static spl_type_id_t ir_result_tid(const spl_ir_node_t *n) {
|
|
switch (n->kind) {
|
|
case SPL_IR_ARITH_ADD:
|
|
case SPL_IR_ARITH_SUB:
|
|
case SPL_IR_ARITH_MUL:
|
|
case SPL_IR_ARITH_DIV:
|
|
case SPL_IR_ARITH_REM:
|
|
case SPL_IR_ARITH_AND:
|
|
case SPL_IR_ARITH_OR:
|
|
case SPL_IR_ARITH_XOR:
|
|
case SPL_IR_ARITH_SHL:
|
|
case SPL_IR_ARITH_SHR:
|
|
case SPL_IR_ARITH_NEG:
|
|
case SPL_IR_ARITH_ABS:
|
|
case SPL_IR_ARITH_NOT:
|
|
return n->arith.tid;
|
|
case SPL_IR_CMP_EQ:
|
|
case SPL_IR_CMP_NE:
|
|
case SPL_IR_CMP_LT:
|
|
case SPL_IR_CMP_LE:
|
|
case SPL_IR_CMP_GT:
|
|
case SPL_IR_CMP_GE:
|
|
return n->cmp.tid;
|
|
case SPL_IR_CAST_TRUNC:
|
|
case SPL_IR_CAST_ZEXT:
|
|
case SPL_IR_CAST_SEXT:
|
|
case SPL_IR_CAST_FEXT:
|
|
case SPL_IR_CAST_FTRUNC:
|
|
case SPL_IR_CAST_BITCAST:
|
|
case SPL_IR_CAST_PTR2INT:
|
|
case SPL_IR_CAST_INT2PTR:
|
|
case SPL_IR_CAST_BOOL2INT:
|
|
case SPL_IR_CASE_INT2FLOAT:
|
|
case SPL_IR_CASE_FLOAT2INT:
|
|
return n->cast.to_tid;
|
|
case SPL_IR_MEM_ALLOCA:
|
|
return n->mem_alloc.tid;
|
|
case SPL_IR_MEM_GLOBAL_ALLOC:
|
|
return n->mem_global_alloc.tid;
|
|
case SPL_IR_MEM_LOAD:
|
|
return n->mem_load.tid;
|
|
case SPL_IR_MEM_OFFSET:
|
|
return n->mem_offset.tid;
|
|
case SPL_IR_MEM_FIELD_PTR:
|
|
return n->mem_field_ptr.tid;
|
|
case SPL_IR_TYPE_CONST:
|
|
return n->type_const.tid;
|
|
case SPL_IR_TYPE_BITSIZEOF:
|
|
return n->bitsizeof.tid;
|
|
case SPL_IR_TYPE_SIZEOF:
|
|
return n->ir_sizeof.tid;
|
|
case SPL_IR_TYPE_ALIGNOF:
|
|
return n->ir_alignof.tid;
|
|
case SPL_IR_TYPE_OFFSETOF:
|
|
return n->ir_offsetof.tid;
|
|
case SPL_IR_TYPE_FIELD_COUNT:
|
|
return n->field_count.tid;
|
|
case SPL_IR_AGG_CONSTRUCT:
|
|
return n->agg_construct.tid;
|
|
case SPL_IR_AGG_EXTRACT:
|
|
return n->agg_extract.field_tid;
|
|
case SPL_IR_AGG_INSERT:
|
|
return n->agg_insert.tid;
|
|
case SPL_IR_CONTROL_SELECT:
|
|
return n->control_select.tid;
|
|
case SPL_IR_CONTROL_CALL:
|
|
return n->control_call.tid;
|
|
case SPL_IR_CONTROL_PARAM:
|
|
return n->control_param.tid;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static usize align_up(usize x, usize a) {
|
|
if (a <= 1)
|
|
return x;
|
|
return (x + a - 1) / a * a;
|
|
}
|
|
|
|
typedef struct {
|
|
usize result_off;
|
|
usize data_off;
|
|
} slot_t;
|
|
|
|
typedef struct {
|
|
VEC(slot_t) slots;
|
|
usize args_bytes;
|
|
usize frame_bytes;
|
|
} frame_layout_t;
|
|
|
|
static usize node_data_size(spl_ir2vm_t *ctx, const spl_ir_func_t *f, const spl_ir_node_t *node) {
|
|
switch (node->kind) {
|
|
case SPL_IR_MEM_ALLOCA: {
|
|
usize count = 1;
|
|
if (node->mem_alloc.count < vec_size(f->nodes)) {
|
|
const spl_ir_node_t *cn = &vec_at(f->nodes, node->mem_alloc.count);
|
|
if (cn->kind == SPL_IR_TYPE_CONST)
|
|
count = (usize)cn->type_const.int_lit;
|
|
}
|
|
if (count == 0)
|
|
count = 1;
|
|
usize sz = ir_type_size(ctx, node->mem_alloc.tid);
|
|
if (sz == 0)
|
|
sz = 1;
|
|
return sz * count;
|
|
}
|
|
case SPL_IR_AGG_CONSTRUCT:
|
|
case SPL_IR_AGG_INSERT: {
|
|
usize sz = ir_type_size(ctx, node->agg_construct.tid);
|
|
return sz ? sz : 8;
|
|
}
|
|
case SPL_IR_AGG_EXTRACT:
|
|
if (ir_is_aggregate(ctx, node->agg_extract.field_tid))
|
|
return ir_type_size(ctx, node->agg_extract.field_tid);
|
|
return 0;
|
|
case SPL_IR_MEM_LOAD:
|
|
if (ir_is_aggregate(ctx, node->mem_load.tid))
|
|
return ir_type_size(ctx, node->mem_load.tid);
|
|
return 0;
|
|
case SPL_IR_CONTROL_CALL:
|
|
if (ir_is_aggregate(ctx, node->control_call.tid))
|
|
return ir_type_size(ctx, node->control_call.tid);
|
|
return 0;
|
|
case SPL_IR_TYPE_CONST: {
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, node->type_const.tid);
|
|
if (tn && (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_ARRAY ||
|
|
tn->kind == SPL_TYPE_UNION))
|
|
return ir_type_size(ctx, node->type_const.tid);
|
|
return 0;
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void frame_build(frame_layout_t *fl, spl_ir2vm_t *ctx, spl_ir_func_ref_t fid) {
|
|
vec_init(fl->slots);
|
|
fl->args_bytes = 0;
|
|
fl->frame_bytes = 0;
|
|
|
|
const spl_ir_func_t *f = spl_ir_func((spl_ir_t *)ctx->ir, fid);
|
|
if (!f)
|
|
return;
|
|
|
|
usize n = vec_size(f->nodes);
|
|
for (usize i = 0; i < n; ++i)
|
|
vec_push(fl->slots, (slot_t){0});
|
|
|
|
spl_type_node_t *ftn = spl_type_node(ctx->type, f->fn_tid);
|
|
usize nargs = (ftn && ftn->kind == SPL_TYPE_FN) ? vec_size(ftn->fn_type.params) : 0;
|
|
fl->args_bytes = nargs * sizeof(spl_vm_val_t);
|
|
|
|
usize off = fl->args_bytes;
|
|
|
|
for (usize i = 1; i < n; ++i) {
|
|
const spl_ir_node_t *node = &vec_at(f->nodes, i);
|
|
if (ir_result_tid(node)) {
|
|
vec_at(fl->slots, i).result_off = off;
|
|
off += sizeof(spl_vm_val_t);
|
|
}
|
|
usize extra = node_data_size(ctx, f, node);
|
|
if (extra) {
|
|
off = align_up(off, 8);
|
|
vec_at(fl->slots, i).data_off = off;
|
|
off += align_up(extra, 8);
|
|
}
|
|
}
|
|
|
|
fl->frame_bytes = align_up(off, 8);
|
|
}
|
|
|
|
static void frame_drop(frame_layout_t *fl) { vec_free(fl->slots); }
|
|
|
|
typedef enum {
|
|
FIX_JMP,
|
|
FIX_FNADDR,
|
|
} fix_kind_t;
|
|
|
|
typedef struct {
|
|
fix_kind_t kind;
|
|
usize ins_idx;
|
|
spl_ir_node_ref_t node_target;
|
|
spl_ir_func_ref_t fn_target;
|
|
} fix_t;
|
|
typedef VEC(fix_t) fix_vec_t;
|
|
|
|
typedef struct {
|
|
spl_ir2vm_t *ctx;
|
|
spl_ir_func_ref_t fid;
|
|
const spl_ir_func_t *irf;
|
|
frame_layout_t frame;
|
|
VEC(usize) node_ip;
|
|
fix_vec_t *fixups;
|
|
} cgen_t;
|
|
|
|
static void emit(cgen_t *cg, uint8_t op, uint8_t ty, spl_vm_val_t imm) {
|
|
spl_vm_ins_t ins = {.opcode = op, .type = ty, .imm = imm};
|
|
vec_push(cg->ctx->prog.insns, ins);
|
|
}
|
|
|
|
static usize ip_of(cgen_t *cg) { return vec_size(cg->ctx->prog.insns); }
|
|
|
|
static void CG_LADDR(cgen_t *cg, usize off) { emit(cg, SPL_LADDR, SPL_VOID, off); }
|
|
static void CG_LOAD(cgen_t *cg, uint8_t k) { emit(cg, SPL_LOAD, k, 0); }
|
|
static void CG_STORE(cgen_t *cg, uint8_t k) { emit(cg, SPL_STORE, k, 0); }
|
|
static void CG_PUSH(cgen_t *cg, uint8_t k, spl_vm_val_t v) { emit(cg, SPL_PUSH, k, v); }
|
|
static void CG_OP(cgen_t *cg, uint8_t op, uint8_t k) { emit(cg, op, k, 0); }
|
|
static void CG_SWAP(cgen_t *cg) { emit(cg, SPL_SWAP, SPL_VOID, 0); }
|
|
static void CG_GADDR(cgen_t *cg, spl_vm_val_t idx) { emit(cg, SPL_GADDR, SPL_VOID, idx); }
|
|
|
|
static void cg_load_slot(cgen_t *cg, usize ref, uint8_t k) {
|
|
CG_LADDR(cg, vec_at(cg->frame.slots, ref).result_off);
|
|
CG_LOAD(cg, k);
|
|
}
|
|
|
|
static void cg_store_slot(cgen_t *cg, usize ref, uint8_t k) {
|
|
CG_LADDR(cg, vec_at(cg->frame.slots, ref).result_off);
|
|
CG_SWAP(cg);
|
|
CG_STORE(cg, k);
|
|
}
|
|
|
|
static void cg_store_at(cgen_t *cg, usize addr, uint8_t k) {
|
|
CG_LADDR(cg, addr);
|
|
CG_SWAP(cg);
|
|
CG_STORE(cg, k);
|
|
}
|
|
|
|
typedef struct {
|
|
bool indirect;
|
|
usize off;
|
|
usize base;
|
|
} data_loc_t;
|
|
|
|
static data_loc_t direct_data(usize off) {
|
|
return (data_loc_t){.indirect = false, .off = off, .base = 0};
|
|
}
|
|
static data_loc_t direct_data_base(usize off, usize base) {
|
|
return (data_loc_t){.indirect = false, .off = off, .base = base};
|
|
}
|
|
static data_loc_t ptr_slot(usize slot_off) {
|
|
return (data_loc_t){.indirect = true, .off = slot_off, .base = 0};
|
|
}
|
|
static data_loc_t ptr_slot_base(usize slot_off, usize base) {
|
|
return (data_loc_t){.indirect = true, .off = slot_off, .base = base};
|
|
}
|
|
|
|
static void cg_push_addr(cgen_t *cg, data_loc_t loc, usize extra) {
|
|
usize delta = loc.base + extra;
|
|
if (loc.indirect) {
|
|
CG_LADDR(cg, loc.off);
|
|
CG_LOAD(cg, SPL_PTR);
|
|
if (delta) {
|
|
CG_PUSH(cg, SPL_U64, delta);
|
|
CG_OP(cg, SPL_ADD, SPL_PTR);
|
|
}
|
|
} else {
|
|
CG_LADDR(cg, loc.off + delta);
|
|
}
|
|
}
|
|
|
|
static void cg_copy_agg(cgen_t *cg, data_loc_t src, data_loc_t dst, spl_type_id_t tid) {
|
|
usize sz = ir_type_size(cg->ctx, tid);
|
|
usize done = 0;
|
|
while (sz - done >= 8) {
|
|
cg_push_addr(cg, dst, done);
|
|
cg_push_addr(cg, src, done);
|
|
CG_LOAD(cg, SPL_U64);
|
|
CG_STORE(cg, SPL_U64);
|
|
done += 8;
|
|
}
|
|
while (done < sz) {
|
|
cg_push_addr(cg, dst, done);
|
|
cg_push_addr(cg, src, done);
|
|
CG_LOAD(cg, SPL_U8);
|
|
CG_STORE(cg, SPL_U8);
|
|
done += 1;
|
|
}
|
|
}
|
|
|
|
static void cg_add_fix(cgen_t *cg, usize ins_idx, fix_kind_t kind, spl_ir_node_ref_t node_target,
|
|
spl_ir_func_ref_t fn_target) {
|
|
fix_t fx = {
|
|
.kind = kind, .ins_idx = ins_idx, .node_target = node_target, .fn_target = fn_target};
|
|
vec_push(*cg->fixups, fx);
|
|
}
|
|
|
|
static void cg_binop(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
|
uint8_t k = vm_kind_of(cg->ctx, n->arith.tid);
|
|
cg_load_slot(cg, n->arith.left, k);
|
|
cg_load_slot(cg, n->arith.right, k);
|
|
CG_OP(cg, op, k);
|
|
cg_store_slot(cg, ref, k);
|
|
}
|
|
|
|
static void cg_unop(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
|
uint8_t k = vm_kind_of(cg->ctx, n->arith.tid);
|
|
cg_load_slot(cg, n->arith.left, k);
|
|
CG_OP(cg, op, k);
|
|
cg_store_slot(cg, ref, k);
|
|
}
|
|
|
|
static void cg_cmp(cgen_t *cg, usize ref, const spl_ir_node_t *n, uint8_t op) {
|
|
uint8_t k = vm_kind_of(cg->ctx, n->cmp.tid);
|
|
cg_load_slot(cg, n->cmp.a, k);
|
|
cg_load_slot(cg, n->cmp.b, k);
|
|
CG_OP(cg, op, k);
|
|
cg_store_slot(cg, ref, SPL_BOOL);
|
|
}
|
|
|
|
static void cg_const(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
spl_ir2vm_t *ctx = cg->ctx;
|
|
spl_type_id_t tid = n->type_const.tid;
|
|
spl_type_node_t *tn = spl_type_node(ctx->type, tid);
|
|
if (!tn)
|
|
return;
|
|
|
|
if (tn->kind == SPL_TYPE_FN) {
|
|
usize idx = ip_of(cg);
|
|
CG_PUSH(cg, SPL_PTR, 0);
|
|
cg_add_fix(cg, idx, FIX_FNADDR, 0, n->type_const.fn);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
return;
|
|
}
|
|
|
|
if (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_ARRAY || tn->kind == SPL_TYPE_UNION) {
|
|
const char *bytes = (const char *)n->type_const.ptr_lit;
|
|
if (!bytes)
|
|
bytes = "";
|
|
usize len = strlen(bytes);
|
|
usize data_idx = (usize)spl_prog_add_data(&ctx->prog, (void *)bytes, len + 1);
|
|
usize base = vec_at(cg->frame.slots, ref).data_off;
|
|
usize ptr_off = ir_agg_field_off(ctx, tid, 0);
|
|
usize len_off = ir_agg_field_off(ctx, tid, 1);
|
|
|
|
CG_GADDR(cg, data_idx);
|
|
cg_store_at(cg, base + ptr_off, SPL_PTR);
|
|
|
|
CG_PUSH(cg, SPL_U64, len);
|
|
cg_store_at(cg, base + len_off, SPL_U64);
|
|
|
|
CG_LADDR(cg, base);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
return;
|
|
}
|
|
|
|
uint8_t k = vm_kind_of(ctx, tid);
|
|
spl_vm_val_t imm = 0;
|
|
switch (tn->kind) {
|
|
case SPL_TYPE_BOOL:
|
|
imm = n->type_const.bool_lit ? 1 : 0;
|
|
break;
|
|
case SPL_TYPE_INT:
|
|
if (tn->int_type.bits == 8)
|
|
imm = (spl_vm_val_t)(unsigned char)n->type_const.ch_lit;
|
|
else
|
|
imm = (spl_vm_val_t)n->type_const.int_lit;
|
|
break;
|
|
case SPL_TYPE_FLOAT:
|
|
memcpy(&imm, &n->type_const.float_lit, sizeof(double));
|
|
break;
|
|
case SPL_TYPE_PTR:
|
|
imm = (spl_vm_val_t)(usize)n->type_const.ptr_lit;
|
|
break;
|
|
case SPL_TYPE_UNIT:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
CG_PUSH(cg, k, imm);
|
|
cg_store_slot(cg, ref, k);
|
|
}
|
|
|
|
static void cg_cast(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
spl_ir2vm_t *ctx = cg->ctx;
|
|
spl_type_node_t *ft = spl_type_node(ctx->type, n->cast.from_tid);
|
|
spl_type_node_t *tt = spl_type_node(ctx->type, n->cast.to_tid);
|
|
if (!ft || !tt)
|
|
return;
|
|
|
|
uint8_t fk = vm_kind_of(ctx, n->cast.from_tid);
|
|
uint8_t tk = vm_kind_of(ctx, n->cast.to_tid);
|
|
|
|
cg_load_slot(cg, n->cast.val, fk);
|
|
|
|
if (ft->kind == SPL_TYPE_INT && tt->kind == SPL_TYPE_INT) {
|
|
usize fb = ft->int_type.bits;
|
|
usize tb = tt->int_type.bits;
|
|
if (fb > tb)
|
|
emit(cg, SPL_TRUNC, SPL_VOID, tb);
|
|
else if (fb < tb)
|
|
emit(cg, ft->int_type.is_signed ? SPL_SEXT : SPL_ZEXT, SPL_VOID, fb);
|
|
} else if (ft->kind == SPL_TYPE_BOOL && tt->kind == SPL_TYPE_INT) {
|
|
emit(cg, SPL_ZEXT, SPL_VOID, 1);
|
|
}
|
|
|
|
cg_store_slot(cg, ref, tk);
|
|
}
|
|
|
|
static void cg_mem(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
spl_ir2vm_t *ctx = cg->ctx;
|
|
switch (n->kind) {
|
|
|
|
case SPL_IR_MEM_ALLOCA: {
|
|
CG_LADDR(cg, vec_at(cg->frame.slots, ref).data_off);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_MEM_LOAD: {
|
|
spl_type_id_t tid = n->mem_load.tid;
|
|
if (ir_is_aggregate(ctx, tid)) {
|
|
usize data = vec_at(cg->frame.slots, ref).data_off;
|
|
cg_copy_agg(cg, ptr_slot(vec_at(cg->frame.slots, n->mem_load.ptr).result_off),
|
|
direct_data(data), tid);
|
|
CG_LADDR(cg, data);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
} else {
|
|
uint8_t k = vm_kind_of(ctx, tid);
|
|
cg_load_slot(cg, n->mem_load.ptr, SPL_PTR);
|
|
CG_LOAD(cg, k);
|
|
cg_store_slot(cg, ref, k);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_MEM_STORE: {
|
|
spl_type_id_t tid = n->mem_store.tid;
|
|
if (ir_is_aggregate(ctx, tid)) {
|
|
cg_copy_agg(cg, ptr_slot(vec_at(cg->frame.slots, n->mem_store.val).result_off),
|
|
ptr_slot(vec_at(cg->frame.slots, n->mem_store.ptr).result_off), tid);
|
|
} else {
|
|
uint8_t k = vm_kind_of(ctx, tid);
|
|
cg_load_slot(cg, n->mem_store.ptr, SPL_PTR);
|
|
cg_load_slot(cg, n->mem_store.val, k);
|
|
CG_STORE(cg, k);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_MEM_OFFSET: {
|
|
usize elem_size = ir_type_size(ctx, n->mem_offset.tid);
|
|
if (elem_size == 0)
|
|
elem_size = 1;
|
|
cg_load_slot(cg, n->mem_offset.ptr, SPL_PTR);
|
|
cg_load_slot(cg, n->mem_offset.offset, SPL_U64);
|
|
CG_PUSH(cg, SPL_U64, elem_size);
|
|
CG_OP(cg, SPL_MUL, SPL_U64);
|
|
CG_OP(cg, SPL_ADD, SPL_PTR);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_MEM_FIELD_PTR: {
|
|
spl_type_id_t agg_tid = n->mem_field_ptr.tid;
|
|
usize off = ir_agg_field_off(ctx, agg_tid, n->mem_field_ptr.field_idx);
|
|
cg_load_slot(cg, n->mem_field_ptr.agg, SPL_PTR);
|
|
if (off) {
|
|
CG_PUSH(cg, SPL_U64, off);
|
|
CG_OP(cg, SPL_ADD, SPL_PTR);
|
|
}
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cg_agg(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
spl_ir2vm_t *ctx = cg->ctx;
|
|
switch (n->kind) {
|
|
|
|
case SPL_IR_AGG_CONSTRUCT: {
|
|
usize base = vec_at(cg->frame.slots, ref).data_off;
|
|
spl_type_id_t agg_t = n->agg_construct.tid;
|
|
|
|
for (usize i = 0; i < vec_size(n->agg_construct.fields); ++i) {
|
|
spl_ir_node_ref_t fref = vec_at(n->agg_construct.fields, i);
|
|
spl_type_id_t ftid = ir_agg_field_tid(ctx, agg_t, i);
|
|
if (!ftid)
|
|
continue;
|
|
usize foff = ir_agg_field_off(ctx, agg_t, i);
|
|
|
|
if (ir_is_aggregate(ctx, ftid)) {
|
|
cg_copy_agg(cg, ptr_slot(vec_at(cg->frame.slots, fref).result_off),
|
|
direct_data_base(base, foff), ftid);
|
|
} else {
|
|
uint8_t fk = vm_kind_of(ctx, ftid);
|
|
cg_load_slot(cg, fref, fk);
|
|
cg_store_at(cg, base + foff, fk);
|
|
}
|
|
}
|
|
CG_LADDR(cg, base);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_AGG_EXTRACT: {
|
|
spl_type_id_t agg_t = n->agg_extract.tid;
|
|
spl_type_id_t ftid = n->agg_extract.field_tid;
|
|
usize foff = ir_agg_field_off(ctx, agg_t, n->agg_extract.field_idx);
|
|
|
|
if (ir_is_aggregate(ctx, ftid)) {
|
|
usize data = vec_at(cg->frame.slots, ref).data_off;
|
|
cg_copy_agg(cg,
|
|
ptr_slot_base(vec_at(cg->frame.slots, n->agg_extract.val).result_off, foff),
|
|
direct_data(data), ftid);
|
|
CG_LADDR(cg, data);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
} else {
|
|
uint8_t fk = vm_kind_of(ctx, ftid);
|
|
cg_load_slot(cg, n->agg_extract.val, SPL_PTR);
|
|
if (foff) {
|
|
CG_PUSH(cg, SPL_U64, foff);
|
|
CG_OP(cg, SPL_ADD, SPL_PTR);
|
|
}
|
|
CG_LOAD(cg, fk);
|
|
cg_store_slot(cg, ref, fk);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_AGG_INSERT: {
|
|
usize base = vec_at(cg->frame.slots, ref).data_off;
|
|
spl_type_id_t agg_t = n->agg_insert.tid;
|
|
|
|
cg_copy_agg(cg, ptr_slot(vec_at(cg->frame.slots, n->agg_insert.agg).result_off),
|
|
direct_data(base), agg_t);
|
|
|
|
usize foff = ir_agg_field_off(ctx, agg_t, n->agg_insert.field_idx);
|
|
spl_type_id_t ftid = ir_agg_field_tid(ctx, agg_t, n->agg_insert.field_idx);
|
|
if (ftid) {
|
|
if (ir_is_aggregate(ctx, ftid)) {
|
|
cg_copy_agg(cg, ptr_slot(vec_at(cg->frame.slots, n->agg_insert.field).result_off),
|
|
direct_data_base(base, foff), ftid);
|
|
} else {
|
|
uint8_t fk = vm_kind_of(ctx, ftid);
|
|
cg_load_slot(cg, n->agg_insert.field, fk);
|
|
cg_store_at(cg, base + foff, fk);
|
|
}
|
|
}
|
|
|
|
CG_LADDR(cg, base);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cg_typeop(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
spl_ir2vm_t *ctx = cg->ctx;
|
|
usize v = 0;
|
|
switch (n->kind) {
|
|
case SPL_IR_TYPE_SIZEOF:
|
|
v = ir_type_size(ctx, n->ir_sizeof.tid);
|
|
break;
|
|
case SPL_IR_TYPE_BITSIZEOF:
|
|
v = ir_type_size(ctx, n->bitsizeof.tid) * 8;
|
|
break;
|
|
case SPL_IR_TYPE_ALIGNOF:
|
|
v = ir_type_size(ctx, n->ir_alignof.tid);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
CG_PUSH(cg, SPL_U64, v);
|
|
cg_store_slot(cg, ref, SPL_U64);
|
|
}
|
|
|
|
static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
spl_ir2vm_t *ctx = cg->ctx;
|
|
switch (n->kind) {
|
|
|
|
case SPL_IR_CONTROL_PARAM: {
|
|
uint8_t k = vm_kind_of(ctx, n->control_param.tid);
|
|
usize idx = 0;
|
|
if (n->control_param.idx < vec_size(cg->irf->nodes)) {
|
|
const spl_ir_node_t *idx_node = &vec_at(cg->irf->nodes, n->control_param.idx);
|
|
if (idx_node->kind == SPL_IR_TYPE_CONST)
|
|
idx = (usize)idx_node->type_const.int_lit;
|
|
}
|
|
CG_LADDR(cg, idx * sizeof(spl_vm_val_t));
|
|
CG_LOAD(cg, k);
|
|
cg_store_slot(cg, ref, k);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_CONTROL_RET: {
|
|
uint8_t k = vm_kind_of(ctx, n->control_ret.tid);
|
|
if (k != SPL_VOID && n->control_ret.val)
|
|
cg_load_slot(cg, n->control_ret.val, k);
|
|
emit(cg, SPL_RET, k, 0);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_CONTROL_BR: {
|
|
cg_load_slot(cg, n->control_br.cond, SPL_BOOL);
|
|
usize bz_idx = ip_of(cg);
|
|
emit(cg, SPL_BZ, SPL_VOID, 0);
|
|
cg_add_fix(cg, bz_idx, FIX_JMP, n->control_br.false_label, 0);
|
|
usize jmp_idx = ip_of(cg);
|
|
emit(cg, SPL_JMP, SPL_VOID, 0);
|
|
cg_add_fix(cg, jmp_idx, FIX_JMP, n->control_br.true_label, 0);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_CONTROL_JMP: {
|
|
usize idx = ip_of(cg);
|
|
emit(cg, SPL_JMP, SPL_VOID, 0);
|
|
cg_add_fix(cg, idx, FIX_JMP, n->control_jmp.label, 0);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_CONTROL_CALL: {
|
|
usize nargs = vec_size(n->control_call.params);
|
|
for (usize i = 0; i < nargs; ++i) {
|
|
spl_ir_node_ref_t pref = vec_at(n->control_call.params, i);
|
|
const spl_ir_node_t *pn = &vec_at(cg->irf->nodes, pref);
|
|
spl_type_id_t ptid = ir_result_tid(pn);
|
|
uint8_t pk = vm_kind_of(ctx, ptid);
|
|
cg_load_slot(cg, pref, pk);
|
|
}
|
|
|
|
bool direct = false;
|
|
spl_ir_node_ref_t fref = n->control_call.func;
|
|
if (fref < vec_size(cg->irf->nodes)) {
|
|
const spl_ir_node_t *fn_node = &vec_at(cg->irf->nodes, fref);
|
|
if (fn_node->kind == SPL_IR_TYPE_CONST) {
|
|
spl_type_node_t *ftn = spl_type_node(ctx->type, fn_node->type_const.tid);
|
|
if (ftn && ftn->kind == SPL_TYPE_FN) {
|
|
usize idx = ip_of(cg);
|
|
CG_PUSH(cg, SPL_PTR, 0);
|
|
cg_add_fix(cg, idx, FIX_FNADDR, 0, fn_node->type_const.fn);
|
|
emit(cg, SPL_CALL, SPL_VOID, nargs);
|
|
direct = true;
|
|
}
|
|
}
|
|
}
|
|
if (!direct)
|
|
emit(cg, SPL_HALT, SPL_VOID, 0);
|
|
|
|
spl_type_id_t rtid = n->control_call.tid;
|
|
if (ir_is_aggregate(ctx, rtid)) {
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
usize data = vec_at(cg->frame.slots, ref).data_off;
|
|
cg_copy_agg(cg, ptr_slot(vec_at(cg->frame.slots, ref).result_off), direct_data(data),
|
|
rtid);
|
|
CG_LADDR(cg, data);
|
|
cg_store_slot(cg, ref, SPL_PTR);
|
|
} else {
|
|
uint8_t rk = vm_kind_of(ctx, rtid);
|
|
if (rk != SPL_VOID)
|
|
cg_store_slot(cg, ref, rk);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_CONTROL_UNREACHABLE:
|
|
case SPL_IR_CONTROL_TRAP:
|
|
emit(cg, SPL_HALT, SPL_VOID, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void cg_node(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|
switch (n->kind) {
|
|
case SPL_IR_TYPE_CONST:
|
|
cg_const(cg, ref, n);
|
|
break;
|
|
|
|
case SPL_IR_ARITH_ADD:
|
|
cg_binop(cg, ref, n, SPL_ADD);
|
|
break;
|
|
case SPL_IR_ARITH_SUB:
|
|
cg_binop(cg, ref, n, SPL_SUB);
|
|
break;
|
|
case SPL_IR_ARITH_MUL:
|
|
cg_binop(cg, ref, n, SPL_MUL);
|
|
break;
|
|
case SPL_IR_ARITH_DIV: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->arith.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_binop(cg, ref, n, sgn ? SPL_DIV_S : SPL_DIV_U);
|
|
break;
|
|
}
|
|
case SPL_IR_ARITH_REM: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->arith.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_binop(cg, ref, n, sgn ? SPL_REM_S : SPL_REM_U);
|
|
break;
|
|
}
|
|
case SPL_IR_ARITH_AND:
|
|
cg_binop(cg, ref, n, SPL_AND);
|
|
break;
|
|
case SPL_IR_ARITH_OR:
|
|
cg_binop(cg, ref, n, SPL_OR);
|
|
break;
|
|
case SPL_IR_ARITH_XOR:
|
|
cg_binop(cg, ref, n, SPL_XOR);
|
|
break;
|
|
case SPL_IR_ARITH_SHL:
|
|
cg_binop(cg, ref, n, SPL_SHL);
|
|
break;
|
|
case SPL_IR_ARITH_SHR: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->arith.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_binop(cg, ref, n, sgn ? SPL_SHR_S : SPL_SHR_U);
|
|
break;
|
|
}
|
|
case SPL_IR_ARITH_NEG:
|
|
cg_unop(cg, ref, n, SPL_NEG);
|
|
break;
|
|
case SPL_IR_ARITH_NOT:
|
|
cg_unop(cg, ref, n, SPL_NOT);
|
|
break;
|
|
case SPL_IR_ARITH_ABS:
|
|
break;
|
|
|
|
case SPL_IR_CMP_EQ:
|
|
cg_cmp(cg, ref, n, SPL_EQ);
|
|
break;
|
|
case SPL_IR_CMP_NE:
|
|
cg_cmp(cg, ref, n, SPL_NE);
|
|
break;
|
|
case SPL_IR_CMP_LT: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->cmp.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_cmp(cg, ref, n, sgn ? SPL_SLT : SPL_ULT);
|
|
break;
|
|
}
|
|
case SPL_IR_CMP_LE: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->cmp.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_cmp(cg, ref, n, sgn ? SPL_SLE : SPL_ULE);
|
|
break;
|
|
}
|
|
case SPL_IR_CMP_GT: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->cmp.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_cmp(cg, ref, n, sgn ? SPL_SGT : SPL_UGT);
|
|
break;
|
|
}
|
|
case SPL_IR_CMP_GE: {
|
|
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->cmp.tid);
|
|
bool sgn = tn && tn->kind == SPL_TYPE_INT && tn->int_type.is_signed;
|
|
cg_cmp(cg, ref, n, sgn ? SPL_SGE : SPL_UGE);
|
|
break;
|
|
}
|
|
|
|
case SPL_IR_CAST_TRUNC:
|
|
case SPL_IR_CAST_ZEXT:
|
|
case SPL_IR_CAST_SEXT:
|
|
case SPL_IR_CAST_FEXT:
|
|
case SPL_IR_CAST_FTRUNC:
|
|
case SPL_IR_CAST_BITCAST:
|
|
case SPL_IR_CAST_PTR2INT:
|
|
case SPL_IR_CAST_INT2PTR:
|
|
case SPL_IR_CAST_BOOL2INT:
|
|
case SPL_IR_CASE_INT2FLOAT:
|
|
case SPL_IR_CASE_FLOAT2INT:
|
|
cg_cast(cg, ref, n);
|
|
break;
|
|
|
|
case SPL_IR_MEM_ALLOCA:
|
|
case SPL_IR_MEM_LOAD:
|
|
case SPL_IR_MEM_STORE:
|
|
case SPL_IR_MEM_OFFSET:
|
|
case SPL_IR_MEM_FIELD_PTR:
|
|
cg_mem(cg, ref, n);
|
|
break;
|
|
|
|
case SPL_IR_AGG_CONSTRUCT:
|
|
case SPL_IR_AGG_EXTRACT:
|
|
case SPL_IR_AGG_INSERT:
|
|
cg_agg(cg, ref, n);
|
|
break;
|
|
|
|
case SPL_IR_TYPE_SIZEOF:
|
|
case SPL_IR_TYPE_BITSIZEOF:
|
|
case SPL_IR_TYPE_ALIGNOF:
|
|
cg_typeop(cg, ref, n);
|
|
break;
|
|
|
|
case SPL_IR_CONTROL_PARAM:
|
|
case SPL_IR_CONTROL_RET:
|
|
case SPL_IR_CONTROL_BR:
|
|
case SPL_IR_CONTROL_JMP:
|
|
case SPL_IR_CONTROL_CALL:
|
|
case SPL_IR_CONTROL_UNREACHABLE:
|
|
case SPL_IR_CONTROL_TRAP:
|
|
cg_control(cg, ref, n);
|
|
break;
|
|
|
|
case SPL_IR_DBG_BREAKPOINT:
|
|
emit(cg, SPL_BK, SPL_VOID, 0);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static usize translate_function(spl_ir2vm_t *ctx, spl_ir_func_ref_t fid, fix_vec_t *all_fixups) {
|
|
const spl_ir_func_t *irf = spl_ir_func((spl_ir_t *)ctx->ir, fid);
|
|
if (!irf)
|
|
return 0;
|
|
|
|
cgen_t cg = {0};
|
|
cg.ctx = ctx;
|
|
cg.fid = fid;
|
|
cg.irf = irf;
|
|
frame_build(&cg.frame, ctx, fid);
|
|
|
|
fix_vec_t local_fixups;
|
|
vec_init(local_fixups);
|
|
cg.fixups = &local_fixups;
|
|
|
|
usize func_ip = ip_of(&cg);
|
|
|
|
spl_vm_func_t entry = {0};
|
|
entry.name = strdup(irf->name ? irf->name : "");
|
|
entry.idx_of_strtab = 0;
|
|
spl_type_node_t *ftn = spl_type_node(ctx->type, irf->fn_tid);
|
|
entry.nargs = (ftn && ftn->kind == SPL_TYPE_FN) ? vec_size(ftn->fn_type.params) : 0;
|
|
entry.ninsns = 0;
|
|
entry.address = func_ip;
|
|
vec_push(ctx->prog.funcs, entry);
|
|
|
|
usize local_slots = (cg.frame.frame_bytes - cg.frame.args_bytes + 7) / 8;
|
|
emit(&cg, SPL_ALLOC, SPL_VOID, local_slots);
|
|
|
|
usize nnodes = vec_size(irf->nodes);
|
|
vec_init(cg.node_ip);
|
|
for (usize i = 0; i < nnodes; ++i)
|
|
vec_push(cg.node_ip, 0);
|
|
|
|
for (usize i = 1; i < nnodes; ++i) {
|
|
vec_at(cg.node_ip, i) = ip_of(&cg);
|
|
cg_node(&cg, i, &vec_at(irf->nodes, i));
|
|
}
|
|
|
|
for (usize i = 0; i < vec_size(local_fixups); ++i) {
|
|
fix_t *fx = &vec_at(local_fixups, i);
|
|
if (fx->kind == FIX_JMP) {
|
|
spl_vm_ins_t *ins = &vec_at(ctx->prog.insns, fx->ins_idx);
|
|
usize target_ip = (fx->node_target < nnodes) ? vec_at(cg.node_ip, fx->node_target) : 0;
|
|
ins->imm = (spl_vm_val_t)(isize)(target_ip - fx->ins_idx - 1);
|
|
} else if (fx->kind == FIX_FNADDR) {
|
|
vec_push(*all_fixups, *fx);
|
|
}
|
|
}
|
|
|
|
vec_free(cg.node_ip);
|
|
vec_free(local_fixups);
|
|
frame_drop(&cg.frame);
|
|
return func_ip;
|
|
}
|
|
|
|
void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type) {
|
|
ctx->ir = ir;
|
|
ctx->type = type;
|
|
spl_prog_init(&ctx->prog);
|
|
}
|
|
|
|
void spl_ir2vm_drop(spl_ir2vm_t *ctx) { spl_prog_drop(&ctx->prog); }
|
|
|
|
int spl_ir2vm_run_func(spl_ir2vm_t *ctx, spl_ir_func_ref_t ref) {
|
|
fix_vec_t all_fixups;
|
|
vec_init(all_fixups);
|
|
translate_function(ctx, ref, &all_fixups);
|
|
vec_free(all_fixups);
|
|
return 0;
|
|
}
|
|
|
|
int spl_ir2vm_run(spl_ir2vm_t *ctx) {
|
|
usize n = vec_size(ctx->ir->funcs);
|
|
if (n <= 1)
|
|
return 0;
|
|
|
|
usize *fid_ip = calloc(n, sizeof(usize));
|
|
fix_vec_t all_fixups;
|
|
vec_init(all_fixups);
|
|
|
|
for (spl_ir_func_ref_t i = 1; i < n; ++i)
|
|
fid_ip[i] = translate_function(ctx, i, &all_fixups);
|
|
|
|
for (usize i = 0; i < vec_size(all_fixups); ++i) {
|
|
fix_t *fx = &vec_at(all_fixups, i);
|
|
if (fx->kind != FIX_FNADDR)
|
|
continue;
|
|
spl_vm_ins_t *ins = &vec_at(ctx->prog.insns, fx->ins_idx);
|
|
usize target_ip = (fx->fn_target > 0 && fx->fn_target < n) ? fid_ip[fx->fn_target] : 0;
|
|
ins->imm = (spl_vm_val_t)target_ip;
|
|
}
|
|
|
|
for (usize i = 0; i < vec_size(ctx->prog.funcs); ++i) {
|
|
spl_vm_func_t *f = &vec_at(ctx->prog.funcs, i);
|
|
usize next = (i + 1 < vec_size(ctx->prog.funcs)) ? vec_at(ctx->prog.funcs, i + 1).address
|
|
: vec_size(ctx->prog.insns);
|
|
f->ninsns = (spl_vm_val_t)(next - f->address);
|
|
}
|
|
|
|
free(fid_ip);
|
|
vec_free(all_fixups);
|
|
return 0;
|
|
}
|
|
|
|
void spl_ir2vm_dump(spl_ir2vm_t *ctx) { spl_prog_dump(&ctx->prog); }
|