Files
spl/stage2/ast2ir.spl
T

2313 lines
84 KiB
Plaintext

// Expect retcode = 0
// AST -> IR。两遍:先 reserve 所有函数拿稳定 func ref,再发射函数体;def2ir_map
// 记录 def -> 槽位(变量=内存指针,函数=func ref)。
type .{ .Error } = @import("utils/error.spl");
type .{ .Vec } = @import("utils/vec.spl");
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
type .{ .ByteView } = @import("utils/byte_view.spl");
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
type .{ .hash_usize, .eq_usize } = @import("utils/hash.spl");
type BigInt = @import("utils/mp.spl");
type .{ .append_usize } = @import("utils/fmt.spl");
type .{ .Sources, .Source, .set_at } = @import("utils/source.spl");
type .{ .Span, .span_none, .Code, .no_file } = @import("./diag.spl");
type Diag = @import("./diag.spl");
type Ast = @import("./ast.spl");
type .{ .Node } = @import("./ast.spl");
type Sema = @import("./sema.spl");
type LangType = @import("./lang_type.spl");
type .{ .LangKind, .LangEntry, .LangMethod, .LangExtra } = @import("./lang_type.spl");
type TypeTable = @import("./type_table.spl");
type .{ .TypeNode, .TypeEntry, .Member, .AggKind, .LayoutMode } = @import("./type_table.spl");
type .{ .DefKind, .DefNode, .DeclState } = @import("./def.spl");
type .{ .Intrin, .IntrinOps } = @import("./intrin.spl");
type Ir = @import("./ir.spl");
type .{ .IrKind, .ConstLit, .IrEntry, .IrFunc, .IrBody } = @import("./ir.spl");
type IrBuilder = @import("./ir_builder.spl");
type .{ .Punct } = @import("./token.spl");
type LoopCtx = struct {
cont_blk: usize,
defer_base: usize,
label: ByteView,
breaks: Vec, // ir node ref
result_slot: usize,
result_tid: usize,
}
type Ast2Ir = @self;
sema: *Sema,
ir: *IrBuilder,
ast: *Ast,
def2ir_map: Map,
glob_gidx: Map, // def_id -> gidx+1(全局变量)
loop_stack: Vec, // LoopCtx
defer_stack: Vec, // ast node ref
cur_ret_lang: usize,
cur_defer_base: usize,
diag: *Diag,
owned: Vec, // ByteBuffer:为 dbg.panic 消息提供稳定存储
fn list_at(v: Vec, i: usize): usize {
if i >= v.len { ret 0; }
ret ((v.data as *usize) + i).*;
}
fn push_u(v: *Vec, x: usize) {
Vec.push(v, &x as *u8);
}
fn irt(self: *Ast2Ir, id: usize): *TypeEntry {
ret TypeTable.node(&self.sema.types.ir, id);
}
fn ltn(self: *Ast2Ir, id: usize): *LangEntry {
ret LangType.lang_node(&self.sema.types, id);
}
fn defn(self: *Ast2Ir, id: usize): *DefNode {
ret LangType.def_node(&self.sema.types, id);
}
fn to_ir_type(self: *Ast2Ir, lang: usize): usize {
if lang == 0 { ret 0; }
ret LangType.get_ir_type(&self.sema.types, lang);
}
fn node_lang(n: *Node): usize {
if n == null { ret 0; }
ret n.resolved_lang_tid;
}
fn node_ir(self: *Ast2Ir, n: *Node): usize {
ret to_ir_type(self, node_lang(n));
}
fn const_usize(self: *Ast2Ir, v: usize): usize {
var t = LangType.make_int(&self.sema.types, @sizeof(usize) * 8, false);
ret IrBuilder.type_const_int(self.ir, to_ir_type(self, t), v);
}
fn ir_map_get(self: *Ast2Ir, def_id: usize, out: *usize): bool {
match Map.get(&self.def2ir_map, &def_id as *u8, out as *u8) {
.Found => { ret true; },
.NotFound => { ret false; },
}
ret false;
}
fn ir_map_put(self: *Ast2Ir, def_id: usize, val: usize) {
Map.put(&self.def2ir_map, &def_id as *u8, &val as *u8);
}
fn glob_get(self: *Ast2Ir, def_id: usize, out: *usize): bool {
match Map.get(&self.glob_gidx, &def_id as *u8, out as *u8) {
.Found => { ret true; },
.NotFound => { ret false; },
}
ret false;
}
fn glob_put(self: *Ast2Ir, def_id: usize, val: usize) {
Map.put(&self.glob_gidx, &def_id as *u8, &val as *u8);
}
// 顶层 var/const → 模块全局(alloc 于静态存储;引用是 *T 指针常量)。
fn emit_global_var(self: *Ast2Ir, d: *Node) {
if d == null || d.resolved_def_id == 0 { ret; }
var def_id = d.resolved_def_id;
var existing: usize = 0;
if glob_get(self, def_id, &existing) { ret; }
var lang = LangType.def_lang_id(&self.sema.types, def_id);
var ir = to_ir_type(self, lang);
if ir == 0 { ret; }
var init_ref: usize = 0;
match d.body {
.VarDecl(x) => { init_ref = x.init_expr; },
.ConstDecl(x) => { init_ref = x.init_expr; },
_ => {},
}
var zero = true;
var bytes = Vec.blank(1, 1);
if init_ref != 0 {
var inode = Ast.node(self.ast, init_ref);
if inode != null {
match inode.body {
.ExprIntLit(bi) => {
var v: u64 = 0;
BigInt.to_u64(&v, bi.value);
var bits = LangType.int_bits(&self.sema.types, ir);
var size = bits / 8;
if size != 0 {
zero = false;
var i: usize = 0;
while i < size {
var b = ((v >> (8 * i)) & 255) as u8;
Vec.push(&bytes, &b as *u8);
i = i + 1;
}
}
},
_ => {},
}
}
}
var gidx = IrBuilder.add_global(self.ir, ir, zero, bytes);
glob_put(self, def_id, gidx + 1);
}
fn kind_is(self: *Ast2Ir, lang_id: usize, want: LangKind): bool {
var e = ltn(self, lang_id);
if e == null { ret false; }
ret @enum2int(e.kind) == @enum2int(want);
}
fn punct_is(op: Punct, want: Punct): bool { ret @enum2int(op) == @enum2int(want); }
fn is_assign_arith(op: Punct): bool {
match op {
.AddAssign => { ret true; }, .SubAssign => { ret true; }, .MulAssign => { ret true; },
.DivAssign => { ret true; }, .ModAssign => { ret true; }, .AndAssign => { ret true; },
.OrAssign => { ret true; }, .XorAssign => { ret true; }, .LShAssign => { ret true; },
.RShAssign => { ret true; }, _ => { ret false; },
}
ret false;
}
fn int_lit_u64(n: *Node): u64 {
match n.body {
.ExprIntLit(&x) => {
var out: u64 = 0;
BigInt.to_u64(&out, x.value);
ret out;
},
_ => {},
}
ret 0;
}
fn ir_kind_of(self: *Ast2Ir, ref: usize): IrKind {
var e = Ir.node(&self.ir.ir, self.ir.current_fn, ref);
if e == null { ret .ControlUnreachable; }
ret e.kind;
}
fn emit_zero_value(self: *Ast2Ir, tid: usize): usize {
var tn = irt(self, tid);
if tn == null { ret 0; }
var is_bool = false;
var is_int = false;
var is_float = false;
var is_ptr = false;
var is_fn = false;
var is_arr = false;
var arr_len: usize = 0;
var arr_elem: usize = 0;
var is_struct = false;
var is_shape = false;
var is_union = false;
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match tn.node {
.Bool => { is_bool = true; },
.Int(&x) => { is_int = true; },
.Float(&x) => { is_float = true; },
.Ptr(&x) => { is_ptr = true; },
.Fn(&x) => { is_fn = true; },
.Array(&x) => { is_arr = true; arr_len = x.len; arr_elem = x.element; },
.Struct(&x) => { is_struct = true; variants = x.variants; },
.Shape(&x) => { is_shape = true; variants = x.variants; },
.Union(&x) => { is_union = true; variants = x.variants; },
_ => { ret 0; },
}
if is_bool { ret IrBuilder.type_const_bool(self.ir, tid, false); }
if is_int { ret IrBuilder.type_const_int(self.ir, tid, 0); }
if is_float { ret IrBuilder.type_const_float(self.ir, tid, 0.0); }
if is_ptr || is_fn { ret IrBuilder.type_const_ptr(self.ir, tid, 0); }
if is_arr {
var node = IrBuilder.agg_construct(self.ir, tid);
var i: usize = 0;
while i < arr_len {
var f = emit_zero_value(self, arr_elem);
if f == 0 { ret 0; }
IrBuilder.agg_construct_field(self.ir, node, f);
i = i + 1;
}
ret node;
}
if is_struct || is_shape {
var tids = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < variants.len {
push_u(&tids, variant_at(variants, i));
i = i + 1;
}
var node = IrBuilder.agg_construct(self.ir, tid);
i = 0;
while i < tids.len {
var f = emit_zero_value(self, list_at(tids, i));
if f == 0 { Vec.drop(&tids); ret 0; }
IrBuilder.agg_construct_field(self.ir, node, f);
i = i + 1;
}
Vec.drop(&tids);
ret node;
}
if is_union {
if variants.len == 0 { ret IrBuilder.agg_construct(self.ir, tid); }
var node = IrBuilder.agg_construct(self.ir, tid);
var f = emit_zero_value(self, variant_at(variants, 0));
if f == 0 { ret 0; }
IrBuilder.agg_construct_field(self.ir, node, f);
ret node;
}
ret 0;
}
fn variant_at(v: Vec, idx: usize): usize {
var m = TypeTable.member_at(&v, idx);
if m == null { ret 0; }
ret m.type_id;
}
fn pattern_has_ref(self: *Ast2Ir, pat_ref: usize): bool {
if pat_ref == 0 { ret false; }
var pat = Ast.node(self.ast, pat_ref);
if pat == null { ret false; }
match pat.body {
.PatternRef(_) => { ret true; },
.PatternList(x) => {
var i: usize = 0;
while i < x.items.len {
if pattern_has_ref(self, list_at(x.items, i)) { ret true; }
i = i + 1;
}
ret false;
},
.PatternTuple(x) => {
var i: usize = 0;
while i < x.items.len {
if pattern_has_ref(self, list_at(x.items, i)) { ret true; }
i = i + 1;
}
ret false;
},
.PatternDot(x) => { ret pattern_has_ref(self, x.sub_pattern); },
.PatternAggregate(x) => {
var i: usize = 0;
while i < x.items.len {
var f = Ast.node(self.ast, list_at(x.items, i));
if f != null {
match f.body {
.PatternField(pf) => {
if pattern_has_ref(self, pf.pattern) { ret true; }
},
_ => {},
}
}
i = i + 1;
}
ret false;
},
_ => { ret false; },
}
ret false;
}
fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize, place_mode: bool) {
if pat_ref == 0 || src_ptr == 0 { ret; }
var pat = Ast.node(self.ast, pat_ref);
if pat == null { ret; }
match pat.body {
.PatternIdent(x) => {
if pat.resolved_def_id != 0 {
if place_mode {
var slot = IrBuilder.mem_alloca(self.ir, tid, const_usize(self, 1));
var v = IrBuilder.mem_load(self.ir, tid, src_ptr);
IrBuilder.mem_store(self.ir, tid, slot, v);
ir_map_put(self, pat.resolved_def_id, slot);
} else {
ir_map_put(self, pat.resolved_def_id, src_ptr);
}
}
ret;
},
.PatternRef(x) => {
var in = Ast.node(self.ast, x.pat);
var ptr_tid: usize = 0;
if in != null { ptr_tid = node_ir(self, in); }
if ptr_tid == 0 { ptr_tid = TypeTable.make_ptr(&self.sema.types.ir, tid); }
var slot = IrBuilder.mem_alloca(self.ir, ptr_tid, const_usize(self, 1));
IrBuilder.mem_store(self.ir, ptr_tid, slot, src_ptr);
emit_pattern_bind(self, x.pat, slot, ptr_tid, false);
ret;
},
.ExprIntLit(x) => { ret; },
.ExprFloatLit(x) => { ret; },
.ExprCharLit(x) => { ret; },
.ExprStringLit(x) => { ret; },
.ExprBoolLit(x) => { ret; },
.ExprNullLit => { ret; },
.PatternUnderscore => { ret; },
.PatternList(x) => {
var i: usize = 0;
while i < x.items.len {
emit_pattern_bind(self, list_at(x.items, i), src_ptr, tid, place_mode);
i = i + 1;
}
ret;
},
.PatternTuple(x) => {
var tn = irt(self, tid);
if tn == null { ret; }
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match tn.node { .Struct(a) => { variants = a.variants; }, _ => { ret; } }
var n = x.items.len;
var nf = variants.len;
if n > nf { ret; }
var i: usize = 0;
while i < n {
var ft = variant_at(variants, i);
var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, i);
emit_pattern_bind(self, list_at(x.items, i), fp, ft, place_mode);
i = i + 1;
}
ret;
},
.PatternDot(x) => {
var tn = irt(self, tid);
if tn == null { ret; }
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match tn.node { .Enum(a) => { variants = a.variants; }, _ => { ret; } }
var vidx = pat.resolved_def_id;
if vidx == 0 { ret; }
vidx = vidx - 1;
if vidx >= variants.len { ret; }
var payload_ir = variant_at(variants, vidx);
var pptr = IrBuilder.mem_field(self.ir, tid, src_ptr, vidx + 1);
if x.sub_pattern != 0 {
emit_pattern_bind(self, x.sub_pattern, pptr, payload_ir, place_mode);
}
ret;
},
.PatternAggregate(x) => {
var tn = irt(self, tid);
if tn == null { ret; }
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match tn.node {
.Struct(a) => { variants = a.variants; },
.Union(a) => { variants = a.variants; },
_ => { ret; },
}
var i: usize = 0;
while i < x.items.len {
var f = Ast.node(self.ast, list_at(x.items, i));
if f != null {
match f.body {
.PatternField(pf) => {
var idx = f.resolved_def_id;
if idx < variants.len {
var ft = variant_at(variants, idx);
var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, idx);
if pf.pattern != 0 {
emit_pattern_bind(self, pf.pattern, fp, ft, place_mode);
}
}
},
_ => {},
}
}
i = i + 1;
}
ret;
},
_ => { ret; },
}
}
fn emit_match_check(self: *Ast2Ir, pat_ref: usize, scrut_ir: usize, scrut_slot: usize): usize {
var pat = Ast.node(self.ast, pat_ref);
if pat == null { ret 0; }
match pat.body {
.PatternUnderscore => { ret 0; },
.PatternIdent(x) => { ret 0; },
.PatternList(x) => {
var bt = LangType.make_bool(&self.sema.types);
var bt_ir = to_ir_type(self, bt);
var combined: usize = 0;
var i: usize = 0;
while i < x.items.len {
var c = emit_match_check(self, list_at(x.items, i), scrut_ir, scrut_slot);
if c == 0 { ret 0; }
if combined == 0 {
combined = c;
} else {
combined = IrBuilder.arith_or(self.ir, bt_ir, combined, c);
}
i = i + 1;
}
ret combined;
},
.PatternDot(x) => {
var tn = irt(self, scrut_ir);
if tn == null { ret 0; }
var tag_ir: usize = 0;
match tn.node { .Enum(a) => { tag_ir = a.tag_type; }, _ => { ret 0; } }
var vidx = pat.resolved_def_id;
if vidx == 0 { ret 0; }
vidx = vidx - 1;
var tag_ptr = IrBuilder.mem_field(self.ir, scrut_ir, scrut_slot, 0);
var tag_val = IrBuilder.mem_load(self.ir, tag_ir, tag_ptr);
var vidx_const = IrBuilder.type_const_int(self.ir, tag_ir, vidx);
ret IrBuilder.cmp_eq(self.ir, tag_ir, tag_val, vidx_const);
},
.ExprIntLit(x) => { ret match_check_literal(self, pat_ref, scrut_ir, scrut_slot); },
.ExprFloatLit(x) => { ret match_check_literal(self, pat_ref, scrut_ir, scrut_slot); },
.ExprCharLit(x) => { ret match_check_literal(self, pat_ref, scrut_ir, scrut_slot); },
.ExprStringLit(x) => { ret match_check_literal(self, pat_ref, scrut_ir, scrut_slot); },
.ExprBoolLit(x) => { ret match_check_literal(self, pat_ref, scrut_ir, scrut_slot); },
.ExprNullLit => { ret match_check_literal(self, pat_ref, scrut_ir, scrut_slot); },
_ => { ret 0; },
}
ret 0;
}
fn match_check_literal(self: *Ast2Ir, pat_ref: usize, scrut_ir: usize, scrut_slot: usize): usize {
var val = emit_expr(self, pat_ref);
if val == 0 { ret 0; }
var loaded = IrBuilder.mem_load(self.ir, scrut_ir, scrut_slot);
ret IrBuilder.cmp_eq(self.ir, scrut_ir, loaded, val);
}
fn emit_var_decl(self: *Ast2Ir, n: *Node): usize {
var var_ir = node_ir(self, n);
if var_ir == 0 { ret 0; }
var iref: usize = 0;
var pat_ref: usize = 0;
match n.body {
.VarDecl(x) => { iref = x.init_expr; pat_ref = x.pattern; },
.ConstDecl(x) => { iref = x.init_expr; pat_ref = x.pattern; },
_ => {},
}
if pattern_has_ref(self, pat_ref) {
if iref == 0 {
Diag.err(self.diag, n.span, .NoLvalue,
"reference binding requires an initialized place");
ret 0;
}
var place = emit_lvalue(self, iref);
if place == 0 {
var in = Ast.node(self.ast, iref);
if in != null {
Diag.err(self.diag, in.span, .NoLvalue,
"cannot bind by reference: initializer is not an lvalue");
} else {
Diag.err(self.diag, n.span, .NoLvalue,
"cannot bind by reference: initializer is not an lvalue");
}
ret 0;
}
emit_pattern_bind(self, pat_ref, place, var_ir, true);
ret place;
}
var slot = IrBuilder.mem_alloca(self.ir, var_ir, const_usize(self, 1));
if iref != 0 {
var v = emit_expr(self, iref);
if v != 0 { IrBuilder.mem_store(self.ir, var_ir, slot, v); }
}
emit_pattern_bind(self, pat_ref, slot, var_ir, false);
ret slot;
}
fn copy_attrs_node(self: *Ast2Ir, n: *Node, ir_ref: usize) {
if ir_ref == 0 || n == null || n.attrs.len == 0 { ret; }
var irn = Ir.node(&self.ir.ir, self.ir.current_fn, ir_ref);
if irn == null { ret; }
var i: usize = 0;
while i < n.attrs.len {
var a = list_at(n.attrs, i);
Vec.push(&irn.attrs, &a as *u8);
i = i + 1;
}
}
fn copy_attrs_func(self: *Ast2Ir, n: *Node, fn_ref: usize) {
if fn_ref == 0 || n == null || n.attrs.len == 0 { ret; }
var irf = Ir.func(&self.ir.ir, fn_ref);
if irf == null { ret; }
var i: usize = 0;
while i < n.attrs.len {
var a = list_at(n.attrs, i);
Vec.push(&irf.attrs, &a as *u8);
i = i + 1;
}
}
fn ast2ir_intrin_cast(self: *Ast2Ir, n: *Node, in: Intrin, args: Vec): usize {
if args.len < 1 {
Diag.err(self.diag, n.span, .IntrinArgCount, "cast builtin requires at least 1 argument");
ret 0;
}
var v = emit_expr(self, list_at(args, 0));
if v == 0 { ret 0; }
var src = Ast.node(self.ast, list_at(args, 0));
var ft = node_ir(self, src);
var tt: usize = 0;
if args.len >= 2 {
var targ = Ast.node(self.ast, list_at(args, 1));
tt = node_ir(self, targ);
} else {
tt = node_ir(self, n);
}
if ft == 0 || tt == 0 { ret 0; }
ret ast2ir_emit_cast_builtin(self, n, in, ft, tt, v);
}
fn ast2ir_intrin_call(self: *Ast2Ir, n: *Node, in: Intrin, args: Vec): usize {
match in {
.BitSizeof => {
var arg = Ast.node(self.ast, list_at(args, 0));
ret IrBuilder.type_bitsizeof(self.ir, node_ir(self, arg));
},
.Sizeof => {
var arg = Ast.node(self.ast, list_at(args, 0));
ret IrBuilder.type_sizeof(self.ir, node_ir(self, arg));
},
.Alignof => {
var arg = Ast.node(self.ast, list_at(args, 0));
ret IrBuilder.type_alignof(self.ir, node_ir(self, arg));
},
.Enum2Int => {
var src = Ast.node(self.ast, list_at(args, 0));
var v = emit_expr(self, list_at(args, 0));
if v == 0 { ret 0; }
var agg_t = node_ir(self, src);
var tag_t = node_ir(self, n);
if agg_t == 0 || tag_t == 0 { ret 0; }
ret IrBuilder.agg_extract(self.ir, agg_t, tag_t, 0, v);
},
.FieldCount => {
var arg = Ast.node(self.ast, list_at(args, 0));
ret IrBuilder.type_field_count(self.ir, node_ir(self, arg));
},
.OffsetOf => {
var arg = Ast.node(self.ast, list_at(args, 0));
var agg_t = node_ir(self, arg);
if agg_t == 0 { ret 0; }
var fnode = Ast.node(self.ast, list_at(args, 1));
if fnode == null { ret 0; }
var idx: usize = 0;
match fnode.body {
.ExprIntLit(x) => { idx = int_lit_u64(fnode) as usize; },
.ExprIdent(e) => {
if !TypeTable.agg_member_idx(&self.sema.types.ir, agg_t, e.name, &idx) { ret 0; }
},
_ => { ret 0; },
}
var idx_node = const_usize(self, idx);
ret IrBuilder.type_offsetof(self.ir, agg_t, idx_node);
},
.Assert => { ret ast2ir_intrin_assert(self, n, args); },
.IntCast, .Truncate, .BitCast, .FloatCast, .Float2Int, .Int2Float,
.Bool2Int, .Ptr2Int, .Int2Ptr, .PtrCast => {
ret ast2ir_intrin_cast(self, n, in, args);
},
.Breakpoint => { ret IrBuilder.dbg_breakpoint(self.ir); },
_ => { ret 0; },
}
ret 0;
}
fn ast2ir_intrin_assert(self: *Ast2Ir, n: *Node, args: Vec): usize {
var cond = emit_expr(self, list_at(args, 0));
if cond == 0 { ret 0; }
var b = LangType.make_bool(&self.sema.types);
var notc = IrBuilder.arith_not(self.ir, to_ir_type(self, b), cond);
var br = IrBuilder.control_br(self.ir, notc);
IrBuilder.block_end(self.ir);
var panic_blk = IrBuilder.block_begin(self.ir);
var msg = ByteBuffer.new();
// 交给 Ast2Ir 持有:dbg.panic 只借用这段字节。
match Vec.push(&self.owned, &msg as *u8) {
.Ok => {},
.Err(e) => { ByteBuffer.drop(&msg); ret 0; },
}
// 消息:<fname>:<line>:<col>: assert failed[: user]
if self.sema.sources != null && n.span.file != no_file() {
var src = set_at(self.sema.sources, n.span.file);
if src != null {
append_bytes(&msg, ByteView.as_slice(src.fname));
var pos = Source.line_col(src.*, n.span.start);
append_bytes(&msg, ":");
append_num(&msg, pos.line);
append_bytes(&msg, ":");
append_num(&msg, pos.col);
}
}
append_bytes(&msg, ": assert failed");
if args.len == 2 {
var mn = Ast.node(self.ast, list_at(args, 1));
if mn != null {
match mn.body {
.ExprStringLit(sl) => {
append_bytes(&msg, ": ");
append_bytes(&msg, ByteView.as_slice(sl.data));
},
_ => {},
}
}
}
var msg_view = ByteView.from_slice(ByteBuffer.as_slice(msg));
@assert(msg_view.len == 0 || msg_view.ptr != null, "panic message bytes must be non-null");
IrBuilder.dbg_panic(self.ir, msg_view);
IrBuilder.control_trap(self.ir);
IrBuilder.block_end(self.ir);
var cont_blk = IrBuilder.block_begin(self.ir);
IrBuilder.control_br_fixup(self.ir, br, panic_blk, cont_blk);
ret 0;
}
fn append_bytes(buf: *ByteBuffer, bytes: []u8): bool {
match ByteBuffer.append(buf, bytes) { .Ok => { ret true; }, .Err(e) => { ret false; } }
ret false;
}
fn append_num(buf: *ByteBuffer, v: usize): bool {
match append_usize(buf, v, 10, false) { .Ok => { ret true; }, .Err(e) => { ret false; } }
ret false;
}
fn ast2ir_intrin_field(self: *Ast2Ir, n: *Node, in: Intrin): usize {
if @enum2int(in) == @enum2int(Intrin.Len) { ret ast2ir_intrin_len(self, n); }
ret 0;
}
fn ast2ir_intrin_len(self: *Ast2Ir, n: *Node): usize {
var obj_ref: usize = 0;
var fname = ByteView.empty();
match n.body { .ExprField(&x) => { obj_ref = x.obj; fname = x.name; }, _ => {} }
var obj = Ast.node(self.ast, obj_ref);
var recv_lang = node_lang(obj);
var recv = ltn(self, recv_lang);
if recv == null { ret 0; }
var usize_lang = LangType.make_int(&self.sema.types, @sizeof(usize) * 8, false);
var usize_ir = to_ir_type(self, usize_lang);
if kind_is(self, recv_lang, .Array) {
var arr = irt(self, recv.ir_type_id);
if arr == null { ret 0; }
match arr.node {
.Array(a) => { ret IrBuilder.type_const_int(self.ir, usize_ir, a.len); },
_ => { ret 0; },
}
}
if kind_is(self, recv_lang, .Slice) {
var slice_val = emit_expr(self, obj_ref);
if slice_val == 0 { ret 0; }
ret IrBuilder.agg_extract(self.ir, recv.ir_type_id, usize_ir, 1, slice_val);
}
ret 0;
}
fn emit_lvalue(self: *Ast2Ir, ref: usize): usize {
var n = Ast.node(self.ast, ref);
if n == null { ret 0; }
match n.body {
.ExprIdent(&x) => {
var d = defn(self, n.resolved_def_id);
if d != null && @enum2int(d.kind) == @enum2int(DefKind.Fn) { ret 0; }
var g: usize = 0;
if glob_get(self, n.resolved_def_id, &g) {
var lang = node_lang(n);
var ir = to_ir_type(self, lang);
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, ir);
ret IrBuilder.type_const_global(self.ir, ptr_ir, g - 1);
}
var slot: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &slot) { ret 0; }
ret slot;
},
.ExprUnary(&x) => {
if !punct_is(x.op, .Mul) { ret 0; }
ret emit_expr(self, x.operand);
},
.ExprField(&x) => { ret lvalue_field(self, n, x.obj, x.name); },
.ExprIndex(&x) => { ret lvalue_index(self, n, x.obj, x.index); },
_ => { ret 0; },
}
ret 0;
}
fn lvalue_field(self: *Ast2Ir, n: *Node, obj_ref: usize, field_name: ByteView): usize {
var obj = Ast.node(self.ast, obj_ref);
if obj == null { ret 0; }
var obj_lang = node_lang(obj);
var obj_ln = ltn(self, obj_lang);
if obj_ln == null { ret 0; }
var base: usize = 0;
var agg_lang: usize = 0;
if kind_is(self, obj_lang, .Ptr) {
base = emit_expr(self, obj_ref);
agg_lang = extra_elem(obj_ln.*);
} else {
base = emit_lvalue(self, obj_ref);
agg_lang = obj_lang;
}
if base == 0 { ret 0; }
var agg_ir = to_ir_type(self, agg_lang);
if agg_ir == 0 { ret 0; }
var idx: usize = 0;
if !TypeTable.agg_member_idx(&self.sema.types.ir, agg_ir, field_name, &idx) { ret 0; }
if kind_is(self, agg_lang, .Enum) { idx = idx + 1; }
ret IrBuilder.mem_field(self.ir, agg_ir, base, idx);
}
fn lvalue_index(self: *Ast2Ir, n: *Node, obj_ref: usize, index_ref: usize): usize {
var obj = Ast.node(self.ast, obj_ref);
if obj == null { ret 0; }
var obj_lang = node_lang(obj);
var obj_ln = ltn(self, obj_lang);
if obj_ln == null { ret 0; }
var elem_lang: usize = 0;
if kind_is(self, obj_lang, .Ptr) || kind_is(self, obj_lang, .Array) ||
kind_is(self, obj_lang, .Slice) {
elem_lang = extra_elem(obj_ln.*);
}
if kind_is(self, obj_lang, .Tuple) {
var idx_node = Ast.node(self.ast, index_ref);
if idx_node == null || !is_int_lit(idx_node) { ret 0; }
var i = int_lit_u64(idx_node) as usize;
var base = emit_lvalue(self, obj_ref);
if base == 0 { ret 0; }
var tuple_ir = to_ir_type(self, obj_lang);
if tuple_ir == 0 { ret 0; }
ret IrBuilder.mem_field(self.ir, tuple_ir, base, i);
}
var base: usize = 0;
if kind_is(self, obj_lang, .Ptr) {
base = emit_expr(self, obj_ref);
} else if kind_is(self, obj_lang, .Array) {
base = emit_lvalue(self, obj_ref);
} else if kind_is(self, obj_lang, .Slice) {
var slice_val = emit_expr(self, obj_ref);
if slice_val == 0 { ret 0; }
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, to_ir_type(self, elem_lang));
base = IrBuilder.agg_extract(self.ir, ptr_ir, ptr_ir, 0, slice_val);
} else {
ret 0;
}
if base == 0 { ret 0; }
var idx = emit_expr(self, index_ref);
if idx == 0 { ret 0; }
var elem_ir = to_ir_type(self, elem_lang);
if elem_ir == 0 { ret 0; }
ret IrBuilder.mem_offset(self.ir, elem_ir, base, idx);
}
fn is_int_lit(n: *Node): bool {
match n.body { .ExprIntLit(&x) => { ret true; }, _ => { ret false; } }
ret false;
}
fn extra_elem(e: LangEntry): usize {
match e.extra {
.Elem(x) => { ret x; },
.Array(a) => { ret a.elem; },
_ => { ret 0; },
}
ret 0;
}
fn ast2ir_emit_cast_builtin(self: *Ast2Ir, n: *Node, in: Intrin, ft: usize, tt: usize,
v: usize): usize {
match in {
.IntCast => {
var fs = irt(self, ft);
if fs != null {
match fs.node {
.Int(x) => { if x.is_signed { ret IrBuilder.cast_sext(self.ir, ft, tt, v); } },
_ => {},
}
}
ret IrBuilder.cast_zext(self.ir, ft, tt, v);
},
.Truncate => { ret IrBuilder.cast_trunc(self.ir, ft, tt, v); },
.BitCast => { ret IrBuilder.cast_bitcast(self.ir, ft, tt, v); },
.FloatCast => { ret ir_float_cast(self, ft, tt, v); },
.Float2Int => { ret IrBuilder.case_float2int(self.ir, ft, tt, v); },
.Int2Float => { ret IrBuilder.case_int2float(self.ir, ft, tt, v); },
.Bool2Int => { ret IrBuilder.cast_bool2int(self.ir, ft, tt, v); },
.Ptr2Int => { ret IrBuilder.cast_ptr2int(self.ir, ft, tt, v); },
.Int2Ptr => { ret IrBuilder.cast_int2ptr(self.ir, ft, tt, v); },
.PtrCast => { ret IrBuilder.cast_bitcast(self.ir, ft, tt, v); },
_ => {},
}
Diag.err(self.diag, n.span, .Internal, "ast2ir: unknown cast builtin");
ret 0;
}
fn ir_float_cast(self: *Ast2Ir, ft: usize, tt: usize, v: usize): usize {
var fs = irt(self, ft);
var ts = irt(self, tt);
var fbits: usize = 0;
var tbits: usize = 0;
if fs != null { match fs.node { .Float(x) => { fbits = x.bits; }, _ => {} } }
if ts != null { match ts.node { .Float(x) => { tbits = x.bits; }, _ => {} } }
if fbits > tbits { ret IrBuilder.cast_ftrunc(self.ir, ft, tt, v); }
ret IrBuilder.cast_fext(self.ir, ft, tt, v);
}
fn emit_cast(self: *Ast2Ir, n: *Node): usize {
var eref: usize = 0;
var tref: usize = 0;
match n.body { .ExprCast(&x) => { eref = x.expr; tref = x.type_expr; }, _ => {} }
var v = emit_expr_raw(self, eref);
if v == 0 { ret 0; }
var src = Ast.node(self.ast, eref);
var dst = Ast.node(self.ast, tref);
var ft = node_ir(self, src);
var tt = node_ir(self, dst);
if ft == 0 || tt == 0 { ret 0; }
var in = IntrinOps.lookup_cast(&self.sema.types.ir, ft, tt);
if @enum2int(in) == @enum2int(Intrin.None) {
Diag.err(self.diag, n.span, .CastInvalid, "as: no conversion between the given types");
ret 0;
}
ret ast2ir_emit_cast_builtin(self, n, in, ft, tt, v);
}
fn ast2ir_field_from_value(self: *Ast2Ir, n: *Node): usize {
var obj_ref: usize = 0;
var field_name = ByteView.empty();
match n.body { .ExprField(&x) => { obj_ref = x.obj; field_name = x.name; }, _ => {} }
var obj = Ast.node(self.ast, obj_ref);
if obj == null { ret 0; }
var obj_lang = node_lang(obj);
var obj_ln = ltn(self, obj_lang);
if obj_ln == null { ret 0; }
var agg_ir = to_ir_type(self, obj_lang);
if agg_ir == 0 { ret 0; }
var idx: usize = 0;
if !TypeTable.agg_member_idx(&self.sema.types.ir, agg_ir, field_name, &idx) { ret 0; }
var field_idx = idx;
if kind_is(self, obj_lang, .Enum) { field_idx = idx + 1; }
var field_ir: usize = 0;
var agg_tn = irt(self, agg_ir);
if agg_tn != null {
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match agg_tn.node {
.Struct(a) => { variants = a.variants; },
.Union(a) => { variants = a.variants; },
.Enum(a) => { variants = a.variants; },
.Shape(a) => { variants = a.variants; },
_ => {},
}
if field_idx < variants.len { field_ir = variant_at(variants, field_idx); }
}
var val = emit_expr(self, obj_ref);
if val == 0 { ret 0; }
ret IrBuilder.agg_extract(self.ir, agg_ir, field_ir, field_idx, val);
}
fn ast2ir_enum_variant(self: *Ast2Ir, n: *Node): usize {
var t = node_ir(self, n);
if t == 0 { ret 0; }
var tn = irt(self, t);
if tn == null { ret 0; }
var field_name = ByteView.empty();
match n.body { .ExprField(&x) => { field_name = x.name; }, _ => {} }
var vidx: usize = 0;
if !TypeTable.agg_member_idx(&self.sema.types.ir, t, field_name, &vidx) { ret 0; }
var tag_ir: usize = 0;
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match tn.node { .Enum(a) => { tag_ir = a.tag_type; variants = a.variants; }, _ => { ret 0; } }
var eptr = IrBuilder.mem_alloca(self.ir, t, const_usize(self, 1));
var tptr = IrBuilder.mem_field(self.ir, t, eptr, 0);
var tag_val = IrBuilder.type_const_int(self.ir, tag_ir, vidx);
IrBuilder.mem_store(self.ir, tag_ir, tptr, tag_val);
if vidx < variants.len {
var payload_ir = variant_at(variants, vidx);
if payload_ir != 0 {
var pptr = IrBuilder.mem_field(self.ir, t, eptr, vidx + 1);
var zero = emit_zero_value(self, payload_ir);
IrBuilder.mem_store(self.ir, payload_ir, pptr, zero);
}
}
ret eptr;
}
fn emit_expr(self: *Ast2Ir, ref: usize): usize {
var val = emit_expr_raw(self, ref);
if val == 0 { ret 0; }
var n = Ast.node(self.ast, ref);
if n == null || n.expected_lang_tid == 0 { ret val; }
copy_attrs_node(self, n, val);
var from = n.resolved_lang_tid;
var to = n.expected_lang_tid;
if from == 0 || from == to { ret val; }
var from_ir = to_ir_type(self, from);
var to_ir = to_ir_type(self, to);
if from_ir == 0 || to_ir == 0 || from_ir == to_ir { ret val; }
ret emit_implicit_cast(self, n, from_ir, to_ir, val);
}
fn emit_expr_raw(self: *Ast2Ir, ref: usize): usize {
var n = Ast.node(self.ast, ref);
if n == null { ret 0; }
match n.body {
.ExprIntLit(&x) => { ret emit_int_lit(self, n); },
.ExprFloatLit(&x) => { ret IrBuilder.type_const_float(self.ir, node_ir(self, n), 0.0); },
.ExprCharLit(&x) => { ret IrBuilder.type_const_char(self.ir, node_ir(self, n), x.value); },
.ExprStringLit(&sl) => { ret emit_string_lit(self, n, sl.data); },
.ExprBoolLit(&x) => { ret IrBuilder.type_const_bool(self.ir, node_ir(self, n), x.value); },
.ExprNullLit => { ret IrBuilder.type_const_ptr(self.ir, node_ir(self, n), 0); },
.ExprUndefinedLit => { ret 0; },
.ExprIdent(&x) => { ret emit_ident(self, n); },
.ExprUnary(&x) => { ret emit_unary(self, n, ref, x.op, x.operand); },
.ExprBinary(&x) => { ret emit_binary(self, n, x.op, x.left, x.right); },
.ExprCall(&x) => { ret emit_call(self, n, x.callee, x.args); },
.ExprField(&x) => { ret emit_field(self, n, ref, x.obj, x.name); },
.ExprIndex(&x) => {
var ptr = emit_lvalue(self, ref);
if ptr == 0 { ret 0; }
ret IrBuilder.mem_load(self.ir, node_ir(self, n), ptr);
},
.ExprCast(&x) => { ret emit_cast(self, n); },
.ExprTuple(&x) => { ret emit_tuple(self, n, x.items); },
.ExprArrayLit(&x) => { ret emit_array_lit(self, n, x.elems); },
.ExprAggregateLit(&x) => { ret emit_aggregate_lit(self, n, x.name, x.fields); },
.ExprBlock(&x) => { ret emit_block(self, n, x.stmts, x.tail_expr); },
.ExprIf(&x) => { ret emit_if_like(self, ref); },
.ExprIfvar(&x) => { ret emit_if_like(self, ref); },
.ExprMatch(&x) => { ret emit_match(self, n, x.matched_expr, x.arms); },
.ExprWhile(&x) => { ret emit_loop_like(self, ref); },
.ExprLoop(&x) => { ret emit_loop_like(self, ref); },
.ExprFor(&x) => {
Diag.err(self.diag, n.span, .UnhandledNode, "for is not implemented yet");
ret 0;
},
.ExprSlice(&x) => { ret emit_slice(self, n, x.obj, x.begin, x.end); },
.ExprComptimeCall(&x) => {
var in = IntrinOps.lookup_call(x.name);
if @enum2int(in) == @enum2int(Intrin.None) { ret 0; }
ret ast2ir_intrin_call(self, n, in, x.args);
},
_ => { ret 0; },
}
ret 0;
}
fn emit_int_lit(self: *Ast2Ir, n: *Node): usize {
var it = node_ir(self, n);
var itn = irt(self, it);
var is_float = false;
if itn != null {
match itn.node { .Float(x) => { is_float = true; }, _ => {} }
}
if is_float { ret IrBuilder.type_const_float(self.ir, it, 0.0); }
ret IrBuilder.type_const_int(self.ir, it, int_lit_u64(n) as usize);
}
fn emit_string_lit(self: *Ast2Ir, n: *Node, data: ByteView): usize {
// parser 已解码(不含引号);字节由 Ast 持有,IR 只借用。
@assert(data.len != 0 || data.ptr != null, "empty string bytes must be non-null");
var len = data.len;
var u8_lang = LangType.make_int(&self.sema.types, 8, false);
var u8_ir = to_ir_type(self, u8_lang);
var ptr_u8_ir = TypeTable.make_ptr(&self.sema.types.ir, u8_ir);
var usize_lang = LangType.make_int(&self.sema.types, @sizeof(usize) * 8, false);
var usize_ir = to_ir_type(self, usize_lang);
var arr_lang = LangType.make_array(&self.sema.types, u8_lang, len);
var arr_ir = to_ir_type(self, arr_lang);
var bytes_ptr = data.ptr as usize;
var raw_c = IrBuilder.type_const_bytes(self.ir, arr_ir, bytes_ptr);
var ptr = IrBuilder.mem_global_alloc(self.ir, ptr_u8_ir, raw_c);
var len_c = IrBuilder.type_const_int(self.ir, usize_ir, len);
var slice_ir = node_ir(self, n);
var node = IrBuilder.agg_construct(self.ir, slice_ir);
IrBuilder.agg_construct_field(self.ir, node, ptr);
IrBuilder.agg_construct_field(self.ir, node, len_c);
ret node;
}
fn emit_ident(self: *Ast2Ir, n: *Node): usize {
var d = defn(self, n.resolved_def_id);
if d != null && @enum2int(d.kind) == @enum2int(DefKind.Fn) {
var fn_ref: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &fn_ref) { ret 0; }
ret IrBuilder.type_const_fn(self.ir, node_ir(self, n), fn_ref);
}
var g: usize = 0;
if glob_get(self, n.resolved_def_id, &g) {
var ir = node_ir(self, n);
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, ir);
var ptr = IrBuilder.type_const_global(self.ir, ptr_ir, g - 1);
ret IrBuilder.mem_load(self.ir, ir, ptr);
}
var slot: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &slot) { ret 0; }
ret IrBuilder.mem_load(self.ir, node_ir(self, n), slot);
}
fn emit_unary(self: *Ast2Ir, n: *Node, ref: usize, op: Punct, operand: usize): usize {
if punct_is(op, .And) { ret emit_lvalue(self, operand); }
if punct_is(op, .Mul) {
var ptr = emit_lvalue(self, ref);
if ptr == 0 { ret 0; }
ret IrBuilder.mem_load(self.ir, node_ir(self, n), ptr);
}
var v = emit_expr(self, operand);
if v == 0 { ret 0; }
var t = node_ir(self, n);
if punct_is(op, .Sub) { ret IrBuilder.arith_neg(self.ir, t, v); }
if punct_is(op, .Not) { ret IrBuilder.arith_not(self.ir, t, v); }
if punct_is(op, .BitNot) { ret IrBuilder.arith_not(self.ir, t, v); }
ret 0;
}
fn emit_binary(self: *Ast2Ir, n: *Node, op: Punct, left: usize, right: usize): usize {
if punct_is(op, .Assign) {
var ptr = emit_lvalue(self, left);
var v = emit_expr(self, right);
if ptr == 0 || v == 0 { ret 0; }
var ln = Ast.node(self.ast, left);
var t = node_ir(self, ln);
IrBuilder.mem_store(self.ir, t, ptr, v);
ret v;
}
if is_assign_arith(op) { ret emit_assign_arith(self, op, left, right); }
if punct_is(op, .OrOr) || punct_is(op, .AndAnd) {
ret emit_short_circuit(self, op, left, right);
}
var l = emit_expr(self, left);
var r = emit_expr(self, right);
if l == 0 || r == 0 { ret 0; }
var ln = Ast.node(self.ast, left);
var lt = node_lang(ln);
var lln = ltn(self, lt);
if lln != null && punct_is_op_ptr_addsub(op) && kind_is(self, lt, .Ptr) {
var elem_lang = extra_elem(lln.*);
var elem_ir = to_ir_type(self, elem_lang);
var rn = Ast.node(self.ast, right);
var rt_ir = node_ir(self, rn);
var off = r;
if punct_is(op, .Sub) { off = IrBuilder.arith_neg(self.ir, rt_ir, r); }
ret IrBuilder.mem_offset(self.ir, elem_ir, l, off);
}
var t = node_ir(self, n);
if punct_is(op, .Add) { ret IrBuilder.arith_add(self.ir, t, l, r); }
if punct_is(op, .Sub) { ret IrBuilder.arith_sub(self.ir, t, l, r); }
if punct_is(op, .Mul) { ret IrBuilder.arith_mul(self.ir, t, l, r); }
if punct_is(op, .Div) { ret IrBuilder.arith_div(self.ir, t, l, r); }
if punct_is(op, .Mod) { ret IrBuilder.arith_rem(self.ir, t, l, r); }
if punct_is(op, .And) { ret IrBuilder.arith_and(self.ir, t, l, r); }
if punct_is(op, .Or) { ret IrBuilder.arith_or(self.ir, t, l, r); }
if punct_is(op, .Xor) { ret IrBuilder.arith_xor(self.ir, t, l, r); }
if punct_is(op, .LSh) { ret IrBuilder.arith_shl(self.ir, t, l, r); }
if punct_is(op, .RSh) { ret IrBuilder.arith_shr(self.ir, t, l, r); }
if is_cmp_punct(op) {
var op_tid = node_ir(self, ln);
if punct_is(op, .Eq) { ret IrBuilder.cmp_eq(self.ir, op_tid, l, r); }
if punct_is(op, .Neq) { ret IrBuilder.cmp_ne(self.ir, op_tid, l, r); }
if punct_is(op, .Lt) { ret IrBuilder.cmp_lt(self.ir, op_tid, l, r); }
if punct_is(op, .Le) { ret IrBuilder.cmp_le(self.ir, op_tid, l, r); }
if punct_is(op, .Gt) { ret IrBuilder.cmp_gt(self.ir, op_tid, l, r); }
if punct_is(op, .Ge) { ret IrBuilder.cmp_ge(self.ir, op_tid, l, r); }
ret 0;
}
ret 0;
}
fn punct_is_op_ptr_addsub(op: Punct): bool {
ret punct_is(op, .Add) || punct_is(op, .Sub);
}
fn is_cmp_punct(op: Punct): bool {
match op {
.Eq => { ret true; }, .Neq => { ret true; }, .Lt => { ret true; },
.Le => { ret true; }, .Gt => { ret true; }, .Ge => { ret true; }, _ => { ret false; },
}
ret false;
}
fn emit_assign_arith(self: *Ast2Ir, op: Punct, left: usize, right: usize): usize {
var ptr = emit_lvalue(self, left);
if ptr == 0 { ret 0; }
var ln = Ast.node(self.ast, left);
var t = node_ir(self, ln);
var lv = IrBuilder.mem_load(self.ir, t, ptr);
var rv = emit_expr(self, right);
if lv == 0 || rv == 0 { ret 0; }
var res: usize = 0;
if punct_is(op, .AddAssign) { res = IrBuilder.arith_add(self.ir, t, lv, rv); }
else if punct_is(op, .SubAssign) { res = IrBuilder.arith_sub(self.ir, t, lv, rv); }
else if punct_is(op, .MulAssign) { res = IrBuilder.arith_mul(self.ir, t, lv, rv); }
else if punct_is(op, .DivAssign) { res = IrBuilder.arith_div(self.ir, t, lv, rv); }
else if punct_is(op, .ModAssign) { res = IrBuilder.arith_rem(self.ir, t, lv, rv); }
else if punct_is(op, .AndAssign) { res = IrBuilder.arith_and(self.ir, t, lv, rv); }
else if punct_is(op, .OrAssign) { res = IrBuilder.arith_or(self.ir, t, lv, rv); }
else if punct_is(op, .XorAssign) { res = IrBuilder.arith_xor(self.ir, t, lv, rv); }
else if punct_is(op, .LShAssign) { res = IrBuilder.arith_shl(self.ir, t, lv, rv); }
else if punct_is(op, .RShAssign) { res = IrBuilder.arith_shr(self.ir, t, lv, rv); }
else { ret 0; }
if res == 0 { ret 0; }
IrBuilder.mem_store(self.ir, t, ptr, res);
ret res;
}
fn emit_short_circuit(self: *Ast2Ir, op: Punct, left: usize, right: usize): usize {
var b_lang = LangType.make_bool(&self.sema.types);
var b_ir = to_ir_type(self, b_lang);
var slot = IrBuilder.mem_alloca(self.ir, b_ir, const_usize(self, 1));
var l = emit_expr(self, left);
if l == 0 { ret 0; }
IrBuilder.mem_store(self.ir, b_ir, slot, l);
var br = IrBuilder.control_br(self.ir, l);
IrBuilder.block_end(self.ir);
var rhs_blk = IrBuilder.block_begin(self.ir);
var r = emit_expr(self, right);
if r != 0 { IrBuilder.mem_store(self.ir, b_ir, slot, r); }
var rhs_end: usize = 0;
if !self.ir.terminated { rhs_end = IrBuilder.control_jmp(self.ir); }
IrBuilder.block_end(self.ir);
var end_blk = IrBuilder.block_begin(self.ir);
if punct_is(op, .AndAnd) {
IrBuilder.control_br_fixup(self.ir, br, rhs_blk, end_blk);
} else {
IrBuilder.control_br_fixup(self.ir, br, end_blk, rhs_blk);
}
if rhs_end != 0 { IrBuilder.control_jmp_fixup(self.ir, rhs_end, end_blk); }
ret IrBuilder.mem_load(self.ir, b_ir, slot);
}
fn emit_call(self: *Ast2Ir, n: *Node, callee_ref: usize, args: Vec): usize {
var callee = Ast.node(self.ast, callee_ref);
if callee != null && is_ident(callee) {
var cname = ident_name(callee);
var in = IntrinOps.lookup_call(cname);
if @enum2int(in) != @enum2int(Intrin.None) {
ret ast2ir_intrin_call(self, n, in, args);
}
}
var fn_val = emit_expr(self, callee_ref);
if fn_val == 0 { ret 0; }
var arg_refs = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < args.len {
push_u(&arg_refs, emit_expr(self, list_at(args, i)));
i = i + 1;
}
var ret_t = node_ir(self, n);
var call = IrBuilder.control_call(self.ir, ret_t, fn_val);
if call == 0 { Vec.drop(&arg_refs); ret 0; }
i = 0;
while i < arg_refs.len {
IrBuilder.control_call_param(self.ir, call, list_at(arg_refs, i));
i = i + 1;
}
Vec.drop(&arg_refs);
ret call;
}
fn is_ident(n: *Node): bool {
match n.body { .ExprIdent(&x) => { ret true; }, _ => { ret false; } }
ret false;
}
fn ident_name(n: *Node): ByteView {
match n.body { .ExprIdent(&x) => { ret x.name; }, _ => { ret ByteView.empty(); } }
ret ByteView.empty();
}
fn emit_field(self: *Ast2Ir, n: *Node, ref: usize, obj_ref: usize, field_name: ByteView): usize {
var obj = Ast.node(self.ast, obj_ref);
var obj_lang = node_lang(obj);
var obj_ln = ltn(self, obj_lang);
if obj_ln != null {
var in = IntrinOps.lookup_field(obj_ln.kind, field_name);
if @enum2int(in) != @enum2int(Intrin.None) {
ret ast2ir_intrin_field(self, n, in);
}
}
if n.resolved_def_id != 0 {
var fd = defn(self, n.resolved_def_id);
if fd != null && @enum2int(fd.kind) == @enum2int(DefKind.Fn) {
var fn_ref: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &fn_ref) { ret 0; }
ret IrBuilder.type_const_fn(self.ir, node_ir(self, n), fn_ref);
}
}
var objn = Ast.node(self.ast, obj_ref);
if objn != null && is_ident(objn) {
var od = defn(self, objn.resolved_def_id);
var et = node_lang(objn);
if od != null && @enum2int(od.kind) == @enum2int(DefKind.Agg) && kind_is(self, et, .Enum) {
ret ast2ir_enum_variant(self, n);
}
}
var ptr = emit_lvalue(self, ref);
if ptr != 0 { ret IrBuilder.mem_load(self.ir, node_ir(self, n), ptr); }
ret ast2ir_field_from_value(self, n);
}
fn emit_tuple(self: *Ast2Ir, n: *Node, items: Vec): usize {
var t = node_ir(self, n);
var nelems = items.len;
var field_refs = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < nelems {
var f = emit_expr(self, list_at(items, i));
if f == 0 { Vec.drop(&field_refs); ret 0; }
push_u(&field_refs, f);
i = i + 1;
}
var node = IrBuilder.agg_construct(self.ir, t);
if node == 0 { Vec.drop(&field_refs); ret 0; }
i = 0;
while i < nelems {
IrBuilder.agg_construct_field(self.ir, node, list_at(field_refs, i));
i = i + 1;
}
Vec.drop(&field_refs);
ret node;
}
fn emit_array_lit(self: *Ast2Ir, n: *Node, elems: Vec): usize {
var t = node_ir(self, n);
if t == 0 { ret 0; }
var nelems = elems.len;
var sln = ltn(self, node_lang(n));
if sln != null && kind_is(self, node_lang(n), .Slice) {
ret emit_array_to_slice(self, n, t, elems, extra_elem(sln.*), nelems);
}
var field_refs = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < nelems {
var f = emit_expr(self, list_at(elems, i));
if f == 0 { Vec.drop(&field_refs); ret 0; }
push_u(&field_refs, f);
i = i + 1;
}
var node = IrBuilder.agg_construct(self.ir, t);
if node == 0 { Vec.drop(&field_refs); ret 0; }
i = 0;
while i < nelems {
IrBuilder.agg_construct_field(self.ir, node, list_at(field_refs, i));
i = i + 1;
}
Vec.drop(&field_refs);
ret node;
}
fn emit_array_to_slice(self: *Ast2Ir, n: *Node, t: usize, elems: Vec, elem_lang: usize,
nelems: usize): usize {
var elem_ir = to_ir_type(self, elem_lang);
if elem_ir == 0 { ret 0; }
var usize_lang = LangType.make_int(&self.sema.types, @sizeof(usize) * 8, false);
var usize_ir = to_ir_type(self, usize_lang);
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, elem_ir);
var base: usize = 0;
if nelems == 0 {
base = IrBuilder.type_const_ptr(self.ir, ptr_ir, 0);
} else {
var arr_ir = TypeTable.make_array(&self.sema.types.ir, elem_ir, nelems);
var slot = IrBuilder.mem_alloca(self.ir, arr_ir, const_usize(self, 1));
var i: usize = 0;
while i < nelems {
var fptr = IrBuilder.mem_field(self.ir, arr_ir, slot, i);
var v = emit_expr(self, list_at(elems, i));
if v == 0 { ret 0; }
IrBuilder.mem_store(self.ir, elem_ir, fptr, v);
i = i + 1;
}
base = IrBuilder.mem_field(self.ir, arr_ir, slot, 0);
}
if base == 0 { ret 0; }
var len = IrBuilder.type_const_int(self.ir, usize_ir, nelems);
var node = IrBuilder.agg_construct(self.ir, t);
if node == 0 { ret 0; }
IrBuilder.agg_construct_field(self.ir, node, base);
IrBuilder.agg_construct_field(self.ir, node, len);
ret node;
}
fn emit_aggregate_lit(self: *Ast2Ir, n: *Node, type_name: ByteView, fields: Vec): usize {
var t = node_ir(self, n);
if t == 0 { ret 0; }
var tn = irt(self, t);
if tn == null { ret 0; }
var is_enum = false;
var tag_ir: usize = 0;
var variants = Vec.blank(@sizeof(usize), @alignof(usize));
match tn.node {
.Enum(a) => { is_enum = true; tag_ir = a.tag_type; variants = a.variants; },
.Struct(a) => { variants = a.variants; },
.Union(a) => { variants = a.variants; },
.Shape(a) => { variants = a.variants; },
_ => {},
}
if is_enum {
if fields.len != 1 { ret 0; }
var item = Ast.node(self.ast, list_at(fields, 0));
if item == null { ret 0; }
var iname = ByteView.empty();
var iexpr: usize = 0;
match item.body { .StructInitItem(si) => { iname = si.name; iexpr = si.expr; }, _ => { ret 0; } }
var vidx: usize = 0;
if !TypeTable.agg_member_idx(&self.sema.types.ir, t, iname, &vidx) { ret 0; }
var eptr = IrBuilder.mem_alloca(self.ir, t, const_usize(self, 1));
var tptr = IrBuilder.mem_field(self.ir, t, eptr, 0);
var tag_val = IrBuilder.type_const_int(self.ir, tag_ir, vidx);
IrBuilder.mem_store(self.ir, tag_ir, tptr, tag_val);
if iexpr != 0 {
var payload_ir = variant_at(variants, vidx);
var pptr = IrBuilder.mem_field(self.ir, t, eptr, vidx + 1);
var payload = emit_expr(self, iexpr);
if payload == 0 { ret 0; }
IrBuilder.mem_store(self.ir, payload_ir, pptr, payload);
}
ret eptr;
}
var nfields = variants.len;
var field_tids = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < nfields {
push_u(&field_tids, variant_at(variants, i));
i = i + 1;
}
var fvals = Vec.blank(@sizeof(usize), @alignof(usize));
i = 0;
while i < nfields {
push_u(&fvals, 0);
i = i + 1;
}
i = 0;
while i < fields.len {
var item = Ast.node(self.ast, list_at(fields, i));
if item != null {
var iname = ByteView.empty();
var iexpr: usize = 0;
match item.body { .StructInitItem(si) => { iname = si.name; iexpr = si.expr; }, _ => { i = i + 1; continue; } }
var idx: usize = 0;
if TypeTable.agg_member_idx(&self.sema.types.ir, t, iname, &idx) {
var f = emit_expr(self, iexpr);
if f != 0 { set_u(&fvals, idx, f); }
}
}
i = i + 1;
}
i = 0;
while i < nfields {
if list_at(fvals, i) == 0 {
var f = emit_zero_value(self, list_at(field_tids, i));
if f == 0 { Vec.drop(&field_tids); Vec.drop(&fvals); ret 0; }
set_u(&fvals, i, f);
}
i = i + 1;
}
var node = IrBuilder.agg_construct(self.ir, t);
if node == 0 { Vec.drop(&field_tids); Vec.drop(&fvals); ret 0; }
i = 0;
while i < nfields {
IrBuilder.agg_construct_field(self.ir, node, list_at(fvals, i));
i = i + 1;
}
Vec.drop(&field_tids);
Vec.drop(&fvals);
ret node;
}
fn set_u(v: *Vec, i: usize, x: usize) {
Vec.set(v, i, &x as *u8);
}
fn emit_deferred_until(self: *Ast2Ir, base: usize) {
if self.defer_stack.len <= base { ret; }
var saved = self.defer_stack;
self.defer_stack = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < saved.len {
push_u(&self.defer_stack, list_at(saved, i));
i = i + 1;
}
while !self.ir.terminated && self.defer_stack.len > base {
var out: usize = 0;
match Vec.pop(&self.defer_stack, &out as *u8) { .Ok => {}, .Err(e) => { break; } }
emit_stmt(self, out);
}
Vec.drop(&self.defer_stack);
self.defer_stack = saved;
}
fn emit_block(self: *Ast2Ir, n: *Node, stmts: Vec, tail: usize): usize {
var defer_base = self.defer_stack.len;
var i: usize = 0;
while i < stmts.len && !self.ir.terminated {
emit_stmt(self, list_at(stmts, i));
i = i + 1;
}
var value: usize = 0;
if !self.ir.terminated && tail != 0 { value = emit_expr(self, tail); }
if !self.ir.terminated { emit_deferred_until(self, defer_base); }
self.defer_stack.len = defer_base;
ret value;
}
fn emit_match(self: *Ast2Ir, n: *Node, matched_ref: usize, arms: Vec): usize {
var matched_node = Ast.node(self.ast, matched_ref);
var matched_lang = node_lang(matched_node);
var matched_ir = to_ir_type(self, matched_lang);
if matched_ir == 0 { ret 0; }
var matched_place_mode = false;
var scan: usize = 0;
while scan < arms.len && !matched_place_mode {
var sa = Ast.node(self.ast, list_at(arms, scan));
if sa != null {
var spat: usize = 0;
match sa.body { .MatchArm(ma) => { spat = ma.pattern; }, _ => {} }
if pattern_has_ref(self, spat) { matched_place_mode = true; }
}
scan = scan + 1;
}
var scrut_slot: usize = 0;
if matched_place_mode {
scrut_slot = emit_lvalue(self, matched_ref);
if scrut_slot == 0 {
var mn = Ast.node(self.ast, matched_ref);
if mn != null {
Diag.err(self.diag, mn.span, .NoLvalue,
"cannot bind by reference: matched value is not an lvalue");
} else {
Diag.err(self.diag, n.span, .NoLvalue,
"cannot bind by reference: matched value is not an lvalue");
}
ret 0;
}
} else {
scrut_slot = IrBuilder.mem_alloca(self.ir, matched_ir, const_usize(self, 1));
var scrut_val = emit_expr(self, matched_ref);
if scrut_val == 0 { ret 0; }
IrBuilder.mem_store(self.ir, matched_ir, scrut_slot, scrut_val);
}
var narms = arms.len;
var arm_branches = Vec.blank(@sizeof(usize), @alignof(usize));
var cond_blks = Vec.blank(@sizeof(usize), @alignof(usize));
var i: usize = 0;
while i < narms {
push_u(&cond_blks, self.ir.current_block);
var arm = Ast.node(self.ast, list_at(arms, i));
var cond: usize = 0;
if arm != null {
var pat: usize = 0;
match arm.body { .MatchArm(ma) => { pat = ma.pattern; }, _ => {} }
cond = emit_match_check(self, pat, matched_ir, scrut_slot);
}
if cond != 0 {
push_u(&arm_branches, IrBuilder.control_br(self.ir, cond));
} else {
push_u(&arm_branches, IrBuilder.control_jmp(self.ir));
}
IrBuilder.block_end(self.ir);
if i + 1 < narms { IrBuilder.block_begin(self.ir); }
i = i + 1;
}
var no_match_blk = IrBuilder.block_begin(self.ir);
var no_match_jmp = IrBuilder.control_jmp(self.ir);
IrBuilder.block_end(self.ir);
var body_jmps = Vec.blank(@sizeof(usize), @alignof(usize));
var body_blks = Vec.blank(@sizeof(usize), @alignof(usize));
i = 0;
while i < narms {
push_u(&body_blks, IrBuilder.block_begin(self.ir));
var arm = Ast.node(self.ast, list_at(arms, i));
if arm != null {
var pat: usize = 0;
var body: usize = 0;
match arm.body { .MatchArm(ma) => { pat = ma.pattern; body = ma.body; }, _ => {} }
emit_pattern_bind(self, pat, scrut_slot, matched_ir, matched_place_mode);
if body != 0 { emit_expr(self, body); }
}
var j: usize = 0;
if !self.ir.terminated { j = IrBuilder.control_jmp(self.ir); }
push_u(&body_jmps, j);
IrBuilder.block_end(self.ir);
i = i + 1;
}
var end_blk = IrBuilder.block_begin(self.ir);
i = 0;
while i < narms {
var branch = list_at(arm_branches, i);
if branch != 0 {
var bk = ir_kind_of(self, branch);
var body_blk = list_at(body_blks, i);
match bk {
.ControlBr => {
var next_blk = no_match_blk;
if i + 1 < narms { next_blk = list_at(cond_blks, i + 1); }
IrBuilder.control_br_fixup(self.ir, branch, body_blk, next_blk);
},
.ControlJmp => { IrBuilder.control_jmp_fixup(self.ir, branch, body_blk); },
_ => {},
}
}
i = i + 1;
}
i = 0;
while i < narms {
var j = list_at(body_jmps, i);
if j != 0 { IrBuilder.control_jmp_fixup(self.ir, j, end_blk); }
i = i + 1;
}
IrBuilder.control_jmp_fixup(self.ir, no_match_jmp, end_blk);
Vec.drop(&arm_branches);
Vec.drop(&cond_blks);
Vec.drop(&body_jmps);
Vec.drop(&body_blks);
ret 0;
}
fn emit_slice(self: *Ast2Ir, n: *Node, obj_ref: usize, begin_ref: usize, end_ref: usize): usize {
var obj = Ast.node(self.ast, obj_ref);
var obj_lang = node_lang(obj);
var obj_ln = ltn(self, obj_lang);
if obj_ln == null { ret 0; }
var obj_elem = extra_elem(obj_ln.*);
var obj_ir = obj_ln.ir_type_id;
var slice_ir = node_ir(self, n);
if slice_ir == 0 { ret 0; }
var elem_ir = to_ir_type(self, obj_elem);
var usize_lang = LangType.make_int(&self.sema.types, @sizeof(usize) * 8, false);
var usize_ir = to_ir_type(self, usize_lang);
var base: usize = 0;
if kind_is(self, obj_lang, .Ptr) {
base = emit_expr(self, obj_ref);
} else if kind_is(self, obj_lang, .Slice) {
var slice_val = emit_expr(self, obj_ref);
if slice_val == 0 { ret 0; }
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, elem_ir);
base = IrBuilder.agg_extract(self.ir, obj_ir, ptr_ir, 0, slice_val);
} else {
base = emit_lvalue(self, obj_ref);
}
if base == 0 { ret 0; }
var begin_v: usize = 0;
if begin_ref != 0 { begin_v = emit_expr(self, begin_ref); }
else { begin_v = IrBuilder.type_const_int(self.ir, usize_ir, 0); }
if begin_v == 0 { ret 0; }
var ptr = IrBuilder.mem_offset(self.ir, elem_ir, base, begin_v);
if ptr == 0 { ret 0; }
var end_v: usize = 0;
if end_ref != 0 {
end_v = emit_expr(self, end_ref);
} else if kind_is(self, obj_lang, .Array) {
var arr = irt(self, obj_ir);
if arr == null { ret 0; }
var arr_len: usize = 0;
match arr.node { .Array(a) => { arr_len = a.len; }, _ => { ret 0; } }
end_v = IrBuilder.type_const_int(self.ir, usize_ir, arr_len);
} else if kind_is(self, obj_lang, .Slice) {
var slice_val = emit_expr(self, obj_ref);
end_v = IrBuilder.agg_extract(self.ir, obj_ir, usize_ir, 1, slice_val);
} else {
ret 0;
}
if end_v == 0 { ret 0; }
var len = IrBuilder.arith_sub(self.ir, usize_ir, end_v, begin_v);
var node = IrBuilder.agg_construct(self.ir, slice_ir);
if node == 0 { ret 0; }
IrBuilder.agg_construct_field(self.ir, node, ptr);
IrBuilder.agg_construct_field(self.ir, node, len);
ret node;
}
fn emit_if_like(self: *Ast2Ir, ref: usize): usize {
var n = Ast.node(self.ast, ref);
if n == null { ret 0; }
var is_ifvar = false;
match n.body { .ExprIfvar(&x) => { is_ifvar = true; }, _ => {} }
var result_t = node_ir(self, n);
var need_result = false;
if result_t != 0 {
var rtn = irt(self, result_t);
if rtn != null && !ir_is_unit(rtn) { need_result = true; }
}
var result_slot: usize = 0;
if need_result { result_slot = IrBuilder.mem_alloca(self.ir, result_t, const_usize(self, 1)); }
var cond: usize = 0;
var then_ref: usize = 0;
var else_ref: usize = 0;
if is_ifvar {
var vd_ref: usize = 0;
match n.body { .ExprIfvar(&x) => { vd_ref = x.var_decl; }, _ => {} }
var vd = Ast.node(self.ast, vd_ref);
if vd == null { ret 0; }
var slot = emit_var_decl(self, vd);
if slot == 0 { ret 0; }
cond = IrBuilder.mem_load(self.ir, node_ir(self, vd), slot);
match n.body {
.ExprIfvar(x) => { then_ref = x.then_block; else_ref = x.else_branch; },
_ => {},
}
} else {
match n.body {
.ExprIf(x) => { cond = emit_expr(self, x.cond); then_ref = x.then_block; else_ref = x.else_branch; },
_ => {},
}
}
if cond == 0 { ret 0; }
var br = IrBuilder.control_br(self.ir, cond);
IrBuilder.block_end(self.ir);
var then_blk = IrBuilder.block_begin(self.ir);
var then_v: usize = 0;
if then_ref != 0 { then_v = emit_expr(self, then_ref); }
if result_slot != 0 && then_v != 0 {
IrBuilder.mem_store(self.ir, result_t, result_slot, then_v);
}
var then_end: usize = 0;
if !self.ir.terminated { then_end = IrBuilder.control_jmp(self.ir); }
IrBuilder.block_end(self.ir);
var else_blk = IrBuilder.block_begin(self.ir);
var else_v: usize = 0;
if else_ref != 0 { else_v = emit_expr(self, else_ref); }
if result_slot != 0 && else_v != 0 {
IrBuilder.mem_store(self.ir, result_t, result_slot, else_v);
}
var else_end: usize = 0;
if !self.ir.terminated { else_end = IrBuilder.control_jmp(self.ir); }
IrBuilder.block_end(self.ir);
var merge_blk = IrBuilder.block_begin(self.ir);
IrBuilder.control_br_fixup(self.ir, br, then_blk, else_blk);
if then_end != 0 { IrBuilder.control_jmp_fixup(self.ir, then_end, merge_blk); }
if else_end != 0 { IrBuilder.control_jmp_fixup(self.ir, else_end, merge_blk); }
if result_slot == 0 { ret 0; }
ret IrBuilder.mem_load(self.ir, result_t, result_slot);
}
fn ir_is_unit(e: *TypeEntry): bool {
match e.node { .Unit => { ret true; }, _ => { ret false; } }
ret false;
}
fn emit_loop_like(self: *Ast2Ir, ref: usize): usize {
var n = Ast.node(self.ast, ref);
if n == null { ret 0; }
var is_while = false;
match n.body { .ExprWhile(&x) => { is_while = true; }, _ => {} }
var result_t = node_ir(self, n);
var need_result = false;
if result_t != 0 {
var rtn = irt(self, result_t);
if rtn != null && !ir_is_unit(rtn) { need_result = true; }
}
var result_slot: usize = 0;
if need_result { result_slot = IrBuilder.mem_alloca(self.ir, result_t, const_usize(self, 1)); }
var pre_jmp: usize = 0;
if !self.ir.terminated { pre_jmp = IrBuilder.control_jmp(self.ir); }
IrBuilder.block_end(self.ir);
var head_blk: usize = 0;
var br: usize = 0;
if is_while {
head_blk = IrBuilder.block_begin(self.ir);
var cond: usize = 0;
match n.body { .ExprWhile(&x) => { cond = emit_expr(self, x.cond); }, _ => {} }
if cond == 0 { ret 0; }
br = IrBuilder.control_br(self.ir, cond);
IrBuilder.block_end(self.ir);
}
var body_blk = IrBuilder.block_begin(self.ir);
var cont_blk = body_blk;
if is_while { cont_blk = head_blk; }
var ctx = LoopCtx {
.cont_blk = cont_blk,
.defer_base = self.defer_stack.len,
.label = ByteView.empty(),
.breaks = Vec.blank(@sizeof(usize), @alignof(usize)),
.result_slot = result_slot,
.result_tid = result_t,
};
var label_ref: usize = 0;
var body_ref: usize = 0;
var else_ref: usize = 0;
if is_while {
match n.body {
.ExprWhile(x) => { label_ref = x.label; body_ref = x.body; else_ref = x.else_branch; },
_ => {},
}
} else {
match n.body {
.ExprLoop(x) => { label_ref = x.label; body_ref = x.body; },
_ => {},
}
}
if label_ref != 0 { ctx.label = label_name_of(self, label_ref); }
Vec.push(&self.loop_stack, &ctx as *u8);
if body_ref != 0 { emit_expr(self, body_ref); }
var body_end: usize = 0;
if !self.ir.terminated { body_end = IrBuilder.control_jmp(self.ir); }
IrBuilder.block_end(self.ir);
var after_blk = IrBuilder.block_begin(self.ir);
var else_v: usize = 0;
if is_while && else_ref != 0 { else_v = emit_expr(self, else_ref); }
if result_slot != 0 && else_v != 0 {
IrBuilder.mem_store(self.ir, result_t, result_slot, else_v);
}
if pre_jmp != 0 { IrBuilder.control_jmp_fixup(self.ir, pre_jmp, cont_blk); }
if br != 0 { IrBuilder.control_br_fixup(self.ir, br, body_blk, after_blk); }
if body_end != 0 { IrBuilder.control_jmp_fixup(self.ir, body_end, cont_blk); }
var top = loop_at(self, self.loop_stack.len - 1);
if top != null {
var i: usize = 0;
while i < top.breaks.len {
IrBuilder.control_jmp_fixup(self.ir, list_at(top.breaks, i), after_blk);
i = i + 1;
}
Vec.drop(&top.breaks);
}
pop_loop(self);
if result_slot == 0 { ret 0; }
ret IrBuilder.mem_load(self.ir, result_t, result_slot);
}
fn loop_at(self: *Ast2Ir, i: usize): *LoopCtx {
match Vec.get(&self.loop_stack, i) { .Ok(p) => { ret p as *LoopCtx; }, .Err(e) => { ret null; } }
ret null;
}
fn pop_loop(self: *Ast2Ir) {
var out: LoopCtx = LoopCtx {
.cont_blk = 0, .defer_base = 0, .label = ByteView.empty(),
.breaks = Vec.blank(@sizeof(usize), @alignof(usize)), .result_slot = 0, .result_tid = 0,
};
Vec.pop(&self.loop_stack, &out as *u8);
}
fn label_name_of(self: *Ast2Ir, ref: usize): ByteView {
if ref == 0 { ret ByteView.empty(); }
var ln = Ast.node(self.ast, ref);
if ln == null { ret ByteView.empty(); }
match ln.body { .Label(l) => { ret l.name; }, _ => { ret ByteView.empty(); } }
ret ByteView.empty();
}
fn find_loop_with_label(self: *Ast2Ir, lbl: ByteView): usize {
var i = self.loop_stack.len;
while i > 0 {
var c = loop_at(self, i - 1);
if c != null && c.label.len != 0 && ByteView.eq(c.label, lbl) { ret i - 1; }
i = i - 1;
}
ret ~(0 as usize);
}
fn emit_break_stmt(self: *Ast2Ir, n: *Node): usize {
if self.loop_stack.len == 0 { ret 0; }
var lbl = ByteView.empty();
var expr_ref: usize = 0;
match n.body {
.StatementBreak(&x) => { lbl = label_name_of(self, x.label); expr_ref = x.expr; },
_ => {},
}
var target_index = self.loop_stack.len - 1;
if lbl.len != 0 { target_index = find_loop_with_label(self, lbl); }
if target_index == ~(0 as usize) { ret 0; }
var target = loop_at(self, target_index);
if target == null { ret 0; }
if expr_ref != 0 && target.result_slot != 0 {
var bv = emit_expr(self, expr_ref);
if bv != 0 {
IrBuilder.mem_store(self.ir, target.result_tid, target.result_slot, bv);
}
}
emit_deferred_until(self, target.defer_base);
if self.ir.terminated { ret 0; }
var jmp = IrBuilder.control_jmp(self.ir);
var t2 = loop_at(self, target_index);
if t2 != null { push_u(&t2.breaks, jmp); }
ret 0;
}
fn emit_stmt(self: *Ast2Ir, ref: usize) {
var n = Ast.node(self.ast, ref);
if n == null { ret; }
match n.body {
.StatementRet(&x) => {
var ret_ir = to_ir_type(self, self.cur_ret_lang);
var v: usize = 0;
if x.expr != 0 { v = emit_expr(self, x.expr); }
emit_deferred_until(self, self.cur_defer_base);
if !self.ir.terminated { IrBuilder.control_ret(self.ir, ret_ir, v); }
},
.StatementDefer(&x) => { if x.body != 0 { push_u(&self.defer_stack, x.body); } },
.StatementComptime(&x) => { if x.body != 0 { emit_expr(self, x.body); } },
.StatementDecl(&x) => { if x.decl != 0 { emit_decl(self, x.decl); } },
.StatementExpr(&x) => { if x.expr != 0 { emit_expr(self, x.expr); } },
.ExprIf(&x) => { emit_if_like(self, ref); },
.ExprIfvar(&x) => { emit_if_like(self, ref); },
.ExprWhile(&x) => { emit_loop_like(self, ref); },
.ExprLoop(&x) => { emit_loop_like(self, ref); },
.StatementBreak(&x) => { emit_break_stmt(self, n); },
.StatementContinue(&x) => { emit_continue_stmt(self, n); },
.ExprBlock(&x) => { emit_expr(self, ref); },
_ => { emit_expr(self, ref); },
}
}
fn emit_continue_stmt(self: *Ast2Ir, n: *Node) {
if self.loop_stack.len == 0 { ret; }
var lbl = ByteView.empty();
match n.body { .StatementContinue(&x) => { lbl = label_name_of(self, x.label); }, _ => {} }
var target_index = self.loop_stack.len - 1;
if lbl.len != 0 { target_index = find_loop_with_label(self, lbl); }
if target_index == ~(0 as usize) { ret; }
var target = loop_at(self, target_index);
if target == null { ret; }
var cont_blk = target.cont_blk;
var defer_base = target.defer_base;
emit_deferred_until(self, defer_base);
if !self.ir.terminated {
var jmp = IrBuilder.control_jmp(self.ir);
IrBuilder.control_jmp_fixup(self.ir, jmp, cont_blk);
}
}
fn emit_decl(self: *Ast2Ir, ref: usize) {
var n = Ast.node(self.ast, ref);
if n == null { ret; }
match n.body {
.VarDecl(&x) => { if self.ir.current_fn != 0 { emit_var_decl(self, n); } },
.ConstDecl(&x) => { if self.ir.current_fn != 0 { emit_var_decl(self, n); } },
_ => {},
}
}
fn emit_fn_reserve(self: *Ast2Ir, n: *Node): usize {
if n == null || n.resolved_def_id == 0 { ret 0; }
var existing: usize = 0;
if ir_map_get(self, n.resolved_def_id, &existing) {
diag_name(self, n.span, .Internal, "fn reserved twice `", fn_name_of(n));
ret 0;
}
var fn_lang = LangType.def_lang_id(&self.sema.types, n.resolved_def_id);
var fn_ln = ltn(self, fn_lang);
if fn_ln == null || !kind_is(self, fn_lang, .Fn) { ret 0; }
var fn_ir = fn_ln.ir_type_id;
if fn_ir == 0 { ret 0; }
var fn_ref = IrBuilder.fn_reserve(self.ir, fn_name_of(n), fn_ir);
ir_map_put(self, n.resolved_def_id, fn_ref);
copy_attrs_func(self, n, fn_ref);
ret fn_ref;
}
fn fn_name_of(n: *Node): ByteView {
match n.body { .FnDecl(&x) => { ret x.name; }, _ => { ret ByteView.empty(); } }
ret ByteView.empty();
}
fn diag_name(self: *Ast2Ir, span: Span, code: Code, prefix: []u8, name: ByteView) {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
ByteBuffer.append(&b, prefix);
ByteBuffer.append(&b, ByteView.as_slice(name));
Diag.err(self.diag, span, code, ByteBuffer.as_slice(b));
}
fn emit_fn_body(self: *Ast2Ir, n: *Node) {
if n == null || n.resolved_def_id == 0 { ret; }
var fn_ref: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &fn_ref) { ret; }
var fn_lang = LangType.def_lang_id(&self.sema.types, n.resolved_def_id);
var fn_ln = ltn(self, fn_lang);
if fn_ln == null || !kind_is(self, fn_lang, .Fn) { ret; }
var ret_lang = fn_ret(fn_ln.*);
var ret_ir = to_ir_type(self, ret_lang);
var ret_is_unit = kind_is(self, ret_lang, .Unit);
IrBuilder.fn_begin_at(self.ir, fn_ref);
var saved_ret = self.cur_ret_lang;
self.cur_ret_lang = ret_lang;
var saved_defer_base = self.cur_defer_base;
self.cur_defer_base = self.defer_stack.len;
var defer_base = self.cur_defer_base;
var loop_base = self.loop_stack.len;
IrBuilder.block_begin(self.ir);
var params = Vec.blank(@sizeof(usize), @alignof(usize));
var body_ref: usize = 0;
match n.body { .FnDecl(&x) => { params = x.params; body_ref = x.body; }, _ => {} }
var i: usize = 0;
while i < params.len {
var pref = list_at(params, i);
var pd = Ast.node(self.ast, pref);
if pd != null && pd.resolved_def_id != 0 {
var p_lang = LangType.def_lang_id(&self.sema.types, pd.resolved_def_id);
var p_ir = to_ir_type(self, p_lang);
if p_ir != 0 {
var idx = const_usize(self, i);
var pv = IrBuilder.control_param(self.ir, p_ir, idx);
var one = const_usize(self, 1);
var slot = IrBuilder.mem_alloca(self.ir, p_ir, one);
IrBuilder.mem_store(self.ir, p_ir, slot, pv);
ir_map_put(self, pd.resolved_def_id, slot);
}
}
i = i + 1;
}
if body_ref != 0 { emit_expr(self, body_ref); }
if !self.ir.terminated { emit_deferred_until(self, defer_base); }
if !self.ir.terminated {
if ret_is_unit {
IrBuilder.control_ret(self.ir, ret_ir, 0);
} else if ret_ir != 0 {
var zero = emit_zero_value(self, ret_ir);
IrBuilder.control_ret(self.ir, ret_ir, zero);
} else {
IrBuilder.control_unreachable(self.ir);
}
}
self.defer_stack.len = defer_base;
while self.loop_stack.len > loop_base {
var top = loop_at(self, self.loop_stack.len - 1);
if top != null { Vec.drop(&top.breaks); }
pop_loop(self);
}
self.cur_ret_lang = saved_ret;
self.cur_defer_base = saved_defer_base;
IrBuilder.block_end(self.ir);
IrBuilder.fn_end(self.ir);
}
fn fn_ret(e: LangEntry): usize {
match e.extra { .Fn(f) => { ret f.ret_type; }, _ => { ret 0; } }
ret 0;
}
fn emit_aggregate_fns_reserve(self: *Ast2Ir, te: *Node) {
var members = Vec.blank(@sizeof(usize), @alignof(usize));
match te.body { .TypeAggregate(&x) => { members = x.members; }, _ => { ret; } }
var i: usize = 0;
while i < members.len {
var mn = Ast.node(self.ast, list_at(members, i));
if mn != null {
match mn.body {
.FnDecl(x) => { emit_fn_reserve(self, mn); },
.MemberDecl(md) => {
if md.type_expr != 0 {
var mt = Ast.node(self.ast, md.type_expr);
if mt != null && is_type_aggregate(mt) { emit_aggregate_fns_reserve(self, mt); }
}
},
_ => {},
}
}
i = i + 1;
}
}
fn emit_aggregate_fns_body(self: *Ast2Ir, te: *Node) {
var members = Vec.blank(@sizeof(usize), @alignof(usize));
match te.body { .TypeAggregate(&x) => { members = x.members; }, _ => { ret; } }
var i: usize = 0;
while i < members.len {
var mn = Ast.node(self.ast, list_at(members, i));
if mn != null {
match mn.body {
.FnDecl(x) => { emit_fn_body(self, mn); },
.MemberDecl(md) => {
if md.type_expr != 0 {
var mt = Ast.node(self.ast, md.type_expr);
if mt != null && is_type_aggregate(mt) { emit_aggregate_fns_body(self, mt); }
}
},
_ => {},
}
}
i = i + 1;
}
}
fn is_type_aggregate(n: *Node): bool {
match n.body { .TypeAggregate(&x) => { ret true; }, _ => { ret false; } }
ret false;
}
fn emit_container_reserve(self: *Ast2Ir, ref: usize) {
var n = Ast.node(self.ast, ref);
if n == null { ret; }
var is_tlb = false;
var tlb_block: usize = 0;
match n.body { .TopLevelBlock(&x) => { is_tlb = true; tlb_block = x.block; }, _ => {} }
if is_tlb {
if tlb_block != 0 { emit_container_reserve(self, tlb_block); }
ret;
}
var decls = Vec.blank(@sizeof(usize), @alignof(usize));
var is_cm = false;
match n.body { .ContainerMembers(&x) => { is_cm = true; decls = x.declarations; }, _ => {} }
if !is_cm { ret; }
var i: usize = 0;
while i < decls.len {
var dref = list_at(decls, i);
var d = Ast.node(self.ast, dref);
if d != null {
match d.body {
.FnDecl(x) => { emit_fn_reserve(self, d); },
.TypeDecl(td) => {
var tpat = Ast.node(self.ast, td.pattern);
if tpat != null && is_pattern_ident(tpat) {
var te = Ast.node(self.ast, td.type_expr);
if te != null && is_type_aggregate(te) { emit_aggregate_fns_reserve(self, te); }
}
},
.TopLevelBlock(x) => { emit_container_reserve(self, dref); },
.VarDecl(_) => { emit_global_var(self, d); },
.ConstDecl(_) => { emit_global_var(self, d); },
_ => {},
}
}
i = i + 1;
}
}
fn emit_container_body(self: *Ast2Ir, ref: usize) {
var n = Ast.node(self.ast, ref);
if n == null { ret; }
var is_tlb = false;
var tlb_block: usize = 0;
match n.body { .TopLevelBlock(&x) => { is_tlb = true; tlb_block = x.block; }, _ => {} }
if is_tlb {
if tlb_block != 0 { emit_container_body(self, tlb_block); }
ret;
}
var decls = Vec.blank(@sizeof(usize), @alignof(usize));
var is_cm = false;
match n.body { .ContainerMembers(&x) => { is_cm = true; decls = x.declarations; }, _ => {} }
if !is_cm { ret; }
var i: usize = 0;
while i < decls.len {
var dref = list_at(decls, i);
var d = Ast.node(self.ast, dref);
if d != null {
match d.body {
.FnDecl(x) => { emit_fn_body(self, d); },
.TypeDecl(td) => {
var tpat = Ast.node(self.ast, td.pattern);
if tpat != null && is_pattern_ident(tpat) {
var te = Ast.node(self.ast, td.type_expr);
if te != null && is_type_aggregate(te) { emit_aggregate_fns_body(self, te); }
}
},
.TopLevelBlock(x) => { emit_container_body(self, dref); },
.VarDecl(_) => {},
.ConstDecl(_) => {},
_ => {},
}
}
i = i + 1;
}
}
fn is_pattern_ident(n: *Node): bool {
match n.body { .PatternIdent(&x) => { ret true; }, _ => { ret false; } }
ret false;
}
fn emit_implicit_cast(self: *Ast2Ir, n: *Node, ft: usize, tt: usize, v: usize): usize {
if ft == 0 || tt == 0 || ft == tt { ret v; }
var fs = irt(self, ft);
var ts = irt(self, tt);
if fs == null || ts == null { ret v; }
var f_int = false;
var t_int = false;
var f_bits: usize = 0;
var t_bits: usize = 0;
var f_signed = false;
var f_flt = false;
var t_flt = false;
var f_bool = false;
var f_ptr = false;
var t_ptr = false;
var f_fn = false;
var t_fn = false;
match fs.node {
.Int(x) => { f_int = true; f_bits = x.bits; f_signed = x.is_signed; },
.Float(x) => { f_flt = true; f_bits = x.bits; },
.Bool => { f_bool = true; },
.Ptr(x) => { f_ptr = true; },
.Fn(x) => { f_fn = true; },
_ => {},
}
match ts.node {
.Int(x) => { t_int = true; t_bits = x.bits; },
.Float(x) => { t_flt = true; t_bits = x.bits; },
.Ptr(x) => { t_ptr = true; },
.Fn(x) => { t_fn = true; },
_ => {},
}
if f_int && t_int {
if f_bits == t_bits { ret IrBuilder.cast_bitcast(self.ir, ft, tt, v); }
if f_bits > t_bits { ret IrBuilder.cast_trunc(self.ir, ft, tt, v); }
if f_signed { ret IrBuilder.cast_sext(self.ir, ft, tt, v); }
ret IrBuilder.cast_zext(self.ir, ft, tt, v);
}
if f_flt && t_flt {
if f_bits > t_bits { ret IrBuilder.cast_ftrunc(self.ir, ft, tt, v); }
ret IrBuilder.cast_fext(self.ir, ft, tt, v);
}
if f_bool && t_int { ret IrBuilder.cast_bool2int(self.ir, ft, tt, v); }
var f_ptr_like = f_ptr || f_fn;
var t_ptr_like = t_ptr || t_fn;
if f_ptr_like && t_ptr_like { ret v; }
if f_ptr && t_int { ret IrBuilder.cast_ptr2int(self.ir, ft, tt, v); }
if f_int && t_ptr { ret IrBuilder.cast_int2ptr(self.ir, ft, tt, v); }
Diag.err(self.diag, n.span, .ImplicitCastFail, "implicit cast: cannot convert");
ret 0;
}
fn new(builder: *IrBuilder, sema: *Sema, diag: *Diag): Ast2Ir {
ret Ast2Ir {
.sema = sema,
.ir = builder,
.ast = sema.ast,
.def2ir_map = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
hash_kind_usize()),
.glob_gidx = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
hash_kind_usize()),
.loop_stack = Vec.blank(@sizeof(LoopCtx), @alignof(LoopCtx)),
.defer_stack = Vec.blank(@sizeof(usize), @alignof(usize)),
.cur_ret_lang = 0,
.cur_defer_base = 0,
.diag = diag,
.owned = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
};
}
fn drop(self: *Ast2Ir) {
var i: usize = 0;
while i < self.loop_stack.len {
var c = loop_at(self, i);
if c != null { Vec.drop(&c.breaks); }
i = i + 1;
}
Vec.drop(&self.loop_stack);
Vec.drop(&self.defer_stack);
Map.drop(&self.def2ir_map);
Map.drop(&self.glob_gidx);
var k: usize = 0;
while k < self.owned.len {
match Vec.get(&self.owned, k) {
.Ok(ptr) => { ByteBuffer.drop(ptr as *ByteBuffer); },
.Err(e) => {},
}
k = k + 1;
}
Vec.drop(&self.owned);
}
fn run(self: *Ast2Ir, asts: Vec) {
var n = asts.len;
var i: usize = 0;
while i < n {
var ast = list_ptr(asts, i);
if ast != null && ast.root != 0 {
self.ast = ast;
var root = Ast.node(ast, ast.root);
if root != null {
var members: usize = 0;
match root.body { .Root(r) => { members = r.members; }, _ => {} }
if members != 0 { emit_container_reserve(self, members); }
}
}
i = i + 1;
}
i = 0;
while i < n {
var ast = list_ptr(asts, i);
if ast != null && ast.root != 0 {
self.ast = ast;
var root = Ast.node(ast, ast.root);
if root != null {
var members: usize = 0;
match root.body { .Root(r) => { members = r.members; }, _ => {} }
if members != 0 { emit_container_body(self, members); }
}
}
i = i + 1;
}
}
fn list_ptr(v: Vec, i: usize): *Ast {
match Vec.get(&v, i) { .Ok(p) => { ret (p as **Ast).*; }, .Err(e) => { ret null; } }
ret null;
}
fn main() {}