2143 lines
79 KiB
Plaintext
2143 lines
79 KiB
Plaintext
// Expect retcode = 0
|
|
|
|
// AST -> SIR(移植自 stage1/spl_ast2ir.c)。两遍:先 reserve 所有函数(拿稳定
|
|
// func ref),再发射函数体。def2ir_map 记录 def -> IR 槽位(变量=内存指针,
|
|
// 函数=func ref)。loop_stack/defer_stack 驱动 break/continue/ret 的收尾。
|
|
|
|
type .{ .Error } = @import("utils/error.spl");
|
|
type .{ .Vec } = @import("utils/vec.spl");
|
|
type .{ .Map } = @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,
|
|
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 {
|
|
match Vec.get(&v, i) { .Ok(p) => { ret (p as *usize).*; }, .Err(e) => { ret 0; } }
|
|
ret 0;
|
|
}
|
|
|
|
fn push_u(v: *Vec, x: usize) {
|
|
match Vec.push(v, &x as *u8) { .Ok => {}, .Err(e) => {} }
|
|
}
|
|
|
|
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) {
|
|
match Map.put(&self.def2ir_map, &def_id as *u8, &val as *u8) { .Ok => {}, .Err(e) => {} }
|
|
}
|
|
|
|
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;
|
|
match BigInt.to_u64(&out, x.value) { .Ok => {}, .Err(e) => {} }
|
|
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 emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize) {
|
|
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 { ir_map_put(self, pat.resolved_def_id, src_ptr); }
|
|
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);
|
|
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);
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
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 slot = IrBuilder.mem_alloca(self.ir, var_ir, const_usize(self, 1));
|
|
|
|
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 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);
|
|
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);
|
|
match Vec.push(&irn.attrs, &a as *u8) { .Ok => {}, .Err(e) => {} }
|
|
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);
|
|
match Vec.push(&irf.attrs, &a as *u8) { .Ok => {}, .Err(e) => {} }
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
// ---- intrinsic ----
|
|
|
|
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 {
|
|
if @enum2int(in) == @enum2int(Intrin.BitSizeof) {
|
|
var arg = Ast.node(self.ast, list_at(args, 0));
|
|
ret IrBuilder.type_bitsizeof(self.ir, node_ir(self, arg));
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.Sizeof) {
|
|
var arg = Ast.node(self.ast, list_at(args, 0));
|
|
ret IrBuilder.type_sizeof(self.ir, node_ir(self, arg));
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.Alignof) {
|
|
var arg = Ast.node(self.ast, list_at(args, 0));
|
|
ret IrBuilder.type_alignof(self.ir, node_ir(self, arg));
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.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);
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.FieldCount) {
|
|
var arg = Ast.node(self.ast, list_at(args, 0));
|
|
ret IrBuilder.type_field_count(self.ir, node_ir(self, arg));
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.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));
|
|
var idx: usize = 0;
|
|
if fnode != null {
|
|
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; },
|
|
}
|
|
} else {
|
|
ret 0;
|
|
}
|
|
var idx_node = const_usize(self, idx);
|
|
ret IrBuilder.type_offsetof(self.ir, agg_t, idx_node);
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.Assert) {
|
|
ret ast2ir_intrin_assert(self, n, args);
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.IntCast) ||
|
|
@enum2int(in) == @enum2int(Intrin.Truncate) ||
|
|
@enum2int(in) == @enum2int(Intrin.BitCast) ||
|
|
@enum2int(in) == @enum2int(Intrin.FloatCast) ||
|
|
@enum2int(in) == @enum2int(Intrin.Float2Int) ||
|
|
@enum2int(in) == @enum2int(Intrin.Int2Float) ||
|
|
@enum2int(in) == @enum2int(Intrin.Bool2Int) ||
|
|
@enum2int(in) == @enum2int(Intrin.Ptr2Int) ||
|
|
@enum2int(in) == @enum2int(Intrin.Int2Ptr) ||
|
|
@enum2int(in) == @enum2int(Intrin.PtrCast) {
|
|
ret ast2ir_intrin_cast(self, n, in, args);
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.Breakpoint) {
|
|
ret IrBuilder.dbg_breakpoint(self.ir);
|
|
}
|
|
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 {
|
|
ignore_bool(append_bytes(&msg, ByteView.as_slice(src.fname)));
|
|
var pos = Source.line_col(src.*, n.span.start);
|
|
ignore_bool(append_bytes(&msg, ":"));
|
|
ignore_bool(append_num(&msg, pos.line));
|
|
ignore_bool(append_bytes(&msg, ":"));
|
|
ignore_bool(append_num(&msg, pos.col));
|
|
}
|
|
}
|
|
ignore_bool(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) => {
|
|
ignore_bool(append_bytes(&msg, ": "));
|
|
ignore_bool(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");
|
|
ignore_usize(IrBuilder.dbg_panic(self.ir, msg_view));
|
|
ignore_usize(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 ignore_bool(b: bool) {}
|
|
|
|
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 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 ignore_usize(x: usize) {}
|
|
|
|
// ---- 转换 ----
|
|
|
|
fn ast2ir_emit_cast_builtin(self: *Ast2Ir, n: *Node, in: Intrin, ft: usize, tt: usize,
|
|
v: usize): usize {
|
|
if @enum2int(in) == @enum2int(Intrin.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);
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.Truncate) { ret IrBuilder.cast_trunc(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.BitCast) { ret IrBuilder.cast_bitcast(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.FloatCast) {
|
|
ret ir_float_cast(self, ft, tt, v);
|
|
}
|
|
if @enum2int(in) == @enum2int(Intrin.Float2Int) { ret IrBuilder.case_float2int(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.Int2Float) { ret IrBuilder.case_int2float(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.Bool2Int) { ret IrBuilder.cast_bool2int(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.Ptr2Int) { ret IrBuilder.cast_ptr2int(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.Int2Ptr) { ret IrBuilder.cast_int2ptr(self.ir, ft, tt, v); }
|
|
if @enum2int(in) == @enum2int(Intrin.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);
|
|
ignore_usize(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);
|
|
ignore_usize(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 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);
|
|
ignore_usize(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; }
|
|
ignore_usize(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; }
|
|
ignore_usize(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 { ignore_usize(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; }
|
|
ignore_usize(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);
|
|
ignore_usize(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; }
|
|
ignore_usize(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) {
|
|
match Vec.set(v, i, &x as *u8) { .Ok => {}, .Err(e) => {} }
|
|
}
|
|
|
|
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 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; }
|
|
ignore_usize(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 { ignore_usize(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);
|
|
if body != 0 { ignore_usize(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);
|
|
if @enum2int(bk) == @enum2int(IrKind.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);
|
|
} else if @enum2int(bk) == @enum2int(IrKind.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;
|
|
}
|
|
|
|
// ---- if / loop ----
|
|
|
|
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 {
|
|
ignore_usize(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 {
|
|
ignore_usize(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); }
|
|
match Vec.push(&self.loop_stack, &ctx as *u8) { .Ok => {}, .Err(e) => {} }
|
|
|
|
if body_ref != 0 { ignore_usize(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 {
|
|
ignore_usize(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,
|
|
};
|
|
match Vec.pop(&self.loop_stack, &out as *u8) { .Ok => {}, .Err(e) => {} }
|
|
}
|
|
|
|
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 {
|
|
ignore_usize(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 { ignore_usize(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 { ignore_usize(emit_expr(self, x.body)); } },
|
|
.StatementDecl(x) => { if x.decl != 0 { emit_decl(self, x.decl); } },
|
|
.StatementExpr(x) => { if x.expr != 0 { ignore_usize(emit_expr(self, x.expr)); } },
|
|
.ExprIf(x) => { ignore_usize(emit_if_like(self, ref)); },
|
|
.ExprIfvar(x) => { ignore_usize(emit_if_like(self, ref)); },
|
|
.ExprWhile(x) => { ignore_usize(emit_loop_like(self, ref)); },
|
|
.ExprLoop(x) => { ignore_usize(emit_loop_like(self, ref)); },
|
|
.StatementBreak(x) => { ignore_usize(emit_break_stmt(self, n)); },
|
|
.StatementContinue(x) => { emit_continue_stmt(self, n); },
|
|
.ExprBlock(x) => { ignore_usize(emit_expr(self, ref)); },
|
|
_ => { ignore_usize(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 { ignore_usize(emit_var_decl(self, n)); } },
|
|
.ConstDecl(x) => { if self.ir.current_fn != 0 { ignore_usize(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); }
|
|
match ByteBuffer.append(&b, prefix) { .Ok => {}, .Err(e) => {} }
|
|
match ByteBuffer.append(&b, ByteView.as_slice(name)) { .Ok => {}, .Err(e) => {} }
|
|
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;
|
|
|
|
ignore_usize(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);
|
|
ignore_usize(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 { ignore_usize(emit_expr(self, body_ref)); }
|
|
if !self.ir.terminated { emit_deferred_until(self, defer_base); }
|
|
if !self.ir.terminated {
|
|
if ret_is_unit {
|
|
ignore_usize(IrBuilder.control_ret(self.ir, ret_ir, 0));
|
|
} else if ret_ir != 0 {
|
|
var zero = emit_zero_value(self, ret_ir);
|
|
ignore_usize(IrBuilder.control_ret(self.ir, ret_ir, zero));
|
|
} else {
|
|
ignore_usize(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) => { ignore_usize(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) => { ignore_usize(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); },
|
|
_ => {},
|
|
}
|
|
}
|
|
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); },
|
|
_ => {},
|
|
}
|
|
}
|
|
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_usize, eq_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);
|
|
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() {}
|