Files
spl/stage2/ir.spl
T

611 lines
20 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Expect retcode = 0
// SIR 中间表示:节点用 arena 序号引用(0=error),按函数分桶;公共字段在 IrEntry,
// kind 专属载荷在 IrBody ADT(kind 与载荷一一对应)。builder 负责发射与跳转回填。
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();
Vec.push(&sentinel.nodes, &sn as *u8);
Vec.push(&ir.funcs, &sentinel as *u8);
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 "?";
}
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。
match e.kind {
.ArithNeg, .ArithAbs, .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, "'");
}
// 其他常量统一按 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();
}