618 lines
20 KiB
Plaintext
618 lines
20 KiB
Plaintext
// 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,
|
||
Global: usize, // 全局变量下标(Ir.globals);值 = 指向该全局的 *T
|
||
}
|
||
|
||
// 模块级全局变量:编译期分配的静态存储,引用时是一个 *T 指针常量(同 LLVM GlobalVariable)。
|
||
type Global = struct {
|
||
tid: usize,
|
||
zero: bool, // true = 零初始化(BSS,不携带字节)
|
||
data: Vec, // u8 初始字节(zero=false 时使用)
|
||
}
|
||
|
||
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
|
||
globals: Vec, // Global
|
||
|
||
// ---- 生命周期 ----
|
||
|
||
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)),
|
||
.globals = Vec.blank(@sizeof(Global), @alignof(Global)) };
|
||
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 {
|
||
var f = (self.funcs.data as *IrFunc) + i;
|
||
var j: usize = 0;
|
||
while j < f.nodes.len {
|
||
var e = (f.nodes.data as *IrEntry) + j;
|
||
Vec.drop(&e.attrs);
|
||
drop_body(e.body);
|
||
j = j + 1;
|
||
}
|
||
Vec.drop(&f.attrs);
|
||
Vec.drop(&f.nodes);
|
||
i = i + 1;
|
||
}
|
||
Vec.drop(&self.funcs);
|
||
var g: usize = 0;
|
||
while g < self.globals.len {
|
||
var ge = (self.globals.data as *Global) + g;
|
||
Vec.drop(&ge.data);
|
||
g = g + 1;
|
||
}
|
||
Vec.drop(&self.globals);
|
||
}
|
||
|
||
// 追加一个全局变量;返回其下标(gidx)。bytes 的所有权转移进表。
|
||
fn add_global(self: *Ir, tid: usize, zero: bool, bytes: Vec): usize {
|
||
var g = Global { .tid = tid, .zero = zero, .data = bytes };
|
||
match Vec.push(&self.globals, &g as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||
ret self.globals.len - 1;
|
||
}
|
||
|
||
fn global_at(self: *Ir, gidx: usize): *Global {
|
||
if gidx >= self.globals.len { ret null; }
|
||
ret (self.globals.data as *Global) + gidx;
|
||
}
|
||
|
||
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; }
|
||
var f = (self.funcs.data as *IrFunc) + fn_id;
|
||
var n = new_entry();
|
||
match Vec.push(&f.nodes, &n as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||
ret f.nodes.len - 1;
|
||
}
|
||
|
||
fn func(self: *Ir, fn_id: usize): *IrFunc {
|
||
if fn_id == 0 || fn_id >= self.funcs.len { ret null; }
|
||
ret (self.funcs.data as *IrFunc) + fn_id;
|
||
}
|
||
|
||
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; }
|
||
ret (f.nodes.data as *IrEntry) + node_id;
|
||
}
|
||
|
||
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 {
|
||
if i >= v.len { ret 0; }
|
||
ret ((v.data as *usize) + i).*;
|
||
}
|
||
|
||
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; }
|
||
match c.lit {
|
||
.Global(g) => {
|
||
if !out_str(out, " global#") { ret false; }
|
||
ret out_u(out, g);
|
||
},
|
||
_ => {},
|
||
}
|
||
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();
|
||
}
|