Files
spl/stage1/spl_ir2vm.c
T

1492 lines
48 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "spl_ir2vm.h"
#include "spl_layout.h"
static usize align_up(usize x, usize a);
/* size/align/offset 统一取自 layout(spl_layout 是唯一准确来源)。 */
static const spl_layout_info_t *ir_layout(spl_ir2vm_t *ctx, spl_type_id_t tid) {
return spl_layout_info_of(ctx->layout, tid);
}
static usize ir_type_size(spl_ir2vm_t *ctx, spl_type_id_t tid) {
const spl_layout_info_t *info = ir_layout(ctx, tid);
if (!info)
return 0;
return (info->size_bits + 7) / 8;
}
static usize ir_type_align(spl_ir2vm_t *ctx, spl_type_id_t tid) {
const spl_layout_info_t *info = ir_layout(ctx, tid);
if (!info)
return 1;
usize a = info->align_bits / 8;
return a ? a : 1;
}
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;
}
/* 从 fn_tid 取返回类型;非 fn 类型返回 0。
集中处理 fn 类型查表,避免调用点各自 spl_type_node + kind 检查。 */
static spl_type_id_t fn_ret_type(spl_ir2vm_t *ctx, spl_type_id_t fn_tid) {
spl_type_node_t *ftn = spl_type_node(ctx->type, fn_tid);
if (!ftn || ftn->kind != SPL_TYPE_FN)
return 0;
return ftn->fn_type.ret;
}
/* 从 fn_tid 取参数个数;非 fn 类型返回 0 */
static usize fn_nargs(spl_ir2vm_t *ctx, spl_type_id_t fn_tid) {
spl_type_node_t *ftn = spl_type_node(ctx->type, fn_tid);
if (!ftn || ftn->kind != SPL_TYPE_FN)
return 0;
return vec_size(ftn->fn_type.params);
}
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_ENUM) {
if (idx == SPL_ENUM_TAG_FIELD_IDX)
return tn->agg_type.tag_type;
usize vidx = idx - 1;
if (vidx < vec_size(tn->agg_type.variants))
return vec_at(tn->agg_type.variants, vidx).type_id;
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_ARRAY)
return idx * ir_type_size(ctx, tn->array_type.element);
const spl_layout_info_t *info = ir_layout(ctx, tid);
if (!info || idx >= vec_size(info->field_bit_offsets))
return 0;
return vec_at(info->field_bit_offsets, idx) / 8;
}
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;
}
}
/* 计算 IR 节点的"值类型"。
* 语义区分:
* mem.offset(elem_t, ptr, off) → 结果类型 *elem_t
* mem.field(agg_t, base, idx) → 结果类型 *field_t
* 这两个 builder 的 .tid 存的是 pointee / aggregate,不能直接当结果类型用。 */
static spl_type_id_t ir_result_tid(spl_ir2vm_t *ctx, 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 spl_type_builder_ptr((spl_type_t *)ctx->type, 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 spl_type_builder_ptr((spl_type_t *)ctx->type, n->mem_offset.tid);
case SPL_IR_MEM_FIELD_PTR: {
spl_type_id_t field =
ir_agg_field_tid(ctx, n->mem_field_ptr.tid, n->mem_field_ptr.field_idx);
return spl_type_builder_ptr((spl_type_t *)ctx->type, field);
}
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});
/* 聚合返回时 fp+0 是 caller 传入的 sret 指针,占用一个隐藏参数槽 */
usize nargs = fn_nargs(ctx, f->fn_tid);
usize hidden_sret = ir_is_aggregate(ctx, fn_ret_type(ctx, f->fn_tid)) ? 1 : 0;
fl->args_bytes = (nargs + hidden_sret) * 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(ctx, 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;
/* 块内栈化(opt) */
int opt;
uint8_t *stk_loc; /* per node: 1 = 值当前在操作数栈上 */
usize *stk_pos; /* 在虚拟栈中的位置 */
usize *use_left; /* 剩余使用次数 */
usize *at_pos; /* 每个栈位置对应的 node ref */
usize tracked; /* 当前栈上被跟踪的值个数 */
} 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);
}
/* ---------- 块内栈化(opt)辅助 ---------- */
static uint8_t node_result_kind(cgen_t *cg, usize ref) {
const spl_ir_node_t *n = &vec_at(cg->irf->nodes, ref);
return vm_kind_of(cg->ctx, ir_result_tid(cg->ctx, n));
}
static bool node_stackable(cgen_t *cg, 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_NEG:
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_NOT:
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:
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:
case SPL_IR_TYPE_CONST: {
spl_type_node_t *tn = spl_type_node(cg->ctx->type, n->type_const.tid);
return tn && tn->kind != SPL_TYPE_UNIT;
}
case SPL_IR_TYPE_BITSIZEOF:
case SPL_IR_TYPE_SIZEOF:
case SPL_IR_TYPE_ALIGNOF:
case SPL_IR_TYPE_OFFSETOF:
case SPL_IR_TYPE_FIELD_COUNT:
case SPL_IR_MEM_OFFSET:
case SPL_IR_MEM_FIELD_PTR:
return true;
case SPL_IR_MEM_LOAD:
return !ir_is_aggregate(cg->ctx, n->mem_load.tid);
default:
return false;
}
}
/* 该节点必须使用槽(直接按槽偏移读写 / 控制转移 / 聚合数据),生成前需 flush。 */
static bool node_needs_slots(cgen_t *cg, const spl_ir_node_t *n) {
switch (n->kind) {
case SPL_IR_MEM_ALLOCA:
case SPL_IR_MEM_GLOBAL_ALLOC:
case SPL_IR_MEM_COPY:
case SPL_IR_MEM_SET:
case SPL_IR_AGG_CONSTRUCT:
case SPL_IR_AGG_EXTRACT:
case SPL_IR_AGG_INSERT:
case SPL_IR_CONTROL_SELECT:
case SPL_IR_CONTROL_BR:
case SPL_IR_CONTROL_JMP:
case SPL_IR_CONTROL_RET:
case SPL_IR_CONTROL_UNREACHABLE:
case SPL_IR_CONTROL_TRAP:
case SPL_IR_DBG_PANIC:
case SPL_IR_DBG_BREAKPOINT:
return true;
case SPL_IR_MEM_LOAD:
return ir_is_aggregate(cg->ctx, n->mem_load.tid);
case SPL_IR_MEM_STORE:
return ir_is_aggregate(cg->ctx, n->mem_store.tid);
default:
return false;
}
}
/* 取操作数到 TOS:若在栈上就 PICK,否则从槽 load。j = 本节点已压入的操作数个数。 */
static void cg_operand(cgen_t *cg, usize ref, uint8_t k, usize j) {
if (cg->opt) {
if (cg->use_left[ref] > 0)
cg->use_left[ref]--;
if (cg->stk_loc[ref]) {
/* 始终复制一份到 TOS,绝不消费栈上的“被跟踪原值”,否则位置模型失效。 */
usize dist = cg->tracked + j - 1 - cg->stk_pos[ref];
emit(cg, SPL_PICK, SPL_VOID, dist);
return;
}
}
cg_load_slot(cg, ref, k);
}
/* 节点结果:若仍会被使用则留在栈上,否则 DROP。 */
static void cg_result(cgen_t *cg, usize ref, uint8_t k) {
if (cg->opt) {
if (cg->use_left[ref] > 0) {
cg->stk_loc[ref] = 1;
cg->stk_pos[ref] = cg->tracked;
cg->at_pos[cg->tracked] = ref;
cg->tracked++;
} else {
emit(cg, SPL_DROP, SPL_VOID, 0);
}
return;
}
cg_store_slot(cg, ref, k);
}
/* 把栈上仍存活的值溢出回各自槽(TOS 起),死值 DROP;清空跟踪。 */
static void cg_flush(cgen_t *cg) {
if (!cg->opt)
return;
while (cg->tracked > 0) {
cg->tracked--;
usize r = cg->at_pos[cg->tracked];
cg->stk_loc[r] = 0;
if (cg->use_left[r] > 0) {
uint8_t k = node_result_kind(cg, r);
cg_store_slot(cg, r, k);
} else {
emit(cg, SPL_DROP, SPL_VOID, 0);
}
}
}
/* 统计被引用次数(所有节点载荷中的 node ref)。 */
static void count_ref(cgen_t *cg, usize r) {
if (r != 0 && r < vec_size(cg->irf->nodes))
cg->use_left[r]++;
}
static void count_refs_vec(cgen_t *cg, const spl_ir_node_ref_vec_t *v) {
for (usize i = 0; i < vec_size(*v); i++)
count_ref(cg, vec_at(*v, i));
}
static void count_refs(cgen_t *cg, 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_NEG:
case SPL_IR_ARITH_ABS: 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_NOT:
count_ref(cg, n->arith.left);
count_ref(cg, n->arith.right);
break;
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:
count_ref(cg, n->cmp.a);
count_ref(cg, n->cmp.b);
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:
count_ref(cg, n->cast.val);
break;
case SPL_IR_MEM_ALLOCA:
count_ref(cg, n->mem_alloc.count);
break;
case SPL_IR_MEM_GLOBAL_ALLOC:
count_ref(cg, n->mem_global_alloc.const_node);
break;
case SPL_IR_MEM_LOAD:
count_ref(cg, n->mem_load.ptr);
break;
case SPL_IR_MEM_STORE:
count_ref(cg, n->mem_store.ptr);
count_ref(cg, n->mem_store.val);
break;
case SPL_IR_MEM_OFFSET:
count_ref(cg, n->mem_offset.ptr);
count_ref(cg, n->mem_offset.offset);
break;
case SPL_IR_MEM_FIELD_PTR:
count_ref(cg, n->mem_field_ptr.agg);
break;
case SPL_IR_MEM_COPY:
count_ref(cg, n->mem_copy.dst);
count_ref(cg, n->mem_copy.src);
count_ref(cg, n->mem_copy.size);
break;
case SPL_IR_MEM_SET:
count_ref(cg, n->mem_set.dst);
count_ref(cg, n->mem_set.val);
count_ref(cg, n->mem_set.size);
break;
case SPL_IR_TYPE_OFFSETOF:
count_ref(cg, n->ir_offsetof.field_idx);
break;
case SPL_IR_AGG_CONSTRUCT:
count_refs_vec(cg, &n->agg_construct.fields);
break;
case SPL_IR_AGG_EXTRACT:
count_ref(cg, n->agg_extract.val);
break;
case SPL_IR_AGG_INSERT:
count_ref(cg, n->agg_insert.agg);
count_ref(cg, n->agg_insert.field);
break;
case SPL_IR_CONTROL_SELECT:
count_ref(cg, n->control_select.cond);
count_ref(cg, n->control_select.true_val);
count_ref(cg, n->control_select.false_val);
break;
case SPL_IR_CONTROL_BR:
count_ref(cg, n->control_br.cond);
count_ref(cg, n->control_br.true_label);
count_ref(cg, n->control_br.false_label);
break;
case SPL_IR_CONTROL_JMP:
count_ref(cg, n->control_jmp.label);
break;
case SPL_IR_CONTROL_CALL:
count_ref(cg, n->control_call.func);
count_refs_vec(cg, &n->control_call.params);
break;
case SPL_IR_CONTROL_PARAM:
count_ref(cg, n->control_param.idx);
break;
case SPL_IR_CONTROL_RET:
count_ref(cg, n->control_ret.val);
break;
default:
break;
}
}
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_operand(cg, n->arith.left, k, 0);
cg_operand(cg, n->arith.right, k, 1);
CG_OP(cg, op, k);
cg_result(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_operand(cg, n->arith.left, k, 0);
CG_OP(cg, op, k);
cg_result(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_operand(cg, n->cmp.a, k, 0);
cg_operand(cg, n->cmp.b, k, 1);
CG_OP(cg, op, k);
cg_result(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_result(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_result(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_operand(cg, n->cast.val, fk, 0);
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_result(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_operand(cg, n->mem_load.ptr, SPL_PTR, 0);
CG_LOAD(cg, k);
cg_result(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_operand(cg, n->mem_store.ptr, SPL_PTR, 0);
cg_operand(cg, n->mem_store.val, k, 1);
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_operand(cg, n->mem_offset.ptr, SPL_PTR, 0);
cg_operand(cg, n->mem_offset.offset, SPL_U64, 1);
CG_PUSH(cg, SPL_U64, elem_size);
CG_OP(cg, SPL_MUL, SPL_U64);
CG_OP(cg, SPL_ADD, SPL_PTR);
cg_result(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_operand(cg, n->mem_field_ptr.agg, SPL_PTR, 0);
if (off) {
CG_PUSH(cg, SPL_U64, off);
CG_OP(cg, SPL_ADD, SPL_PTR);
}
cg_result(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: {
const spl_layout_info_t *info = ir_layout(ctx, n->bitsizeof.tid);
v = info ? info->size_bits : 0;
break;
}
case SPL_IR_TYPE_ALIGNOF:
v = ir_type_align(ctx, n->ir_alignof.tid);
break;
case SPL_IR_TYPE_OFFSETOF: {
usize idx = 0;
if (n->ir_offsetof.field_idx < vec_size(cg->irf->nodes)) {
const spl_ir_node_t *idx_node = &vec_at(cg->irf->nodes, n->ir_offsetof.field_idx);
if (idx_node->kind == SPL_IR_TYPE_CONST)
idx = (usize)idx_node->type_const.int_lit;
}
v = ir_agg_field_off(ctx, n->ir_offsetof.tid, idx);
break;
}
case SPL_IR_TYPE_FIELD_COUNT: {
spl_type_node_t *tn = spl_type_node(ctx->type, n->field_count.tid);
if (tn && (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_UNION ||
tn->kind == SPL_TYPE_ENUM || tn->kind == SPL_TYPE_SHAPE))
v = vec_size(tn->agg_type.variants);
break;
}
default:
return;
}
CG_PUSH(cg, SPL_U64, v);
cg_result(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;
}
/* 聚合返回时 fp+0 是 sret 指针,实参从 fp+8 起 */
usize shift = ir_is_aggregate(ctx, fn_ret_type(ctx, cg->irf->fn_tid)) ? 1 : 0;
CG_LADDR(cg, (idx + shift) * sizeof(spl_vm_val_t));
CG_LOAD(cg, k);
cg_store_slot(cg, ref, k);
break;
}
case SPL_IR_CONTROL_RET: {
spl_type_id_t ret_tid = n->control_ret.tid;
if (ir_is_aggregate(ctx, ret_tid)) {
/* sret:把返回值写入 *(fp+0),然后以 void 返回 */
if (n->control_ret.val) {
usize sz = ir_type_size(ctx, ret_tid);
CG_LADDR(cg, 0);
CG_LOAD(cg, SPL_PTR);
cg_load_slot(cg, n->control_ret.val, SPL_PTR);
CG_PUSH(cg, SPL_U64, sz);
int nat = get_or_add_native(ctx, "vm_memcpy");
CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat);
emit(cg, SPL_NCALL, SPL_VOID, 3);
emit(cg, SPL_DROP, SPL_VOID, 0);
}
emit(cg, SPL_RET, SPL_VOID, 0);
break;
}
uint8_t k = vm_kind_of(ctx, 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(ctx, pn);
uint8_t pk = vm_kind_of(ctx, ptid);
cg_operand(cg, pref, pk, i);
}
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 always push result */
else
cg_result(cg, ref, rk);
} else {
spl_type_id_t ret_t = n->control_call.tid;
bool ret_is_sret = ir_is_aggregate(ctx, ret_t);
usize sret_off = 0;
usize j = 0;
if (ret_is_sret) {
sret_off = vec_at(cg->frame.slots, ref).data_off;
CG_LADDR(cg, sret_off);
j = 1;
}
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(ctx, pn);
uint8_t pk = vm_kind_of(ctx, ptid);
cg_operand(cg, pref, pk, j);
j++;
}
cg_operand(cg, fref, SPL_PTR, j);
spl_vm_val_t actual_nargs = nargs + (ret_is_sret ? 1 : 0);
emit(cg, SPL_CALL, SPL_VOID, actual_nargs);
if (ret_is_sret) {
/* result slot saves sret pointer */
CG_LADDR(cg, sret_off);
cg_result(cg, ref, SPL_PTR);
} else {
uint8_t rk = vm_kind_of(ctx, ret_t);
if (rk != SPL_VOID)
cg_result(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_operand(cg, n->arith.left, SPL_BOOL, 0);
CG_PUSH(cg, SPL_BOOL, 0);
CG_OP(cg, SPL_EQ, SPL_BOOL);
cg_result(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:
case SPL_IR_TYPE_OFFSETOF:
case SPL_IR_TYPE_FIELD_COUNT:
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;
entry.nargs = fn_nargs(ctx, irf->fn_tid);
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);
unsigned char *leader = NULL;
cg.opt = ctx->opt;
if (ctx->opt) {
cg.stk_loc = (uint8_t *)calloc(nnodes, 1);
cg.stk_pos = (usize *)calloc(nnodes, sizeof(usize));
cg.use_left = (usize *)calloc(nnodes, sizeof(usize));
cg.at_pos = (usize *)calloc(nnodes, sizeof(usize));
leader = (unsigned char *)calloc(nnodes, 1);
cg.tracked = 0;
for (usize i = 1; i < nnodes; ++i)
count_refs(&cg, &vec_at(irf->nodes, i));
if (nnodes > 1)
leader[1] = 1;
for (usize i = 1; i < nnodes; ++i) {
const spl_ir_node_t *n = &vec_at(irf->nodes, i);
if (n->kind == SPL_IR_CONTROL_BR) {
if (n->control_br.true_label < nnodes)
leader[n->control_br.true_label] = 1;
if (n->control_br.false_label < nnodes)
leader[n->control_br.false_label] = 1;
} else if (n->kind == SPL_IR_CONTROL_JMP) {
if (n->control_jmp.label < nnodes)
leader[n->control_jmp.label] = 1;
}
}
}
for (usize i = 1; i < nnodes; ++i) {
const spl_ir_node_t *n = &vec_at(irf->nodes, i);
if (cg.opt) {
if (leader[i] || node_needs_slots(&cg, n))
cg_flush(&cg);
}
vec_at(cg.node_ip, i) = ip_of(&cg);
cg_node(&cg, i, n);
}
if (cg.opt) {
cg_flush(&cg);
free(cg.stk_loc);
free(cg.stk_pos);
free(cg.use_left);
free(cg.at_pos);
free(leader);
}
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,
spl_layout_t *layout, const spl_attr_t *attr, int opt) {
Assert(ir != NULL && type != NULL && attr != NULL);
ctx->ir = ir;
ctx->type = type;
ctx->attr = attr;
ctx->layout = layout;
ctx->opt = opt;
map_init(ctx->extern_native, spl_map_hash, spl_map_cmp);
spl_prog_init(&ctx->prog);
ctx->diag = NULL;
}
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);
}
}
/* 保留 insn 0 作为哨兵:确保真实函数入口地址非零,
从而 0 可唯一表示 null 函数指针(函数指针与 null 比较)。 */
spl_prog_add_instr(&ctx->prog, SPL_HALT, SPL_VOID, 0);
/* 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); }