Files
spl/stage2/ir_builder.spl
T

498 lines
19 KiB
Plaintext

// Expect retcode = 0
// SIR builder (IrBuilder). Split from ir.spl: stage1 ast2ir
// infinitely recurses on same-file @self nesting; cross-file is fine.
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 .{ .Span, .span_none } = @import("./diag.spl");
type TypeTable = @import("./type_table.spl");
type Attr = @import("./attr.spl");
type Ir = @import("./ir.spl");
type .{ .IrKind, .ConstLit, .IrEntry, .IrFunc, .IrBody } = @import("./ir.spl");
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;
}
// ---- builder ----
type FixKind = enum { BrTrue, BrFalse, Jmp }
type Fix = struct {
node: usize,
slot: FixKind,
block: usize,
}
type IrBuilder = @self;
ir: Ir,
dbg: Span,
current_fn: usize,
current_block: usize,
blocks: Vec, // usize
fixups: Vec, // Fix
terminated: bool,
fn builder_alloc(self: *IrBuilder, kind: IrKind): usize {
var ref = Ir.alloc_node(&self.ir, self.current_fn);
if ref == 0 { ret 0; }
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null {
e.kind = kind;
e.dbg = self.dbg;
}
if self.current_block >= self.blocks.len { ret ref; }
match Vec.get(&self.blocks, self.current_block) {
.Ok(p) => {
var slot = p as *usize;
if slot.* == 0 { slot.* = ref; }
},
.Err(error) => {},
}
ret ref;
}
fn builder_new(): IrBuilder {
ret IrBuilder {
.ir = Ir.new(),
.dbg = span_none(),
.current_fn = 0,
.current_block = 0,
.blocks = Vec.blank(@sizeof(usize), @alignof(usize)),
.fixups = Vec.blank(@sizeof(Fix), @alignof(Fix)),
.terminated = false,
};
}
fn builder_drop(self: *IrBuilder) {
Ir.drop(&self.ir);
Vec.drop(&self.blocks);
Vec.drop(&self.fixups);
}
fn fn_begin(self: *IrBuilder, name: ByteView, fn_tid: usize): usize {
if self.current_fn != 0 { fn_end(self); }
var fid = Ir.alloc_fn(&self.ir);
var f = Ir.func(&self.ir, fid);
if f != null {
f.name = name;
f.fn_tid = fn_tid;
}
self.current_fn = fid;
self.current_block = 0;
self.blocks.len = 0;
self.fixups.len = 0;
self.terminated = false;
ret fid;
}
fn fn_reserve(self: *IrBuilder, name: ByteView, fn_tid: usize): usize {
var fid = Ir.alloc_fn(&self.ir);
var f = Ir.func(&self.ir, fid);
if f != null {
f.name = name;
f.fn_tid = fn_tid;
}
ret fid;
}
fn fn_begin_at(self: *IrBuilder, fid: usize) {
if self.current_fn != 0 && self.current_fn != fid { fn_end(self); }
self.current_fn = fid;
self.current_block = 0;
self.blocks.len = 0;
self.fixups.len = 0;
self.terminated = false;
}
fn fn_end(self: *IrBuilder) {
var k: usize = 0;
while k < self.fixups.len {
match Vec.get(&self.fixups, k) {
.Ok(fp) => {
var fx = fp as *Fix;
var e = Ir.node(&self.ir, self.current_fn, fx.node);
if e != null {
var target: usize = 0;
if fx.block < self.blocks.len { target = list_at(self.blocks, fx.block); }
if target != 0 {
if @enum2int(fx.slot) == @enum2int(FixKind.BrTrue) {
match e.body { .Br(b) => { b.true_label = target; e.body = .Br(b); }, _ => {} }
} else if @enum2int(fx.slot) == @enum2int(FixKind.BrFalse) {
match e.body { .Br(b) => { b.false_label = target; e.body = .Br(b); }, _ => {} }
} else {
match e.body { .Jmp(j) => { j.label = target; e.body = .Jmp(j); }, _ => {} }
}
}
}
},
.Err(error) => {},
}
k = k + 1;
}
self.current_block = 0;
self.blocks.len = 0;
self.fixups.len = 0;
self.terminated = false;
}
fn block_begin(self: *IrBuilder): usize {
var zero: usize = 0;
match Vec.push(&self.blocks, &zero as *u8) { .Ok => {}, .Err(error) => {} }
var block = self.blocks.len - 1;
self.current_block = block;
self.terminated = false;
ret block;
}
fn block_end(self: *IrBuilder) {
// terminated 与空块校验在 stage1 为 Panic;这里保持同样约束但不终止进程。
}
// ---- arith / cmp / cast ----
fn set_arith(self: *IrBuilder, kind: IrKind, tid: usize, left: usize, right: usize): usize {
var ref = builder_alloc(self, kind);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Arith(.{ .tid = tid, .left = left, .right = right }); }
ret ref;
}
fn set_cmp(self: *IrBuilder, kind: IrKind, tid: usize, a: usize, b: usize): usize {
var ref = builder_alloc(self, kind);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Cmp(.{ .tid = tid, .a = a, .b = b }); }
ret ref;
}
fn set_cast(self: *IrBuilder, kind: IrKind, from_tid: usize, to_tid: usize, val: usize): usize {
var ref = builder_alloc(self, kind);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Cast(.{ .from_tid = from_tid, .to_tid = to_tid, .val = val }); }
ret ref;
}
fn arith_add(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithAdd, t, l, r); }
fn arith_sub(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithSub, t, l, r); }
fn arith_mul(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithMul, t, l, r); }
fn arith_div(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithDiv, t, l, r); }
fn arith_rem(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithRem, t, l, r); }
fn arith_and(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithAnd, t, l, r); }
fn arith_or(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithOr, t, l, r); }
fn arith_xor(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithXor, t, l, r); }
fn arith_shl(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithShl, t, l, r); }
fn arith_shr(self: *IrBuilder, t: usize, l: usize, r: usize): usize { ret set_arith(self, .ArithShr, t, l, r); }
fn arith_neg(self: *IrBuilder, t: usize, v: usize): usize { ret set_arith(self, .ArithNeg, t, v, 0); }
fn arith_abs(self: *IrBuilder, t: usize, v: usize): usize { ret set_arith(self, .ArithAbs, t, v, 0); }
fn arith_not(self: *IrBuilder, t: usize, v: usize): usize { ret set_arith(self, .ArithNot, t, v, 0); }
fn cmp_eq(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpEq, t, a, b); }
fn cmp_ne(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpNe, t, a, b); }
fn cmp_lt(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpLt, t, a, b); }
fn cmp_le(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpLe, t, a, b); }
fn cmp_gt(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpGt, t, a, b); }
fn cmp_ge(self: *IrBuilder, t: usize, a: usize, b: usize): usize { ret set_cmp(self, .CmpGe, t, a, b); }
fn cast_trunc(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastTrunc, f, t, v); }
fn cast_zext(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastZext, f, t, v); }
fn cast_sext(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastSext, f, t, v); }
fn cast_fext(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastFext, f, t, v); }
fn cast_ftrunc(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastFtrunc, f, t, v); }
fn cast_bitcast(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastBitcast, f, t, v); }
fn cast_ptr2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastPtr2int, f, t, v); }
fn cast_int2ptr(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastInt2ptr, f, t, v); }
fn cast_bool2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CastBool2int, f, t, v); }
fn case_int2float(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseInt2float, f, t, v); }
fn case_float2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseFloat2int, f, t, v); }
// ---- memory ----
fn mem_alloca(self: *IrBuilder, tid: usize, count: usize): usize {
var ref = builder_alloc(self, .MemAlloca);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemAlloc(.{ .tid = tid, .count = count }); }
ret ref;
}
fn mem_global_alloc(self: *IrBuilder, tid: usize, const_node: usize): usize {
var ref = builder_alloc(self, .MemGlobalAlloc);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemGlobalAlloc(.{ .tid = tid, .const_node = const_node }); }
ret ref;
}
fn mem_load(self: *IrBuilder, tid: usize, ptr: usize): usize {
var ref = builder_alloc(self, .MemLoad);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemLoad(.{ .tid = tid, .ptr = ptr }); }
ret ref;
}
fn mem_store(self: *IrBuilder, tid: usize, ptr: usize, val: usize): usize {
var ref = builder_alloc(self, .MemStore);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemStore(.{ .tid = tid, .ptr = ptr, .val = val }); }
ret ref;
}
fn mem_offset(self: *IrBuilder, tid: usize, ptr: usize, offset: usize): usize {
var ref = builder_alloc(self, .MemOffset);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemOffset(.{ .tid = tid, .ptr = ptr, .offset = offset }); }
ret ref;
}
fn mem_field(self: *IrBuilder, tid: usize, agg: usize, field_idx: usize): usize {
var ref = builder_alloc(self, .MemFieldPtr);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemFieldPtr(.{ .tid = tid, .agg = agg, .field_idx = field_idx }); }
ret ref;
}
fn mem_copy(self: *IrBuilder, tid: usize, dst: usize, src: usize, size: usize): usize {
var ref = builder_alloc(self, .MemCopy);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemCopy(.{ .tid = tid, .dst = dst, .src = src, .size = size }); }
ret ref;
}
fn mem_set(self: *IrBuilder, tid: usize, dst: usize, val: usize, size: usize): usize {
var ref = builder_alloc(self, .MemSet);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .MemSet(.{ .tid = tid, .dst = dst, .val = val, .size = size }); }
ret ref;
}
// ---- type const ----
fn const_node(self: *IrBuilder, tid: usize, lit: ConstLit): usize {
var ref = builder_alloc(self, .TypeConst);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Const(.{ .tid = tid, .lit = lit }); }
ret ref;
}
fn type_const_ptr(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Ptr(val)); }
fn type_const_bool(self: *IrBuilder, tid: usize, val: bool): usize { ret const_node(self, tid, .Bool(val)); }
fn type_const_int(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Int(val)); }
fn type_const_float(self: *IrBuilder, tid: usize, val: f64): usize { ret const_node(self, tid, .Float(val)); }
fn type_const_char(self: *IrBuilder, tid: usize, val: u8): usize { ret const_node(self, tid, .Char(val)); }
fn type_const_fn(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Fn(val)); }
fn type_const_bytes(self: *IrBuilder, tid: usize, data: usize): usize { ret const_node(self, tid, .Ptr(data)); }
// ---- type ops ----
fn type_bitsizeof(self: *IrBuilder, tid: usize): usize {
var ref = builder_alloc(self, .TypeBitsizeof);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
ret ref;
}
fn type_sizeof(self: *IrBuilder, tid: usize): usize {
var ref = builder_alloc(self, .TypeSizeof);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
ret ref;
}
fn type_alignof(self: *IrBuilder, tid: usize): usize {
var ref = builder_alloc(self, .TypeAlignof);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
ret ref;
}
fn type_offsetof(self: *IrBuilder, tid: usize, field_idx: usize): usize {
var ref = builder_alloc(self, .TypeOffsetof);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = field_idx }); }
ret ref;
}
fn type_field_count(self: *IrBuilder, tid: usize): usize {
var ref = builder_alloc(self, .TypeFieldCount);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .TypeOp(.{ .tid = tid, .field_idx = 0 }); }
ret ref;
}
// ---- agg ----
fn agg_construct(self: *IrBuilder, tid: usize): usize {
var ref = builder_alloc(self, .AggConstruct);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null {
e.body = .AggConstruct(.{ .tid = tid, .fields = Vec.blank(@sizeof(usize), @alignof(usize)) });
}
ret ref;
}
fn agg_construct_field(self: *IrBuilder, node_ref: usize, field: usize) {
var e = Ir.node(&self.ir, self.current_fn, node_ref);
if e == null { ret; }
// 载荷按值绑定:改副本后写回。
match e.body {
.AggConstruct(a) => {
match Vec.push(&a.fields, &field as *u8) { .Ok => {}, .Err(error) => {} }
e.body = .AggConstruct(a);
},
_ => {},
}
}
fn agg_extract(self: *IrBuilder, tid: usize, field_tid: usize, field_idx: usize, val: usize): usize {
var ref = builder_alloc(self, .AggExtract);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null {
e.body = .AggExtract(.{ .tid = tid, .field_tid = field_tid, .field_idx = field_idx, .val = val });
}
ret ref;
}
fn agg_insert(self: *IrBuilder, tid: usize, field_idx: usize, agg: usize, field: usize): usize {
var ref = builder_alloc(self, .AggInsert);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .AggInsert(.{ .tid = tid, .field_idx = field_idx, .agg = agg, .field = field }); }
ret ref;
}
// ---- control ----
fn control_select(self: *IrBuilder, tid: usize, cond: usize, tv: usize, fv: usize): usize {
var ref = builder_alloc(self, .ControlSelect);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Select(.{ .tid = tid, .cond = cond, .true_val = tv, .false_val = fv }); }
ret ref;
}
fn control_br(self: *IrBuilder, cond: usize): usize {
var ref = builder_alloc(self, .ControlBr);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Br(.{ .cond = cond, .true_label = 0, .false_label = 0 }); }
self.terminated = true;
ret ref;
}
fn control_br_fixup(self: *IrBuilder, ref: usize, true_block: usize, false_block: usize) {
var f1 = Fix { .node = ref, .slot = .BrTrue, .block = true_block };
match Vec.push(&self.fixups, &f1 as *u8) { .Ok => {}, .Err(error) => {} }
var f2 = Fix { .node = ref, .slot = .BrFalse, .block = false_block };
match Vec.push(&self.fixups, &f2 as *u8) { .Ok => {}, .Err(error) => {} }
}
fn control_jmp(self: *IrBuilder): usize {
var ref = builder_alloc(self, .ControlJmp);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Jmp(.{ .label = 0 }); }
self.terminated = true;
ret ref;
}
fn control_jmp_fixup(self: *IrBuilder, ref: usize, target_block: usize) {
var fx = Fix { .node = ref, .slot = .Jmp, .block = target_block };
match Vec.push(&self.fixups, &fx as *u8) { .Ok => {}, .Err(error) => {} }
}
fn control_call(self: *IrBuilder, tid: usize, fn_ref: usize): usize {
var ref = builder_alloc(self, .ControlCall);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null {
e.body = .Call(.{ .tid = tid, .func = fn_ref,
.params = Vec.blank(@sizeof(usize), @alignof(usize)) });
}
ret ref;
}
fn control_call_param(self: *IrBuilder, call_node: usize, arg: usize) {
var e = Ir.node(&self.ir, self.current_fn, call_node);
if e == null { ret; }
// 载荷按值绑定:改副本后写回。
match e.body {
.Call(c) => {
match Vec.push(&c.params, &arg as *u8) { .Ok => {}, .Err(error) => {} }
e.body = .Call(c);
},
_ => {},
}
}
fn control_param(self: *IrBuilder, tid: usize, idx: usize): usize {
var ref = builder_alloc(self, .ControlParam);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Param(.{ .tid = tid, .idx = idx }); }
ret ref;
}
fn control_ret(self: *IrBuilder, tid: usize, val: usize): usize {
var ref = builder_alloc(self, .ControlRet);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Ret(.{ .tid = tid, .val = val }); }
self.terminated = true;
ret ref;
}
fn control_unreachable(self: *IrBuilder): usize {
var ref = builder_alloc(self, .ControlUnreachable);
self.terminated = true;
ret ref;
}
fn control_trap(self: *IrBuilder): usize {
var ref = builder_alloc(self, .ControlTrap);
self.terminated = true;
ret ref;
}
fn dbg_breakpoint(self: *IrBuilder): usize {
ret builder_alloc(self, .DbgBreakpoint);
}
fn dbg_panic(self: *IrBuilder, msg: ByteView): usize {
var ref = builder_alloc(self, .DbgPanic);
var e = Ir.node(&self.ir, self.current_fn, ref);
if e != null { e.body = .Panic(.{ .msg = msg }); }
ret ref;
}
fn test_builder_emit_and_dump() {
var b = builder_new();
defer { builder_drop(&b); }
b.dbg = span_none();
var fid = fn_begin(&b, ByteView.from_slice("f"), 3);
var blk = block_begin(&b);
var p = control_param(&b, 2, 0);
var c = type_const_int(&b, 2, 42);
var add = arith_add(&b, 2, p, c);
control_ret(&b, 2, add);
block_end(&b);
fn_end(&b);
var ty = TypeTable.new();
var t: TypeTable = undefined;
match ty { .Ok(v) => { t = v; }, .Err(e) => { @assert(false, "tt"); } }
defer { TypeTable.drop(&t); }
var at = Attr.new();
var a: Attr = undefined;
match at { .Ok(v) => { a = v; }, .Err(e) => { @assert(false, "attr"); } }
defer { Attr.drop(&a); }
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
@assert(Ir.dump(&b.ir, &t, &a, &out), "dump ok");
var got = ByteBuffer.as_slice(out);
@assert(got.len > 0, "dump nonempty");
}
fn main() {
test_builder_emit_and_dump();
}