// Expect retcode = 0 // IR -> VM 机器码(移植自 stage1/spl_ir2vm.c)。 // 依赖:IR(ir.spl)、IR 类型表(type_table.spl)、布局(layout.spl)、 // 属性(attr.spl)、字节码容器(mcode.spl)。 // // extern_native 用「按函数 ref 下标的 isize 数组」代替 stage1 的 map: // 默认 -1 表示非 extern,>=0 表示 native 索引(语义等价)。 type .{ .Vec } = @import("utils/vec.spl"); type .{ .ByteView } = @import("utils/byte_view.spl"); type .{ .ByteBuffer } = @import("utils/byte_buffer.spl"); type TypeTable = @import("./type_table.spl"); type LayoutM = @import("./layout.spl"); type .{ .Layout } = @import("./layout.spl"); type AttrM = @import("./attr.spl"); type .{ .Attr, .AttrArg } = @import("./attr.spl"); type IrM = @import("./ir.spl"); type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl"); type Mcode = @import("./mcode.spl"); type .{ .Prog, .Op, .VmKind, .VmIns, .VmFunc } = @import("./mcode.spl"); type Mem = @import("sys/mem.spl"); type Ir2Vm = @self; ir: *Ir, ty: *TypeTable, attr: *Attr, layout: *Layout, extern_native: Vec, // isize prog: Prog, // ---- 类型/布局查询 ---- fn info_of(ctx: *Ir2Vm, tid: usize): *LayoutM.LayoutInfo { ret LayoutM.info_of(ctx.layout, tid); } fn ir_type_size(ctx: *Ir2Vm, tid: usize): usize { var info = info_of(ctx, tid); if info == null { ret 0; } ret (info.size_bits + 7) / 8; } fn ir_type_align(ctx: *Ir2Vm, tid: usize): usize { var info = info_of(ctx, tid); if info == null { ret 1; } var a = info.align_bits / 8; if a == 0 { a = 1; } ret a; } fn ir_is_aggregate(ctx: *Ir2Vm, tid: usize): bool { var te = TypeTable.node(ctx.ty, tid); if te == null { ret false; } match te.node { .Array => { ret true; }, .Struct => { ret true; }, .Union => { ret true; }, .Enum => { ret true; }, .Shape => { ret true; }, _ => { ret false; }, } ret false; } fn fn_ret_type(ctx: *Ir2Vm, fn_tid: usize): usize { var te = TypeTable.node(ctx.ty, fn_tid); if te == null { ret 0; } match te.node { .Fn(f) => { ret f.ret_type; }, _ => { ret 0; }, } ret 0; } fn fn_nargs(ctx: *Ir2Vm, fn_tid: usize): usize { var te = TypeTable.node(ctx.ty, fn_tid); if te == null { ret 0; } match te.node { .Fn(f) => { ret f.params.len; }, _ => { ret 0; }, } ret 0; } fn ir_agg_field_tid(ctx: *Ir2Vm, tid: usize, idx: usize): usize { var te = TypeTable.node(ctx.ty, tid); if te == null { ret 0; } match te.node { .Enum(agg) => { if idx == 0 { ret agg.tag_type; } var vidx = idx - 1; if vidx < agg.variants.len { var m = TypeTable.member_at(&agg.variants, vidx); if m != null { ret m.type_id; } } ret 0; }, .Struct(agg) => { if idx < agg.variants.len { var m = TypeTable.member_at(&agg.variants, idx); if m != null { ret m.type_id; } } ret 0; }, .Union(agg) => { if idx < agg.variants.len { var m = TypeTable.member_at(&agg.variants, idx); if m != null { ret m.type_id; } } ret 0; }, .Array(a) => { ret a.element; }, _ => { ret 0; }, } ret 0; } fn ir_agg_field_off(ctx: *Ir2Vm, tid: usize, idx: usize): usize { var te = TypeTable.node(ctx.ty, tid); if te == null { ret 0; } match te.node { .Array(a) => { ret idx * ir_type_size(ctx, a.element); }, _ => {}, } var info = info_of(ctx, tid); if info == null || idx >= info.field_bit_offsets.len { ret 0; } ret usize_at(&info.field_bit_offsets, idx) / 8; } fn vm_kind_of(ctx: *Ir2Vm, tid: usize): VmKind { var te = TypeTable.node(ctx.ty, tid); if te == null { ret .Void; } match te.node { .Unit => { ret .Void; }, .Bool => { ret .Bool; }, .Int(x) => { if x.bits == 0 { ret .I32; } if x.bits == 8 { if x.is_signed { ret .I8; } ret .U8; } if x.bits == 16 { if x.is_signed { ret .I16; } ret .U16; } if x.bits == 32 { if x.is_signed { ret .I32; } ret .U32; } if x.is_signed { ret .I64; } ret .U64; }, .Float(x) => { if x.bits == 32 { ret .F32; } ret .F64; }, _ => { ret .Ptr; }, } ret .Ptr; } fn tid_is_signed(ctx: *Ir2Vm, tid: usize): bool { var te = TypeTable.node(ctx.ty, tid); if te == null { ret false; } match te.node { .Int(x) => { ret x.is_signed; }, _ => { ret false; }, } ret false; } // 节点的「值类型」;0 表示无结果(控制/存储类)。 fn ir_result_tid(ctx: *Ir2Vm, n: *IrEntry): usize { match n.body { .Arith(a) => { ret a.tid; }, .Cmp(c) => { ret c.tid; }, .Cast(c) => { ret c.to_tid; }, .MemAlloc(m) => { ret TypeTable.make_ptr(ctx.ty, m.tid); }, .MemGlobalAlloc(m) => { ret m.tid; }, .MemLoad(m) => { ret m.tid; }, .MemOffset(m) => { ret TypeTable.make_ptr(ctx.ty, m.tid); }, .MemFieldPtr(m) => { var field = ir_agg_field_tid(ctx, m.tid, m.field_idx); ret TypeTable.make_ptr(ctx.ty, field); }, .Const(c) => { ret c.tid; }, .TypeOp(t) => { ret t.tid; }, .AggConstruct(a) => { ret a.tid; }, .AggExtract(a) => { ret a.field_tid; }, .AggInsert(a) => { ret a.tid; }, .Select(s) => { ret s.tid; }, .Call(c) => { ret c.tid; }, .Param(p) => { ret p.tid; }, _ => { ret 0; }, } ret 0; } // ---- 小工具 ---- // 内部热路径:假定索引有效,直接取址(省去 Vec.get/valid)。 fn usize_at(v: *Vec, i: usize): usize { ret (v.data as *usize)[i]; } fn push_usize(v: *Vec, x: usize) { ignore_bool(Vec.push_raw(v, &x as *u8)); } fn fnode(f: *IrFunc, i: usize): *IrEntry { ret (f.nodes.data as *IrEntry) + i; } fn align_up(x: usize, a: usize): usize { if a <= 1 { ret x; } ret (x + a - 1) / a * a; } fn const_usize(n: *IrEntry): usize { if n == null { ret 0; } match n.body { .Const(c) => { match c.lit { .Int(v) => { ret v; }, .Char(v) => { ret v as usize; }, .Bool(v) => { if v { ret 1; } ret 0; }, .Ptr(v) => { ret v; }, .Fn(v) => { ret v; }, .Float(v) => { ret 0; }, } }, _ => { ret 0; }, } ret 0; } fn const_fn(n: *IrEntry): usize { if n == null { ret 0; } match n.body { .Const(c) => { match c.lit { .Fn(v) => { ret v; }, _ => { ret 0; }, } }, _ => { ret 0; }, } ret 0; } // ---- extern ---- fn fn_check_extern(ctx: *Ir2Vm, f: *IrFunc): bool { if ctx.attr == null || f == null { ret false; } var i: usize = 0; while i < f.attrs.len { var a = AttrM.node(ctx.attr, usize_at(&f.attrs, i)); if a != null { var info = AttrM.info_at(ctx.attr, a.info_index); if info != null && ByteView.eq(info.name, ByteView.from_slice("extern")) { a.consumed_mask = a.consumed_mask | (1 as u32 << 2); ret true; } } i = i + 1; } ret false; } fn extern_get(ctx: *Ir2Vm, ref: usize): isize { if ref == 0 || ref >= ctx.extern_native.len { ret -1; } match Vec.get(&ctx.extern_native, ref) { .Ok(ptr) => { ret (ptr as *isize).*; }, .Err(error) => { ret -1; }, } ret -1; } fn extern_set(ctx: *Ir2Vm, ref: usize, value: isize) { if ref >= ctx.extern_native.len { ret; } match Vec.get(&ctx.extern_native, ref) { .Ok(ptr) => { (ptr as *isize).* = value; }, .Err(error) => {}, } } // ---- 帧布局 ---- type Slot = struct { result_off: usize, data_off: usize, } type FrameLayout = struct { slots: Vec, // Slot args_bytes: usize, frame_bytes: usize, } fn slot_at(frame: *FrameLayout, i: usize): *Slot { ret (frame.slots.data as *Slot) + i; } fn slot_result(frame: *FrameLayout, i: usize): usize { var s = slot_at(frame, i); if s == null { ret 0; } ret s.result_off; } fn slot_data(frame: *FrameLayout, i: usize): usize { var s = slot_at(frame, i); if s == null { ret 0; } ret s.data_off; } fn node_data_size(ctx: *Ir2Vm, f: *IrFunc, node: *IrEntry): usize { match node.body { .MemAlloc(m) => { var count: usize = 1; if m.count < f.nodes.len { var cn = fnode(f, m.count); count = const_usize(cn); } if count == 0 { count = 1; } var sz = ir_type_size(ctx, m.tid); if sz == 0 { sz = 1; } ret sz * count; }, .AggConstruct(a) => { var sz = ir_type_size(ctx, a.tid); if sz == 0 { ret 8; } ret sz; }, .AggInsert(a) => { var sz = ir_type_size(ctx, a.tid); if sz == 0 { ret 8; } ret sz; }, .AggExtract(a) => { if ir_is_aggregate(ctx, a.field_tid) { ret ir_type_size(ctx, a.field_tid); } ret 0; }, .MemLoad(m) => { if ir_is_aggregate(ctx, m.tid) { ret ir_type_size(ctx, m.tid); } ret 0; }, .Panic(p) => { ret @sizeof(usize) * 4; }, .Call(c) => { if ir_is_aggregate(ctx, c.tid) { ret ir_type_size(ctx, c.tid); } ret 0; }, _ => { ret 0; }, } ret 0; } fn frame_build(fl: *FrameLayout, ctx: *Ir2Vm, fid: usize) { fl.args_bytes = 0; fl.frame_bytes = 0; var f = IrM.func(ctx.ir, fid); if f == null { fl.slots = Vec.blank(@sizeof(Slot), @alignof(Slot)); ret; } var n = f.nodes.len; match Vec.with_capacity(@sizeof(Slot), @alignof(Slot), n) { .Ok(v) => { fl.slots = v; }, .Err(error) => { fl.slots = Vec.blank(@sizeof(Slot), @alignof(Slot)); }, } var i: usize = 0; while i < n { var blank: Slot = .{ .result_off = 0, .data_off = 0 }; ignore_bool(Vec.push_raw(&fl.slots, &blank as *u8)); i = i + 1; } var nargs = fn_nargs(ctx, f.fn_tid); var hidden_sret: usize = 0; if ir_is_aggregate(ctx, fn_ret_type(ctx, f.fn_tid)) { hidden_sret = 1; } fl.args_bytes = (nargs + hidden_sret) * @sizeof(usize); var off = fl.args_bytes; i = 1; while i < n { var node = fnode(f, i); if ir_result_tid(ctx, node) != 0 { var s = slot_at(fl, i); s.result_off = off; off = off + @sizeof(usize); } var extra = node_data_size(ctx, f, node); if extra != 0 { off = align_up(off, 8); var s2 = slot_at(fl, i); s2.data_off = off; off = off + align_up(extra, 8); } i = i + 1; } fl.frame_bytes = align_up(off, 8); } fn frame_drop(fl: *FrameLayout) { Vec.drop(&fl.slots); } // ---- cgen ---- type FixKind = enum { Jmp, Fnaddr, } type Fix = struct { kind: FixKind, ins_idx: usize, node_target: usize, fn_target: usize, } type Cgen = @self; ctx: *Ir2Vm, fid: usize, irf: *IrFunc, frame: FrameLayout, node_ip: Vec, // usize fixups: *Vec, terminated: bool, fn emit(cg: *Cgen, op: Op, ty: VmKind, imm: usize) { var prog = &cg.ctx.prog; var i = prog.insns.len; if i < prog.insns.cap { var p = (prog.insns.data as *VmIns) + i; p.opcode = op; p.ty = ty; p.imm = imm; prog.insns.len = i + 1; ret; } ignore_usize(Mcode.add_instr(prog, op, ty, imm)); } fn ip_of(cg: *Cgen): usize { ret cg.ctx.prog.insns.len; } fn ins_ptr(prog: *Prog, i: usize): *VmIns { ret (prog.insns.data as *VmIns) + i; } fn func_ptr(prog: *Prog, i: usize): *VmFunc { ret (prog.funcs.data as *VmFunc) + i; } fn cg_laddr(cg: *Cgen, off: usize) { emit(cg, .Laddr, .Void, off); } fn cg_load(cg: *Cgen, k: VmKind) { emit(cg, .Load, k, 0); } fn cg_store(cg: *Cgen, k: VmKind) { emit(cg, .Store, k, 0); } fn cg_push(cg: *Cgen, k: VmKind, v: usize) { emit(cg, .Push, k, v); } fn cg_op(cg: *Cgen, op: Op, k: VmKind) { emit(cg, op, k, 0); } fn cg_swap(cg: *Cgen) { emit(cg, .Swap, .Void, 0); } fn cg_gaddr(cg: *Cgen, idx: usize) { emit(cg, .Gaddr, .Void, idx); } fn cg_load_slot(cg: *Cgen, ref: usize, k: VmKind) { cg_laddr(cg, slot_result(&cg.frame, ref)); cg_load(cg, k); } fn cg_store_slot(cg: *Cgen, ref: usize, k: VmKind) { cg_laddr(cg, slot_result(&cg.frame, ref)); cg_swap(cg); cg_store(cg, k); } fn cg_store_at(cg: *Cgen, addr: usize, k: VmKind) { cg_laddr(cg, addr); cg_swap(cg); cg_store(cg, k); } type DataLoc = struct { indirect: bool, off: usize, base: usize, } fn direct_data(off: usize): DataLoc { ret DataLoc { .indirect = false, .off = off, .base = 0 }; } fn direct_data_base(off: usize, base: usize): DataLoc { ret DataLoc { .indirect = false, .off = off, .base = base }; } fn ptr_slot(slot_off: usize): DataLoc { ret DataLoc { .indirect = true, .off = slot_off, .base = 0 }; } fn ptr_slot_base(slot_off: usize, base: usize): DataLoc { ret DataLoc { .indirect = true, .off = slot_off, .base = base }; } fn cg_push_addr(cg: *Cgen, loc: DataLoc, extra: usize) { var delta = loc.base + extra; if loc.indirect { cg_laddr(cg, loc.off); cg_load(cg, .Ptr); if delta != 0 { cg_push(cg, .U64, delta); cg_op(cg, .Add, .Ptr); } } else { cg_laddr(cg, loc.off + delta); } } fn cg_copy_agg(cg: *Cgen, src: DataLoc, dst: DataLoc, tid: usize) { var sz = ir_type_size(cg.ctx, tid); var done: usize = 0; while sz - done >= 8 { cg_push_addr(cg, dst, done); cg_push_addr(cg, src, done); cg_load(cg, .U64); cg_store(cg, .U64); done = done + 8; } while done < sz { cg_push_addr(cg, dst, done); cg_push_addr(cg, src, done); cg_load(cg, .U8); cg_store(cg, .U8); done = done + 1; } } fn cg_add_fix(cg: *Cgen, ins_idx: usize, kind: FixKind, node_target: usize, fn_target: usize) { var fx = Fix { .kind = kind, .ins_idx = ins_idx, .node_target = node_target, .fn_target = fn_target, }; match Vec.push(cg.fixups, &fx as *u8) { .Ok => {}, .Err(error) => {} } } fn arith_tid(n: *IrEntry): usize { match n.body { .Arith(a) => { ret a.tid; }, _ => { ret 0; }, } ret 0; } fn cg_binop(cg: *Cgen, ref: usize, n: *IrEntry, op: Op) { match n.body { .Arith(a) => { var k = vm_kind_of(cg.ctx, a.tid); cg_load_slot(cg, a.left, k); cg_load_slot(cg, a.right, k); cg_op(cg, op, k); cg_store_slot(cg, ref, k); }, _ => {}, } } fn cg_unop(cg: *Cgen, ref: usize, n: *IrEntry, op: Op) { match n.body { .Arith(a) => { var k = vm_kind_of(cg.ctx, a.tid); cg_load_slot(cg, a.left, k); cg_op(cg, op, k); cg_store_slot(cg, ref, k); }, _ => {}, } } fn cg_cmp(cg: *Cgen, ref: usize, n: *IrEntry, op: Op) { match n.body { .Cmp(c) => { var k = vm_kind_of(cg.ctx, c.tid); cg_load_slot(cg, c.a, k); cg_load_slot(cg, c.b, k); cg_op(cg, op, k); cg_store_slot(cg, ref, .Bool); }, _ => {}, } } fn cg_const(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; var tid: usize = 0; match n.body { .Const(c) => { tid = c.tid; }, _ => { ret; } } var te = TypeTable.node(ctx.ty, tid); if te == null { ret; } match te.node { .Fn(f) => { var cref = const_fn(n); var nat_idx: isize = -1; if cref > 0 { nat_idx = extern_get(ctx, cref); } if nat_idx >= 0 { cg_push(cg, .U64, nat_idx as usize); } else { var idx = ip_of(cg); cg_push(cg, .Ptr, 0); cg_add_fix(cg, idx, .Fnaddr, 0, cref); } cg_store_slot(cg, ref, .Ptr); ret; }, .Unit => { ret; }, _ => {}, } var k = vm_kind_of(ctx, tid); var imm: usize = 0; match te.node { .Bool => { match n.body { .Const(c) => { match c.lit { .Bool(v) => { if v { imm = 1; } }, _ => {} } }, _ => {}, } }, .Int(x) => { imm = const_usize(n); }, .Float => { var fv: f64 = 0.0; match n.body { .Const(c) => { match c.lit { .Float(v) => { fv = v; }, _ => {} } }, _ => {}, } Mem.copy(&imm as *any, &fv as *any, 8); }, .Ptr => { imm = const_usize(n); }, _ => {}, } cg_push(cg, k, imm); cg_store_slot(cg, ref, k); } fn cg_cast(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; match n.body { .Cast(c) => { var ft = TypeTable.node(ctx.ty, c.from_tid); var tt = TypeTable.node(ctx.ty, c.to_tid); if ft == null || tt == null { ret; } var fk = vm_kind_of(ctx, c.from_tid); var tk = vm_kind_of(ctx, c.to_tid); cg_load_slot(cg, c.val, fk); var f_int = false; var f_bits: usize = 0; var f_signed = false; match ft.node { .Int(x) => { f_int = true; f_bits = x.bits; f_signed = x.is_signed; }, _ => {}, } var t_int = false; var t_bits: usize = 0; match tt.node { .Int(x) => { t_int = true; t_bits = x.bits; }, _ => {}, } var t_bool = false; match tt.node { .Bool => { t_bool = true; }, _ => {} } var f_bool = false; match ft.node { .Bool => { f_bool = true; }, _ => {} } if f_int && t_int { if f_bits > t_bits { emit(cg, .Trunc, .Void, t_bits); } else if f_bits < t_bits { if f_signed { emit(cg, .Sext, .Void, f_bits); } else { emit(cg, .Zext, .Void, f_bits); } } } else if f_bool && t_int { emit(cg, .Zext, .Void, 1); } ignore_bool(t_bool); cg_store_slot(cg, ref, tk); }, _ => {}, } } fn cg_mem(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; match n.body { .MemAlloc(m) => { cg_laddr(cg, slot_data(&cg.frame, ref)); cg_store_slot(cg, ref, .Ptr); }, .MemGlobalAlloc(m) => { var idx: usize = 0; if m.const_node < cg.irf.nodes.len { var cn = fnode(cg.irf, m.const_node); var pv: usize = 0; var ctid: usize = 0; match cn.body { .Const(c) => { ctid = c.tid; match c.lit { .Ptr(v) => { pv = v; }, _ => {} } }, _ => {}, } if pv != 0 { var size = ir_type_size(ctx, ctid); idx = Mcode.add_data(&ctx.prog, pv, size); } } if idx > 0 { emit(cg, .Gaddr, .Void, idx); } else { cg_push(cg, .Ptr, 0); } cg_store_slot(cg, ref, .Ptr); }, .MemLoad(m) => { var tid = m.tid; if ir_is_aggregate(ctx, tid) { var data = slot_data(&cg.frame, ref); cg_copy_agg(cg, ptr_slot(slot_result(&cg.frame, m.ptr)), direct_data(data), tid); cg_laddr(cg, data); cg_store_slot(cg, ref, .Ptr); } else { var k = vm_kind_of(ctx, tid); cg_load_slot(cg, m.ptr, .Ptr); cg_load(cg, k); cg_store_slot(cg, ref, k); } }, .MemStore(m) => { var tid = m.tid; if ir_is_aggregate(ctx, tid) { cg_copy_agg(cg, ptr_slot(slot_result(&cg.frame, m.val)), ptr_slot(slot_result(&cg.frame, m.ptr)), tid); } else { var k = vm_kind_of(ctx, tid); cg_load_slot(cg, m.ptr, .Ptr); cg_load_slot(cg, m.val, k); cg_store(cg, k); } }, .MemOffset(m) => { var elem_size = ir_type_size(ctx, m.tid); if elem_size == 0 { elem_size = 1; } cg_load_slot(cg, m.ptr, .Ptr); cg_load_slot(cg, m.offset, .U64); cg_push(cg, .U64, elem_size); cg_op(cg, .Mul, .U64); cg_op(cg, .Add, .Ptr); cg_store_slot(cg, ref, .Ptr); }, .MemFieldPtr(m) => { var off = ir_agg_field_off(ctx, m.tid, m.field_idx); cg_load_slot(cg, m.agg, .Ptr); if off != 0 { cg_push(cg, .U64, off); cg_op(cg, .Add, .Ptr); } cg_store_slot(cg, ref, .Ptr); }, _ => {}, } } fn cg_agg(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; match n.body { .AggConstruct(a) => { var base = slot_data(&cg.frame, ref); var agg_t = a.tid; var i: usize = 0; while i < a.fields.len { var fref = usize_at(&a.fields, i); var ftid = ir_agg_field_tid(ctx, agg_t, i); if ftid != 0 { var foff = ir_agg_field_off(ctx, agg_t, i); if ir_is_aggregate(ctx, ftid) { cg_copy_agg(cg, ptr_slot(slot_result(&cg.frame, fref)), direct_data_base(base, foff), ftid); } else { var fk = vm_kind_of(ctx, ftid); cg_load_slot(cg, fref, fk); cg_store_at(cg, base + foff, fk); } } i = i + 1; } cg_laddr(cg, base); cg_store_slot(cg, ref, .Ptr); }, .AggExtract(a) => { var agg_t = a.tid; var ftid = a.field_tid; var foff = ir_agg_field_off(ctx, agg_t, a.field_idx); if ir_is_aggregate(ctx, ftid) { var data = slot_data(&cg.frame, ref); cg_copy_agg(cg, ptr_slot_base(slot_result(&cg.frame, a.val), foff), direct_data(data), ftid); cg_laddr(cg, data); cg_store_slot(cg, ref, .Ptr); } else { var fk = vm_kind_of(ctx, ftid); cg_load_slot(cg, a.val, .Ptr); if foff != 0 { cg_push(cg, .U64, foff); cg_op(cg, .Add, .Ptr); } cg_load(cg, fk); cg_store_slot(cg, ref, fk); } }, .AggInsert(a) => { var base = slot_data(&cg.frame, ref); var agg_t = a.tid; cg_copy_agg(cg, ptr_slot(slot_result(&cg.frame, a.agg)), direct_data(base), agg_t); var foff = ir_agg_field_off(ctx, agg_t, a.field_idx); var ftid = ir_agg_field_tid(ctx, agg_t, a.field_idx); if ftid != 0 { if ir_is_aggregate(ctx, ftid) { cg_copy_agg(cg, ptr_slot(slot_result(&cg.frame, a.field)), direct_data_base(base, foff), ftid); } else { var fk = vm_kind_of(ctx, ftid); cg_load_slot(cg, a.field, fk); cg_store_at(cg, base + foff, fk); } } cg_laddr(cg, base); cg_store_slot(cg, ref, .Ptr); }, _ => {}, } } fn cg_typeop(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; var v: usize = 0; match n.body { .TypeOp(t) => { match n.kind { .TypeSizeof => { v = ir_type_size(ctx, t.tid); }, .TypeBitsizeof => { var info = info_of(ctx, t.tid); if info != null { v = info.size_bits; } }, .TypeAlignof => { v = ir_type_align(ctx, t.tid); }, .TypeOffsetof => { var idx: usize = 0; if t.field_idx < cg.irf.nodes.len { idx = const_usize(fnode(cg.irf, t.field_idx)); } v = ir_agg_field_off(ctx, t.tid, idx); }, .TypeFieldCount => { var te = TypeTable.node(ctx.ty, t.tid); if te != null { match te.node { .Struct(agg) => { v = agg.variants.len; }, .Union(agg) => { v = agg.variants.len; }, .Enum(agg) => { v = agg.variants.len; }, .Shape(agg) => { v = agg.variants.len; }, _ => {}, } } }, _ => { ret; }, } }, _ => { ret; }, } cg_push(cg, .U64, v); cg_store_slot(cg, ref, .U64); } fn cg_control(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; match n.body { .Param(p) => { var k = vm_kind_of(ctx, p.tid); var idx: usize = 0; if p.idx < cg.irf.nodes.len { idx = const_usize(fnode(cg.irf, p.idx)); } var shift: usize = 0; if ir_is_aggregate(ctx, fn_ret_type(ctx, cg.irf.fn_tid)) { shift = 1; } cg_laddr(cg, (idx + shift) * @sizeof(usize)); cg_load(cg, k); cg_store_slot(cg, ref, k); }, .Ret(r) => { var ret_tid = r.tid; if ir_is_aggregate(ctx, ret_tid) { if r.val != 0 { var sz = ir_type_size(ctx, ret_tid); cg_laddr(cg, 0); cg_load(cg, .Ptr); cg_load_slot(cg, r.val, .Ptr); cg_push(cg, .U64, sz); var nat = Mcode.get_or_add_native(&ctx.prog, ByteView.from_slice("vm_memcpy")); cg_push(cg, .U64, nat as usize); emit(cg, .Ncall, .Void, 3); emit(cg, .Drop, .Void, 0); } emit(cg, .Ret, .Void, 0); } else { var k = vm_kind_of(ctx, ret_tid); if @enum2int(k) != @enum2int(VmKind.Void) && r.val != 0 { cg_load_slot(cg, r.val, k); } emit(cg, .Ret, k, 0); } }, .Br(b) => { cg_load_slot(cg, b.cond, .Bool); var bz_idx = ip_of(cg); emit(cg, .Bz, .Void, 0); cg_add_fix(cg, bz_idx, .Jmp, b.false_label, 0); var jmp_idx = ip_of(cg); emit(cg, .Jmp, .Void, 0); cg_add_fix(cg, jmp_idx, .Jmp, b.true_label, 0); }, .Jmp(j) => { var idx = ip_of(cg); emit(cg, .Jmp, .Void, 0); cg_add_fix(cg, idx, .Jmp, j.label, 0); }, .Call(cl) => { var callee: usize = 0; if cl.func < cg.irf.nodes.len { callee = const_fn(fnode(cg.irf, cl.func)); } var nat_idx: isize = -1; if callee > 0 { nat_idx = extern_get(ctx, callee); } var nargs = cl.params.len; if nat_idx >= 0 { var i: usize = 0; while i < nargs { var pref = usize_at(&cl.params, i); var pn = fnode(cg.irf, pref); var pk = vm_kind_of(ctx, ir_result_tid(ctx, pn)); cg_load_slot(cg, pref, pk); i = i + 1; } cg_push(cg, .U64, nat_idx as usize); emit(cg, .Ncall, .Void, nargs); var rk = vm_kind_of(ctx, cl.tid); if @enum2int(rk) == @enum2int(VmKind.Void) { emit(cg, .Drop, .Void, 0); } else { cg_store_slot(cg, ref, rk); } } else { var ret_t = cl.tid; var ret_is_sret = ir_is_aggregate(ctx, ret_t); var sret_off: usize = 0; if ret_is_sret { sret_off = slot_data(&cg.frame, ref); cg_laddr(cg, sret_off); } var i: usize = 0; while i < nargs { var pref = usize_at(&cl.params, i); var pn = fnode(cg.irf, pref); var pk = vm_kind_of(ctx, ir_result_tid(ctx, pn)); cg_load_slot(cg, pref, pk); i = i + 1; } cg_load_slot(cg, cl.func, .Ptr); var actual = nargs; if ret_is_sret { actual = actual + 1; } emit(cg, .Call, .Void, actual); if ret_is_sret { cg_laddr(cg, sret_off); cg_store_slot(cg, ref, .Ptr); } else { var rk = vm_kind_of(ctx, ret_t); if @enum2int(rk) != @enum2int(VmKind.Void) { cg_store_slot(cg, ref, rk); } } } }, _ => {}, } } fn cg_node(cg: *Cgen, ref: usize, n: *IrEntry) { var ctx = cg.ctx; match n.kind { .TypeConst => { cg_const(cg, ref, n); }, .ArithAdd => { cg_binop(cg, ref, n, .Add); }, .ArithSub => { cg_binop(cg, ref, n, .Sub); }, .ArithMul => { cg_binop(cg, ref, n, .Mul); }, .ArithDiv => { if tid_is_signed(ctx, arith_tid(n)) { cg_binop(cg, ref, n, .DivS); } else { cg_binop(cg, ref, n, .DivU); } }, .ArithRem => { if tid_is_signed(ctx, arith_tid(n)) { cg_binop(cg, ref, n, .RemS); } else { cg_binop(cg, ref, n, .RemU); } }, .ArithAnd => { cg_binop(cg, ref, n, .And); }, .ArithOr => { cg_binop(cg, ref, n, .Or); }, .ArithXor => { cg_binop(cg, ref, n, .Xor); }, .ArithShl => { cg_binop(cg, ref, n, .Shl); }, .ArithShr => { if tid_is_signed(ctx, arith_tid(n)) { cg_binop(cg, ref, n, .ShrS); } else { cg_binop(cg, ref, n, .ShrU); } }, .ArithNeg => { cg_unop(cg, ref, n, .Neg); }, .ArithNot => { var tid = arith_tid(n); var te = TypeTable.node(ctx.ty, tid); var is_bool = false; if te != null { match te.node { .Bool => { is_bool = true; }, _ => {} } } if is_bool { match n.body { .Arith(a) => { cg_load_slot(cg, a.left, .Bool); }, _ => {} } cg_push(cg, .Bool, 0); cg_op(cg, .Eq, .Bool); cg_store_slot(cg, ref, .Bool); } else { cg_unop(cg, ref, n, .Not); } }, .ArithAbs => {}, .CmpEq => { cg_cmp(cg, ref, n, .Eq); }, .CmpNe => { cg_cmp(cg, ref, n, .Ne); }, .CmpLt => { var k = cmp_tid(n); if tid_is_signed(ctx, k) { cg_cmp(cg, ref, n, .Slt); } else { cg_cmp(cg, ref, n, .Ult); } }, .CmpLe => { var k = cmp_tid(n); if tid_is_signed(ctx, k) { cg_cmp(cg, ref, n, .Sle); } else { cg_cmp(cg, ref, n, .Ule); } }, .CmpGt => { var k = cmp_tid(n); if tid_is_signed(ctx, k) { cg_cmp(cg, ref, n, .Sgt); } else { cg_cmp(cg, ref, n, .Ugt); } }, .CmpGe => { var k = cmp_tid(n); if tid_is_signed(ctx, k) { cg_cmp(cg, ref, n, .Sge); } else { cg_cmp(cg, ref, n, .Uge); } }, .CastTrunc => { cg_cast(cg, ref, n); }, .CastZext => { cg_cast(cg, ref, n); }, .CastSext => { cg_cast(cg, ref, n); }, .CastFext => { cg_cast(cg, ref, n); }, .CastFtrunc => { cg_cast(cg, ref, n); }, .CastBitcast => { cg_cast(cg, ref, n); }, .CastPtr2int => { cg_cast(cg, ref, n); }, .CastInt2ptr => { cg_cast(cg, ref, n); }, .CastBool2int => { cg_cast(cg, ref, n); }, .CaseInt2float => { cg_cast(cg, ref, n); }, .CaseFloat2int => { cg_cast(cg, ref, n); }, .MemAlloca => { cg_mem(cg, ref, n); }, .MemGlobalAlloc => { cg_mem(cg, ref, n); }, .MemLoad => { cg_mem(cg, ref, n); }, .MemStore => { cg_mem(cg, ref, n); }, .MemOffset => { cg_mem(cg, ref, n); }, .MemFieldPtr => { cg_mem(cg, ref, n); }, .AggConstruct => { cg_agg(cg, ref, n); }, .AggExtract => { cg_agg(cg, ref, n); }, .AggInsert => { cg_agg(cg, ref, n); }, .TypeSizeof => { cg_typeop(cg, ref, n); }, .TypeBitsizeof => { cg_typeop(cg, ref, n); }, .TypeAlignof => { cg_typeop(cg, ref, n); }, .TypeOffsetof => { cg_typeop(cg, ref, n); }, .TypeFieldCount => { cg_typeop(cg, ref, n); }, .ControlParam => { cg_control(cg, ref, n); }, .ControlRet => { cg_control(cg, ref, n); }, .ControlBr => { cg_control(cg, ref, n); }, .ControlJmp => { cg_control(cg, ref, n); }, .ControlCall => { cg_control(cg, ref, n); }, .ControlUnreachable => { emit(cg, .Halt, .Void, 1); }, .ControlTrap => { emit(cg, .Halt, .Void, 1); }, .DbgPanic => { var data = slot_data(&cg.frame, ref); var msg = ByteView.empty(); match n.body { .Panic(p) => { msg = p.msg; }, _ => {} } var msg_idx = Mcode.add_data(&ctx.prog, msg.ptr as usize, msg.len); var fmt = ByteView.from_slice("%s"); var fmt_idx = Mcode.add_data(&ctx.prog, fmt.ptr as usize, 2); if msg_idx <= 0 || fmt_idx <= 0 { ret; } cg_gaddr(cg, fmt_idx); cg_store_at(cg, data + 0, .Ptr); cg_push(cg, .U64, 2); cg_store_at(cg, data + 8, .U64); cg_gaddr(cg, msg_idx); cg_store_at(cg, data + 16, .Ptr); cg_push(cg, .U64, msg.len); cg_store_at(cg, data + 24, .U64); cg_laddr(cg, data + 0); cg_laddr(cg, data + 16); var nat = Mcode.get_or_add_native(&ctx.prog, ByteView.from_slice("vm_printf")); cg_push(cg, .U64, nat as usize); emit(cg, .Ncall, .Void, 2); emit(cg, .Drop, .Void, 0); }, .DbgBreakpoint => { emit(cg, .Bk, .Void, 0); }, _ => {}, } } fn cmp_tid(n: *IrEntry): usize { match n.body { .Cmp(c) => { ret c.tid; }, _ => { ret 0; }, } ret 0; } // ---- 翻译 ---- fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize { var irf = IrM.func(ctx.ir, fid); if irf == null { ret 0; } var local_fixups = Vec.blank(@sizeof(Fix), @alignof(Fix)); var cg: Cgen = undefined; cg.ctx = ctx; cg.fid = fid; cg.irf = irf; cg.frame.slots = Vec.blank(@sizeof(Slot), @alignof(Slot)); cg.frame.args_bytes = 0; cg.frame.frame_bytes = 0; match Vec.with_capacity(@sizeof(usize), @alignof(usize), irf.nodes.len) { .Ok(v) => { cg.node_ip = v; }, .Err(error) => { cg.node_ip = Vec.blank(@sizeof(usize), @alignof(usize)); }, } cg.fixups = &local_fixups; cg.terminated = false; frame_build(&cg.frame, ctx, fid); var func_ip = ip_of(&cg); ignore_usize(Mcode.add_func(&ctx.prog, irf.name, fn_nargs(ctx, irf.fn_tid), func_ip)); var local_slots = (cg.frame.frame_bytes - cg.frame.args_bytes + 7) / 8; emit(&cg, .Alloc, .Void, local_slots); var nnodes = irf.nodes.len; var i: usize = 0; while i < nnodes { push_usize(&cg.node_ip, 0); i = i + 1; } i = 1; while i < nnodes { (cg.node_ip.data as *usize)[i] = ip_of(&cg); cg_node(&cg, i, fnode(irf, i)); i = i + 1; } i = 0; while i < local_fixups.len { match Vec.get(&local_fixups, i) { .Ok(ptr) => { var fx = ptr as *Fix; if @enum2int(fx.kind) == @enum2int(FixKind.Jmp) { var ins = ins_ptr(&ctx.prog, fx.ins_idx); if ins != null { var target_ip: usize = 0; if fx.node_target < nnodes { target_ip = usize_at(&cg.node_ip, fx.node_target); } ins.imm = target_ip - fx.ins_idx - 1; } } else if @enum2int(fx.kind) == @enum2int(FixKind.Fnaddr) { match Vec.push(all_fixups, ptr as *u8) { .Ok => {}, .Err(error) => {} } } }, .Err(error) => {}, } i = i + 1; } Vec.drop(&cg.node_ip); Vec.drop(&local_fixups); frame_drop(&cg.frame); ret func_ip; } // ---- 生命周期 / 入口 ---- // 就地初始化(避免返回含 Prog 的大聚合;对应 stage1 spl_ir2vm_init)。 fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) { ctx.ir = ir; ctx.ty = ty; ctx.attr = attr; ctx.layout = layout; ctx.extern_native = Vec.blank(@sizeof(isize), @alignof(isize)); match Mcode.new() { .Ok(v) => { ctx.prog = v; }, .Err(error) => { @assert(false, "ir2vm: prog init failed"); }, } } fn drop(ctx: *Ir2Vm) { Mcode.drop(&ctx.prog); Vec.drop(&ctx.extern_native); } fn run_func(ctx: *Ir2Vm, ref: usize): i32 { var all_fixups = Vec.blank(@sizeof(Fix), @alignof(Fix)); defer { Vec.drop(&all_fixups); } ignore_usize(translate_function(ctx, ref, &all_fixups)); ret 0; } fn run(ctx: *Ir2Vm): i32 { var n = ctx.ir.funcs.len; if n <= 1 { ret 0; } // Pass 1:登记 extern 函数 -> native 索引。 var i: usize = 0; while i < n { var v: isize = -1; match Vec.push(&ctx.extern_native, &v as *u8) { .Ok => {}, .Err(error) => {} } i = i + 1; } i = 1; while i < n { var irf = IrM.func(ctx.ir, i); if irf != null && fn_check_extern(ctx, irf) { var nat = Mcode.get_or_add_native(&ctx.prog, irf.name); extern_set(ctx, i, nat); } i = i + 1; } // 哨兵:保证真实函数入口地址非零。 ignore_usize(Mcode.add_instr(&ctx.prog, .Halt, .Void, 0)); // 预分配指令容量(按 IR 节点数粗略估计),减少逐条 append 的扩容/拷贝。 var est: usize = 16; i = 1; while i < n { var irf = IrM.func(ctx.ir, i); if irf != null { est = est + irf.nodes.len * 8 + 32; } i = i + 1; } match Vec.reserve(&ctx.prog.insns, est) { .Ok => {}, .Err(error) => {} } // Pass 2:翻译非 extern 函数。 var fid_ip = Vec.blank(@sizeof(usize), @alignof(usize)); i = 0; while i < n { push_usize(&fid_ip, 0); i = i + 1; } var all_fixups = Vec.blank(@sizeof(Fix), @alignof(Fix)); defer { Vec.drop(&all_fixups); } i = 1; while i < n { if extern_get(ctx, i) < 0 { var ip = translate_function(ctx, i, &all_fixups); (fid_ip.data as *usize)[i] = ip; } i = i + 1; } // FNADDR 回填。 i = 0; while i < all_fixups.len { match Vec.get(&all_fixups, i) { .Ok(ptr) => { var fx = ptr as *Fix; if @enum2int(fx.kind) == @enum2int(FixKind.Fnaddr) { var ins = ins_ptr(&ctx.prog, fx.ins_idx); if ins != null { var target: usize = 0; if fx.fn_target > 0 && fx.fn_target < n { target = usize_at(&fid_ip, fx.fn_target); } ins.imm = target; } } }, .Err(error) => {}, } i = i + 1; } // 每个函数的 ninsns = 下一个入口 - 本入口。 i = 0; while i < ctx.prog.funcs.len { var f = func_ptr(&ctx.prog, i); if f != null { var next = ctx.prog.insns.len; if i + 1 < ctx.prog.funcs.len { var nf = func_ptr(&ctx.prog, i + 1); if nf != null { next = nf.address; } } f.ninsns = next - f.address; } i = i + 1; } Vec.drop(&fid_ip); ret 0; } fn dump(ctx: *Ir2Vm, out: *ByteBuffer): bool { ret Mcode.dump(&ctx.prog, out); } // 把翻译结果写成 .sir(二进制)。 fn store(ctx: *Ir2Vm, path: ByteView): bool { match Mcode.store(&ctx.prog, path) { .Ok => { ret true; }, .Err(error) => { ret false; }, } ret false; } // ---- 工具 ---- fn ignore_usize(v: usize) {} fn ignore_bool(b: bool) {} // ---- 测试 ---- fn main() { var ir = IrM.new(); defer { IrM.drop(&ir); } var tt: TypeTable = undefined; match TypeTable.new() { .Ok(v) => { tt = v; }, .Err(error) => { @assert(false, "tt init"); ret; }, } defer { TypeTable.drop(&tt); } var layout: Layout = undefined; match LayoutM.new() { .Ok(v) => { layout = v; }, .Err(error) => { @assert(false, "layout init"); ret; }, } defer { LayoutM.drop(&layout); } var attr: Attr = undefined; match AttrM.new() { .Ok(v) => { attr = v; }, .Err(error) => { @assert(false, "attr init"); ret; }, } defer { AttrM.drop(&attr); } var ctx: Ir2Vm = undefined; init(&ctx, &ir, &tt, &layout, &attr); defer { drop(&ctx); } var rc = run(&ctx); @assert(rc == 0, "run empty ir"); // 只有哨兵函数:无指令。 @assert(ctx.prog.insns.len == 0, "empty ir yields no insns"); }