stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮

This commit is contained in:
zzy committed 2026-09-19 18:08:43 +08:00
1 parent b5a7a811b3
commit e02b0c633a
35 files changed
+14018 -216

No files matched your search

+605
View File
@@ -0,0 +1,605 @@
// Expect retcode = 0
// SIR 中间表示(移植自 stage1/spl_ir.c/.h)。节点用 arena 序号引用(0 = error),
// 按函数分桶。节点公共字段(kind/dbg/attrs)在外层 entry,kind 专属载荷在 ADT
// IrBody;kind 与载荷变体一一对应(多种 kind 可共享同一载荷形状)。
//
// builder 维护当前函数/基本块/fixup 表,负责发射节点与回填跳转。
type .{ .Error } = @import("utils/error.spl");
type .{ .Vec } = @import("utils/vec.spl");
type .{ .ByteView } = @import("utils/byte_view.spl");
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
type .{ .Span, .span_none } = @import("./diag.spl");
type TypeTable = @import("./type_table.spl");
type Attr = @import("./attr.spl");
type IrKind = enum {
ArithAdd,
ArithSub,
ArithMul,
ArithDiv,
ArithRem,
ArithNeg,
ArithAbs,
ArithAnd,
ArithOr,
ArithXor,
ArithShl,
ArithShr,
ArithNot,
CmpEq,
CmpNe,
CmpLt,
CmpLe,
CmpGt,
CmpGe,
CastTrunc,
CastZext,
CastSext,
CastFext,
CastFtrunc,
CastBitcast,
CastPtr2int,
CastInt2ptr,
CastBool2int,
CaseInt2float,
CaseFloat2int,
MemAlloca,
MemGlobalAlloc,
MemLoad,
MemStore,
MemOffset,
MemFieldPtr,
MemCopy,
MemSet,
TypeConst,
TypeBitsizeof,
TypeSizeof,
TypeAlignof,
TypeOffsetof,
TypeFieldCount,
AggConstruct,
AggExtract,
AggInsert,
ControlSelect,
ControlBr,
ControlJmp,
ControlCall,
ControlParam,
ControlRet,
ControlUnreachable,
ControlTrap,
DbgBreakpoint,
DbgPanic,
}
type ConstLit = enum {
Bool: bool,
Ptr: usize,
Int: usize,
Float: f64,
Char: u8,
Fn: usize,
}
type IrBody = enum {
None,
Arith: struct { tid: usize, left: usize, right: usize },
Cmp: struct { tid: usize, a: usize, b: usize },
Cast: struct { from_tid: usize, to_tid: usize, val: usize },
MemAlloc: struct { tid: usize, count: usize },
MemGlobalAlloc: struct { tid: usize, const_node: usize },
MemLoad: struct { tid: usize, ptr: usize },
MemStore: struct { tid: usize, ptr: usize, val: usize },
MemOffset: struct { tid: usize, ptr: usize, offset: usize },
MemFieldPtr: struct { tid: usize, agg: usize, field_idx: usize },
MemCopy: struct { tid: usize, dst: usize, src: usize, size: usize },
MemSet: struct { tid: usize, dst: usize, val: usize, size: usize },
Const: struct { tid: usize, lit: ConstLit },
TypeOp: struct { tid: usize, field_idx: usize },
AggConstruct: struct { tid: usize, fields: Vec },
AggExtract: struct { tid: usize, field_tid: usize, field_idx: usize, val: usize },
AggInsert: struct { tid: usize, field_idx: usize, agg: usize, field: usize },
Select: struct { tid: usize, cond: usize, true_val: usize, false_val: usize },
Br: struct { cond: usize, true_label: usize, false_label: usize },
Jmp: struct { label: usize },
Call: struct { tid: usize, func: usize, params: Vec },
Param: struct { tid: usize, idx: usize },
Ret: struct { tid: usize, val: usize },
Panic: struct { msg: ByteView },
}
type IrEntry = struct {
kind: IrKind,
dbg: Span,
attrs: Vec, // attr id
body: IrBody,
}
type IrFunc = struct {
name: ByteView,
attrs: Vec, // attr id
fn_tid: usize,
nodes: Vec, // IrEntry
}
type Ir = @self;
funcs: Vec, // IrFunc
// ---- 生命周期 ----
fn new_entry(): IrEntry {
ret IrEntry {
.kind = .ArithAdd,
.dbg = span_none(),
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
.body = .None,
};
}
fn new(): Ir {
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)) };
var sentinel = IrFunc {
.name = ByteView.empty(),
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
.fn_tid = 0,
.nodes = Vec.blank(@sizeof(IrEntry), @alignof(IrEntry)),
};
var sn = new_entry();
match Vec.push(&sentinel.nodes, &sn as *u8) { .Ok => {}, .Err(error) => {} }
match Vec.push(&ir.funcs, &sentinel as *u8) { .Ok => {}, .Err(error) => {} }
ret ir;
}
fn drop_body(body: IrBody) {
match body {
.AggConstruct(a) => { Vec.drop(&a.fields); },
.Call(c) => { Vec.drop(&c.params); },
_ => {},
}
}
fn drop(self: *Ir) {
var i: usize = 0;
while i < self.funcs.len {
match Vec.get(&self.funcs, i) {
.Ok(ptr) => {
var f = ptr as *IrFunc;
var j: usize = 0;
while j < f.nodes.len {
match Vec.get(&f.nodes, j) {
.Ok(np) => {
var e = np as *IrEntry;
Vec.drop(&e.attrs);
drop_body(e.body);
},
.Err(error) => {},
}
j = j + 1;
}
Vec.drop(&f.attrs);
Vec.drop(&f.nodes);
},
.Err(error) => {},
}
i = i + 1;
}
Vec.drop(&self.funcs);
}
fn alloc_fn(self: *Ir): usize {
var f = IrFunc {
.name = ByteView.empty(),
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
.fn_tid = 0,
.nodes = Vec.blank(@sizeof(IrEntry), @alignof(IrEntry)),
};
var sn = new_entry();
match Vec.push(&f.nodes, &sn as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
match Vec.push(&self.funcs, &f as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
ret self.funcs.len - 1;
}
fn alloc_node(self: *Ir, fn_id: usize): usize {
if fn_id == 0 || fn_id >= self.funcs.len { ret 0; }
match Vec.get(&self.funcs, fn_id) {
.Ok(ptr) => {
var f = ptr as *IrFunc;
var n = new_entry();
match Vec.push(&f.nodes, &n as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
ret f.nodes.len - 1;
},
.Err(error) => {},
}
ret 0;
}
fn func(self: *Ir, fn_id: usize): *IrFunc {
if fn_id == 0 || fn_id >= self.funcs.len { ret null; }
match Vec.get(&self.funcs, fn_id) {
.Ok(ptr) => { ret ptr as *IrFunc; },
.Err(error) => { ret null; },
}
ret null;
}
fn node(self: *Ir, fn_id: usize, node_id: usize): *IrEntry {
var f = func(self, fn_id);
if f == null { ret null; }
if node_id == 0 || node_id >= f.nodes.len { ret null; }
match Vec.get(&f.nodes, node_id) {
.Ok(ptr) => { ret ptr as *IrEntry; },
.Err(error) => { ret null; },
}
ret null;
}
fn kind_name(kind: IrKind): []u8 {
match kind {
.ArithAdd => { ret "arith.add"; },
.ArithSub => { ret "arith.sub"; },
.ArithMul => { ret "arith.mul"; },
.ArithDiv => { ret "arith.div"; },
.ArithRem => { ret "arith.rem"; },
.ArithNeg => { ret "arith.neg"; },
.ArithAbs => { ret "arith.abs"; },
.ArithAnd => { ret "arith.and"; },
.ArithOr => { ret "arith.or"; },
.ArithXor => { ret "arith.xor"; },
.ArithShl => { ret "arith.shl"; },
.ArithShr => { ret "arith.shr"; },
.ArithNot => { ret "arith.not"; },
.CmpEq => { ret "cmp.eq"; },
.CmpNe => { ret "cmp.ne"; },
.CmpLt => { ret "cmp.lt"; },
.CmpLe => { ret "cmp.le"; },
.CmpGt => { ret "cmp.gt"; },
.CmpGe => { ret "cmp.ge"; },
.CastTrunc => { ret "cast.trunc"; },
.CastZext => { ret "cast.zext"; },
.CastSext => { ret "cast.sext"; },
.CastFext => { ret "cast.fext"; },
.CastFtrunc => { ret "cast.ftrunc"; },
.CastBitcast => { ret "cast.bitcast"; },
.CastPtr2int => { ret "cast.ptr2int"; },
.CastInt2ptr => { ret "cast.int2ptr"; },
.CastBool2int => { ret "cast.bool2int"; },
.CaseInt2float => { ret "case.int2float"; },
.CaseFloat2int => { ret "case.float2int"; },
.MemAlloca => { ret "mem.alloca"; },
.MemGlobalAlloc => { ret "mem.global_alloc"; },
.MemLoad => { ret "mem.load"; },
.MemStore => { ret "mem.store"; },
.MemOffset => { ret "mem.offset"; },
.MemFieldPtr => { ret "mem.field"; },
.MemCopy => { ret "mem.copy"; },
.MemSet => { ret "mem.set"; },
.TypeConst => { ret "type.const"; },
.TypeBitsizeof => { ret "type.bitsizeof"; },
.TypeSizeof => { ret "type.sizeof"; },
.TypeAlignof => { ret "type.alignof"; },
.TypeOffsetof => { ret "type.offsetof"; },
.TypeFieldCount => { ret "type.field_count"; },
.AggConstruct => { ret "agg.construct"; },
.AggExtract => { ret "agg.extract"; },
.AggInsert => { ret "agg.insert"; },
.ControlSelect => { ret "control.select"; },
.ControlBr => { ret "control.br"; },
.ControlJmp => { ret "control.jmp"; },
.ControlCall => { ret "control.call"; },
.ControlParam => { ret "control.param"; },
.ControlRet => { ret "control.ret"; },
.ControlUnreachable => { ret "control.unreachable"; },
.ControlTrap => { ret "control.trap"; },
.DbgBreakpoint => { ret "dbg.breakpoint"; },
.DbgPanic => { ret "dbg.panic"; },
}
ret "?";
}
// ---- dump ----
fn out_str(out: *ByteBuffer, s: []u8): bool {
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
ret false;
}
fn out_u(out: *ByteBuffer, v: usize): bool {
match append_usize(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
ret false;
}
fn out_i(out: *ByteBuffer, v: i64): bool {
match append_i64(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
ret false;
}
fn dump_type(out: *ByteBuffer, ty: *TypeTable, tid: usize): bool {
if !out_str(out, "(") { ret false; }
if !TypeTable.pure_dump(ty, tid, out) { ret false; }
ret out_str(out, ")");
}
fn dump_ref(out: *ByteBuffer, ref: usize): bool {
if ref == 0 { ret out_str(out, " _"); }
if !out_str(out, " %") { ret false; }
ret out_u(out, ref);
}
fn dump_attrs(out: *ByteBuffer, attr: *Attr, ids: Vec): bool {
if ids.len == 0 { ret true; }
if !out_str(out, " attrs=[") { ret false; }
var i: usize = 0;
while i < ids.len {
if i > 0 && !out_str(out, ", ") { ret false; }
var id = list_at(ids, i);
if !Attr.dump_id(attr, id, out) { ret false; }
i = i + 1;
}
ret out_str(out, "]");
}
fn list_at(v: Vec, i: usize): usize {
match Vec.get(&v, i) { .Ok(p) => { ret (p as *usize).*; }, .Err(e) => { ret 0; } }
ret 0;
}
fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
match e.body {
.None => { ret true; },
.Arith(a) => {
if !dump_type(out, ty, a.tid) { ret false; }
if !dump_ref(out, a.left) { ret false; }
// 一元 arith(neg/abs/not)不打印 right(镜像 stage1)。
if @enum2int(e.kind) == @enum2int(IrKind.ArithNeg) ||
@enum2int(e.kind) == @enum2int(IrKind.ArithAbs) ||
@enum2int(e.kind) == @enum2int(IrKind.ArithNot) {
ret true;
}
ret dump_ref(out, a.right);
},
.Cmp(c) => {
if !dump_type(out, ty, c.tid) { ret false; }
if !dump_ref(out, c.a) { ret false; }
ret dump_ref(out, c.b);
},
.Cast(c) => {
if !out_str(out, " ") { ret false; }
if !TypeTable.pure_dump(ty, c.from_tid, out) { ret false; }
if !out_str(out, " -> ") { ret false; }
if !TypeTable.pure_dump(ty, c.to_tid, out) { ret false; }
ret dump_ref(out, c.val);
},
.MemAlloc(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
ret dump_ref(out, m.count);
},
.MemGlobalAlloc(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
if !out_str(out, " g#") { ret false; }
ret out_u(out, m.const_node);
},
.MemLoad(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
ret dump_ref(out, m.ptr);
},
.MemStore(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
if !dump_ref(out, m.ptr) { ret false; }
ret dump_ref(out, m.val);
},
.MemOffset(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
if !dump_ref(out, m.ptr) { ret false; }
ret dump_ref(out, m.offset);
},
.MemFieldPtr(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
if !dump_ref(out, m.agg) { ret false; }
if !out_str(out, " #") { ret false; }
ret out_u(out, m.field_idx);
},
.MemCopy(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
if !dump_ref(out, m.dst) { ret false; }
if !dump_ref(out, m.src) { ret false; }
ret dump_ref(out, m.size);
},
.MemSet(m) => {
if !dump_type(out, ty, m.tid) { ret false; }
if !dump_ref(out, m.dst) { ret false; }
if !dump_ref(out, m.val) { ret false; }
ret dump_ref(out, m.size);
},
.Const(c) => {
if !dump_type(out, ty, c.tid) { ret false; }
var te = TypeTable.node(ty, c.tid);
var is_float = false;
var is_fn = false;
var is_u8 = false;
var is_arr = false;
if te != null {
match te.node {
.Float(x) => { is_float = true; },
.Fn(x) => { is_fn = true; },
.Array(x) => { is_arr = true; },
.Int(x) => { if x.bits == 8 { is_u8 = true; } },
_ => {},
}
}
if is_float {
ret out_str(out, " float=?");
}
if is_arr {
ret out_str(out, " bytes");
}
if is_fn {
if !out_str(out, " fn#") { ret false; }
match c.lit { .Fn(v) => { ret out_u(out, v); }, _ => { ret out_str(out, "?"); } }
}
if is_u8 {
var cv: usize = 0;
match c.lit {
.Char(v) => { cv = v as usize; },
.Int(v) => { cv = v; },
_ => {},
}
if !out_str(out, " char='") { ret false; }
match ByteBuffer.append_byte(out, cv as u8) { .Ok => {}, .Err(error) => { ret false; } }
ret out_str(out, "'");
}
// stage1 对其他常量统一按 int_lit 打印(union 别名)。
var iv: usize = 0;
match c.lit {
.Int(v) => { iv = v; },
.Bool(v) => { if v { iv = 1; } },
.Ptr(v) => { iv = v; },
.Char(v) => { iv = v as usize; },
.Fn(v) => { iv = v; },
.Float(v) => {},
}
if !out_str(out, " int=") { ret false; }
ret out_u(out, iv);
},
.TypeOp(t) => {
if !dump_type(out, ty, t.tid) { ret false; }
if @enum2int(e.kind) == @enum2int(IrKind.TypeOffsetof) {
ret dump_ref(out, t.field_idx);
}
ret true;
},
.AggConstruct(a) => {
if !dump_type(out, ty, a.tid) { ret false; }
var i: usize = 0;
while i < a.fields.len {
if !dump_ref(out, list_at(a.fields, i)) { ret false; }
i = i + 1;
}
ret true;
},
.AggExtract(a) => {
if !dump_type(out, ty, a.tid) { ret false; }
if !out_str(out, " #") { ret false; }
if !out_u(out, a.field_idx) { ret false; }
ret dump_ref(out, a.val);
},
.AggInsert(a) => {
if !dump_type(out, ty, a.tid) { ret false; }
if !out_str(out, " #") { ret false; }
if !out_u(out, a.field_idx) { ret false; }
if !dump_ref(out, a.agg) { ret false; }
ret dump_ref(out, a.field);
},
.Select(s) => {
if !dump_type(out, ty, s.tid) { ret false; }
if !dump_ref(out, s.cond) { ret false; }
if !dump_ref(out, s.true_val) { ret false; }
ret dump_ref(out, s.false_val);
},
.Br(b) => {
if !dump_ref(out, b.cond) { ret false; }
if !dump_ref(out, b.true_label) { ret false; }
ret dump_ref(out, b.false_label);
},
.Jmp(j) => { ret dump_ref(out, j.label); },
.Call(c) => {
if !dump_type(out, ty, c.tid) { ret false; }
if !dump_ref(out, c.func) { ret false; }
var i: usize = 0;
while i < c.params.len {
if !dump_ref(out, list_at(c.params, i)) { ret false; }
i = i + 1;
}
ret true;
},
.Param(p) => {
if !dump_type(out, ty, p.tid) { ret false; }
ret dump_ref(out, p.idx);
},
.Ret(r) => {
if !dump_type(out, ty, r.tid) { ret false; }
ret dump_ref(out, r.val);
},
.Panic(p) => {
if !out_str(out, " \"") { ret false; }
if !out_str(out, ByteView.as_slice(p.msg)) { ret false; }
ret out_str(out, "\"");
},
}
ret false;
}
fn dump(self: *Ir, ty: *TypeTable, attr: *Attr, out: *ByteBuffer): bool {
var i: usize = 1;
while i < self.funcs.len {
var f = func(self, i);
if f == null { i = i + 1; continue; }
if !out_str(out, "func ") { ret false; }
if f.name.len == 0 {
if !out_str(out, "?") { ret false; }
} else {
if !out_str(out, ByteView.as_slice(f.name)) { ret false; }
}
if !out_str(out, " %") { ret false; }
if !out_u(out, i) { ret false; }
if !out_str(out, " tid = ") { ret false; }
if !TypeTable.pure_dump(ty, f.fn_tid, out) { ret false; }
if !dump_attrs(out, attr, f.attrs) { ret false; }
if !out_str(out, "\n") { ret false; }
var j: usize = 1;
while j < f.nodes.len {
var e = node(self, i, j);
if e == null { j = j + 1; continue; }
if !out_str(out, " #") { ret false; }
if !out_u(out, j) { ret false; }
if !out_str(out, " ") { ret false; }
if !out_str(out, kind_name(e.kind)) { ret false; }
if !dump_body(out, ty, e) { ret false; }
if !dump_attrs(out, attr, e.attrs) { ret false; }
if !out_str(out, "\n") { ret false; }
j = j + 1;
}
i = i + 1;
}
ret true;
}
// ---- 测试 ----
fn test_kinds() {
@assert(ByteView.eq(ByteView.from_slice(kind_name(.ArithAdd)), ByteView.from_slice("arith.add")),
"arith.add name");
@assert(ByteView.eq(ByteView.from_slice(kind_name(.ControlCall)),
ByteView.from_slice("control.call")),
"control.call name");
@assert(ByteView.eq(ByteView.from_slice(kind_name(.DbgPanic)), ByteView.from_slice("dbg.panic")),
"dbg.panic name");
}
fn test_alloc_and_node() {
var ir = new();
defer { drop(&ir); }
var fid = alloc_fn(&ir);
@assert(fid == 1, "first fn is 1");
var nid = alloc_node(&ir, fid);
@assert(nid == 1, "first node is 1");
var e = node(&ir, fid, nid);
@assert(e != null, "node lookup");
e.kind = .ArithAdd;
e.body = .Arith(.{ .tid = 2, .left = 1, .right = 1 });
var f = func(&ir, fid);
@assert(f != null && f.nodes.len == 2, "fn nodes");
}
fn main() {
test_kinds();
test_alloc_and_node();
}