diff options
106 files changed, 3875 insertions, 2103 deletions
@@ -76,21 +76,25 @@ This produces a `zig2` executable in the current working directory. This is a [without LLVM extensions](https://github.com/ziglang/zig/issues/16270), and is therefore lacking these features: - Release mode optimizations -- aarch64 machine code backend -- `@cImport` / `zig translate-c` -- Ability to compile C files -- Ability to compile assembly files +- [aarch64 machine code backend](https://github.com/ziglang/zig/issues/21172) +- [@cImport](https://github.com/ziglang/zig/issues/20630) +- [zig translate-c](https://github.com/ziglang/zig/issues/20875) +- [Ability to compile assembly files](https://github.com/ziglang/zig/issues/21169) - [Some ELF linking features](https://github.com/ziglang/zig/issues/17749) - [Most COFF/PE linking features](https://github.com/ziglang/zig/issues/17751) - [Some WebAssembly linking features](https://github.com/ziglang/zig/issues/17750) - [Ability to create import libs from def files](https://github.com/ziglang/zig/issues/17807) -- [Automatic importlib file generation for Windows DLLs](https://github.com/ziglang/zig/issues/17753) - [Ability to create static archives from object files](https://github.com/ziglang/zig/issues/9828) -- Ability to compile C++, Objective-C, and Objective-C++ files +- Ability to compile C, C++, Objective-C, and Objective-C++ files However, a compiler built this way does provide a C backend, which may be useful for creating system packages of Zig projects using the system C -toolchain. In such case, LLVM is not needed! +toolchain. **In this case, LLVM is not needed!** + +Furthermore, a compiler built this way provides an LLVM backend that produces +bitcode files, which may be compiled into object files via a system Clang +package. This can be used to produce system packages of Zig applications +without the Zig package dependency on LLVM. ## Contributing diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 04431ba960..f4a4e0e13b 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -64,7 +64,7 @@ stage3-debug/bin/zig build \ stage3-debug/bin/zig build test docs \ --maxrss 21000000000 \ - -Dlldb=$HOME/deps/lldb-zig/Debug-f96d3e6fc/bin/lldb \ + -Dlldb=$HOME/deps/lldb-zig/Debug-62538077d/bin/lldb \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ diff --git a/ci/x86_64-linux-release.sh b/ci/x86_64-linux-release.sh index 57f17bdc76..f9ebad2ade 100755 --- a/ci/x86_64-linux-release.sh +++ b/ci/x86_64-linux-release.sh @@ -64,7 +64,7 @@ stage3-release/bin/zig build \ stage3-release/bin/zig build test docs \ --maxrss 21000000000 \ - -Dlldb=$HOME/deps/lldb-zig/Release-f96d3e6fc/bin/lldb \ + -Dlldb=$HOME/deps/lldb-zig/Release-62538077d/bin/lldb \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ diff --git a/lib/compiler/aro/aro/Builtins/Builtin.zig b/lib/compiler/aro/aro/Builtins/Builtin.zig index 9564bfecb5..c5cf98608b 100644 --- a/lib/compiler/aro/aro/Builtins/Builtin.zig +++ b/lib/compiler/aro/aro/Builtins/Builtin.zig @@ -5165,7 +5165,7 @@ const dafsa = [_]Node{ .{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 4913 }, }; pub const data = blk: { - @setEvalBranchQuota(3986); + @setEvalBranchQuota(30_000); break :blk [_]@This(){ // _Block_object_assign .{ .tag = @enumFromInt(0), .properties = .{ .param_str = "vv*vC*iC", .header = .blocks, .attributes = .{ .lib_function_without_prefix = true } } }, diff --git a/lib/compiler/aro/aro/Parser.zig b/lib/compiler/aro/aro/Parser.zig index 956dc5e114..1cb5e18934 100644 --- a/lib/compiler/aro/aro/Parser.zig +++ b/lib/compiler/aro/aro/Parser.zig @@ -4802,6 +4802,7 @@ const CallExpr = union(enum) { } fn shouldPromoteVarArg(self: CallExpr, arg_idx: u32) bool { + @setEvalBranchQuota(2000); return switch (self) { .standard => true, .builtin => |builtin| switch (builtin.tag) { @@ -4902,6 +4903,7 @@ const CallExpr = union(enum) { } fn returnType(self: CallExpr, p: *Parser, callable_ty: Type) Type { + @setEvalBranchQuota(6000); return switch (self) { .standard => callable_ty.returnType(), .builtin => |builtin| switch (builtin.tag) { diff --git a/lib/compiler/objcopy.zig b/lib/compiler/objcopy.zig index f3360c8108..b48fb52e82 100644 --- a/lib/compiler/objcopy.zig +++ b/lib/compiler/objcopy.zig @@ -201,9 +201,10 @@ fn cmdObjCopy( if (seen_update) fatal("zig objcopy only supports 1 update for now", .{}); seen_update = true; - try server.serveEmitBinPath(output, .{ - .flags = .{ .cache_hit = false }, - }); + // The build system already knows what the output is at this point, we + // only need to communicate that the process has finished. + // Use the empty error bundle to indicate that the update is done. + try server.serveErrorBundle(std.zig.ErrorBundle.empty); }, else => fatal("unsupported message: {s}", .{@tagName(hdr.tag)}), } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 03743cf52e..82810bb02f 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2373,7 +2373,7 @@ pub const LazyPath = union(enum) { // basis for not traversing up too many directories. var file_path: Cache.Path = .{ - .root_dir = gen.file.step.owner.build_root, + .root_dir = Cache.Directory.cwd(), .sub_path = gen.file.path orelse { std.debug.lockStdErr(); const stderr = std.io.getStdErr(); diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 1dafcf3bb7..1eabdd54e6 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -896,8 +896,8 @@ pub const Manifest = struct { } } - /// Returns a hex encoded hash of the inputs. - pub fn final(self: *Manifest) HexDigest { + /// Returns a binary hash of the inputs. + pub fn finalBin(self: *Manifest) BinDigest { assert(self.manifest_file != null); // We don't close the manifest file yet, because we want to @@ -908,7 +908,12 @@ pub const Manifest = struct { var bin_digest: BinDigest = undefined; self.hash.hasher.final(&bin_digest); + return bin_digest; + } + /// Returns a hex encoded hash of the inputs. + pub fn final(self: *Manifest) HexDigest { + const bin_digest = self.finalBin(); return binToHex(bin_digest); } diff --git a/lib/std/Build/Fuzz.zig b/lib/std/Build/Fuzz.zig index 9857db5a1f..23f8a02692 100644 --- a/lib/std/Build/Fuzz.zig +++ b/lib/std/Build/Fuzz.zig @@ -100,6 +100,15 @@ pub fn start( } fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void { + rebuildTestsWorkerRunFallible(run, ttyconf, parent_prog_node) catch |err| { + const compile = run.producer.?; + log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{ + compile.step.name, @errorName(err), + }); + }; +} + +fn rebuildTestsWorkerRunFallible(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) !void { const gpa = run.step.owner.allocator; const stderr = std.io.getStdErr(); @@ -121,14 +130,9 @@ fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog const rebuilt_bin_path = result catch |err| switch (err) { error.MakeFailed => return, - else => { - log.err("step '{s}': failed to rebuild in fuzz mode: {s}", .{ - compile.step.name, @errorName(err), - }); - return; - }, + else => |other| return other, }; - run.rebuilt_executable = rebuilt_bin_path; + run.rebuilt_executable = try rebuilt_bin_path.join(gpa, compile.out_filename); } fn fuzzWorkerRun( diff --git a/lib/std/Build/Fuzz/WebServer.zig b/lib/std/Build/Fuzz/WebServer.zig index 8534d95d16..fbf6b8dbce 100644 --- a/lib/std/Build/Fuzz/WebServer.zig +++ b/lib/std/Build/Fuzz/WebServer.zig @@ -8,6 +8,8 @@ const Coverage = std.debug.Coverage; const abi = std.Build.Fuzz.abi; const log = std.log; const assert = std.debug.assert; +const Cache = std.Build.Cache; +const Path = Cache.Path; const WebServer = @This(); @@ -31,6 +33,10 @@ coverage_mutex: std.Thread.Mutex, /// Signaled when `coverage_files` changes. coverage_condition: std.Thread.Condition, +const fuzzer_bin_name = "fuzzer"; +const fuzzer_arch_os_abi = "wasm32-freestanding"; +const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext"; + const CoverageMap = struct { mapped_memory: []align(std.mem.page_size) const u8, coverage: Coverage, @@ -181,9 +187,18 @@ fn serveWasm( // Do the compilation every request, so that the user can edit the files // and see the changes without restarting the server. - const wasm_binary_path = try buildWasmBinary(ws, arena, optimize_mode); + const wasm_base_path = try buildWasmBinary(ws, arena, optimize_mode); + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = fuzzer_bin_name, + .target = std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{ + .arch_os_abi = fuzzer_arch_os_abi, + .cpu_features = fuzzer_cpu_features, + }) catch unreachable) catch unreachable, + .output_mode = .Exe, + }); // std.http.Server does not have a sendfile API yet. - const file_contents = try std.fs.cwd().readFileAlloc(gpa, wasm_binary_path, 10 * 1024 * 1024); + const bin_path = try wasm_base_path.join(arena, bin_name); + const file_contents = try bin_path.root_dir.handle.readFileAlloc(gpa, bin_path.sub_path, 10 * 1024 * 1024); defer gpa.free(file_contents); try request.respond(file_contents, .{ .extra_headers = &.{ @@ -197,7 +212,7 @@ fn buildWasmBinary( ws: *WebServer, arena: Allocator, optimize_mode: std.builtin.OptimizeMode, -) ![]const u8 { +) !Path { const gpa = ws.gpa; const main_src_path: Build.Cache.Path = .{ @@ -219,11 +234,11 @@ fn buildWasmBinary( ws.zig_exe_path, "build-exe", // "-fno-entry", // "-O", @tagName(optimize_mode), // - "-target", "wasm32-freestanding", // - "-mcpu", "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext", // + "-target", fuzzer_arch_os_abi, // + "-mcpu", fuzzer_cpu_features, // "--cache-dir", ws.global_cache_directory.path orelse ".", // "--global-cache-dir", ws.global_cache_directory.path orelse ".", // - "--name", "fuzzer", // + "--name", fuzzer_bin_name, // "-rdynamic", // "-fsingle-threaded", // "--dep", "Walk", // @@ -251,7 +266,7 @@ fn buildWasmBinary( try sendMessage(child.stdin.?, .exit); const Header = std.zig.Server.Message.Header; - var result: ?[]const u8 = null; + var result: ?Path = null; var result_error_bundle = std.zig.ErrorBundle.empty; const stdout = poller.fifo(.stdout); @@ -288,13 +303,17 @@ fn buildWasmBinary( .extra = extra_array, }; }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; + .emit_digest => { + const EbpHdr = std.zig.Server.Message.EmitDigest; const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); if (!ebp_hdr.flags.cache_hit) { log.info("source changes detected; rebuilt wasm component", .{}); } - result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); + const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; + result = Path{ + .root_dir = ws.global_cache_directory, + .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), + }; }, else => {}, // ignore other messages } @@ -568,10 +587,7 @@ fn prepareTables( }; errdefer gop.value_ptr.coverage.deinit(gpa); - const rebuilt_exe_path: Build.Cache.Path = .{ - .root_dir = Build.Cache.Directory.cwd(), - .sub_path = run_step.rebuilt_executable.?, - }; + const rebuilt_exe_path = run_step.rebuilt_executable.?; var debug_info = std.debug.Info.load(gpa, rebuilt_exe_path, &gop.value_ptr.coverage) catch |err| { log.err("step '{s}': failed to load debug information for '{}': {s}", .{ run_step.step.name, rebuilt_exe_path, @errorName(err), diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 47a6e49a82..346ab2c9b3 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -317,6 +317,8 @@ const Build = std.Build; const Allocator = std.mem.Allocator; const assert = std.debug.assert; const builtin = @import("builtin"); +const Cache = Build.Cache; +const Path = Cache.Path; pub fn evalChildProcess(s: *Step, argv: []const []const u8) ![]u8 { const run_result = try captureChildProcess(s, std.Progress.Node.none, argv); @@ -373,7 +375,7 @@ pub fn evalZigProcess( argv: []const []const u8, prog_node: std.Progress.Node, watch: bool, -) !?[]const u8 { +) !?Path { if (s.getZigProcess()) |zp| update: { assert(watch); if (std.Progress.have_ipc) if (zp.progress_ipc_fd) |fd| prog_node.setIpcFd(fd); @@ -477,7 +479,7 @@ pub fn evalZigProcess( return result; } -fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?[]const u8 { +fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { const b = s.owner; const arena = b.allocator; @@ -487,7 +489,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?[]const u8 { if (!watch) try sendMessage(zp.child.stdin.?, .exit); const Header = std.zig.Server.Message.Header; - var result: ?[]const u8 = null; + var result: ?Path = null; const stdout = zp.poller.fifo(.stdout); @@ -531,16 +533,15 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?[]const u8 { break; } }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; + .emit_digest => { + const EbpHdr = std.zig.Server.Message.EmitDigest; const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); s.result_cached = ebp_hdr.flags.cache_hit; - result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); - if (watch) { - // This message indicates the end of the update. - stdout.discard(body.len); - break; - } + const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; + result = Path{ + .root_dir = b.cache_root, + .sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)), + }; }, .file_system_inputs => { s.clearWatchInputs(); diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 054dda9e90..1aeebbb55b 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -17,6 +17,7 @@ const Module = std.Build.Module; const InstallDir = std.Build.InstallDir; const GeneratedFile = std.Build.GeneratedFile; const Compile = @This(); +const Path = std.Build.Cache.Path; pub const base_id: Step.Id = .compile; @@ -1765,7 +1766,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const zig_args = try getZigArgs(compile, false); - const maybe_output_bin_path = step.evalZigProcess( + const maybe_output_dir = step.evalZigProcess( zig_args, options.progress_node, (b.graph.incremental == true) and options.watch, @@ -1779,53 +1780,51 @@ fn make(step: *Step, options: Step.MakeOptions) !void { }; // Update generated files - if (maybe_output_bin_path) |output_bin_path| { - const output_dir = fs.path.dirname(output_bin_path).?; - + if (maybe_output_dir) |output_dir| { if (compile.emit_directory) |lp| { - lp.path = output_dir; + lp.path = b.fmt("{}", .{output_dir}); } // -femit-bin[=path] (default) Output machine code if (compile.generated_bin) |bin| { - bin.path = b.pathJoin(&.{ output_dir, compile.out_filename }); + bin.path = output_dir.joinString(b.allocator, compile.out_filename) catch @panic("OOM"); } - const sep = std.fs.path.sep; + const sep = std.fs.path.sep_str; // output PDB if someone requested it if (compile.generated_pdb) |pdb| { - pdb.path = b.fmt("{s}{c}{s}.pdb", .{ output_dir, sep, compile.name }); + pdb.path = b.fmt("{}" ++ sep ++ "{s}.pdb", .{ output_dir, compile.name }); } // -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL if (compile.generated_implib) |implib| { - implib.path = b.fmt("{s}{c}{s}.lib", .{ output_dir, sep, compile.name }); + implib.path = b.fmt("{}" ++ sep ++ "{s}.lib", .{ output_dir, compile.name }); } // -femit-h[=path] Generate a C header file (.h) if (compile.generated_h) |lp| { - lp.path = b.fmt("{s}{c}{s}.h", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.h", .{ output_dir, compile.name }); } // -femit-docs[=path] Create a docs/ dir with html documentation if (compile.generated_docs) |generated_docs| { - generated_docs.path = b.pathJoin(&.{ output_dir, "docs" }); + generated_docs.path = output_dir.joinString(b.allocator, "docs") catch @panic("OOM"); } // -femit-asm[=path] Output .s (assembly code) if (compile.generated_asm) |lp| { - lp.path = b.fmt("{s}{c}{s}.s", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.s", .{ output_dir, compile.name }); } // -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions) if (compile.generated_llvm_ir) |lp| { - lp.path = b.fmt("{s}{c}{s}.ll", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.ll", .{ output_dir, compile.name }); } // -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions) if (compile.generated_llvm_bc) |lp| { - lp.path = b.fmt("{s}{c}{s}.bc", .{ output_dir, sep, compile.name }); + lp.path = b.fmt("{}" ++ sep ++ "{s}.bc", .{ output_dir, compile.name }); } } @@ -1841,7 +1840,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } } -pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) ![]const u8 { +pub fn rebuildInFuzzMode(c: *Compile, progress_node: std.Progress.Node) !Path { const gpa = c.step.owner.allocator; c.step.result_error_msgs.clearRetainingCapacity(); diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index 4e778d897c..3d404eb8ca 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -125,10 +125,10 @@ fn make(step: *Step, options: Step.MakeOptions) !void { if (install_artifact.dest_dir) |dest_dir| { const full_dest_path = b.getInstallPath(dest_dir, install_artifact.dest_sub_path); - const full_src_path = install_artifact.emitted_bin.?.getPath2(b, step); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { + const src_path = install_artifact.emitted_bin.?.getPath3(b, step); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_dest_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_dest_path, @errorName(err), + src_path.sub_path, full_dest_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; @@ -141,22 +141,22 @@ fn make(step: *Step, options: Step.MakeOptions) !void { } if (install_artifact.implib_dir) |implib_dir| { - const full_src_path = install_artifact.emitted_implib.?.getPath2(b, step); - const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(full_src_path)); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { + const src_path = install_artifact.emitted_implib.?.getPath3(b, step); + const full_implib_path = b.getInstallPath(implib_dir, fs.path.basename(src_path.sub_path)); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_implib_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_implib_path, @errorName(err), + src_path.sub_path, full_implib_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; } if (install_artifact.pdb_dir) |pdb_dir| { - const full_src_path = install_artifact.emitted_pdb.?.getPath2(b, step); - const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(full_src_path)); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { + const src_path = install_artifact.emitted_pdb.?.getPath3(b, step); + const full_pdb_path = b.getInstallPath(pdb_dir, fs.path.basename(src_path.sub_path)); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_pdb_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_pdb_path, @errorName(err), + src_path.sub_path, full_pdb_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; @@ -164,11 +164,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { if (install_artifact.h_dir) |h_dir| { if (install_artifact.emitted_h) |emitted_h| { - const full_src_path = emitted_h.getPath2(b, step); - const full_h_path = b.getInstallPath(h_dir, fs.path.basename(full_src_path)); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { + const src_path = emitted_h.getPath3(b, step); + const full_h_path = b.getInstallPath(h_dir, fs.path.basename(src_path.sub_path)); + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_h_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_h_path, @errorName(err), + src_path.sub_path, full_h_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; @@ -176,22 +176,22 @@ fn make(step: *Step, options: Step.MakeOptions) !void { for (install_artifact.artifact.installed_headers.items) |installation| switch (installation) { .file => |file| { - const full_src_path = file.source.getPath2(b, step); + const src_path = file.source.getPath3(b, step); const full_h_path = b.getInstallPath(h_dir, file.dest_rel_path); - const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { + const p = fs.Dir.updateFile(src_path.root_dir.handle, src_path.sub_path, cwd, full_h_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_path, full_h_path, @errorName(err), + src_path.sub_path, full_h_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; }, .directory => |dir| { - const full_src_dir_path = dir.source.getPath2(b, step); + const src_dir_path = dir.source.getPath3(b, step); const full_h_prefix = b.getInstallPath(h_dir, dir.dest_rel_path); - var src_dir = b.build_root.handle.openDir(full_src_dir_path, .{ .iterate = true }) catch |err| { + var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.sub_path, .{ .iterate = true }) catch |err| { return step.fail("unable to open source directory '{s}': {s}", .{ - full_src_dir_path, @errorName(err), + src_dir_path.sub_path, @errorName(err), }); }; defer src_dir.close(); @@ -208,14 +208,15 @@ fn make(step: *Step, options: Step.MakeOptions) !void { continue :next_entry; } } - const full_src_entry_path = b.pathJoin(&.{ full_src_dir_path, entry.path }); + + const src_entry_path = src_dir_path.join(b.allocator, entry.path) catch @panic("OOM"); const full_dest_path = b.pathJoin(&.{ full_h_prefix, entry.path }); switch (entry.kind) { .directory => try cwd.makePath(full_dest_path), .file => { - const p = fs.Dir.updateFile(cwd, full_src_entry_path, cwd, full_dest_path, .{}) catch |err| { + const p = fs.Dir.updateFile(src_entry_path.root_dir.handle, src_entry_path.sub_path, cwd, full_dest_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ - full_src_entry_path, full_dest_path, @errorName(err), + src_entry_path.sub_path, full_dest_path, @errorName(err), }); }; all_cached = all_cached and p == .fresh; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 5d9ebce9aa..0c011e25ed 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -7,6 +7,7 @@ const mem = std.mem; const process = std.process; const EnvMap = process.EnvMap; const assert = std.debug.assert; +const Path = Build.Cache.Path; const Run = @This(); @@ -93,7 +94,7 @@ cached_test_metadata: ?CachedTestMetadata = null, /// Populated during the fuzz phase if this run step corresponds to a unit test /// executable that contains fuzz tests. -rebuilt_executable: ?[]const u8, +rebuilt_executable: ?Path, /// If this Run step was produced by a Compile step, it is tracked here. producer: ?*Step.Compile, @@ -872,7 +873,7 @@ pub fn rerunInFuzzMode( .artifact => |pa| { const artifact = pa.artifact; const file_path = if (artifact == run.producer.?) - run.rebuilt_executable.? + b.fmt("{}", .{run.rebuilt_executable.?}) else (artifact.installed_path orelse artifact.generated_bin.?.path.?); try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, file_path })); diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index ac4729abd0..9ef5f7acdf 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -29,7 +29,7 @@ pub const Options = struct { pub fn create(owner: *std.Build, options: Options) *TranslateC { const translate_c = owner.allocator.create(TranslateC) catch @panic("OOM"); const source = options.root_source_file.dupe(owner); - translate_c.* = TranslateC{ + translate_c.* = .{ .step = Step.init(.{ .id = base_id, .name = "translate-c", @@ -42,7 +42,7 @@ pub fn create(owner: *std.Build, options: Options) *TranslateC { .out_basename = undefined, .target = options.target, .optimize = options.optimize, - .output_file = std.Build.GeneratedFile{ .step = &translate_c.step }, + .output_file = .{ .step = &translate_c.step }, .link_libc = options.link_libc, .use_clang = options.use_clang, }; @@ -89,6 +89,9 @@ pub fn addModule(translate_c: *TranslateC, name: []const u8) *std.Build.Module { pub fn createModule(translate_c: *TranslateC) *std.Build.Module { return translate_c.step.owner.createModule(.{ .root_source_file = translate_c.getOutput(), + .target = translate_c.target, + .optimize = translate_c.optimize, + .link_libc = translate_c.link_libc, }); } @@ -153,12 +156,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void { try argv_list.append(c_macro); } - try argv_list.append(translate_c.source.getPath2(b, step)); + const c_source_path = translate_c.source.getPath2(b, step); + try argv_list.append(c_source_path); - const output_path = try step.evalZigProcess(argv_list.items, prog_node, false); + const output_dir = try step.evalZigProcess(argv_list.items, prog_node, false); - translate_c.out_basename = fs.path.basename(output_path.?); - const output_dir = fs.path.dirname(output_path.?).?; - - translate_c.output_file.path = b.pathJoin(&.{ output_dir, translate_c.out_basename }); + const basename = std.fs.path.stem(std.fs.path.basename(c_source_path)); + translate_c.out_basename = b.fmt("{s}.zig", .{basename}); + translate_c.output_file.path = output_dir.?.joinString(b.allocator, translate_c.out_basename) catch @panic("OOM"); } diff --git a/lib/std/c.zig b/lib/std/c.zig index 92d36dd135..d430cf6a83 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -2722,7 +2722,39 @@ pub const SYS = switch (native_os) { }; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. pub const Sigaction = switch (native_os) { - .linux => linux.Sigaction, + .linux => switch (native_arch) { + .mips, + .mipsel, + .mips64, + .mips64el, + => if (builtin.target.isMusl()) + linux.Sigaction + else if (builtin.target.ptrBitWidth() == 64) extern struct { + pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; + pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void; + + flags: c_uint, + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, + mask: sigset_t, + restorer: ?*const fn () callconv(.C) void = null, + } else extern struct { + pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; + pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void; + + flags: c_uint, + handler: extern union { + handler: ?handler_fn, + sigaction: ?sigaction_fn, + }, + mask: sigset_t, + restorer: ?*const fn () callconv(.C) void = null, + __resv: [1]c_int = .{0}, + }, + else => linux.Sigaction, + }, .emscripten => emscripten.Sigaction, .netbsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct { pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; @@ -6326,16 +6358,46 @@ pub const Stat = switch (native_os) { return self.ctim; } }, - .mips, .mipsel => extern struct { + .mips, .mipsel => if (builtin.target.isMusl()) extern struct { dev: dev_t, - __pad0: [2]u32, + __pad0: [2]i32, ino: ino_t, mode: mode_t, nlink: nlink_t, uid: uid_t, gid: gid_t, rdev: dev_t, - __pad1: [2]u32, + __pad1: [2]i32, + size: off_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + blksize: blksize_t, + __pad3: i32, + blocks: blkcnt_t, + __pad4: [14]i32, + + pub fn atime(self: @This()) timespec { + return self.atim; + } + + pub fn mtime(self: @This()) timespec { + return self.mtim; + } + + pub fn ctime(self: @This()) timespec { + return self.ctim; + } + } else extern struct { + dev: dev_t, + __pad0: [3]u32, + ino: ino_t, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + __pad1: [3]u32, size: off_t, atim: timespec, mtim: timespec, @@ -6357,6 +6419,68 @@ pub const Stat = switch (native_os) { return self.ctim; } }, + .mips64, .mips64el => if (builtin.target.isMusl()) extern struct { + dev: dev_t, + __pad0: [3]i32, + ino: ino_t, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + __pad1: [2]u32, + size: off_t, + __pad2: i32, + atim: timespec, + mtim: timespec, + ctim: timespec, + blksize: blksize_t, + __pad3: u32, + blocks: blkcnt_t, + __pad4: [14]i32, + + pub fn atime(self: @This()) timespec { + return self.atim; + } + + pub fn mtime(self: @This()) timespec { + return self.mtim; + } + + pub fn ctime(self: @This()) timespec { + return self.ctim; + } + } else extern struct { + dev: dev_t, + __pad0: [3]u32, + ino: ino_t, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + __pad1: [3]u32, + size: off_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + blksize: blksize_t, + __pad3: u32, + blocks: blkcnt_t, + __pad4: [14]i32, + + pub fn atime(self: @This()) timespec { + return self.atim; + } + + pub fn mtime(self: @This()) timespec { + return self.mtim; + } + + pub fn ctime(self: @This()) timespec { + return self.ctim; + } + }, else => std.os.linux.Stat, // libc stat is the same as kernel stat. }, diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 1e184e4e8e..3e2cf53b60 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -1156,6 +1156,11 @@ pub const CPUFAMILY = enum(u32) { ARM_FIRESTORM_ICESTORM = 0x1b588bb3, ARM_BLIZZARD_AVALANCHE = 0xda33d83d, ARM_EVEREST_SAWTOOTH = 0x8765edea, + ARM_COLL = 0x2876f5b5, + ARM_IBIZA = 0xfa33415e, + ARM_LOBOS = 0x5f4dea93, + ARM_PALMA = 0x72015832, + ARM_DONAN = 0x6f5129ac, _, }; diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 186f287fdd..aa524fa2c2 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -74,8 +74,9 @@ pub const dh = struct { /// Key Encapsulation Mechanisms. pub const kem = struct { - pub const kyber_d00 = @import("crypto/ml_kem.zig").kyber_d00; - pub const ml_kem_01 = @import("crypto/ml_kem.zig").ml_kem_01; + pub const kyber_d00 = @import("crypto/ml_kem.zig").d00; + pub const ml_kem = @import("crypto/ml_kem.zig").nist; + pub const ml_kem_01 = @compileError("deprecated: final version of the specification has been published, use ml_kem instead"); }; /// Elliptic-curve arithmetic. diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig index 9e7af0606d..fd0dfaf001 100644 --- a/lib/std/crypto/aes/soft.zig +++ b/lib/std/crypto/aes/soft.zig @@ -629,6 +629,8 @@ fn generateSbox(invert: bool) [256]u8 { // Generate lookup tables. fn generateTable(invert: bool) [4][256]u32 { + @setEvalBranchQuota(50000); + var table: [4][256]u32 = undefined; for (generateSbox(invert), 0..) |value, index| { diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig index 255011de87..1a285080b5 100644 --- a/lib/std/crypto/blake2.zig +++ b/lib/std/crypto/blake2.zig @@ -786,7 +786,7 @@ test "blake2b384 streaming" { test "comptime blake2b384" { comptime { - @setEvalBranchQuota(10000); + @setEvalBranchQuota(20000); var block = [_]u8{0} ** Blake2b384.block_length; var out: [Blake2b384.digest_length]u8 = undefined; @@ -878,7 +878,7 @@ test "blake2b512 keyed" { test "comptime blake2b512" { comptime { - @setEvalBranchQuota(10000); + @setEvalBranchQuota(12000); var block = [_]u8{0} ** Blake2b512.block_length; var out: [Blake2b512.digest_length]u8 = undefined; diff --git a/lib/std/crypto/ml_kem.zig b/lib/std/crypto/ml_kem.zig index 00eade1c71..9a3a35492c 100644 --- a/lib/std/crypto/ml_kem.zig +++ b/lib/std/crypto/ml_kem.zig @@ -1,15 +1,8 @@ //! Implementation of the IND-CCA2 post-quantum secure key encapsulation mechanism (KEM) //! ML-KEM (NIST FIPS-203 publication) and CRYSTALS-Kyber (v3.02/"draft00" CFRG draft). //! -//! The schemes are not finalized yet, and are still subject to breaking changes. -//! -//! The Kyber namespace suffix (currently `_d00`) refers to the version currently -//! implemented, in accordance with the draft. -//! The ML-KEM namespace suffix (currently `_01`) refers to the NIST FIPS-203 draft -//! published on August 24, 2023, with the unintentional transposition of  having been reverted. -//! -//! Suffixes may not be updated if new versions of the documents only include editorial changes. -//! The suffixes will be removed once the schemes are finalized. +//! The namespace `d00` refers to the version currently implemented, in accordance with the CFRG draft. +//! The `nist` namespace refers to the FIPS-203 publication. //! //! Quoting from the CFRG I-D: //! @@ -148,7 +141,7 @@ const Params = struct { dv: u8, }; -pub const kyber_d00 = struct { +pub const d00 = struct { pub const Kyber512 = Kyber(.{ .name = "Kyber512", .k = 2, @@ -174,7 +167,7 @@ pub const kyber_d00 = struct { }); }; -pub const ml_kem_01 = struct { +pub const nist = struct { pub const MLKem512 = Kyber(.{ .name = "ML-KEM-512", .ml_kem = true, @@ -204,12 +197,12 @@ pub const ml_kem_01 = struct { }; const modes = [_]type{ - kyber_d00.Kyber512, - kyber_d00.Kyber768, - kyber_d00.Kyber1024, - ml_kem_01.MLKem512, - ml_kem_01.MLKem768, - ml_kem_01.MLKem1024, + d00.Kyber512, + d00.Kyber768, + d00.Kyber1024, + nist.MLKem512, + nist.MLKem768, + nist.MLKem1024, }; const h_length: usize = 32; const inner_seed_length: usize = 32; @@ -505,7 +498,10 @@ fn Kyber(comptime p: Params) type { // Derives inner PKE keypair from given seed. fn innerKeyFromSeed(seed: [inner_seed_length]u8, pk: *InnerPk, sk: *InnerSk) void { var expanded_seed: [64]u8 = undefined; - sha3.Sha3_512.hash(&seed, &expanded_seed, .{}); + var h = sha3.Sha3_512.init(.{}); + if (p.ml_kem) h.update(&[1]u8{p.k}); + h.update(&seed); + h.final(&expanded_seed); pk.rho = expanded_seed[0..32].*; const sigma = expanded_seed[32..64]; pk.aT = M.uniform(pk.rho, false); // Expand ρ to A; we'll transpose later on @@ -1722,9 +1718,9 @@ const sha2 = crypto.hash.sha2; test "NIST KAT test" { inline for (.{ - .{ kyber_d00.Kyber512, "e9c2bd37133fcb40772f81559f14b1f58dccd1c816701be9ba6214d43baf4547" }, - .{ kyber_d00.Kyber1024, "89248f2f33f7f4f7051729111f3049c409a933ec904aedadf035f30fa5646cd5" }, - .{ kyber_d00.Kyber768, "a1e122cad3c24bc51622e4c242d8b8acbcd3f618fee4220400605ca8f9ea02c2" }, + .{ d00.Kyber512, "e9c2bd37133fcb40772f81559f14b1f58dccd1c816701be9ba6214d43baf4547" }, + .{ d00.Kyber1024, "89248f2f33f7f4f7051729111f3049c409a933ec904aedadf035f30fa5646cd5" }, + .{ d00.Kyber768, "a1e122cad3c24bc51622e4c242d8b8acbcd3f618fee4220400605ca8f9ea02c2" }, }) |modeHash| { const mode = modeHash[0]; var seed: [48]u8 = undefined; diff --git a/lib/std/crypto/pcurves/p384.zig b/lib/std/crypto/pcurves/p384.zig index f8c5713209..3ab95da5e8 100644 --- a/lib/std/crypto/pcurves/p384.zig +++ b/lib/std/crypto/pcurves/p384.zig @@ -393,7 +393,7 @@ pub const P384 = struct { } const basePointPc = pc: { - @setEvalBranchQuota(50000); + @setEvalBranchQuota(70000); break :pc precompute(P384.basePoint, 15); }; diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 08ee3b7cde..826978abe4 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -56,6 +56,8 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { // TODO: Make this work. .mips, .mipsel, + .mips64, + .mips64el, => false, // `@returnAddress()` in LLVM 10 gives diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index fb72316675..7cce30df38 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -2275,9 +2275,11 @@ pub const ElfModule = struct { break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk; } if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| { - const path = std.fs.path.join(gpa, &[_][]const u8{ cache_path, "debuginfod_client" }) catch break :blk; - defer gpa.free(path); - break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk; + if (cache_path.len > 0) { + const path = std.fs.path.join(gpa, &[_][]const u8{ cache_path, "debuginfod_client" }) catch break :blk; + defer gpa.free(path); + break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk; + } } if (std.posix.getenv("HOME")) |home_path| { const path = std.fs.path.join(gpa, &[_][]const u8{ home_path, ".cache", "debuginfod_client" }) catch break :blk; diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index f836d75ae0..b17533ebf8 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -89,6 +89,7 @@ pub const LNS = struct { }; pub const LNE = struct { + pub const padding = 0x00; pub const end_sequence = 0x01; pub const set_address = 0x02; pub const define_file = 0x03; diff --git a/lib/std/enums.zig b/lib/std/enums.zig index aea194683d..1cc7bde8d2 100644 --- a/lib/std/enums.zig +++ b/lib/std/enums.zig @@ -6,7 +6,7 @@ const testing = std.testing; const EnumField = std.builtin.Type.EnumField; /// Increment this value when adding APIs that add single backwards branches. -const eval_branch_quota_cushion = 5; +const eval_branch_quota_cushion = 10; /// Returns a struct with a field matching each unique named enum element. /// If the enum is extern and has multiple names for the same value, only diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig index a05a431648..cce99ea8cc 100644 --- a/lib/std/fs/get_app_data_dir.zig +++ b/lib/std/fs/get_app_data_dir.zig @@ -32,7 +32,9 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi }, .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => { if (posix.getenv("XDG_DATA_HOME")) |xdg| { - return fs.path.join(allocator, &[_][]const u8{ xdg, appname }); + if (xdg.len > 0) { + return fs.path.join(allocator, &[_][]const u8{ xdg, appname }); + } } const home_dir = posix.getenv("HOME") orelse { diff --git a/lib/std/hash/xxhash.zig b/lib/std/hash/xxhash.zig index 88ec3ba372..1d7c8399fc 100644 --- a/lib/std/hash/xxhash.zig +++ b/lib/std/hash/xxhash.zig @@ -890,7 +890,7 @@ test "xxhash32 smhasher" { } }; try Test.do(); - @setEvalBranchQuota(75000); + @setEvalBranchQuota(85000); comptime try Test.do(); } diff --git a/lib/std/json/stringify.zig b/lib/std/json/stringify.zig index 6a8efb7e0e..965b7c3113 100644 --- a/lib/std/json/stringify.zig +++ b/lib/std/json/stringify.zig @@ -156,36 +156,23 @@ pub fn writeStreamArbitraryDepth( /// | <array> /// | write /// | print +/// | <writeRawStream> /// <object> = beginObject ( <field> <value> )* endObject -/// <field> = objectField | objectFieldRaw +/// <field> = objectField | objectFieldRaw | <objectFieldRawStream> /// <array> = beginArray ( <value> )* endArray +/// <writeRawStream> = beginWriteRaw ( stream.writeAll )* endWriteRaw +/// <objectFieldRawStream> = beginObjectFieldRaw ( stream.writeAll )* endObjectFieldRaw /// ``` /// -/// Supported types: -/// * Zig `bool` -> JSON `true` or `false`. -/// * Zig `?T` -> `null` or the rendering of `T`. -/// * Zig `i32`, `u64`, etc. -> JSON number or string. -/// * When option `emit_nonportable_numbers_as_strings` is true, if the value is outside the range `+-1<<53` (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number. -/// * Zig floats -> JSON number or string. -/// * If the value cannot be precisely represented by an f64, it is rendered as a JSON string. Otherwise, it is rendered as JSON number. -/// * TODO: Float rendering will likely change in the future, e.g. to remove the unnecessary "e+00". -/// * Zig `[]const u8`, `[]u8`, `*[N]u8`, `@Vector(N, u8)`, and similar -> JSON string. -/// * See `StringifyOptions.emit_strings_as_arrays`. -/// * If the content is not valid UTF-8, rendered as an array of numbers instead. -/// * Zig `[]T`, `[N]T`, `*[N]T`, `@Vector(N, T)`, and similar -> JSON array of the rendering of each item. -/// * Zig tuple -> JSON array of the rendering of each item. -/// * Zig `struct` -> JSON object with each field in declaration order. -/// * If the struct declares a method `pub fn jsonStringify(self: *@This(), jw: anytype) !void`, it is called to do the serialization instead of the default behavior. The given `jw` is a pointer to this `WriteStream`. See `std.json.Value` for an example. -/// * See `StringifyOptions.emit_null_optional_fields`. -/// * Zig `union(enum)` -> JSON object with one field named for the active tag and a value representing the payload. -/// * If the payload is `void`, then the emitted value is `{}`. -/// * If the union declares a method `pub fn jsonStringify(self: *@This(), jw: anytype) !void`, it is called to do the serialization instead of the default behavior. The given `jw` is a pointer to this `WriteStream`. -/// * Zig `enum` -> JSON string naming the active tag. -/// * If the enum declares a method `pub fn jsonStringify(self: *@This(), jw: anytype) !void`, it is called to do the serialization instead of the default behavior. The given `jw` is a pointer to this `WriteStream`. -/// * Zig untyped enum literal -> JSON string naming the active tag. -/// * Zig error -> JSON string naming the error. -/// * Zig `*T` -> the rendering of `T`. Note there is no guard against circular-reference infinite recursion. -/// +/// The `safety_checks_hint` parameter determines how much memory is used to enable assertions that the above grammar is being followed, +/// e.g. tripping an assertion rather than allowing `endObject` to emit the final `}` in `[[[]]}`. +/// "Depth" in this context means the depth of nested `[]` or `{}` expressions +/// (or equivalently the amount of recursion on the `<value>` grammar expression above). +/// For example, emitting the JSON `[[[]]]` requires a depth of 3. +/// If `.checked_to_fixed_depth` is used, there is additionally an assertion that the nesting depth never exceeds the given limit. +/// `.checked_to_arbitrary_depth` requires a runtime allocator for the memory. +/// `.checked_to_fixed_depth` embeds the storage required in the `WriteStream` struct. +/// `.assumed_correct` requires no space and performs none of these assertions. /// In `ReleaseFast` and `ReleaseSmall` mode, the given `safety_checks_hint` is ignored and is always treated as `.assumed_correct`. pub fn WriteStream( comptime OutStream: type, @@ -197,10 +184,14 @@ pub fn WriteStream( ) type { return struct { const Self = @This(); - const safety_checks: @TypeOf(safety_checks_hint) = switch (@import("builtin").mode) { - .Debug, .ReleaseSafe => safety_checks_hint, - .ReleaseFast, .ReleaseSmall => .assumed_correct, + const build_mode_has_safety = switch (@import("builtin").mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, }; + const safety_checks: @TypeOf(safety_checks_hint) = if (build_mode_has_safety) + safety_checks_hint + else + .assumed_correct; pub const Stream = OutStream; pub const Error = switch (safety_checks) { @@ -225,6 +216,11 @@ pub fn WriteStream( .assumed_correct => void, }, + raw_streaming_mode: if (build_mode_has_safety) + enum { none, value, objectField } + else + void = if (build_mode_has_safety) .none else {}, + pub fn init(safety_allocator: Allocator, stream: OutStream, options: StringifyOptions) Self { return .{ .options = options, @@ -237,6 +233,7 @@ pub fn WriteStream( }; } + /// Only necessary with .checked_to_arbitrary_depth. pub fn deinit(self: *Self) void { switch (safety_checks) { .checked_to_arbitrary_depth => self.nesting_stack.deinit(), @@ -246,6 +243,7 @@ pub fn WriteStream( } pub fn beginArray(self: *Self) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); try self.valueStart(); try self.stream.writeByte('['); try self.pushIndentation(ARRAY_MODE); @@ -253,6 +251,7 @@ pub fn WriteStream( } pub fn beginObject(self: *Self) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); try self.valueStart(); try self.stream.writeByte('{'); try self.pushIndentation(OBJECT_MODE); @@ -260,6 +259,7 @@ pub fn WriteStream( } pub fn endArray(self: *Self) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); self.popIndentation(ARRAY_MODE); switch (self.next_punctuation) { .none => {}, @@ -273,6 +273,7 @@ pub fn WriteStream( } pub fn endObject(self: *Self) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); self.popIndentation(OBJECT_MODE); switch (self.next_punctuation) { .none => {}, @@ -389,16 +390,39 @@ pub fn WriteStream( /// e.g. `"1"`, `"[]"`, `"[1,2]"`, not `"1,2"`. /// This function may be useful for doing your own number formatting. pub fn print(self: *Self, comptime fmt: []const u8, args: anytype) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); try self.valueStart(); try self.stream.print(fmt, args); self.valueDone(); } + /// An alternative to calling `write` that allows you to write directly to the `.stream` field, e.g. with `.stream.writeAll()`. + /// Call `beginWriteRaw()`, then write a complete value (including any quotes if necessary) directly to the `.stream` field, + /// then call `endWriteRaw()`. + /// This can be useful for streaming very long strings into the output without needing it all buffered in memory. + pub fn beginWriteRaw(self: *Self) !void { + if (build_mode_has_safety) { + assert(self.raw_streaming_mode == .none); + self.raw_streaming_mode = .value; + } + try self.valueStart(); + } + + /// See `beginWriteRaw`. + pub fn endWriteRaw(self: *Self) void { + if (build_mode_has_safety) { + assert(self.raw_streaming_mode == .value); + self.raw_streaming_mode = .none; + } + self.valueDone(); + } + /// See `WriteStream` for when to call this method. /// `key` is the string content of the property name. /// Surrounding quotes will be added and any special characters will be escaped. /// See also `objectFieldRaw`. pub fn objectField(self: *Self, key: []const u8) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); try self.objectFieldStart(); try encodeJsonString(key, self.options, self.stream); self.next_punctuation = .colon; @@ -408,14 +432,65 @@ pub fn WriteStream( /// A few assertions are performed on the given value to ensure that the caller of this function understands the API contract. /// See also `objectField`. pub fn objectFieldRaw(self: *Self, quoted_key: []const u8) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); assert(quoted_key.len >= 2 and quoted_key[0] == '"' and quoted_key[quoted_key.len - 1] == '"'); // quoted_key should be "quoted". try self.objectFieldStart(); try self.stream.writeAll(quoted_key); self.next_punctuation = .colon; } - /// See `WriteStream`. + /// In the rare case that you need to write very long object field names, + /// this is an alternative to `objectField` and `objectFieldRaw` that allows you to write directly to the `.stream` field + /// similar to `beginWriteRaw`. + /// Call `endObjectFieldRaw()` when you're done. + pub fn beginObjectFieldRaw(self: *Self) !void { + if (build_mode_has_safety) { + assert(self.raw_streaming_mode == .none); + self.raw_streaming_mode = .objectField; + } + try self.objectFieldStart(); + } + + /// See `beginObjectFieldRaw`. + pub fn endObjectFieldRaw(self: *Self) void { + if (build_mode_has_safety) { + assert(self.raw_streaming_mode == .objectField); + self.raw_streaming_mode = .none; + } + self.next_punctuation = .colon; + } + + /// Renders the given Zig value as JSON. + /// + /// Supported types: + /// * Zig `bool` -> JSON `true` or `false`. + /// * Zig `?T` -> `null` or the rendering of `T`. + /// * Zig `i32`, `u64`, etc. -> JSON number or string. + /// * When option `emit_nonportable_numbers_as_strings` is true, if the value is outside the range `+-1<<53` (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number. + /// * Zig floats -> JSON number or string. + /// * If the value cannot be precisely represented by an f64, it is rendered as a JSON string. Otherwise, it is rendered as JSON number. + /// * TODO: Float rendering will likely change in the future, e.g. to remove the unnecessary "e+00". + /// * Zig `[]const u8`, `[]u8`, `*[N]u8`, `@Vector(N, u8)`, and similar -> JSON string. + /// * See `StringifyOptions.emit_strings_as_arrays`. + /// * If the content is not valid UTF-8, rendered as an array of numbers instead. + /// * Zig `[]T`, `[N]T`, `*[N]T`, `@Vector(N, T)`, and similar -> JSON array of the rendering of each item. + /// * Zig tuple -> JSON array of the rendering of each item. + /// * Zig `struct` -> JSON object with each field in declaration order. + /// * If the struct declares a method `pub fn jsonStringify(self: *@This(), jw: anytype) !void`, it is called to do the serialization instead of the default behavior. The given `jw` is a pointer to this `WriteStream`. See `std.json.Value` for an example. + /// * See `StringifyOptions.emit_null_optional_fields`. + /// * Zig `union(enum)` -> JSON object with one field named for the active tag and a value representing the payload. + /// * If the payload is `void`, then the emitted value is `{}`. + /// * If the union declares a method `pub fn jsonStringify(self: *@This(), jw: anytype) !void`, it is called to do the serialization instead of the default behavior. The given `jw` is a pointer to this `WriteStream`. + /// * Zig `enum` -> JSON string naming the active tag. + /// * If the enum declares a method `pub fn jsonStringify(self: *@This(), jw: anytype) !void`, it is called to do the serialization instead of the default behavior. The given `jw` is a pointer to this `WriteStream`. + /// * Zig untyped enum literal -> JSON string naming the active tag. + /// * Zig error -> JSON string naming the error. + /// * Zig `*T` -> the rendering of `T`. Note there is no guard against circular-reference infinite recursion. + /// + /// See also alternative functions `print` and `beginWriteRaw`. + /// For writing object field names, use `objectField` instead. pub fn write(self: *Self, value: anytype) Error!void { + if (build_mode_has_safety) assert(self.raw_streaming_mode == .none); const T = @TypeOf(value); switch (@typeInfo(T)) { .Int => { diff --git a/lib/std/json/stringify_test.zig b/lib/std/json/stringify_test.zig index 1722868d2a..c0003b87dc 100644 --- a/lib/std/json/stringify_test.zig +++ b/lib/std/json/stringify_test.zig @@ -443,3 +443,53 @@ test "nonportable numbers" { try testStringify("9999999999999999", 9999999999999999, .{}); try testStringify("\"9999999999999999\"", 9999999999999999, .{ .emit_nonportable_numbers_as_strings = true }); } + +test "stringify raw streaming" { + var out_buf: [1024]u8 = undefined; + var slice_stream = std.io.fixedBufferStream(&out_buf); + const out = slice_stream.writer(); + + { + var w = writeStream(out, .{ .whitespace = .indent_2 }); + try testRawStreaming(&w, &slice_stream); + } + + { + var w = writeStreamMaxDepth(out, .{ .whitespace = .indent_2 }, 8); + try testRawStreaming(&w, &slice_stream); + } + + { + var w = writeStreamMaxDepth(out, .{ .whitespace = .indent_2 }, null); + try testRawStreaming(&w, &slice_stream); + } + + { + var w = writeStreamArbitraryDepth(testing.allocator, out, .{ .whitespace = .indent_2 }); + defer w.deinit(); + try testRawStreaming(&w, &slice_stream); + } +} + +fn testRawStreaming(w: anytype, slice_stream: anytype) !void { + slice_stream.reset(); + + try w.beginObject(); + try w.beginObjectFieldRaw(); + try w.stream.writeAll("\"long"); + try w.stream.writeAll(" key\""); + w.endObjectFieldRaw(); + try w.beginWriteRaw(); + try w.stream.writeAll("\"long"); + try w.stream.writeAll(" value\""); + w.endWriteRaw(); + try w.endObject(); + + const result = slice_stream.getWritten(); + const expected = + \\{ + \\ "long key": "long value" + \\} + ; + try std.testing.expectEqualStrings(expected, result); +} diff --git a/lib/std/math.zig b/lib/std/math.zig index 0c00818a1e..f18739095c 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -749,31 +749,23 @@ test rotl { try testing.expect(rotl(@Vector(1, u32), @Vector(1, u32){1 << 31}, @as(isize, -1))[0] == @as(u32, 1) << 30); } -/// Returns an unsigned int type that can hold the number of bits in T -/// - 1. Suitable for 0-based bit indices of T. +/// Returns an unsigned int type that can hold the number of bits in T - 1. +/// Suitable for 0-based bit indices of T. pub fn Log2Int(comptime T: type) type { // comptime ceil log2 if (T == comptime_int) return comptime_int; - comptime var count = 0; - comptime var s = @typeInfo(T).Int.bits - 1; - inline while (s != 0) : (s >>= 1) { - count += 1; - } - - return std.meta.Int(.unsigned, count); + const bits: u16 = @typeInfo(T).Int.bits; + const log2_bits = 16 - @clz(bits - 1); + return std.meta.Int(.unsigned, log2_bits); } /// Returns an unsigned int type that can hold the number of bits in T. pub fn Log2IntCeil(comptime T: type) type { // comptime ceil log2 if (T == comptime_int) return comptime_int; - comptime var count = 0; - comptime var s = @typeInfo(T).Int.bits; - inline while (s != 0) : (s >>= 1) { - count += 1; - } - - return std.meta.Int(.unsigned, count); + const bits: u16 = @typeInfo(T).Int.bits; + const log2_bits = 16 - @clz(bits); + return std.meta.Int(.unsigned, log2_bits); } /// Returns the smallest integer type that can hold both from and to. diff --git a/lib/std/math/hypot.zig b/lib/std/math/hypot.zig index cc0dc17ab1..ddc9408aba 100644 --- a/lib/std/math/hypot.zig +++ b/lib/std/math/hypot.zig @@ -114,6 +114,7 @@ test "hypot.precise" { } test "hypot.special" { + @setEvalBranchQuota(2000); inline for (.{ f16, f32, f64, f128 }) |T| { try expect(math.isNan(hypot(nan(T), 0.0))); try expect(math.isNan(hypot(0.0, nan(T)))); diff --git a/lib/std/math/nextafter.zig b/lib/std/math/nextafter.zig index 717cbf4700..b88648229b 100644 --- a/lib/std/math/nextafter.zig +++ b/lib/std/math/nextafter.zig @@ -144,7 +144,7 @@ test "int" { } test "float" { - @setEvalBranchQuota(3000); + @setEvalBranchQuota(4000); // normal -> normal try expect(nextAfter(f16, 0x1.234p0, 2.0) == 0x1.238p0); diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 54ad53fe06..20f1370bfc 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -2272,7 +2272,7 @@ pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize { length_halves[0], length_halves[1], ); - } else if (comptime native_arch == .mips or native_arch == .mipsel) { + } else if (native_arch.isMIPS32()) { // MIPS O32 does not deal with the register alignment issue, so pass a dummy value. const offset_halves = splitValue64(offset); @@ -2382,7 +2382,7 @@ pub fn map_shadow_stack(addr: u64, size: u64, flags: u32) usize { } pub const E = switch (native_arch) { - .mips, .mipsel => enum(u16) { + .mips, .mipsel, .mips64, .mips64el => enum(u16) { /// No error occurred. SUCCESS = 0, @@ -6784,7 +6784,258 @@ pub const termios2 = if (is_mips) extern struct { ospeed: speed_t, }; +/// Linux-specific socket ioctls +pub const SIOCINQ = T.FIONREAD; + +/// Linux-specific socket ioctls +/// output queue size (not sent + not acked) +pub const SIOCOUTQ = T.IOCOUTQ; + +pub const SOCK_IOC_TYPE = 0x89; + +pub const SIOCGSTAMP_NEW = IOCTL.IOR(SOCK_IOC_TYPE, 0x06, i64[2]); +pub const SIOCGSTAMP_OLD = IOCTL.IOR('s', 100, timeval); + +/// Get stamp (timeval) +pub const SIOCGSTAMP = if (native_arch == .x86_64 or @sizeOf(timeval) == 8) SIOCGSTAMP_OLD else SIOCGSTAMP_NEW; + +pub const SIOCGSTAMPNS_NEW = IOCTL.IOR(SOCK_IOC_TYPE, 0x07, i64[2]); +pub const SIOCGSTAMPNS_OLD = IOCTL.IOR('s', 101, kernel_timespec); + +/// Get stamp (timespec) +pub const SIOCGSTAMPNS = if (native_arch == .x86_64 or @sizeOf(timespec) == 8) SIOCGSTAMPNS_OLD else SIOCGSTAMPNS_NEW; + +// Routing table calls. +/// Add routing table entry +pub const SIOCADDRT = 0x890B; + +/// Delete routing table entry +pub const SIOCDELRT = 0x890C; + +/// Unused +pub const SIOCRTMSG = 0x890D; + +// Socket configuration controls. +/// Get iface name +pub const SIOCGIFNAME = 0x8910; + +/// Set iface channel +pub const SIOCSIFLINK = 0x8911; + +/// Get iface list +pub const SIOCGIFCONF = 0x8912; + +/// Get flags +pub const SIOCGIFFLAGS = 0x8913; + +/// Set flags +pub const SIOCSIFFLAGS = 0x8914; + +/// Get PA address +pub const SIOCGIFADDR = 0x8915; + +/// Set PA address +pub const SIOCSIFADDR = 0x8916; + +/// Get remote PA address +pub const SIOCGIFDSTADDR = 0x8917; + +/// Set remote PA address +pub const SIOCSIFDSTADDR = 0x8918; + +/// Get broadcast PA address +pub const SIOCGIFBRDADDR = 0x8919; + +/// Set broadcast PA address +pub const SIOCSIFBRDADDR = 0x891a; + +/// Get network PA mask +pub const SIOCGIFNETMASK = 0x891b; + +/// Set network PA mask +pub const SIOCSIFNETMASK = 0x891c; + +/// Get metric +pub const SIOCGIFMETRIC = 0x891d; + +/// Set metric +pub const SIOCSIFMETRIC = 0x891e; + +/// Get memory address (BSD) +pub const SIOCGIFMEM = 0x891f; + +/// Set memory address (BSD) +pub const SIOCSIFMEM = 0x8920; + +/// Get MTU size +pub const SIOCGIFMTU = 0x8921; + +/// Set MTU size +pub const SIOCSIFMTU = 0x8922; + +/// Set interface name +pub const SIOCSIFNAME = 0x8923; + +/// Set hardware address +pub const SIOCSIFHWADDR = 0x8924; + +/// Get encapsulations +pub const SIOCGIFENCAP = 0x8925; + +/// Set encapsulations +pub const SIOCSIFENCAP = 0x8926; + +/// Get hardware address +pub const SIOCGIFHWADDR = 0x8927; + +/// Driver slaving support +pub const SIOCGIFSLAVE = 0x8929; + +/// Driver slaving support +pub const SIOCSIFSLAVE = 0x8930; + +/// Add to Multicast address lists +pub const SIOCADDMULTI = 0x8931; + +/// Delete from Multicast address lists +pub const SIOCDELMULTI = 0x8932; + +/// name -> if_index mapping pub const SIOCGIFINDEX = 0x8933; + +/// Set extended flags set +pub const SIOCSIFPFLAGS = 0x8934; + +/// Get extended flags set +pub const SIOCGIFPFLAGS = 0x8935; + +/// Delete PA address +pub const SIOCDIFADDR = 0x8936; + +/// Set hardware broadcast addr +pub const SIOCSIFHWBROADCAST = 0x8937; + +/// Get number of devices +pub const SIOCGIFCOUNT = 0x8938; + +/// Bridging support +pub const SIOCGIFBR = 0x8940; + +/// Set bridging options +pub const SIOCSIFBR = 0x8941; + +/// Get the tx queue length +pub const SIOCGIFTXQLEN = 0x8942; + +/// Set the tx queue length +pub const SIOCSIFTXQLEN = 0x8943; + +/// Ethtool interface +pub const SIOCETHTOOL = 0x8946; + +/// Get address of MII PHY in use. +pub const SIOCGMIIPHY = 0x8947; + +/// Read MII PHY register. +pub const SIOCGMIIREG = 0x8948; + +/// Write MII PHY register. +pub const SIOCSMIIREG = 0x8949; + +/// Get / Set netdev parameters +pub const SIOCWANDEV = 0x894A; + +/// Output queue size (not sent only) +pub const SIOCOUTQNSD = 0x894B; + +/// Get socket network namespace +pub const SIOCGSKNS = 0x894C; + +// ARP cache control calls. +// 0x8950 - 0x8952 obsolete calls. +/// Delete ARP table entry +pub const SIOCDARP = 0x8953; + +/// Get ARP table entry +pub const SIOCGARP = 0x8954; + +/// Set ARP table entry +pub const SIOCSARP = 0x8955; + +// RARP cache control calls. +/// Delete RARP table entry +pub const SIOCDRARP = 0x8960; + +/// Get RARP table entry +pub const SIOCGRARP = 0x8961; + +/// Set RARP table entry +pub const SIOCSRARP = 0x8962; + +// Driver configuration calls +/// Get device parameters +pub const SIOCGIFMAP = 0x8970; + +/// Set device parameters +pub const SIOCSIFMAP = 0x8971; + +// DLCI configuration calls +/// Create new DLCI device +pub const SIOCADDDLCI = 0x8980; + +/// Delete DLCI device +pub const SIOCDELDLCI = 0x8981; + +/// 802.1Q VLAN support +pub const SIOCGIFVLAN = 0x8982; + +/// Set 802.1Q VLAN options +pub const SIOCSIFVLAN = 0x8983; + +// bonding calls +/// Enslave a device to the bond +pub const SIOCBONDENSLAVE = 0x8990; + +/// Release a slave from the bond +pub const SIOCBONDRELEASE = 0x8991; + +/// Set the hw addr of the bond +pub const SIOCBONDSETHWADDR = 0x8992; + +/// rtn info about slave state +pub const SIOCBONDSLAVEINFOQUERY = 0x8993; + +/// rtn info about bond state +pub const SIOCBONDINFOQUERY = 0x8994; + +/// Update to a new active slave +pub const SIOCBONDCHANGEACTIVE = 0x8995; + +// Bridge calls +/// Create new bridge device +pub const SIOCBRADDBR = 0x89a0; + +/// Remove bridge device +pub const SIOCBRDELBR = 0x89a1; + +/// Add interface to bridge +pub const SIOCBRADDIF = 0x89a2; + +/// Remove interface from bridge +pub const SIOCBRDELIF = 0x89a3; + +/// Get hardware time stamp config +pub const SIOCSHWTSTAMP = 0x89b0; + +/// Set hardware time stamp config +pub const SIOCGHWTSTAMP = 0x89b1; + +/// Device private ioctl calls +pub const SIOCDEVPRIVATE = 0x89F0; + +/// These 16 ioctl calls are protocol private +pub const SIOCPROTOPRIVATE = 0x89E0; + pub const IFNAMESIZE = 16; pub const ifmap = extern struct { @@ -6817,53 +7068,166 @@ pub const ifreq = extern struct { }; // doc comments copied from musl -pub const rlimit_resource = if (native_arch.isMIPS() or native_arch.isSPARC()) - arch_bits.rlimit_resource -else - enum(c_int) { - /// Per-process CPU limit, in seconds. - CPU, - /// Largest file that can be created, in bytes. - FSIZE, - /// Maximum size of data segment, in bytes. - DATA, - /// Maximum size of stack segment, in bytes. - STACK, - /// Largest core file that can be created, in bytes. - CORE, - /// Largest resident set size, in bytes. - /// This affects swapping; processes that are exceeding their - /// resident set size will be more likely to have physical memory - /// taken from them. - RSS, - /// Number of processes. - NPROC, - /// Number of open files. - NOFILE, - /// Locked-in-memory address space. - MEMLOCK, - /// Address space limit. - AS, - /// Maximum number of file locks. - LOCKS, - /// Maximum number of pending signals. - SIGPENDING, - /// Maximum bytes in POSIX message queues. - MSGQUEUE, - /// Maximum nice priority allowed to raise to. - /// Nice levels 19 .. -20 correspond to 0 .. 39 - /// values of this resource limit. - NICE, - /// Maximum realtime priority allowed for non-privileged - /// processes. - RTPRIO, - /// Maximum CPU time in µs that a process scheduled under a real-time - /// scheduling policy may consume without making a blocking system - /// call before being forcibly descheduled. - RTTIME, +pub const rlimit_resource = if (native_arch.isMIPS()) enum(c_int) { + /// Per-process CPU limit, in seconds. + CPU = 0, - _, - }; + /// Largest file that can be created, in bytes. + FSIZE = 1, + + /// Maximum size of data segment, in bytes. + DATA = 2, + + /// Maximum size of stack segment, in bytes. + STACK = 3, + + /// Largest core file that can be created, in bytes. + CORE = 4, + + /// Number of open files. + NOFILE = 5, + + /// Address space limit. + AS = 6, + + /// Largest resident set size, in bytes. + /// This affects swapping; processes that are exceeding their + /// resident set size will be more likely to have physical memory + /// taken from them. + RSS = 7, + + /// Number of processes. + NPROC = 8, + + /// Locked-in-memory address space. + MEMLOCK = 9, + + /// Maximum number of file locks. + LOCKS = 10, + + /// Maximum number of pending signals. + SIGPENDING = 11, + + /// Maximum bytes in POSIX message queues. + MSGQUEUE = 12, + + /// Maximum nice priority allowed to raise to. + /// Nice levels 19 .. -20 correspond to 0 .. 39 + /// values of this resource limit. + NICE = 13, + + /// Maximum realtime priority allowed for non-privileged + /// processes. + RTPRIO = 14, + + /// Maximum CPU time in µs that a process scheduled under a real-time + /// scheduling policy may consume without making a blocking system + /// call before being forcibly descheduled. + RTTIME = 15, + + _, +} else if (native_arch.isSPARC()) enum(c_int) { + /// Per-process CPU limit, in seconds. + CPU = 0, + + /// Largest file that can be created, in bytes. + FSIZE = 1, + + /// Maximum size of data segment, in bytes. + DATA = 2, + + /// Maximum size of stack segment, in bytes. + STACK = 3, + + /// Largest core file that can be created, in bytes. + CORE = 4, + + /// Largest resident set size, in bytes. + /// This affects swapping; processes that are exceeding their + /// resident set size will be more likely to have physical memory + /// taken from them. + RSS = 5, + + /// Number of open files. + NOFILE = 6, + + /// Number of processes. + NPROC = 7, + + /// Locked-in-memory address space. + MEMLOCK = 8, + + /// Address space limit. + AS = 9, + + /// Maximum number of file locks. + LOCKS = 10, + + /// Maximum number of pending signals. + SIGPENDING = 11, + + /// Maximum bytes in POSIX message queues. + MSGQUEUE = 12, + + /// Maximum nice priority allowed to raise to. + /// Nice levels 19 .. -20 correspond to 0 .. 39 + /// values of this resource limit. + NICE = 13, + + /// Maximum realtime priority allowed for non-privileged + /// processes. + RTPRIO = 14, + + /// Maximum CPU time in µs that a process scheduled under a real-time + /// scheduling policy may consume without making a blocking system + /// call before being forcibly descheduled. + RTTIME = 15, + + _, +} else enum(c_int) { + /// Per-process CPU limit, in seconds. + CPU = 0, + /// Largest file that can be created, in bytes. + FSIZE = 1, + /// Maximum size of data segment, in bytes. + DATA = 2, + /// Maximum size of stack segment, in bytes. + STACK = 3, + /// Largest core file that can be created, in bytes. + CORE = 4, + /// Largest resident set size, in bytes. + /// This affects swapping; processes that are exceeding their + /// resident set size will be more likely to have physical memory + /// taken from them. + RSS = 5, + /// Number of processes. + NPROC = 6, + /// Number of open files. + NOFILE = 7, + /// Locked-in-memory address space. + MEMLOCK = 8, + /// Address space limit. + AS = 9, + /// Maximum number of file locks. + LOCKS = 10, + /// Maximum number of pending signals. + SIGPENDING = 11, + /// Maximum bytes in POSIX message queues. + MSGQUEUE = 12, + /// Maximum nice priority allowed to raise to. + /// Nice levels 19 .. -20 correspond to 0 .. 39 + /// values of this resource limit. + NICE = 13, + /// Maximum realtime priority allowed for non-privileged + /// processes. + RTPRIO = 14, + /// Maximum CPU time in µs that a process scheduled under a real-time + /// scheduling policy may consume without making a blocking system + /// call before being forcibly descheduled. + RTTIME = 15, + + _, +}; pub const rlim_t = u64; diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index aa02841926..e6cb5f7900 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -14,7 +14,8 @@ const timespec = linux.timespec; pub fn syscall0(number: SYS) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -28,7 +29,7 @@ pub fn syscall_pipe(fd: *[2]i32) usize { \\ .set noat \\ .set noreorder \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f \\ nop \\ b 2f \\ subu $2, $0, $2 @@ -46,7 +47,8 @@ pub fn syscall_pipe(fd: *[2]i32) usize { pub fn syscall1(number: SYS, arg1: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -59,7 +61,8 @@ pub fn syscall1(number: SYS, arg1: usize) usize { pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -73,7 +76,8 @@ pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -88,7 +92,8 @@ pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -108,7 +113,8 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, \\ sw %[arg5], 16($sp) \\ syscall \\ addu $sp, $sp, 24 - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -141,7 +147,8 @@ pub fn syscall6( \\ sw %[arg6], 20($sp) \\ syscall \\ addu $sp, $sp, 24 - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -174,7 +181,8 @@ pub fn syscall7( \\ sw %[arg7], 24($sp) \\ syscall \\ addu $sp, $sp, 32 - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ subu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -314,7 +322,7 @@ pub const msghdr_const = extern struct { flags: i32, }; -pub const blksize_t = i32; +pub const blksize_t = u32; pub const nlink_t = u32; pub const time_t = i32; pub const mode_t = u32; @@ -323,36 +331,47 @@ pub const ino_t = u64; pub const dev_t = u64; pub const blkcnt_t = i64; -// The `stat` definition used by the Linux kernel. +// The `stat64` definition used by the Linux kernel. pub const Stat = extern struct { - dev: u32, - __pad0: [3]u32, // Reserved for st_dev expansion + dev: dev_t, + __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). ino: ino_t, mode: mode_t, nlink: nlink_t, uid: uid_t, gid: gid_t, - rdev: u32, - __pad1: [3]u32, + rdev: dev_t, + __pad1: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). size: off_t, - atim: timespec, - mtim: timespec, - ctim: timespec, + atim: i32, + atim_nsec: u32, + mtim: i32, + mtim_nsec: u32, + ctim: i32, + ctim_nsec: u32, blksize: blksize_t, __pad3: u32, blocks: blkcnt_t, - __pad4: [14]usize, pub fn atime(self: @This()) timespec { - return self.atim; + return .{ + .sec = self.atim, + .nsec = self.atim_nsec, + }; } pub fn mtime(self: @This()) timespec { - return self.mtim; + return .{ + .sec = self.mtim, + .nsec = self.mtim_nsec, + }; } pub fn ctime(self: @This()) timespec { - return self.ctim; + return .{ + .sec = self.ctim, + .nsec = self.ctim_nsec, + }; } }; @@ -368,66 +387,6 @@ pub const timezone = extern struct { pub const Elf_Symndx = u32; -pub const rlimit_resource = enum(c_int) { - /// Per-process CPU limit, in seconds. - CPU, - - /// Largest file that can be created, in bytes. - FSIZE, - - /// Maximum size of data segment, in bytes. - DATA, - - /// Maximum size of stack segment, in bytes. - STACK, - - /// Largest core file that can be created, in bytes. - CORE, - - /// Number of open files. - NOFILE, - - /// Address space limit. - AS, - - /// Largest resident set size, in bytes. - /// This affects swapping; processes that are exceeding their - /// resident set size will be more likely to have physical memory - /// taken from them. - RSS, - - /// Number of processes. - NPROC, - - /// Locked-in-memory address space. - MEMLOCK, - - /// Maximum number of file locks. - LOCKS, - - /// Maximum number of pending signals. - SIGPENDING, - - /// Maximum bytes in POSIX message queues. - MSGQUEUE, - - /// Maximum nice priority allowed to raise to. - /// Nice levels 19 .. -20 correspond to 0 .. 39 - /// values of this resource limit. - NICE, - - /// Maximum realtime priority allowed for non-privileged - /// processes. - RTPRIO, - - /// Maximum CPU time in µs that a process scheduled under a real-time - /// scheduling policy may consume without making a blocking system - /// call before being forcibly descheduled. - RTTIME, - - _, -}; - /// TODO pub const ucontext_t = void; diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig index 579d41ca75..5e6661eae5 100644 --- a/lib/std/os/linux/mips64.zig +++ b/lib/std/os/linux/mips64.zig @@ -14,7 +14,8 @@ const timespec = linux.timespec; pub fn syscall0(number: SYS) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -28,7 +29,7 @@ pub fn syscall_pipe(fd: *[2]i32) usize { \\ .set noat \\ .set noreorder \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f \\ nop \\ b 2f \\ subu $2, $0, $2 @@ -46,7 +47,9 @@ pub fn syscall_pipe(fd: *[2]i32) usize { pub fn syscall1(number: SYS, arg1: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f + \\ nop \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -59,7 +62,8 @@ pub fn syscall1(number: SYS, arg1: usize) usize { pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -73,7 +77,8 @@ pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -88,7 +93,8 @@ pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -104,7 +110,8 @@ pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -129,7 +136,8 @@ pub fn syscall6( ) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -156,7 +164,8 @@ pub fn syscall7( ) usize { return asm volatile ( \\ syscall - \\ blez $7, 1f + \\ beq $7, $zero, 1f + \\ blez $2, 1f \\ dsubu $2, $0, $2 \\ 1: : [ret] "={$2}" (-> usize), @@ -292,7 +301,7 @@ pub const msghdr_const = extern struct { flags: i32, }; -pub const blksize_t = i32; +pub const blksize_t = u32; pub const nlink_t = u32; pub const time_t = i32; pub const mode_t = u32; @@ -303,34 +312,45 @@ pub const blkcnt_t = i64; // The `stat` definition used by the Linux kernel. pub const Stat = extern struct { - dev: u32, - __pad0: [3]u32, // Reserved for st_dev expansion + dev: dev_t, + __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). ino: ino_t, mode: mode_t, nlink: nlink_t, uid: uid_t, gid: gid_t, - rdev: u32, - __pad1: [3]u32, + rdev: dev_t, + __pad1: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). size: off_t, - atim: timespec, - mtim: timespec, - ctim: timespec, + atim: u32, + atim_nsec: u32, + mtim: u32, + mtim_nsec: u32, + ctim: u32, + ctim_nsec: u32, blksize: blksize_t, __pad3: u32, blocks: blkcnt_t, - __pad4: [14]usize, pub fn atime(self: @This()) timespec { - return self.atim; + return .{ + .sec = self.atim, + .nsec = self.atim_nsec, + }; } pub fn mtime(self: @This()) timespec { - return self.mtim; + return .{ + .sec = self.mtim, + .nsec = self.mtim_nsec, + }; } pub fn ctime(self: @This()) timespec { - return self.ctim; + return .{ + .sec = self.ctim, + .nsec = self.ctim_nsec, + }; } }; @@ -346,66 +366,6 @@ pub const timezone = extern struct { pub const Elf_Symndx = u32; -pub const rlimit_resource = enum(c_int) { - /// Per-process CPU limit, in seconds. - CPU, - - /// Largest file that can be created, in bytes. - FSIZE, - - /// Maximum size of data segment, in bytes. - DATA, - - /// Maximum size of stack segment, in bytes. - STACK, - - /// Largest core file that can be created, in bytes. - CORE, - - /// Number of open files. - NOFILE, - - /// Address space limit. - AS, - - /// Largest resident set size, in bytes. - /// This affects swapping; processes that are exceeding their - /// resident set size will be more likely to have physical memory - /// taken from them. - RSS, - - /// Number of processes. - NPROC, - - /// Locked-in-memory address space. - MEMLOCK, - - /// Maximum number of file locks. - LOCKS, - - /// Maximum number of pending signals. - SIGPENDING, - - /// Maximum bytes in POSIX message queues. - MSGQUEUE, - - /// Maximum nice priority allowed to raise to. - /// Nice levels 19 .. -20 correspond to 0 .. 39 - /// values of this resource limit. - NICE, - - /// Maximum realtime priority allowed for non-privileged - /// processes. - RTPRIO, - - /// Maximum CPU time in µs that a process scheduled under a real-time - /// scheduling policy may consume without making a blocking system - /// call before being forcibly descheduled. - RTTIME, - - _, -}; - /// TODO pub const ucontext_t = void; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index a705b58fb6..b30f001000 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -448,63 +448,3 @@ pub const ucontext_t = extern struct { /// TODO pub const getcontext = {}; - -pub const rlimit_resource = enum(c_int) { - /// Per-process CPU limit, in seconds. - CPU, - - /// Largest file that can be created, in bytes. - FSIZE, - - /// Maximum size of data segment, in bytes. - DATA, - - /// Maximum size of stack segment, in bytes. - STACK, - - /// Largest core file that can be created, in bytes. - CORE, - - /// Largest resident set size, in bytes. - /// This affects swapping; processes that are exceeding their - /// resident set size will be more likely to have physical memory - /// taken from them. - RSS, - - /// Number of open files. - NOFILE, - - /// Number of processes. - NPROC, - - /// Locked-in-memory address space. - MEMLOCK, - - /// Address space limit. - AS, - - /// Maximum number of file locks. - LOCKS, - - /// Maximum number of pending signals. - SIGPENDING, - - /// Maximum bytes in POSIX message queues. - MSGQUEUE, - - /// Maximum nice priority allowed to raise to. - /// Nice levels 19 .. -20 correspond to 0 .. 39 - /// values of this resource limit. - NICE, - - /// Maximum realtime priority allowed for non-privileged - /// processes. - RTPRIO, - - /// Maximum CPU time in µs that a process scheduled under a real-time - /// scheduling policy may consume without making a blocking system - /// call before being forcibly descheduled. - RTTIME, - - _, -}; diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index a8fa1454a5..4c6ec1294b 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -535,6 +535,7 @@ fn testUtf16CountCodepoints() !void { } test "utf16 count codepoints" { + @setEvalBranchQuota(2000); try testUtf16CountCodepoints(); try comptime testUtf16CountCodepoints(); } diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index d0f1c73fc8..3450e39cc6 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -66,6 +66,10 @@ scratch: std.ArrayListUnmanaged(u32) = .{}, /// of ZIR. /// The key is the ref operand; the value is the ref instruction. ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}, +/// Any information which should trigger invalidation of incremental compilation +/// data should be used to update this hasher. The result is the final source +/// hash of the enclosing declaration/etc. +src_hasher: std.zig.SrcHasher, const InnerError = error{ OutOfMemory, AnalysisFail }; @@ -137,6 +141,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { .arena = arena.allocator(), .tree = &tree, .nodes_need_rl = &nodes_need_rl, + .src_hasher = undefined, // `structDeclInner` for the root struct will set this }; defer astgen.deinit(gpa); @@ -1422,6 +1427,8 @@ fn fnProtoExpr( .is_extern = false, .is_noinline = false, .noalias_bits = noalias_bits, + + .proto_hash = undefined, // ignored for `body_gz == null` }); _ = try block_scope.addBreak(.break_inline, block_inst, result); @@ -4007,6 +4014,13 @@ fn fnDecl( const tree = astgen.tree; const token_tags = tree.tokens.items(.tag); + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + // We don't add the full source yet, because we also need the prototype hash! + // The source slice is added towards the *end* of this function. + astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); + // missing function name already happened in scanDecls() const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail; @@ -4300,11 +4314,21 @@ fn fnDecl( .is_extern = true, .is_noinline = is_noinline, .noalias_bits = noalias_bits, + .proto_hash = undefined, // ignored for `body_gz == null` }); } else func: { // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz fn_gz.instructions_top = ret_gz.instructions.items.len; + // Construct the prototype hash. + // Leave `astgen.src_hasher` unmodified; this will be used for hashing + // the *whole* function declaration, including its body. + var proto_hasher = astgen.src_hasher; + const proto_node = tree.nodes.items(.data)[decl_node].lhs; + proto_hasher.update(tree.getNodeSource(proto_node)); + var proto_hash: std.zig.SrcHash = undefined; + proto_hasher.final(&proto_hash); + const prev_fn_block = astgen.fn_block; const prev_fn_ret_ty = astgen.fn_ret_ty; defer { @@ -4362,16 +4386,22 @@ fn fnDecl( .is_extern = false, .is_noinline = is_noinline, .noalias_bits = noalias_bits, + .proto_hash = proto_hash, }); }; + // *Now* we can incorporate the full source code into the hasher. + astgen.src_hasher.update(tree.getNodeSource(decl_node)); + // We add this at the end so that its instruction index marks the end range // of the top level declaration. addFunc already unstacked fn_gz and ret_gz. _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst); + var hash: std.zig.SrcHash = undefined; + astgen.src_hasher.final(&hash); try setDeclaration( decl_inst, - std.zig.hashSrc(tree.getNodeSource(decl_node)), + hash, .{ .named = fn_name_token }, decl_gz.decl_line, is_pub, @@ -4395,6 +4425,12 @@ fn globalVarDecl( const tree = astgen.tree; const token_tags = tree.tokens.items(.tag); + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + astgen.src_hasher.update(tree.getNodeSource(node)); + astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); + const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var; // We do this at the beginning so that the instruction index marks the range start // of the top level declaration. @@ -4534,9 +4570,11 @@ fn globalVarDecl( _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); } + var hash: std.zig.SrcHash = undefined; + astgen.src_hasher.final(&hash); try setDeclaration( decl_inst, - std.zig.hashSrc(tree.getNodeSource(node)), + hash, .{ .named = name_token }, block_scope.decl_line, is_pub, @@ -4562,6 +4600,12 @@ fn comptimeDecl( const node_datas = tree.nodes.items(.data); const body_node = node_datas[node].lhs; + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + astgen.src_hasher.update(tree.getNodeSource(node)); + astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); + // Up top so the ZIR instruction index marks the start range of this // top-level declaration. const decl_inst = try gz.makeDeclaration(node); @@ -4584,9 +4628,11 @@ fn comptimeDecl( _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value); } + var hash: std.zig.SrcHash = undefined; + astgen.src_hasher.final(&hash); try setDeclaration( decl_inst, - std.zig.hashSrc(tree.getNodeSource(node)), + hash, .@"comptime", decl_block.decl_line, false, @@ -4607,6 +4653,12 @@ fn usingnamespaceDecl( const tree = astgen.tree; const node_datas = tree.nodes.items(.data); + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + astgen.src_hasher.update(tree.getNodeSource(node)); + astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); + const type_expr = node_datas[node].lhs; const is_pub = blk: { const main_tokens = tree.nodes.items(.main_token); @@ -4634,9 +4686,11 @@ fn usingnamespaceDecl( const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr); _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst); + var hash: std.zig.SrcHash = undefined; + astgen.src_hasher.final(&hash); try setDeclaration( decl_inst, - std.zig.hashSrc(tree.getNodeSource(node)), + hash, .@"usingnamespace", decl_block.decl_line, is_pub, @@ -4658,6 +4712,12 @@ fn testDecl( const node_datas = tree.nodes.items(.data); const body_node = node_datas[node].rhs; + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + astgen.src_hasher.update(tree.getNodeSource(node)); + astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column)); + // Up top so the ZIR instruction index marks the start range of this // top-level declaration. const decl_inst = try gz.makeDeclaration(node); @@ -4819,13 +4879,18 @@ fn testDecl( .is_extern = false, .is_noinline = false, .noalias_bits = 0, + + // Tests don't have a prototype that needs hashing + .proto_hash = .{0} ** 16, }); _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst); + var hash: std.zig.SrcHash = undefined; + astgen.src_hasher.final(&hash); try setDeclaration( decl_inst, - std.zig.hashSrc(tree.getNodeSource(node)), + hash, test_name, decl_block.decl_line, false, @@ -4983,10 +5048,12 @@ fn structDeclInner( } }; - var fields_hasher = std.zig.SrcHasher.init(.{}); - fields_hasher.update(@tagName(layout)); + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + astgen.src_hasher.update(@tagName(layout)); if (backing_int_node != 0) { - fields_hasher.update(tree.getNodeSource(backing_int_node)); + astgen.src_hasher.update(tree.getNodeSource(backing_int_node)); } var sfba = std.heap.stackFallback(256, astgen.arena); @@ -5009,7 +5076,7 @@ fn structDeclInner( .field => |field| field, }; - fields_hasher.update(tree.getNodeSource(member_node)); + astgen.src_hasher.update(tree.getNodeSource(member_node)); if (!is_tuple) { const field_name = try astgen.identAsString(member.ast.main_token); @@ -5139,7 +5206,7 @@ fn structDeclInner( } var fields_hash: std.zig.SrcHash = undefined; - fields_hasher.final(&fields_hash); + astgen.src_hasher.final(&fields_hash); try gz.setStruct(decl_inst, .{ .src_node = node, @@ -5240,11 +5307,13 @@ fn unionDeclInner( var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); defer wip_members.deinit(); - var fields_hasher = std.zig.SrcHasher.init(.{}); - fields_hasher.update(@tagName(layout)); - fields_hasher.update(&.{@intFromBool(auto_enum_tok != null)}); + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); + astgen.src_hasher.update(@tagName(layout)); + astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)}); if (arg_node != 0) { - fields_hasher.update(astgen.tree.getNodeSource(arg_node)); + astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node)); } var sfba = std.heap.stackFallback(256, astgen.arena); @@ -5261,7 +5330,7 @@ fn unionDeclInner( .decl => continue, .field => |field| field, }; - fields_hasher.update(astgen.tree.getNodeSource(member_node)); + astgen.src_hasher.update(astgen.tree.getNodeSource(member_node)); member.convertToNonTupleLike(astgen.tree.nodes); if (member.ast.tuple_like) { return astgen.failTok(member.ast.main_token, "union field missing name", .{}); @@ -5364,7 +5433,7 @@ fn unionDeclInner( } var fields_hash: std.zig.SrcHash = undefined; - fields_hasher.final(&fields_hash); + astgen.src_hasher.final(&fields_hash); if (!block_scope.isEmpty()) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); @@ -5578,11 +5647,13 @@ fn containerDecl( var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size); defer wip_members.deinit(); - var fields_hasher = std.zig.SrcHasher.init(.{}); + const old_hasher = astgen.src_hasher; + defer astgen.src_hasher = old_hasher; + astgen.src_hasher = std.zig.SrcHasher.init(.{}); if (container_decl.ast.arg != 0) { - fields_hasher.update(tree.getNodeSource(container_decl.ast.arg)); + astgen.src_hasher.update(tree.getNodeSource(container_decl.ast.arg)); } - fields_hasher.update(&.{@intFromBool(nonexhaustive)}); + astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)}); var sfba = std.heap.stackFallback(256, astgen.arena); const sfba_allocator = sfba.get(); @@ -5596,7 +5667,7 @@ fn containerDecl( for (container_decl.ast.members) |member_node| { if (member_node == counts.nonexhaustive_node) continue; - fields_hasher.update(tree.getNodeSource(member_node)); + astgen.src_hasher.update(tree.getNodeSource(member_node)); var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) { .decl => continue, .field => |field| field, @@ -5676,7 +5747,7 @@ fn containerDecl( } var fields_hash: std.zig.SrcHash = undefined; - fields_hasher.final(&fields_hash); + astgen.src_hasher.final(&fields_hash); const body = block_scope.instructionsSlice(); const body_len = astgen.countBodyLenAfterFixups(body); @@ -8478,6 +8549,10 @@ fn tunnelThroughClosure( }); } + // Incorporate the capture index into the source hash, so that changes in + // the order of captures cause suitable re-analysis. + astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index)); + // Add an instruction to get the value from the closure. return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index); } @@ -9306,6 +9381,13 @@ fn builtinCall( }, .src => { + // Incorporate the source location into the source hash, so that + // changes in the source location of `@src()` result in re-analysis. + astgen.src_hasher.update( + std.mem.asBytes(&astgen.source_line) ++ + std.mem.asBytes(&astgen.source_column), + ); + const token_starts = tree.tokens.items(.start); const node_start = token_starts[tree.firstToken(node)]; astgen.advanceSourceCursor(node_start); @@ -12122,6 +12204,9 @@ const GenZir = struct { is_test: bool, is_extern: bool, is_noinline: bool, + + /// Ignored if `body_gz == null`. + proto_hash: std.zig.SrcHash, }) !Zir.Inst.Ref { assert(args.src_node != 0); const astgen = gz.astgen; @@ -12150,15 +12235,7 @@ const GenZir = struct { const columns = args.lbrace_column | (rbrace_column << 16); - const proto_hash: std.zig.SrcHash = switch (node_tags[fn_decl]) { - .fn_decl => sig_hash: { - const proto_node = node_datas[fn_decl].lhs; - break :sig_hash std.zig.hashSrc(tree.getNodeSource(proto_node)); - }, - .test_decl => std.zig.hashSrc(""), // tests don't have a prototype - else => unreachable, - }; - const proto_hash_arr: [4]u32 = @bitCast(proto_hash); + const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash); src_locs_and_hash_buffer = .{ args.lbrace_line, diff --git a/lib/std/zig/Server.zig b/lib/std/zig/Server.zig index 7ce017045d..0ed9cfcd0b 100644 --- a/lib/std/zig/Server.zig +++ b/lib/std/zig/Server.zig @@ -14,8 +14,8 @@ pub const Message = struct { zig_version, /// Body is an ErrorBundle. error_bundle, - /// Body is a EmitBinPath. - emit_bin_path, + /// Body is a EmitDigest. + emit_digest, /// Body is a TestMetadata test_metadata, /// Body is a TestResults @@ -82,8 +82,8 @@ pub const Message = struct { }; /// Trailing: - /// * file system path where the emitted binary can be found - pub const EmitBinPath = extern struct { + /// * the hex digest of the cache directory within the /o/ subdirectory. + pub const EmitDigest = extern struct { flags: Flags, pub const Flags = packed struct(u8) { @@ -196,17 +196,17 @@ pub fn serveU64Message(s: *Server, tag: OutMessage.Tag, int: u64) !void { }, &.{std.mem.asBytes(&msg_le)}); } -pub fn serveEmitBinPath( +pub fn serveEmitDigest( s: *Server, - fs_path: []const u8, - header: OutMessage.EmitBinPath, + digest: *const [Cache.bin_digest_len]u8, + header: OutMessage.EmitDigest, ) !void { try s.serveMessage(.{ - .tag = .emit_bin_path, - .bytes_len = @intCast(fs_path.len + @sizeOf(OutMessage.EmitBinPath)), + .tag = .emit_digest, + .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)), }, &.{ std.mem.asBytes(&header), - fs_path, + digest, }); } @@ -328,3 +328,4 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const native_endian = builtin.target.cpu.arch.endian(); const need_bswap = native_endian != .little; +const Cache = std.Build.Cache; diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 8318a5a345..26f4c4780f 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -3548,7 +3548,7 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { const datas = zir.instructions.items(.data); switch (tags[@intFromEnum(decl_inst)]) { // Functions are allowed and yield no iterations. - // There is one case matching this in the extended instruction set below. + // This is because they are returned by `findDecls`. .func, .func_inferred, .func_fancy => return .{ .extra_index = undefined, .decls_remaining = 0, @@ -3558,6 +3558,13 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator { .extended => { const extended = datas[@intFromEnum(decl_inst)].extended; switch (extended.opcode) { + // Reifications are allowed and yield no iterations. + // This is because they are returned by `findDecls`. + .reify => return .{ + .extra_index = undefined, + .decls_remaining = 0, + .zir = zir, + }, .struct_decl => { const small: Inst.StructDecl.Small = @bitCast(extended.small); var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.StructDecl).Struct.fields.len); @@ -3690,6 +3697,17 @@ pub fn findDecls(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.In if (bodies.addrspace_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b); } +/// Like `findDecls`, but only considers the `main_struct_inst` instruction. This may return more than +/// just that instruction because it will also traverse fields. +pub fn findDeclsRoot(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.Index)) !void { + list.clearRetainingCapacity(); + + var found_defers: std.AutoHashMapUnmanaged(u32, void) = .{}; + defer found_defers.deinit(gpa); + + try zir.findDeclsInner(gpa, list, &found_defers, .main_struct_inst); +} + fn findDeclsInner( zir: Zir, gpa: Allocator, diff --git a/lib/std/zig/system/darwin/macos.zig b/lib/std/zig/system/darwin/macos.zig index 0bc08b319c..c0a9aa47d7 100644 --- a/lib/std/zig/system/darwin/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -419,6 +419,11 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { .ARM_TYPHOON => &Target.aarch64.cpu.apple_a8, .ARM_CYCLONE => &Target.aarch64.cpu.cyclone, else => return null, + .ARM_COLL => &Target.aarch64.cpu.apple_a17, + .ARM_IBIZA => &Target.aarch64.cpu.apple_m3, // base + .ARM_LOBOS => &Target.aarch64.cpu.apple_m3, // pro + .ARM_PALMA => &Target.aarch64.cpu.apple_m3, // max + // .ARM_DONAN => &Target.aarch64.cpu.apple_m4, // decl not available until llvm 19 }; return Target.Cpu{ diff --git a/src/Air.zig b/src/Air.zig index aa821dc420..5c559a4088 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -456,6 +456,8 @@ pub const Inst = struct { /// Same as `dbg_var_ptr` except the local is a const, not a var, and the /// operand is the local's value. dbg_var_val, + /// Same as `dbg_var_val` except the local is an inline function argument. + dbg_arg_inline, /// ?T => bool /// Result type is always bool. /// Uses the `un_op` field. @@ -1022,10 +1024,7 @@ pub const Inst = struct { ty: Ref, /// Index into `extra` of a null-terminated string representing the parameter name. /// This is `.none` if debug info is stripped. - name: enum(u32) { - none = std.math.maxInt(u32), - _, - }, + name: NullTerminatedString, }, ty_op: struct { ty: Ref, @@ -1440,6 +1439,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .dbg_stmt, .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, .store, .store_safe, .fence, @@ -1562,14 +1562,16 @@ pub fn value(air: Air, inst: Inst.Ref, pt: Zcu.PerThread) !?Value { return air.typeOfIndex(index, &pt.zcu.intern_pool).onePossibleValue(pt); } -pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 { - const bytes = std.mem.sliceAsBytes(air.extra[index..]); - var end: usize = 0; - while (bytes[end] != 0) { - end += 1; +pub const NullTerminatedString = enum(u32) { + none = std.math.maxInt(u32), + _, + + pub fn toSlice(nts: NullTerminatedString, air: Air) [:0]const u8 { + if (nts == .none) return ""; + const bytes = std.mem.sliceAsBytes(air.extra[@intFromEnum(nts)..]); + return bytes[0..std.mem.indexOfScalar(u8, bytes, 0).? :0]; } - return bytes[0..end :0]; -} +}; /// Returns whether the given instruction must always be lowered, for instance /// because it can cause side effects. If an instruction does not need to be @@ -1596,6 +1598,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .dbg_inline_block, .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, .ret, .ret_safe, .ret_load, diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig index 77c8344a86..4b92a3a94f 100644 --- a/src/Air/types_resolved.zig +++ b/src/Air/types_resolved.zig @@ -339,6 +339,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => { if (!checkRef(data.pl_op.operand, zcu)) return false; }, diff --git a/src/Compilation.zig b/src/Compilation.zig index fab0496b22..dc7d0ba925 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -39,6 +39,8 @@ const Air = @import("Air.zig"); const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const dev = @import("dev.zig"); +pub const Directory = Cache.Directory; +const Path = Cache.Path; pub const Config = @import("Compilation/Config.zig"); @@ -70,9 +72,9 @@ bin_file: ?*link.File, /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) sysroot: ?[]const u8, /// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used. -implib_emit: ?Emit, +implib_emit: ?Path, /// This is non-null when `-femit-docs` is provided. -docs_emit: ?Emit, +docs_emit: ?Path, root_name: [:0]const u8, include_compiler_rt: bool, objects: []Compilation.LinkObject, @@ -269,27 +271,9 @@ llvm_opt_bisect_limit: c_int, file_system_inputs: ?*std.ArrayListUnmanaged(u8), -pub const Emit = struct { - /// Where the output will go. - directory: Directory, - /// Path to the output file, relative to `directory`. - sub_path: []const u8, - - /// Returns the full path to `basename` if it were in the same directory as the - /// `Emit` sub_path. - pub fn basenamePath(emit: Emit, arena: Allocator, basename: []const u8) ![:0]const u8 { - const full_path = if (emit.directory.path) |p| - try std.fs.path.join(arena, &[_][]const u8{ p, emit.sub_path }) - else - emit.sub_path; - - if (std.fs.path.dirname(full_path)) |dirname| { - return try std.fs.path.joinZ(arena, &.{ dirname, basename }); - } else { - return try arena.dupeZ(u8, basename); - } - } -}; +/// This is the digest of the cache for the current compilation. +/// This digest will be known after update() is called. +digest: ?[Cache.bin_digest_len]u8 = null, pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Zcu.SemaError; @@ -868,8 +852,6 @@ pub const LldError = struct { } }; -pub const Directory = Cache.Directory; - pub const EmitLoc = struct { /// If this is `null` it means the file will be output to the cache directory. /// When provided, both the open file handle and the path name must outlive the `Compilation`. @@ -1672,7 +1654,9 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil // In the case of incremental cache mode, this `artifact_directory` // is computed based on a hash of non-linker inputs, and it is where all // build artifacts are stored (even while in-progress). + comp.digest = hash.peekBin(); const digest = hash.final(); + const artifact_sub_dir = "o" ++ std.fs.path.sep_str ++ digest; var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); errdefer artifact_dir.close(); @@ -1688,8 +1672,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil comp.cache_use = .{ .incremental = incremental }; if (options.emit_bin) |emit_bin| { - const emit: Emit = .{ - .directory = emit_bin.directory orelse artifact_directory, + const emit: Path = .{ + .root_dir = emit_bin.directory orelse artifact_directory, .sub_path = emit_bin.basename, }; comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts); @@ -1697,14 +1681,14 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil if (options.emit_implib) |emit_implib| { comp.implib_emit = .{ - .directory = emit_implib.directory orelse artifact_directory, + .root_dir = emit_implib.directory orelse artifact_directory, .sub_path = emit_implib.basename, }; } if (options.emit_docs) |emit_docs| { comp.docs_emit = .{ - .directory = emit_docs.directory orelse artifact_directory, + .root_dir = emit_docs.directory orelse artifact_directory, .sub_path = emit_docs.basename, }; } @@ -2121,9 +2105,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { comp.last_update_was_cache_hit = true; log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); - const digest = man.final(); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); - comp.wholeCacheModeSetBinFilePath(whole, &digest); + comp.digest = bin_digest; + comp.wholeCacheModeSetBinFilePath(whole, &hex_digest); assert(whole.lock == null); whole.lock = man.toOwnedLock(); @@ -2155,21 +2141,21 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { if (whole.implib_sub_path) |sub_path| { comp.implib_emit = .{ - .directory = tmp_artifact_directory, + .root_dir = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; } if (whole.docs_sub_path) |sub_path| { comp.docs_emit = .{ - .directory = tmp_artifact_directory, + .root_dir = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; } if (whole.bin_sub_path) |sub_path| { - const emit: Emit = .{ - .directory = tmp_artifact_directory, + const emit: Path = .{ + .root_dir = tmp_artifact_directory, .sub_path = std.fs.path.basename(sub_path), }; comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); @@ -2329,7 +2315,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { try man.populateOtherManifest(pwc.manifest, pwc.prefix_map); } - const digest = man.final(); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); // Rename the temporary directory into place. // Close tmp dir and link.File to avoid open handle during rename. @@ -2341,7 +2328,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { const s = std.fs.path.sep_str; const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int); - const o_sub_path = "o" ++ s ++ digest; + const o_sub_path = "o" ++ s ++ hex_digest; // Work around windows `AccessDenied` if any files within this // directory are open by closing and reopening the file handles. @@ -2376,14 +2363,15 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { }, ); }; - comp.wholeCacheModeSetBinFilePath(whole, &digest); + comp.digest = bin_digest; + comp.wholeCacheModeSetBinFilePath(whole, &hex_digest); // The linker flush functions need to know the final output path // for debug info purposes because executable debug info contains // references object file paths. if (comp.bin_file) |lf| { lf.emit = .{ - .directory = comp.local_cache_directory, + .root_dir = comp.local_cache_directory, .sub_path = whole.bin_sub_path.?, }; @@ -2393,9 +2381,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { } } - try flush(comp, arena, .main, main_progress_node); - - if (try comp.totalErrorCount() != 0) return; + try flush(comp, arena, .{ + .root_dir = comp.local_cache_directory, + .sub_path = o_sub_path, + }, .main, main_progress_node); // Failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { @@ -2410,8 +2399,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { assert(whole.lock == null); whole.lock = man.toOwnedLock(); }, - .incremental => { - try flush(comp, arena, .main, main_progress_node); + .incremental => |incremental| { + try flush(comp, arena, .{ + .root_dir = incremental.artifact_directory, + }, .main, main_progress_node); }, } } @@ -2440,7 +2431,13 @@ pub fn appendFileSystemInput( std.debug.panic("missing prefix directory: {}, {s}", .{ root, sub_file_path }); } -fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { +fn flush( + comp: *Compilation, + arena: Allocator, + default_artifact_directory: Path, + tid: Zcu.PerThread.Id, + prog_node: std.Progress.Node, +) !void { if (comp.bin_file) |lf| { // This is needed before reading the error flags. lf.flush(arena, tid, prog_node) catch |err| switch (err) { @@ -2454,17 +2451,7 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try link.File.C.flushEmitH(zcu); if (zcu.llvm_object) |llvm_object| { - const default_emit = switch (comp.cache_use) { - .whole => |whole| .{ - .directory = whole.tmp_artifact_directory.?, - .sub_path = "dummy", - }, - .incremental => |incremental| .{ - .directory = incremental.artifact_directory, - .sub_path = "dummy", - }, - }; - try emitLlvmObject(comp, arena, default_emit, null, llvm_object, prog_node); + try emitLlvmObject(comp, arena, default_artifact_directory, null, llvm_object, prog_node); } } } @@ -2533,7 +2520,7 @@ fn wholeCacheModeSetBinFilePath( @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.implib_emit = .{ - .directory = comp.local_cache_directory, + .root_dir = comp.local_cache_directory, .sub_path = sub_path, }; } @@ -2542,7 +2529,7 @@ fn wholeCacheModeSetBinFilePath( @memcpy(sub_path[digest_start..][0..digest.len], digest); comp.docs_emit = .{ - .directory = comp.local_cache_directory, + .root_dir = comp.local_cache_directory, .sub_path = sub_path, }; } @@ -2745,7 +2732,7 @@ fn emitOthers(comp: *Compilation) void { pub fn emitLlvmObject( comp: *Compilation, arena: Allocator, - default_emit: Emit, + default_artifact_directory: Path, bin_emit_loc: ?EmitLoc, llvm_object: LlvmObject.Ptr, prog_node: std.Progress.Node, @@ -2756,10 +2743,10 @@ pub fn emitLlvmObject( try llvm_object.emit(.{ .pre_ir_path = comp.verbose_llvm_ir, .pre_bc_path = comp.verbose_llvm_bc, - .bin_path = try resolveEmitLoc(arena, default_emit, bin_emit_loc), - .asm_path = try resolveEmitLoc(arena, default_emit, comp.emit_asm), - .post_ir_path = try resolveEmitLoc(arena, default_emit, comp.emit_llvm_ir), - .post_bc_path = try resolveEmitLoc(arena, default_emit, comp.emit_llvm_bc), + .bin_path = try resolveEmitLoc(arena, default_artifact_directory, bin_emit_loc), + .asm_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_asm), + .post_ir_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_ir), + .post_bc_path = try resolveEmitLoc(arena, default_artifact_directory, comp.emit_llvm_bc), .is_debug = comp.root_mod.optimize_mode == .Debug, .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, @@ -2772,14 +2759,14 @@ pub fn emitLlvmObject( fn resolveEmitLoc( arena: Allocator, - default_emit: Emit, + default_artifact_directory: Path, opt_loc: ?EmitLoc, ) Allocator.Error!?[*:0]const u8 { const loc = opt_loc orelse return null; const slice = if (loc.directory) |directory| try directory.joinZ(arena, &.{loc.basename}) else - try default_emit.basenamePath(arena, loc.basename); + try default_artifact_directory.joinStringZ(arena, loc.basename); return slice.ptr; } @@ -3010,14 +2997,26 @@ pub fn saveState(comp: *Compilation) !void { addBuf(&bufs, mem.sliceAsBytes(ip.free_dep_entries.items)); for (ip.locals, pt_headers.items) |*local, pt_header| { - addBuf(&bufs, mem.sliceAsBytes(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len])); - addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]); - addBuf(&bufs, mem.sliceAsBytes(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len])); - addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len])); + if (pt_header.intern_pool.limbs_len > 0) { + addBuf(&bufs, mem.sliceAsBytes(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len])); + } + if (pt_header.intern_pool.extra_len > 0) { + addBuf(&bufs, mem.sliceAsBytes(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len])); + } + if (pt_header.intern_pool.items_len > 0) { + addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len])); + addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len])); + } + if (pt_header.intern_pool.string_bytes_len > 0) { + addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]); + } + if (pt_header.intern_pool.tracked_insts_len > 0) { + addBuf(&bufs, mem.sliceAsBytes(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len])); + } + if (pt_header.intern_pool.files_len > 0) { + addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len])); + addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len])); + } } //// TODO: compilation errors @@ -3035,7 +3034,7 @@ pub fn saveState(comp: *Compilation) !void { // Using an atomic file prevents a crash or power failure from corrupting // the previous incremental compilation state. - var af = try lf.emit.directory.handle.atomicFile(basename, .{}); + var af = try lf.emit.root_dir.handle.atomicFile(basename, .{}); defer af.deinit(); try af.file.pwritevAll(bufs.items, 0); try af.finish(); @@ -4000,11 +3999,11 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{}); const emit = comp.docs_emit.?; - var out_dir = emit.directory.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { + var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create output directory '{}{s}': {s}", - .{ emit.directory, emit.sub_path, @errorName(err) }, + .{ emit.root_dir, emit.sub_path, @errorName(err) }, ); }; defer out_dir.close(); @@ -4024,7 +4023,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create '{}{s}/sources.tar': {s}", - .{ emit.directory, emit.sub_path, @errorName(err) }, + .{ emit.root_dir, emit.sub_path, @errorName(err) }, ); }; defer tar_file.close(); @@ -4223,11 +4222,11 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node); const emit = comp.docs_emit.?; - var out_dir = emit.directory.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { + var out_dir = emit.root_dir.handle.makeOpenPath(emit.sub_path, .{}) catch |err| { return comp.lockAndSetMiscFailure( .docs_copy, "unable to create output directory '{}{s}': {s}", - .{ emit.directory, emit.sub_path, @errorName(err) }, + .{ emit.root_dir, emit.sub_path, @errorName(err) }, ); }; defer out_dir.close(); @@ -4241,7 +4240,7 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anye return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}{s}' to '{}{s}': {s}", .{ sub_compilation.local_cache_directory, sub_compilation.cache_use.whole.bin_sub_path.?, - emit.directory, + emit.root_dir, emit.sub_path, @errorName(err), }); @@ -4403,7 +4402,7 @@ pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest } pub const CImportResult = struct { - out_zig_path: []u8, + digest: [Cache.bin_digest_len]u8, cache_hit: bool, errors: std.zig.ErrorBundle, @@ -4413,8 +4412,6 @@ pub const CImportResult = struct { }; /// Caller owns returned memory. -/// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked -/// a bit when we want to start using it from self-hosted. pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult { dev.check(.translate_c_command); @@ -4503,7 +4500,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module error.OutOfMemory => return error.OutOfMemory, error.SemanticAnalyzeFail => { return CImportResult{ - .out_zig_path = "", + .digest = undefined, .cache_hit = actual_hit, .errors = errors, }; @@ -4528,8 +4525,9 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module .incremental => {}, } - const digest = man.final(); - const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + const o_sub_path = "o" ++ std.fs.path.sep_str ++ hex_digest; var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); defer o_dir.close(); @@ -4541,8 +4539,8 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module try out_zig_file.writeAll(formatted); - break :digest digest; - } else man.final(); + break :digest bin_digest; + } else man.finalBin(); if (man.have_exclusive_lock) { // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is @@ -4554,14 +4552,8 @@ pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module }; } - const out_zig_path = try comp.local_cache_directory.join(comp.arena, &.{ - "o", &digest, cimport_zig_basename, - }); - if (comp.verbose_cimport) { - log.info("C import output: {s}", .{out_zig_path}); - } return CImportResult{ - .out_zig_path = out_zig_path, + .digest = digest, .cache_hit = actual_hit, .errors = std.zig.ErrorBundle.empty, }; @@ -4800,7 +4792,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Pr try argv.appendSlice(c_object.src.cache_exempt_flags); const out_obj_path = if (comp.bin_file) |lf| - try lf.emit.directory.join(arena, &.{lf.emit.sub_path}) + try lf.emit.root_dir.join(arena, &.{lf.emit.sub_path}) else "/dev/null"; diff --git a/src/InternPool.zig b/src/InternPool.zig index d953755987..83732a29f6 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2391,6 +2391,7 @@ pub const Key = union(enum) { func: Index, arg_values: []const Index, result: Index, + branch_count: u32, }; pub fn hash32(key: Key, ip: *const InternPool) u32 { @@ -6157,6 +6158,7 @@ pub const MemoizedCall = struct { func: Index, args_len: u32, result: Index, + branch_count: u32, }; pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void { @@ -6785,6 +6787,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .func = extra.data.func, .arg_values = @ptrCast(extra_list.view().items(.@"0")[extra.end..][0..extra.data.args_len]), .result = extra.data.result, + .branch_count = extra.data.branch_count, } }; }, }; @@ -7955,6 +7958,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All .func = memoized_call.func, .args_len = @intCast(memoized_call.arg_values.len), .result = memoized_call.result, + .branch_count = memoized_call.branch_count, }), }); extra.appendSliceAssumeCapacity(.{@ptrCast(memoized_call.arg_values)}); diff --git a/src/Liveness.zig b/src/Liveness.zig index 4ca28758e2..b75fc402dd 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -464,6 +464,7 @@ pub fn categorizeOperand( .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => { const o = air_datas[@intFromEnum(inst)].pl_op.operand; if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); @@ -1097,6 +1098,7 @@ fn analyzeInst( .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => { const operand = inst_datas[@intFromEnum(inst)].pl_op.operand; return analyzeOperands(a, pass, data, inst, .{ operand, .none, .none }); diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index 4392f25e10..7a9959481a 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -157,6 +157,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { }, .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, .wasm_memory_grow, => { const pl_op = data[@intFromEnum(inst)].pl_op; diff --git a/src/Sema.zig b/src/Sema.zig index c8e2f2ae15..3752cefe3f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -113,6 +113,11 @@ type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}, /// `AnalUnit` multiple times. dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .{}, +/// Whether memoization of this call is permitted. Operations with side effects global +/// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed +/// by `analyzeCall`. +allow_memoize: bool = true, + const MaybeComptimeAlloc = struct { /// The runtime index of the `alloc` instruction. runtime_index: Value.RuntimeIndex, @@ -183,6 +188,7 @@ const InternPool = @import("InternPool.zig"); const Alignment = InternPool.Alignment; const AnalUnit = InternPool.AnalUnit; const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; +const Cache = std.Build.Cache; pub const default_branch_quota = 1000; pub const default_reference_trace_len = 2; @@ -375,7 +381,7 @@ pub const Block = struct { c_import_buf: ?*std.ArrayList(u8) = null, - /// If not `null`, this boolean is set when a `dbg_var_ptr` or `dbg_var_val` + /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`. /// instruction is emitted. It signals that the innermost lexically /// enclosing `block`/`block_inline` should be translated into a real AIR /// `block` in order for codegen to match lexical scoping for debug vars. @@ -5523,6 +5529,7 @@ fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi .needed_comptime_reason = "eval branch quota must be comptime-known", })); sema.branch_quota = @max(sema.branch_quota, quota); + sema.allow_memoize = false; } fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { @@ -5871,16 +5878,18 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr return sema.failWithOwnedErrorMsg(&child_block, msg); } const parent_mod = parent_block.ownerModule(); + const digest = Cache.binToHex(c_import_res.digest); + const c_import_zig_path = try comp.arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ digest); const c_import_mod = Package.Module.create(comp.arena, .{ .global_cache_directory = comp.global_cache_directory, .paths = .{ .root = .{ - .root_dir = Compilation.Directory.cwd(), - .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", + .root_dir = comp.local_cache_directory, + .sub_path = c_import_zig_path, }, - .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), + .root_src_path = "cimport.zig", }, - .fully_qualified_name = c_import_res.out_zig_path, + .fully_qualified_name = c_import_zig_path, .cc_argv = parent_mod.cc_argv, .inherited = .{}, .global = comp.config, @@ -6413,6 +6422,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst } zcu.intern_pool.funcMaxStackAlignment(sema.func_index, alignment); + sema.allow_memoize = false; } fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { @@ -6431,6 +6441,7 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) .cau => return, // does nothing outside a function }; ip.funcSetCold(func, is_cold); + sema.allow_memoize = false; } fn zirDisableInstrumentation(sema: *Sema) CompileError!void { @@ -6442,6 +6453,7 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void { .cau => return, // does nothing outside a function }; ip.funcSetDisableInstrumentation(func); + sema.allow_memoize = false; } fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { @@ -6564,7 +6576,7 @@ fn addDbgVar( const operand_ty = sema.typeOf(operand); const val_ty = switch (air_tag) { .dbg_var_ptr => operand_ty.childType(mod), - .dbg_var_val => operand_ty, + .dbg_var_val, .dbg_arg_inline => operand_ty, else => unreachable, }; if (try sema.typeRequiresComptime(val_ty)) return; @@ -6583,25 +6595,26 @@ fn addDbgVar( if (block.need_debug_scope) |ptr| ptr.* = true; // Add the name to the AIR. - const name_extra_index = try sema.appendAirString(name); + const name_nts = try sema.appendAirString(name); _ = try block.addInst(.{ .tag = air_tag, .data = .{ .pl_op = .{ - .payload = name_extra_index, + .payload = @intFromEnum(name_nts), .operand = operand, } }, }); } -pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!u32 { - const str_extra_index: u32 = @intCast(sema.air_extra.items.len); +pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString { + if (str.len == 0) return .none; + const nts: Air.NullTerminatedString = @enumFromInt(sema.air_extra.items.len); const elements_used = str.len / 4 + 1; const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used); const buffer = mem.sliceAsBytes(elements); @memcpy(buffer[0..str.len], str); buffer[str.len] = 0; - return str_extra_index; + return nts; } fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -7585,6 +7598,9 @@ fn analyzeCall( const module_fn = zcu.funcInfo(module_fn_index); + // The call site definitely depends on the function's signature. + try sema.declareDependency(.{ .src_hash = module_fn.zir_body_inst }); + // This is not a function instance, so the function's `Nav` has a // `Cau` -- we don't need to check `generic_owner`. const fn_nav = ip.getNav(module_fn.owner_nav); @@ -7721,101 +7737,121 @@ fn analyzeCall( } })); } - // This `res2` is here instead of directly breaking from `res` due to a stage1 - // bug generating invalid LLVM IR. - const res2: Air.Inst.Ref = res2: { - if (should_memoize and is_comptime_call) { - if (zcu.intern_pool.getIfExists(.{ .memoized_call = .{ + memoize: { + if (!should_memoize) break :memoize; + if (!is_comptime_call) break :memoize; + const memoized_call_index = ip.getIfExists(.{ + .memoized_call = .{ .func = module_fn_index, .arg_values = memoized_arg_values, - .result = .none, - } })) |memoized_call_index| { - const memoized_call = zcu.intern_pool.indexToKey(memoized_call_index).memoized_call; - break :res2 Air.internedToRef(memoized_call.result); - } + .result = undefined, // ignored by hash+eql + .branch_count = undefined, // ignored by hash+eql + }, + }) orelse break :memoize; + const memoized_call = ip.indexToKey(memoized_call_index).memoized_call; + if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) { + // Let the call play out se we get the correct source location for the + // "evaluation exceeded X backwards branches" error. + break :memoize; } + sema.branch_count += memoized_call.branch_count; + break :res Air.internedToRef(memoized_call.result); + } - new_fn_info.return_type = sema.fn_ret_ty.toIntern(); - if (!is_comptime_call and !block.is_typeof) { - const zir_tags = sema.code.instructions.items(.tag); - for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { - .param, .param_comptime => { - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok; - const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); - const param_name = sema.code.nullTerminatedString(extra.data.name); - const inst = sema.inst_map.get(param).?; + // Since we're doing an inline call, we depend on the source code of the whole + // function declaration. + try sema.declareDependency(.{ .src_hash = fn_cau.zir_index }); - try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name); - }, - .param_anytype, .param_anytype_comptime => { - const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok; - const param_name = inst_data.get(sema.code); - const inst = sema.inst_map.get(param).?; + new_fn_info.return_type = sema.fn_ret_ty.toIntern(); + if (!is_comptime_call and !block.is_typeof) { + const zir_tags = sema.code.instructions.items(.tag); + for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { + .param, .param_comptime => { + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok; + const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); + const param_name = sema.code.nullTerminatedString(extra.data.name); + const inst = sema.inst_map.get(param).?; + + try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name); + }, + .param_anytype, .param_anytype_comptime => { + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok; + const param_name = inst_data.get(sema.code); + const inst = sema.inst_map.get(param).?; - try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name); - }, - else => continue, - }; - } + try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name); + }, + else => continue, + }; + } - if (is_comptime_call and ensure_result_used) { - try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src); - } + if (is_comptime_call and ensure_result_used) { + try sema.ensureResultUsed(block, sema.fn_ret_ty, call_src); + } - if (is_comptime_call or block.is_typeof) { - // Save the error trace as our first action in the function - // to match the behavior of runtime function calls. - const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); - sema.error_return_trace_index_on_fn_entry = error_return_trace_index; - child_block.error_return_trace_index = error_return_trace_index; - } + if (is_comptime_call or block.is_typeof) { + // Save the error trace as our first action in the function + // to match the behavior of runtime function calls. + const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); + sema.error_return_trace_index_on_fn_entry = error_return_trace_index; + child_block.error_return_trace_index = error_return_trace_index; + } - const result = result: { - sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) { - error.ComptimeReturn => break :result inlining.comptime_result, - else => |e| return e, - }; - break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope); + // We temporarily set `allow_memoize` to `true` to track this comptime call. + // It is restored after this call finishes analysis, so that a caller may + // know whether an in-progress call (containing this call) may be memoized. + const old_allow_memoize = sema.allow_memoize; + defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize; + sema.allow_memoize = true; + + // Store the current eval branch count so we can find out how many eval branches + // the comptime call caused. + const old_branch_count = sema.branch_count; + + const result = result: { + sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) { + error.ComptimeReturn => break :result inlining.comptime_result, + else => |e| return e, }; + break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope); + }; - if (is_comptime_call) { - const result_val = try sema.resolveConstValue(block, LazySrcLoc.unneeded, result, undefined); - const result_interned = result_val.toIntern(); - - // Transform ad-hoc inferred error set types into concrete error sets. - const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned); - - // If the result can mutate comptime vars, we must not memoize it, as it contains - // a reference to `comptime_allocs` so is not stable across instances of `Sema`. - // TODO: check whether any external comptime memory was mutated by the - // comptime function call. If so, then do not memoize the call here. - if (should_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) { - _ = try pt.intern(.{ .memoized_call = .{ - .func = module_fn_index, - .arg_values = memoized_arg_values, - .result = result_transformed, - } }); - } + if (is_comptime_call) { + const result_val = try sema.resolveConstValue(block, LazySrcLoc.unneeded, result, undefined); + const result_interned = result_val.toIntern(); - break :res2 Air.internedToRef(result_transformed); - } + // Transform ad-hoc inferred error set types into concrete error sets. + const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_interned); - if (try sema.resolveValue(result)) |result_val| { - const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); - break :res2 Air.internedToRef(result_transformed); + // If the result can mutate comptime vars, we must not memoize it, as it contains + // a reference to `comptime_allocs` so is not stable across instances of `Sema`. + // TODO: check whether any external comptime memory was mutated by the + // comptime function call. If so, then do not memoize the call here. + if (should_memoize and sema.allow_memoize and !Value.fromInterned(result_interned).canMutateComptimeVarState(zcu)) { + _ = try pt.intern(.{ .memoized_call = .{ + .func = module_fn_index, + .arg_values = memoized_arg_values, + .result = result_transformed, + .branch_count = sema.branch_count - old_branch_count, + } }); } - const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern()); - if (new_ty != .none) { - // TODO: mutate in place the previous instruction if possible - // rather than adding a bitcast instruction. - break :res2 try block.addBitCast(Type.fromInterned(new_ty), result); - } + break :res Air.internedToRef(result_transformed); + } - break :res2 result; - }; + if (try sema.resolveValue(result)) |result_val| { + const result_transformed = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); + break :res Air.internedToRef(result_transformed); + } + + const new_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result).toIntern()); + if (new_ty != .none) { + // TODO: mutate in place the previous instruction if possible + // rather than adding a bitcast instruction. + break :res try block.addBitCast(Type.fromInterned(new_ty), result); + } - break :res res2; + break :res result; } else res: { assert(!func_ty_info.is_generic); @@ -8263,7 +8299,7 @@ fn instantiateGenericCall( .name = if (child_block.ownerModule().strip) .none else - @enumFromInt(try sema.appendAirString(fn_zir.nullTerminatedString(param_name))), + try sema.appendAirString(fn_zir.nullTerminatedString(param_name)), } }, })); try child_block.params.append(sema.arena, .{ diff --git a/src/Type.zig b/src/Type.zig index 0a37e5a6f5..4437722f7d 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -202,6 +202,7 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error .C => try writer.writeAll("[*c]"), .Slice => try writer.writeAll("[]"), } + if (info.flags.is_allowzero and info.flags.size != .C) try writer.writeAll("allowzero "); if (info.flags.alignment != .none or info.packed_offset.host_size != 0 or info.flags.vector_index != .none) @@ -229,7 +230,6 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error } if (info.flags.is_const) try writer.writeAll("const "); if (info.flags.is_volatile) try writer.writeAll("volatile "); - if (info.flags.is_allowzero and info.flags.size != .C) try writer.writeAll("allowzero "); try print(Type.fromInterned(info.child), writer, pt); return; diff --git a/src/Zcu.zig b/src/Zcu.zig index 53ad80444e..9754740833 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -2554,18 +2554,29 @@ pub fn mapOldZirToNew( var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{}; defer match_stack.deinit(gpa); - // Main struct inst is always matched - try match_stack.append(gpa, .{ - .old_inst = .main_struct_inst, - .new_inst = .main_struct_inst, - }); - // Used as temporary buffers for namespace declaration instructions var old_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; defer old_decls.deinit(gpa); var new_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; defer new_decls.deinit(gpa); + // Map the main struct inst (and anything in its fields) + { + try old_zir.findDeclsRoot(gpa, &old_decls); + try new_zir.findDeclsRoot(gpa, &new_decls); + + assert(old_decls.items[0] == .main_struct_inst); + assert(new_decls.items[0] == .main_struct_inst); + + // We don't have any smart way of matching up these type declarations, so we always + // correlate them based on source order. + const n = @min(old_decls.items.len, new_decls.items.len); + try match_stack.ensureUnusedCapacity(gpa, n); + for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| { + match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst }); + } + } + while (match_stack.popOrNull()) |match_item| { // Match the namespace declaration itself try inst_map.put(gpa, match_item.old_inst, match_item.new_inst); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 0db30bfdbd..291518f5f0 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1240,11 +1240,11 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult { }; } - const nav_already_populated, const queue_linker_work = switch (ip.indexToKey(decl_val.toIntern())) { - .func => |f| .{ f.owner_nav == nav_index, false }, - .variable => |v| .{ false, v.owner_nav == nav_index }, - .@"extern" => .{ false, false }, - else => .{ false, true }, + const nav_already_populated, const queue_linker_work, const resolve_type = switch (ip.indexToKey(decl_val.toIntern())) { + .func => |f| .{ f.owner_nav == nav_index, true, false }, + .variable => |v| .{ false, v.owner_nav == nav_index, true }, + .@"extern" => .{ false, false, false }, + else => .{ false, true, true }, }; if (nav_already_populated) { @@ -1317,14 +1317,16 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult { queue_codegen: { if (!queue_linker_work) break :queue_codegen; - // Needed for codegen_nav which will call updateDecl and then the - // codegen backend wants full access to the Decl Type. - // We also need this for the `isFnOrHasRuntimeBits` check below. - // TODO: we could make the language more lenient by deferring this work - // to the `codegen_nav` job. - try decl_ty.resolveFully(pt); + if (resolve_type) { + // Needed for codegen_nav which will call updateDecl and then the + // codegen backend wants full access to the Decl Type. + // We also need this for the `isFnOrHasRuntimeBits` check below. + // TODO: we could make the language more lenient by deferring this work + // to the `codegen_nav` job. + try decl_ty.resolveFully(pt); + } - if (!decl_ty.isFnOrHasRuntimeBits(pt)) { + if (!resolve_type or !decl_ty.hasRuntimeBits(pt)) { if (zcu.comp.config.use_llvm) break :queue_codegen; if (file.mod.strip) break :queue_codegen; } @@ -2158,7 +2160,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError! .name = if (inner_block.ownerModule().strip) .none else - @enumFromInt(try sema.appendAirString(sema.code.nullTerminatedString(param_name))), + try sema.appendAirString(sema.code.nullTerminatedString(param_name)), } }, }); } diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index cb16dba688..882f3e98e3 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -170,7 +170,9 @@ const DbgInfoReloc = struct { fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void { switch (reloc.tag) { - .arg => try reloc.genArgDbgInfo(function), + .arg, + .dbg_arg_inline, + => try reloc.genArgDbgInfo(function), .dbg_var_ptr, .dbg_var_val, @@ -201,7 +203,7 @@ const DbgInfoReloc = struct { else => unreachable, // not a possible argument }; - try dw.genVarDebugInfo(.local_arg, reloc.name, reloc.ty, loc); + try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc); }, .plan9 => {}, .none => {}, @@ -237,7 +239,7 @@ const DbgInfoReloc = struct { break :blk .empty; }, }; - try dwarf.genVarDebugInfo(.local_var, reloc.name, reloc.ty, loc); + try dwarf.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc); }, .plan9 => {}, .none => {}, @@ -799,6 +801,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => try self.airDbgVar(inst), .call => try self.airCall(inst, .auto), @@ -4220,17 +4223,13 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const ty = self.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - - const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - if (name_nts != .none) { - const name = self.air.nullTerminatedString(@intFromEnum(name_nts)); - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name, - .mcv = self.args[arg_index], - }); - } + const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{ + .tag = tag, + .ty = ty, + .name = name.toSlice(self.air), + .mcv = self.args[arg_index], + }); const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; return self.finishAir(inst, result, .{ .none, .none, .none }); @@ -4644,14 +4643,14 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const ty = self.typeOf(operand); const mcv = try self.resolveInst(operand); - const name = self.air.nullTerminatedString(pl_op.payload); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv }); try self.dbg_info_relocs.append(self.gpa, .{ .tag = tag, .ty = ty, - .name = name, + .name = name.toSlice(self.air), .mcv = mcv, }); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 6e33b9b51f..796d3e34dc 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -248,7 +248,9 @@ const DbgInfoReloc = struct { fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void { switch (reloc.tag) { - .arg => try reloc.genArgDbgInfo(function), + .arg, + .dbg_arg_inline, + => try reloc.genArgDbgInfo(function), .dbg_var_ptr, .dbg_var_val, @@ -279,7 +281,7 @@ const DbgInfoReloc = struct { else => unreachable, // not a possible argument }; - try dw.genVarDebugInfo(.local_arg, reloc.name, reloc.ty, loc); + try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc); }, .plan9 => {}, .none => {}, @@ -315,7 +317,7 @@ const DbgInfoReloc = struct { break :blk .empty; }, }; - try dw.genVarDebugInfo(.local_var, reloc.name, reloc.ty, loc); + try dw.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc); }, .plan9 => {}, .none => {}, @@ -786,6 +788,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => try self.airDbgVar(inst), .call => try self.airCall(inst, .auto), @@ -4199,16 +4202,13 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const ty = self.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - if (name_nts != .none) { - const name = self.air.nullTerminatedString(@intFromEnum(name_nts)); - try self.dbg_info_relocs.append(self.gpa, .{ - .tag = tag, - .ty = ty, - .name = name, - .mcv = self.args[arg_index], - }); - } + const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{ + .tag = tag, + .ty = ty, + .name = name.toSlice(self.air), + .mcv = self.args[arg_index], + }); const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; return self.finishAir(inst, result, .{ .none, .none, .none }); @@ -4612,14 +4612,14 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const ty = self.typeOf(operand); const mcv = try self.resolveInst(operand); - const name = self.air.nullTerminatedString(pl_op.payload); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv }); try self.dbg_info_relocs.append(self.gpa, .{ .tag = tag, .ty = ty, - .name = name, + .name = name.toSlice(self.air), .mcv = mcv, }); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index bfbd91ba21..deeb0dc4da 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1644,6 +1644,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => try func.airDbgVar(inst), .dbg_inline_block => try func.airDbgInlineBlock(inst), @@ -4673,11 +4674,15 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void { const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); if (arg.name == .none) return; - const name = func.air.nullTerminatedString(@intFromEnum(arg.name)); switch (func.debug_output) { .dwarf => |dw| switch (mcv) { - .register => |reg| try dw.genVarDebugInfo(.local_arg, name, ty, .{ .reg = reg.dwarfNum() }), + .register => |reg| try dw.genLocalDebugInfo( + .local_arg, + arg.name.toSlice(func.air), + ty, + .{ .reg = reg.dwarfNum() }, + ), .load_frame => {}, else => {}, }, @@ -5179,16 +5184,17 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void { const operand = pl_op.operand; const ty = func.typeOf(operand); const mcv = try func.resolveInst(operand); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - const name = func.air.nullTerminatedString(pl_op.payload); - - try func.genVarDbgInfo(ty, mcv, name); + const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)]; + try func.genVarDbgInfo(tag, ty, mcv, name.toSlice(func.air)); return func.finishAir(inst, .unreach, .{ operand, .none, .none }); } fn genVarDbgInfo( func: Func, + tag: Air.Inst.Tag, ty: Type, mcv: MCValue, name: []const u8, @@ -5205,7 +5211,11 @@ fn genVarDbgInfo( break :blk .empty; }, }; - try dwarf.genVarDebugInfo(.local_var, name, ty, loc); + try dwarf.genLocalDebugInfo(switch (tag) { + else => unreachable, + .dbg_var_ptr, .dbg_var_val => .local_var, + .dbg_arg_inline => .local_arg, + }, name, ty, loc); }, .plan9 => {}, .none => {}, diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index a4fadad526..258941f19d 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -56,17 +56,17 @@ pub fn emitMir(emit: *Emit) Error!void { const hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20); const lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I); - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = start_offset, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | hi_r_type, .r_addend = 0, - }); + }, zo); - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = start_offset + 4, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | lo_r_type, .r_addend = 0, - }); + }, zo); }, .load_tlv_reloc => |symbol| { const elf_file = emit.bin_file.cast(.elf).?; @@ -76,23 +76,23 @@ pub fn emitMir(emit: *Emit) Error!void { const R_RISCV = std.elf.R_RISCV; - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = start_offset, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_HI20), .r_addend = 0, - }); + }, zo); - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = start_offset + 4, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_ADD), .r_addend = 0, - }); + }, zo); - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = start_offset + 8, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | @intFromEnum(R_RISCV.TPREL_LO12_I), .r_addend = 0, - }); + }, zo); }, .call_extern_fn_reloc => |symbol| { const elf_file = emit.bin_file.cast(.elf).?; @@ -101,11 +101,11 @@ pub fn emitMir(emit: *Emit) Error!void { const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT); - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = start_offset, .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, .r_addend = 0, - }); + }, zo); }, }; } diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index a3d3f107a2..99192aa554 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -643,6 +643,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => try self.airDbgVar(inst), .call => try self.airCall(inst, .auto), @@ -1662,7 +1663,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const name = self.air.nullTerminatedString(pl_op.payload); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); const operand = pl_op.operand; // TODO emit debug info for this variable _ = name; @@ -3582,13 +3583,15 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); if (arg.name == .none) return; - const name = self.air.nullTerminatedString(@intFromEnum(arg.name)); switch (self.debug_output) { .dwarf => |dw| switch (mcv) { - .register => |reg| try dw.genVarDebugInfo(.local_arg, name, ty, .{ - .reg = reg.dwarfNum(), - }), + .register => |reg| try dw.genLocalDebugInfo( + .local_arg, + arg.name.toSlice(self.air), + ty, + .{ .reg = reg.dwarfNum() }, + ), else => {}, }, else => {}, diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 33b1fd31c2..d2e9db8062 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1917,8 +1917,9 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .dbg_stmt => func.airDbgStmt(inst), .dbg_inline_block => func.airDbgInlineBlock(inst), - .dbg_var_ptr => func.airDbgVar(inst, true), - .dbg_var_val => func.airDbgVar(inst, false), + .dbg_var_ptr => func.airDbgVar(inst, .local_var, true), + .dbg_var_val => func.airDbgVar(inst, .local_var, false), + .dbg_arg_inline => func.airDbgVar(inst, .local_arg, false), .call => func.airCall(inst, .auto), .call_always_tail => func.airCall(inst, .always_tail), @@ -2585,13 +2586,13 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { switch (func.debug_output) { .dwarf => |dwarf| { - const name_nts = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - if (name_nts != .none) { - const name = func.air.nullTerminatedString(@intFromEnum(name_nts)); - try dwarf.genVarDebugInfo(.local_arg, name, arg_ty, .{ - .wasm_ext = .{ .local = arg.local.value }, - }); - } + const name = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name != .none) try dwarf.genLocalDebugInfo( + .local_arg, + name.toSlice(func.air), + arg_ty, + .{ .wasm_ext = .{ .local = arg.local.value } }, + ); }, else => {}, } @@ -6454,7 +6455,12 @@ fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len])); } -fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void { +fn airDbgVar( + func: *CodeGen, + inst: Air.Inst.Index, + local_tag: link.File.Dwarf.WipNav.LocalTag, + is_ptr: bool, +) InnerError!void { _ = is_ptr; if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); @@ -6464,8 +6470,8 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), operand }); - const name = func.air.nullTerminatedString(pl_op.payload); - log.debug(" var name = ({s})", .{name}); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + log.debug(" var name = ({s})", .{name.toSlice(func.air)}); const loc: link.File.Dwarf.Loc = switch (operand) { .local => |local| .{ .wasm_ext = .{ .local = local.value } }, @@ -6474,7 +6480,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void break :blk .empty; }, }; - try func.debug_output.dwarf.genVarDebugInfo(.local_var, name, ty, loc); + try func.debug_output.dwarf.genLocalDebugInfo(local_tag, name.toSlice(func.air), ty, loc); return func.finishAir(inst, .none, &.{}); } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 86fb2bf3e9..316389a7c2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -59,19 +59,19 @@ owner: Owner, inline_func: InternPool.Index, mod: *Package.Module, err_msg: ?*ErrorMsg, +arg_index: u32, args: []MCValue, va_info: union { sysv: struct { gp_count: u32, fp_count: u32, - overflow_arg_area: FrameAddr, - reg_save_area: FrameAddr, + overflow_arg_area: bits.FrameAddr, + reg_save_area: bits.FrameAddr, }, win64: struct {}, }, ret_mcv: InstTracking, fn_type: Type, -arg_index: u32, src_loc: Zcu.LazySrcLoc, eflags_inst: ?Air.Inst.Index = null, @@ -81,9 +81,6 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, /// MIR extra data mir_extra: std.ArrayListUnmanaged(u32) = .{}, -stack_args: std.ArrayListUnmanaged(StackVar) = .{}, -stack_vars: std.ArrayListUnmanaged(StackVar) = .{}, - /// Byte offset within the source file of the ending curly. end_di_line: u32, end_di_column: u32, @@ -113,10 +110,6 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; -const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; -const RegisterOffset = struct { reg: Register, off: i32 = 0 }; -const SymbolOffset = struct { sym: u32, off: i32 = 0 }; - const Owner = union(enum) { nav_index: InternPool.Nav.Index, lazy_sym: link.File.LazySymbol, @@ -174,7 +167,7 @@ pub const MCValue = union(enum) { /// The value is split across two registers. register_pair: [2]Register, /// The value is a constant offset from the value in a register. - register_offset: RegisterOffset, + register_offset: bits.RegisterOffset, /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register. register_overflow: struct { reg: Register, eflags: Condition }, /// The value is in memory at a hard-coded address. @@ -182,11 +175,11 @@ pub const MCValue = union(enum) { memory: u64, /// The value is in memory at an address not-yet-allocated by the linker. /// This traditionally corresponds to a relocation emitted in a relocatable object file. - load_symbol: SymbolOffset, + load_symbol: bits.SymbolOffset, /// The address of the memory location not-yet-allocated by the linker. - lea_symbol: SymbolOffset, + lea_symbol: bits.SymbolOffset, /// The value is in memory at a constant offset from the address in a register. - indirect: RegisterOffset, + indirect: bits.RegisterOffset, /// The value is in memory. /// Payload is a symbol index. load_direct: u32, @@ -207,10 +200,10 @@ pub const MCValue = union(enum) { lea_tlv: u32, /// The value stored at an offset from a frame index /// Payload is a frame address. - load_frame: FrameAddr, + load_frame: bits.FrameAddr, /// The address of an offset from a frame index /// Payload is a frame address. - lea_frame: FrameAddr, + lea_frame: bits.FrameAddr, /// Supports integer_per_element abi elementwise_regs_then_frame: packed struct { regs: u3 = 0, frame_off: i29 = 0, frame_index: FrameIndex }, /// This indicates that we have already allocated a frame index for this instruction, @@ -426,10 +419,7 @@ pub const MCValue = union(enum) { .load_symbol => |sym_off| { assert(sym_off.off == 0); return .{ - .base = .{ .reloc = .{ - .atom_index = try function.owner.getSymbolIndex(function), - .sym_index = sym_off.sym, - } }, + .base = .{ .reloc = sym_off.sym_index }, .mod = .{ .rm = .{ .size = size, .disp = sym_off.off, @@ -456,8 +446,8 @@ pub const MCValue = union(enum) { .register_overflow => |pl| try writer.print("{s}:{s}", .{ @tagName(pl.eflags), @tagName(pl.reg), }), - .load_symbol => |pl| try writer.print("[{} + 0x{x}]", .{ pl.sym, pl.off }), - .lea_symbol => |pl| try writer.print("{} + 0x{x}", .{ pl.sym, pl.off }), + .load_symbol => |pl| try writer.print("[{} + 0x{x}]", .{ pl.sym_index, pl.off }), + .lea_symbol => |pl| try writer.print("{} + 0x{x}", .{ pl.sym_index, pl.off }), .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}), .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), @@ -728,12 +718,6 @@ const InstTracking = struct { } }; -const StackVar = struct { - name: []const u8, - type: Type, - frame_addr: FrameAddr, -}; - const FrameAlloc = struct { abi_size: u31, spill_pad: u3, @@ -818,11 +802,11 @@ pub fn generate( .owner = .{ .nav_index = func.owner_nav }, .inline_func = func_index, .err_msg = null, + .arg_index = undefined, .args = undefined, // populated after `resolveCallingConventionValues` .va_info = undefined, // populated after `resolveCallingConventionValues` .ret_mcv = undefined, // populated after `resolveCallingConventionValues` .fn_type = fn_type, - .arg_index = 0, .src_loc = src_loc, .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, @@ -839,8 +823,6 @@ pub fn generate( function.exitlude_jump_relocs.deinit(gpa); function.mir_instructions.deinit(gpa); function.mir_extra.deinit(gpa); - function.stack_args.deinit(gpa); - function.stack_vars.deinit(gpa); } wip_mir_log.debug("{}:", .{fmtNav(func.owner_nav, ip)}); @@ -895,6 +877,7 @@ pub fn generate( }), ); function.va_info = switch (cc) { + else => undefined, .SysV => .{ .sysv = .{ .gp_count = call_info.gp_count, .fp_count = call_info.fp_count, @@ -902,7 +885,6 @@ pub fn generate( .reg_save_area = undefined, } }, .Win64 => .{ .win64 = .{} }, - else => undefined, }; function.gen() catch |err| switch (err) { @@ -913,9 +895,6 @@ pub fn generate( else => |e| return e, }; - try function.genStackVarDebugInfo(.local_arg, function.stack_args.items); - try function.genStackVarDebugInfo(.local_var, function.stack_vars.items); - var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), .extra = try function.mir_extra.toOwnedSlice(gpa), @@ -924,6 +903,7 @@ pub fn generate( defer mir.deinit(gpa); var emit: Emit = .{ + .air = function.air, .lower = .{ .bin_file = bin_file, .allocator = gpa, @@ -934,6 +914,13 @@ pub fn generate( .link_mode = comp.config.link_mode, .pic = mod.pic, }, + .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { + error.CodegenFail => return Result{ .fail = function.err_msg.? }, + error.OutOfRegisters => return Result{ + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }, .debug_output = debug_output, .code = code, .prev_di_pc = 0, @@ -991,11 +978,11 @@ pub fn generateLazy( .owner = .{ .lazy_sym = lazy_sym }, .inline_func = undefined, .err_msg = null, + .arg_index = undefined, .args = undefined, .va_info = undefined, .ret_mcv = undefined, .fn_type = undefined, - .arg_index = undefined, .src_loc = src_loc, .end_di_line = undefined, // no debug info yet .end_di_column = undefined, // no debug info yet @@ -1013,14 +1000,15 @@ pub fn generateLazy( else => |e| return e, }; - var mir = Mir{ + var mir: Mir = .{ .instructions = function.mir_instructions.toOwnedSlice(), .extra = try function.mir_extra.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; defer mir.deinit(gpa); - var emit = Emit{ + var emit: Emit = .{ + .air = function.air, .lower = .{ .bin_file = bin_file, .allocator = gpa, @@ -1031,6 +1019,13 @@ pub fn generateLazy( .link_mode = comp.config.link_mode, .pic = mod.pic, }, + .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { + error.CodegenFail => return Result{ .fail = function.err_msg.? }, + error.OutOfRegisters => return Result{ + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + }, + else => |e| return e, + }, .debug_output = debug_output, .code = code, .prev_di_pc = undefined, // no debug info yet @@ -1116,7 +1111,7 @@ fn formatWipMir( ) @TypeOf(writer).Error!void { const comp = data.self.bin_file.comp; const mod = comp.root_mod; - var lower = Lower{ + var lower: Lower = .{ .bin_file = data.self.bin_file, .allocator = data.self.gpa, .mir = .{ @@ -1204,6 +1199,7 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { self.mir_extra.appendAssumeCapacity(switch (field.type) { u32 => @field(extra, field.name), i32, Mir.Memory.Info => @bitCast(@field(extra, field.name)), + bits.FrameIndex => @intFromEnum(@field(extra, field.name)), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }); } @@ -1357,6 +1353,124 @@ fn asmPlaceholder(self: *Self) !Mir.Inst.Index { }); } +const MirTagAir = enum { dbg_local }; + +fn asmAir(self: *Self, tag: MirTagAir, inst: Air.Inst.Index) !void { + _ = try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_a, + }, + .data = .{ .a = .{ .air_inst = inst } }, + }); +} + +fn asmAirImmediate(self: *Self, tag: MirTagAir, inst: Air.Inst.Index, imm: Immediate) !void { + switch (imm) { + .signed => |s| _ = try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_ai_s, + }, + .data = .{ .ai = .{ + .air_inst = inst, + .i = @bitCast(s), + } }, + }), + .unsigned => |u| _ = if (math.cast(u32, u)) |small| try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_ai_u, + }, + .data = .{ .ai = .{ + .air_inst = inst, + .i = small, + } }, + }) else try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_ai_64, + }, + .data = .{ .ai = .{ + .air_inst = inst, + .i = try self.addExtra(Mir.Imm64.encode(u)), + } }, + }), + .reloc => |sym_off| _ = if (sym_off.off == 0) try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_as, + }, + .data = .{ .as = .{ + .air_inst = inst, + .sym_index = sym_off.sym_index, + } }, + }) else try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_aso, + }, + .data = .{ .ax = .{ + .air_inst = inst, + .payload = try self.addExtra(sym_off), + } }, + }), + } +} + +fn asmAirRegisterImmediate( + self: *Self, + tag: MirTagAir, + inst: Air.Inst.Index, + reg: Register, + imm: Immediate, +) !void { + _ = try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_aro, + }, + .data = .{ .rx = .{ + .r1 = reg, + .payload = try self.addExtra(Mir.AirOffset{ + .air_inst = inst, + .off = imm.signed, + }), + } }, + }); +} + +fn asmAirFrameAddress( + self: *Self, + tag: MirTagAir, + inst: Air.Inst.Index, + frame_addr: bits.FrameAddr, +) !void { + _ = try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_af, + }, + .data = .{ .ax = .{ + .air_inst = inst, + .payload = try self.addExtra(frame_addr), + } }, + }); +} + +fn asmAirMemory(self: *Self, tag: MirTagAir, inst: Air.Inst.Index, m: Memory) !void { + _ = try self.addInst(.{ + .tag = .pseudo, + .ops = switch (tag) { + .dbg_local => .pseudo_dbg_local_am, + }, + .data = .{ .ax = .{ + .air_inst = inst, + .payload = try self.addExtra(Mir.Memory.encode(m)), + } }, + }); +} + fn asmOpOnly(self: *Self, tag: Mir.Inst.FixedTag) !void { _ = try self.addInst(.{ .tag = tag[1], @@ -1368,6 +1482,8 @@ fn asmOpOnly(self: *Self, tag: Mir.Inst.FixedTag) !void { } fn asmPseudo(self: *Self, ops: Mir.Inst.Ops) !void { + assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and + std.mem.endsWith(u8, @tagName(ops), "_none")); _ = try self.addInst(.{ .tag = .pseudo, .ops = ops, @@ -1395,9 +1511,9 @@ fn asmImmediate(self: *Self, tag: Mir.Inst.FixedTag, imm: Immediate) !void { .reloc => .rel, }, .data = switch (imm) { - .reloc => |x| reloc: { + .reloc => |sym_off| reloc: { assert(tag[0] == ._); - break :reloc .{ .reloc = x }; + break :reloc .{ .reloc = sym_off }; }, .signed, .unsigned => .{ .i = .{ .fixes = tag[0], @@ -1424,31 +1540,22 @@ fn asmRegisterRegister(self: *Self, tag: Mir.Inst.FixedTag, reg1: Register, reg2 } fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm: Immediate) !void { - const ops: Mir.Inst.Ops = switch (imm) { - .signed => .ri_s, - .unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64, + const ops: Mir.Inst.Ops, const i: u32 = switch (imm) { + .signed => |s| .{ .ri_s, @bitCast(s) }, + .unsigned => |u| if (math.cast(u32, u)) |small| + .{ .ri_u, small } + else + .{ .ri_64, try self.addExtra(Mir.Imm64.encode(imm.unsigned)) }, .reloc => unreachable, }; _ = try self.addInst(.{ .tag = tag[1], .ops = ops, - .data = switch (ops) { - .ri_s, .ri_u => .{ .ri = .{ - .fixes = tag[0], - .r1 = reg, - .i = switch (imm) { - .signed => |s| @bitCast(s), - .unsigned => |u| @intCast(u), - .reloc => unreachable, - }, - } }, - .ri64 => .{ .rx = .{ - .fixes = tag[0], - .r1 = reg, - .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)), - } }, - else => unreachable, - }, + .data = .{ .ri = .{ + .fixes = tag[0], + .r1 = reg, + .i = i, + } }, }); } @@ -1996,6 +2103,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { const ip = &mod.intern_pool; const air_tags = self.air.instructions.items(.tag); + self.arg_index = 0; for (body) |inst| { wip_mir_log.debug("{}", .{self.fmtAir(inst)}); verbose_tracking_log.debug("{}", .{self.fmtTracking()}); @@ -2009,6 +2117,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { self.checkInvariantsAfterAirInst(inst, old_air_bookkeeping); } + if (self.arg_index == 0) try self.airDbgVarArgs(); + self.arg_index = 0; for (body) |inst| { if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue; wip_mir_log.debug("{}", .{self.fmtAir(inst)}); @@ -2158,6 +2268,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => try self.airDbgVar(inst), .call => try self.airCall(inst, .auto), @@ -2485,12 +2596,12 @@ fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayo }; } -fn getFrameAddrAlignment(self: *Self, frame_addr: FrameAddr) Alignment { +fn getFrameAddrAlignment(self: *Self, frame_addr: bits.FrameAddr) Alignment { const alloc_align = self.frame_allocs.get(@intFromEnum(frame_addr.index)).abi_align; return @enumFromInt(@min(@intFromEnum(alloc_align), @ctz(frame_addr.off))); } -fn getFrameAddrSize(self: *Self, frame_addr: FrameAddr) u32 { +fn getFrameAddrSize(self: *Self, frame_addr: bits.FrameAddr) u32 { return self.frame_allocs.get(@intFromEnum(frame_addr.index)).abi_size - @as(u31, @intCast(frame_addr.off)); } @@ -11949,89 +12060,81 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { } fn airDbgArg(self: *Self, inst: Air.Inst.Index) !void { - defer self.finishAirBookkeeping(); - if (self.debug_output == .none) return; - const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; - const name = self.air.nullTerminatedString(@intFromEnum(name_nts)); - if (name.len > 0) { - const arg_ty = self.typeOfIndex(inst); - const arg_mcv = self.getResolvedInstValue(inst).short; - try self.genVarDebugInfo(.local_arg, .dbg_var_val, name, arg_ty, arg_mcv); + // skip zero-bit arguments as they don't have a corresponding arg instruction + var arg_index = self.arg_index; + while (self.args[arg_index] == .none) arg_index += 1; + self.arg_index = arg_index + 1; + + if (self.debug_output != .none) { + const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name; + if (name != .none) try self.genLocalDebugInfo(inst, self.getResolvedInstValue(inst).short); + if (self.liveness.isUnused(inst)) try self.processDeath(inst); } - if (self.liveness.isUnused(inst)) try self.processDeath(inst); + for (self.args[self.arg_index..]) |arg| { + if (arg != .none) break; + } else try self.airDbgVarArgs(); + self.finishAirBookkeeping(); +} + +fn airDbgVarArgs(self: *Self) !void { + if (self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args) + try self.asmPseudo(.pseudo_dbg_var_args_none); } -fn genVarDebugInfo( +fn genLocalDebugInfo( self: *Self, - var_tag: link.File.Dwarf.WipNav.VarTag, - tag: Air.Inst.Tag, - name: []const u8, - ty: Type, + inst: Air.Inst.Index, mcv: MCValue, ) !void { - const stack_vars = switch (var_tag) { - .local_arg => &self.stack_args, - .local_var => &self.stack_vars, - }; - switch (self.debug_output) { - .dwarf => |dwarf| switch (tag) { - else => unreachable, - .dbg_var_ptr => { - const var_ty = ty.childType(self.pt.zcu); - switch (mcv) { - else => { - log.info("dbg_var_ptr({s}({}))", .{ @tagName(mcv), mcv }); - unreachable; - }, - .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable, - .lea_frame => |frame_addr| try stack_vars.append(self.gpa, .{ - .name = name, - .type = var_ty, - .frame_addr = frame_addr, - }), - .lea_symbol => |sym_off| try dwarf.genVarDebugInfo(var_tag, name, var_ty, .{ .plus = .{ - &.{ .addr = .{ .sym = sym_off.sym } }, - &.{ .consts = sym_off.off }, - } }), - } - }, - .dbg_var_val => switch (mcv) { - .none => try dwarf.genVarDebugInfo(var_tag, name, ty, .empty), + if (self.debug_output == .none) return; + switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) { + else => unreachable, + .arg, .dbg_arg_inline, .dbg_var_val => |tag| { + switch (mcv) { + .none => try self.asmAir(.dbg_local, inst), .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable, - .immediate => |immediate| try dwarf.genVarDebugInfo(var_tag, name, ty, .{ .stack_value = &.{ - .constu = immediate, - } }), + .immediate => |imm| try self.asmAirImmediate(.dbg_local, inst, Immediate.u(imm)), + .lea_frame => |frame_addr| try self.asmAirFrameAddress(.dbg_local, inst, frame_addr), + .lea_symbol => |sym_off| try self.asmAirImmediate(.dbg_local, inst, Immediate.rel(sym_off)), else => { + const ty = switch (tag) { + else => unreachable, + .arg => self.typeOfIndex(inst), + .dbg_arg_inline, .dbg_var_val => self.typeOf( + self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op.operand, + ), + }; const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(ty, self.pt)); try self.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{}); - try stack_vars.append(self.gpa, .{ - .name = name, - .type = ty, - .frame_addr = .{ .index = frame_index }, + try self.asmAirMemory(.dbg_local, inst, .{ + .base = .{ .frame = frame_index }, + .mod = .{ .rm = .{ .size = .qword } }, }); }, - }, + } }, - .plan9 => {}, - .none => {}, - } -} - -fn genStackVarDebugInfo( - self: Self, - var_tag: link.File.Dwarf.WipNav.VarTag, - stack_vars: []const StackVar, -) !void { - switch (self.debug_output) { - .dwarf => |dwarf| for (stack_vars) |stack_var| { - const frame_loc = self.frame_locs.get(@intFromEnum(stack_var.frame_addr.index)); - try dwarf.genVarDebugInfo(var_tag, stack_var.name, stack_var.type, .{ .plus = .{ - &.{ .breg = frame_loc.base.dwarfNum() }, - &.{ .consts = @as(i33, frame_loc.disp) + stack_var.frame_addr.off }, - } }); + .dbg_var_ptr => switch (mcv) { + else => unreachable, + .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable, + .lea_frame => |frame_addr| try self.asmAirMemory(.dbg_local, inst, .{ + .base = .{ .frame = frame_addr.index }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = frame_addr.off, + } }, + }), + .lea_symbol => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{ + .base = .{ .reloc = sym_off.sym_index }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = sym_off.off, + } }, + }), + .lea_direct, .lea_got, .lea_tlv => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{ + .base = .{ .reloc = sym_index }, + .mod = .{ .rm = .{ .size = .qword } }, + }), }, - .plan9 => {}, - .none => {}, } } @@ -12351,10 +12454,7 @@ fn genCall(self: *Self, info: union(enum) { if (self.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; const sym_index = try zo.getOrCreateMetadataForNav(elf_file, func.owner_nav); - try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym_index, - })); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index })); } else if (self.bin_file.cast(.coff)) |coff_file| { const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; @@ -12364,10 +12464,7 @@ fn genCall(self: *Self, info: union(enum) { const zo = macho_file.getZigObject().?; const sym_index = try zo.getOrCreateMetadataForNav(macho_file, func.owner_nav); const sym = zo.symbols.items[sym_index]; - try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym.nlist_idx, - })); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym.nlist_idx })); } else if (self.bin_file.cast(.plan9)) |p9| { const atom_index = try p9.seeNav(pt, func.owner_nav); const atom = p9.getAtom(atom_index); @@ -12385,19 +12482,13 @@ fn genCall(self: *Self, info: union(enum) { @"extern".name.toSlice(ip), @"extern".lib_name.toSlice(ip), ); - try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = target_sym_index, - })); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index })); } else if (self.bin_file.cast(.macho)) |macho_file| { const target_sym_index = try macho_file.getGlobalSymbol( @"extern".name.toSlice(ip), @"extern".lib_name.toSlice(ip), ); - try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = target_sym_index, - })); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index })); } else try self.genExternSymbolRef( .call, @"extern".lib_name.toSlice(ip), @@ -12412,16 +12503,10 @@ fn genCall(self: *Self, info: union(enum) { }, .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| { const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib); - try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = target_sym_index, - })); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index })); } else if (self.bin_file.cast(.macho)) |macho_file| { const target_sym_index = try macho_file.getGlobalSymbol(lib.callee, lib.lib); - try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = target_sym_index, - })); + try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = target_sym_index })); } else try self.genExternSymbolRef(.call, lib.lib, lib.callee), } return call_info.return_value.short; @@ -13060,29 +13145,21 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { self.inline_func = extra.data.func; _ = try self.addInst(.{ .tag = .pseudo, - .ops = .pseudo_dbg_inline_func, + .ops = .pseudo_dbg_enter_inline_func, .data = .{ .func = extra.data.func }, }); try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); _ = try self.addInst(.{ .tag = .pseudo, - .ops = .pseudo_dbg_inline_func, + .ops = .pseudo_dbg_leave_inline_func, .data = .{ .func = old_inline_func }, }); } fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const operand = pl_op.operand; - const ty = self.typeOf(operand); - const mcv = try self.resolveInst(operand); - - const name = self.air.nullTerminatedString(pl_op.payload); - - const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; - try self.genVarDebugInfo(.local_var, tag, name, ty, mcv); - - return self.finishAir(inst, .unreach, .{ operand, .none, .none }); + try self.genLocalDebugInfo(inst, try self.resolveInst(pl_op.operand)); + return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); } fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !Mir.Inst.Index { @@ -14970,10 +15047,7 @@ fn genSetReg( .general_purpose => { assert(sym_off.off == 0); try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(dst_reg, abi_size), .{ - .base = .{ .reloc = .{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym_off.sym, - } }, + .base = .{ .reloc = sym_off.sym_index }, .mod = .{ .rm = .{ .size = self.memSize(ty), .disp = sym_off.off, @@ -14991,10 +15065,7 @@ fn genSetReg( .ops = .direct_reloc, .data = .{ .rx = .{ .r1 = registerAlias(dst_reg, abi_size), - .payload = try self.addExtra(bits.Symbol{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym_index, - }), + .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), } }, }); return; @@ -15019,52 +15090,38 @@ fn genSetReg( }, ); }, - .lea_symbol => |sym_index| { - const atom_index = try self.owner.getSymbolIndex(self); - switch (self.bin_file.tag) { - .elf, .macho => { - try self.asmRegisterMemory( - .{ ._, .lea }, - dst_reg.to64(), - .{ - .base = .{ .reloc = .{ - .atom_index = atom_index, - .sym_index = sym_index.sym, - } }, - .mod = .{ .rm = .{ - .size = .qword, - .disp = sym_index.off, - } }, - }, - ); - }, - else => return self.fail("TODO emit symbol sequence on {s}", .{ - @tagName(self.bin_file.tag), - }), - } - }, - .lea_direct, .lea_got => |sym_index| { - const atom_index = try self.owner.getSymbolIndex(self); - _ = try self.addInst(.{ - .tag = switch (src_mcv) { - .lea_direct => .lea, - .lea_got => .mov, - else => unreachable, - }, - .ops = switch (src_mcv) { - .lea_direct => .direct_reloc, - .lea_got => .got_reloc, - else => unreachable, + .lea_symbol => |sym_off| switch (self.bin_file.tag) { + .elf, .macho => try self.asmRegisterMemory( + .{ ._, .lea }, + dst_reg.to64(), + .{ + .base = .{ .reloc = sym_off.sym_index }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = sym_off.off, + } }, }, - .data = .{ .rx = .{ - .r1 = dst_reg.to64(), - .payload = try self.addExtra(bits.Symbol{ - .atom_index = atom_index, - .sym_index = sym_index, - }), - } }, - }); + ), + else => return self.fail("TODO emit symbol sequence on {s}", .{ + @tagName(self.bin_file.tag), + }), }, + .lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{ + .tag = switch (src_mcv) { + .lea_direct => .lea, + .lea_got => .mov, + else => unreachable, + }, + .ops = switch (src_mcv) { + .lea_direct => .direct_reloc, + .lea_got => .got_reloc, + else => unreachable, + }, + .data = .{ .rx = .{ + .r1 = dst_reg.to64(), + .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), + } }, + }), .lea_tlv => unreachable, // TODO: remove this .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref), opts), } @@ -15085,7 +15142,7 @@ fn genSetMem( .none => .{ .immediate = @bitCast(@as(i64, disp)) }, .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, - .reloc => |base_symbol| .{ .lea_symbol = .{ .sym = base_symbol.sym_index, .off = disp } }, + .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } }, }; switch (src_mcv) { .none, @@ -15328,7 +15385,6 @@ fn genExternSymbolRef( lib: ?[]const u8, callee: []const u8, ) InnerError!void { - const atom_index = try self.owner.getSymbolIndex(self); if (self.bin_file.cast(.coff)) |coff_file| { const global_index = try coff_file.getGlobalSymbol(callee, lib); _ = try self.addInst(.{ @@ -15336,8 +15392,7 @@ fn genExternSymbolRef( .ops = .import_reloc, .data = .{ .rx = .{ .r1 = .rax, - .payload = try self.addExtra(bits.Symbol{ - .atom_index = atom_index, + .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = link.File.Coff.global_symbol_bit | global_index, }), } }, @@ -15364,10 +15419,10 @@ fn genLazySymbolRef( if (self.mod.pic) { switch (tag) { .lea, .call => try self.genSetReg(reg, Type.usize, .{ - .lea_symbol = .{ .sym = sym_index }, + .lea_symbol = .{ .sym_index = sym_index }, }, .{}), .mov => try self.genSetReg(reg, Type.usize, .{ - .load_symbol = .{ .sym = sym_index }, + .load_symbol = .{ .sym_index = sym_index }, }, .{}), else => unreachable, } @@ -15376,19 +15431,13 @@ fn genLazySymbolRef( .call => try self.asmRegister(.{ ._, .call }, reg), else => unreachable, } - } else { - const reloc = bits.Symbol{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym_index, - }; - switch (tag) { - .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ - .base = .{ .reloc = reloc }, - .mod = .{ .rm = .{ .size = .qword } }, - }), - .call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(reloc)), - else => unreachable, - } + } else switch (tag) { + .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ + .base = .{ .reloc = sym_index }, + .mod = .{ .rm = .{ .size = .qword } }, + }), + .call => try self.asmImmediate(.{ ._, .call }, Immediate.rel(.{ .sym_index = sym_index })), + else => unreachable, } } else if (self.bin_file.cast(.plan9)) |p9_file| { const atom_index = p9_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| @@ -15438,10 +15487,10 @@ fn genLazySymbolRef( const sym = zo.symbols.items[sym_index]; switch (tag) { .lea, .call => try self.genSetReg(reg, Type.usize, .{ - .lea_symbol = .{ .sym = sym.nlist_idx }, + .lea_symbol = .{ .sym_index = sym.nlist_idx }, }, .{}), .mov => try self.genSetReg(reg, Type.usize, .{ - .load_symbol = .{ .sym = sym.nlist_idx }, + .load_symbol = .{ .sym_index = sym.nlist_idx }, }, .{}), else => unreachable, } @@ -18786,7 +18835,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { .{ .frame = frame_index }, 0, Type.usize, - .{ .lea_symbol = .{ .sym = tlv_sym } }, + .{ .lea_symbol = .{ .sym_index = tlv_sym } }, .{}, ); break :init .{ .load_frame = .{ .index = frame_index } }; @@ -18842,8 +18891,8 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { .undef => .undef, .immediate => |imm| .{ .immediate = imm }, .memory => |addr| .{ .memory = addr }, - .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, - .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } }, + .load_symbol => |sym_index| .{ .load_symbol = .{ .sym_index = sym_index } }, + .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index } }, .load_direct => |sym_index| .{ .load_direct = sym_index }, .lea_direct => |sym_index| .{ .lea_direct = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index }, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index f168464f6f..0461ce245a 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,6 +1,8 @@ //! This file contains the functionality for emitting x86_64 MIR as machine code +air: Air, lower: Lower, +atom_index: u32, debug_output: DebugInfoOutput, code: *std.ArrayList(u8), @@ -36,108 +38,109 @@ pub fn emitMir(emit: *Emit) Error!void { }) switch (lowered_relocs[0].target) { .inst => |target| try emit.relocs.append(emit.lower.allocator, .{ .source = start_offset, + .source_offset = end_offset - 4, .target = target, - .offset = end_offset - 4, + .target_offset = lowered_relocs[0].off, .length = @intCast(end_offset - start_offset), }), - .linker_extern_fn => |symbol| if (emit.lower.bin_file.cast(.elf)) |elf_file| { + .linker_extern_fn => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { // Add relocation to the decl. const zo = elf_file.zigObjectPtr().?; - const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?; + const atom_ptr = zo.symbol(emit.atom_index).atom(elf_file).?; const r_type = @intFromEnum(std.elf.R_X86_64.PLT32); - try atom_ptr.addReloc(elf_file, .{ + try atom_ptr.addReloc(elf_file.base.comp.gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | r_type, - .r_addend = -4, - }); + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_addend = lowered_relocs[0].off - 4, + }, zo); } else if (emit.lower.bin_file.cast(.macho)) |macho_file| { // Add relocation to the decl. const zo = macho_file.getZigObject().?; - const atom = zo.symbols.items[symbol.atom_index].getAtom(macho_file).?; + const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; try atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = end_offset - 4, - .target = symbol.sym_index, - .addend = 0, + .target = sym_index, + .addend = lowered_relocs[0].off, .type = .branch, .meta = .{ .pcrel = true, .has_subtractor = false, .length = 2, - .symbolnum = @intCast(symbol.sym_index), + .symbolnum = @intCast(sym_index), }, }); } else if (emit.lower.bin_file.cast(.coff)) |coff_file| { // Add relocation to the decl. const atom_index = coff_file.getAtomIndexForSymbol( - .{ .sym_index = symbol.atom_index, .file = null }, + .{ .sym_index = emit.atom_index, .file = null }, ).?; - const target = if (link.File.Coff.global_symbol_bit & symbol.sym_index != 0) - coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & symbol.sym_index) + const target = if (link.File.Coff.global_symbol_bit & sym_index != 0) + coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index) else - link.File.Coff.SymbolWithLoc{ .sym_index = symbol.sym_index, .file = null }; + link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null }; try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ .type = .direct, .target = target, .offset = end_offset - 4, - .addend = 0, + .addend = @intCast(lowered_relocs[0].off), .pcrel = true, .length = 2, }); } else return emit.fail("TODO implement extern reloc for {s}", .{ @tagName(emit.lower.bin_file.tag), }), - .linker_tlsld => |data| { + .linker_tlsld => |sym_index| { const elf_file = emit.lower.bin_file.cast(.elf).?; const zo = elf_file.zigObjectPtr().?; - const atom = zo.symbol(data.atom_index).atom(elf_file).?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD); - try atom.addReloc(elf_file, .{ + try atom.addReloc(elf_file.base.comp.gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = -4, - }); + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_addend = lowered_relocs[0].off - 4, + }, zo); }, - .linker_dtpoff => |data| { + .linker_dtpoff => |sym_index| { const elf_file = emit.lower.bin_file.cast(.elf).?; const zo = elf_file.zigObjectPtr().?; - const atom = zo.symbol(data.atom_index).atom(elf_file).?; + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32); - try atom.addReloc(elf_file, .{ + try atom.addReloc(elf_file.base.comp.gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = 0, - }); + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_addend = lowered_relocs[0].off, + }, zo); }, - .linker_reloc => |data| if (emit.lower.bin_file.cast(.elf)) |elf_file| { + .linker_reloc => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; - const atom = zo.symbol(data.atom_index).atom(elf_file).?; - const sym = zo.symbol(data.sym_index); + const atom = zo.symbol(emit.atom_index).atom(elf_file).?; + const sym = zo.symbol(sym_index); if (emit.lower.pic) { const r_type: u32 = if (sym.flags.is_extern_ptr) @intFromEnum(std.elf.R_X86_64.GOTPCREL) else @intFromEnum(std.elf.R_X86_64.PC32); - try atom.addReloc(elf_file, .{ + try atom.addReloc(elf_file.base.comp.gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = -4, - }); + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_addend = lowered_relocs[0].off - 4, + }, zo); } else { const r_type: u32 = if (sym.flags.is_tls) @intFromEnum(std.elf.R_X86_64.TPOFF32) else @intFromEnum(std.elf.R_X86_64.@"32"); - try atom.addReloc(elf_file, .{ + try atom.addReloc(elf_file.base.comp.gpa, .{ .r_offset = end_offset - 4, - .r_info = (@as(u64, @intCast(data.sym_index)) << 32) | r_type, - .r_addend = 0, - }); + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + .r_addend = lowered_relocs[0].off, + }, zo); } } else if (emit.lower.bin_file.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; - const atom = zo.symbols.items[data.atom_index].getAtom(macho_file).?; - const sym = &zo.symbols.items[data.sym_index]; + const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; + const sym = &zo.symbols.items[sym_index]; const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr) .got_load else if (sym.flags.tlv) @@ -147,33 +150,33 @@ pub fn emitMir(emit: *Emit) Error!void { try atom.addReloc(macho_file, .{ .tag = .@"extern", .offset = @intCast(end_offset - 4), - .target = data.sym_index, - .addend = 0, + .target = sym_index, + .addend = lowered_relocs[0].off, .type = @"type", .meta = .{ .pcrel = true, .has_subtractor = false, .length = 2, - .symbolnum = @intCast(data.sym_index), + .symbolnum = @intCast(sym_index), }, }); } else unreachable, .linker_got, .linker_direct, .linker_import, - => |symbol| if (emit.lower.bin_file.cast(.elf)) |_| { + => |sym_index| if (emit.lower.bin_file.cast(.elf)) |_| { unreachable; } else if (emit.lower.bin_file.cast(.macho)) |_| { unreachable; } else if (emit.lower.bin_file.cast(.coff)) |coff_file| { const atom_index = coff_file.getAtomIndexForSymbol(.{ - .sym_index = symbol.atom_index, + .sym_index = emit.atom_index, .file = null, }).?; - const target = if (link.File.Coff.global_symbol_bit & symbol.sym_index != 0) - coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & symbol.sym_index) + const target = if (link.File.Coff.global_symbol_bit & sym_index != 0) + coff_file.getGlobalByIndex(link.File.Coff.global_symbol_mask & sym_index) else - link.File.Coff.SymbolWithLoc{ .sym_index = symbol.sym_index, .file = null }; + link.File.Coff.SymbolWithLoc{ .sym_index = sym_index, .file = null }; try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ .type = switch (lowered_relocs[0].target) { .linker_got => .got, @@ -183,16 +186,15 @@ pub fn emitMir(emit: *Emit) Error!void { }, .target = target, .offset = @intCast(end_offset - 4), - .addend = 0, + .addend = @intCast(lowered_relocs[0].off), .pcrel = true, .length = 2, }); } else if (emit.lower.bin_file.cast(.plan9)) |p9_file| { - const atom_index = symbol.atom_index; - try p9_file.addReloc(atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct - .target = symbol.sym_index, // we set sym_index to just be the atom index + try p9_file.addReloc(emit.atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct + .target = sym_index, // we set sym_index to just be the atom index .offset = @intCast(end_offset - 4), - .addend = 0, + .addend = @intCast(lowered_relocs[0].off), .type = .pcrel, }); } else return emit.fail("TODO implement linker reloc for {s}", .{ @@ -232,18 +234,163 @@ pub fn emitMir(emit: *Emit) Error!void { .none => {}, } }, - .pseudo_dbg_inline_func => { + .pseudo_dbg_enter_inline_func => { switch (emit.debug_output) { .dwarf => |dw| { - log.debug("mirDbgInline (line={d}, col={d})", .{ + log.debug("mirDbgEnterInline (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column, }); - try dw.setInlineFunc(mir_inst.data.func); + try dw.enterInlineFunc(mir_inst.data.func, emit.code.items.len, emit.prev_di_line, emit.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } + }, + .pseudo_dbg_leave_inline_func => { + switch (emit.debug_output) { + .dwarf => |dw| { + log.debug("mirDbgLeaveInline (line={d}, col={d})", .{ + emit.prev_di_line, emit.prev_di_column, + }); + try dw.leaveInlineFunc(mir_inst.data.func, emit.code.items.len); + }, + .plan9 => {}, + .none => {}, + } + }, + .pseudo_dbg_local_a, + .pseudo_dbg_local_ai_s, + .pseudo_dbg_local_ai_u, + .pseudo_dbg_local_ai_64, + .pseudo_dbg_local_as, + .pseudo_dbg_local_aso, + .pseudo_dbg_local_aro, + .pseudo_dbg_local_af, + .pseudo_dbg_local_am, + => { + switch (emit.debug_output) { + .dwarf => |dw| { + var loc_buf: [2]link.File.Dwarf.Loc = undefined; + const air_inst_index, const loc: link.File.Dwarf.Loc = switch (mir_inst.ops) { + else => unreachable, + .pseudo_dbg_local_a => .{ mir_inst.data.a.air_inst, .empty }, + .pseudo_dbg_local_ai_s, + .pseudo_dbg_local_ai_u, + .pseudo_dbg_local_ai_64, + => .{ mir_inst.data.ai.air_inst, .{ .stack_value = stack_value: { + loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.ai.i)) { + .signed => |s| .{ .consts = s }, + .unsigned => |u| .{ .constu = u }, + }; + break :stack_value &loc_buf[0]; + } } }, + .pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ .addr = .{ + .sym = mir_inst.data.as.sym_index, + } } }, + .pseudo_dbg_local_aso => loc: { + const sym_off = emit.lower.mir.extraData( + bits.SymbolOffset, + mir_inst.data.ax.payload, + ).data; + break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ + sym: { + loc_buf[0] = .{ .addr = .{ .sym = sym_off.sym_index } }; + break :sym &loc_buf[0]; + }, + off: { + loc_buf[1] = .{ .consts = sym_off.off }; + break :off &loc_buf[1]; + }, + } } }; + }, + .pseudo_dbg_local_aro => loc: { + const air_off = emit.lower.mir.extraData( + Mir.AirOffset, + mir_inst.data.rx.payload, + ).data; + break :loc .{ air_off.air_inst, .{ .plus = .{ + reg: { + loc_buf[0] = .{ .breg = mir_inst.data.rx.r1.dwarfNum() }; + break :reg &loc_buf[0]; + }, + off: { + loc_buf[1] = .{ .consts = air_off.off }; + break :off &loc_buf[1]; + }, + } } }; + }, + .pseudo_dbg_local_af => loc: { + const reg_off = emit.lower.mir.resolveFrameAddr(emit.lower.mir.extraData( + bits.FrameAddr, + mir_inst.data.ax.payload, + ).data); + break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ + reg: { + loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() }; + break :reg &loc_buf[0]; + }, + off: { + loc_buf[1] = .{ .consts = reg_off.off }; + break :off &loc_buf[1]; + }, + } } }; + }, + .pseudo_dbg_local_am => loc: { + const mem = emit.lower.mem(mir_inst.data.ax.payload); + break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ + base: { + loc_buf[0] = switch (mem.base()) { + .none => .{ .constu = 0 }, + .reg => |reg| .{ .breg = reg.dwarfNum() }, + .frame => unreachable, + .reloc => |sym_index| .{ .addr = .{ .sym = sym_index } }, + }; + break :base &loc_buf[0]; + }, + disp: { + loc_buf[1] = switch (mem.disp()) { + .signed => |s| .{ .consts = s }, + .unsigned => |u| .{ .constu = u }, + }; + break :disp &loc_buf[1]; + }, + } } }; + }, + }; + const ip = &emit.lower.bin_file.comp.module.?.intern_pool; + const air_inst = emit.air.instructions.get(@intFromEnum(air_inst_index)); + const name: Air.NullTerminatedString = switch (air_inst.tag) { + else => unreachable, + .arg => air_inst.data.arg.name, + .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => @enumFromInt(air_inst.data.pl_op.payload), + }; + try dw.genLocalDebugInfo( + switch (air_inst.tag) { + else => unreachable, + .arg, .dbg_arg_inline => .local_arg, + .dbg_var_ptr, .dbg_var_val => .local_var, + }, + name.toSlice(emit.air), + switch (air_inst.tag) { + else => unreachable, + .arg => emit.air.typeOfIndex(air_inst_index, ip), + .dbg_var_ptr => emit.air.typeOf(air_inst.data.pl_op.operand, ip).childTypeIp(ip), + .dbg_var_val, .dbg_arg_inline => emit.air.typeOf(air_inst.data.pl_op.operand, ip), + }, + loc, + ); }, .plan9 => {}, .none => {}, } }, + .pseudo_dbg_var_args_none => { + switch (emit.debug_output) { + .dwarf => |dw| try dw.genVarArgsDebugInfo(), + .plan9 => {}, + .none => {}, + } + }, .pseudo_dead_none => {}, }, } @@ -268,10 +415,12 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { const Reloc = struct { /// Offset of the instruction. source: usize, + /// Offset of the relocation within the instruction. + source_offset: u32, /// Target of the relocation. target: Mir.Inst.Index, - /// Offset of the relocation within the instruction. - offset: u32, + /// Offset from the target instruction. + target_offset: i32, /// Length of the instruction. length: u5, }; @@ -284,8 +433,8 @@ fn fixupRelocs(emit: *Emit) Error!void { for (emit.relocs.items) |reloc| { const target = emit.code_offset_mapping.get(reloc.target) orelse return emit.fail("JMP/CALL relocation target not found!", .{}); - const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)); - mem.writeInt(i32, emit.code.items[reloc.offset..][0..4], @intCast(disp), .little); + const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length)) + reloc.target_offset; + std.mem.writeInt(i32, emit.code.items[reloc.source_offset..][0..4], @intCast(disp), .little); } } @@ -338,11 +487,12 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void { } } +const bits = @import("bits.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.emit); -const mem = std.mem; const std = @import("std"); +const Air = @import("../../Air.zig"); const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; const Emit = @This(); const Lower = @import("Lower.zig"); diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 2275ed5f9b..69cd548cc3 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -4,10 +4,10 @@ bin_file: *link.File, output_mode: std.builtin.OutputMode, link_mode: std.builtin.LinkMode, pic: bool, -allocator: Allocator, +allocator: std.mem.Allocator, mir: Mir, cc: std.builtin.CallingConvention, -err_msg: ?*ErrorMsg = null, +err_msg: ?*Zcu.ErrorMsg = null, src_loc: Zcu.LazySrcLoc, result_insts_len: u8 = undefined, result_relocs_len: u8 = undefined, @@ -52,16 +52,17 @@ pub const Error = error{ pub const Reloc = struct { lowered_inst_index: u8, target: Target, + off: i32, const Target = union(enum) { inst: Mir.Inst.Index, - linker_reloc: bits.Symbol, - linker_tlsld: bits.Symbol, - linker_dtpoff: bits.Symbol, - linker_extern_fn: bits.Symbol, - linker_got: bits.Symbol, - linker_direct: bits.Symbol, - linker_import: bits.Symbol, + linker_reloc: u32, + linker_tlsld: u32, + linker_dtpoff: u32, + linker_extern_fn: u32, + linker_got: u32, + linker_direct: u32, + linker_import: u32, }; }; @@ -173,19 +174,19 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_j_z_and_np_inst => { assert(inst.data.inst.fixes == ._); try lower.emit(.none, .jnz, &.{ - .{ .imm = lower.reloc(.{ .inst = index + 1 }) }, + .{ .imm = lower.reloc(.{ .inst = index + 1 }, 0) }, }); try lower.emit(.none, .jnp, &.{ - .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) }, + .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) }, }); }, .pseudo_j_nz_or_p_inst => { assert(inst.data.inst.fixes == ._); try lower.emit(.none, .jnz, &.{ - .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) }, + .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) }, }); try lower.emit(.none, .jp, &.{ - .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) }, + .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) }, }); }, @@ -195,7 +196,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .{ .imm = Immediate.s(@bitCast(inst.data.ri.i)) }, }); try lower.emit(.none, .jz, &.{ - .{ .imm = lower.reloc(.{ .inst = index + 1 }) }, + .{ .imm = lower.reloc(.{ .inst = index + 1 }, 0) }, }); try lower.emit(.none, .lea, &.{ .{ .reg = inst.data.ri.r1 }, @@ -211,7 +212,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .{ .reg = inst.data.ri.r1.to32() }, }); try lower.emit(.none, .jmp, &.{ - .{ .imm = lower.reloc(.{ .inst = index }) }, + .{ .imm = lower.reloc(.{ .inst = index }, 0) }, }); assert(lower.result_insts_len == pseudo_probe_align_insts); }, @@ -257,7 +258,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .{ .imm = Immediate.s(page_size) }, }); try lower.emit(.none, .jae, &.{ - .{ .imm = lower.reloc(.{ .inst = index }) }, + .{ .imm = lower.reloc(.{ .inst = index }, 0) }, }); assert(lower.result_insts_len == pseudo_probe_adjust_loop_insts); }, @@ -267,7 +268,18 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { .pseudo_dbg_prologue_end_none, .pseudo_dbg_line_line_column, .pseudo_dbg_epilogue_begin_none, - .pseudo_dbg_inline_func, + .pseudo_dbg_enter_inline_func, + .pseudo_dbg_leave_inline_func, + .pseudo_dbg_local_a, + .pseudo_dbg_local_ai_s, + .pseudo_dbg_local_ai_u, + .pseudo_dbg_local_ai_64, + .pseudo_dbg_local_as, + .pseudo_dbg_local_aso, + .pseudo_dbg_local_aro, + .pseudo_dbg_local_af, + .pseudo_dbg_local_am, + .pseudo_dbg_var_args_none, .pseudo_dead_none, => {}, else => unreachable, @@ -283,17 +295,18 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct { pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error { @setCold(true); assert(lower.err_msg == null); - lower.err_msg = try ErrorMsg.create(lower.allocator, lower.src_loc, format, args); + lower.err_msg = try Zcu.ErrorMsg.create(lower.allocator, lower.src_loc, format, args); return error.LowerFail; } -fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate { +pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate { return switch (ops) { .rri_s, .ri_s, .i_s, .mi_s, .rmi_s, + .pseudo_dbg_local_ai_s, => Immediate.s(@bitCast(i)), .rrri, @@ -306,22 +319,26 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate { .mri, .rrm, .rrmi, + .pseudo_dbg_local_ai_u, => Immediate.u(i), - .ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()), + .ri_64, + .pseudo_dbg_local_ai_64, + => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()), else => unreachable, }; } -fn mem(lower: Lower, payload: u32) Memory { +pub fn mem(lower: Lower, payload: u32) Memory { return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode(); } -fn reloc(lower: *Lower, target: Reloc.Target) Immediate { +fn reloc(lower: *Lower, target: Reloc.Target, off: i32) Immediate { lower.result_relocs[lower.result_relocs_len] = .{ .lowered_inst_index = lower.result_insts_len, .target = target, + .off = off, }; lower.result_relocs_len += 1; return Immediate.s(0); @@ -337,37 +354,36 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) else => op, .mem => |mem_op| switch (mem_op.base()) { else => op, - .reloc => |sym| op: { + .reloc => |sym_index| op: { assert(prefix == .none); assert(mem_op.sib.disp == 0); assert(mem_op.sib.scale_index.scale == 0); if (lower.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; - const elf_sym = zo.symbol(sym.sym_index); + const elf_sym = zo.symbol(sym_index); if (elf_sym.flags.is_tls) { // TODO handle extern TLS vars, i.e., emit GD model if (lower.pic) { // Here, we currently assume local dynamic TLS vars, and so // we emit LD model. - _ = lower.reloc(.{ .linker_tlsld = sym }); + _ = lower.reloc(.{ .linker_tlsld = sym_index }, 0); lower.result_insts[lower.result_insts_len] = try Instruction.new(.none, .lea, &[_]Operand{ .{ .reg = .rdi }, .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }, }); lower.result_insts_len += 1; - _ = lower.reloc(.{ .linker_extern_fn = .{ - .atom_index = sym.atom_index, - .sym_index = try elf_file.getGlobalSymbol("__tls_get_addr", null), - } }); + _ = lower.reloc(.{ + .linker_extern_fn = try elf_file.getGlobalSymbol("__tls_get_addr", null), + }, 0); lower.result_insts[lower.result_insts_len] = try Instruction.new(.none, .call, &[_]Operand{ .{ .imm = Immediate.s(0) }, }); lower.result_insts_len += 1; - _ = lower.reloc(.{ .linker_dtpoff = sym }); + _ = lower.reloc(.{ .linker_dtpoff = sym_index }, 0); emit_mnemonic = .lea; break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ .reg = .rax }, @@ -381,7 +397,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) .{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .fs } }) }, }); lower.result_insts_len += 1; - _ = lower.reloc(.{ .linker_reloc = sym }); + _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); emit_mnemonic = .lea; break :op .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{ .base = .{ .reg = .rax }, @@ -390,7 +406,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) } } - _ = lower.reloc(.{ .linker_reloc = sym }); + _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); if (lower.pic) switch (mnemonic) { .lea => { if (elf_sym.flags.is_extern_ptr) emit_mnemonic = .mov; @@ -427,10 +443,10 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) } } else if (lower.bin_file.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; - const macho_sym = zo.symbols.items[sym.sym_index]; + const macho_sym = zo.symbols.items[sym_index]; if (macho_sym.flags.tlv) { - _ = lower.reloc(.{ .linker_reloc = sym }); + _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); lower.result_insts[lower.result_insts_len] = try Instruction.new(.none, .mov, &[_]Operand{ .{ .reg = .rdi }, @@ -446,7 +462,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) break :op .{ .reg = .rax }; } - _ = lower.reloc(.{ .linker_reloc = sym }); + _ = lower.reloc(.{ .linker_reloc = sym_index }, 0); break :op switch (mnemonic) { .lea => { if (macho_sym.flags.is_extern_ptr) emit_mnemonic = .mov; @@ -490,8 +506,8 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .rrrr => inst.data.rrrr.fixes, .rrri => inst.data.rrri.fixes, .rri_s, .rri_u => inst.data.rri.fixes, - .ri_s, .ri_u => inst.data.ri.fixes, - .ri64, .rm, .rmi_s, .mr => inst.data.rx.fixes, + .ri_s, .ri_u, .ri_64 => inst.data.ri.fixes, + .rm, .rmi_s, .mr => inst.data.rx.fixes, .mrr, .rrm, .rmr => inst.data.rrx.fixes, .rmi, .mri => inst.data.rix.fixes, .rrmr => inst.data.rrrx.fixes, @@ -525,7 +541,7 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { }, switch (inst.ops) { .none => &.{}, .inst => &.{ - .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }) }, + .{ .imm = lower.reloc(.{ .inst = inst.data.inst.inst }, 0) }, }, .i_s, .i_u => &.{ .{ .imm = lower.imm(inst.ops, inst.data.i.i) }, @@ -554,14 +570,10 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .{ .reg = inst.data.rrri.r3 }, .{ .imm = lower.imm(inst.ops, inst.data.rrri.i) }, }, - .ri_s, .ri_u => &.{ + .ri_s, .ri_u, .ri_64 => &.{ .{ .reg = inst.data.ri.r1 }, .{ .imm = lower.imm(inst.ops, inst.data.ri.i) }, }, - .ri64 => &.{ - .{ .reg = inst.data.rx.r1 }, - .{ .imm = lower.imm(inst.ops, inst.data.rx.payload) }, - }, .rri_s, .rri_u => &.{ .{ .reg = inst.data.rri.r1 }, .{ .reg = inst.data.rri.r2 }, @@ -631,17 +643,17 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void { .{ .imm = lower.imm(inst.ops, inst.data.rrix.i) }, }, .extern_fn_reloc, .rel => &.{ - .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) }, + .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc.sym_index }, inst.data.reloc.off) }, }, .got_reloc, .direct_reloc, .import_reloc => ops: { const reg = inst.data.rx.r1; - const extra = lower.mir.extraData(bits.Symbol, inst.data.rx.payload).data; + const extra = lower.mir.extraData(bits.SymbolOffset, inst.data.rx.payload).data; _ = lower.reloc(switch (inst.ops) { - .got_reloc => .{ .linker_got = extra }, - .direct_reloc => .{ .linker_direct = extra }, - .import_reloc => .{ .linker_import = extra }, + .got_reloc => .{ .linker_got = extra.sym_index }, + .direct_reloc => .{ .linker_direct = extra.sym_index }, + .import_reloc => .{ .linker_import = extra.sym_index }, else => unreachable, - }); + }, extra.off); break :ops &.{ .{ .reg = reg }, .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, @@ -670,9 +682,6 @@ const encoder = @import("encoder.zig"); const link = @import("../../link.zig"); const std = @import("std"); -const Air = @import("../../Air.zig"); -const Allocator = std.mem.Allocator; -const ErrorMsg = Zcu.ErrorMsg; const Immediate = Instruction.Immediate; const Instruction = encoder.Instruction; const Lower = @This(); diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 75fe8cffe2..2ccb609839 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -760,8 +760,8 @@ pub const Inst = struct { /// Uses `ri` payload. ri_u, /// Register, 64-bit unsigned immediate operands. - /// Uses `rx` payload with payload type `Imm64`. - ri64, + /// Uses `ri` payload with `i` index of extra data of type `Imm64`. + ri_64, /// Immediate (sign-extended) operand. /// Uses `imm` payload. i_s, @@ -796,7 +796,7 @@ pub const Inst = struct { /// Uses `rrix` payload with extra data of type `Memory`. rrmi, /// Single memory operand. - /// Uses `x` with extra data of type `Memory`. + /// Uses `x` payload with extra data of type `Memory`. m, /// Memory, immediate (sign-extend) operands. /// Uses `x` payload with extra data of type `Imm32` followed by `Memory`. @@ -820,16 +820,16 @@ pub const Inst = struct { /// Uses `reloc` payload. extern_fn_reloc, /// Linker relocation - GOT indirection. - /// Uses `rx` payload with extra data of type `bits.Symbol`. + /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. got_reloc, /// Linker relocation - direct reference. - /// Uses `rx` payload with extra data of type `bits.Symbol`. + /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. direct_reloc, /// Linker relocation - imports table indirection (binding). - /// Uses `rx` payload with extra data of type `bits.Symbol`. + /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. import_reloc, /// Linker relocation - threadlocal variable via GOT indirection. - /// Uses `rx` payload with extra data of type `bits.Symbol`. + /// Uses `rx` payload with extra data of type `bits.SymbolOffset`. tlv_reloc, // Pseudo instructions: @@ -868,16 +868,16 @@ pub const Inst = struct { pseudo_j_nz_or_p_inst, /// Probe alignment - /// Uses `ri` payload + /// Uses `ri` payload. pseudo_probe_align_ri_s, /// Probe adjust unrolled - /// Uses `ri` payload + /// Uses `ri` payload. pseudo_probe_adjust_unrolled_ri_s, /// Probe adjust setup - /// Uses `rri` payload + /// Uses `rri` payload. pseudo_probe_adjust_setup_rri_s, /// Probe adjust loop - /// Uses `rr` payload + /// Uses `rr` payload. pseudo_probe_adjust_loop_rr, /// Push registers /// Uses `reg_list` payload. @@ -893,8 +893,39 @@ pub const Inst = struct { pseudo_dbg_line_line_column, /// Start of epilogue pseudo_dbg_epilogue_begin_none, - /// Start or end of inline function - pseudo_dbg_inline_func, + /// Start of inline function + pseudo_dbg_enter_inline_func, + /// End of inline function + pseudo_dbg_leave_inline_func, + /// Local argument or variable. + /// Uses `a` payload. + pseudo_dbg_local_a, + /// Local argument or variable. + /// Uses `ai` payload. + pseudo_dbg_local_ai_s, + /// Local argument or variable. + /// Uses `ai` payload. + pseudo_dbg_local_ai_u, + /// Local argument or variable. + /// Uses `ai` payload with extra data of type `Imm64`. + pseudo_dbg_local_ai_64, + /// Local argument or variable. + /// Uses `as` payload. + pseudo_dbg_local_as, + /// Local argument or variable. + /// Uses `ax` payload with extra data of type `bits.SymbolOffset`. + pseudo_dbg_local_aso, + /// Local argument or variable. + /// Uses `rx` payload with extra data of type `AirOffset`. + pseudo_dbg_local_aro, + /// Local argument or variable. + /// Uses `ax` payload with extra data of type `bits.FrameAddr`. + pseudo_dbg_local_af, + /// Local argument or variable. + /// Uses `ax` payload with extra data of type `Memory`. + pseudo_dbg_local_am, + /// Remaining arguments are varargs. + pseudo_dbg_var_args_none, /// Tombstone /// Emitter should skip this instruction. @@ -997,10 +1028,28 @@ pub const Inst = struct { fixes: Fixes = ._, payload: u32, }, + ix: struct { + payload: u32, + }, + a: struct { + air_inst: Air.Inst.Index, + }, + ai: struct { + air_inst: Air.Inst.Index, + i: u32, + }, + as: struct { + air_inst: Air.Inst.Index, + sym_index: u32, + }, + ax: struct { + air_inst: Air.Inst.Index, + payload: u32, + }, /// Relocation for the linker where: - /// * `atom_index` is the index of the source /// * `sym_index` is the index of the target - reloc: bits.Symbol, + /// * `off` is the offset from the target + reloc: bits.SymbolOffset, /// Debug line and column position line_column: struct { line: u32, @@ -1020,6 +1069,8 @@ pub const Inst = struct { } }; +pub const AirOffset = struct { air_inst: Air.Inst.Index, off: i32 }; + /// Used in conjunction with payload to transfer a list of used registers in a compact manner. pub const RegisterList = struct { bitset: BitSet = BitSet.initEmpty(), @@ -1118,15 +1169,13 @@ pub const Memory = struct { .none => undefined, .reg => |reg| @intFromEnum(reg), .frame => |frame_index| @intFromEnum(frame_index), - .reloc => |symbol| symbol.sym_index, + .reloc => |sym_index| sym_index, }, .off = switch (mem.mod) { .rm => |rm| @bitCast(rm.disp), .off => |off| @truncate(off), }, - .extra = if (mem.base == .reloc) - mem.base.reloc.atom_index - else if (mem.mod == .off) + .extra = if (mem.mod == .off) @intCast(mem.mod.off >> 32) else undefined, @@ -1146,7 +1195,7 @@ pub const Memory = struct { .none => .none, .reg => .{ .reg = @enumFromInt(mem.base) }, .frame => .{ .frame = @enumFromInt(mem.base) }, - .reloc => .{ .reloc = .{ .atom_index = mem.extra, .sym_index = mem.base } }, + .reloc => .{ .reloc = mem.base }, }, .scale_index = switch (mem.info.index) { .none => null, @@ -1186,6 +1235,7 @@ pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end: @field(result, field.name) = switch (field.type) { u32 => mir.extra[i], i32, Memory.Info => @bitCast(mir.extra[i]), + bits.FrameIndex, Air.Inst.Index => @enumFromInt(mir.extra[i]), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }; i += 1; @@ -1201,6 +1251,11 @@ pub const FrameLoc = struct { disp: i32, }; +pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffset { + const frame_loc = mir.frame_locs.get(@intFromEnum(frame_addr.index)); + return .{ .reg = frame_loc.base, .off = frame_loc.disp + frame_addr.off }; +} + pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory { return switch (mem.info.base) { .none, .reg, .reloc => mem, @@ -1225,6 +1280,7 @@ const builtin = @import("builtin"); const encoder = @import("encoder.zig"); const std = @import("std"); +const Air = @import("../../Air.zig"); const IntegerBitSet = std.bit_set.IntegerBitSet; const InternPool = @import("../../InternPool.zig"); const Mir = @This(); diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 56b5fcc5d4..839084456a 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -447,26 +447,11 @@ pub const FrameIndex = enum(u32) { } }; -/// A linker symbol not yet allocated in VM. -pub const Symbol = struct { - /// Index of the containing atom. - atom_index: u32, - /// Index into the linker's symbol table. - sym_index: u32, +pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; - pub fn format( - sym: Symbol, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) @TypeOf(writer).Error!void { - try writer.writeAll("Symbol("); - try std.fmt.formatType(sym.atom_index, fmt, options, writer, 0); - try writer.writeAll(", "); - try std.fmt.formatType(sym.sym_index, fmt, options, writer, 0); - try writer.writeByte(')'); - } -}; +pub const RegisterOffset = struct { reg: Register, off: i32 = 0 }; + +pub const SymbolOffset = struct { sym_index: u32, off: i32 = 0 }; pub const Memory = struct { base: Base, @@ -476,7 +461,7 @@ pub const Memory = struct { none, reg: Register, frame: FrameIndex, - reloc: Symbol, + reloc: u32, pub const Tag = @typeInfo(Base).Union.tag_type.?; @@ -568,7 +553,7 @@ pub const Memory = struct { pub const Immediate = union(enum) { signed: i32, unsigned: u64, - reloc: Symbol, + reloc: SymbolOffset, pub fn u(x: u64) Immediate { return .{ .unsigned = x }; @@ -578,19 +563,19 @@ pub const Immediate = union(enum) { return .{ .signed = x }; } - pub fn rel(symbol: Symbol) Immediate { - return .{ .reloc = symbol }; + pub fn rel(sym_off: SymbolOffset) Immediate { + return .{ .reloc = sym_off }; } pub fn format( imm: Immediate, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, + comptime _: []const u8, + _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { switch (imm) { - .reloc => |x| try std.fmt.formatType(x, fmt, options, writer, 0), - inline else => |x| try writer.print("{d}", .{x}), + inline else => |int| try writer.print("{d}", .{int}), + .reloc => |sym_off| try writer.print("Symbol({[sym_index]d}) + {[off]d}", sym_off), } } }; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index b525b0e11e..ce5b9c4d0f 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -128,8 +128,8 @@ pub const Instruction = struct { } }; } - pub fn rip(ptr_size: PtrSize, disp: i32) Memory { - return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } }; + pub fn rip(ptr_size: PtrSize, displacement: i32) Memory { + return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } }; } pub fn isSegmentRegister(mem: Memory) bool { @@ -158,6 +158,14 @@ pub const Instruction = struct { }; } + pub fn disp(mem: Memory) Immediate { + return switch (mem) { + .sib => |s| Immediate.s(s.disp), + .rip => |r| Immediate.s(r.disp), + .moffs => |m| Immediate.u(m.offset), + }; + } + pub fn bitSize(mem: Memory) u64 { return switch (mem) { .rip => |r| r.ptr_size.bitSize(), @@ -258,17 +266,12 @@ pub const Instruction = struct { try writer.writeByte('['); - var any = false; + var any = true; switch (sib.base) { - .none => {}, - .reg => |reg| { - try writer.print("{s}", .{@tagName(reg)}); - any = true; - }, - inline .frame, .reloc => |payload| { - try writer.print("{}", .{payload}); - any = true; - }, + .none => any = false, + .reg => |reg| try writer.print("{s}", .{@tagName(reg)}), + .frame => |frame_index| try writer.print("{}", .{frame_index}), + .reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}), } if (mem.scaleIndex()) |si| { if (any) try writer.writeAll(" + "); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 7c35a178a0..397cb071b6 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3293,7 +3293,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .dbg_stmt => try airDbgStmt(f, inst), .dbg_inline_block => try airDbgInlineBlock(f, inst), - .dbg_var_ptr, .dbg_var_val => try airDbgVar(f, inst), + .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => try airDbgVar(f, inst), .call => try airCall(f, inst, .auto), .call_always_tail => .none, @@ -4590,14 +4590,15 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue { fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; + const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)]; const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const name = f.air.nullTerminatedString(pl_op.payload); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); const operand_is_undef = if (try f.air.value(pl_op.operand, pt)) |v| v.isUndefDeep(zcu) else false; if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); try reap(f, inst, &.{pl_op.operand}); const writer = f.object.writer(); - try writer.print("/* var:{s} */\n", .{name}); + try writer.print("/* {s}:{s} */\n", .{ @tagName(tag), name.toSlice(f.air) }); return .none; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 8fb4fa7ef9..233cf7e3eb 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1665,6 +1665,7 @@ pub const Object = struct { .ret_ptr = ret_ptr, .args = args.items, .arg_index = 0, + .arg_inline_index = 0, .func_inst_table = .{}, .blocks = .{}, .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, @@ -4769,7 +4770,8 @@ pub const FuncGen = struct { /// it omits 0-bit types. If the function uses sret as the first parameter, /// this slice does not include it. args: []const Builder.Value, - arg_index: usize, + arg_index: u32, + arg_inline_index: u32, err_ret_trace: Builder.Value = .none, @@ -5082,7 +5084,8 @@ pub const FuncGen = struct { .dbg_stmt => try self.airDbgStmt(inst), .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr => try self.airDbgVarPtr(inst), - .dbg_var_val => try self.airDbgVarVal(inst), + .dbg_var_val => try self.airDbgVarVal(inst, false), + .dbg_arg_inline => try self.airDbgVarVal(inst, true), .c_va_arg => try self.airCVaArg(inst), .c_va_copy => try self.airCVaCopy(inst), @@ -6677,6 +6680,7 @@ pub const FuncGen = struct { fn airDbgInlineBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); + self.arg_inline_index = 0; return self.lowerBlock(inst, extra.data.func, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); } @@ -6685,11 +6689,11 @@ pub const FuncGen = struct { const mod = o.pt.zcu; const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = try self.resolveInst(pl_op.operand); - const name = self.air.nullTerminatedString(pl_op.payload); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); const ptr_ty = self.typeOf(pl_op.operand); const debug_local_var = try o.builder.debugLocalVar( - try o.builder.metadataString(name), + try o.builder.metadataString(name.toSlice(self.air)), self.file, self.scope, self.prev_dbg_line, @@ -6712,15 +6716,25 @@ pub const FuncGen = struct { return .none; } - fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index, is_arg: bool) !Builder.Value { const o = self.ng.object; const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = try self.resolveInst(pl_op.operand); const operand_ty = self.typeOf(pl_op.operand); - const name = self.air.nullTerminatedString(pl_op.payload); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - const debug_local_var = try o.builder.debugLocalVar( - try o.builder.metadataString(name), + const debug_local_var = if (is_arg) try o.builder.debugParameter( + try o.builder.metadataString(name.toSlice(self.air)), + self.file, + self.scope, + self.prev_dbg_line, + try o.lowerDebugType(operand_ty), + arg_no: { + self.arg_inline_index += 1; + break :arg_no self.arg_inline_index; + }, + ) else try o.builder.debugLocalVar( + try o.builder.metadataString(name.toSlice(self.air)), self.file, self.scope, self.prev_dbg_line, @@ -8835,12 +8849,12 @@ pub const FuncGen = struct { const lbrace_col = func.lbrace_column + 1; const debug_parameter = try o.builder.debugParameter( - try o.builder.metadataString(self.air.nullTerminatedString(@intFromEnum(name))), + try o.builder.metadataString(name.toSlice(self.air)), self.file, self.scope, lbrace_line, try o.lowerDebugType(inst_ty), - @intCast(self.arg_index), + self.arg_index, ); const old_location = self.wip.debug_location; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index b13be401ab..a89dd8f10b 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -6366,8 +6366,8 @@ const NavGen = struct { fn airDbgVar(self: *NavGen, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const target_id = try self.resolve(pl_op.operand); - const name = self.air.nullTerminatedString(pl_op.payload); - try self.spv.debugName(target_id, name); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + try self.spv.debugName(target_id, name.toSlice(self.air)); } fn airAssembly(self: *NavGen, inst: Air.Inst.Index) !?IdRef { diff --git a/src/introspect.zig b/src/introspect.zig index 341b1cddeb..4193440461 100644 --- a/src/introspect.zig +++ b/src/introspect.zig @@ -90,8 +90,11 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { if (builtin.os.tag != .windows) { if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| { - return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); - } else if (std.zig.EnvVar.HOME.getPosix()) |home| { + if (cache_root.len > 0) { + return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); + } + } + if (std.zig.EnvVar.HOME.getPosix()) |home| { return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); } } diff --git a/src/link.zig b/src/link.zig index b3e0aaa4a8..894074cdda 100644 --- a/src/link.zig +++ b/src/link.zig @@ -11,6 +11,7 @@ const wasi_libc = @import("wasi_libc.zig"); const Air = @import("Air.zig"); const Allocator = std.mem.Allocator; const Cache = std.Build.Cache; +const Path = Cache.Path; const Compilation = @import("Compilation.zig"); const LibCInstallation = std.zig.LibCInstallation; const Liveness = @import("Liveness.zig"); @@ -56,7 +57,7 @@ pub const File = struct { /// The owner of this output File. comp: *Compilation, - emit: Compilation.Emit, + emit: Path, file: ?fs.File, /// When linking with LLD, this linker code will output an object file only at @@ -189,7 +190,7 @@ pub const File = struct { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: OpenOptions, ) !*File { switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { @@ -204,7 +205,7 @@ pub const File = struct { pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: OpenOptions, ) !*File { switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { @@ -243,8 +244,8 @@ pub const File = struct { emit.sub_path, std.crypto.random.int(u32), }); defer gpa.free(tmp_sub_path); - try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); - try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); + try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, .{}); + try emit.root_dir.handle.rename(tmp_sub_path, emit.sub_path); switch (builtin.os.tag) { .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| { log.warn("ptrace failure: {s}", .{@errorName(err)}); @@ -260,7 +261,7 @@ pub const File = struct { const use_lld = build_options.have_llvm and comp.config.use_lld; const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; - base.file = try emit.directory.handle.createFile(emit.sub_path, .{ + base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = false, .read = true, .mode = determineMode(use_lld, output_mode, link_mode), @@ -603,7 +604,7 @@ pub const File = struct { // Until then, we do `lld -r -o output.o input.o` even though the output is the same // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file // to the final location. See also the corresponding TODO in Coff linking. - const full_out_path = try emit.directory.join(gpa, &[_][]const u8{emit.sub_path}); + const full_out_path = try emit.root_dir.join(gpa, &[_][]const u8{emit.sub_path}); defer gpa.free(full_out_path); assert(comp.c_object_table.count() == 1); const the_key = comp.c_object_table.keys()[0]; @@ -751,7 +752,7 @@ pub const File = struct { const comp = base.comp; const gpa = comp.gpa; - const directory = base.emit.directory; // Just an alias to make it shorter to type. + const directory = base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); const full_out_path_z = try arena.dupeZ(u8, full_out_path); const opt_zcu = comp.module; @@ -1029,7 +1030,10 @@ pub const File = struct { llvm_object: LlvmObject.Ptr, prog_node: std.Progress.Node, ) !void { - return base.comp.emitLlvmObject(arena, base.emit, .{ + return base.comp.emitLlvmObject(arena, .{ + .root_dir = base.emit.root_dir, + .sub_path = std.fs.path.dirname(base.emit.sub_path) orelse "", + }, .{ .directory = null, .basename = base.zcu_object_sub_path.?, }, llvm_object, prog_node); diff --git a/src/link/C.zig b/src/link/C.zig index e7c8f6a7b0..585389aa3f 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -3,6 +3,7 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = std.mem.Allocator; const fs = std.fs; +const Path = std.Build.Cache.Path; const C = @This(); const build_options = @import("build_options"); @@ -104,7 +105,7 @@ pub fn addString(this: *C, s: []const u8) Allocator.Error!String { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*C { return createEmpty(arena, comp, emit, options); @@ -113,7 +114,7 @@ pub fn open( pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*C { const target = comp.root_mod.resolved_target.result; @@ -127,7 +128,7 @@ pub fn createEmpty( assert(!use_lld); assert(!use_llvm); - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ // Truncation is done on `flush`. .truncate = false, }); @@ -316,8 +317,18 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ! defer tracy.end(); const gpa = self.base.comp.gpa; - const zcu = pt.zcu; + const ip = &zcu.intern_pool; + + const nav = ip.getNav(nav_index); + const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) { + .func => return, + .@"extern" => .none, + .variable => |variable| variable.init, + else => nav.status.resolved.val, + }; + if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(pt)) return; + const gop = try self.navs.getOrPut(gpa, nav_index); errdefer _ = self.navs.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 7bdcc8d411..d6ebcc278e 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -219,7 +219,7 @@ pub const min_text_capacity = padToIdeal(minimum_text_block_size); pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Coff { const target = comp.root_mod.resolved_target.result; @@ -315,7 +315,7 @@ pub fn createEmpty( // If using LLD to link, this code should produce an object file so that it // can be passed to LLD. const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - self.base.file = try emit.directory.handle.createFile(sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(use_lld, output_mode, link_mode), @@ -416,7 +416,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Coff { // TODO: restore saved linker state, don't truncate the file, and @@ -1207,6 +1207,7 @@ pub fn updateNav( const nav_val = zcu.navValue(nav_index); const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { + .func => return, .variable => |variable| Value.fromInterned(variable.init), .@"extern" => |@"extern"| { if (ip.isFunctionType(@"extern".ty)) return; @@ -1220,7 +1221,7 @@ pub fn updateNav( else => nav_val, }; - if (nav_init.typeOf(zcu).isFnOrHasRuntimeBits(pt)) { + if (nav_init.typeOf(zcu).hasRuntimeBits(pt)) { const atom_index = try self.getOrCreateAtomForNav(nav_index); Atom.freeRelocations(self, atom_index); const atom = self.getAtom(atom_index); @@ -2714,6 +2715,7 @@ const math = std.math; const mem = std.mem; const Allocator = std.mem.Allocator; +const Path = std.Build.Cache.Path; const codegen = @import("../codegen.zig"); const link = @import("../link.zig"); diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index ae56183e77..7273aa39b6 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -27,7 +27,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no const comp = self.base.comp; const gpa = comp.gpa; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it @@ -248,7 +248,7 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); if (comp.implib_emit) |emit| { - const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); + const implib_out_path = try emit.root_dir.join(arena, &[_][]const u8{emit.sub_path}); try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 1b5739030b..26ea6207e8 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -21,8 +21,9 @@ pub const UpdateError = std.fs.File.OpenError || std.fs.File.SetEndPosError || std.fs.File.CopyRangeError || + std.fs.File.PReadError || std.fs.File.PWriteError || - error{ Overflow, Underflow, UnexpectedEndOfFile }; + error{ EndOfStream, Overflow, Underflow, UnexpectedEndOfFile }; pub const FlushError = UpdateError || @@ -54,7 +55,10 @@ const ModInfo = struct { const DebugAbbrev = struct { section: Section, const unit: Unit.Index = @enumFromInt(0); - const entry: Entry.Index = @enumFromInt(0); + + const header_bytes = 0; + + const trailer_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.null)); }; const DebugAranges = struct { @@ -118,8 +122,7 @@ const DebugLine = struct { 1 + uleb128Bytes(DW.LNCT.path) + uleb128Bytes(DW.FORM.line_strp) + uleb128Bytes(DW.LNCT.directory_index) + uleb128Bytes(@intFromEnum(dir_index_info.form)) + uleb128Bytes(DW.LNCT.LLVM_source) + uleb128Bytes(DW.FORM.line_strp) + uleb128Bytes(file_count) + (dwarf.sectionOffsetBytes() + dir_index_info.bytes + dwarf.sectionOffsetBytes()) * file_count; } - const trailer_bytes = 1 + uleb128Bytes(0) + - 1 + uleb128Bytes(1) + 1; + const trailer_bytes = 1 + uleb128Bytes(1) + 1; }; const DebugLocLists = struct { @@ -172,10 +175,15 @@ const StringSection = struct { errdefer _ = str_sec.map.pop(); const entry: Entry.Index = @enumFromInt(gop.index); if (!gop.found_existing) { - assert(try str_sec.section.addEntry(unit, dwarf) == entry); - errdefer _ = str_sec.section.getUnit(unit).entries.pop(); - const entry_ptr = str_sec.section.getUnit(unit).getEntry(entry); - assert(entry_ptr.off == str_sec.contents.items.len); + const unit_ptr = str_sec.section.getUnit(unit); + assert(try str_sec.section.getUnit(unit).addEntry(dwarf.gpa) == entry); + errdefer _ = unit_ptr.entries.pop(); + const entry_ptr = unit_ptr.getEntry(entry); + if (unit_ptr.last.unwrap()) |last_entry| + unit_ptr.getEntry(last_entry).next = entry.toOptional(); + entry_ptr.prev = unit_ptr.last; + unit_ptr.last = entry.toOptional(); + entry_ptr.off = @intCast(str_sec.contents.items.len); entry_ptr.len = @intCast(str.len + 1); try str_sec.contents.ensureUnusedCapacity(dwarf.gpa, str.len + 1); str_sec.contents.appendSliceAssumeCapacity(str); @@ -200,7 +208,7 @@ const StringSection = struct { }; /// A linker section containing a sequence of `Unit`s. -const Section = struct { +pub const Section = struct { dirty: bool, pad_to_ideal: bool, alignment: InternPool.Alignment, @@ -242,7 +250,7 @@ const Section = struct { fn addUnit(sec: *Section, header_len: u32, trailer_len: u32, dwarf: *Dwarf) UpdateError!Unit.Index { const unit: Unit.Index = @enumFromInt(sec.units.items.len); const unit_ptr = try sec.units.addOne(dwarf.gpa); - errdefer sec.popUnit(); + errdefer sec.popUnit(dwarf.gpa); unit_ptr.* = .{ .prev = sec.last, .next = .none, @@ -253,10 +261,8 @@ const Section = struct { .trailer_len = trailer_len, .len = header_len + trailer_len, .entries = .{}, - .cross_entry_relocs = .{}, .cross_unit_relocs = .{}, .cross_section_relocs = .{}, - .external_relocs = .{}, }; if (sec.last.unwrap()) |last_unit| { const last_unit_ptr = sec.getUnit(last_unit); @@ -278,23 +284,34 @@ const Section = struct { if (sec.last.unwrap().? == unit) sec.last = unit_ptr.prev; } - fn popUnit(sec: *Section) void { - const unit: Unit.Index = @enumFromInt(sec.units.items.len - 1); - sec.unlinkUnit(unit); - _ = sec.units.pop(); - } - - fn addEntry(sec: *Section, unit: Unit.Index, dwarf: *Dwarf) UpdateError!Entry.Index { - return sec.getUnit(unit).addEntry(sec, dwarf); + fn popUnit(sec: *Section, gpa: std.mem.Allocator) void { + const unit_index: Unit.Index = @enumFromInt(sec.units.items.len - 1); + sec.unlinkUnit(unit_index); + var unit = sec.units.pop(); + unit.deinit(gpa); } - fn getUnit(sec: *Section, unit: Unit.Index) *Unit { + pub fn getUnit(sec: *Section, unit: Unit.Index) *Unit { return &sec.units.items[@intFromEnum(unit)]; } fn replaceEntry(sec: *Section, unit: Unit.Index, entry: Entry.Index, dwarf: *Dwarf, contents: []const u8) UpdateError!void { const unit_ptr = sec.getUnit(unit); - try unit_ptr.getEntry(entry).replace(unit_ptr, sec, dwarf, contents); + const entry_ptr = unit_ptr.getEntry(entry); + if (contents.len > 0) { + if (entry_ptr.len == 0) { + assert(entry_ptr.prev == .none and entry_ptr.next == .none); + entry_ptr.off = if (unit_ptr.last.unwrap()) |last_entry| off: { + const last_entry_ptr = unit_ptr.getEntry(last_entry); + last_entry_ptr.next = entry.toOptional(); + break :off last_entry_ptr.off + sec.padToIdeal(last_entry_ptr.len); + } else 0; + entry_ptr.prev = unit_ptr.last; + unit_ptr.last = entry.toOptional(); + } + try entry_ptr.replace(unit_ptr, sec, dwarf, contents); + } + assert(entry_ptr.len == contents.len); } fn resize(sec: *Section, dwarf: *Dwarf, len: u64) UpdateError!void { @@ -358,10 +375,8 @@ const Unit = struct { /// data length in bytes len: u32, entries: std.ArrayListUnmanaged(Entry), - cross_entry_relocs: std.ArrayListUnmanaged(CrossEntryReloc), cross_unit_relocs: std.ArrayListUnmanaged(CrossUnitReloc), cross_section_relocs: std.ArrayListUnmanaged(CrossSectionReloc), - external_relocs: std.ArrayListUnmanaged(ExternalReloc), const Index = enum(u32) { main, @@ -371,7 +386,7 @@ const Unit = struct { none = std.math.maxInt(u32), _, - fn unwrap(uio: Optional) ?Index { + pub fn unwrap(uio: Optional) ?Index { return if (uio != .none) @enumFromInt(@intFromEnum(uio)) else null; } }; @@ -381,36 +396,36 @@ const Unit = struct { } }; + fn clear(unit: *Unit) void { + unit.cross_unit_relocs.clearRetainingCapacity(); + unit.cross_section_relocs.clearRetainingCapacity(); + } + fn deinit(unit: *Unit, gpa: std.mem.Allocator) void { + for (unit.entries.items) |*entry| entry.deinit(gpa); unit.entries.deinit(gpa); - unit.cross_entry_relocs.deinit(gpa); unit.cross_unit_relocs.deinit(gpa); unit.cross_section_relocs.deinit(gpa); - unit.external_relocs.deinit(gpa); unit.* = undefined; } - fn addEntry(unit: *Unit, sec: *Section, dwarf: *Dwarf) UpdateError!Entry.Index { + fn addEntry(unit: *Unit, gpa: std.mem.Allocator) std.mem.Allocator.Error!Entry.Index { const entry: Entry.Index = @enumFromInt(unit.entries.items.len); - const entry_ptr = try unit.entries.addOne(dwarf.gpa); + const entry_ptr = try unit.entries.addOne(gpa); entry_ptr.* = .{ - .prev = unit.last, + .prev = .none, .next = .none, .off = 0, .len = 0, + .cross_entry_relocs = .{}, + .cross_unit_relocs = .{}, + .cross_section_relocs = .{}, + .external_relocs = .{}, }; - if (unit.last.unwrap()) |last_entry| { - const last_entry_ptr = unit.getEntry(last_entry); - last_entry_ptr.next = entry.toOptional(); - entry_ptr.off = last_entry_ptr.off + sec.padToIdeal(last_entry_ptr.len); - } - if (unit.first == .none) - unit.first = entry.toOptional(); - unit.last = entry.toOptional(); return entry; } - fn getEntry(unit: *Unit, entry: Entry.Index) *Entry { + pub fn getEntry(unit: *Unit, entry: Entry.Index) *Entry { return &unit.entries.items[@intFromEnum(entry)]; } @@ -451,6 +466,14 @@ const Unit = struct { unit_ptr.len = len; } + fn trim(unit: *Unit) void { + const len = unit.getEntry(unit.first.unwrap() orelse return).off; + if (len == 0) return; + for (unit.entries.items) |*entry| entry.off -= len; + unit.off += len; + unit.len -= len; + } + fn move(unit: *Unit, sec: *Section, dwarf: *Dwarf, new_off: u32) UpdateError!void { if (unit.off == new_off) return; if (try dwarf.getFile().?.copyRangeAll( @@ -463,6 +486,7 @@ const Unit = struct { } fn resizeHeader(unit: *Unit, sec: *Section, dwarf: *Dwarf, len: u32) UpdateError!void { + unit.trim(); if (unit.header_len == len) return; const available_len = if (unit.prev.unwrap()) |prev_unit| prev_excess: { const prev_unit_ptr = sec.getUnit(prev_unit); @@ -495,63 +519,61 @@ const Unit = struct { const last_entry_ptr = unit.getEntry(last_entry); break :end last_entry_ptr.off + last_entry_ptr.len; } else 0; - const end = if (unit.next.unwrap()) |next_unit| - sec.getUnit(next_unit).off - else - sec.len; - const trailer_len: usize = @intCast(end - start); - assert(trailer_len >= unit.trailer_len); - var trailer = try std.ArrayList(u8).initCapacity(dwarf.gpa, trailer_len); + const end = if (unit.next.unwrap()) |next_unit| sec.getUnit(next_unit).off else sec.len; + const len: usize = @intCast(end - start); + assert(len >= unit.trailer_len); + if (sec == &dwarf.debug_line.section) { + var buf: [1 + uleb128Bytes(std.math.maxInt(u32)) + 1]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + const writer = fbs.writer(); + writer.writeByte(DW.LNS.extended_op) catch unreachable; + const extended_op_bytes = fbs.pos; + var op_len_bytes: u5 = 1; + while (true) switch (std.math.order(len - extended_op_bytes - op_len_bytes, @as(u32, 1) << 7 * op_len_bytes)) { + .lt => break uleb128(writer, len - extended_op_bytes - op_len_bytes) catch unreachable, + .eq => { + // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte + op_len_bytes += 1; + std.leb.writeUnsignedExtended(buf[fbs.pos..][0..op_len_bytes], len - extended_op_bytes - op_len_bytes); + fbs.pos += op_len_bytes; + break; + }, + .gt => op_len_bytes += 1, + }; + assert(fbs.pos == extended_op_bytes + op_len_bytes); + writer.writeByte(DW.LNE.padding) catch unreachable; + assert(fbs.pos >= unit.trailer_len and fbs.pos <= len); + return dwarf.getFile().?.pwriteAll(fbs.getWritten(), sec.off + start); + } + var trailer = try std.ArrayList(u8).initCapacity(dwarf.gpa, len); defer trailer.deinit(); - const fill_byte: u8 = if (sec == &dwarf.debug_aranges.section) fill: { + const fill_byte: u8 = if (sec == &dwarf.debug_abbrev.section) fill: { + assert(uleb128Bytes(@intFromEnum(AbbrevCode.null)) == 1); + trailer.appendAssumeCapacity(@intFromEnum(AbbrevCode.null)); + break :fill @intFromEnum(AbbrevCode.null); + } else if (sec == &dwarf.debug_aranges.section) fill: { trailer.appendNTimesAssumeCapacity(0, @intFromEnum(dwarf.address_size) * 2); break :fill 0; } else if (sec == &dwarf.debug_info.section) fill: { assert(uleb128Bytes(@intFromEnum(AbbrevCode.null)) == 1); trailer.appendNTimesAssumeCapacity(@intFromEnum(AbbrevCode.null), 2); break :fill @intFromEnum(AbbrevCode.null); - } else if (sec == &dwarf.debug_line.section) fill: { - unit.len -= unit.trailer_len; - const extra_len: u32 = @intCast((trailer_len - DebugLine.trailer_bytes) & 1); - unit.trailer_len = DebugLine.trailer_bytes + extra_len; - unit.len += unit.trailer_len; - - // prevent end sequence from emitting an invalid file index - trailer.appendAssumeCapacity(DW.LNS.set_file); - uleb128(trailer.fixedWriter(), 0) catch unreachable; - - trailer.appendAssumeCapacity(DW.LNS.extended_op); - std.leb.writeUnsignedExtended(trailer.addManyAsSliceAssumeCapacity(uleb128Bytes(1) + extra_len), 1); - trailer.appendAssumeCapacity(DW.LNE.end_sequence); - break :fill DW.LNS.extended_op; } else if (sec == &dwarf.debug_rnglists.section) fill: { trailer.appendAssumeCapacity(DW.RLE.end_of_list); break :fill DW.RLE.end_of_list; } else unreachable; assert(trailer.items.len == unit.trailer_len); - trailer.appendNTimesAssumeCapacity(fill_byte, trailer_len - trailer.items.len); - assert(trailer.items.len == trailer_len); + trailer.appendNTimesAssumeCapacity(fill_byte, len - trailer.items.len); + assert(trailer.items.len == len); try dwarf.getFile().?.pwriteAll(trailer.items, sec.off + start); } fn resolveRelocs(unit: *Unit, sec: *Section, dwarf: *Dwarf) RelocError!void { - for (unit.cross_entry_relocs.items) |reloc| { - try dwarf.resolveReloc( - sec.off + unit.off + (if (reloc.source_entry.unwrap()) |source_entry| - unit.header_len + unit.getEntry(source_entry).off - else - 0) + reloc.source_off, - unit.off + unit.header_len + unit.getEntry(reloc.target_entry).assertNonEmpty(unit, sec, dwarf).off + reloc.target_off, - dwarf.sectionOffsetBytes(), - ); - } + const unit_off = sec.off + unit.off; for (unit.cross_unit_relocs.items) |reloc| { const target_unit = sec.getUnit(reloc.target_unit); try dwarf.resolveReloc( - sec.off + unit.off + (if (reloc.source_entry.unwrap()) |source_entry| - unit.header_len + unit.getEntry(source_entry).off - else - 0) + reloc.source_off, + unit_off + reloc.source_off, target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry| target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sec, dwarf).off else @@ -565,10 +587,7 @@ const Unit = struct { }; const target_unit = target_sec.getUnit(reloc.target_unit); try dwarf.resolveReloc( - sec.off + unit.off + (if (reloc.source_entry.unwrap()) |source_entry| - unit.header_len + unit.getEntry(source_entry).off - else - 0) + reloc.source_off, + unit_off + reloc.source_off, target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry| target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sec, dwarf).off else @@ -576,57 +595,8 @@ const Unit = struct { dwarf.sectionOffsetBytes(), ); } - if (dwarf.bin_file.cast(.elf)) |elf_file| { - const zo = elf_file.zigObjectPtr().?; - for (unit.external_relocs.items) |reloc| { - const symbol = zo.symbol(reloc.target_sym); - try dwarf.resolveReloc( - sec.off + unit.off + unit.header_len + unit.getEntry(reloc.source_entry).off + reloc.source_off, - @bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(reloc.target_off)) - - if (symbol.flags.is_tls) elf_file.dtpAddress() else 0), - @intFromEnum(dwarf.address_size), - ); - } - } else if (dwarf.bin_file.cast(.macho)) |macho_file| { - const zo = macho_file.getZigObject().?; - for (unit.external_relocs.items) |reloc| { - const ref = zo.getSymbolRef(reloc.target_sym, macho_file); - try dwarf.resolveReloc( - sec.off + unit.off + unit.header_len + unit.getEntry(reloc.source_entry).off + reloc.source_off, - ref.getSymbol(macho_file).?.getAddress(.{}, macho_file), - @intFromEnum(dwarf.address_size), - ); - } - } + for (unit.entries.items) |*entry| try entry.resolveRelocs(unit, sec, dwarf); } - - const CrossEntryReloc = struct { - source_entry: Entry.Index.Optional = .none, - source_off: u32 = 0, - target_entry: Entry.Index, - target_off: u32 = 0, - }; - const CrossUnitReloc = struct { - source_entry: Entry.Index.Optional = .none, - source_off: u32 = 0, - target_unit: Unit.Index, - target_entry: Entry.Index.Optional = .none, - target_off: u32 = 0, - }; - const CrossSectionReloc = struct { - source_entry: Entry.Index.Optional = .none, - source_off: u32 = 0, - target_sec: Section.Index, - target_unit: Unit.Index, - target_entry: Entry.Index.Optional = .none, - target_off: u32 = 0, - }; - const ExternalReloc = struct { - source_entry: Entry.Index, - source_off: u32 = 0, - target_sym: u32, - target_off: u64 = 0, - }; }; /// An indivisible entry within a `Unit` containing section-specific data. @@ -637,6 +607,25 @@ const Entry = struct { off: u32, /// data length in bytes len: u32, + cross_entry_relocs: std.ArrayListUnmanaged(CrossEntryReloc), + cross_unit_relocs: std.ArrayListUnmanaged(CrossUnitReloc), + cross_section_relocs: std.ArrayListUnmanaged(CrossSectionReloc), + external_relocs: std.ArrayListUnmanaged(ExternalReloc), + + fn clear(entry: *Entry) void { + entry.cross_entry_relocs.clearRetainingCapacity(); + entry.cross_unit_relocs.clearRetainingCapacity(); + entry.cross_section_relocs.clearRetainingCapacity(); + entry.external_relocs.clearRetainingCapacity(); + } + + fn deinit(entry: *Entry, gpa: std.mem.Allocator) void { + entry.cross_entry_relocs.deinit(gpa); + entry.cross_unit_relocs.deinit(gpa); + entry.cross_section_relocs.deinit(gpa); + entry.external_relocs.deinit(gpa); + entry.* = undefined; + } const Index = enum(u32) { _, @@ -645,7 +634,7 @@ const Entry = struct { none = std.math.maxInt(u32), _, - fn unwrap(eio: Optional) ?Index { + pub fn unwrap(eio: Optional) ?Index { return if (eio != .none) @enumFromInt(@intFromEnum(eio)) else null; } }; @@ -656,45 +645,62 @@ const Entry = struct { }; fn pad(entry: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf) UpdateError!void { + assert(entry.len > 0); const start = entry.off + entry.len; const len = unit.getEntry(entry.next.unwrap() orelse return).off - start; - if (sec == &dwarf.debug_info.section) { - var buf: [ - @max( - uleb128Bytes(@intFromEnum(AbbrevCode.pad_1)), - uleb128Bytes(@intFromEnum(AbbrevCode.pad_n)) + uleb128Bytes(std.math.maxInt(u32)), - ) - ]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - switch (len) { - 0 => {}, - 1 => uleb128(fbs.writer(), @intFromEnum(AbbrevCode.pad_1)) catch unreachable, - else => { - uleb128(fbs.writer(), @intFromEnum(AbbrevCode.pad_n)) catch unreachable; - const abbrev_code_bytes = fbs.pos; - var block_len_bytes: u5 = 1; - while (true) switch (std.math.order(len - abbrev_code_bytes - block_len_bytes, @as(u32, 1) << 7 * block_len_bytes)) { - .lt => break uleb128(fbs.writer(), len - abbrev_code_bytes - block_len_bytes) catch unreachable, - .eq => { - // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte - block_len_bytes += 1; - std.leb.writeUnsignedExtended(buf[fbs.pos..][0..block_len_bytes], len - abbrev_code_bytes - block_len_bytes); - fbs.pos += block_len_bytes; - break; - }, - .gt => block_len_bytes += 1, - }; - assert(fbs.pos == abbrev_code_bytes + block_len_bytes); - }, - } - assert(fbs.pos <= len); - try dwarf.getFile().?.pwriteAll(fbs.getWritten(), sec.off + unit.off + unit.header_len + start); - } else if (sec == &dwarf.debug_line.section) { - const buf = try dwarf.gpa.alloc(u8, len); - defer dwarf.gpa.free(buf); - @memset(buf, DW.LNS.const_add_pc); - try dwarf.getFile().?.pwriteAll(buf, sec.off + unit.off + unit.header_len + start); + var buf: [ + @max( + uleb128Bytes(@intFromEnum(AbbrevCode.pad_1)), + uleb128Bytes(@intFromEnum(AbbrevCode.pad_n)) + uleb128Bytes(std.math.maxInt(u32)), + 1 + uleb128Bytes(std.math.maxInt(u32)) + 1, + ) + ]u8 = undefined; + var fbs = std.io.fixedBufferStream(&buf); + const writer = fbs.writer(); + if (sec == &dwarf.debug_info.section) switch (len) { + 0 => {}, + 1 => uleb128(writer, try dwarf.refAbbrevCode(.pad_1)) catch unreachable, + else => { + uleb128(writer, try dwarf.refAbbrevCode(.pad_n)) catch unreachable; + const abbrev_code_bytes = fbs.pos; + var block_len_bytes: u5 = 1; + while (true) switch (std.math.order(len - abbrev_code_bytes - block_len_bytes, @as(u32, 1) << 7 * block_len_bytes)) { + .lt => break uleb128(writer, len - abbrev_code_bytes - block_len_bytes) catch unreachable, + .eq => { + // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte + block_len_bytes += 1; + std.leb.writeUnsignedExtended(buf[fbs.pos..][0..block_len_bytes], len - abbrev_code_bytes - block_len_bytes); + fbs.pos += block_len_bytes; + break; + }, + .gt => block_len_bytes += 1, + }; + assert(fbs.pos == abbrev_code_bytes + block_len_bytes); + }, + } else if (sec == &dwarf.debug_line.section) switch (len) { + 0 => {}, + 1 => writer.writeByte(DW.LNS.const_add_pc) catch unreachable, + else => { + writer.writeByte(DW.LNS.extended_op) catch unreachable; + const extended_op_bytes = fbs.pos; + var op_len_bytes: u5 = 1; + while (true) switch (std.math.order(len - extended_op_bytes - op_len_bytes, @as(u32, 1) << 7 * op_len_bytes)) { + .lt => break uleb128(writer, len - extended_op_bytes - op_len_bytes) catch unreachable, + .eq => { + // no length will ever work, so undercount and futz with the leb encoding to make up the missing byte + op_len_bytes += 1; + std.leb.writeUnsignedExtended(buf[fbs.pos..][0..op_len_bytes], len - extended_op_bytes - op_len_bytes); + fbs.pos += op_len_bytes; + break; + }, + .gt => op_len_bytes += 1, + }; + assert(fbs.pos == extended_op_bytes + op_len_bytes); + if (len > 2) writer.writeByte(DW.LNE.padding) catch unreachable; + }, } else assert(!sec.pad_to_ideal and len == 0); + assert(fbs.pos <= len); + try dwarf.getFile().?.pwriteAll(fbs.getWritten(), sec.off + unit.off + unit.header_len + start); } fn replace(entry_ptr: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf, contents: []const u8) UpdateError!void { @@ -722,15 +728,7 @@ const Entry = struct { try unit.resize(sec, dwarf, 0, @intCast(unit.header_len + entry_ptr.off + sec.padToIdeal(contents.len) + unit.trailer_len)); } entry_ptr.len = @intCast(contents.len); - { - var prev_entry_ptr = entry_ptr; - while (prev_entry_ptr.prev.unwrap()) |prev_entry| { - prev_entry_ptr = unit.getEntry(prev_entry); - if (prev_entry_ptr.len == 0) continue; - try prev_entry_ptr.pad(unit, sec, dwarf); - break; - } - } + if (entry_ptr.prev.unwrap()) |prev_entry| try unit.getEntry(prev_entry).pad(unit, sec, dwarf); try dwarf.getFile().?.pwriteAll(contents, sec.off + unit.off + unit.header_len + entry_ptr.off); try entry_ptr.pad(unit, sec, dwarf); if (false) { @@ -767,7 +765,7 @@ const Entry = struct { } } - fn assertNonEmpty(entry: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf) *Entry { + pub fn assertNonEmpty(entry: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf) *Entry { if (entry.len > 0) return entry; if (std.debug.runtime_safety) { log.err("missing {} from {s}", .{ @@ -803,6 +801,88 @@ const Entry = struct { } @panic("missing dwarf relocation target"); } + + fn resolveRelocs(entry: *Entry, unit: *Unit, sec: *Section, dwarf: *Dwarf) RelocError!void { + const entry_off = sec.off + unit.off + unit.header_len + entry.off; + for (entry.cross_entry_relocs.items) |reloc| { + try dwarf.resolveReloc( + entry_off + reloc.source_off, + unit.off + unit.header_len + unit.getEntry(reloc.target_entry).assertNonEmpty(unit, sec, dwarf).off + reloc.target_off, + dwarf.sectionOffsetBytes(), + ); + } + for (entry.cross_unit_relocs.items) |reloc| { + const target_unit = sec.getUnit(reloc.target_unit); + try dwarf.resolveReloc( + entry_off + reloc.source_off, + target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry| + target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sec, dwarf).off + else + 0) + reloc.target_off, + dwarf.sectionOffsetBytes(), + ); + } + for (entry.cross_section_relocs.items) |reloc| { + const target_sec = switch (reloc.target_sec) { + inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section, + }; + const target_unit = target_sec.getUnit(reloc.target_unit); + try dwarf.resolveReloc( + entry_off + reloc.source_off, + target_unit.off + (if (reloc.target_entry.unwrap()) |target_entry| + target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sec, dwarf).off + else + 0) + reloc.target_off, + dwarf.sectionOffsetBytes(), + ); + } + if (dwarf.bin_file.cast(.elf)) |elf_file| { + const zo = elf_file.zigObjectPtr().?; + for (entry.external_relocs.items) |reloc| { + const symbol = zo.symbol(reloc.target_sym); + try dwarf.resolveReloc( + entry_off + reloc.source_off, + @bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(reloc.target_off)) - + if (symbol.flags.is_tls) elf_file.dtpAddress() else 0), + @intFromEnum(dwarf.address_size), + ); + } + } else if (dwarf.bin_file.cast(.macho)) |macho_file| { + const zo = macho_file.getZigObject().?; + for (entry.external_relocs.items) |reloc| { + const ref = zo.getSymbolRef(reloc.target_sym, macho_file); + try dwarf.resolveReloc( + entry_off + reloc.source_off, + ref.getSymbol(macho_file).?.getAddress(.{}, macho_file), + @intFromEnum(dwarf.address_size), + ); + } + } + } +}; + +const CrossEntryReloc = struct { + source_off: u32 = 0, + target_entry: Entry.Index, + target_off: u32 = 0, +}; +const CrossUnitReloc = struct { + source_off: u32 = 0, + target_unit: Unit.Index, + target_entry: Entry.Index.Optional = .none, + target_off: u32 = 0, +}; +const CrossSectionReloc = struct { + source_off: u32 = 0, + target_sec: Section.Index, + target_unit: Unit.Index, + target_entry: Entry.Index.Optional = .none, + target_off: u32 = 0, +}; +const ExternalReloc = struct { + source_off: u32 = 0, + target_sym: u32, + target_off: u64 = 0, }; pub const Loc = union(enum) { @@ -986,7 +1066,12 @@ pub const WipNav = struct { entry: Entry.Index, any_children: bool, func: InternPool.Index, + func_sym_index: u32, func_high_reloc: u32, + inlined_funcs: std.ArrayListUnmanaged(struct { + abbrev_code: u32, + high_reloc: u32, + }), debug_info: std.ArrayListUnmanaged(u8), debug_line: std.ArrayListUnmanaged(u8), debug_loclists: std.ArrayListUnmanaged(u8), @@ -994,6 +1079,7 @@ pub const WipNav = struct { pub fn deinit(wip_nav: *WipNav) void { const gpa = wip_nav.dwarf.gpa; + if (wip_nav.func != .none) wip_nav.inlined_funcs.deinit(gpa); wip_nav.debug_info.deinit(gpa); wip_nav.debug_line.deinit(gpa); wip_nav.debug_loclists.deinit(gpa); @@ -1004,23 +1090,28 @@ pub const WipNav = struct { return wip_nav.debug_info.writer(wip_nav.dwarf.gpa); } - pub const VarTag = enum { local_arg, local_var }; - pub fn genVarDebugInfo( + pub const LocalTag = enum { local_arg, local_var }; + pub fn genLocalDebugInfo( wip_nav: *WipNav, - tag: VarTag, + tag: LocalTag, name: []const u8, ty: Type, loc: Loc, ) UpdateError!void { - wip_nav.any_children = true; assert(wip_nav.func != .none); - const diw = wip_nav.debug_info.writer(wip_nav.dwarf.gpa); - try uleb128(diw, @intFromEnum(switch (tag) { + try wip_nav.abbrevCode(switch (tag) { inline else => |ct_tag| @field(AbbrevCode, @tagName(ct_tag)), - })); + }); try wip_nav.strp(name); try wip_nav.refType(ty); try wip_nav.exprloc(loc); + wip_nav.any_children = true; + } + + pub fn genVarArgsDebugInfo(wip_nav: *WipNav) UpdateError!void { + assert(wip_nav.func != .none); + try wip_nav.abbrevCode(.is_var_args); + wip_nav.any_children = true; } pub fn advancePCAndLine( @@ -1078,6 +1169,53 @@ pub const WipNav = struct { try dlw.writeByte(DW.LNS.set_epilogue_begin); } + pub fn enterInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64, line: u32, column: u32) UpdateError!void { + const dwarf = wip_nav.dwarf; + const zcu = wip_nav.pt.zcu; + const diw = wip_nav.debug_info.writer(dwarf.gpa); + const inlined_func = try wip_nav.inlined_funcs.addOne(dwarf.gpa); + + inlined_func.abbrev_code = @intCast(wip_nav.debug_info.items.len); + try wip_nav.abbrevCode(.inlined_func); + try wip_nav.refNav(zcu.funcInfo(func).owner_nav); + try uleb128(diw, zcu.navSrcLine(zcu.funcInfo(wip_nav.func).owner_nav) + line + 1); + try uleb128(diw, column + 1); + const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs; + try external_relocs.ensureUnusedCapacity(dwarf.gpa, 2); + external_relocs.appendAssumeCapacity(.{ + .source_off = @intCast(wip_nav.debug_info.items.len), + .target_sym = wip_nav.func_sym_index, + .target_off = code_off, + }); + try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size)); + inlined_func.high_reloc = @intCast(external_relocs.items.len); + external_relocs.appendAssumeCapacity(.{ + .source_off = @intCast(wip_nav.debug_info.items.len), + .target_sym = wip_nav.func_sym_index, + .target_off = undefined, + }); + try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size)); + try wip_nav.setInlineFunc(func); + wip_nav.any_children = false; + } + + pub fn leaveInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64) UpdateError!void { + const inlined_func_bytes = comptime uleb128Bytes(@intFromEnum(AbbrevCode.inlined_func)); + const inlined_func = wip_nav.inlined_funcs.pop(); + const external_relocs = &wip_nav.dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs; + external_relocs.items[inlined_func.high_reloc].target_off = code_off; + if (wip_nav.any_children) + try uleb128(wip_nav.debug_info.writer(wip_nav.dwarf.gpa), @intFromEnum(AbbrevCode.null)) + else + std.leb.writeUnsignedFixed( + inlined_func_bytes, + wip_nav.debug_info.items[inlined_func.abbrev_code..][0..inlined_func_bytes], + try wip_nav.dwarf.refAbbrevCode(.empty_inlined_func), + ); + try wip_nav.setInlineFunc(func); + wip_nav.any_children = true; + } + pub fn setInlineFunc(wip_nav: *WipNav, func: InternPool.Index) UpdateError!void { const zcu = wip_nav.pt.zcu; const dwarf = wip_nav.dwarf; @@ -1096,8 +1234,7 @@ pub const WipNav = struct { try dlw.writeByte(DW.LNS.extended_op); try uleb128(dlw, 1 + dwarf.sectionOffsetBytes()); try dlw.writeByte(DW.LNE.ZIG_set_decl); - try dwarf.debug_line.section.getUnit(wip_nav.unit).cross_section_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry.toOptional(), + try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_section_relocs.append(dwarf.gpa, .{ .source_off = @intCast(wip_nav.debug_line.items.len), .target_sec = .debug_info, .target_unit = new_unit, @@ -1130,12 +1267,16 @@ pub const WipNav = struct { wip_nav.func = func; } + fn abbrevCode(wip_nav: *WipNav, abbrev_code: AbbrevCode) UpdateError!void { + try uleb128(wip_nav.debug_info.writer(wip_nav.dwarf.gpa), try wip_nav.dwarf.refAbbrevCode(abbrev_code)); + } + fn infoSectionOffset(wip_nav: *WipNav, sec: Section.Index, unit: Unit.Index, entry: Entry.Index, off: u32) UpdateError!void { const dwarf = wip_nav.dwarf; const gpa = dwarf.gpa; + const entry_ptr = dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry); if (sec != .debug_info) { - try dwarf.debug_info.section.getUnit(wip_nav.unit).cross_section_relocs.append(gpa, .{ - .source_entry = wip_nav.entry.toOptional(), + try entry_ptr.cross_section_relocs.append(gpa, .{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_sec = sec, .target_unit = unit, @@ -1143,16 +1284,14 @@ pub const WipNav = struct { .target_off = off, }); } else if (unit != wip_nav.unit) { - try dwarf.debug_info.section.getUnit(wip_nav.unit).cross_unit_relocs.append(gpa, .{ - .source_entry = wip_nav.entry.toOptional(), + try entry_ptr.cross_unit_relocs.append(gpa, .{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_unit = unit, .target_entry = entry.toOptional(), .target_off = off, }); } else { - try dwarf.debug_info.section.getUnit(wip_nav.unit).cross_entry_relocs.append(gpa, .{ - .source_entry = wip_nav.entry.toOptional(), + try entry_ptr.cross_entry_relocs.append(gpa, .{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_entry = entry, .target_off = off, @@ -1167,8 +1306,7 @@ pub const WipNav = struct { fn addrSym(wip_nav: *WipNav, sym_index: u32) UpdateError!void { const dwarf = wip_nav.dwarf; - try dwarf.debug_info.section.getUnit(wip_nav.unit).external_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry, + try dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.append(dwarf.gpa, .{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_sym = sym_index, }); @@ -1217,12 +1355,20 @@ pub const WipNav = struct { try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0); } + fn refNav(wip_nav: *WipNav, nav_index: InternPool.Nav.Index) UpdateError!void { + const zcu = wip_nav.pt.zcu; + const ip = &zcu.intern_pool; + const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(ip.getNav(nav_index).srcInst(ip).resolveFile(ip)).mod); + const nav_gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index); + if (!nav_gop.found_existing) nav_gop.value_ptr.* = try wip_nav.dwarf.addCommonEntry(unit); + try wip_nav.infoSectionOffset(.debug_info, unit, nav_gop.value_ptr.*, 0); + } + fn refForward(wip_nav: *WipNav) std.mem.Allocator.Error!u32 { const dwarf = wip_nav.dwarf; - const cross_entry_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).cross_entry_relocs; + const cross_entry_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_entry_relocs; const reloc_index: u32 = @intCast(cross_entry_relocs.items.len); try cross_entry_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry.toOptional(), .source_off = @intCast(wip_nav.debug_info.items.len), .target_entry = undefined, .target_off = undefined, @@ -1232,7 +1378,7 @@ pub const WipNav = struct { } fn finishForward(wip_nav: *WipNav, reloc_index: u32) void { - const reloc = &wip_nav.dwarf.debug_info.section.getUnit(wip_nav.unit).cross_entry_relocs.items[reloc_index]; + const reloc = &wip_nav.dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_entry_relocs.items[reloc_index]; reloc.target_entry = wip_nav.entry; reloc.target_off = @intCast(wip_nav.debug_info.items.len); } @@ -1240,9 +1386,13 @@ pub const WipNav = struct { fn enumConstValue( wip_nav: *WipNav, loaded_enum: InternPool.LoadedEnumType, - abbrev_code: std.enums.EnumFieldStruct(std.builtin.Signedness, AbbrevCode, null), + abbrev_code: struct { + sdata: AbbrevCode, + udata: AbbrevCode, + block: AbbrevCode, + }, field_index: usize, - ) std.mem.Allocator.Error!void { + ) UpdateError!void { const zcu = wip_nav.pt.zcu; const ip = &zcu.intern_pool; const diw = wip_nav.debug_info.writer(wip_nav.dwarf.gpa); @@ -1250,20 +1400,15 @@ pub const WipNav = struct { .comptime_int_type => .signed, else => Type.fromInterned(loaded_enum.tag_ty).intInfo(zcu).signedness, }; - try uleb128(diw, @intFromEnum(switch (signedness) { - inline .signed, .unsigned => |ct_signedness| @field(abbrev_code, @tagName(ct_signedness)), - })); - if (loaded_enum.values.len > 0) switch (ip.indexToKey(loaded_enum.values.get(ip)[field_index]).int.storage) { - .u64 => |value| switch (signedness) { - .signed => try sleb128(diw, value), - .unsigned => try uleb128(diw, value), - }, - .i64 => |value| switch (signedness) { - .signed => try sleb128(diw, value), - .unsigned => unreachable, - }, - .big_int => |big_int| { - const bits = big_int.bitCountTwosCompForSignedness(signedness); + if (loaded_enum.values.len > 0) { + var big_int_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; + const big_int = ip.indexToKey(loaded_enum.values.get(ip)[field_index]).int.storage.toBigInt(&big_int_space); + const bits = @max(1, big_int.bitCountTwosCompForSignedness(signedness)); + if (bits <= 64) { + try wip_nav.abbrevCode(switch (signedness) { + .signed => abbrev_code.sdata, + .unsigned => abbrev_code.udata, + }); try wip_nav.debug_info.ensureUnusedCapacity(wip_nav.dwarf.gpa, std.math.divCeil(usize, bits, 7) catch unreachable); var bit: usize = 0; var carry: u1 = 1; @@ -1272,11 +1417,8 @@ pub const WipNav = struct { const limb_index = bit / limb_bits; const limb_shift: std.math.Log2Int(std.math.big.Limb) = @intCast(bit % limb_bits); const low_abs_part: u7 = @truncate(big_int.limbs[limb_index] >> limb_shift); - const abs_part = if (limb_shift > limb_bits - 7) abs_part: { - const next_limb: std.math.big.Limb = if (limb_index + 1 < big_int.limbs.len) - big_int.limbs[limb_index + 1] - else if (big_int.positive) 0 else std.math.maxInt(std.math.big.Limb); - const high_abs_part: u7 = @truncate(next_limb << -%limb_shift); + const abs_part = if (limb_shift > limb_bits - 7 and limb_index + 1 < big_int.limbs.len) abs_part: { + const high_abs_part: u7 = @truncate(big_int.limbs[limb_index + 1] << -%limb_shift); break :abs_part high_abs_part | low_abs_part; } else low_abs_part; const twos_comp_part = if (big_int.positive) abs_part else twos_comp_part: { @@ -1285,11 +1427,21 @@ pub const WipNav = struct { }; wip_nav.debug_info.appendAssumeCapacity(@as(u8, if (bit + 7 < bits) 0x80 else 0x00) | twos_comp_part); } - }, - .lazy_align, .lazy_size => unreachable, + } else { + try wip_nav.abbrevCode(abbrev_code.block); + const bytes = Type.fromInterned(loaded_enum.tag_ty).abiSize(wip_nav.pt); + try uleb128(diw, bytes); + big_int.writeTwosComplement(try wip_nav.debug_info.addManyAsSlice(wip_nav.dwarf.gpa, @intCast(bytes)), wip_nav.dwarf.endian); + } } else switch (signedness) { - .signed => try sleb128(diw, field_index), - .unsigned => try uleb128(diw, field_index), + .signed => { + try wip_nav.abbrevCode(abbrev_code.sdata); + try sleb128(diw, field_index); + }, + .unsigned => { + try wip_nav.abbrevCode(abbrev_code.udata); + try uleb128(diw, field_index); + }, } } @@ -1441,20 +1593,21 @@ pub fn initMetadata(dwarf: *Dwarf) UpdateError!void { dwarf.reloadSectionMetadata(); dwarf.debug_abbrev.section.pad_to_ideal = false; - assert(try dwarf.debug_abbrev.section.addUnit(0, 0, dwarf) == DebugAbbrev.unit); - errdefer dwarf.debug_abbrev.section.popUnit(); - assert(try dwarf.debug_abbrev.section.addEntry(DebugAbbrev.unit, dwarf) == DebugAbbrev.entry); + assert(try dwarf.debug_abbrev.section.addUnit(DebugAbbrev.header_bytes, DebugAbbrev.trailer_bytes, dwarf) == DebugAbbrev.unit); + errdefer dwarf.debug_abbrev.section.popUnit(dwarf.gpa); + for (std.enums.values(AbbrevCode)) |abbrev_code| + assert(@intFromEnum(try dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).addEntry(dwarf.gpa)) == @intFromEnum(abbrev_code)); dwarf.debug_aranges.section.pad_to_ideal = false; dwarf.debug_aranges.section.alignment = InternPool.Alignment.fromNonzeroByteUnits(@intFromEnum(dwarf.address_size) * 2); dwarf.debug_line_str.section.pad_to_ideal = false; assert(try dwarf.debug_line_str.section.addUnit(0, 0, dwarf) == StringSection.unit); - errdefer dwarf.debug_line_str.section.popUnit(); + errdefer dwarf.debug_line_str.section.popUnit(dwarf.gpa); dwarf.debug_str.section.pad_to_ideal = false; assert(try dwarf.debug_str.section.addUnit(0, 0, dwarf) == StringSection.unit); - errdefer dwarf.debug_str.section.popUnit(); + errdefer dwarf.debug_str.section.popUnit(dwarf.gpa); dwarf.debug_loclists.section.pad_to_ideal = false; @@ -1495,31 +1648,31 @@ fn getUnit(dwarf: *Dwarf, mod: *Module) UpdateError!Unit.Index { DebugAranges.trailerBytes(dwarf), dwarf, ) == unit); - errdefer dwarf.debug_aranges.section.popUnit(); + errdefer dwarf.debug_aranges.section.popUnit(dwarf.gpa); assert(try dwarf.debug_info.section.addUnit( DebugInfo.headerBytes(dwarf), DebugInfo.trailer_bytes, dwarf, ) == unit); - errdefer dwarf.debug_info.section.popUnit(); + errdefer dwarf.debug_info.section.popUnit(dwarf.gpa); assert(try dwarf.debug_line.section.addUnit( DebugLine.headerBytes(dwarf, 5, 25), DebugLine.trailer_bytes, dwarf, ) == unit); - errdefer dwarf.debug_line.section.popUnit(); + errdefer dwarf.debug_line.section.popUnit(dwarf.gpa); assert(try dwarf.debug_loclists.section.addUnit( DebugLocLists.headerBytes(dwarf), DebugLocLists.trailer_bytes, dwarf, ) == unit); - errdefer dwarf.debug_loclists.section.popUnit(); + errdefer dwarf.debug_loclists.section.popUnit(dwarf.gpa); assert(try dwarf.debug_rnglists.section.addUnit( DebugRngLists.headerBytes(dwarf), DebugRngLists.trailer_bytes, dwarf, ) == unit); - errdefer dwarf.debug_rnglists.section.popUnit(); + errdefer dwarf.debug_rnglists.section.popUnit(dwarf.gpa); } return unit; } @@ -1545,7 +1698,15 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In const unit = try dwarf.getUnit(file.mod); const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); errdefer _ = dwarf.navs.pop(); - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (nav_gop.found_existing) { + for ([_]*Section{ + &dwarf.debug_aranges.section, + &dwarf.debug_info.section, + &dwarf.debug_line.section, + &dwarf.debug_loclists.section, + &dwarf.debug_rnglists.section, + }) |sec| sec.getUnit(unit).getEntry(nav_gop.value_ptr.*).clear(); + } else nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); const nav_val = zcu.navValue(nav_index); var wip_nav: WipNav = .{ .dwarf = dwarf, @@ -1554,7 +1715,9 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In .entry = nav_gop.value_ptr.*, .any_children = false, .func = .none, + .func_sym_index = undefined, .func_high_reloc = undefined, + .inlined_funcs = undefined, .debug_info = .{}, .debug_line = .{}, .debug_loclists = .{}, @@ -1595,7 +1758,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In } else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private }; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_var)); + try wip_nav.abbrevCode(.decl_var); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -1608,18 +1771,9 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In try wip_nav.exprloc(.{ .addr = .{ .sym = sym_index } }); try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse ty.abiAlignment(pt).toByteUnits().?); - const func_unit = InternPool.AnalUnit.wrap(.{ .func = nav_val.toIntern() }); - try diw.writeByte(@intFromBool(for (if (zcu.single_exports.get(func_unit)) |export_index| - zcu.all_exports.items[export_index..][0..1] - else if (zcu.multi_exports.get(func_unit)) |export_range| - zcu.all_exports.items[export_range.index..][0..export_range.len] - else - &.{}) |@"export"| - { - if (@"export".exported == .nav and @"export".exported.nav == nav_index) break true; - } else false)); + try diw.writeByte(@intFromBool(false)); wip_nav.finishForward(ty_reloc_index); - try uleb128(diw, @intFromEnum(AbbrevCode.is_const)); + try wip_nav.abbrevCode(.is_const); try wip_nav.refType(ty); }, .variable => |variable| { @@ -1654,7 +1808,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In } else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private }; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_var)); + try wip_nav.abbrevCode(.decl_var); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -1668,16 +1822,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In try wip_nav.exprloc(if (variable.is_threadlocal) .{ .form_tls_address = &addr } else addr); try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse ty.abiAlignment(pt).toByteUnits().?); - const func_unit = InternPool.AnalUnit.wrap(.{ .func = nav_val.toIntern() }); - try diw.writeByte(@intFromBool(for (if (zcu.single_exports.get(func_unit)) |export_index| - zcu.all_exports.items[export_index..][0..1] - else if (zcu.multi_exports.get(func_unit)) |export_range| - zcu.all_exports.items[export_range.index..][0..export_range.len] - else - &.{}) |@"export"| - { - if (@"export".exported == .nav and @"export".exported.nav == nav_index) break true; - } else false)); + try diw.writeByte(@intFromBool(false)); }, .func => |func| { assert(file.zir_loaded); @@ -1712,9 +1857,11 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In const func_type = ip.indexToKey(func.ty).func_type; wip_nav.func = nav_val.toIntern(); + wip_nav.func_sym_index = sym_index; + wip_nav.inlined_funcs = .{}; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_func)); + try wip_nav.abbrevCode(.decl_func); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -1723,32 +1870,23 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In try wip_nav.strp(nav.name.toSlice(ip)); try wip_nav.strp(nav.fqn.toSlice(ip)); try wip_nav.refType(Type.fromInterned(func_type.return_type)); - const external_relocs = &dwarf.debug_info.section.getUnit(unit).external_relocs; - try external_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry, + const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs; + try external_relocs.ensureUnusedCapacity(dwarf.gpa, 2); + external_relocs.appendAssumeCapacity(.{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_sym = sym_index, }); try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size)); wip_nav.func_high_reloc = @intCast(external_relocs.items.len); - try external_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry, + external_relocs.appendAssumeCapacity(.{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_sym = sym_index, + .target_off = undefined, }); try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size)); try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse target_info.defaultFunctionAlignment(file.mod.resolved_target.result).toByteUnits().?); - const func_unit = InternPool.AnalUnit.wrap(.{ .func = nav_val.toIntern() }); - try diw.writeByte(@intFromBool(for (if (zcu.single_exports.get(func_unit)) |export_index| - zcu.all_exports.items[export_index..][0..1] - else if (zcu.multi_exports.get(func_unit)) |export_range| - zcu.all_exports.items[export_range.index..][0..export_range.len] - else - &.{}) |@"export"| - { - if (@"export".exported == .nav and @"export".exported.nav == nav_index) break true; - } else false)); + try diw.writeByte(@intFromBool(false)); try diw.writeByte(@intFromBool(func_type.return_type == .noreturn_type)); const dlw = wip_nav.debug_line.writer(dwarf.gpa); @@ -1756,8 +1894,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In if (dwarf.incremental()) { try uleb128(dlw, 1 + dwarf.sectionOffsetBytes()); try dlw.writeByte(DW.LNE.ZIG_set_decl); - try dwarf.debug_line.section.getUnit(wip_nav.unit).cross_section_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry.toOptional(), + try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).cross_section_relocs.append(dwarf.gpa, .{ .source_off = @intCast(wip_nav.debug_line.items.len), .target_sec = .debug_info, .target_unit = wip_nav.unit, @@ -1772,8 +1909,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In } else { try uleb128(dlw, 1 + @intFromEnum(dwarf.address_size)); try dlw.writeByte(DW.LNE.set_address); - try dwarf.debug_line.section.getUnit(wip_nav.unit).external_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry, + try dwarf.debug_line.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.append(dwarf.gpa, .{ .source_off = @intCast(wip_nav.debug_line.items.len), .target_sym = sym_index, }); @@ -1806,19 +1942,19 @@ pub fn finishWipNav( log.debug("finishWipNav({})", .{nav.fqn.fmt(ip)}); if (wip_nav.func != .none) { - dwarf.debug_info.section.getUnit(wip_nav.unit).external_relocs.items[wip_nav.func_high_reloc].target_off = sym.size; + const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs; + external_relocs.items[wip_nav.func_high_reloc].target_off = sym.size; if (wip_nav.any_children) { const diw = wip_nav.debug_info.writer(dwarf.gpa); try uleb128(diw, @intFromEnum(AbbrevCode.null)); } else std.leb.writeUnsignedFixed( AbbrevCode.decl_bytes, wip_nav.debug_info.items[0..AbbrevCode.decl_bytes], - @intFromEnum(AbbrevCode.decl_func_empty), + try dwarf.refAbbrevCode(.decl_empty_func), ); var aranges_entry = [1]u8{0} ** (8 + 8); - try dwarf.debug_aranges.section.getUnit(wip_nav.unit).external_relocs.append(dwarf.gpa, .{ - .source_entry = wip_nav.entry, + try dwarf.debug_aranges.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.append(dwarf.gpa, .{ .target_sym = sym.index, }); dwarf.writeInt(aranges_entry[0..@intFromEnum(dwarf.address_size)], 0); @@ -1832,14 +1968,12 @@ pub fn finishWipNav( aranges_entry[0 .. @intFromEnum(dwarf.address_size) * 2], ); - try dwarf.debug_rnglists.section.getUnit(wip_nav.unit).external_relocs.appendSlice(dwarf.gpa, &.{ + try dwarf.debug_rnglists.section.getUnit(wip_nav.unit).getEntry(wip_nav.entry).external_relocs.appendSlice(dwarf.gpa, &.{ .{ - .source_entry = wip_nav.entry, .source_off = 1, .target_sym = sym.index, }, .{ - .source_entry = wip_nav.entry, .source_off = 1 + @intFromEnum(dwarf.address_size), .target_sym = sym.index, .target_off = sym.size, @@ -1883,15 +2017,16 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool const loc = tree.tokenLocation(0, tree.nodes.items(.main_token)[decl_inst.data.declaration.src_node]); assert(loc.line == zcu.navSrcLine(nav_index)); - const unit = try dwarf.getUnit(file.mod); var wip_nav: WipNav = .{ .dwarf = dwarf, .pt = pt, - .unit = unit, + .unit = try dwarf.getUnit(file.mod), .entry = undefined, .any_children = false, .func = .none, + .func_sym_index = undefined, .func_high_reloc = undefined, + .inlined_funcs = undefined, .debug_info = .{}, .debug_line = .{}, .debug_loclists = .{}, @@ -1902,6 +2037,65 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); errdefer _ = dwarf.navs.pop(); switch (ip.indexToKey(nav_val.toIntern())) { + .func => |func| { + if (nav_gop.found_existing) { + const unit_ptr = dwarf.debug_info.section.getUnit(wip_nav.unit); + const entry_ptr = unit_ptr.getEntry(nav_gop.value_ptr.*); + if (entry_ptr.len >= AbbrevCode.decl_bytes) { + var abbrev_code_buf: [AbbrevCode.decl_bytes]u8 = undefined; + if (try dwarf.getFile().?.preadAll( + &abbrev_code_buf, + dwarf.debug_info.section.off + unit_ptr.off + unit_ptr.header_len + entry_ptr.off, + ) != abbrev_code_buf.len) return error.InputOutput; + var abbrev_code_fbs = std.io.fixedBufferStream(&abbrev_code_buf); + const abbrev_code: AbbrevCode = @enumFromInt( + std.leb.readUleb128(@typeInfo(AbbrevCode).Enum.tag_type, abbrev_code_fbs.reader()) catch unreachable, + ); + switch (abbrev_code) { + else => unreachable, + .decl_func, .decl_empty_func => return, + .decl_func_generic, .decl_empty_func_generic => {}, + } + } + entry_ptr.clear(); + } else nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); + wip_nav.entry = nav_gop.value_ptr.*; + + const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: { + const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace); + break :parent .{ + parent_namespace_ptr.owner_type, + if (parent_namespace_ptr.pub_decls.containsContext(nav_index, .{ .zcu = zcu })) + DW.ACCESS.public + else if (parent_namespace_ptr.priv_decls.containsContext(nav_index, .{ .zcu = zcu })) + DW.ACCESS.private + else + unreachable, + }; + } else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private }; + + const func_type = ip.indexToKey(func.ty).func_type; + const diw = wip_nav.debug_info.writer(dwarf.gpa); + try wip_nav.abbrevCode(if (func_type.param_types.len > 0 or func_type.is_var_args) + .decl_func_generic + else + .decl_empty_func_generic); + try wip_nav.refType(Type.fromInterned(parent_type)); + assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); + try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); + try uleb128(diw, loc.column + 1); + try diw.writeByte(accessibility); + try wip_nav.strp(nav.name.toSlice(ip)); + try wip_nav.refType(Type.fromInterned(func_type.return_type)); + if (func_type.param_types.len > 0 or func_type.is_var_args) { + for (0..func_type.param_types.len) |param_index| { + try wip_nav.abbrevCode(.func_type_param); + try wip_nav.refType(Type.fromInterned(func_type.param_types.get(ip)[param_index])); + } + if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); + try uleb128(diw, @intFromEnum(AbbrevCode.null)); + } + }, .struct_type => done: { const loaded_struct = ip.loadStructType(nav_val.toIntern()); @@ -1941,8 +2135,14 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool if (type_inst_info.inst != value_inst) break :decl_struct; const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) nav_gop.value_ptr.* = type_gop.value_ptr.* else { - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (type_gop.found_existing) { + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).clear(); + nav_gop.value_ptr.* = type_gop.value_ptr.*; + } else { + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); type_gop.value_ptr.* = nav_gop.value_ptr.*; } wip_nav.entry = nav_gop.value_ptr.*; @@ -1950,10 +2150,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool switch (loaded_struct.layout) { .auto, .@"extern" => { - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (loaded_struct.field_types.len == 0) - .decl_namespace_struct - else - .decl_struct))); + try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .decl_namespace_struct else .decl_struct); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -1965,7 +2162,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool try uleb128(diw, nav_val.toType().abiAlignment(pt).toByteUnits().?); for (0..loaded_struct.field_types.len) |field_index| { const is_comptime = loaded_struct.fieldIsComptime(ip, field_index); - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (is_comptime) .struct_field_comptime else .struct_field))); + try wip_nav.abbrevCode(if (is_comptime) .struct_field_comptime else .struct_field); if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else { const field_name = try std.fmt.allocPrint(dwarf.gpa, "{d}", .{field_index}); defer dwarf.gpa.free(field_name); @@ -1983,7 +2180,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool } }, .@"packed" => { - try uleb128(diw, @intFromEnum(AbbrevCode.decl_packed_struct)); + try wip_nav.abbrevCode(.decl_packed_struct); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -1993,7 +2190,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool try wip_nav.refType(Type.fromInterned(loaded_struct.backingIntTypeUnordered(ip))); var field_bit_offset: u16 = 0; for (0..loaded_struct.field_types.len) |field_index| { - try uleb128(diw, @intFromEnum(@as(AbbrevCode, .packed_struct_field))); + try wip_nav.abbrevCode(.packed_struct_field); try wip_nav.strp(loaded_struct.fieldName(ip, field_index).unwrap().?.toSlice(ip)); const field_type = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); try wip_nav.refType(field_type); @@ -2006,10 +2203,13 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool break :done; } - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_alias)); + try wip_nav.abbrevCode(.decl_alias); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2057,13 +2257,19 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool if (type_inst_info.inst != value_inst) break :decl_enum; const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) nav_gop.value_ptr.* = type_gop.value_ptr.* else { - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (type_gop.found_existing) { + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).clear(); + nav_gop.value_ptr.* = type_gop.value_ptr.*; + } else { + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); type_gop.value_ptr.* = nav_gop.value_ptr.*; } wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_enum)); + try wip_nav.abbrevCode(if (loaded_enum.names.len > 0) .decl_enum else .decl_empty_enum); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2073,19 +2279,23 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool try wip_nav.refType(Type.fromInterned(loaded_enum.tag_ty)); for (0..loaded_enum.names.len) |field_index| { try wip_nav.enumConstValue(loaded_enum, .{ - .signed = .signed_enum_field, - .unsigned = .unsigned_enum_field, + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, }, field_index); try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip)); } - try uleb128(diw, @intFromEnum(AbbrevCode.null)); + if (loaded_enum.names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); break :done; } - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_alias)); + try wip_nav.abbrevCode(.decl_alias); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2131,13 +2341,19 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool if (type_inst_info.inst != value_inst) break :decl_union; const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) nav_gop.value_ptr.* = type_gop.value_ptr.* else { - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (type_gop.found_existing) { + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).clear(); + nav_gop.value_ptr.* = type_gop.value_ptr.*; + } else { + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); type_gop.value_ptr.* = nav_gop.value_ptr.*; } wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_union)); + try wip_nav.abbrevCode(.decl_union); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2149,7 +2365,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool try uleb128(diw, union_layout.abi_align.toByteUnits().?); const loaded_tag = loaded_union.loadTagType(ip); if (loaded_union.hasTag(ip)) { - try uleb128(diw, @intFromEnum(AbbrevCode.tagged_union)); + try wip_nav.abbrevCode(.tagged_union); try wip_nav.infoSectionOffset( .debug_info, wip_nav.unit, @@ -2157,18 +2373,19 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()), ); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("tag"); try wip_nav.refType(Type.fromInterned(loaded_union.enum_tag_ty)); try uleb128(diw, union_layout.tagOffset()); for (0..loaded_union.field_types.len) |field_index| { try wip_nav.enumConstValue(loaded_tag, .{ - .signed = .signed_tagged_union_field, - .unsigned = .unsigned_tagged_union_field, + .sdata = .signed_tagged_union_field, + .udata = .unsigned_tagged_union_field, + .block = .big_tagged_union_field, }, field_index); { - try uleb128(diw, @intFromEnum(AbbrevCode.struct_field)); + try wip_nav.abbrevCode(.struct_field); try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); const field_type = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); try wip_nav.refType(field_type); @@ -2184,7 +2401,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool if (ip.indexToKey(loaded_union.enum_tag_ty).enum_type == .generated_tag) try wip_nav.pending_types.append(dwarf.gpa, loaded_union.enum_tag_ty); } else for (0..loaded_union.field_types.len) |field_index| { - try uleb128(diw, @intFromEnum(AbbrevCode.untagged_union_field)); + try wip_nav.abbrevCode(.untagged_union_field); try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); const field_type = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); try wip_nav.refType(field_type); @@ -2195,10 +2412,13 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool break :done; } - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_alias)); + try wip_nav.abbrevCode(.decl_alias); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2244,13 +2464,19 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool if (type_inst_info.inst != value_inst) break :decl_opaque; const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern()); - if (type_gop.found_existing) nav_gop.value_ptr.* = type_gop.value_ptr.* else { - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (type_gop.found_existing) { + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).clear(); + nav_gop.value_ptr.* = type_gop.value_ptr.*; + } else { + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); type_gop.value_ptr.* = nav_gop.value_ptr.*; } wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_namespace_struct)); + try wip_nav.abbrevCode(.decl_namespace_struct); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2261,10 +2487,13 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool break :done; } - if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit); + if (nav_gop.found_existing) + dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(nav_gop.value_ptr.*).clear() + else + nav_gop.value_ptr.* = try dwarf.addCommonEntry(wip_nav.unit); wip_nav.entry = nav_gop.value_ptr.*; const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(AbbrevCode.decl_alias)); + try wip_nav.abbrevCode(.decl_alias); try wip_nav.refType(Type.fromInterned(parent_type)); assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf)); try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian); @@ -2279,7 +2508,6 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool }, } try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items); - try dwarf.debug_loclists.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_loclists.items); try wip_nav.flush(); } @@ -2304,7 +2532,9 @@ fn updateType( .entry = dwarf.types.get(type_index).?, .any_children = false, .func = .none, + .func_sym_index = undefined, .func_high_reloc = undefined, + .inlined_funcs = undefined, .debug_info = .{}, .debug_line = .{}, .debug_loclists = .{}, @@ -2324,7 +2554,7 @@ fn updateType( switch (ip.indexToKey(type_index)) { .int_type => |int_type| { - try uleb128(diw, @intFromEnum(AbbrevCode.numeric_type)); + try wip_nav.abbrevCode(.numeric_type); try wip_nav.strp(name); try diw.writeByte(switch (int_type.signedness) { inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)), @@ -2336,7 +2566,7 @@ fn updateType( .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .One, .Many, .C => { const ptr_child_type = Type.fromInterned(ptr_type.child); - try uleb128(diw, @intFromEnum(AbbrevCode.ptr_type)); + try wip_nav.abbrevCode(.ptr_type); try wip_nav.strp(name); try uleb128(diw, ptr_type.flags.alignment.toByteUnits() orelse ptr_child_type.abiAlignment(pt).toByteUnits().?); @@ -2348,7 +2578,7 @@ fn updateType( @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()), ) else try wip_nav.refType(ptr_child_type); if (ptr_type.flags.is_const) { - try uleb128(diw, @intFromEnum(AbbrevCode.is_const)); + try wip_nav.abbrevCode(.is_const); if (ptr_type.flags.is_volatile) try wip_nav.infoSectionOffset( .debug_info, wip_nav.unit, @@ -2357,21 +2587,21 @@ fn updateType( ) else try wip_nav.refType(ptr_child_type); } if (ptr_type.flags.is_volatile) { - try uleb128(diw, @intFromEnum(AbbrevCode.is_volatile)); + try wip_nav.abbrevCode(.is_volatile); try wip_nav.refType(ptr_child_type); } }, .Slice => { - try uleb128(diw, @intFromEnum(AbbrevCode.struct_type)); + try wip_nav.abbrevCode(.struct_type); try wip_nav.strp(name); try uleb128(diw, ty.abiSize(pt)); try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("ptr"); const ptr_field_type = ty.slicePtrFieldType(zcu); try wip_nav.refType(ptr_field_type); try uleb128(diw, 0); - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("len"); const len_field_type = Type.usize; try wip_nav.refType(len_field_type); @@ -2380,28 +2610,28 @@ fn updateType( }, }, inline .array_type, .vector_type => |array_type, ty_tag| { - try uleb128(diw, @intFromEnum(AbbrevCode.array_type)); + try wip_nav.abbrevCode(.array_type); try wip_nav.strp(name); try wip_nav.refType(Type.fromInterned(array_type.child)); try diw.writeByte(@intFromBool(ty_tag == .vector_type)); - try uleb128(diw, @intFromEnum(AbbrevCode.array_index)); + try wip_nav.abbrevCode(.array_index); try wip_nav.refType(Type.usize); try uleb128(diw, array_type.len); try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, .opt_type => |opt_child_type_index| { const opt_child_type = Type.fromInterned(opt_child_type_index); - try uleb128(diw, @intFromEnum(AbbrevCode.union_type)); + try wip_nav.abbrevCode(.union_type); try wip_nav.strp(name); try uleb128(diw, ty.abiSize(pt)); try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); if (opt_child_type.isNoReturn(zcu)) { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("null"); try wip_nav.refType(Type.null); try uleb128(diw, 0); } else { - try uleb128(diw, @intFromEnum(AbbrevCode.tagged_union)); + try wip_nav.abbrevCode(.tagged_union); try wip_nav.infoSectionOffset( .debug_info, wip_nav.unit, @@ -2409,7 +2639,7 @@ fn updateType( @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()), ); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("has_value"); const repr: enum { unpacked, error_set, pointer } = switch (opt_child_type_index) { .anyerror_type => .error_set, @@ -2440,19 +2670,19 @@ fn updateType( }, } - try uleb128(diw, @intFromEnum(AbbrevCode.unsigned_tagged_union_field)); + try wip_nav.abbrevCode(.unsigned_tagged_union_field); try uleb128(diw, 0); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("null"); try wip_nav.refType(Type.null); try uleb128(diw, 0); } try uleb128(diw, @intFromEnum(AbbrevCode.null)); - try uleb128(diw, @intFromEnum(AbbrevCode.tagged_union_default_field)); + try wip_nav.abbrevCode(.tagged_union_default_field); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("?"); try wip_nav.refType(opt_child_type); try uleb128(diw, 0); @@ -2467,15 +2697,27 @@ fn updateType( .error_union_type => |error_union_type| { const error_union_error_set_type = Type.fromInterned(error_union_type.error_set_type); const error_union_payload_type = Type.fromInterned(error_union_type.payload_type); - const error_union_error_set_offset = codegen.errUnionErrorOffset(error_union_payload_type, pt); - const error_union_payload_offset = codegen.errUnionPayloadOffset(error_union_payload_type, pt); + const error_union_error_set_offset, const error_union_payload_offset = switch (error_union_type.payload_type) { + .generic_poison_type => .{ 0, 0 }, + else => .{ + codegen.errUnionErrorOffset(error_union_payload_type, pt), + codegen.errUnionPayloadOffset(error_union_payload_type, pt), + }, + }; - try uleb128(diw, @intFromEnum(AbbrevCode.union_type)); + try wip_nav.abbrevCode(.union_type); try wip_nav.strp(name); - try uleb128(diw, ty.abiSize(pt)); - try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); + if (error_union_type.error_set_type != .generic_poison_type and + error_union_type.payload_type != .generic_poison_type) + { + try uleb128(diw, ty.abiSize(pt)); + try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); + } else { + try uleb128(diw, 0); + try uleb128(diw, 1); + } { - try uleb128(diw, @intFromEnum(AbbrevCode.tagged_union)); + try wip_nav.abbrevCode(.tagged_union); try wip_nav.infoSectionOffset( .debug_info, wip_nav.unit, @@ -2483,7 +2725,7 @@ fn updateType( @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()), ); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("is_error"); try wip_nav.refType(Type.fromInterned(try pt.intern(.{ .int_type = .{ .signedness = .unsigned, @@ -2491,19 +2733,19 @@ fn updateType( } }))); try uleb128(diw, error_union_error_set_offset); - try uleb128(diw, @intFromEnum(AbbrevCode.unsigned_tagged_union_field)); + try wip_nav.abbrevCode(.unsigned_tagged_union_field); try uleb128(diw, 0); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("value"); try wip_nav.refType(error_union_payload_type); try uleb128(diw, error_union_payload_offset); } try uleb128(diw, @intFromEnum(AbbrevCode.null)); - try uleb128(diw, @intFromEnum(AbbrevCode.tagged_union_default_field)); + try wip_nav.abbrevCode(.tagged_union_default_field); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("error"); try wip_nav.refType(error_union_error_set_type); try uleb128(diw, error_union_error_set_offset); @@ -2534,7 +2776,7 @@ fn updateType( .c_longdouble, .bool, => { - try uleb128(diw, @intFromEnum(AbbrevCode.numeric_type)); + try wip_nav.abbrevCode(.numeric_type); try wip_nav.strp(name); try diw.writeByte(if (type_index == .bool_type) DW.ATE.boolean @@ -2561,7 +2803,7 @@ fn updateType( .enum_literal, .generic_poison, => { - try uleb128(diw, @intFromEnum(AbbrevCode.void_type)); + try wip_nav.abbrevCode(.void_type); try wip_nav.strp(if (type_index == .generic_poison_type) "anytype" else name); }, .anyerror => return, // delay until flush @@ -2571,15 +2813,19 @@ fn updateType( .union_type, .opaque_type, => unreachable, - .anon_struct_type => |anon_struct_type| { - try uleb128(diw, @intFromEnum(AbbrevCode.struct_type)); + .anon_struct_type => |anon_struct_type| if (anon_struct_type.types.len == 0) { + try wip_nav.abbrevCode(.namespace_struct_type); + try wip_nav.strp(name); + try diw.writeByte(@intFromBool(false)); + } else { + try wip_nav.abbrevCode(.struct_type); try wip_nav.strp(name); try uleb128(diw, ty.abiSize(pt)); try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); var field_byte_offset: u64 = 0; for (0..anon_struct_type.types.len) |field_index| { const comptime_value = anon_struct_type.values.get(ip)[field_index]; - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (comptime_value != .none) .struct_field_comptime else .struct_field))); + try wip_nav.abbrevCode(if (comptime_value != .none) .struct_field_comptime else .struct_field); if (anon_struct_type.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else { const field_name = try std.fmt.allocPrint(dwarf.gpa, "{d}", .{field_index}); defer dwarf.gpa.free(field_name); @@ -2599,21 +2845,22 @@ fn updateType( }, .enum_type => { const loaded_enum = ip.loadEnumType(type_index); - try uleb128(diw, @intFromEnum(AbbrevCode.enum_type)); + try wip_nav.abbrevCode(if (loaded_enum.names.len > 0) .enum_type else .empty_enum_type); try wip_nav.strp(name); try wip_nav.refType(Type.fromInterned(loaded_enum.tag_ty)); for (0..loaded_enum.names.len) |field_index| { try wip_nav.enumConstValue(loaded_enum, .{ - .signed = .signed_enum_field, - .unsigned = .unsigned_enum_field, + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, }, field_index); try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip)); } - try uleb128(diw, @intFromEnum(AbbrevCode.null)); + if (loaded_enum.names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, .func_type => |func_type| { const is_nullary = func_type.param_types.len == 0 and !func_type.is_var_args; - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (is_nullary) .nullary_func_type else .func_type))); + try wip_nav.abbrevCode(if (is_nullary) .nullary_func_type else .func_type); try wip_nav.strp(name); try diw.writeByte(@intFromEnum(@as(DW.CC, switch (func_type.cc) { .Unspecified, .C => .normal, @@ -2633,15 +2880,15 @@ fn updateType( try wip_nav.refType(Type.fromInterned(func_type.return_type)); if (!is_nullary) { for (0..func_type.param_types.len) |param_index| { - try uleb128(diw, @intFromEnum(AbbrevCode.func_type_param)); + try wip_nav.abbrevCode(.func_type_param); try wip_nav.refType(Type.fromInterned(func_type.param_types.get(ip)[param_index])); } - if (func_type.is_var_args) try uleb128(diw, @intFromEnum(AbbrevCode.is_var_args)); + if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args); try uleb128(diw, @intFromEnum(AbbrevCode.null)); } }, .error_set_type => |error_set_type| { - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (error_set_type.names.len > 0) .enum_type else .empty_enum_type))); + try wip_nav.abbrevCode(if (error_set_type.names.len > 0) .enum_type else .empty_enum_type); try wip_nav.strp(name); try wip_nav.refType(Type.fromInterned(try pt.intern(.{ .int_type = .{ .signedness = .unsigned, @@ -2649,16 +2896,22 @@ fn updateType( } }))); for (0..error_set_type.names.len) |field_index| { const field_name = error_set_type.names.get(ip)[field_index]; - try uleb128(diw, @intFromEnum(AbbrevCode.unsigned_enum_field)); + try wip_nav.abbrevCode(.unsigned_enum_field); try uleb128(diw, ip.getErrorValueIfExists(field_name).?); try wip_nav.strp(field_name.toSlice(ip)); } if (error_set_type.names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, - .inferred_error_set_type => |func| { - try uleb128(diw, @intFromEnum(AbbrevCode.inferred_error_set_type)); - try wip_nav.strp(name); - try wip_nav.refType(Type.fromInterned(ip.funcIesResolvedUnordered(func))); + .inferred_error_set_type => |func| switch (ip.funcIesResolvedUnordered(func)) { + .none => { + try wip_nav.abbrevCode(.void_type); + try wip_nav.strp(name); + }, + else => |ies| { + try wip_nav.abbrevCode(.inferred_error_set_type); + try wip_nav.strp(name); + try wip_nav.refType(Type.fromInterned(ies)); + }, }, // values, not types @@ -2705,7 +2958,9 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP .entry = type_gop.value_ptr.*, .any_children = false, .func = .none, + .func_sym_index = undefined, .func_high_reloc = undefined, + .inlined_funcs = undefined, .debug_info = .{}, .debug_line = .{}, .debug_loclists = .{}, @@ -2716,7 +2971,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP const loaded_struct = ip.loadStructType(type_index); const diw = wip_nav.debug_info.writer(dwarf.gpa); - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (loaded_struct.field_types.len == 0) .namespace_file else .file))); + try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .namespace_file else .file); const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, inst_info.file); try uleb128(diw, file_gop.index); try wip_nav.strp(loaded_struct.name.toSlice(ip)); @@ -2725,7 +2980,7 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); for (0..loaded_struct.field_types.len) |field_index| { const is_comptime = loaded_struct.fieldIsComptime(ip, field_index); - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (is_comptime) .struct_field_comptime else .struct_field))); + try wip_nav.abbrevCode(if (is_comptime) .struct_field_comptime else .struct_field); if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else { const field_name = try std.fmt.allocPrint(dwarf.gpa, "{d}", .{field_index}); defer dwarf.gpa.free(field_name); @@ -2766,7 +3021,9 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP .entry = type_gop.value_ptr.*, .any_children = false, .func = .none, + .func_sym_index = undefined, .func_high_reloc = undefined, + .inlined_funcs = undefined, .debug_info = .{}, .debug_line = .{}, .debug_loclists = .{}, @@ -2782,17 +3039,14 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP const loaded_struct = ip.loadStructType(type_index); switch (loaded_struct.layout) { .auto, .@"extern" => { - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (loaded_struct.field_types.len == 0) - .namespace_struct_type - else - .struct_type))); + try wip_nav.abbrevCode(if (loaded_struct.field_types.len == 0) .namespace_struct_type else .struct_type); try wip_nav.strp(name); if (loaded_struct.field_types.len == 0) try diw.writeByte(@intFromBool(false)) else { try uleb128(diw, ty.abiSize(pt)); try uleb128(diw, ty.abiAlignment(pt).toByteUnits().?); for (0..loaded_struct.field_types.len) |field_index| { const is_comptime = loaded_struct.fieldIsComptime(ip, field_index); - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (is_comptime) .struct_field_comptime else .struct_field))); + try wip_nav.abbrevCode(if (is_comptime) .struct_field_comptime else .struct_field); if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| try wip_nav.strp(field_name.toSlice(ip)) else { const field_name = try std.fmt.allocPrint(dwarf.gpa, "{d}", .{field_index}); defer dwarf.gpa.free(field_name); @@ -2810,46 +3064,47 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP } }, .@"packed" => { - try uleb128(diw, @intFromEnum(AbbrevCode.packed_struct_type)); + try wip_nav.abbrevCode(if (loaded_struct.field_types.len > 0) .packed_struct_type else .empty_packed_struct_type); try wip_nav.strp(name); try wip_nav.refType(Type.fromInterned(loaded_struct.backingIntTypeUnordered(ip))); var field_bit_offset: u16 = 0; for (0..loaded_struct.field_types.len) |field_index| { - try uleb128(diw, @intFromEnum(@as(AbbrevCode, .packed_struct_field))); + try wip_nav.abbrevCode(.packed_struct_field); try wip_nav.strp(loaded_struct.fieldName(ip, field_index).unwrap().?.toSlice(ip)); const field_type = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); try wip_nav.refType(field_type); try uleb128(diw, field_bit_offset); field_bit_offset += @intCast(field_type.bitSize(pt)); } - try uleb128(diw, @intFromEnum(AbbrevCode.null)); + if (loaded_struct.field_types.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, } }, .enum_type => { const loaded_enum = ip.loadEnumType(type_index); - try uleb128(diw, @intFromEnum(AbbrevCode.enum_type)); + try wip_nav.abbrevCode(if (loaded_enum.names.len > 0) .enum_type else .empty_enum_type); try wip_nav.strp(name); try wip_nav.refType(Type.fromInterned(loaded_enum.tag_ty)); for (0..loaded_enum.names.len) |field_index| { try wip_nav.enumConstValue(loaded_enum, .{ - .signed = .signed_enum_field, - .unsigned = .unsigned_enum_field, + .sdata = .signed_enum_field, + .udata = .unsigned_enum_field, + .block = .big_enum_field, }, field_index); try wip_nav.strp(loaded_enum.names.get(ip)[field_index].toSlice(ip)); } - try uleb128(diw, @intFromEnum(AbbrevCode.null)); + if (loaded_enum.names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, .union_type => { const loaded_union = ip.loadUnionType(type_index); - try uleb128(diw, @intFromEnum(AbbrevCode.union_type)); + try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type); try wip_nav.strp(name); const union_layout = pt.getUnionLayout(loaded_union); try uleb128(diw, union_layout.abi_size); try uleb128(diw, union_layout.abi_align.toByteUnits().?); const loaded_tag = loaded_union.loadTagType(ip); if (loaded_union.hasTag(ip)) { - try uleb128(diw, @intFromEnum(AbbrevCode.tagged_union)); + try wip_nav.abbrevCode(.tagged_union); try wip_nav.infoSectionOffset( .debug_info, wip_nav.unit, @@ -2857,18 +3112,19 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()), ); { - try uleb128(diw, @intFromEnum(AbbrevCode.generated_field)); + try wip_nav.abbrevCode(.generated_field); try wip_nav.strp("tag"); try wip_nav.refType(Type.fromInterned(loaded_union.enum_tag_ty)); try uleb128(diw, union_layout.tagOffset()); for (0..loaded_union.field_types.len) |field_index| { try wip_nav.enumConstValue(loaded_tag, .{ - .signed = .signed_tagged_union_field, - .unsigned = .unsigned_tagged_union_field, + .sdata = .signed_tagged_union_field, + .udata = .unsigned_tagged_union_field, + .block = .big_tagged_union_field, }, field_index); { - try uleb128(diw, @intFromEnum(AbbrevCode.struct_field)); + try wip_nav.abbrevCode(.struct_field); try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); const field_type = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); try wip_nav.refType(field_type); @@ -2884,17 +3140,17 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP if (ip.indexToKey(loaded_union.enum_tag_ty).enum_type == .generated_tag) try wip_nav.pending_types.append(dwarf.gpa, loaded_union.enum_tag_ty); } else for (0..loaded_union.field_types.len) |field_index| { - try uleb128(diw, @intFromEnum(AbbrevCode.untagged_union_field)); + try wip_nav.abbrevCode(.untagged_union_field); try wip_nav.strp(loaded_tag.names.get(ip)[field_index].toSlice(ip)); const field_type = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); try wip_nav.refType(field_type); try uleb128(diw, loaded_union.fieldAlign(ip, field_index).toByteUnits() orelse field_type.abiAlignment(pt).toByteUnits().?); } - try uleb128(diw, @intFromEnum(AbbrevCode.null)); + if (loaded_union.field_types.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); }, .opaque_type => { - try uleb128(diw, @intFromEnum(AbbrevCode.namespace_struct_type)); + try wip_nav.abbrevCode(.namespace_struct_type); try wip_nav.strp(name); try diw.writeByte(@intFromBool(true)); }, @@ -2930,6 +3186,23 @@ pub fn freeNav(dwarf: *Dwarf, nav_index: InternPool.Nav.Index) void { _ = nav_index; } +fn refAbbrevCode(dwarf: *Dwarf, abbrev_code: AbbrevCode) UpdateError!@typeInfo(AbbrevCode).Enum.tag_type { + assert(abbrev_code != .null); + const entry: Entry.Index = @enumFromInt(@intFromEnum(abbrev_code)); + if (dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).getEntry(entry).len > 0) return @intFromEnum(abbrev_code); + var debug_abbrev = std.ArrayList(u8).init(dwarf.gpa); + defer debug_abbrev.deinit(); + const daw = debug_abbrev.writer(); + const abbrev = AbbrevCode.abbrevs.get(abbrev_code); + try uleb128(daw, @intFromEnum(abbrev_code)); + try uleb128(daw, @intFromEnum(abbrev.tag)); + try daw.writeByte(if (abbrev.children) DW.CHILDREN.yes else DW.CHILDREN.no); + for (abbrev.attrs) |*attr| inline for (attr) |info| try uleb128(daw, @intFromEnum(info)); + for (0..2) |_| try uleb128(daw, 0); + try dwarf.debug_abbrev.section.replaceEntry(DebugAbbrev.unit, entry, dwarf, debug_abbrev.items); + return @intFromEnum(abbrev_code); +} + pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { const ip = &pt.zcu.intern_pool; if (dwarf.types.get(.anyerror_type)) |entry| { @@ -2940,7 +3213,9 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { .entry = entry, .any_children = false, .func = .none, + .func_sym_index = undefined, .func_high_reloc = undefined, + .inlined_funcs = undefined, .debug_info = .{}, .debug_line = .{}, .debug_loclists = .{}, @@ -2949,14 +3224,14 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { defer wip_nav.deinit(); const diw = wip_nav.debug_info.writer(dwarf.gpa); const global_error_set_names = ip.global_error_set.getNamesFromMainThread(); - try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (global_error_set_names.len > 0) .enum_type else .empty_enum_type))); + try wip_nav.abbrevCode(if (global_error_set_names.len > 0) .enum_type else .empty_enum_type); try wip_nav.strp("anyerror"); try wip_nav.refType(Type.fromInterned(try pt.intern(.{ .int_type = .{ .signedness = .unsigned, .bits = pt.zcu.errorSetBits(), } }))); for (global_error_set_names, 1..) |name, value| { - try uleb128(diw, @intFromEnum(AbbrevCode.unsigned_enum_field)); + try wip_nav.abbrevCode(.unsigned_enum_field); try uleb128(diw, value); try wip_nav.strp(name.toSlice(ip)); } @@ -2980,25 +3255,11 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { var header = std.ArrayList(u8).init(dwarf.gpa); defer header.deinit(); - if (dwarf.debug_abbrev.section.dirty) { - for (1.., &AbbrevCode.abbrevs) |code, *abbrev| { - try uleb128(header.writer(), code); - try uleb128(header.writer(), @intFromEnum(abbrev.tag)); - try header.append(if (abbrev.children) DW.CHILDREN.yes else DW.CHILDREN.no); - for (abbrev.attrs) |*attr| { - try uleb128(header.writer(), @intFromEnum(attr[0])); - try uleb128(header.writer(), @intFromEnum(attr[1])); - } - try header.appendSlice(&.{ 0, 0 }); - } - try header.append(@intFromEnum(AbbrevCode.null)); - try dwarf.debug_abbrev.section.replaceEntry(DebugAbbrev.unit, DebugAbbrev.entry, dwarf, header.items); - dwarf.debug_abbrev.section.dirty = false; - } if (dwarf.debug_aranges.section.dirty) { for (dwarf.debug_aranges.section.units.items, 0..) |*unit_ptr, unit_index| { const unit: Unit.Index = @enumFromInt(unit_index); - try unit_ptr.cross_section_relocs.ensureUnusedCapacity(dwarf.gpa, 1); + unit_ptr.clear(); + try unit_ptr.cross_section_relocs.ensureTotalCapacity(dwarf.gpa, 1); header.clearRetainingCapacity(); try header.ensureTotalCapacity(unit_ptr.header_len); const unit_len = (if (unit_ptr.next.unwrap()) |next_unit| @@ -3029,8 +3290,9 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { if (dwarf.debug_info.section.dirty) { for (dwarf.mods.keys(), dwarf.mods.values(), dwarf.debug_info.section.units.items, 0..) |mod, mod_info, *unit_ptr, unit_index| { const unit: Unit.Index = @enumFromInt(unit_index); - try unit_ptr.cross_unit_relocs.ensureUnusedCapacity(dwarf.gpa, 1); - try unit_ptr.cross_section_relocs.ensureUnusedCapacity(dwarf.gpa, 7); + unit_ptr.clear(); + try unit_ptr.cross_unit_relocs.ensureTotalCapacity(dwarf.gpa, 1); + try unit_ptr.cross_section_relocs.ensureTotalCapacity(dwarf.gpa, 7); header.clearRetainingCapacity(); try header.ensureTotalCapacity(unit_ptr.header_len); const unit_len = (if (unit_ptr.next.unwrap()) |next_unit| @@ -3050,11 +3312,10 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { .source_off = @intCast(header.items.len), .target_sec = .debug_abbrev, .target_unit = DebugAbbrev.unit, - .target_entry = DebugAbbrev.entry.toOptional(), }); header.appendNTimesAssumeCapacity(0, dwarf.sectionOffsetBytes()); const compile_unit_off: u32 = @intCast(header.items.len); - uleb128(header.fixedWriter(), @intFromEnum(AbbrevCode.compile_unit)) catch unreachable; + uleb128(header.fixedWriter(), try dwarf.refAbbrevCode(.compile_unit)) catch unreachable; header.appendAssumeCapacity(DW.LANG.Zig); unit_ptr.cross_section_relocs.appendAssumeCapacity(.{ .source_off = @intCast(header.items.len), @@ -3097,7 +3358,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { }); header.appendNTimesAssumeCapacity(0, dwarf.sectionOffsetBytes()); uleb128(header.fixedWriter(), 0) catch unreachable; - uleb128(header.fixedWriter(), @intFromEnum(AbbrevCode.module)) catch unreachable; + uleb128(header.fixedWriter(), try dwarf.refAbbrevCode(.module)) catch unreachable; unit_ptr.cross_section_relocs.appendAssumeCapacity(.{ .source_off = @intCast(header.items.len), .target_sec = .debug_str, @@ -3111,6 +3372,11 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { } dwarf.debug_info.section.dirty = false; } + if (dwarf.debug_abbrev.section.dirty) { + assert(!dwarf.debug_info.section.dirty); + try dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).writeTrailer(&dwarf.debug_abbrev.section, dwarf); + dwarf.debug_abbrev.section.dirty = false; + } if (dwarf.debug_str.section.dirty) { const contents = dwarf.debug_str.contents.items; try dwarf.debug_str.section.resize(dwarf, contents.len); @@ -3118,10 +3384,14 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { dwarf.debug_str.section.dirty = false; } if (dwarf.debug_line.section.dirty) { - for (dwarf.mods.values(), dwarf.debug_line.section.units.items) |mod_info, *unit| - try unit.resizeHeader(&dwarf.debug_line.section, dwarf, DebugLine.headerBytes(dwarf, @intCast(mod_info.dirs.count()), @intCast(mod_info.files.count()))); + for (dwarf.mods.values(), dwarf.debug_line.section.units.items) |mod_info, *unit| try unit.resizeHeader( + &dwarf.debug_line.section, + dwarf, + DebugLine.headerBytes(dwarf, @intCast(mod_info.dirs.count()), @intCast(mod_info.files.count())), + ); for (dwarf.mods.values(), dwarf.debug_line.section.units.items) |mod_info, *unit| { - try unit.cross_section_relocs.ensureUnusedCapacity(dwarf.gpa, 2 * (1 + mod_info.files.count())); + unit.clear(); + try unit.cross_section_relocs.ensureTotalCapacity(dwarf.gpa, 2 * (1 + mod_info.files.count())); header.clearRetainingCapacity(); try header.ensureTotalCapacity(unit.header_len); const unit_len = (if (unit.next.unwrap()) |next_unit| @@ -3137,14 +3407,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { } std.mem.writeInt(u16, header.addManyAsArrayAssumeCapacity(@sizeOf(u16)), 5, dwarf.endian); header.appendSliceAssumeCapacity(&.{ @intFromEnum(dwarf.address_size), 0 }); - switch (dwarf.format) { - inline .@"32", .@"64" => |format| std.mem.writeInt( - SectionOffset(format), - header.addManyAsArrayAssumeCapacity(@sizeOf(SectionOffset(format))), - @intCast(unit.header_len - header.items.len), - dwarf.endian, - ), - } + dwarf.writeInt(header.addManyAsSliceAssumeCapacity(dwarf.sectionOffsetBytes()), unit.header_len - header.items.len); const StandardOpcode = DeclValEnum(DW.LNS); header.appendSliceAssumeCapacity(&[_]u8{ dwarf.debug_line.header.minimum_instruction_length, @@ -3226,6 +3489,9 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { try dwarf.getFile().?.pwriteAll(contents, dwarf.debug_line_str.section.off); dwarf.debug_line_str.section.dirty = false; } + if (dwarf.debug_loclists.section.dirty) { + dwarf.debug_loclists.section.dirty = false; + } if (dwarf.debug_rnglists.section.dirty) { for (dwarf.debug_rnglists.section.units.items) |*unit| { header.clearRetainingCapacity(); @@ -3244,19 +3510,20 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { std.mem.writeInt(u16, header.addManyAsArrayAssumeCapacity(@sizeOf(u16)), 5, dwarf.endian); header.appendSliceAssumeCapacity(&.{ @intFromEnum(dwarf.address_size), 0 }); std.mem.writeInt(u32, header.addManyAsArrayAssumeCapacity(@sizeOf(u32)), 1, dwarf.endian); - switch (dwarf.format) { - inline .@"32", .@"64" => |format| std.mem.writeInt( - SectionOffset(format), - header.addManyAsArrayAssumeCapacity(@sizeOf(SectionOffset(format))), - @sizeOf(SectionOffset(format)), - dwarf.endian, - ), - } + dwarf.writeInt(header.addManyAsSliceAssumeCapacity(dwarf.sectionOffsetBytes()), dwarf.sectionOffsetBytes() * 1); try unit.replaceHeader(&dwarf.debug_rnglists.section, dwarf, header.items); try unit.writeTrailer(&dwarf.debug_rnglists.section, dwarf); } dwarf.debug_rnglists.section.dirty = false; } + assert(!dwarf.debug_abbrev.section.dirty); + assert(!dwarf.debug_aranges.section.dirty); + assert(!dwarf.debug_info.section.dirty); + assert(!dwarf.debug_line.section.dirty); + assert(!dwarf.debug_line_str.section.dirty); + assert(!dwarf.debug_loclists.section.dirty); + assert(!dwarf.debug_rnglists.section.dirty); + assert(!dwarf.debug_str.section.dirty); } pub fn resolveRelocs(dwarf: *Dwarf) RelocError!void { @@ -3295,7 +3562,7 @@ fn DeclValEnum(comptime T: type) type { } }); } -const AbbrevCode = enum(u8) { +const AbbrevCode = enum { null, // padding codes must be one byte uleb128 values to function pad_1, @@ -3303,13 +3570,16 @@ const AbbrevCode = enum(u8) { // decl codes are assumed to all have the same uleb128 length decl_alias, decl_enum, + decl_empty_enum, decl_namespace_struct, decl_struct, decl_packed_struct, decl_union, decl_var, decl_func, - decl_func_empty, + decl_empty_func, + decl_func_generic, + decl_empty_func_generic, // the rest are unrestricted compile_unit, module, @@ -3317,6 +3587,7 @@ const AbbrevCode = enum(u8) { file, signed_enum_field, unsigned_enum_field, + big_enum_field, generated_field, struct_field, struct_field_comptime, @@ -3325,6 +3596,7 @@ const AbbrevCode = enum(u8) { tagged_union, signed_tagged_union_field, unsigned_tagged_union_field, + big_tagged_union_field, tagged_union_default_field, void_type, numeric_type, @@ -3343,11 +3615,15 @@ const AbbrevCode = enum(u8) { namespace_struct_type, struct_type, packed_struct_type, + empty_packed_struct_type, union_type, + empty_union_type, + empty_inlined_func, + inlined_func, local_arg, local_var, - const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_func_empty)); + const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_empty_func_generic)); const Attr = struct { DeclValEnum(DW.AT), @@ -3387,6 +3663,12 @@ const AbbrevCode = enum(u8) { .{ .type, .ref_addr }, }, }, + .decl_empty_enum = .{ + .tag = .enumeration_type, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, .decl_namespace_struct = .{ .tag = .structure_type, .attrs = decl_abbrev_common_attrs ++ .{ @@ -3439,7 +3721,7 @@ const AbbrevCode = enum(u8) { .{ .noreturn, .flag }, }, }, - .decl_func_empty = .{ + .decl_empty_func = .{ .tag = .subprogram, .attrs = decl_abbrev_common_attrs ++ .{ .{ .linkage_name, .strp }, @@ -3451,6 +3733,19 @@ const AbbrevCode = enum(u8) { .{ .noreturn, .flag }, }, }, + .decl_func_generic = .{ + .tag = .subprogram, + .children = true, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, + .decl_empty_func_generic = .{ + .tag = .subprogram, + .attrs = decl_abbrev_common_attrs ++ .{ + .{ .type, .ref_addr }, + }, + }, .compile_unit = .{ .tag = .compile_unit, .children = true, @@ -3504,6 +3799,13 @@ const AbbrevCode = enum(u8) { .{ .name, .strp }, }, }, + .big_enum_field = .{ + .tag = .enumerator, + .attrs = &.{ + .{ .const_value, .block }, + .{ .name, .strp }, + }, + }, .generated_field = .{ .tag = .member, .attrs = &.{ @@ -3567,6 +3869,13 @@ const AbbrevCode = enum(u8) { .{ .discr_value, .udata }, }, }, + .big_tagged_union_field = .{ + .tag = .variant, + .children = true, + .attrs = &.{ + .{ .discr_value, .block }, + }, + }, .tagged_union_default_field = .{ .tag = .variant, .children = true, @@ -3697,6 +4006,13 @@ const AbbrevCode = enum(u8) { .{ .type, .ref_addr }, }, }, + .empty_packed_struct_type = .{ + .tag = .structure_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .type, .ref_addr }, + }, + }, .union_type = .{ .tag = .union_type, .children = true, @@ -3706,6 +4022,35 @@ const AbbrevCode = enum(u8) { .{ .alignment, .udata }, }, }, + .empty_union_type = .{ + .tag = .union_type, + .attrs = &.{ + .{ .name, .strp }, + .{ .byte_size, .udata }, + .{ .alignment, .udata }, + }, + }, + .empty_inlined_func = .{ + .tag = .inlined_subroutine, + .attrs = &.{ + .{ .abstract_origin, .ref_addr }, + .{ .call_line, .udata }, + .{ .call_column, .udata }, + .{ .low_pc, .addr }, + .{ .high_pc, .addr }, + }, + }, + .inlined_func = .{ + .tag = .inlined_subroutine, + .children = true, + .attrs = &.{ + .{ .abstract_origin, .ref_addr }, + .{ .call_line, .udata }, + .{ .call_column, .udata }, + .{ .low_pc, .addr }, + .{ .high_pc, .addr }, + }, + }, .local_arg = .{ .tag = .formal_parameter, .attrs = &.{ @@ -3723,7 +4068,7 @@ const AbbrevCode = enum(u8) { }, }, .null = undefined, - }).values[1..].*; + }); }; fn getFile(dwarf: *Dwarf) ?std.fs.File { @@ -3732,11 +4077,11 @@ fn getFile(dwarf: *Dwarf) ?std.fs.File { } fn addCommonEntry(dwarf: *Dwarf, unit: Unit.Index) UpdateError!Entry.Index { - const entry = try dwarf.debug_aranges.section.addEntry(unit, dwarf); - assert(try dwarf.debug_info.section.addEntry(unit, dwarf) == entry); - assert(try dwarf.debug_line.section.addEntry(unit, dwarf) == entry); - assert(try dwarf.debug_loclists.section.addEntry(unit, dwarf) == entry); - assert(try dwarf.debug_rnglists.section.addEntry(unit, dwarf) == entry); + const entry = try dwarf.debug_aranges.section.getUnit(unit).addEntry(dwarf.gpa); + assert(try dwarf.debug_info.section.getUnit(unit).addEntry(dwarf.gpa) == entry); + assert(try dwarf.debug_line.section.getUnit(unit).addEntry(dwarf.gpa) == entry); + assert(try dwarf.debug_loclists.section.getUnit(unit).addEntry(dwarf.gpa) == entry); + assert(try dwarf.debug_rnglists.section.getUnit(unit).addEntry(dwarf.gpa) == entry); return entry; } @@ -3770,13 +4115,6 @@ fn sectionOffsetBytes(dwarf: *Dwarf) u32 { }; } -fn SectionOffset(comptime format: DW.Format) type { - return switch (format) { - .@"32" => u32, - .@"64" => u64, - }; -} - fn uleb128Bytes(value: anytype) u32 { var cw = std.io.countingWriter(std.io.null_writer); try uleb128(cw.writer(), value); diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 5a35e67b02..333e490d17 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -204,7 +204,7 @@ pub const SortSection = enum { name, alignment }; pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Elf { const target = comp.root_mod.resolved_target.result; @@ -321,7 +321,7 @@ pub fn createEmpty( // If using LLD to link, this code should produce an object file so that it // can be passed to LLD. const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - self.base.file = try emit.directory.handle.createFile(sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(use_lld, output_mode, link_mode), @@ -401,7 +401,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Elf { // TODO: restore saved linker state, don't truncate the file, and @@ -585,7 +585,7 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { const ptr_size = self.ptrWidthBytes(); const target = self.base.comp.root_mod.resolved_target.result; const ptr_bit_width = target.ptrBitWidth(); - const zig_object = self.zigObjectPtr().?; + const zo = self.zigObjectPtr().?; const fillSection = struct { fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) !void { @@ -766,7 +766,27 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_bss_section_index.?, .{}); } - if (zig_object.dwarf) |*dwarf| { + if (zo.dwarf) |*dwarf| { + const addSectionSymbol = struct { + fn addSectionSymbol( + zig_object: *ZigObject, + alloc: Allocator, + name: [:0]const u8, + alignment: Atom.Alignment, + shndx: u32, + ) !Symbol.Index { + const name_off = try zig_object.addString(alloc, name); + const index = try zig_object.newSymbolWithAtom(alloc, name_off); + const sym = zig_object.symbol(index); + const esym = &zig_object.symtab.items(.elf_sym)[sym.esym_index]; + esym.st_info |= elf.STT_SECTION; + const atom_ptr = zig_object.atom(sym.ref.index).?; + atom_ptr.alignment = alignment; + atom_ptr.output_section_index = shndx; + return index; + } + }.addSectionSymbol; + if (self.debug_str_section_index == null) { self.debug_str_section_index = try self.addSection(.{ .name = try self.insertShString(".debug_str"), @@ -775,7 +795,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_str_section_dirty = true; + zo.debug_str_section_dirty = true; + zo.debug_str_index = try addSectionSymbol(zo, gpa, ".debug_str", .@"1", self.debug_str_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_str_section_index.?, .{}); } @@ -785,7 +806,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_info_section_dirty = true; + zo.debug_info_section_dirty = true; + zo.debug_info_index = try addSectionSymbol(zo, gpa, ".debug_info", .@"1", self.debug_info_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_info_section_index.?, .{}); } @@ -795,7 +817,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_abbrev_section_dirty = true; + zo.debug_abbrev_section_dirty = true; + zo.debug_abbrev_index = try addSectionSymbol(zo, gpa, ".debug_abbrev", .@"1", self.debug_abbrev_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_abbrev_section_index.?, .{}); } @@ -805,7 +828,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 16, }); - zig_object.debug_aranges_section_dirty = true; + zo.debug_aranges_section_dirty = true; + zo.debug_aranges_index = try addSectionSymbol(zo, gpa, ".debug_aranges", .@"16", self.debug_aranges_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_aranges_section_index.?, .{}); } @@ -815,7 +839,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_line_section_dirty = true; + zo.debug_line_section_dirty = true; + zo.debug_line_index = try addSectionSymbol(zo, gpa, ".debug_line", .@"1", self.debug_line_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_line_section_index.?, .{}); } @@ -827,7 +852,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_line_str_section_dirty = true; + zo.debug_line_str_section_dirty = true; + zo.debug_line_str_index = try addSectionSymbol(zo, gpa, ".debug_line_str", .@"1", self.debug_line_str_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_line_str_section_index.?, .{}); } @@ -837,7 +863,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_loclists_section_dirty = true; + zo.debug_loclists_section_dirty = true; + zo.debug_loclists_index = try addSectionSymbol(zo, gpa, ".debug_loclists", .@"1", self.debug_loclists_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_loclists_section_index.?, .{}); } @@ -847,7 +874,8 @@ pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { .type = elf.SHT_PROGBITS, .addralign = 1, }); - zig_object.debug_rnglists_section_dirty = true; + zo.debug_rnglists_section_dirty = true; + zo.debug_rnglists_index = try addSectionSymbol(zo, gpa, ".debug_rnglists", .@"1", self.debug_rnglists_section_index.?); try self.output_sections.putNoClobber(gpa, self.debug_rnglists_section_index.?, .{}); } @@ -999,7 +1027,7 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod const target = comp.root_mod.resolved_target.result; const link_mode = comp.config.link_mode; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -1254,7 +1282,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod try self.addCommentString(); try self.finalizeMergeSections(); try self.initOutputSections(); - try self.initMergeSections(); if (self.linkerDefinedPtr()) |obj| { try obj.initStartStopSymbols(self); } @@ -1317,8 +1344,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod try self.base.file.?.pwriteAll(code, file_offset); } - if (zo.dwarf) |*dwarf| try dwarf.resolveRelocs(); - if (has_reloc_errors) return error.FlushFailure; } @@ -1356,7 +1381,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { const target = self.base.comp.root_mod.resolved_target.result; const link_mode = self.base.comp.config.link_mode; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -2054,7 +2079,7 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s const comp = self.base.comp; const gpa = comp.gpa; - const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it @@ -2652,15 +2677,6 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s } } -fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { - const target = self.base.comp.root_mod.resolved_target.result; - const target_endian = target.cpu.arch.endian(); - switch (self.ptr_width) { - .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @as(u32, @intCast(addr)), target_endian), - .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian), - } -} - pub fn writeShdrTable(self: *Elf) !void { const gpa = self.base.comp.gpa; const target = self.base.comp.root_mod.resolved_target.result; @@ -3032,16 +3048,16 @@ pub fn finalizeMergeSections(self: *Elf) !void { pub fn updateMergeSectionSizes(self: *Elf) !void { for (self.merge_sections.items) |*msec| { + msec.updateSize(); + } + for (self.merge_sections.items) |*msec| { const shdr = &self.shdrs.items[msec.output_section_index]; - for (msec.finalized_subsections.items) |msub_index| { - const msub = msec.mergeSubsection(msub_index); - assert(msub.alive); - const offset = msub.alignment.forward(shdr.sh_size); - const padding = offset - shdr.sh_size; - msub.value = @intCast(offset); - shdr.sh_size += padding + msub.size; - shdr.sh_addralign = @max(shdr.sh_addralign, msub.alignment.toByteUnits() orelse 1); - } + const offset = msec.alignment.forward(shdr.sh_size); + const padding = offset - shdr.sh_size; + msec.value = @intCast(offset); + shdr.sh_size += padding + msec.size; + shdr.sh_addralign = @max(shdr.sh_addralign, msec.alignment.toByteUnits() orelse 1); + shdr.sh_entsize = if (shdr.sh_entsize == 0) msec.entsize else @min(shdr.sh_entsize, msec.entsize); } } @@ -3052,7 +3068,8 @@ pub fn writeMergeSections(self: *Elf) !void { for (self.merge_sections.items) |*msec| { const shdr = self.shdrs.items[msec.output_section_index]; - const size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; + const fileoff = math.cast(usize, msec.value + shdr.sh_offset) orelse return error.Overflow; + const size = math.cast(usize, msec.size) orelse return error.Overflow; try buffer.ensureTotalCapacity(size); buffer.appendNTimesAssumeCapacity(0, size); @@ -3064,7 +3081,7 @@ pub fn writeMergeSections(self: *Elf) !void { @memcpy(buffer.items[off..][0..string.len], string); } - try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset); + try self.base.file.?.pwriteAll(buffer.items, fileoff); buffer.clearRetainingCapacity(); } } @@ -3073,26 +3090,9 @@ fn initOutputSections(self: *Elf) !void { for (self.objects.items) |index| { try self.file(index).?.object.initOutputSections(self); } -} - -pub fn initMergeSections(self: *Elf) !void { for (self.merge_sections.items) |*msec| { if (msec.finalized_subsections.items.len == 0) continue; - const name = msec.name(self); - const shndx = self.sectionByName(name) orelse try self.addSection(.{ - .name = msec.name_offset, - .type = msec.type, - .flags = msec.flags, - }); - msec.output_section_index = shndx; - - var entsize = msec.mergeSubsection(msec.finalized_subsections.items[0]).entsize; - for (msec.finalized_subsections.items) |msub_index| { - const msub = msec.mergeSubsection(msub_index); - entsize = @min(entsize, msub.entsize); - } - const shdr = &self.shdrs.items[shndx]; - shdr.sh_entsize = entsize; + try msec.initOutputSection(self); } } @@ -4184,26 +4184,21 @@ pub fn allocateNonAllocSections(self: *Elf) !void { shdr.sh_offset, new_offset, }); - const zig_object = self.zigObjectPtr().?; - const existing_size = blk: { - if (shndx == self.debug_info_section_index.?) - break :blk zig_object.debug_info_section_zig_size; - if (shndx == self.debug_abbrev_section_index.?) - break :blk zig_object.debug_abbrev_section_zig_size; - if (shndx == self.debug_str_section_index.?) - break :blk zig_object.debug_str_section_zig_size; - if (shndx == self.debug_aranges_section_index.?) - break :blk zig_object.debug_aranges_section_zig_size; - if (shndx == self.debug_line_section_index.?) - break :blk zig_object.debug_line_section_zig_size; - if (shndx == self.debug_line_str_section_index.?) - break :blk zig_object.debug_line_str_section_zig_size; - if (shndx == self.debug_loclists_section_index.?) - break :blk zig_object.debug_loclists_section_zig_size; - if (shndx == self.debug_rnglists_section_index.?) - break :blk zig_object.debug_rnglists_section_zig_size; - unreachable; - }; + const zo = self.zigObjectPtr().?; + const existing_size = for ([_]Symbol.Index{ + zo.debug_info_index.?, + zo.debug_abbrev_index.?, + zo.debug_aranges_index.?, + zo.debug_str_index.?, + zo.debug_line_index.?, + zo.debug_line_str_index.?, + zo.debug_loclists_index.?, + zo.debug_rnglists_index.?, + }) |sym_index| { + const sym = zo.symbol(sym_index); + const atom_ptr = sym.atom(self).?; + if (atom_ptr.output_section_index == shndx) break atom_ptr.size; + } else 0; const amt = try self.base.file.?.copyRangeAll( shdr.sh_offset, self.base.file.?, @@ -4299,24 +4294,21 @@ fn writeAtoms(self: *Elf) !void { // TODO really, really handle debug section separately const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: { - const zig_object = self.zigObjectPtr().?; - if (shndx == self.debug_info_section_index.?) - break :blk zig_object.debug_info_section_zig_size; - if (shndx == self.debug_abbrev_section_index.?) - break :blk zig_object.debug_abbrev_section_zig_size; - if (shndx == self.debug_str_section_index.?) - break :blk zig_object.debug_str_section_zig_size; - if (shndx == self.debug_aranges_section_index.?) - break :blk zig_object.debug_aranges_section_zig_size; - if (shndx == self.debug_line_section_index.?) - break :blk zig_object.debug_line_section_zig_size; - if (shndx == self.debug_line_str_section_index.?) - break :blk zig_object.debug_line_str_section_zig_size; - if (shndx == self.debug_loclists_section_index.?) - break :blk zig_object.debug_loclists_section_zig_size; - if (shndx == self.debug_rnglists_section_index.?) - break :blk zig_object.debug_rnglists_section_zig_size; - unreachable; + const zo = self.zigObjectPtr().?; + break :blk for ([_]Symbol.Index{ + zo.debug_info_index.?, + zo.debug_abbrev_index.?, + zo.debug_aranges_index.?, + zo.debug_str_index.?, + zo.debug_line_index.?, + zo.debug_line_str_index.?, + zo.debug_loclists_index.?, + zo.debug_rnglists_index.?, + }) |sym_index| { + const sym = zo.symbol(sym_index); + const atom_ptr = sym.atom(self).?; + if (atom_ptr.output_section_index == shndx) break atom_ptr.size; + } else 0; } else 0; const sh_offset = shdr.sh_offset + base_offset; const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow; @@ -4410,7 +4402,6 @@ pub fn updateSymtabSize(self: *Elf) !void { if (self.eh_frame_section_index) |_| { nlocals += 1; } - nlocals += @intCast(self.merge_sections.items.len); if (self.requiresThunks()) for (self.thunks.items) |*th| { th.output_symtab_ctx.ilocal = nlocals + 1; @@ -4734,30 +4725,12 @@ fn writeSectionSymbols(self: *Elf) void { }; ilocal += 1; } - - for (self.merge_sections.items) |msec| { - const shdr = self.shdrs.items[msec.output_section_index]; - const out_sym = &self.symtab.items[ilocal]; - out_sym.* = .{ - .st_name = 0, - .st_value = shdr.sh_addr, - .st_info = elf.STT_SECTION, - .st_shndx = @intCast(msec.output_section_index), - .st_size = 0, - .st_other = 0, - }; - ilocal += 1; - } } pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 { if (self.eh_frame_section_index) |index| { if (index == shndx) return @intCast(self.output_sections.keys().len + 1); } - const base: usize = if (self.eh_frame_section_index == null) 0 else 1; - for (self.merge_sections.items, 0..) |msec, index| { - if (msec.output_section_index == shndx) return @intCast(self.output_sections.keys().len + 1 + index + base); - } return @intCast(self.output_sections.getIndex(shndx).? + 1); } @@ -5520,10 +5493,11 @@ fn formatShdr( _ = options; _ = unused_fmt_string; const shdr = ctx.shdr; - try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : flags({})", .{ + try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({})", .{ ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset, shdr.sh_addr, shdr.sh_addralign, - shdr.sh_size, fmtShdrFlags(shdr.sh_flags), + shdr.sh_size, shdr.sh_entsize, + fmtShdrFlags(shdr.sh_flags), }); } @@ -6016,6 +5990,7 @@ const Allocator = std.mem.Allocator; const Archive = @import("Elf/Archive.zig"); pub const Atom = @import("Elf/Atom.zig"); const Cache = std.Build.Cache; +const Path = Cache.Path; const Compilation = @import("../Compilation.zig"); const ComdatGroupSection = synthetic_sections.ComdatGroupSection; const CopyRelSection = synthetic_sections.CopyRelSection; diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index a9050dcd00..3eb447ab75 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -302,7 +302,7 @@ pub fn free(self: *Atom, elf_file: *Elf) void { } // TODO create relocs free list - self.freeRelocs(elf_file); + self.freeRelocs(zo); // TODO figure out how to free input section mappind in ZigModule // const zig_object = elf_file.zigObjectPtr().? // assert(zig_object.atoms.swapRemove(self.atom_index)); @@ -377,21 +377,19 @@ pub fn markFdesDead(self: Atom, elf_file: *Elf) void { } } -pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - const file_ptr = self.file(elf_file).?; - assert(file_ptr == .zig_object); - const zig_object = file_ptr.zig_object; - const rels = &zig_object.relocs.items[self.relocs_section_index]; - try rels.append(gpa, reloc); +pub fn addReloc(self: Atom, alloc: Allocator, reloc: elf.Elf64_Rela, zo: *ZigObject) !void { + const rels = &zo.relocs.items[self.relocs_section_index]; + try rels.ensureUnusedCapacity(alloc, 1); + self.addRelocAssumeCapacity(reloc, zo); } -pub fn freeRelocs(self: Atom, elf_file: *Elf) void { - const file_ptr = self.file(elf_file).?; - assert(file_ptr == .zig_object); - const zig_object = file_ptr.zig_object; - zig_object.relocs.items[self.relocs_section_index].clearRetainingCapacity(); +pub fn addRelocAssumeCapacity(self: Atom, reloc: elf.Elf64_Rela, zo: *ZigObject) void { + const rels = &zo.relocs.items[self.relocs_section_index]; + rels.appendAssumeCapacity(reloc); +} + +pub fn freeRelocs(self: Atom, zo: *ZigObject) void { + zo.relocs.items[self.relocs_section_index].clearRetainingCapacity(); } pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index d592f6e9d3..ef6ec27b41 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -50,16 +50,14 @@ debug_line_str_section_dirty: bool = false, debug_loclists_section_dirty: bool = false, debug_rnglists_section_dirty: bool = false, -/// Size contribution of Zig's metadata to each debug section. -/// Used to track start of metadata from input object files. -debug_info_section_zig_size: u64 = 0, -debug_abbrev_section_zig_size: u64 = 0, -debug_str_section_zig_size: u64 = 0, -debug_aranges_section_zig_size: u64 = 0, -debug_line_section_zig_size: u64 = 0, -debug_line_str_section_zig_size: u64 = 0, -debug_loclists_section_zig_size: u64 = 0, -debug_rnglists_section_zig_size: u64 = 0, +debug_info_index: ?Symbol.Index = null, +debug_abbrev_index: ?Symbol.Index = null, +debug_aranges_index: ?Symbol.Index = null, +debug_str_index: ?Symbol.Index = null, +debug_line_index: ?Symbol.Index = null, +debug_line_str_index: ?Symbol.Index = null, +debug_loclists_index: ?Symbol.Index = null, +debug_rnglists_index: ?Symbol.Index = null, pub const global_symbol_bit: u32 = 0x80000000; pub const symbol_mask: u32 = 0x7fffffff; @@ -172,12 +170,209 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi const pt: Zcu.PerThread = .{ .zcu = elf_file.base.comp.module.?, .tid = tid }; try dwarf.flushModule(pt); + const gpa = elf_file.base.comp.gpa; + const cpu_arch = elf_file.getTarget().cpu.arch; + + // TODO invert this logic so that we manage the output section with the atom, not the + // other way around + for ([_]u32{ + self.debug_info_index.?, + self.debug_abbrev_index.?, + self.debug_str_index.?, + self.debug_aranges_index.?, + self.debug_line_index.?, + self.debug_line_str_index.?, + self.debug_loclists_index.?, + self.debug_rnglists_index.?, + }, [_]*Dwarf.Section{ + &dwarf.debug_info.section, + &dwarf.debug_abbrev.section, + &dwarf.debug_str.section, + &dwarf.debug_aranges.section, + &dwarf.debug_line.section, + &dwarf.debug_line_str.section, + &dwarf.debug_loclists.section, + &dwarf.debug_rnglists.section, + }) |sym_index, sect| { + const sym = self.symbol(sym_index); + const atom_ptr = self.atom(sym.ref.index).?; + if (!atom_ptr.alive) continue; + const shndx = sym.outputShndx(elf_file).?; + const shdr = elf_file.shdrs.items[shndx]; + const esym = &self.symtab.items(.elf_sym)[sym.esym_index]; + esym.st_size = shdr.sh_size; + atom_ptr.size = shdr.sh_size; + atom_ptr.alignment = Atom.Alignment.fromNonzeroByteUnits(shdr.sh_addralign); + + log.debug("parsing relocs in {s}", .{sym.name(elf_file)}); + + const relocs = &self.relocs.items[atom_ptr.relocsShndx().?]; + for (sect.units.items) |*unit| { + try relocs.ensureUnusedCapacity(gpa, unit.cross_unit_relocs.items.len + + unit.cross_section_relocs.items.len); + for (unit.cross_unit_relocs.items) |reloc| { + const target_unit = sect.getUnit(reloc.target_unit); + const r_offset = unit.off + reloc.source_off; + const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry| + target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off + else + 0)); + const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch); + log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{ + self.symbol(sym_index).name(elf_file), + r_offset, + r_addend, + relocation.fmtRelocType(r_type, cpu_arch), + }); + atom_ptr.addRelocAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + }, self); + } + for (unit.cross_section_relocs.items) |reloc| { + const target_sym_index = switch (reloc.target_sec) { + .debug_abbrev => self.debug_abbrev_index.?, + .debug_info => self.debug_info_index.?, + .debug_line => self.debug_line_index.?, + .debug_line_str => self.debug_line_str_index.?, + .debug_loclists => self.debug_loclists_index.?, + .debug_rnglists => self.debug_rnglists_index.?, + .debug_str => self.debug_str_index.?, + }; + const target_sec = switch (reloc.target_sec) { + inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section, + }; + const target_unit = target_sec.getUnit(reloc.target_unit); + const r_offset = unit.off + reloc.source_off; + const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry| + target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off + else + 0)); + const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch); + log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{ + self.symbol(target_sym_index).name(elf_file), + r_offset, + r_addend, + relocation.fmtRelocType(r_type, cpu_arch), + }); + atom_ptr.addRelocAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(target_sym_index)) << 32) | r_type, + }, self); + } + + for (unit.entries.items) |*entry| { + const entry_off = unit.off + unit.header_len + entry.off; + + try relocs.ensureUnusedCapacity(gpa, entry.cross_entry_relocs.items.len + + entry.cross_unit_relocs.items.len + entry.cross_section_relocs.items.len + + entry.external_relocs.items.len); + for (entry.cross_entry_relocs.items) |reloc| { + const r_offset = entry_off + reloc.source_off; + const r_addend: i64 = @intCast(unit.off + reloc.target_off + unit.header_len + unit.getEntry(reloc.target_entry).assertNonEmpty(unit, sect, dwarf).off); + const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch); + log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{ + self.symbol(sym_index).name(elf_file), + r_offset, + r_addend, + relocation.fmtRelocType(r_type, cpu_arch), + }); + atom_ptr.addRelocAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + }, self); + } + for (entry.cross_unit_relocs.items) |reloc| { + const target_unit = sect.getUnit(reloc.target_unit); + const r_offset = entry_off + reloc.source_off; + const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry| + target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off + else + 0)); + const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch); + log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{ + self.symbol(sym_index).name(elf_file), + r_offset, + r_addend, + relocation.fmtRelocType(r_type, cpu_arch), + }); + atom_ptr.addRelocAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, + }, self); + } + for (entry.cross_section_relocs.items) |reloc| { + const target_sym_index = switch (reloc.target_sec) { + .debug_abbrev => self.debug_abbrev_index.?, + .debug_info => self.debug_info_index.?, + .debug_line => self.debug_line_index.?, + .debug_line_str => self.debug_line_str_index.?, + .debug_loclists => self.debug_loclists_index.?, + .debug_rnglists => self.debug_rnglists_index.?, + .debug_str => self.debug_str_index.?, + }; + const target_sec = switch (reloc.target_sec) { + inline else => |target_sec| &@field(dwarf, @tagName(target_sec)).section, + }; + const target_unit = target_sec.getUnit(reloc.target_unit); + const r_offset = entry_off + reloc.source_off; + const r_addend: i64 = @intCast(target_unit.off + reloc.target_off + (if (reloc.target_entry.unwrap()) |target_entry| + target_unit.header_len + target_unit.getEntry(target_entry).assertNonEmpty(unit, sect, dwarf).off + else + 0)); + const r_type = relocation.dwarf.crossSectionRelocType(dwarf.format, cpu_arch); + log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{ + self.symbol(target_sym_index).name(elf_file), + r_offset, + r_addend, + relocation.fmtRelocType(r_type, cpu_arch), + }); + atom_ptr.addRelocAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(target_sym_index)) << 32) | r_type, + }, self); + } + for (entry.external_relocs.items) |reloc| { + const target_sym = self.symbol(reloc.target_sym); + const r_offset = entry_off + reloc.source_off; + const r_addend: i64 = @intCast(reloc.target_off); + const r_type = relocation.dwarf.externalRelocType(target_sym.*, dwarf.address_size, cpu_arch); + log.debug(" {s} <- r_off={x}, r_add={x}, r_type={}", .{ + target_sym.name(elf_file), + r_offset, + r_addend, + relocation.fmtRelocType(r_type, cpu_arch), + }); + atom_ptr.addRelocAssumeCapacity(.{ + .r_offset = r_offset, + .r_addend = r_addend, + .r_info = (@as(u64, @intCast(reloc.target_sym)) << 32) | r_type, + }, self); + } + } + } + + if (elf_file.base.isRelocatable() and relocs.items.len > 0) { + const gop = try elf_file.output_rela_sections.getOrPut(gpa, shndx); + if (!gop.found_existing) { + const rela_sect_name = try std.fmt.allocPrintZ(gpa, ".rela{s}", .{elf_file.getShString(shdr.sh_name)}); + defer gpa.free(rela_sect_name); + const rela_sh_name = try elf_file.insertShString(rela_sect_name); + const rela_shndx = try elf_file.addRelaShdr(rela_sh_name, shndx); + gop.value_ptr.* = .{ .shndx = rela_shndx }; + } + } + } + self.debug_abbrev_section_dirty = false; self.debug_aranges_section_dirty = false; self.debug_rnglists_section_dirty = false; self.debug_str_section_dirty = false; - - self.saveDebugSectionsSizes(elf_file); } // The point of flushModule() is to commit changes, so in theory, nothing should @@ -190,33 +385,6 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !voi assert(!self.debug_str_section_dirty); } -fn saveDebugSectionsSizes(self: *ZigObject, elf_file: *Elf) void { - if (elf_file.debug_info_section_index) |shndx| { - self.debug_info_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_abbrev_section_index) |shndx| { - self.debug_abbrev_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_str_section_index) |shndx| { - self.debug_str_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_aranges_section_index) |shndx| { - self.debug_aranges_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_line_section_index) |shndx| { - self.debug_line_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_line_str_section_index) |shndx| { - self.debug_line_str_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_loclists_section_index) |shndx| { - self.debug_loclists_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } - if (elf_file.debug_rnglists_section_index) |shndx| { - self.debug_rnglists_section_zig_size = elf_file.shdrs.items[shndx].sh_size; - } -} - fn newSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, st_bind: u4) !Symbol.Index { try self.symtab.ensureUnusedCapacity(allocator, 1); try self.symbols.ensureUnusedCapacity(allocator, 1); @@ -278,7 +446,7 @@ fn newAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Atom.Index { return index; } -fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { +pub fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index { const atom_index = try self.newAtom(allocator, name_off); const sym_index = try self.newLocalSymbol(allocator, name_off); const sym = self.symbol(sym_index); @@ -642,11 +810,11 @@ pub fn getNavVAddr( const vaddr = this_sym.address(.{}, elf_file); const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?; const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); - try parent_atom.addReloc(elf_file, .{ + try parent_atom.addReloc(elf_file.base.comp.gpa, .{ .r_offset = reloc_info.offset, .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type, .r_addend = reloc_info.addend, - }); + }, self); return @intCast(vaddr); } @@ -661,11 +829,11 @@ pub fn getUavVAddr( const vaddr = sym.address(.{}, elf_file); const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?; const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch); - try parent_atom.addReloc(elf_file, .{ + try parent_atom.addReloc(elf_file.base.comp.gpa, .{ .r_offset = reloc_info.offset, .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type, .r_addend = reloc_info.addend, - }); + }, self); return @intCast(vaddr); } @@ -1012,7 +1180,7 @@ pub fn updateFunc( log.debug("updateFunc {}({d})", .{ ip.getNav(func.owner_nav).fqn.fmt(ip), func.owner_nav }); const sym_index = try self.getOrCreateMetadataForNav(elf_file, func.owner_nav); - self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); + self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1122,9 +1290,9 @@ pub fn updateNav( log.debug("updateNav {}({d})", .{ nav.fqn.fmt(ip), nav_index }); - const nav_val = zcu.navValue(nav_index); - const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { - .variable => |variable| Value.fromInterned(variable.init), + const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) { + .func => .none, + .variable => |variable| variable.init, .@"extern" => |@"extern"| { if (ip.isFunctionType(@"extern".ty)) return; const sym_index = try self.getGlobalSymbol( @@ -1135,12 +1303,12 @@ pub fn updateNav( self.symbol(sym_index).flags.is_extern_ptr = true; return; }, - else => nav_val, + else => nav.status.resolved.val, }; - if (nav_init.typeOf(zcu).isFnOrHasRuntimeBits(pt)) { + if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(pt)) { const sym_index = try self.getOrCreateMetadataForNav(elf_file, nav_index); - self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); + self.symbol(sym_index).atom(elf_file).?.freeRelocs(self); var code_buffer = std.ArrayList(u8).init(zcu.gpa); defer code_buffer.deinit(); @@ -1153,7 +1321,7 @@ pub fn updateNav( &elf_file.base, pt, zcu.navSrcLoc(nav_index), - nav_init, + Value.fromInterned(nav_init), &code_buffer, if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, .{ .parent_atom_index = sym_index }, @@ -1529,7 +1697,7 @@ pub fn asFile(self: *ZigObject) File { return .{ .zig_object = self }; } -fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !u32 { +pub fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !u32 { return self.strtab.insert(allocator, string); } diff --git a/src/link/Elf/merge_section.zig b/src/link/Elf/merge_section.zig index baec316c3d..7ffb17e963 100644 --- a/src/link/Elf/merge_section.zig +++ b/src/link/Elf/merge_section.zig @@ -1,4 +1,8 @@ pub const MergeSection = struct { + value: u64 = 0, + size: u64 = 0, + alignment: Atom.Alignment = .@"1", + entsize: u32 = 0, name_offset: u32 = 0, type: u32 = 0, flags: u64 = 0, @@ -26,7 +30,7 @@ pub const MergeSection = struct { pub fn address(msec: MergeSection, elf_file: *Elf) i64 { const shdr = elf_file.shdrs.items[msec.output_section_index]; - return @intCast(shdr.sh_addr); + return @intCast(shdr.sh_addr + msec.value); } const InsertResult = struct { @@ -90,6 +94,29 @@ pub const MergeSection = struct { std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn); } + pub fn updateSize(msec: *MergeSection) void { + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.mergeSubsection(msub_index); + assert(msub.alive); + const offset = msub.alignment.forward(msec.size); + const padding = offset - msec.size; + msub.value = @intCast(offset); + msec.size += padding + msub.size; + msec.alignment = msec.alignment.max(msub.alignment); + msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize); + } + } + + pub fn initOutputSection(msec: *MergeSection, elf_file: *Elf) !void { + const shndx = elf_file.sectionByName(msec.name(elf_file)) orelse try elf_file.addSection(.{ + .name = msec.name_offset, + .type = msec.type, + .flags = msec.flags, + }); + try elf_file.output_sections.put(elf_file.base.comp.gpa, shndx, .{}); + msec.output_section_index = shndx; + } + pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index { const index: MergeSubsection.Index = @intCast(msec.subsections.items.len); const msub = try msec.subsections.addOne(allocator); @@ -163,9 +190,12 @@ pub const MergeSection = struct { _ = unused_fmt_string; const msec = ctx.msec; const elf_file = ctx.elf_file; - try writer.print("{s} : @{x} : type({x}) : flags({x})\n", .{ + try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ msec.name(elf_file), msec.address(elf_file), + msec.size, + msec.alignment.toByteUnits() orelse 0, + msec.entsize, msec.type, msec.flags, }); diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 213b43a95d..50b9b562d1 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -42,7 +42,11 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co try elf_file.finalizeMergeSections(); zig_object.claimUnresolvedObject(elf_file); - try elf_file.initMergeSections(); + for (elf_file.merge_sections.items) |*msec| { + if (msec.finalized_subsections.items.len == 0) continue; + try msec.initOutputSection(elf_file); + } + try elf_file.initSymtab(); try elf_file.initShStrtab(); try elf_file.sortShdrs(); @@ -198,7 +202,6 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const claimUnresolved(elf_file); try initSections(elf_file); - try elf_file.initMergeSections(); try elf_file.sortShdrs(); if (elf_file.zigObjectPtr()) |zig_object| { try zig_object.addAtomsToRelaSections(elf_file); @@ -294,6 +297,11 @@ fn initSections(elf_file: *Elf) !void { try object.initRelaSections(elf_file); } + for (elf_file.merge_sections.items) |*msec| { + if (msec.finalized_subsections.items.len == 0) continue; + try msec.initOutputSection(elf_file); + } + const needs_eh_frame = for (elf_file.objects.items) |index| { if (elf_file.file(index).?.object.cies.items.len > 0) break true; } else false; @@ -423,24 +431,21 @@ fn writeAtoms(elf_file: *Elf) !void { // TODO really, really handle debug section separately const base_offset = if (elf_file.isDebugSection(@intCast(shndx))) blk: { - const zig_object = elf_file.zigObjectPtr().?; - if (shndx == elf_file.debug_info_section_index.?) - break :blk zig_object.debug_info_section_zig_size; - if (shndx == elf_file.debug_abbrev_section_index.?) - break :blk zig_object.debug_abbrev_section_zig_size; - if (shndx == elf_file.debug_str_section_index.?) - break :blk zig_object.debug_str_section_zig_size; - if (shndx == elf_file.debug_aranges_section_index.?) - break :blk zig_object.debug_aranges_section_zig_size; - if (shndx == elf_file.debug_line_section_index.?) - break :blk zig_object.debug_line_section_zig_size; - if (shndx == elf_file.debug_line_str_section_index.?) - break :blk zig_object.debug_line_str_section_zig_size; - if (shndx == elf_file.debug_loclists_section_index.?) - break :blk zig_object.debug_loclists_section_zig_size; - if (shndx == elf_file.debug_rnglists_section_index.?) - break :blk zig_object.debug_rnglists_section_zig_size; - unreachable; + const zo = elf_file.zigObjectPtr().?; + break :blk for ([_]Symbol.Index{ + zo.debug_info_index.?, + zo.debug_abbrev_index.?, + zo.debug_aranges_index.?, + zo.debug_str_index.?, + zo.debug_line_index.?, + zo.debug_line_str_index.?, + zo.debug_loclists_index.?, + zo.debug_rnglists_index.?, + }) |sym_index| { + const sym = zo.symbol(sym_index); + const atom_ptr = sym.atom(elf_file).?; + if (atom_ptr.output_section_index == shndx) break atom_ptr.size; + } else 0; } else 0; const sh_offset = shdr.sh_offset + base_offset; const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow; @@ -586,3 +591,4 @@ const Compilation = @import("../../Compilation.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Object = @import("Object.zig"); +const Symbol = @import("Symbol.zig"); diff --git a/src/link/Elf/relocation.zig b/src/link/Elf/relocation.zig index e1a317f67d..d6f8dc5d10 100644 --- a/src/link/Elf/relocation.zig +++ b/src/link/Elf/relocation.zig @@ -91,6 +91,44 @@ pub fn encode(comptime kind: Kind, cpu_arch: std.Target.Cpu.Arch) u32 { }; } +pub const dwarf = struct { + pub fn crossSectionRelocType(format: DW.Format, cpu_arch: std.Target.Cpu.Arch) u32 { + return switch (cpu_arch) { + .x86_64 => @intFromEnum(switch (format) { + .@"32" => elf.R_X86_64.@"32", + .@"64" => .@"64", + }), + .riscv64 => @intFromEnum(switch (format) { + .@"32" => elf.R_RISCV.@"32", + .@"64" => .@"64", + }), + else => @panic("TODO unhandled cpu arch"), + }; + } + + pub fn externalRelocType( + target: Symbol, + address_size: Dwarf.AddressSize, + cpu_arch: std.Target.Cpu.Arch, + ) u32 { + return switch (cpu_arch) { + .x86_64 => @intFromEnum(switch (address_size) { + .@"32" => if (target.flags.is_tls) elf.R_X86_64.DTPOFF32 else .@"32", + .@"64" => if (target.flags.is_tls) elf.R_X86_64.DTPOFF64 else .@"64", + else => unreachable, + }), + .riscv64 => @intFromEnum(switch (address_size) { + .@"32" => elf.R_RISCV.@"32", + .@"64" => elf.R_RISCV.@"64", + else => unreachable, + }), + else => @panic("TODO unhandled cpu arch"), + }; + } + + const DW = std.dwarf; +}; + const FormatRelocTypeCtx = struct { r_type: u32, cpu_arch: std.Target.Cpu.Arch, @@ -124,4 +162,6 @@ const assert = std.debug.assert; const elf = std.elf; const std = @import("std"); +const Dwarf = @import("../Dwarf.zig"); const Elf = @import("../Elf.zig"); +const Symbol = @import("Symbol.zig"); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index f3a6ffc58d..d99fc2185e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -156,7 +156,7 @@ pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*MachO { const target = comp.root_mod.resolved_target.result; @@ -221,7 +221,7 @@ pub fn createEmpty( } errdefer self.base.destroy(); - self.base.file = try emit.directory.handle.createFile(emit.sub_path, .{ + self.base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, .mode = link.File.determineMode(false, output_mode, link_mode), @@ -260,7 +260,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*MachO { // TODO: restore saved linker state, don't truncate the file, and @@ -353,7 +353,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n const sub_prog_node = prog_node.start("MachO Flush", 0); defer sub_prog_node.end(); - const directory = self.base.emit.directory; + const directory = self.base.emit.root_dir; const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -586,7 +586,7 @@ pub fn flushModule(self: *MachO, arena: Allocator, tid: Zcu.PerThread.Id, prog_n if (codesig) |*csig| { try self.writeCodeSignature(csig); // code signing always comes last const emit = self.base.emit; - try invalidateKernelCache(emit.directory.handle, emit.sub_path); + try invalidateKernelCache(emit.root_dir.handle, emit.sub_path); } } @@ -597,7 +597,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const directory = self.base.emit.directory; + const directory = self.base.emit.root_dir; const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -1909,7 +1909,7 @@ fn calcSectionSizes(self: *MachO) !void { } } - // At this point, we can also calculate symtab and data-in-code linkedit section sizes + // At this point, we can also calculate most of the symtab and data-in-code linkedit section sizes if (self.getZigObject()) |zo| { tp.spawnWg(&wg, File.calcSymtabSize, .{ zo.asFile(), self }); } @@ -2463,6 +2463,9 @@ fn writeSectionsAndUpdateLinkeditSizes(self: *MachO) !void { if (self.getInternalObject()) |obj| { tp.spawnWg(&wg, File.writeSymtab, .{ obj.asFile(), self, self }); } + if (self.requiresThunks()) for (self.thunks.items) |th| { + tp.spawnWg(&wg, Thunk.writeSymtab, .{ th, self, self }); + }; } if (self.has_errors.swap(false, .seq_cst)) return error.FlushFailure; @@ -2725,6 +2728,14 @@ fn calcSymtabSize(self: *MachO) !void { var nimports: u32 = 0; var strsize: u32 = 1; + if (self.requiresThunks()) for (self.thunks.items) |*th| { + th.output_symtab_ctx.ilocal = nlocals; + th.output_symtab_ctx.stroff = strsize; + th.calcSymtabSize(self); + nlocals += th.output_symtab_ctx.nlocals; + strsize += th.output_symtab_ctx.strsize; + }; + for (files.items) |index| { const file = self.getFile(index).?; const ctx = switch (file) { @@ -3199,7 +3210,7 @@ fn copyRangeAllZeroOut(self: *MachO, old_offset: u64, new_offset: u64, size: u64 } const InitMetadataOptions = struct { - emit: Compilation.Emit, + emit: Path, zo: *ZigObject, symbol_count_hint: u64, program_code_size_hint: u64, @@ -3271,7 +3282,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void { ); defer gpa.free(d_sym_path); - var d_sym_bundle = try options.emit.directory.handle.makeOpenPath(d_sym_path, .{}); + var d_sym_bundle = try options.emit.root_dir.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(); const d_sym_file = try d_sym_bundle.createFile(options.emit.sub_path, .{ @@ -4603,6 +4614,7 @@ pub const Atom = @import("MachO/Atom.zig"); const AtomicBool = std.atomic.Value(bool); const Bind = bind.Bind; const Cache = std.Build.Cache; +const Path = Cache.Path; const CodeSignature = @import("MachO/CodeSignature.zig"); const Compilation = @import("../Compilation.zig"); const DataInCode = synthetic.DataInCode; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index 4827dae268..d6b77243a1 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -869,10 +869,11 @@ pub fn updateNav( const zcu = pt.zcu; const ip = &zcu.intern_pool; + const nav = ip.getNav(nav_index); - const nav_val = zcu.navValue(nav_index); - const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { - .variable => |variable| Value.fromInterned(variable.init), + const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) { + .func => .none, + .variable => |variable| variable.init, .@"extern" => |@"extern"| { if (ip.isFunctionType(@"extern".ty)) return; // Extern variable gets a __got entry only @@ -883,10 +884,10 @@ pub fn updateNav( sym.flags.is_extern_ptr = true; return; }, - else => nav_val, + else => nav.status.resolved.val, }; - if (nav_init.typeOf(zcu).isFnOrHasRuntimeBits(pt)) { + if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(pt)) { const sym_index = try self.getOrCreateMetadataForNav(macho_file, nav_index); self.symbols.items[sym_index].getAtom(macho_file).?.freeRelocs(macho_file); @@ -900,7 +901,7 @@ pub fn updateNav( &macho_file.base, pt, zcu.navSrcLoc(nav_index), - nav_init, + Value.fromInterned(nav_init), &code_buffer, if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none, .{ .parent_atom_index = sym_index }, diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index 74d0c58cbd..a891bedbd6 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -53,7 +53,7 @@ pub fn calcLoadCommandsSize(macho_file: *MachO, assume_max_path_len: bool) !u32 if (macho_file.base.isDynLib()) { const emit = macho_file.base.emit; const install_name = macho_file.install_name orelse - try emit.directory.join(gpa, &.{emit.sub_path}); + try emit.root_dir.join(gpa, &.{emit.sub_path}); defer if (macho_file.install_name == null) gpa.free(install_name); sizeofcmds += calcInstallNameLen( @sizeOf(macho.dylib_command), @@ -237,7 +237,7 @@ pub fn writeDylibIdLC(macho_file: *MachO, writer: anytype) !void { assert(comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic); const emit = macho_file.base.emit; const install_name = macho_file.install_name orelse - try emit.directory.join(gpa, &.{emit.sub_path}); + try emit.root_dir.join(gpa, &.{emit.sub_path}); defer if (macho_file.install_name == null) gpa.free(install_name); const curr = comp.version orelse std.SemanticVersion{ .major = 1, diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index 37013c54c4..4248785c54 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -85,6 +85,7 @@ pub const Thunk = struct { value: u64 = 0, out_n_sect: u8 = 0, symbols: std.AutoArrayHashMapUnmanaged(MachO.Ref, void) = .{}, + output_symtab_ctx: MachO.SymtabCtx = .{}, pub fn deinit(thunk: *Thunk, allocator: Allocator) void { thunk.symbols.deinit(allocator); @@ -116,6 +117,34 @@ pub const Thunk = struct { } } + pub fn calcSymtabSize(thunk: *Thunk, macho_file: *MachO) void { + thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); + for (thunk.symbols.keys()) |ref| { + const sym = ref.getSymbol(macho_file).?; + thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(macho_file).len + "__thunk".len + 1)); + } + } + + pub fn writeSymtab(thunk: Thunk, macho_file: *MachO, ctx: anytype) void { + var n_strx = thunk.output_symtab_ctx.stroff; + for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = ref.getSymbol(macho_file).?; + const name = sym.getName(macho_file); + const out_sym = &ctx.symtab.items[ilocal]; + out_sym.n_strx = n_strx; + @memcpy(ctx.strtab.items[n_strx..][0..name.len], name); + n_strx += @intCast(name.len); + @memcpy(ctx.strtab.items[n_strx..][0.."__thunk".len], "__thunk"); + n_strx += @intCast("__thunk".len); + ctx.strtab.items[n_strx] = 0; + n_strx += 1; + out_sym.n_type = macho.N_SECT; + out_sym.n_sect = @intCast(thunk.out_n_sect + 1); + out_sym.n_value = @intCast(thunk.getTargetAddress(ref, macho_file)); + out_sym.n_desc = 0; + } + } + pub fn format( thunk: Thunk, comptime unused_fmt_string: []const u8, diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index cb95779d8e..1488f65ff9 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -11,6 +11,7 @@ const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.link); +const Path = std.Build.Cache.Path; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); @@ -28,7 +29,7 @@ llvm_object: LlvmObject.Ptr, pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*NvPtx { const target = comp.root_mod.resolved_target.result; @@ -70,7 +71,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*NvPtx { const target = comp.root_mod.resolved_target.result; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index d22c3b447c..27a3bf7bc8 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -23,6 +23,7 @@ const mem = std.mem; const Allocator = std.mem.Allocator; const log = std.log.scoped(.link); const assert = std.debug.assert; +const Path = std.Build.Cache.Path; base: link.File, sixtyfour_bit: bool, @@ -275,7 +276,7 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Plan9 { const target = comp.root_mod.resolved_target.result; @@ -447,6 +448,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde const nav = ip.getNav(nav_index); const nav_val = zcu.navValue(nav_index); const nav_init = switch (ip.indexToKey(nav_val.toIntern())) { + .func => return, .variable => |variable| Value.fromInterned(variable.init), .@"extern" => { log.debug("found extern decl: {}", .{nav.name.fmt(ip)}); @@ -455,7 +457,7 @@ pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde else => nav_val, }; - if (nav_init.typeOf(zcu).isFnOrHasRuntimeBits(pt)) { + if (nav_init.typeOf(zcu).hasRuntimeBits(pt)) { const atom_idx = try self.seeNav(pt, nav_index); var code_buffer = std.ArrayList(u8).init(gpa); @@ -1199,7 +1201,7 @@ pub fn deinit(self: *Plan9) void { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Plan9 { const target = comp.root_mod.resolved_target.result; @@ -1213,7 +1215,7 @@ pub fn open( const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .read = true, .mode = link.File.determineMode( use_lld, diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index f76ceec2f5..9964c09df0 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -26,6 +26,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const log = std.log.scoped(.link); +const Path = std.Build.Cache.Path; const Zcu = @import("../Zcu.zig"); const InternPool = @import("../InternPool.zig"); @@ -54,7 +55,7 @@ object: codegen.Object, pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*SpirV { const gpa = comp.gpa; @@ -95,7 +96,7 @@ pub fn createEmpty( pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*SpirV { const target = comp.root_mod.resolved_target.result; @@ -110,7 +111,7 @@ pub fn open( errdefer spirv.base.destroy(); // TODO: read the file and keep valid parts instead of truncating - const file = try emit.directory.handle.createFile(emit.sub_path, .{ + const file = try emit.root_dir.handle.createFile(emit.sub_path, .{ .truncate = true, .read = true, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 87dd8c13f9..2c5fc3fe7a 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -22,6 +22,7 @@ const Air = @import("../Air.zig"); const Allocator = std.mem.Allocator; const Archive = @import("Wasm/Archive.zig"); const Cache = std.Build.Cache; +const Path = Cache.Path; const CodeGen = @import("../arch/wasm/CodeGen.zig"); const Compilation = @import("../Compilation.zig"); const Dwarf = @import("Dwarf.zig"); @@ -346,7 +347,7 @@ pub const StringTable = struct { pub fn open( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Wasm { // TODO: restore saved linker state, don't truncate the file, and @@ -357,7 +358,7 @@ pub fn open( pub fn createEmpty( arena: Allocator, comp: *Compilation, - emit: Compilation.Emit, + emit: Path, options: link.File.OpenOptions, ) !*Wasm { const gpa = comp.gpa; @@ -430,7 +431,7 @@ pub fn createEmpty( // can be passed to LLD. const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; - wasm.base.file = try emit.directory.handle.createFile(sub_path, .{ + wasm.base.file = try emit.root_dir.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) @@ -2496,7 +2497,7 @@ pub fn flushModule(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_no const sub_prog_node = prog_node.start("Wasm Flush", 0); defer sub_prog_node.end(); - const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. + const directory = wasm.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); const module_obj_path: ?[]const u8 = if (wasm.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { @@ -3346,7 +3347,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: const gpa = comp.gpa; - const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. + const directory = wasm.base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index 0f73edefab..05c15f0732 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/src/link/Wasm/ZigObject.zig @@ -259,7 +259,7 @@ pub fn updateNav( else => .{ false, .none, nav_val }, }; - if (nav_init.typeOf(zcu).isFnOrHasRuntimeBits(pt)) { + if (nav_init.typeOf(zcu).hasRuntimeBits(pt)) { const gpa = wasm_file.base.comp.gpa; const atom_index = try zig_object.getOrCreateAtomForNav(wasm_file, pt, nav_index); const atom = wasm_file.getAtomPtr(atom_index); diff --git a/src/main.zig b/src/main.zig index 3429500bf8..226ec79467 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3519,7 +3519,7 @@ fn buildOutputType( if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: { // Default to using `zig run` to execute the produced .c code from `zig test`. const c_code_loc = emit_bin_loc orelse break :default_exec_args; - const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.directory; + const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.root_dir; const c_code_path = try fs.path.join(arena, &[_][]const u8{ c_code_directory.path orelse ".", c_code_loc.basename, }); @@ -4142,7 +4142,7 @@ fn serve( if (output.errors.errorMessageCount() != 0) { try server.serveErrorBundle(output.errors); } else { - try server.serveEmitBinPath(output.out_zig_path, .{ + try server.serveEmitDigest(&output.digest, .{ .flags = .{ .cache_hit = output.cache_hit }, }); } @@ -4229,62 +4229,10 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { return; } - // This logic is counter-intuitive because the protocol accounts for each - // emitted artifact possibly being in a different location, which correctly - // matches the behavior of the compiler, however, the build system - // currently always passes flags that makes all build artifacts output to - // the same local cache directory, and relies on them all being in the same - // directory. - // - // So, until the build system and protocol are changed to reflect this, - // this logic must ensure that emit_bin_path is emitted for at least one - // thing, if there are any artifacts. - - switch (comp.cache_use) { - .incremental => if (comp.bin_file) |lf| { - const full_path = try lf.emit.directory.join(gpa, &.{lf.emit.sub_path}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ - .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, - }); - return; - }, - .whole => |whole| if (whole.bin_sub_path) |sub_path| { - const full_path = try comp.local_cache_directory.join(gpa, &.{sub_path}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ - .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, - }); - return; - }, - } - - for ([_]?Compilation.Emit{ - comp.docs_emit, - comp.implib_emit, - }) |opt_emit| { - const emit = opt_emit orelse continue; - const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ - .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, - }); - return; - } - - for ([_]?Compilation.EmitLoc{ - comp.emit_asm, - comp.emit_llvm_ir, - comp.emit_llvm_bc, - }) |opt_emit_loc| { - const emit_loc = opt_emit_loc orelse continue; - const directory = emit_loc.directory orelse continue; - const full_path = try directory.join(gpa, &.{emit_loc.basename}); - defer gpa.free(full_path); - try s.serveEmitBinPath(full_path, .{ + if (comp.digest) |digest| { + try s.serveEmitDigest(&digest, .{ .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, }); - return; } // Serve empty error bundle to indicate the update is done. @@ -4308,7 +4256,7 @@ fn runOrTest( // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. const exe_path = try fs.path.join(arena, &[_][]const u8{ - lf.emit.directory.path orelse ".", lf.emit.sub_path, + lf.emit.root_dir.path orelse ".", lf.emit.sub_path, }); var argv = std.ArrayList([]const u8).init(gpa); @@ -4420,7 +4368,7 @@ fn runOrTestHotSwap( // tmp zig-cache and use it to spawn the child process. This way we are free to update // the binary with each requested hot update. .windows => blk: { - try lf.emit.directory.handle.copyFile(lf.emit.sub_path, comp.local_cache_directory.handle, lf.emit.sub_path, .{}); + try lf.emit.root_dir.handle.copyFile(lf.emit.sub_path, comp.local_cache_directory.handle, lf.emit.sub_path, .{}); break :blk try fs.path.join(gpa, &[_][]const u8{ comp.local_cache_directory.path orelse ".", lf.emit.sub_path, }); @@ -4429,7 +4377,7 @@ fn runOrTestHotSwap( // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. else => try fs.path.join(gpa, &[_][]const u8{ - lf.emit.directory.path orelse ".", lf.emit.sub_path, + lf.emit.root_dir.path orelse ".", lf.emit.sub_path, }), }; defer gpa.free(exe_path); @@ -4539,9 +4487,11 @@ fn cmdTranslateC( }; if (fancy_output) |p| p.cache_hit = true; - const digest = if (try man.hit()) digest: { + const bin_digest, const hex_digest = if (try man.hit()) digest: { if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); - break :digest man.final(); + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + break :digest .{ bin_digest, hex_digest }; } else digest: { if (fancy_output) |p| p.cache_hit = false; var argv = std.ArrayList([]const u8).init(arena); @@ -4576,7 +4526,12 @@ fn cmdTranslateC( Compilation.dump_argv(argv.items); } - const formatted = switch (comp.config.c_frontend) { + const Result = union(enum) { + success: []const u8, + error_bundle: std.zig.ErrorBundle, + }; + + const result: Result = switch (comp.config.c_frontend) { .aro => f: { var stdout: []u8 = undefined; try jitCmd(comp.gpa, arena, argv.items, .{ @@ -4586,7 +4541,7 @@ fn cmdTranslateC( .capture = &stdout, .progress_node = prog_node, }); - break :f stdout; + break :f .{ .success = stdout }; }, .clang => f: { if (!build_options.have_llvm) unreachable; @@ -4614,33 +4569,47 @@ fn cmdTranslateC( c_headers_dir_path_z, ) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - error.SemanticAnalyzeFail => { - if (fancy_output) |p| { - p.errors = errors; - return; - } else { - errors.renderToStdErr(color.renderOptions()); - process.exit(1); - } - }, + error.SemanticAnalyzeFail => break :f .{ .error_bundle = errors }, }; defer tree.deinit(comp.gpa); - break :f try tree.render(arena); + break :f .{ .success = try tree.render(arena) }; }, }; - if (out_dep_path) |dep_file_path| { + if (out_dep_path) |dep_file_path| add_deps: { const dep_basename = fs.path.basename(dep_file_path); // Add the files depended on to the cache system. - try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); + man.addDepFilePost(zig_cache_tmp_dir, dep_basename) catch |err| switch (err) { + error.FileNotFound => { + // Clang didn't emit the dep file; nothing to add to the manifest. + break :add_deps; + }, + else => |e| return e, + }; // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); }; } - const digest = man.final(); - const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); + const formatted = switch (result) { + .success => |formatted| formatted, + .error_bundle => |eb| { + if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); + if (fancy_output) |p| { + p.errors = eb; + return; + } else { + eb.renderToStdErr(color.renderOptions()); + process.exit(1); + } + }, + }; + + const bin_digest = man.finalBin(); + const hex_digest = Cache.binToHex(bin_digest); + + const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &hex_digest }); var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); defer o_dir.close(); @@ -4656,16 +4625,14 @@ fn cmdTranslateC( if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf); - break :digest digest; + break :digest .{ bin_digest, hex_digest }; }; if (fancy_output) |p| { - p.out_zig_path = try comp.local_cache_directory.join(comp.gpa, &[_][]const u8{ - "o", &digest, translated_zig_basename, - }); + p.digest = bin_digest; p.errors = std.zig.ErrorBundle.empty; } else { - const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); + const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &hex_digest, translated_zig_basename }); const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| { const path = comp.local_cache_directory.path orelse "."; fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ path, fs.path.sep_str, out_zig_path, @errorName(err) }); diff --git a/src/print_air.zig b/src/print_air.zig index 137461e6e4..4db66a1d3c 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -283,6 +283,7 @@ const Writer = struct { .dbg_var_ptr, .dbg_var_val, + .dbg_arg_inline, => try w.writeDbgVar(s, inst), .struct_field_ptr => try w.writeStructField(s, inst), @@ -358,10 +359,7 @@ const Writer = struct { try w.writeType(s, arg.ty.toType()); switch (arg.name) { .none => {}, - _ => { - const name = w.air.nullTerminatedString(@intFromEnum(arg.name)); - try s.print(", \"{}\"", .{std.zig.fmtEscapes(name)}); - }, + _ => try s.print(", \"{}\"", .{std.zig.fmtEscapes(arg.name.toSlice(w.air))}), } } @@ -686,8 +684,8 @@ const Writer = struct { fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; try w.writeOperand(s, inst, 0, pl_op.operand); - const name = w.air.nullTerminatedString(pl_op.payload); - try s.print(", \"{}\"", .{std.zig.fmtEscapes(name)}); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + try s.print(", \"{}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))}); } fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { diff --git a/src/register_manager.zig b/src/register_manager.zig index 751aed988a..7ca117be0c 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -93,6 +93,8 @@ pub fn RegisterManager( comptime set: []const Register, reg: Register, ) ?std.math.IntFittingRange(0, set.len - 1) { + @setEvalBranchQuota(3000); + const Id = @TypeOf(reg.id()); comptime var min_id: Id = std.math.maxInt(Id); comptime var max_id: Id = std.math.minInt(Id); diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index d08bc82828..ab1803c5b1 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -146,6 +146,8 @@ test "@min/max for floats" { }; inline for (.{ f16, f32, f64, f80, f128, c_longdouble }) |T| { + if (T == c_longdouble and builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21090 + try S.doTheTest(T); try comptime S.doTheTest(T); } diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index c4e00d9b0c..214fda28d6 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -773,6 +773,7 @@ test "vector reduce operation" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21091 const S = struct { fn testReduce(comptime op: std.builtin.ReduceOp, x: anytype, expected: anytype) !void { diff --git a/test/incremental/delete_comptime_decls b/test/incremental/delete_comptime_decls index 424dc37ea8..d0da7110c3 100644 --- a/test/incremental/delete_comptime_decls +++ b/test/incremental/delete_comptime_decls @@ -31,7 +31,7 @@ pub fn main() void {} comptime { const x: [*c]u8 = null; var runtime_len: usize = undefined; - runtime_len = 0; + runtime_len = 0; const y = x[0..runtime_len]; _ = y; } diff --git a/test/incremental/modify_inline_fn b/test/incremental/modify_inline_fn new file mode 100644 index 0000000000..633e7a0728 --- /dev/null +++ b/test/incremental/modify_inline_fn @@ -0,0 +1,23 @@ +#target=x86_64-linux +#update=initial version +#file=main.zig +const std = @import("std"); +pub fn main() !void { + const str = getStr(); + try std.io.getStdOut().writeAll(str); +} +inline fn getStr() []const u8 { + return "foo\n"; +} +#expect_stdout="foo\n" +#update=change the string +#file=main.zig +const std = @import("std"); +pub fn main() !void { + const str = getStr(); + try std.io.getStdOut().writeAll(str); +} +inline fn getStr() []const u8 { + return "bar\n"; +} +#expect_stdout="bar\n" diff --git a/test/incremental/move_src b/test/incremental/move_src new file mode 100644 index 0000000000..8313bdfac2 --- /dev/null +++ b/test/incremental/move_src @@ -0,0 +1,29 @@ +#target=x86_64-linux +#update=initial version +#file=main.zig +const std = @import("std"); +pub fn main() !void { + try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() }); +} +fn foo() u32 { + return @src().line; +} +fn bar() u32 { + return 123; +} +#expect_stdout="6 123\n" + +#update=add newline +#file=main.zig +const std = @import("std"); +pub fn main() !void { + try std.io.getStdOut().writer().print("{d} {d}\n", .{ foo(), bar() }); +} + +fn foo() u32 { + return @src().line; +} +fn bar() u32 { + return 123; +} +#expect_stdout="7 123\n" diff --git a/test/link/elf.zig b/test/link/elf.zig index 5539638ba7..98253811b2 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -2973,44 +2973,27 @@ fn testStrip(b: *Build, opts: Options) *Step { fn testThunks(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "thunks", opts); - const src = - \\#include <stdio.h> - \\__attribute__((aligned(0x8000000))) int bar() { - \\ return 42; - \\} - \\int foobar(); - \\int foo() { - \\ return bar() - foobar(); - \\} - \\__attribute__((aligned(0x8000000))) int foobar() { - \\ return 42; - \\} - \\int main() { - \\ printf("bar=%d, foo=%d, foobar=%d", bar(), foo(), foobar()); - \\ return foo(); - \\} - ; - - { - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = src }); - exe.link_function_sections = true; - exe.linkLibC(); - - const run = addRunArtifact(exe); - run.expectStdOutEqual("bar=42, foo=0, foobar=42"); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } - - { - const exe = addExecutable(b, opts, .{ .name = "main2", .c_source_bytes = src }); - exe.linkLibC(); + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = + \\void foo(); + \\__attribute__((section(".bar"))) void bar() { + \\ return foo(); + \\} + \\__attribute__((section(".foo"))) void foo() { + \\ return bar(); + \\} + \\int main() { + \\ foo(); + \\ bar(); + \\ return 0; + \\} + }); - const run = addRunArtifact(exe); - run.expectStdOutEqual("bar=42, foo=0, foobar=42"); - run.expectExitCode(0); - test_step.dependOn(&run.step); - } + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkContains("foo$thunk"); + check.checkInSymtab(); + check.checkContains("bar$thunk"); + test_step.dependOn(&check.step); return test_step; } diff --git a/test/link/macho.zig b/test/link/macho.zig index 30982e6ba2..730edcf3a9 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -2204,25 +2204,28 @@ fn testThunks(b: *Build, opts: Options) *Step { const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include <stdio.h> - \\__attribute__((aligned(0x8000000))) int bar() { - \\ return 42; + \\void bar() { + \\ printf("bar"); \\} - \\int foobar(); - \\int foo() { - \\ return bar() - foobar(); - \\} - \\__attribute__((aligned(0x8000000))) int foobar() { - \\ return 42; + \\void foo() { + \\ fprintf(stdout, "foo"); \\} \\int main() { - \\ printf("bar=%d, foo=%d, foobar=%d", bar(), foo(), foobar()); - \\ return foo(); + \\ foo(); + \\ bar(); + \\ return 0; \\} }); + const check = exe.checkObject(); + check.checkInSymtab(); + check.checkContains("_printf__thunk"); + check.checkInSymtab(); + check.checkContains("_fprintf__thunk"); + test_step.dependOn(&check.step); + const run = addRunArtifact(exe); - run.expectStdOutEqual("bar=42, foo=0, foobar=42"); - run.expectExitCode(0); + run.expectStdOutEqual("foobar"); test_step.dependOn(&run.step); return test_step; diff --git a/test/src/Debugger.zig b/test/src/Debugger.zig index 2ff141dd84..718d89dd1c 100644 --- a/test/src/Debugger.zig +++ b/test/src/Debugger.zig @@ -205,26 +205,26 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void { \\ single_volatile: *volatile u32 = @ptrFromInt(0x1018), \\ single_const_volatile: *const volatile u32 = @ptrFromInt(0x101c), \\ single_allowzero: *allowzero u32 = @ptrFromInt(0x1020), - \\ single_const_allowzero: *const allowzero u32 = @ptrFromInt(0x1024), - \\ single_volatile_allowzero: *volatile allowzero u32 = @ptrFromInt(0x1028), - \\ single_const_volatile_allowzero: *const volatile allowzero u32 = @ptrFromInt(0x102c), + \\ single_allowzero_const: *allowzero const u32 = @ptrFromInt(0x1024), + \\ single_allowzero_volatile: *allowzero volatile u32 = @ptrFromInt(0x1028), + \\ single_allowzero_const_volatile: *allowzero const volatile u32 = @ptrFromInt(0x102c), \\ \\ many: [*]u32 = @ptrFromInt(0x2010), \\ many_const: [*]const u32 = @ptrFromInt(0x2014), \\ many_volatile: [*]volatile u32 = @ptrFromInt(0x2018), \\ many_const_volatile: [*]const volatile u32 = @ptrFromInt(0x201c), \\ many_allowzero: [*]allowzero u32 = @ptrFromInt(0x2020), - \\ many_const_allowzero: [*]const allowzero u32 = @ptrFromInt(0x2024), - \\ many_volatile_allowzero: [*]volatile allowzero u32 = @ptrFromInt(0x2028), - \\ many_const_volatile_allowzero: [*]const volatile allowzero u32 = @ptrFromInt(0x202c), + \\ many_allowzero_const: [*]allowzero const u32 = @ptrFromInt(0x2024), + \\ many_allowzero_volatile: [*]allowzero volatile u32 = @ptrFromInt(0x2028), + \\ many_allowzero_const_volatile: [*]allowzero const volatile u32 = @ptrFromInt(0x202c), \\ slice: []u32 = array[0..1], \\ slice_const: []const u32 = array[0..2], \\ slice_volatile: []volatile u32 = array[0..3], \\ slice_const_volatile: []const volatile u32 = array[0..4], \\ slice_allowzero: []allowzero u32 = array[4..4], - \\ slice_const_allowzero: []const allowzero u32 = array[4..5], - \\ slice_volatile_allowzero: []volatile allowzero u32 = array[4..6], - \\ slice_const_volatile_allowzero: []const volatile allowzero u32 = array[4..7], + \\ slice_allowzero_const: []allowzero const u32 = array[4..5], + \\ slice_allowzero_volatile: []allowzero volatile u32 = array[4..6], + \\ slice_allowzero_const_volatile: []allowzero const volatile u32 = array[4..7], \\ \\ c: [*c]u32 = @ptrFromInt(0x4010), \\ c_const: [*c]const u32 = @ptrFromInt(0x4014), @@ -254,17 +254,17 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void { \\ (*volatile u32) single_volatile = 0x0000000000001018 \\ (*const volatile u32) single_const_volatile = 0x000000000000101c \\ (*allowzero u32) single_allowzero = 0x0000000000001020 - \\ (*const allowzero u32) single_const_allowzero = 0x0000000000001024 - \\ (*volatile allowzero u32) single_volatile_allowzero = 0x0000000000001028 - \\ (*const volatile allowzero u32) single_const_volatile_allowzero = 0x000000000000102c + \\ (*allowzero const u32) single_allowzero_const = 0x0000000000001024 + \\ (*allowzero volatile u32) single_allowzero_volatile = 0x0000000000001028 + \\ (*allowzero const volatile u32) single_allowzero_const_volatile = 0x000000000000102c \\ ([*]u32) many = 0x0000000000002010 \\ ([*]const u32) many_const = 0x0000000000002014 \\ ([*]volatile u32) many_volatile = 0x0000000000002018 \\ ([*]const volatile u32) many_const_volatile = 0x000000000000201c \\ ([*]allowzero u32) many_allowzero = 0x0000000000002020 - \\ ([*]const allowzero u32) many_const_allowzero = 0x0000000000002024 - \\ ([*]volatile allowzero u32) many_volatile_allowzero = 0x0000000000002028 - \\ ([*]const volatile allowzero u32) many_const_volatile_allowzero = 0x000000000000202c + \\ ([*]allowzero const u32) many_allowzero_const = 0x0000000000002024 + \\ ([*]allowzero volatile u32) many_allowzero_volatile = 0x0000000000002028 + \\ ([*]allowzero const volatile u32) many_allowzero_const_volatile = 0x000000000000202c \\ ([]u32) slice = len=1 { \\ (u32) [0] = 3010 \\ } @@ -284,14 +284,14 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void { \\ (u32) [3] = 3022 \\ } \\ ([]allowzero u32) slice_allowzero = len=0 {} - \\ ([]const allowzero u32) slice_const_allowzero = len=1 { + \\ ([]allowzero const u32) slice_allowzero_const = len=1 { \\ (u32) [0] = 3026 \\ } - \\ ([]volatile allowzero u32) slice_volatile_allowzero = len=2 { + \\ ([]allowzero volatile u32) slice_allowzero_volatile = len=2 { \\ (u32) [0] = 3026 \\ (u32) [1] = 3030 \\ } - \\ ([]const volatile allowzero u32) slice_const_volatile_allowzero = len=3 { + \\ ([]allowzero const volatile u32) slice_allowzero_const_volatile = len=3 { \\ (u32) [0] = 3026 \\ (u32) [1] = 3030 \\ (u32) [2] = 3034 @@ -587,7 +587,7 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void { }, ); db.addLldbTest( - "cross_module_call", + "inline_call", target, &.{ .{ @@ -595,8 +595,18 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void { .source = \\const module = @import("module"); \\pub fn main() void { - \\ module.foo(123); - \\ module.bar(456); + \\ fa(12); + \\ fb(34); + \\ module.fc(56); + \\ module.fd(78); + \\} + \\fn fa(pa: u32) void { + \\ const la = ~pa; + \\ _ = la; + \\} + \\inline fn fb(pb: u32) void { + \\ const lb = ~pb; + \\ _ = lb; \\} \\ , @@ -605,32 +615,120 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void { .import = "module", .path = "module.zig", .source = - \\pub fn foo(x: u32) void { - \\ _ = x; + \\pub fn fc(pc: u32) void { + \\ const lc = ~pc; + \\ _ = lc; \\} - \\pub inline fn bar(y: u32) void { - \\ _ = y; + \\pub inline fn fd(pd: u32) void { + \\ const ld = ~pd; + \\ _ = ld; \\} \\ , }, }, - \\breakpoint set --file module.zig --source-pattern-regexp '_ = x;' + \\settings set frame-format 'frame #${frame.index}:{ ${module.file.basename}{\`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${line.file.basename}:${line.number}{:${line.column}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\n' + \\ + \\breakpoint set --file main.zig --source-pattern-regexp '_ = la;' + \\process launch + \\frame variable pa la + \\thread backtrace --count 2 + \\breakpoint delete --force 1 + \\ + \\breakpoint set --file main.zig --source-pattern-regexp '_ = lb;' + \\process continue + \\frame variable pb lb + \\thread backtrace --count 2 + \\breakpoint delete --force 2 + \\ + \\breakpoint set --file module.zig --source-pattern-regexp '_ = lc;' + \\process continue + \\frame variable pc lc + \\thread backtrace --count 2 + \\breakpoint delete --force 3 + \\ + \\breakpoint set --file module.zig --line 7 + \\process continue + \\frame variable pd ld + \\thread backtrace --count 2 + \\breakpoint delete --force 4 + , + &.{ + \\(lldb) frame variable pa la + \\(u32) pa = 12 + \\(u32) la = 4294967283 + \\(lldb) thread backtrace --count 2 + \\* thread #1, name = 'inline_call', stop reason = breakpoint 1.1 + \\ * frame #0: inline_call`main.fa(pa=12) at main.zig:10:5 + \\ frame #1: inline_call`main.main at main.zig:3:7 + \\(lldb) breakpoint delete --force 1 + \\1 breakpoints deleted; 0 breakpoint locations disabled. + , + \\(lldb) frame variable pb lb + \\(u32) pb = 34 + \\(u32) lb = 4294967261 + \\(lldb) thread backtrace --count 2 + \\* thread #1, name = 'inline_call', stop reason = breakpoint 2.1 + \\ * frame #0: inline_call`main.main [inlined] fb(pb=34) at main.zig:14:5 + \\ frame #1: inline_call`main.main at main.zig:4:7 + \\(lldb) breakpoint delete --force 2 + \\1 breakpoints deleted; 0 breakpoint locations disabled. + , + \\(lldb) frame variable pc lc + \\(u32) pc = 56 + \\(u32) lc = 4294967239 + \\(lldb) thread backtrace --count 2 + \\* thread #1, name = 'inline_call', stop reason = breakpoint 3.1 + \\ * frame #0: inline_call`module.fc(pc=56) at module.zig:3:5 + \\ frame #1: inline_call`main.main at main.zig:5:14 + \\(lldb) breakpoint delete --force 3 + \\1 breakpoints deleted; 0 breakpoint locations disabled. + , + \\(lldb) frame variable pd ld + \\(u32) pd = 78 + \\(u32) ld = 4294967217 + \\(lldb) thread backtrace --count 2 + \\* thread #1, name = 'inline_call', stop reason = breakpoint 4.1 + \\ * frame #0: inline_call`main.main [inlined] fd(pd=78) at module.zig:7:5 + \\ frame #1: inline_call`main.main at main.zig:6:14 + \\(lldb) breakpoint delete --force 4 + \\1 breakpoints deleted; 0 breakpoint locations disabled. + }, + ); + db.addLldbTest( + "link_object", + target, + &.{ + .{ + .path = "main.zig", + .source = + \\extern fn fabsf(f32) f32; + \\pub fn main() void { + \\ var x: f32 = -1234.5; + \\ x = fabsf(x); + \\ _ = &x; + \\} + , + }, + }, + \\breakpoint set --file main.zig --source-pattern-regexp 'x = fabsf\(x\);' \\process launch - \\source info + \\frame variable x \\breakpoint delete --force 1 \\ - \\breakpoint set --file module.zig --line 5 + \\breakpoint set --file main.zig --source-pattern-regexp '_ = &x;' \\process continue - \\source info + \\frame variable x \\breakpoint delete --force 2 , &.{ - \\/module.zig:2:5 + \\(lldb) frame variable x + \\(f32) x = -1234.5 \\(lldb) breakpoint delete --force 1 \\1 breakpoints deleted; 0 breakpoint locations disabled. , - \\/module.zig:5:5 + \\(lldb) frame variable x + \\(f32) x = 1234.5 \\(lldb) breakpoint delete --force 2 \\1 breakpoints deleted; 0 breakpoint locations disabled. }, diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 8e4d727642..30ec07823b 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -51,14 +51,15 @@ .install_raw_hex = .{ .path = "install_raw_hex", }, - // https://github.com/ziglang/zig/issues/17484 - //.emit_asm_and_bin = .{ - // .path = "emit_asm_and_bin", - //}, - // https://github.com/ziglang/zig/issues/17484 - //.issue_12588 = .{ - // .path = "issue_12588", - //}, + .emit_asm_and_bin = .{ + .path = "emit_asm_and_bin", + }, + .emit_llvm_no_bin = .{ + .path = "emit_llvm_no_bin", + }, + .emit_asm_no_bin = .{ + .path = "emit_asm_no_bin", + }, .child_process = .{ .path = "child_process", }, diff --git a/test/standalone/emit_asm_no_bin/build.zig b/test/standalone/emit_asm_no_bin/build.zig new file mode 100644 index 0000000000..f2f2391bc1 --- /dev/null +++ b/test/standalone/emit_asm_no_bin/build.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + + const obj = b.addObject(.{ + .name = "main", + .root_source_file = b.path("main.zig"), + .optimize = optimize, + .target = b.graph.host, + }); + _ = obj.getEmittedAsm(); + b.default_step.dependOn(&obj.step); + + test_step.dependOn(&obj.step); +} diff --git a/test/standalone/emit_asm_no_bin/main.zig b/test/standalone/emit_asm_no_bin/main.zig new file mode 100644 index 0000000000..902b554db0 --- /dev/null +++ b/test/standalone/emit_asm_no_bin/main.zig @@ -0,0 +1 @@ +pub fn main() void {} diff --git a/test/standalone/issue_12588/build.zig b/test/standalone/emit_llvm_no_bin/build.zig index f82dd51930..f82dd51930 100644 --- a/test/standalone/issue_12588/build.zig +++ b/test/standalone/emit_llvm_no_bin/build.zig diff --git a/test/standalone/issue_12588/main.zig b/test/standalone/emit_llvm_no_bin/main.zig index 47f67515c7..47f67515c7 100644 --- a/test/standalone/issue_12588/main.zig +++ b/test/standalone/emit_llvm_no_bin/main.zig diff --git a/test/tests.zig b/test/tests.zig index 6b044e91ba..73934950db 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -367,6 +367,54 @@ const test_targets = blk: { .{ .target = .{ + .cpu_arch = .mips64, + .os_tag = .linux, + .abi = .none, + }, + }, + .{ + .target = .{ + .cpu_arch = .mips64, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + .{ + .target = .{ + .cpu_arch = .mips64, + .os_tag = .linux, + .abi = .gnuabi64, + }, + .link_libc = true, + }, + + .{ + .target = .{ + .cpu_arch = .mips64el, + .os_tag = .linux, + .abi = .none, + }, + }, + .{ + .target = .{ + .cpu_arch = .mips64el, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + .{ + .target = .{ + .cpu_arch = .mips64el, + .os_tag = .linux, + .abi = .gnuabi64, + }, + .link_libc = true, + }, + + .{ + .target = .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .none, diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 0386f1a12d..a5c0afc62a 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -1,6 +1,7 @@ const std = @import("std"); const fatal = std.process.fatal; const Allocator = std.mem.Allocator; +const Cache = std.Build.Cache; const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--emit none|bin|c] [--zig-cc-binary /path/to/zig]"; @@ -233,30 +234,52 @@ const Eval = struct { fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data}); } } - if (result_error_bundle.errorMessageCount() == 0) { - // Empty bundle indicates successful update in a `-fno-emit-bin` build. - try eval.checkSuccessOutcome(update, null, prog_node); - } else { + if (result_error_bundle.errorMessageCount() != 0) { try eval.checkErrorOutcome(update, result_error_bundle); } // This message indicates the end of the update. stdout.discard(body.len); return; }, - .emit_bin_path => { - const EbpHdr = std.zig.Server.Message.EmitBinPath; + .emit_digest => { + const EbpHdr = std.zig.Server.Message.EmitDigest; const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); _ = ebp_hdr; - const result_binary = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); if (stderr.readableLength() > 0) { const stderr_data = try stderr.toOwnedSlice(); if (eval.allow_stderr) { - std.log.info("emit_bin_path included stderr:\n{s}", .{stderr_data}); + std.log.info("emit_digest included stderr:\n{s}", .{stderr_data}); } else { - fatal("emit_bin_path included unexpected stderr:\n{s}", .{stderr_data}); + fatal("emit_digest included unexpected stderr:\n{s}", .{stderr_data}); } } - try eval.checkSuccessOutcome(update, result_binary, prog_node); + + if (eval.emit == .none) { + try eval.checkSuccessOutcome(update, null, prog_node); + // This message indicates the end of the update. + stdout.discard(body.len); + return; + } + + const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; + const result_dir = ".local-cache" ++ std.fs.path.sep_str ++ "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*); + + const name = std.fs.path.stem(std.fs.path.basename(eval.case.root_source_file)); + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = name, + .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{ + .arch_os_abi = eval.case.target_query, + .object_format = switch (eval.emit) { + .none => unreachable, + .bin => null, + .c => "c", + }, + })), + .output_mode = .Exe, + }); + const bin_path = try std.fs.path.join(arena, &.{ result_dir, bin_name }); + + try eval.checkSuccessOutcome(update, bin_path, prog_node); // This message indicates the end of the update. stdout.discard(body.len); return; |
