Commit 5aa3f56773

Andrew Kelley <andrew@ziglang.org>
2020-06-25 02:28:52
self-hosted: fix test regressions
I'm allowing incremental compilation of ZIR modules to be broken. This is not a real use case of ZIR, and the feature requires a lot of code duplication with incremental compilation of Zig AST (which works great).
1 parent fd7a97b
src-self-hosted/link.zig
@@ -369,7 +369,7 @@ pub const ElfFile = struct {
             const file_size = self.options.program_code_size_hint;
             const p_align = 0x1000;
             const off = self.findFreeSpace(file_size, p_align);
-            //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
+            //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
             try self.program_headers.append(self.allocator, .{
                 .p_type = elf.PT_LOAD,
                 .p_offset = off,
@@ -390,7 +390,7 @@ pub const ElfFile = struct {
             // page align.
             const p_align = 0x1000;
             const off = self.findFreeSpace(file_size, p_align);
-            //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
+            //std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
             // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
             // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
             // else in virtual memory.
@@ -412,7 +412,7 @@ pub const ElfFile = struct {
             assert(self.shstrtab.items.len == 0);
             try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
             const off = self.findFreeSpace(self.shstrtab.items.len, 1);
-            //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
+            //std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
             try self.sections.append(self.allocator, .{
                 .sh_name = try self.makeString(".shstrtab"),
                 .sh_type = elf.SHT_STRTAB,
@@ -470,7 +470,7 @@ pub const ElfFile = struct {
             const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
             const file_size = self.options.symbol_count_hint * each_size;
             const off = self.findFreeSpace(file_size, min_align);
-            //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
+            //std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
 
             try self.sections.append(self.allocator, .{
                 .sh_name = try self.makeString(".symtab"),
@@ -586,7 +586,7 @@ pub const ElfFile = struct {
                     shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
                 }
                 shstrtab_sect.sh_size = needed_size;
-                //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
+                //std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
 
                 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
                 if (!self.shdr_table_dirty) {
@@ -632,7 +632,7 @@ pub const ElfFile = struct {
 
                     for (buf) |*shdr, i| {
                         shdr.* = self.sections.items[i];
-                        //std.debug.warn("writing section {}\n", .{shdr.*});
+                        //std.log.debug(.link, "writing section {}\n", .{shdr.*});
                         if (foreign_endian) {
                             bswapAllFields(elf.Elf64_Shdr, shdr);
                         }
@@ -956,10 +956,10 @@ pub const ElfFile = struct {
         try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
 
         if (self.local_symbol_free_list.popOrNull()) |i| {
-            //std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
+            //std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
             decl.link.local_sym_index = i;
         } else {
-            //std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
+            //std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
             decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
             _ = self.local_symbols.addOneAssumeCapacity();
         }
@@ -1027,11 +1027,11 @@ pub const ElfFile = struct {
                 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
             if (need_realloc) {
                 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
-                //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
+                //std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
                 if (vaddr != local_sym.st_value) {
                     local_sym.st_value = vaddr;
 
-                    //std.debug.warn("  (writing new offset table entry)\n", .{});
+                    //std.log.debug(.link, "  (writing new offset table entry)\n", .{});
                     self.offset_table.items[decl.link.offset_table_index] = vaddr;
                     try self.writeOffsetTableEntry(decl.link.offset_table_index);
                 }
@@ -1049,7 +1049,7 @@ pub const ElfFile = struct {
             const decl_name = mem.spanZ(decl.name);
             const name_str_index = try self.makeString(decl_name);
             const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
-            //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
+            //std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
             errdefer self.freeTextBlock(&decl.link);
 
             local_sym.* = .{
@@ -1307,7 +1307,6 @@ pub const ElfFile = struct {
             .p32 => @sizeOf(elf.Elf32_Sym),
             .p64 => @sizeOf(elf.Elf64_Sym),
         };
-        //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
         const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
         const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
         switch (self.ptr_width) {
src-self-hosted/main.zig
@@ -38,6 +38,30 @@ const usage =
     \\
 ;
 
+pub fn log(
+    comptime level: std.log.Level,
+    comptime scope: @TypeOf(.EnumLiteral),
+    comptime format: []const u8,
+    args: var,
+) void {
+    if (@enumToInt(level) > @enumToInt(std.log.level))
+        return;
+
+    const scope_prefix = "(" ++ switch (scope) {
+        // Uncomment to hide logs
+        //.compiler,
+        .link,
+        => return,
+
+        else => @tagName(scope),
+    } ++ "): ";
+
+    const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
+
+    // Print the message to stderr, silently ignoring any errors
+    std.debug.print(prefix ++ format, args);
+}
+
 pub fn main() !void {
     // TODO general purpose allocator in the zig std lib
     const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
@@ -450,6 +474,7 @@ fn buildOutputType(
         .link_mode = link_mode,
         .object_format = object_format,
         .optimize_mode = build_mode,
+        .keep_source_files_loaded = zir_out_path != null,
     });
     defer module.deinit();
 
src-self-hosted/Module.zig
@@ -69,6 +69,8 @@ next_anon_name_index: usize = 0,
 /// contains Decls that need to be deleted if they end up having no references to them.
 deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
 
+keep_source_files_loaded: bool,
+
 const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql);
 
 const WorkItem = union(enum) {
@@ -580,11 +582,13 @@ pub const Scope = struct {
                 .loaded_success => {
                     self.contents.module.deinit(allocator);
                     allocator.destroy(self.contents.module);
+                    self.contents = .{ .not_available = {} };
                     self.status = .unloaded_success;
                 },
                 .loaded_sema_failure => {
                     self.contents.module.deinit(allocator);
                     allocator.destroy(self.contents.module);
+                    self.contents = .{ .not_available = {} };
                     self.status = .unloaded_sema_failure;
                 },
             }
@@ -719,6 +723,7 @@ pub const InitOptions = struct {
     link_mode: ?std.builtin.LinkMode = null,
     object_format: ?std.builtin.ObjectFormat = null,
     optimize_mode: std.builtin.Mode = .Debug,
+    keep_source_files_loaded: bool = false,
 };
 
 pub fn init(gpa: *Allocator, options: InitOptions) !Module {
@@ -772,6 +777,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
         .failed_files = std.AutoHashMap(*Scope, *ErrorMsg).init(gpa),
         .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa),
         .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
+        .keep_source_files_loaded = options.keep_source_files_loaded,
     };
 }
 
@@ -869,21 +875,22 @@ pub fn update(self: *Module) !void {
     try self.performAllTheWork();
 
     // Process the deletion set.
-    for (self.deletion_set.items) |decl| {
+    while (self.deletion_set.popOrNull()) |decl| {
         if (decl.dependants.items.len != 0) {
             decl.deletion_flag = false;
             continue;
         }
         try self.deleteDecl(decl);
     }
-    self.deletion_set.shrink(self.allocator, 0);
 
     self.link_error_flags = self.bin_file.error_flags;
 
     // If there are any errors, we anticipate the source files being loaded
     // to report error messages. Otherwise we unload all source files to save memory.
     if (self.totalErrorCount() == 0) {
-        self.root_scope.unload(self.allocator);
+        if (!self.keep_source_files_loaded) {
+            self.root_scope.unload(self.allocator);
+        }
         try self.bin_file.flush();
     }
 }
@@ -1025,7 +1032,6 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
     defer tracy.end();
 
     const subsequent_analysis = switch (decl.analysis) {
-        .complete => return,
         .in_progress => unreachable,
 
         .sema_failure,
@@ -1035,7 +1041,11 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
         .codegen_failure_retryable,
         => return error.AnalysisFail,
 
-        .outdated => blk: {
+        .complete, .outdated => blk: {
+            if (decl.generation == self.generation) {
+                assert(decl.analysis == .complete);
+                return;
+            }
             //std.debug.warn("re-analyzing {}\n", .{decl.name});
 
             // The exports this Decl performs will be re-discovered, so we remove them here
@@ -1044,10 +1054,9 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
             // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
             for (decl.dependencies.items) |dep| {
                 dep.removeDependant(decl);
-                if (dep.dependants.items.len == 0) {
+                if (dep.dependants.items.len == 0 and !dep.deletion_flag) {
                     // We don't perform a deletion here, because this Decl or another one
                     // may end up referencing it before the update is complete.
-                    assert(!dep.deletion_flag);
                     dep.deletion_flag = true;
                     try self.deletion_set.append(self.allocator, dep);
                 }
@@ -1773,6 +1782,9 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
             }
         }
     }
+    for (exports_to_resolve.items) |export_decl| {
+        _ = try self.resolveZirDecl(&root_scope.base, export_decl);
+    }
     {
         // Handle explicitly deleted decls from the source code. Not to be confused
         // with when we delete decls because they are no longer referenced.
@@ -1782,9 +1794,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
             try self.deleteDecl(kv.key);
         }
     }
-    for (exports_to_resolve.items) |export_decl| {
-        _ = try self.resolveZirDecl(&root_scope.base, export_decl);
-    }
 }
 
 fn deleteDecl(self: *Module, decl: *Decl) !void {
@@ -1800,10 +1809,9 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
     // Remove itself from its dependencies, because we are about to destroy the decl pointer.
     for (decl.dependencies.items) |dep| {
         dep.removeDependant(decl);
-        if (dep.dependants.items.len == 0) {
+        if (dep.dependants.items.len == 0 and !dep.deletion_flag) {
             // We don't recursively perform a deletion here, because during the update,
             // another reference to it may turn up.
-            assert(!dep.deletion_flag);
             dep.deletion_flag = true;
             self.deletion_set.appendAssumeCapacity(dep);
         }
@@ -2026,9 +2034,10 @@ fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
     };
     const decl = try self.resolveCompleteZirDecl(scope, entry.decl);
     const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
-    const result = try self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
-    old_inst.analyzed_inst = result;
-    return result;
+    // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
+    // but this would prevent the analyzeDeclRef from happening, which is needed to properly
+    // detect Decl dependencies and dependency failures on updates.
+    return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
 }
 
 fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
src-self-hosted/test.zig
@@ -226,20 +226,36 @@ pub const TestContext = struct {
 
         for (self.zir_cases.items) |case| {
             std.testing.base_allocator_instance.reset();
+
+            var prg_node = root_node.start(case.name, case.updates.items.len);
+            prg_node.activate();
+            defer prg_node.end();
+
+            // So that we can see which test case failed when the leak checker goes off.
+            progress.refresh();
+
             const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
-            try self.runOneZIRCase(std.testing.allocator, root_node, case, info.target);
+            try self.runOneZIRCase(std.testing.allocator, &prg_node, case, info.target);
             try std.testing.allocator_instance.validate();
         }
 
         // TODO: wipe the rest of this function
         for (self.zir_cmp_output_cases.items) |case| {
             std.testing.base_allocator_instance.reset();
-            try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
+
+            var prg_node = root_node.start(case.name, case.src_list.len);
+            prg_node.activate();
+            defer prg_node.end();
+
+            // So that we can see which test case failed when the leak checker goes off.
+            progress.refresh();
+
+            try self.runOneZIRCmpOutputCase(std.testing.allocator, &prg_node, case, native_info.target);
             try std.testing.allocator_instance.validate();
         }
     }
 
-    fn runOneZIRCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: ZIRCase, target: std.Target) !void {
+    fn runOneZIRCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: ZIRCase, target: std.Target) !void {
         var tmp = std.testing.tmpDir(.{});
         defer tmp.cleanup();
 
@@ -247,10 +263,6 @@ pub const TestContext = struct {
         const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
         defer root_pkg.destroy();
 
-        var prg_node = root_node.start(case.name, case.updates.items.len);
-        prg_node.activate();
-        defer prg_node.end();
-
         var module = try Module.init(allocator, .{
             .target = target,
             // This is an Executable, as opposed to e.g. a *library*. This does
@@ -265,6 +277,7 @@ pub const TestContext = struct {
             .bin_file_dir = tmp.dir,
             .bin_file_path = "test_case.o",
             .root_pkg = root_pkg,
+            .keep_source_files_loaded = true,
         });
         defer module.deinit();
 
@@ -329,7 +342,7 @@ pub const TestContext = struct {
                     }
                 },
 
-                else => return error.unimplemented,
+                else => return error.Unimplemented,
             }
         }
     }
@@ -337,7 +350,7 @@ pub const TestContext = struct {
     fn runOneZIRCmpOutputCase(
         self: *TestContext,
         allocator: *Allocator,
-        root_node: *std.Progress.Node,
+        prg_node: *std.Progress.Node,
         case: ZIRCompareOutputCase,
         target: std.Target,
     ) !void {
@@ -348,10 +361,6 @@ pub const TestContext = struct {
         const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
         defer root_pkg.destroy();
 
-        var prg_node = root_node.start(case.name, case.src_list.len);
-        prg_node.activate();
-        defer prg_node.end();
-
         var module = try Module.init(allocator, .{
             .target = target,
             .output_mode = .Exe,
src-self-hosted/tracy.zig
@@ -1,6 +1,6 @@
 pub const std = @import("std");
 
-pub const enable = @import("build_options").enable_tracy;
+pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy;
 
 extern fn ___tracy_emit_zone_begin_callstack(
     srcloc: *const ___tracy_source_location_data,
src-self-hosted/type.zig
@@ -113,6 +113,12 @@ pub const Type = extern union {
             .Undefined => return true,
             .Null => return true,
             .Pointer => {
+                // Hot path for common case:
+                if (a.cast(Payload.SingleConstPointer)) |a_payload| {
+                    if (b.cast(Payload.SingleConstPointer)) |b_payload| {
+                        return eql(a_payload.pointee_type, b_payload.pointee_type);
+                    }
+                }
                 const is_slice_a = isSlice(a);
                 const is_slice_b = isSlice(b);
                 if (is_slice_a != is_slice_b)
src-self-hosted/zir.zig
@@ -710,8 +710,9 @@ pub const Module = struct {
         } else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
             try stream.print("@{}", .{decl_val.positionals.decl.name});
         } else {
-            //try stream.print("?", .{});
-            unreachable;
+            // This should be unreachable in theory, but since ZIR is used for debugging the compiler
+            // we output some debug text instead.
+            try stream.print("?{}?", .{@tagName(inst.tag)});
         }
     }
 };
@@ -1175,6 +1176,39 @@ const EmitZIR = struct {
 
         // Emit all the decls.
         for (src_decls.items) |ir_decl| {
+            switch (ir_decl.analysis) {
+                .unreferenced => continue,
+                .complete => {},
+                .in_progress => unreachable,
+                .outdated => unreachable,
+
+                .sema_failure,
+                .sema_failure_retryable,
+                .codegen_failure,
+                .dependency_failure,
+                .codegen_failure_retryable,
+                => if (self.old_module.failed_decls.getValue(ir_decl)) |err_msg| {
+                    const fail_inst = try self.arena.allocator.create(Inst.CompileError);
+                    fail_inst.* = .{
+                        .base = .{
+                            .src = ir_decl.src(),
+                            .tag = Inst.CompileError.base_tag,
+                        },
+                        .positionals = .{
+                            .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
+                        },
+                        .kw_args = .{},
+                    };
+                    const decl = try self.arena.allocator.create(Decl);
+                    decl.* = .{
+                        .name = mem.spanZ(ir_decl.name),
+                        .contents_hash = undefined,
+                        .inst = &fail_inst.base,
+                    };
+                    try self.decls.append(self.allocator, decl);
+                    continue;
+                },
+            }
             if (self.old_module.export_owners.getValue(ir_decl)) |exports| {
                 for (exports) |module_export| {
                     const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
@@ -1199,20 +1233,27 @@ const EmitZIR = struct {
         }
     }
 
-    fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
+    const ZirBody = struct {
+        inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
+        instructions: *std.ArrayList(*Inst),
+    };
+
+    fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst {
         if (inst.cast(ir.Inst.Constant)) |const_inst| {
-            const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
+            const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
                 const owner_decl = func_pl.func.owner_decl;
                 break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
             } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
-                break :blk try self.emitDeclRef(inst.src, declref.decl);
+                const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
+                try new_body.instructions.append(decl_ref);
+                break :blk decl_ref;
             } else blk: {
                 break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
             };
-            try inst_table.putNoClobber(inst, new_decl);
-            return new_decl;
+            try new_body.inst_table.putNoClobber(inst, new_inst);
+            return new_inst;
         } else {
-            return inst_table.getValue(inst).?;
+            return new_body.inst_table.getValue(inst).?;
         }
     }
 
@@ -1419,6 +1460,10 @@ const EmitZIR = struct {
         inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
         instructions: *std.ArrayList(*Inst),
     ) Allocator.Error!void {
+        const new_body = ZirBody{
+            .inst_table = inst_table,
+            .instructions = instructions,
+        };
         for (body.instructions) |inst| {
             const new_inst = switch (inst.tag) {
                 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
@@ -1428,7 +1473,7 @@ const EmitZIR = struct {
 
                     const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
                     for (args) |*elem, i| {
-                        elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
+                        elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
                     }
                     new_inst.* = .{
                         .base = .{
@@ -1436,7 +1481,7 @@ const EmitZIR = struct {
                             .tag = Inst.Call.base_tag,
                         },
                         .positionals = .{
-                            .func = try self.resolveInst(inst_table, old_inst.args.func),
+                            .func = try self.resolveInst(new_body, old_inst.args.func),
                             .args = args,
                         },
                         .kw_args = .{},
@@ -1453,7 +1498,7 @@ const EmitZIR = struct {
                             .tag = Inst.Return.base_tag,
                         },
                         .positionals = .{
-                            .operand = try self.resolveInst(inst_table, old_inst.args.operand),
+                            .operand = try self.resolveInst(new_body, old_inst.args.operand),
                         },
                         .kw_args = .{},
                     };
@@ -1477,7 +1522,7 @@ const EmitZIR = struct {
 
                     const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
                     for (args) |*elem, i| {
-                        elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
+                        elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
                     }
 
                     new_inst.* = .{
@@ -1511,7 +1556,7 @@ const EmitZIR = struct {
                             .tag = Inst.PtrToInt.base_tag,
                         },
                         .positionals = .{
-                            .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
+                            .ptr = try self.resolveInst(new_body, old_inst.args.ptr),
                         },
                         .kw_args = .{},
                     };
@@ -1527,7 +1572,7 @@ const EmitZIR = struct {
                         },
                         .positionals = .{
                             .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
-                            .operand = try self.resolveInst(inst_table, old_inst.args.operand),
+                            .operand = try self.resolveInst(new_body, old_inst.args.operand),
                         },
                         .kw_args = .{},
                     };
@@ -1542,8 +1587,8 @@ const EmitZIR = struct {
                             .tag = Inst.Cmp.base_tag,
                         },
                         .positionals = .{
-                            .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
-                            .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
+                            .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
+                            .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
                             .op = old_inst.args.op,
                         },
                         .kw_args = .{},
@@ -1569,7 +1614,7 @@ const EmitZIR = struct {
                             .tag = Inst.CondBr.base_tag,
                         },
                         .positionals = .{
-                            .condition = try self.resolveInst(inst_table, old_inst.args.condition),
+                            .condition = try self.resolveInst(new_body, old_inst.args.condition),
                             .true_body = .{ .instructions = true_body.toOwnedSlice() },
                             .false_body = .{ .instructions = false_body.toOwnedSlice() },
                         },
@@ -1586,7 +1631,7 @@ const EmitZIR = struct {
                             .tag = Inst.IsNull.base_tag,
                         },
                         .positionals = .{
-                            .operand = try self.resolveInst(inst_table, old_inst.args.operand),
+                            .operand = try self.resolveInst(new_body, old_inst.args.operand),
                         },
                         .kw_args = .{},
                     };
@@ -1601,7 +1646,7 @@ const EmitZIR = struct {
                             .tag = Inst.IsNonNull.base_tag,
                         },
                         .positionals = .{
-                            .operand = try self.resolveInst(inst_table, old_inst.args.operand),
+                            .operand = try self.resolveInst(new_body, old_inst.args.operand),
                         },
                         .kw_args = .{},
                     };
test/stage2/compile_errors.zig
@@ -27,9 +27,8 @@ pub fn addCases(ctx: *TestContext) !void {
         \\  %0 = call(@notafunc, [])
         \\})
         \\@0 = str("_start")
-        \\@1 = ref(@0)
-        \\@2 = export(@1, @start)
-    , &[_][]const u8{":5:13: error: use of undeclared identifier 'notafunc'"});
+        \\@1 = export(@0, "start")
+    , &[_][]const u8{":5:13: error: decl 'notafunc' not found"});
 
     // TODO: this error should occur at the call site, not the fntype decl
     ctx.addZIRError("call naked function", linux_x64,
@@ -41,8 +40,7 @@ pub fn addCases(ctx: *TestContext) !void {
         \\  %0 = call(@s, [])
         \\})
         \\@0 = str("_start")
-        \\@1 = ref(@0)
-        \\@2 = export(@1, @start)
+        \\@1 = export(@0, "start")
     , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"});
 
     // TODO: re-enable these tests.
test/stage2/zir.zig
@@ -14,23 +14,21 @@ pub fn addCases(ctx: *TestContext) void {
         \\@fnty = fntype([], @void, cc=C)
         \\
         \\@9 = str("entry")
-        \\@10 = ref(@9)
-        \\@11 = export(@10, @entry)
+        \\@11 = export(@9, "entry")
         \\
         \\@entry = fn(@fnty, {
-        \\  %11 = return()
+        \\  %11 = returnvoid()
         \\})
     ,
         \\@void = primitive(void)
         \\@fnty = fntype([], @void, cc=C)
-        \\@9 = str("entry")
-        \\@10 = ref(@9)
-        \\@unnamed$6 = str("entry")
-        \\@unnamed$7 = ref(@unnamed$6)
-        \\@unnamed$8 = export(@unnamed$7, @entry)
-        \\@unnamed$10 = fntype([], @void, cc=C)
-        \\@entry = fn(@unnamed$10, {
-        \\  %0 = return()
+        \\@9 = declref("9$0")
+        \\@9$0 = str("entry")
+        \\@unnamed$4 = str("entry")
+        \\@unnamed$5 = export(@unnamed$4, "entry")
+        \\@unnamed$6 = fntype([], @void, cc=C)
+        \\@entry = fn(@unnamed$6, {
+        \\  %0 = returnvoid()
         \\})
         \\
     );
@@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void {
         \\
         \\@entry = fn(@fnty, {
         \\  %a = str("\x32\x08\x01\x0a")
-        \\  %aref = ref(%a)
-        \\  %eptr0 = elemptr(%aref, @0)
-        \\  %eptr1 = elemptr(%aref, @1)
-        \\  %eptr2 = elemptr(%aref, @2)
-        \\  %eptr3 = elemptr(%aref, @3)
+        \\  %eptr0 = elemptr(%a, @0)
+        \\  %eptr1 = elemptr(%a, @1)
+        \\  %eptr2 = elemptr(%a, @2)
+        \\  %eptr3 = elemptr(%a, @3)
         \\  %v0 = deref(%eptr0)
         \\  %v1 = deref(%eptr1)
         \\  %v2 = deref(%eptr2)
@@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void {
         \\  %expected = int(69)
         \\  %ok = cmp(%result, eq, %expected)
         \\  %10 = condbr(%ok, {
-        \\    %11 = return()
+        \\    %11 = returnvoid()
         \\  }, {
         \\    %12 = breakpoint()
         \\  })
         \\})
         \\
         \\@9 = str("entry")
-        \\@10 = ref(@9)
-        \\@11 = export(@10, @entry)
+        \\@11 = export(@9, "entry")
     ,
         \\@void = primitive(void)
         \\@fnty = fntype([], @void, cc=C)
@@ -77,16 +73,15 @@ pub fn addCases(ctx: *TestContext) void {
         \\@1 = int(1)
         \\@2 = int(2)
         \\@3 = int(3)
-        \\@unnamed$7 = fntype([], @void, cc=C)
-        \\@entry = fn(@unnamed$7, {
-        \\  %0 = return()
+        \\@unnamed$6 = fntype([], @void, cc=C)
+        \\@entry = fn(@unnamed$6, {
+        \\  %0 = returnvoid()
         \\})
-        \\@a = str("2\x08\x01\n")
-        \\@9 = str("entry")
-        \\@10 = ref(@9)
-        \\@unnamed$14 = str("entry")
-        \\@unnamed$15 = ref(@unnamed$14)
-        \\@unnamed$16 = export(@unnamed$15, @entry)
+        \\@entry$1 = str("2\x08\x01\n")
+        \\@9 = declref("9$0")
+        \\@9$0 = str("entry")
+        \\@unnamed$11 = str("entry")
+        \\@unnamed$12 = export(@unnamed$11, "entry")
         \\
     );
 
@@ -97,45 +92,43 @@ pub fn addCases(ctx: *TestContext) void {
             \\@fnty = fntype([], @void, cc=C)
             \\
             \\@9 = str("entry")
-            \\@10 = ref(@9)
-            \\@11 = export(@10, @entry)
+            \\@11 = export(@9, "entry")
             \\
             \\@entry = fn(@fnty, {
             \\  %0 = call(@a, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
             \\
             \\@a = fn(@fnty, {
             \\  %0 = call(@b, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
             \\
             \\@b = fn(@fnty, {
             \\  %0 = call(@a, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
         ,
             \\@void = primitive(void)
             \\@fnty = fntype([], @void, cc=C)
-            \\@9 = str("entry")
-            \\@10 = ref(@9)
-            \\@unnamed$6 = str("entry")
-            \\@unnamed$7 = ref(@unnamed$6)
-            \\@unnamed$8 = export(@unnamed$7, @entry)
-            \\@unnamed$12 = fntype([], @void, cc=C)
-            \\@entry = fn(@unnamed$12, {
+            \\@9 = declref("9$0")
+            \\@9$0 = str("entry")
+            \\@unnamed$4 = str("entry")
+            \\@unnamed$5 = export(@unnamed$4, "entry")
+            \\@unnamed$6 = fntype([], @void, cc=C)
+            \\@entry = fn(@unnamed$6, {
             \\  %0 = call(@a, [], modifier=auto)
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
-            \\@unnamed$17 = fntype([], @void, cc=C)
-            \\@a = fn(@unnamed$17, {
+            \\@unnamed$8 = fntype([], @void, cc=C)
+            \\@a = fn(@unnamed$8, {
             \\  %0 = call(@b, [], modifier=auto)
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
-            \\@unnamed$22 = fntype([], @void, cc=C)
-            \\@b = fn(@unnamed$22, {
+            \\@unnamed$10 = fntype([], @void, cc=C)
+            \\@b = fn(@unnamed$10, {
             \\  %0 = call(@a, [], modifier=auto)
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
             \\
         );
@@ -145,27 +138,26 @@ pub fn addCases(ctx: *TestContext) void {
             \\@fnty = fntype([], @void, cc=C)
             \\
             \\@9 = str("entry")
-            \\@10 = ref(@9)
-            \\@11 = export(@10, @entry)
+            \\@11 = export(@9, "entry")
             \\
             \\@entry = fn(@fnty, {
             \\  %0 = call(@a, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
             \\
             \\@a = fn(@fnty, {
             \\  %0 = call(@b, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
             \\
             \\@b = fn(@fnty, {
             \\  %9 = compileerror("message")
             \\  %0 = call(@a, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
         ,
             &[_][]const u8{
-                ":19:21: error: message",
+                ":18:21: error: message",
             },
         );
         // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
@@ -176,34 +168,32 @@ pub fn addCases(ctx: *TestContext) void {
             \\@fnty = fntype([], @void, cc=C)
             \\
             \\@9 = str("entry")
-            \\@10 = ref(@9)
-            \\@11 = export(@10, @entry)
+            \\@11 = export(@9, "entry")
             \\
             \\@entry = fn(@fnty, {
-            \\  %1 = return()
+            \\  %0 = returnvoid()
             \\})
             \\
             \\@a = fn(@fnty, {
             \\  %0 = call(@b, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
             \\
             \\@b = fn(@fnty, {
             \\  %9 = compileerror("message")
             \\  %0 = call(@a, [])
-            \\  %1 = return()
+            \\  %1 = returnvoid()
             \\})
         ,
             \\@void = primitive(void)
             \\@fnty = fntype([], @void, cc=C)
-            \\@9 = str("entry")
-            \\@10 = ref(@9)
-            \\@unnamed$6 = str("entry")
-            \\@unnamed$7 = ref(@unnamed$6)
-            \\@unnamed$8 = export(@unnamed$7, @entry)
-            \\@unnamed$10 = fntype([], @void, cc=C)
-            \\@entry = fn(@unnamed$10, {
-            \\  %0 = return()
+            \\@9 = declref("9$2")
+            \\@9$2 = str("entry")
+            \\@unnamed$4 = str("entry")
+            \\@unnamed$5 = export(@unnamed$4, "entry")
+            \\@unnamed$6 = fntype([], @void, cc=C)
+            \\@entry = fn(@unnamed$6, {
+            \\  %0 = returnvoid()
             \\})
             \\
         );
@@ -218,7 +208,7 @@ pub fn addCases(ctx: *TestContext) void {
     }
 
     ctx.addZIRCompareOutput(
-        "hello world ZIR, update msg",
+        "hello world ZIR",
         &[_][]const u8{
             \\@noreturn = primitive(noreturn)
             \\@void = primitive(void)
@@ -272,125 +262,10 @@ pub fn addCases(ctx: *TestContext) void {
             \\
             \\@9 = str("_start")
             \\@11 = export(@9, "start")
-        ,
-            \\@noreturn = primitive(noreturn)
-            \\@void = primitive(void)
-            \\@usize = primitive(usize)
-            \\@0 = int(0)
-            \\@1 = int(1)
-            \\@2 = int(2)
-            \\@3 = int(3)
-            \\
-            \\@msg = str("Hello, world!\n")
-            \\@msg2 = str("HELL WORLD\n")
-            \\
-            \\@start_fnty = fntype([], @noreturn, cc=Naked)
-            \\@start = fn(@start_fnty, {
-            \\  %SYS_exit_group = int(231)
-            \\  %exit_code = as(@usize, @0)
-            \\
-            \\  %syscall = str("syscall")
-            \\  %sysoutreg = str("={rax}")
-            \\  %rax = str("{rax}")
-            \\  %rdi = str("{rdi}")
-            \\  %rcx = str("rcx")
-            \\  %rdx = str("{rdx}")
-            \\  %rsi = str("{rsi}")
-            \\  %r11 = str("r11")
-            \\  %memory = str("memory")
-            \\
-            \\  %SYS_write = as(@usize, @1)
-            \\  %STDOUT_FILENO = as(@usize, @1)
-            \\
-            \\  %msg_addr = ptrtoint(@msg2)
-            \\
-            \\  %len_name = str("len")
-            \\  %msg_len_ptr = fieldptr(@msg2, %len_name)
-            \\  %msg_len = deref(%msg_len_ptr)
-            \\  %rc_write = asm(%syscall, @usize,
-            \\    volatile=1,
-            \\    output=%sysoutreg,
-            \\    inputs=[%rax, %rdi, %rsi, %rdx],
-            \\    clobbers=[%rcx, %r11, %memory],
-            \\    args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
-            \\
-            \\  %rc_exit = asm(%syscall, @usize,
-            \\    volatile=1,
-            \\    output=%sysoutreg,
-            \\    inputs=[%rax, %rdi],
-            \\    clobbers=[%rcx, %r11, %memory],
-            \\    args=[%SYS_exit_group, %exit_code])
-            \\
-            \\  %99 = unreachable()
-            \\});
-            \\
-            \\@9 = str("_start")
-            \\@11 = export(@9, "start")
-        ,
-            \\@noreturn = primitive(noreturn)
-            \\@void = primitive(void)
-            \\@usize = primitive(usize)
-            \\@0 = int(0)
-            \\@1 = int(1)
-            \\@2 = int(2)
-            \\@3 = int(3)
-            \\
-            \\@msg = str("Hello, world!\n")
-            \\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n")
-            \\
-            \\@start_fnty = fntype([], @noreturn, cc=Naked)
-            \\@start = fn(@start_fnty, {
-            \\  %SYS_exit_group = int(231)
-            \\  %exit_code = as(@usize, @0)
-            \\
-            \\  %syscall = str("syscall")
-            \\  %sysoutreg = str("={rax}")
-            \\  %rax = str("{rax}")
-            \\  %rdi = str("{rdi}")
-            \\  %rcx = str("rcx")
-            \\  %rdx = str("{rdx}")
-            \\  %rsi = str("{rsi}")
-            \\  %r11 = str("r11")
-            \\  %memory = str("memory")
-            \\
-            \\  %SYS_write = as(@usize, @1)
-            \\  %STDOUT_FILENO = as(@usize, @1)
-            \\
-            \\  %msg_addr = ptrtoint(@msg2)
-            \\
-            \\  %len_name = str("len")
-            \\  %msg_len_ptr = fieldptr(@msg2, %len_name)
-            \\  %msg_len = deref(%msg_len_ptr)
-            \\  %rc_write = asm(%syscall, @usize,
-            \\    volatile=1,
-            \\    output=%sysoutreg,
-            \\    inputs=[%rax, %rdi, %rsi, %rdx],
-            \\    clobbers=[%rcx, %r11, %memory],
-            \\    args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
-            \\
-            \\  %rc_exit = asm(%syscall, @usize,
-            \\    volatile=1,
-            \\    output=%sysoutreg,
-            \\    inputs=[%rax, %rdi],
-            \\    clobbers=[%rcx, %r11, %memory],
-            \\    args=[%SYS_exit_group, %exit_code])
-            \\
-            \\  %99 = unreachable()
-            \\});
-            \\
-            \\@9 = str("_start")
-            \\@11 = export(@9, "start")
         },
         &[_][]const u8{
             \\Hello, world!
             \\
-        ,
-            \\HELL WORLD
-            \\
-        ,
-            \\Editing the same msg2 decl but this time with a much longer message which will
-            \\cause the data to need to be relocated in virtual address space.
-            \\
         },
     );
 
@@ -405,26 +280,18 @@ pub fn addCases(ctx: *TestContext) void {
             \\@2 = int(2)
             \\@3 = int(3)
             \\
-            \\@syscall_array = str("syscall")
-            \\@sysoutreg_array = str("={rax}")
-            \\@rax_array = str("{rax}")
-            \\@rdi_array = str("{rdi}")
-            \\@rcx_array = str("rcx")
-            \\@r11_array = str("r11")
-            \\@memory_array = str("memory")
-            \\
             \\@exit0_fnty = fntype([], @noreturn)
             \\@exit0 = fn(@exit0_fnty, {
             \\  %SYS_exit_group = int(231)
             \\  %exit_code = as(@usize, @0)
             \\
-            \\  %syscall = ref(@syscall_array)
-            \\  %sysoutreg = ref(@sysoutreg_array)
-            \\  %rax = ref(@rax_array)
-            \\  %rdi = ref(@rdi_array)
-            \\  %rcx = ref(@rcx_array)
-            \\  %r11 = ref(@r11_array)
-            \\  %memory = ref(@memory_array)
+            \\  %syscall = str("syscall")
+            \\  %sysoutreg = str("={rax}")
+            \\  %rax = str("{rax}")
+            \\  %rdi = str("{rdi}")
+            \\  %rcx = str("rcx")
+            \\  %r11 = str("r11")
+            \\  %memory = str("memory")
             \\
             \\  %rc = asm(%syscall, @usize,
             \\    volatile=1,
@@ -441,8 +308,7 @@ pub fn addCases(ctx: *TestContext) void {
             \\  %0 = call(@exit0, [])
             \\})
             \\@9 = str("_start")
-            \\@10 = ref(@9)
-            \\@11 = export(@10, @start)
+            \\@11 = export(@9, "start")
         },
         &[_][]const u8{""},
     );