stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮
This commit is contained in:
1 parent
b5a7a811b3
commit
e02b0c633a
35 files changed
+14018
-216
No files matched your search
+605
@@ -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();
|
||||
}
|
||||
Reference in new issue
Block a user