1207 lines
39 KiB
C
1207 lines
39 KiB
C
#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;
|
||
} 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: {
|
||
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_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;
|
||
}
|
||
/* 聚合返回时 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_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 {
|
||
spl_type_id_t ret_t = n->control_call.tid;
|
||
bool ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
||
usize sret_off = 0;
|
||
|
||
if (ret_is_sret) {
|
||
sret_off = vec_at(cg->frame.slots, ref).data_off;
|
||
CG_LADDR(cg, sret_off);
|
||
}
|
||
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_load_slot(cg, pref, pk);
|
||
}
|
||
cg_load_slot(cg, fref, SPL_PTR);
|
||
spl_vm_val_t actual_nargs = nargs + (ret_is_sret ? 1 : 0);
|
||
emit(cg, SPL_CALL, SPL_VOID, actual_nargs);
|
||
|
||
if (ret_is_sret) {
|
||
/* 结果槽保存 sret 指针,后续 agg_extract 把它当聚合首地址 */
|
||
CG_LADDR(cg, sret_off);
|
||
cg_store_slot(cg, ref, SPL_PTR);
|
||
} else {
|
||
uint8_t rk = vm_kind_of(ctx, ret_t);
|
||
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:
|
||
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);
|
||
|
||
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,
|
||
spl_layout_t *layout, const spl_attr_t *attr) {
|
||
Assert(ir != NULL && type != NULL && attr != NULL);
|
||
ctx->ir = ir;
|
||
ctx->type = type;
|
||
ctx->attr = attr;
|
||
ctx->layout = layout;
|
||
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); }
|