#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 int get_or_add_native(spl_ir2vm_t *ctx, const char *name) { if (!name) return -1; for (usize i = 0; i < vec_size(ctx->prog.natives); ++i) { spl_vm_native_t *n = &vec_at(ctx->prog.natives, i); if (n->name && strcmp(n->name, name) == 0) return (int)i; } spl_vm_native_t nat = {0}; nat.name = strdup(name); nat.idx_of_strtab = 0; nat.impl_fn = NULL; vec_push(ctx->prog.natives, nat); return (int)(vec_size(ctx->prog.natives) - 1); } static bool fn_check_extern(spl_ir2vm_t *ctx, spl_ir_func_t *f, const char **out_target) { if (out_target) *out_target = NULL; if (!ctx->attr || !f) return false; for (usize i = 0; i < vec_size(f->attrs); ++i) { spl_attr_node_t *a = spl_attr_node(ctx->attr, vec_at(f->attrs, i)); if (!a || !a->info) continue; if (strcmp(a->info->name, "extern") != 0) continue; a->consumed_mask |= (1u << SPL_ATTR_STAGE_IR); if (out_target && vec_size(a->args) >= 1 && vec_at(a->args, 0).kind == SPL_ATTR_ARG_STR) *out_target = vec_at(a->args, 0).value.s; return true; } return false; } 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_DBG_PANIC: return sizeof(spl_vm_val_t) * 4; /* TODO: fmt slice + msg slice */ 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; 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) { isize nat_idx = -1; if (n->type_const.fn > 0) map_get(ctx->extern_native, n->type_const.fn, &nat_idx); if (nat_idx >= 0) { CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat_idx); } else { 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; } 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_GLOBAL_ALLOC: { const spl_ir_node_t *cn = &vec_at(cg->irf->nodes, n->mem_global_alloc.const_node); int idx = 0; if (cn->kind == SPL_IR_TYPE_CONST && cn->type_const.ptr_lit) { usize size = ir_type_size(ctx, cn->type_const.tid); idx = spl_prog_add_data(&ctx->prog, cn->type_const.ptr_lit, size); } if (idx > 0) emit(cg, SPL_GADDR, SPL_VOID, (spl_vm_val_t)idx); else CG_PUSH(cg, SPL_PTR, 0); 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: { spl_ir_func_ref_t callee_ref = 0; 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) callee_ref = fn_node->type_const.fn; } isize nat_idx = -1; if (callee_ref > 0) map_get(ctx->extern_native, callee_ref, &nat_idx); usize nargs = vec_size(n->control_call.params); if (nat_idx >= 0) { 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); } CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat_idx); emit(cg, SPL_NCALL, SPL_VOID, (spl_vm_val_t)nargs); uint8_t rk = vm_kind_of(ctx, n->control_call.tid); if (rk == SPL_VOID) emit(cg, SPL_DROP, SPL_VOID, 0); /* NCALL 总是 push 一个结果 */ else cg_store_slot(cg, ref, rk); } else { 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); } cg_load_slot(cg, fref, SPL_PTR); emit(cg, SPL_CALL, SPL_VOID, (spl_vm_val_t)nargs); uint8_t rk = vm_kind_of(ctx, n->control_call.tid); 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, 1); 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: { spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->arith.tid); if (tn && tn->kind == SPL_TYPE_BOOL) { cg_load_slot(cg, n->arith.left, SPL_BOOL); CG_PUSH(cg, SPL_BOOL, 0); CG_OP(cg, SPL_EQ, SPL_BOOL); cg_store_slot(cg, ref, SPL_BOOL); } else { 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_GLOBAL_ALLOC: 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_PANIC: { spl_ir2vm_t *ctx = cg->ctx; usize data = vec_at(cg->frame.slots, ref).data_off; int msg_idx = spl_prog_add_data(&ctx->prog, (void *)n->dbg_panic.msg, n->dbg_panic.len); int fmt_idx = spl_prog_add_data(&ctx->prog, "%s", 2); if (msg_idx <= 0 || fmt_idx <= 0) break; /* fmt slice = {ptr, len=2} 写到 data+0 */ CG_GADDR(cg, (spl_vm_val_t)fmt_idx); cg_store_at(cg, data + 0, SPL_PTR); CG_PUSH(cg, SPL_U64, 2); cg_store_at(cg, data + 8, SPL_U64); /* msg slice = {ptr, len} 写到 data+16 */ CG_GADDR(cg, (spl_vm_val_t)msg_idx); cg_store_at(cg, data + 16, SPL_PTR); CG_PUSH(cg, SPL_U64, n->dbg_panic.len); cg_store_at(cg, data + 24, SPL_U64); /* push fmt slice 指针,push msg slice 指针 */ CG_LADDR(cg, data + 0); CG_LADDR(cg, data + 16); /* NCALL vm_printf(2) */ int nat = get_or_add_native(ctx, "vm_printf"); CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat); emit(cg, SPL_NCALL, SPL_VOID, 2); emit(cg, SPL_DROP, SPL_VOID, 0); /* 丢弃返回值 */ 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; } static usize spl_map_hash(usize k) { return MAP_HASH_INT(k); } static int spl_map_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); } void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type, const spl_attr_t *attr) { Assert(ir != NULL && type != NULL && attr != NULL); ctx->ir = ir; ctx->type = type; ctx->attr = attr; map_init(ctx->extern_native, spl_map_hash, spl_map_cmp); spl_prog_init(&ctx->prog); } void spl_ir2vm_drop(spl_ir2vm_t *ctx) { spl_prog_drop(&ctx->prog); map_free(ctx->extern_native); } 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; /* Pass 1:扫描所有 fn,把 extern 的登记到 map 里 */ for (spl_ir_func_ref_t i = 1; i < n; ++i) { spl_ir_func_t *irf = spl_ir_func((spl_ir_t *)ctx->ir, i); if (!irf) continue; if (fn_check_extern(ctx, irf, NULL)) { int nat_idx = get_or_add_native(ctx, irf->name); map_put(ctx->extern_native, i, (isize)nat_idx); } } /* Pass 2:翻译非 extern 的函数 */ 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) { isize dummy = -1; if (map_get(ctx->extern_native, i, &dummy)) continue; fid_ip[i] = translate_function(ctx, i, &all_fixups); } /* FNADDR 回填 */ 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); }