Files

1406 lines
42 KiB
Plaintext
Raw Permalink 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.
// Expect retcode = 0
// IR -> VM 字节码。extern_native 是按函数 ref 下标的 isize 数组:-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, .Global } = @import("./ir.spl");
type Mcode = @import("./mcode.spl");
type .{ .Prog, .Op, .VmKind, .VmIns, .VmFunc } = @import("./mcode.spl");
type Mem = @import("utils/memory.spl");
type Ir2Vm = @self;
ir: *Ir,
ty: *TypeTable,
attr: *Attr,
layout: *Layout,
extern_native: Vec, // isize
global_gaddr: Vec, // usize:每个全局变量在 prog.gdata 的下标
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) {
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;
}
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 };
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);
}
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;
}
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) {
emit(cg, .Laddr, .Void, slot_result(&cg.frame, ref));
emit(cg, .Load, k, 0);
}
fn cg_store_slot(cg: *Cgen, ref: usize, k: VmKind) {
emit(cg, .Laddr, .Void, slot_result(&cg.frame, ref));
emit(cg, .Swap, .Void, 0);
emit(cg, .Store, k, 0);
}
fn cg_store_at(cg: *Cgen, addr: usize, k: VmKind) {
emit(cg, .Laddr, .Void, addr);
emit(cg, .Swap, .Void, 0);
emit(cg, .Store, k, 0);
}
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,
};
Vec.push(cg.fixups, &fx as *u8);
}
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; }
// 全局变量引用:Const{ lit = Global(gidx) } → GADDR(该全局的运行时地址)。
match n.body {
.Const(c) => {
match c.lit {
.Global(g) => {
var di: usize = 0;
if g < ctx.global_gaddr.len { di = (ctx.global_gaddr.data as *usize)[g]; }
emit(cg, .Gaddr, .Void, di);
cg_store_slot(cg, ref, .Ptr);
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); },
.Any => { 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);
}
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);
emit(cg, .Memcpy, .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 {
/* ABI:实参下方预留 2 个空闲 slot(PAD0/PAD1)。 */
cg_push(cg, .U64, 0);
cg_push(cg, .U64, 0);
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);
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 {
var fx = (local_fixups.data as *Fix) + i;
match fx.kind {
.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;
}
},
.Fnaddr => { Vec.push(all_fixups, fx as *u8); },
}
i = i + 1;
}
Vec.drop(&cg.node_ip);
Vec.drop(&local_fixups);
frame_drop(&cg.frame);
ret func_ip;
}
// 就地初始化,避免返回含 Prog 的大聚合。
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));
ctx.global_gaddr = Vec.blank(@sizeof(usize), @alignof(usize));
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);
Vec.drop(&ctx.global_gaddr);
}
fn run_func(ctx: *Ir2Vm, ref: usize): i32 {
var all_fixups = Vec.blank(@sizeof(Fix), @alignof(Fix));
defer { Vec.drop(&all_fixups); }
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;
Vec.push(&ctx.extern_native, &v as *u8);
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;
}
// 登记全局变量到 prog.gdata(Zero→零填/BSS,Bytes→拷贝),记下运行时下标。
var gi: usize = 0;
while gi < ctx.ir.globals.len {
var g = (ctx.ir.globals.data as *Global) + gi;
var gsize = ir_type_size(ctx, g.tid);
var didx: usize = 0;
if g.zero {
didx = Mcode.add_zero_data(&ctx.prog, gsize);
} else {
didx = Mcode.add_data(&ctx.prog, g.data.data as usize, gsize);
}
var dv = didx;
Vec.push(&ctx.global_gaddr, &dv as *u8);
gi = gi + 1;
}
// 哨兵:保证真实函数入口地址非零。
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;
}
Vec.reserve(&ctx.prog.insns, est);
// 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);
}
// 序列化到内存 ByteBuffer(不落盘),供内嵌 VM 直接加载。
fn encode(ctx: *Ir2Vm, out: *ByteBuffer): bool {
match Mcode.encode(&ctx.prog, out) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
// 把翻译结果写成 .sir(二进制)。
fn store(ctx: *Ir2Vm, path: ByteView): bool {
match Mcode.store(&ctx.prog, path) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
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");
}