/* spl_emit.c — IR emission: frame allocator + Layer 1/2 ops */ #include "spl_emit.h" #include "spl_comp.h" #include "spl_type.h" #include /* ============================================================ * Lifecycle * ============================================================ */ void spl_emit_init(spl_emit_t *e, spl_prog_t *prog) { memset(e, 0, sizeof(*e)); e->prog = prog; vec_init(e->fixups); } void spl_emit_drop(spl_emit_t *e) { if (!e) return; vec_free(e->fixups); } /* ============================================================ * Frame allocator — non-inline * ============================================================ */ int fa_alloc_type(spl_frame_alloc_t *fa, spl_type_ctx_t *tctx, int type_idx) { return fa_alloc(fa, spl_type_size(tctx, type_idx)); } spl_val_t emit_raw(spl_emit_t *e, uint16_t opcode, uint16_t type, spl_val_t imm) { return spl_prog_emit(e->prog, opcode, type, imm); } /* ============================================================ * Layer 1 — single-instruction semantic wrappers * ============================================================ */ void emit_drop(spl_emit_t *e) { emit_raw(e, SPL_DROP, SPL_VOID, 0); } void emit_dup(spl_emit_t *e) { emit_raw(e, SPL_DUP, SPL_VOID, 0); } void emit_swap(spl_emit_t *e) { emit_raw(e, SPL_SWAP, SPL_VOID, 0); } void emit_rot(spl_emit_t *e) { emit_raw(e, SPL_ROT, SPL_VOID, 0); } void emit_pick(spl_emit_t *e, int n) { emit_raw(e, SPL_PICK, SPL_VOID, n); } void emit_push_i32(spl_emit_t *e, int v) { emit_raw(e, SPL_PUSH, SPL_I32, (spl_val_t)v); } void emit_push_usize(spl_emit_t *e, usize v) { emit_raw(e, SPL_PUSH, SPL_USIZE, (spl_val_t)v); } void emit_push_u64(spl_emit_t *e, uint64_t v) { emit_raw(e, SPL_PUSH, SPL_U64, (spl_val_t)v); } void emit_push_f64(spl_emit_t *e, uint64_t v) { emit_raw(e, SPL_PUSH, SPL_F64, (spl_val_t)v); } void emit_push_ptr(spl_emit_t *e, spl_val_t v) { emit_raw(e, SPL_PUSH, SPL_PTR, v); } void emit_push_type(spl_emit_t *e, uint16_t bt, spl_val_t v) { emit_raw(e, SPL_PUSH, bt, v); } void emit_load_ptr(spl_emit_t *e) { emit_raw(e, SPL_LOAD, SPL_PTR, 0); } void emit_load_usize(spl_emit_t *e) { emit_raw(e, SPL_LOAD, SPL_USIZE, 0); } void emit_load_type(spl_emit_t *e, uint16_t bt) { emit_raw(e, SPL_LOAD, bt, 0); } void emit_store_i32(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_I32, 0); } void emit_store_ptr(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_PTR, 0); } void emit_store_usize(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_USIZE, 0); } void emit_store_type(spl_emit_t *e, uint16_t bt) { emit_raw(e, SPL_STORE, bt, 0); } void emit_add_usize(spl_emit_t *e) { emit_raw(e, SPL_ADD, SPL_USIZE, 0); } void emit_add_u64(spl_emit_t *e) { emit_raw(e, SPL_ADD, SPL_U64, 0); } void emit_mul_u64(spl_emit_t *e) { emit_raw(e, SPL_MUL, SPL_U64, 0); } void emit_sub_usize(spl_emit_t *e) { emit_raw(e, SPL_SUB, SPL_USIZE, 0); } void emit_ult_usize(spl_emit_t *e) { emit_raw(e, SPL_ULT, SPL_USIZE, 0); } void emit_jmp(spl_emit_t *e, spl_val_t offset) { emit_raw(e, SPL_JMP, SPL_VOID, offset); } spl_val_t emit_jmp_here(spl_emit_t *e) { return emit_raw(e, SPL_JMP, SPL_VOID, 0); } spl_val_t emit_bz_here(spl_emit_t *e) { return emit_raw(e, SPL_BZ, SPL_VOID, 0); } spl_val_t emit_bnz_here(spl_emit_t *e) { return emit_raw(e, SPL_BNZ, SPL_VOID, 0); } void emit_patch(spl_emit_t *e, spl_val_t addr, spl_val_t target) { if (addr < vec_size(e->prog->insns)) vec_at(e->prog->insns, addr).imm = target; } void emit_patch_here(spl_emit_t *e, spl_val_t addr) { spl_val_t here = vec_size(e->prog->insns); emit_patch(e, addr, here - addr - 1); } void emit_laddr(spl_emit_t *e, int offset) { emit_raw(e, SPL_LADDR, SPL_PTR, offset); } void emit_gaddr(spl_emit_t *e, int idx) { emit_raw(e, SPL_GADDR, SPL_PTR, idx); } void emit_call(spl_emit_t *e, int nargs) { emit_raw(e, SPL_CALL, SPL_VOID, nargs); } void emit_ncall(spl_emit_t *e, int nargs) { emit_raw(e, SPL_NCALL, SPL_VOID, nargs); } void emit_alloc(spl_emit_t *e, int slots) { emit_raw(e, SPL_ALLOC, SPL_VOID, slots); } void emit_binop(spl_emit_t *e, uint16_t op, uint16_t bt) { emit_raw(e, op, bt, 0); } void emit_dbg_void(spl_emit_t *e) { emit_raw(e, SPL_DBG, SPL_VOID, 0); } void emit_dbg_usize(spl_emit_t *e) { emit_raw(e, SPL_DBG, SPL_USIZE, 0); } /* ============================================================ * Layer 2 — semantic-level helpers * ============================================================ */ void emit_frame_copy(spl_emit_t *e, int dest_offset, usize nslots) { for (usize i = 0; i < nslots; i++) { if (i < nslots - 1) emit_dup(e); if (i > 0) { emit_push_usize(e, i * sizeof(spl_val_t)); emit_add_usize(e); } emit_load_ptr(e); emit_laddr(e, dest_offset + (int)(i * sizeof(spl_val_t))); emit_swap(e); emit_store_ptr(e); } } void emit_copy_addr_to_addr(spl_emit_t *e, usize nslots, int depth) { for (usize i = 0; i < nslots; i++) { emit_dup(e); if (i > 0) { emit_push_usize(e, i * sizeof(spl_val_t)); emit_add_usize(e); } emit_load_ptr(e); emit_pick(e, 2 + depth); if (i > 0) { emit_push_usize(e, i * sizeof(spl_val_t)); emit_add_usize(e); } emit_swap(e); emit_store_ptr(e); } emit_drop(e); emit_drop(e); } void emit_load_to_var(spl_emit_t *e, spl_type_ctx_t *tctx, int ptr_slot_offset, usize byte_offset, int data_type_idx, int var_offset) { emit_laddr(e, ptr_slot_offset); emit_load_ptr(e); emit_ptr_add(e, byte_offset); if (data_type_idx >= 0 && !spl_type_is_scalar(tctx, data_type_idx) && spl_type_size(tctx, data_type_idx) > sizeof(spl_val_t)) { emit_frame_copy(e, var_offset, spl_type_slot_count(tctx, data_type_idx)); } else { uint16_t bt = spl_type_emit_type(tctx, data_type_idx); emit_load_type(e, bt); emit_laddr(e, var_offset); emit_swap(e); emit_store_type(e, bt); } } void emit_store_to_laddr(spl_emit_t *e, int offset, uint16_t type) { emit_laddr(e, offset); emit_swap(e); emit_store_type(e, type); } void emit_ptr_add(spl_emit_t *e, usize byte_off) { if (byte_off > 0) { emit_push_usize(e, byte_off); emit_add_usize(e); } } spl_val_t emit_call_with_fixup(spl_emit_t *e, int nargs, int func_idx) { spl_val_t fixup_addr = emit_raw(e, SPL_PUSH, SPL_PTR, 0); emit_call(e, nargs); spl_fixup_entry_t fe = {fixup_addr, func_idx}; vec_push(e->fixups, fe); return fixup_addr; } void emit_return(spl_emit_t *e, spl_type_ctx_t *tctx, int ret_type_idx) { if (spl_type_needs_multi_slot(tctx, ret_type_idx)) { emit_laddr(e, (int)sizeof(spl_val_t)); emit_raw(e, SPL_RET, SPL_PTR, 0); } else { uint16_t rt = spl_type_emit_type(tctx, ret_type_idx); emit_raw(e, SPL_RET, rt, 0); } } void emit_patch_call_fixups(spl_emit_t *e, spl_prog_t *prog) { for (usize i = 0; i < vec_size(e->fixups); i++) { spl_fixup_entry_t *fe = &vec_at(e->fixups, i); if (fe->func_idx >= 0 && fe->func_idx < (int)vec_size(prog->funcs)) { spl_val_t addr = vec_at(prog->funcs, fe->func_idx).address; vec_at(prog->insns, fe->insn_idx).imm = addr; } } } void spl_emit_ret(spl_comp_t *ctx, int ret_type_idx) { if (spl_type_needs_multi_slot(&ctx->tctx, ret_type_idx)) { emit_laddr(&ctx->emit, (int)sizeof(spl_val_t)); emit_swap(&ctx->emit); emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, ret_type_idx), 0); } emit_return(&ctx->emit, &ctx->tctx, ret_type_idx); } void spl_emit_store_init(spl_comp_t *ctx, int var_offset, int var_type_idx) { if (var_type_idx >= 0 && (spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_STRUCT || spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ENUM)) { usize sz = spl_type_size(&ctx->tctx, var_type_idx); if (sz <= sizeof(spl_val_t)) { emit_store_to_laddr(&ctx->emit, var_offset, spl_type_emit_type(&ctx->tctx, var_type_idx)); } else { usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); emit_frame_copy(&ctx->emit, var_offset, nslots); } } else if (var_type_idx >= 0 && spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ARRAY) { int elem_idx = spl_type_elem_type(&ctx->tctx, var_type_idx); spl_type_t bt = spl_type_emit_type(&ctx->tctx, elem_idx); usize stride = spl_type_elem_stride(&ctx->tctx, elem_idx); int arr_len = (int)spl_type_array_len(&ctx->tctx, var_type_idx); for (int i = arr_len - 1; i >= 0; i--) { emit_laddr(&ctx->emit, var_offset + (int)(i * stride)); emit_swap(&ctx->emit); if (elem_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, elem_idx)) { emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, elem_idx), 0); } else { emit_store_type(&ctx->emit, bt); } } } else if (var_type_idx >= 0 && spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_SLICE) { emit_store_to_laddr(&ctx->emit, var_offset + (int)sizeof(spl_val_t), SPL_USIZE); emit_store_to_laddr(&ctx->emit, var_offset, SPL_PTR); } else { spl_type_t bt = spl_type_emit_type(&ctx->tctx, var_type_idx); emit_store_to_laddr(&ctx->emit, var_offset, bt); } }