From 5aa3f56773f4b06629184a1e3753c3132b18e0bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 24 Jun 2020 20:28:52 -0400 Subject: [PATCH] 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). --- src-self-hosted/Module.zig | 39 +++-- src-self-hosted/link.zig | 23 ++- src-self-hosted/main.zig | 25 ++++ src-self-hosted/test.zig | 35 +++-- src-self-hosted/tracy.zig | 2 +- src-self-hosted/type.zig | 6 + src-self-hosted/zir.zig | 83 ++++++++--- test/stage2/compile_errors.zig | 8 +- test/stage2/zir.zig | 264 ++++++++------------------------- 9 files changed, 221 insertions(+), 264 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index dfefd3e6ba..89dcac3f41 100644 --- a/src-self-hosted/Module.zig +++ b/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 { diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 012f543a7e..c6acf21b84 100644 --- a/src-self-hosted/link.zig +++ b/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) { diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index c12f3a86dc..aa5a56010c 100644 --- a/src-self-hosted/main.zig +++ b/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(); diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 4cf72ce481..a7942575d0 100644 --- a/src-self-hosted/test.zig +++ b/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, diff --git a/src-self-hosted/tracy.zig b/src-self-hosted/tracy.zig index 1e480d75b0..6f56a87ce6 100644 --- a/src-self-hosted/tracy.zig +++ b/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, diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index d8bc40a4f8..fb97186648 100644 --- a/src-self-hosted/type.zig +++ b/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) diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index a471d35c14..43c0eac197 100644 --- a/src-self-hosted/zir.zig +++ 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 = .{}, }; diff --git a/test/stage2/compile_errors.zig b/test/stage2/compile_errors.zig index 43c41aa364..7596894dca 100644 --- a/test/stage2/compile_errors.zig +++ b/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. diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig index a22c770a9a..832629c6e7 100644 --- a/test/stage2/zir.zig +++ b/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{""}, );