Files
spl/stage1/spl_ir2vm.c
T

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); }