476 lines
18 KiB
Plaintext
476 lines
18 KiB
Plaintext
// Expect retcode = 0
|
|
|
|
// IR 构建器:单独成文件,以免 ast2ir 在同文件 @self 嵌套时无限递归。
|
|
|
|
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 {
|
|
if i >= v.len { ret 0; }
|
|
ret ((v.data as *usize) + i).*;
|
|
}
|
|
|
|
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 {
|
|
var slot = (self.blocks.data as *usize) + self.current_block;
|
|
if slot.* == 0 { slot.* = ref; }
|
|
}
|
|
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 {
|
|
var fx = (self.fixups.data as *Fix) + k;
|
|
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 {
|
|
match fx.slot {
|
|
.BrTrue => { match e.body { .Br(&b) => { b.true_label = target; }, _ => {} } },
|
|
.BrFalse => { match e.body { .Br(&b) => { b.false_label = target; }, _ => {} } },
|
|
.Jmp => { match e.body { .Jmp(&j) => { j.label = target; }, _ => {} } },
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
Vec.push(&self.blocks, &zero as *u8);
|
|
var block = self.blocks.len - 1;
|
|
self.current_block = block;
|
|
self.terminated = false;
|
|
ret block;
|
|
}
|
|
|
|
fn block_end(self: *IrBuilder) {
|
|
// 空实现:本实现不做 terminated/空块校验(不终止进程)。
|
|
}
|
|
|
|
|
|
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); }
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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)); }
|
|
fn type_const_global(self: *IrBuilder, tid: usize, gidx: usize): usize { ret const_node(self, tid, .Global(gidx)); }
|
|
|
|
// 追加模块级全局变量;返回 gidx。bytes 所有权转移进 Ir。
|
|
fn add_global(self: *IrBuilder, tid: usize, zero: bool, bytes: Vec): usize {
|
|
ret Ir.add_global(&self.ir, tid, zero, bytes);
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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) => { Vec.push(&a.fields, &field as *u8); },
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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 };
|
|
Vec.push(&self.fixups, &f1 as *u8);
|
|
var f2 = Fix { .node = ref, .slot = .BrFalse, .block = false_block };
|
|
Vec.push(&self.fixups, &f2 as *u8);
|
|
}
|
|
|
|
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 };
|
|
Vec.push(&self.fixups, &fx as *u8);
|
|
}
|
|
|
|
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) => { Vec.push(&c.params, &arg as *u8); },
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|