1379 lines
42 KiB
Plaintext
1379 lines
42 KiB
Plaintext
// 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");
|
||
}
|