// 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, .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) { 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 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) { match Map.put(&self.glob_gidx, &def_id as *u8, &val as *u8) { .Ok => {}, .Err(e) => {} } } // 顶层 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; match BigInt.to_u64(&v, bi.value) { .Ok => {}, .Err(e) => {} } 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; match Vec.push(&bytes, &b as *u8) { .Ok => {}, .Err(e) => {} } 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; 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; }, } // 消息:::: 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 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 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 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); 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); }, .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() {}