From f08a8f0fc217914690a58035291da8bcc7a86e47 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 8 Oct 2026 01:49:12 -0400 Subject: [PATCH] main: reimplement compiler debug flags * Add `.llvmbc`/`.llvmir` to `ObjectFormat` for emitting unoptimized IR * Replace `--verbose-air`, `--verbose-intern-pool`, `--verbose-generic-instances`, `--verbose-llvm-ir`, and `--verbose-llvm-bc` with various `--debug-log` scopes * `--debug-log illegal-air`: pre-legalized Air * `--debug-log legal-air`: post-legalized Air * `--debug-log air`: final Air with liveness * `--debug-log ip-stats`: InternPool statistics * `--debug-log ip-values`: InternPool values * `--debug-log ip-generic-instances`: InternPool generic instances * `--debug-log llvm-ir`: pre-optimized LLVM IR * `--debug-log opt-llvm-ir`: post-optimized LLVM IR * Delete `--verbose-cimport` which does nothing * Delete `--verbose-llvm-cpu-features` which is equivalent to looking at the outputs of `--show-builtin` and `--debug-log llvm-ir` --- CMakeLists.txt | 2 +- bootstrap.c | 2 +- build.zig | 24 +++-- lib/compiler/Maker.zig | 4 - lib/compiler/Maker/Graph.zig | 3 - lib/compiler/Maker/ScannedConfig.zig | 8 +- lib/compiler/Maker/Step/Compile.zig | 5 +- lib/compiler/aro/aro/Driver.zig | 2 +- lib/std/Build/Configuration.zig | 2 + lib/std/Target.zig | 6 ++ lib/std/debug.zig | 2 +- lib/std/fmt.zig | 8 +- lib/std/zig.zig | 2 + lib/std/zig/llvm/Builder.zig | 21 +++-- src/Air.zig | 10 +-- src/Air/{print.zig => Print.zig} | 15 +++- src/Compilation.zig | 112 ++++++------------------ src/Compilation/Config.zig | 8 +- src/InternPool.zig | 60 +++++++------ src/Module.zig | 19 ++-- src/Sema.zig | 8 +- src/Zcu/PerThread.zig | 58 +++++-------- src/codegen/llvm.zig | 125 ++++++++++++++------------- src/codegen/llvm/bindings.zig | 5 +- src/codegen/riscv64/CodeGen.zig | 2 +- src/codegen/x86_64/CodeGen.zig | 2 +- src/dev.zig | 2 + src/libs/freebsd.zig | 4 - src/libs/glibc.zig | 4 - src/libs/libcxx.zig | 8 -- src/libs/libtsan.zig | 4 - src/libs/libunwind.zig | 4 - src/libs/musl.zig | 3 - src/libs/netbsd.zig | 4 - src/libs/openbsd.zig | 4 - src/link.zig | 39 ++++++++- src/link/Elf.zig | 4 +- src/link/Elf/ZigObject.zig | 2 +- src/link/Elf/gc.zig | 2 +- src/link/Elf/relocatable.zig | 14 +-- src/link/MachO.zig | 4 +- src/link/MachO/dead_strip.zig | 3 +- src/link/MachO/relocatable.zig | 12 +-- src/main.zig | 114 +++++++++--------------- src/target.zig | 2 + src/zig_llvm.cpp | 8 +- src/zig_llvm.h | 8 +- stage1/config.zig.in | 2 +- 48 files changed, 347 insertions(+), 419 deletions(-) rename src/Air/{print.zig => Print.zig} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 48878a454f61f24ab78f47c56974240cefc61b54..fd8a347004186d9f6fd3ab9b16b5c2b438165556 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,7 +330,7 @@ set(ZIG_STAGE2_SOURCES src/Air/Legalize.zig src/Air/Liveness.zig src/Air/Liveness/Verify.zig - src/Air/print.zig + src/Air/Print.zig src/Builtin.zig src/Compilation.zig src/Compilation/Config.zig diff --git a/bootstrap.c b/bootstrap.c index 4e41d2ebdc8a0839182901b33e6214ad3f6ae3f4..eaa506c32fc6b76dcaedd5061c629ad32d2bd31b 100644 --- a/bootstrap.c +++ b/bootstrap.c @@ -179,7 +179,7 @@ int main(int argc, char **argv) { "pub const version: [:0]const u8 = \"%s\";\n" "pub const semver = @import(\"std\").SemanticVersion.parse(version) catch unreachable;\n" "pub const enable_debug_extensions = false;\n" - "pub const enable_logging = false;\n" + "pub const log_level: @import(\"std\").log.Level = .err;\n" "pub const enable_link_snapshots = false;\n" "pub const enable_tracy = false;\n" "pub const value_tracing = false;\n" diff --git a/build.zig b/build.zig index 39bb16c0a0b03001e30797a19f33be2e92e764d0..bcd0f50975e906c61a9e2649e933ac8350da5d43 100644 --- a/build.zig +++ b/build.zig @@ -25,6 +25,10 @@ pub fn build(b: *std.Build) !void { }, }); const optimize = b.standardOptimizeOption(.{}); + const is_debug = switch (optimize) { + .debug => true, + .safe, .fast, .small => false, + }; const flat = b.option(bool, "flat", "Put files into the installation prefix in a manner suited for upstream distribution rather than a posix file system hierarchy standard") orelse false; const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode"); @@ -205,9 +209,9 @@ pub fn build(b: *std.Build) !void { const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false; const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { - if (strip == true) break :blk @as(u32, 0); - if (optimize != .debug) break :blk 0; - break :blk 4; + if (strip == true) break :blk 0; + if (is_debug) break :blk 4; + break :blk 0; }; const exe = addCompilerStep(b, .{ @@ -255,9 +259,15 @@ pub fn build(b: *std.Build) !void { exe.root_module.link_libc = true; } - const is_debug = optimize == .debug; const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug; - const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug; + const log_level: std.log.Level = b.option(std.log.Level, "log", "Enable logs of the given or higher severity. Passing 'debug' still requires passing '--debug-log' to actually see any debug logs.") orelse switch (enable_debug_extensions) { + true => .debug, + false => switch (optimize) { + .debug => .debug, + .safe, .fast => .info, + .small => .err, + }, + }; const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); const version_slice = if (opt_version_string) |version| version else v: { @@ -398,7 +408,7 @@ pub fn build(b: *std.Build) !void { exe_options.addOption(std.SemanticVersion, "semver", semver); exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions); - exe_options.addOption(bool, "enable_logging", enable_logging); + exe_options.addOption(std.log.Level, "log_level", log_level); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); @@ -837,7 +847,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { exe_options.addOption([:0]const u8, "version", version); exe_options.addOption(std.SemanticVersion, "semver", semver); exe_options.addOption(bool, "enable_debug_extensions", false); - exe_options.addOption(bool, "enable_logging", false); + exe_options.addOption(std.log.Level, "log_level", .err); exe_options.addOption(bool, "enable_tracy", false); exe_options.addOption(bool, "enable_tracy_callstack", false); exe_options.addOption(bool, "enable_tracy_allocation", false); diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 149bf0a63f4bc135a8217527df64944d5edaa70e..e0ce8142628ad4aac0466293869eaa9f7bab225d 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -509,14 +509,10 @@ pub fn main(init: process.Init.Minimal) !void { } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) { // --glibc-runtimes was the old name of the flag; kept for compatibility for now. graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_i); - } else if (mem.eql(u8, arg, "--verbose-air")) { - graph.verbose_air = true; } else if (mem.eql(u8, arg, "--verbose-cc")) { graph.verbose_cc = true; } else if (mem.eql(u8, arg, "--verbose-link")) { graph.verbose_link = true; - } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { - graph.verbose_llvm_ir = true; } else if (mem.eql(u8, arg, "--watch")) { watch_flag = true; } else if (mem.eql(u8, arg, "--time-report")) { diff --git a/lib/compiler/Maker/Graph.zig b/lib/compiler/Maker/Graph.zig index ec4159991a7bae901142726540bd9cfddd08688e..2ba152591230df002e64ca6eaa59255b0b0379ea 100644 --- a/lib/compiler/Maker/Graph.zig +++ b/lib/compiler/Maker/Graph.zig @@ -32,11 +32,8 @@ debug_compile_errors: bool = false, debug_incremental: bool = false, fuzzing: bool = false, verbose: bool = false, -verbose_air: bool = false, verbose_cc: bool = false, verbose_link: bool = false, -verbose_llvm_cpu_features: bool = false, -verbose_llvm_ir: bool = false, libc_file: ?[]const u8 = null, /// What does this do? Nobody bothered to document it, and I think it's a /// smelly option. So unless somebody deletes these passive aggressive comments diff --git a/lib/compiler/Maker/ScannedConfig.zig b/lib/compiler/Maker/ScannedConfig.zig index 4e83388ee9f094c98a36e79f504e76b37fb9f148..c516bfb00dcde9adba92d6b47a7cb8d136afdc1f 100644 --- a/lib/compiler/Maker/ScannedConfig.zig +++ b/lib/compiler/Maker/ScannedConfig.zig @@ -351,6 +351,8 @@ const general_usage = \\ preferred optimization mode: fast, safe, small \\ \\ --verbose Print commands before executing them + \\ --verbose-cc Print C compiler commands before executing them + \\ --verbose-link Print linker commands before executing them \\ --color [auto|off|on] Enable or disable colored error messages \\ --error-style [style] Control how build errors are printed \\ verbose (Default) Report errors with full context @@ -417,11 +419,5 @@ const general_usage = \\ none (default) No build ID \\ --debug-log [scope] Enable debugging the compiler \\ --debug-pkg-config Fail if unknown pkg-config flags encountered - \\ --verbose-link Enable compiler debug output for linking - \\ --verbose-air Enable compiler debug output for Zig AIR - \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR - \\ --verbose-cimport Enable compiler debug output for C imports - \\ --verbose-cc Enable compiler debug output for C compilation - \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features \\ ; diff --git a/lib/compiler/Maker/Step/Compile.zig b/lib/compiler/Maker/Step/Compile.zig index 1c8cdc9db4c94aae76542ef3e43d36f1fc6c190c..7e25860277b72a91cf62d23877325cd41ef85a09 100644 --- a/lib/compiler/Maker/Step/Compile.zig +++ b/lib/compiler/Maker/Step/Compile.zig @@ -608,11 +608,8 @@ fn lowerZigArgs( try addBool(gpa, zig_args, "--debug-compile-errors", graph.debug_compile_errors); try addBool(gpa, zig_args, "--debug-incremental", graph.debug_incremental); - try addBool(gpa, zig_args, "--verbose-air", graph.verbose_air); - try addBool(gpa, zig_args, "--verbose-llvm-ir", graph.verbose_llvm_ir); - try addBool(gpa, zig_args, "--verbose-link", graph.verbose_link or conf_comp.flags.verbose_link); try addBool(gpa, zig_args, "--verbose-cc", graph.verbose_cc or conf_comp.flags.verbose_cc); - try addBool(gpa, zig_args, "--verbose-llvm-cpu-features", graph.verbose_llvm_cpu_features); + try addBool(gpa, zig_args, "--verbose-link", graph.verbose_link or conf_comp.flags.verbose_link); try addBool(gpa, zig_args, "--time-report", graph.time_report); if (conf_comp.generated_bin.value == null) try zig_args.append(gpa, "-fno-emit-bin"); diff --git a/lib/compiler/aro/aro/Driver.zig b/lib/compiler/aro/aro/Driver.zig index 676f7397fd3a3560d86e11478a9de5b39ad10a1d..b41a7a191135c0ee0402b90ac2e54e631f641c3a 100644 --- a/lib/compiler/aro/aro/Driver.zig +++ b/lib/compiler/aro/aro/Driver.zig @@ -927,7 +927,7 @@ pub fn parseArgs( break :debug switch (d.comp.target.ofmt) { .elf, .macho, .wasm => .{ .dwarf = .@"32" }, .coff => .code_view, - .c => switch (d.comp.target.os.tag) { + .c, .llvmbc, .llvmir => switch (d.comp.target.os.tag) { .windows, .uefi => .code_view, else => .{ .dwarf = .@"32" }, }, diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index e85e19cd1e7d7ba37128ab208de61e4e39690a3e..c22dd975a4d88c981455431c96df23950594fe02 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -2519,6 +2519,8 @@ pub const TargetQuery = struct { coff, elf, hex, + llvmbc, + llvmir, macho, plan9, raw, diff --git a/lib/std/Target.zig b/lib/std/Target.zig index bba3026b739c3dfd1da086be5ac6114513f83a01..cc75e0ab49dd714197dd8cb197dc0b9b3049b5dd 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -1043,6 +1043,10 @@ pub const ObjectFormat = enum { elf, /// The Intel HEX format for storing binary code in ASCII text. hex, + /// LLVM binary intermediate representation. + llvmbc, + /// LLVM text intermediate representation. + llvmir, /// The Mach object format used by macOS and other Apple platforms. macho, /// The a.out format used by Plan 9 from Bell Labs. @@ -1060,6 +1064,8 @@ pub const ObjectFormat = enum { .coff => ".obj", .elf, .macho, .wasm => ".o", .hex => ".ihex", + .llvmbc => ".bc", + .llvmir => ".ll", .plan9 => arch.plan9Ext(), .raw => ".bin", .spirv => ".spv", diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 37a3f0d7594c8190fd1fa27060a76f7512ac55d3..0822957bc98e790b17f8b00835661bf0ba7f6ba1 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -76,7 +76,7 @@ pub fn TargetInfo(os: std.Target.Os.Tag, arch: std.Target.Cpu.Arch) type { }, .macho => @import("debug/SelfInfo/MachO.zig"), .plan9, .spirv, .wasm, .raw, .hex => void, - .c => unreachable, + .c, .llvmbc, .llvmir => unreachable, }; } diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 1b90c2cff3e96e923e263884a200597251fe6298..a21a82d77883d18031b36735ca4ca3a42ee29972 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -285,7 +285,13 @@ pub fn Alt( pub fn alt( context: anytype, comptime func_name: @EnumLiteral(), -) Alt(@TypeOf(context), @field(@TypeOf(context), @tagName(func_name))) { +) Alt(@TypeOf(context), @field(switch (@typeInfo(@TypeOf(context))) { + else => @TypeOf(context), + .pointer => |pointer| switch (pointer.size) { + else => @TypeOf(context), + .one => pointer.child, + }, +}, @tagName(func_name))) { return .{ .data = context }; } diff --git a/lib/std/zig.zig b/lib/std/zig.zig index b1537b6c166e6cd73c57447d993f219c4274f22a..db8fa41d8c48651220bb676d1540838d67b80dba 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -218,6 +218,8 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe }, .Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}), }, + .llvmbc => return std.fmt.allocPrint(allocator, "{s}.bc", .{root_name}), + .llvmir => return std.fmt.allocPrint(allocator, "{s}.ll", .{root_name}), .macho => switch (options.output_mode) { .Exe => return allocator.dupe(u8, root_name), .Lib => { diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 2722d5d62f0cbf7503aadda61ac5e23e28bd5967..16a4f283ce6ccf976af56428e5ac996c94138bdf 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -10855,11 +10855,22 @@ pub fn resolveAlignmentForwardReference(b: *Builder, fwd_ref: Alignment.Lazy, va b.alignment_forward_references.items[index] = value; } -pub fn dump(b: *Builder, io: Io) void { - var buffer: [4000]u8 = undefined; - const stderr: Io.File = .stderr(); - b.printToFile(io, stderr, &buffer) catch {}; -} +pub const Print = struct { + name: []const u8, + builder: *Builder, + err: ?Allocator.Error, + + pub fn format(p: *Print, w: *Writer) Writer.Error!void { + try w.print("{q}", .{p.name}); + p.builder.print(w) catch |err| switch (err) { + error.WriteFailed => |e| return e, + error.OutOfMemory => |e| { + p.err = e; + return error.WriteFailed; + }, + }; + } +}; pub fn printToFilePath(b: *Builder, io: Io, dir: Io.Dir, path: []const u8) !void { var buffer: [4000]u8 = undefined; diff --git a/src/Air.zig b/src/Air.zig index 3d6edeb5941bc82eae0ac30205b5dde64887a523..56d2e22b8c8f994cec690ce9d2661001dea16223 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -13,10 +13,10 @@ const InternPool = @import("InternPool.zig"); const Type = @import("Type.zig"); const Value = @import("Value.zig"); const Zcu = @import("Zcu.zig"); -const print = @import("Air/print.zig"); pub const Legalize = @import("Air/Legalize.zig"); pub const Liveness = @import("Air/Liveness.zig"); +pub const Print = @import("Air/Print.zig"); pub const Verify = @import("Air/Verify.zig"); instructions: std.MultiArrayList(Inst).Slice, @@ -2517,10 +2517,10 @@ pub fn unwrapShuffleTwo(air: *const Air, zcu: *const Zcu, inst_index: Inst.Index } pub const legalize = Legalize.legalize; -pub const write = print.write; -pub const writeInst = print.writeInst; -pub const dump = print.dump; -pub const dumpInst = print.dumpInst; +pub const write = Print.write; +pub const writeInst = Print.writeInst; +pub const dump = Print.dump; +pub const dumpInst = Print.dumpInst; pub const CoveragePoint = enum(u1) { /// Indicates the block is not a place of interest corresponding to diff --git a/src/Air/print.zig b/src/Air/Print.zig similarity index 98% rename from src/Air/print.zig rename to src/Air/Print.zig index 6f43909740609da4d5ce53040307dadf3d386e1b..cf61419fa35acd0d56d1c2b86b9f892f2c683864 100644 --- a/src/Air/print.zig +++ b/src/Air/Print.zig @@ -8,9 +8,20 @@ const Value = @import("../Value.zig"); const Type = @import("../Type.zig"); const Air = @import("../Air.zig"); const InternPool = @import("../InternPool.zig"); +const Printer = @This(); + +zcu: *Zcu, +fqn: InternPool.NullTerminatedString, +air: *const Air, +liveness: *const ?Air.Liveness, + +pub fn format(p: *const Printer, w: *std.Io.Writer) std.Io.Writer.Error!void { + try w.print("{qf}:\n", .{p.fqn.fmt(&p.zcu.intern_pool)}); + try p.air.write(w, p.zcu, p.liveness.*); +} pub fn write(air: Air, stream: *std.Io.Writer, zcu: *Zcu, liveness: ?Air.Liveness) !void { - comptime assert(build_options.enable_debug_extensions); + comptime assert(build_options.log_level == .debug); const instruction_bytes = air.instructions.len * // Here we don't use @sizeOf(Air.Inst.Data) because it would include // the debug safety tag but we want to measure release size. @@ -60,7 +71,7 @@ pub fn writeInst( zcu: *Zcu, liveness: ?Air.Liveness, ) void { - comptime assert(build_options.enable_debug_extensions); + comptime assert(build_options.log_level == .debug); var writer: Writer = .{ .zcu = zcu, .gpa = zcu.comp.gpa, diff --git a/src/Compilation.zig b/src/Compilation.zig index 38a905723bce8df08bcb2bf1850ecfd9bfe25870..cae0acc1ffafd1231f9cfd451041d5c0ae536498 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -20,6 +20,7 @@ const link = @import("link.zig"); const tracy = @import("tracy.zig"); const trace = tracy.trace; const build_options = @import("build_options"); +const debugLogScopeEnabled = @import("main.zig").debugLogScopeEnabled; const LibCInstallation = std.zig.LibCInstallation; const glibc = @import("libs/glibc.zig"); const musl = @import("libs/musl.zig"); @@ -161,12 +162,6 @@ clang_passthrough_mode: bool, clang_preprocessor_mode: ClangPreprocessorMode, /// Whether to print clang argvs to stdout. verbose_cc: bool, -verbose_air: bool, -verbose_intern_pool: bool, -verbose_generic_instances: bool, -verbose_llvm_ir: ?[]const u8, -verbose_llvm_bc: ?[]const u8, -verbose_llvm_cpu_features: bool, verbose_link: bool, link_depfile: ?[]const u8, disable_c_depfile: bool, @@ -1544,13 +1539,7 @@ pub const CreateOptions = struct { clang_passthrough_mode: bool = false, verbose_cc: bool = false, verbose_link: bool = false, - verbose_air: bool = false, - verbose_intern_pool: bool = false, - verbose_generic_instances: bool = false, - verbose_llvm_ir: ?[]const u8 = null, - verbose_llvm_bc: ?[]const u8 = null, link_depfile: ?[]const u8 = null, - verbose_llvm_cpu_features: bool = false, debug_compiler_runtime_libs: ?std.lang.Optimize = null, debug_compile_errors: bool = false, debug_incremental: bool = false, @@ -1932,21 +1921,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, try options.root_mod.deps.putNoClobber(arena, "zigc", zigc_mod); } - if (options.verbose_llvm_cpu_features) { - if (options.root_mod.resolved_target.llvm_cpu_features) |cf| { - const stderr = try io.lockStderr(&.{}, null); - defer io.unlockStderr(); - const w = &stderr.file_writer.interface; - printVerboseLlvmCpuFeatures(w, arena, options.root_name, target, cf) catch |err| switch (err) { - error.WriteFailed => switch (stderr.file_writer.err.?) { - error.Canceled => |e| return e, - else => {}, - }, - error.OutOfMemory => |e| return e, - }; - } - } - const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1); const main_mod = options.main_mod orelse options.root_mod; @@ -2111,14 +2085,8 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, .clang_passthrough_mode = options.clang_passthrough_mode, .clang_preprocessor_mode = options.clang_preprocessor_mode, .verbose_cc = options.verbose_cc, - .verbose_air = options.verbose_air, - .verbose_intern_pool = options.verbose_intern_pool, - .verbose_generic_instances = options.verbose_generic_instances, - .verbose_llvm_ir = options.verbose_llvm_ir, - .verbose_llvm_bc = options.verbose_llvm_bc, - .link_depfile = options.link_depfile, - .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, .verbose_link = options.verbose_link, + .link_depfile = options.link_depfile, .disable_c_depfile = options.disable_c_depfile, .reference_trace = options.reference_trace, .time_report = if (options.time_report) .init else null, @@ -2353,8 +2321,9 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, comp.emit_asm != null or comp.emit_llvm_ir != null or comp.emit_llvm_bc != null or - comp.verbose_llvm_ir != null or - comp.verbose_llvm_bc != null)) + log_llvm_ir: { + break :log_llvm_ir debugLogScopeEnabled("llvm-ir") or debugLogScopeEnabled("opt-llvm-ir"); + })) { if (opt_zcu) |zcu| { dev.check(.llvm_backend); @@ -2566,19 +2535,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, return comp; } -fn printVerboseLlvmCpuFeatures( - w: *Writer, - arena: Allocator, - root_name: []const u8, - target: *const std.Target, - cf: [*:0]const u8, -) (Writer.Error || Allocator.Error)!void { - try w.print("compilation: {s}\n", .{root_name}); - try w.print(" target: {s}\n", .{try target.zigTriple(arena)}); - try w.print(" cpu: {s}\n", .{target.cpu.model.name}); - try w.print(" features: {s}\n", .{cf}); -} - pub fn destroy(comp: *Compilation) void { const gpa = comp.gpa; const io = comp.io; @@ -2991,14 +2947,19 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE try pt.processExports(); } - if (build_options.enable_debug_extensions and comp.verbose_intern_pool) { - std.debug.print("intern pool stats for {q}:\n", .{comp.root_name}); - zcu.intern_pool.dump(); - } - - if (build_options.enable_debug_extensions and comp.verbose_generic_instances) { - std.debug.print("generic instances for '{s}:0x{x}':\n", .{ comp.root_name, @intFromPtr(zcu) }); - zcu.intern_pool.dumpGenericInstances(gpa); + { + var d: InternPool.Dump = .{ + .name = comp.root_name, + .arena = arena, + .ip = &zcu.intern_pool, + .err = null, + }; + std.log.scoped(.@"ip-stats").debug("{f}", .{&d}); + if (d.err) |err| return err; + std.log.scoped(.@"ip-values").debug("{f}", .{std.fmt.alt(&d, .formatAll)}); + if (d.err) |err| return err; + std.log.scoped(.@"ip-generic-instances").debug("{f}", .{std.fmt.alt(&d, .formatGenericInstances)}); + if (d.err) |err| return err; } } @@ -3220,28 +3181,29 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error comp.time_report.?.stats.real_ns_llvm_emit = ns; }; - const zcu_obj_path: ?Cache.Path = if (comp.bin_file != null) p: { - break :p try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename); - } else null; + const zcu_obj_path: ?Cache.Path = switch (comp.getTarget().ofmt) { + .llvmbc, .llvmir => null, + else => if (comp.bin_file != null) + try comp.resolveEmitPathFlush(arena, .temp, llvm_object.out_bin_basename) + else + null, + }; const active = zcu.activate(tid); defer active.deactivate(); llvm_object.emit(active.pt, .{ - .pre_ir_path = comp.verbose_llvm_ir, - .pre_bc_path = comp.verbose_llvm_bc, - .bin_path = if (zcu_obj_path) |p| try p.toStringZ(arena) else null, .asm_path = p: { const raw = comp.emit_asm orelse break :p null; const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); break :p try p.toStringZ(arena); }, - .post_ir_path = p: { + .opt_ir_path = p: { const raw = comp.emit_llvm_ir orelse break :p null; const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); break :p try p.toStringZ(arena); }, - .post_bc_path = p: { + .opt_bc_path = p: { const raw = comp.emit_llvm_bc orelse break :p null; const p = try comp.resolveEmitPathFlush(arena, .artifact, raw); break :p try p.toStringZ(arena); @@ -3267,7 +3229,7 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error error.Canceled, error.OutOfMemory => |e| return e, error.AlreadyReported => return, }; - } + } else return; } } if (comp.bin_file) |lf| { @@ -4903,12 +4865,6 @@ fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) SubU .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_intern_pool = comp.verbose_intern_pool, - .verbose_generic_instances = comp.verbose_intern_pool, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .environ_map = comp.environ_map, }) catch |err| switch (err) { error.CreateFail => { @@ -7415,12 +7371,6 @@ fn buildOutputFromZig( .data_sections = true, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_intern_pool = comp.verbose_intern_pool, - .verbose_generic_instances = comp.verbose_intern_pool, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, .environ_map = comp.environ_map, @@ -7552,12 +7502,6 @@ pub fn build_crt_file( .c_source_files = c_source_files, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_intern_pool = comp.verbose_intern_pool, - .verbose_generic_instances = comp.verbose_generic_instances, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, .environ_map = comp.environ_map, diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index fdfd8cfe741e1069aa886213940e8d50a88c7677..4ebe3137be59c9bf90f8cce48e072ff74b413a63 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -190,7 +190,7 @@ pub fn resolve(options: Options) ResolveError!Config { // *If* the LLVM backend were to be selected, should Zig use the LLVM // library to build the LLVM module? const use_lib_llvm = b: { - if (!build_options.have_llvm) { + if (!build_options.have_llvm or target.ofmt == .llvmbc or target.ofmt == .llvmir) { if (options.use_lib_llvm == true) return error.LlvmLibraryUnavailable; break :b false; } @@ -324,7 +324,7 @@ pub fn resolve(options: Options) ResolveError!Config { if (!options.have_zcu) break :b false; // If emitting to LLVM bitcode object format, must use LLVM backend. - if (options.emit_llvm_ir or options.emit_llvm_bc) { + if (target.ofmt == .llvmbc or target.ofmt == .llvmir or options.emit_llvm_bc or options.emit_llvm_ir) { if (options.use_llvm == false) return error.EmittingLlvmModuleRequiresLlvmBackend; if (!target_util.hasLlvmSupport(target, target.ofmt)) @@ -379,7 +379,7 @@ pub fn resolve(options: Options) ResolveError!Config { const backend = target_util.zigBackend(target, use_llvm); if (options.emit_bin and options.have_zcu) { - if (!use_lib_llvm and use_llvm) { + if (target.ofmt != .llvmbc and target.ofmt != .llvmir and !use_lib_llvm and use_llvm) { // Explicit request to use LLVM to produce an object file, but without // using LLVM libraries. Impossible. return error.EmittingBinaryRequiresLlvmLibrary; @@ -513,7 +513,7 @@ pub fn resolve(options: Options) ResolveError!Config { break :b switch (target.ofmt) { .elf, .macho, .wasm => .{ .dwarf = .@"32" }, .coff => .code_view, - .c => switch (target.os.tag) { + .c, .llvmbc, .llvmir => switch (target.os.tag) { .windows, .uefi => .code_view, else => .{ .dwarf = .@"32" }, }, diff --git a/src/InternPool.zig b/src/InternPool.zig index ad7c65bff970b9dc7a5666c56949312fef15663e..77bf96a5f0a46a67a56e1d445d3a9d9e3ff4e5d7 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -10591,17 +10591,36 @@ pub fn errorUnionPayload(ip: *const InternPool, ty: Index) Index { return ip.indexToKey(ty).error_union_type.payload_type; } -pub fn dump(ip: *const InternPool) void { - var buffer: [4096]u8 = undefined; - const stderr = std.debug.lockStderr(&buffer); - defer std.debug.unlockStderr(); - const w = &stderr.file_writer.interface; - dumpDependencyStatsFallible(ip, w) catch return; - dumpStatsFallible(ip, w, std.heap.page_allocator) catch return; - dumpAllFallible(ip, w) catch return; -} - -fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void { +pub const Dump = struct { + name: []const u8, + arena: std.mem.Allocator, + ip: *const InternPool, + err: ?Allocator.Error, + + pub fn format(d: *Dump, w: *Io.Writer) Io.Writer.Error!void { + try w.print("{q}:\n", .{d.name}); + try d.ip.dumpDependencyStatsFallible(w); + d.ip.dumpStatsFallible(d.arena, w) catch |err| switch (err) { + error.WriteFailed => |e| return e, + error.OutOfMemory => |e| { + d.err = e; + return error.WriteFailed; + }, + }; + } + + pub fn formatAll(d: *Dump, w: *Io.Writer) Io.Writer.Error!void { + try w.print("{q}:\n", .{d.name}); + try d.ip.dumpAllFallible(w); + } + + pub fn formatGenericInstances(d: *Dump, w: *Io.Writer) Io.Writer.Error!void { + try w.print("{q}:\n", .{d.name}); + d.ip.dumpGenericInstancesFallible(d.arena, w) catch return; + } +}; + +fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) Io.Writer.Error!void { const dep_entries_len = ip.dep_entries.items.len - ip.free_dep_entries.items.len; const src_hash_deps_len = ip.src_hash_deps.count(); const nav_val_deps_len = ip.nav_val_deps.count(); @@ -10668,7 +10687,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void { }); } -fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !void { +fn dumpStatsFallible(ip: *const InternPool, arena: Allocator, w: *Io.Writer) (Allocator.Error || Io.Writer.Error)!void { var items_len: usize = 0; var extra_len: usize = 0; var limbs_len: usize = 0; @@ -10968,7 +10987,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo } } -fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { +fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) Io.Writer.Error!void { for (ip.locals, 0..) |*local, tid| { // Early check for length 0, because `view()` is invalid if capacity is 0 if (local.mutate.items.len == 0) continue; @@ -11071,21 +11090,10 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void { } } -pub fn dumpGenericInstances(ip: *const InternPool, allocator: Allocator) void { - var buffer: [4096]u8 = undefined; - const stderr = std.debug.lockStderr(&buffer); - defer std.debug.unlockStderr(); - const w = &stderr.file_writer.interface; - ip.dumpGenericInstancesFallible(allocator, w) catch return; -} - -pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator, w: *Io.Writer) !void { - var arena_allocator = std.heap.ArenaAllocator.init(allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - +pub fn dumpGenericInstancesFallible(ip: *const InternPool, arena: Allocator, w: *Io.Writer) !void { var instances: std.array_hash_map.Auto(Index, std.ArrayList(Index)) = .empty; for (ip.locals, 0..) |*local, tid| { + if (local.mutate.items.len == 0) continue; const items = local.shared.items.view().slice(); const extra_list = local.shared.extra; for ( diff --git a/src/Module.zig b/src/Module.zig index 5154b853641c4ed2af01fd0aed1b8c1f911204a6..02df432d5ef580021369e4e2ea74b24d17d6be13 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -26,6 +26,7 @@ fully_qualified_name: []const u8, deps: Deps = .{}, resolved_target: ResolvedTarget, +llvm_cpu_features: ?[:0]const u8, optimize_mode: std.lang.Optimize, code_model: std.lang.CodeModel, single_threaded: bool, @@ -94,7 +95,6 @@ pub const ResolvedTarget = struct { is_native_os: bool, is_native_abi: bool, is_explicit_dynamic_linker: bool, - llvm_cpu_features: ?[*:0]const u8 = null, }; pub const CreateError = error{ @@ -362,15 +362,13 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Module { break :b 0; }; - const llvm_cpu_features: ?[*:0]const u8 = b: { - if (resolved_target.llvm_cpu_features) |x| break :b x; + const llvm_cpu_features: ?[:0]const u8 = b: { if (!options.global.use_llvm) break :b null; + if (options.inherited.resolved_target == null) break :b options.parent.?.llvm_cpu_features; + // Append disabled features after enabled ones, so that their effects aren't overwritten. var buf = std.array_list.Managed(u8).init(arena); var disabled_features = std.array_list.Managed(u8).init(arena); - defer disabled_features.deinit(); - - // Append disabled features after enabled ones, so that their effects aren't overwritten. for (target.cpu.arch.allFeaturesList()) |feature| { if (feature.llvm_name) |llvm_name| { // Ignore these until we figure out how to handle the concept of omitting features. @@ -401,10 +399,9 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Module { try buf.appendSlice(disabled_features.items); if (buf.items.len == 0) break :b ""; - assert(std.mem.endsWith(u8, buf.items, ",")); + assert(buf.items[buf.items.len - 1] == ','); buf.items[buf.items.len - 1] = 0; - buf.shrinkAndFree(buf.items.len); - break :b buf.items[0 .. buf.items.len - 1 :0].ptr; + break :b buf.items[0 .. buf.items.len - 1 :0]; }; const mod = try arena.create(Module); @@ -417,8 +414,8 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Module { .is_native_os = resolved_target.is_native_os, .is_native_abi = resolved_target.is_native_abi, .is_explicit_dynamic_linker = resolved_target.is_explicit_dynamic_linker, - .llvm_cpu_features = llvm_cpu_features, }, + .llvm_cpu_features = llvm_cpu_features, .optimize_mode = optimize_mode, .single_threaded = single_threaded, .error_tracing = error_tracing, @@ -494,8 +491,8 @@ pub fn createBuiltin(arena: Allocator, opts: Builtin, dirs: std.zig.Directories) .is_native_os = false, .is_native_abi = false, .is_explicit_dynamic_linker = false, - .llvm_cpu_features = null, }, + .llvm_cpu_features = null, .optimize_mode = opts.optimize_mode, .single_threaded = opts.single_threaded, .error_tracing = opts.error_tracing, diff --git a/src/Sema.zig b/src/Sema.zig index ca4ccd20c9652e741cfa7badfeafad4b8324338b..e5ee288187f627f9438a64f3d91eac833268ed9c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1126,6 +1126,8 @@ fn analyzeBodyInner( crash_info.push(sema, block, body); defer crash_info.pop(); + const path = zcu.namespacePtr(block.namespace).fileScope(zcu).path; + // We use a while (true) loop here to avoid a redundant way of breaking out of // the loop. The only way to break out of the loop is with a `noreturn` // instruction. @@ -1134,11 +1136,7 @@ fn analyzeBodyInner( crash_info.setBodyIndex(i); const inst = body[i]; - // This log is a bit expensive, and LLVM fails to optimize it away. - if (build_options.enable_logging) { - const path = zcu.namespacePtr(block.namespace).fileScope(zcu).path; - std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path.fmt(comp), inst }); - } + std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path.fmt(comp), inst }); const air_ref: Air.Inst.Ref = inst: switch (tags[@backingInt(inst)]) { // zig fmt: off diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index ec764ddfdefc972b4ff9bfbc3d8a920cdabe9762..bb09f50cf02be848121d63c01e679232a6324ad7 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -25,6 +25,7 @@ const InternPool = @import("../InternPool.zig"); const AnalUnit = InternPool.AnalUnit; const Module = @import("../Module.zig"); const Sema = @import("../Sema.zig"); +const debugLogScopeEnabled = @import("../main.zig").debugLogScopeEnabled; const target_util = @import("../target.zig"); const tracy = @import("../tracy.zig"); const trace = tracy.trace; @@ -2303,10 +2304,9 @@ fn analyzeFuncBody( const comp = zcu.comp; - const dump_air = build_options.enable_debug_extensions and comp.verbose_air; - const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); - - if (comp.bin_file != null or zcu.llvm_object != null or dump_air or dump_llvm_ir) { + if (comp.bin_file != null or zcu.llvm_object != null or log_air: { + break :log_air debugLogScopeEnabled("illegal-air") or debugLogScopeEnabled("legal-air") or debugLogScopeEnabled("air"); + }) { zcu.codegen_prog_node.increaseEstimatedTotalItems(1); comp.link_prog_node.increaseEstimatedTotalItems(1); @@ -4522,32 +4522,28 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e tracy_trace.addText(fqn.toSlice(ip)); tracy_trace.addTextFmt("func_ip_index={d}", .{func_index}); - Air.Verify.run(pt, func_index, air); + var liveness: ?Air.Liveness = null; + defer if (liveness) |*l| l.deinit(gpa); + + const ap: Air.Print = .{ + .zcu = zcu, + .fqn = fqn, + .air = air, + .liveness = &liveness, + }; + std.log.scoped(.@"illegal-air").debug("{f}", .{&ap}); if (codegen.legalizeFeatures(pt, nav)) |features| { - try air.legalize(pt, features); - // Verify the AIR again post-legalization. + // Verify the AIR again pre-legalization. Air.Verify.run(pt, func_index, air); + try air.legalize(pt, features); } - var liveness: ?Air.Liveness = if (codegen.wantsLiveness(pt, nav)) - try .analyze(zcu, air.*, ip) - else - null; - defer if (liveness) |*l| l.deinit(gpa); - - if (build_options.enable_debug_extensions and comp.verbose_air) p: { - const io = comp.io; - const stderr = try io.lockStderr(&.{}, null); - defer io.unlockStderr(); - printVerboseAir(pt, liveness, fqn, air, &stderr.file_writer.interface) catch |err| switch (err) { - error.WriteFailed => switch (stderr.file_writer.err.?) { - error.Canceled => |e| return e, - else => break :p, - }, - }; - } + std.log.scoped(.@"legal-air").debug("{f}", .{&ap}); + Air.Verify.run(pt, func_index, air); + if (codegen.wantsLiveness(pt, nav)) liveness = try .analyze(zcu, air.*, ip); + std.log.scoped(.air).debug("{f}", .{&ap}); if (std.debug.runtime_safety) verify_liveness: { var verify: Air.Liveness.Verify = .{ .gpa = gpa, @@ -4577,17 +4573,3 @@ fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) e return codegen.generateFunction(lf, pt, func_index, air, &liveness); } - -fn printVerboseAir( - pt: Zcu.PerThread, - liveness: ?Air.Liveness, - fqn: InternPool.NullTerminatedString, - air: *const Air, - w: *Io.Writer, -) Io.Writer.Error!void { - const zcu = pt.zcu; - const ip = &zcu.intern_pool; - try w.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}); - try air.write(w, zcu, liveness); - try w.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)}); -} diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f4198c2db8da81c76fcb743e8e5dd972ede77bef..279979085b12c4da65da09f2868676ca567af7e0 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -10,6 +10,7 @@ const build_options = @import("build_options"); const Air = @import("../Air.zig"); const codegen = @import("../codegen.zig"); const Compilation = @import("../Compilation.zig"); +const debugLogScopeEnabled = @import("../main.zig").debugLogScopeEnabled; const InternPool = @import("../InternPool.zig"); const link = @import("../link.zig"); const Module = @import("../Module.zig"); @@ -331,12 +332,10 @@ pub const Object = struct { } pub const EmitOptions = struct { - pre_ir_path: ?[]const u8, - pre_bc_path: ?[]const u8, bin_path: ?[:0]const u8, asm_path: ?[:0]const u8, - post_ir_path: ?[:0]const u8, - post_bc_path: ?[:0]const u8, + opt_ir_path: ?[:0]const u8, + opt_bc_path: ?[:0]const u8, is_debug: bool, is_small: bool, @@ -351,6 +350,7 @@ pub const Object = struct { const comp = zcu.comp; const io = comp.io; const diags = &comp.link_diags; + const target = &comp.root_mod.resolved_target.result; { if (o.errors_len_variable != .none) { @@ -403,7 +403,7 @@ pub const Object = struct { const behavior_max = try o.builder.metadataConstant(try o.builder.intConst(.i32, 7)); const behavior_min = try o.builder.metadataConstant(try o.builder.intConst(.i32, 8)); - if (target_util.llvmMachineAbi(&comp.root_mod.resolved_target.result)) |abi| { + if (target_util.llvmMachineAbi(target)) |abi| { module_flags.appendAssumeCapacity(try o.builder.metadataTuple(&.{ behavior_error, (try o.builder.metadataString("target-abi")).toMetadata(), @@ -411,7 +411,7 @@ pub const Object = struct { })); } - const pic_level = target_util.picLevel(&comp.root_mod.resolved_target.result); + const pic_level = target_util.picLevel(target); if (comp.root_mod.pic) { module_flags.appendAssumeCapacity(try o.builder.metadataTuple(&.{ behavior_min, @@ -434,7 +434,7 @@ pub const Object = struct { (try o.builder.metadataString("Code Model")).toMetadata(), try o.builder.metadataConstant(try o.builder.intConst(.i32, @as( i32, - switch (codeModel(comp.root_mod.code_model, &comp.root_mod.resolved_target.result)) { + switch (codeModel(comp.root_mod.code_model, target)) { .default => unreachable, .tiny => 0, .small => 1, @@ -480,7 +480,6 @@ pub const Object = struct { } } - const target = &comp.root_mod.resolved_target.result; if (target.os.tag == .windows and (target.cpu.arch == .x86_64 or target.cpu.arch == .x86)) { // Add the "RegCallv4" flag so that any functions using `x86_regcallcc` use regcall // v4, which is essentially a requirement on Windows. See corresponding logic in @@ -515,21 +514,26 @@ pub const Object = struct { const emit_asm_msg = options.asm_path orelse "(none)"; const emit_bin_msg = options.bin_path orelse "(none)"; - const post_llvm_ir_msg = options.post_ir_path orelse "(none)"; - const post_llvm_bc_msg = options.post_bc_path orelse "(none)"; + const opt_llvm_ir_msg = options.opt_ir_path orelse "(none)"; + const opt_llvm_bc_msg = options.opt_bc_path orelse "(none)"; log.debug("emit LLVM object asm={s} bin={s} ir={s} bc={s}", .{ - emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, + emit_asm_msg, emit_bin_msg, opt_llvm_ir_msg, opt_llvm_bc_msg, }); + const log_opt_llvm_ir = debugLogScopeEnabled("opt-llvm-ir"); const context, const module = emit: { - if (options.pre_ir_path) |path| { - if (std.mem.eql(u8, path, "-")) { - o.builder.dump(io); - } else { - o.builder.printToFilePath(io, Io.Dir.cwd(), path) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {t}", .{ path, err }); - }; - } + { + var p: Builder.Print = .{ .name = comp.root_name, .builder = &o.builder, .err = null }; + std.log.scoped(.@"llvm-ir").debug("{f}", .{&p}); + if (p.err) |err| return err; + } + + if (target.ofmt == .llvmir) { + const path = comp.bin_file.?.emit; + o.builder.printToFilePath(io, path.root_dir.handle, path.sub_path) catch |err| { + return diags.fail("failed emitting LLVM module to {qf}: {t}", .{ path, err }); + }; + return; } const bitcode = try o.builder.toBitcode(o.gpa, .{ @@ -538,23 +542,26 @@ pub const Object = struct { }); defer o.gpa.free(bitcode); - if (options.pre_bc_path) |path| { - var file = Io.Dir.cwd().createFile(io, path, .{}) catch |err| - return diags.fail("failed to create '{s}': {t}", .{ path, err }); + if (target.ofmt == .llvmbc) { + const path = comp.bin_file.?.emit; + var file = path.root_dir.handle.createFile(io, path.sub_path, .{}) catch |err| + return diags.fail("failed to create {qf}: {t}", .{ path, err }); defer file.close(io); file.writeStreamingAll(io, @ptrCast(bitcode)) catch |err| - return diags.fail("failed to write to '{s}': {t}", .{ path, err }); + return diags.fail("failed to write to {qf}: {t}", .{ path, err }); + return; } if (options.asm_path == null and options.bin_path == null and - options.post_ir_path == null and options.post_bc_path == null) return; + options.opt_ir_path == null and options.opt_bc_path == null and + !log_opt_llvm_ir) return; if (!build_options.have_llvm or !comp.config.use_lib_llvm) { return diags.fail("emitting without libllvm not implemented", .{}); } - initializeLLVMTarget(io, comp.root_mod.resolved_target.result.cpu.arch); + initializeLLVMTarget(io, target.cpu.arch); const context: *bindings.Context = .create(); errdefer context.dispose(); @@ -577,9 +584,9 @@ pub const Object = struct { }; defer context.dispose(); - var target: *bindings.Target = undefined; + var llvm_target: *bindings.Target = undefined; var error_message: [*:0]const u8 = undefined; - if (bindings.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) { + if (bindings.Target.getFromTriple(target_triple_sentinel, &llvm_target, &error_message).toBool()) { defer bindings.disposeMessage(error_message); return diags.fail("LLVM failed to parse '{s}': {s}", .{ target_triple_sentinel, error_message }); } @@ -598,7 +605,7 @@ pub const Object = struct { else .Static; - const code_model: bindings.CodeModel = switch (codeModel(comp.root_mod.code_model, &comp.root_mod.resolved_target.result)) { + const code_model: bindings.CodeModel = switch (codeModel(comp.root_mod.code_model, target)) { .default => .Default, .tiny => .Tiny, .small => .Small, @@ -607,24 +614,22 @@ pub const Object = struct { .large => .Large, }; - const float_abi: bindings.TargetMachine.FloatABI = if (comp.root_mod.resolved_target.result.abi.float() == .hard) - .Hard - else - .Soft; - var target_machine = bindings.TargetMachine.create( - target, + llvm_target, target_triple_sentinel, - if (comp.root_mod.resolved_target.result.cpu.model.llvm_name) |s| s.ptr else null, - comp.root_mod.resolved_target.llvm_cpu_features.?, + if (target.cpu.model.llvm_name) |s| s.ptr else null, + comp.root_mod.llvm_cpu_features.?, opt_level, reloc_mode, code_model, comp.function_sections, comp.data_sections, - float_abi, - if (target_util.llvmMachineAbi(&comp.root_mod.resolved_target.result)) |s| s.ptr else null, - target_util.useEmulatedTls(&comp.root_mod.resolved_target.result), + switch (target.abi.float()) { + .hard => .Hard, + .soft => .Soft, + }, + if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, + target_util.useEmulatedTls(target), ); errdefer target_machine.dispose(); @@ -634,7 +639,10 @@ pub const Object = struct { // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. // So we call the entire pipeline multiple times if this is requested. - // var error_message: [*:0]const u8 = undefined; + var time_report_c_str: ?[*:0]u8 = null; + defer if (time_report_c_str) |time_report| bindings.disposeMessage(time_report); + var llvm_ir_c_str: ?[*:0]u8 = null; + defer if (llvm_ir_c_str) |llvm_ir| bindings.disposeMessage(llvm_ir); var lowered_options: bindings.TargetMachine.EmitOptions = .{ .is_debug = options.is_debug, .is_small = options.is_small, @@ -648,11 +656,12 @@ pub const Object = struct { .allow_fast_isel = true, // LLVM's RISC-V backend for some reason enables the machine outliner by default even // though it's clearly not ready and produces multiple miscompilations in our std tests. - .allow_machine_outliner = !comp.root_mod.resolved_target.result.cpu.arch.isRISCV(), + .allow_machine_outliner = !target.cpu.arch.isRISCV(), .asm_filename = null, .bin_filename = if (options.bin_path) |x| x.ptr else null, - .llvm_ir_filename = if (options.post_ir_path) |x| x.ptr else null, - .bitcode_filename = if (options.post_bc_path) |x| x.ptr else null, + .llvm_ir_filename = if (options.opt_ir_path) |x| x.ptr else null, + .bitcode_filename = if (options.opt_bc_path) |x| x.ptr else null, + .llvm_ir_out = if (log_opt_llvm_ir) &llvm_ir_c_str else null, // `.coverage` value is only used when `.sancov` is enabled. .sancov = options.fuzz or comp.config.san_cov_trace_pc_guard, @@ -684,33 +693,31 @@ pub const Object = struct { }; if (options.asm_path != null and options.bin_path != null) { if (target_machine.emitToFile(module, &error_message, &lowered_options)) { - defer bindings.disposeMessage(error_message); return diags.fail("LLVM failed to emit bin={s} ir={s}: {s}", .{ - emit_bin_msg, post_llvm_ir_msg, error_message, + emit_bin_msg, opt_llvm_ir_msg, error_message, }); } lowered_options.bin_filename = null; lowered_options.llvm_ir_filename = null; lowered_options.bitcode_filename = null; + lowered_options.llvm_ir_out = null; } - - var time_report_c_str: [*:0]u8 = undefined; - if (options.time_report != null) { - lowered_options.time_report_out = &time_report_c_str; - } - + if (options.time_report) |_| lowered_options.time_report_out = &time_report_c_str; lowered_options.asm_filename = if (options.asm_path) |x| x.ptr else null; if (target_machine.emitToFile(module, &error_message, &lowered_options)) { defer bindings.disposeMessage(error_message); return diags.fail("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ - emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message, + emit_asm_msg, emit_bin_msg, opt_llvm_ir_msg, opt_llvm_bc_msg, error_message, }); } - if (options.time_report) |tr| { - defer std.c.free(time_report_c_str); - const time_report_data = std.mem.span(time_report_c_str); - assert(tr.llvm_pass_timings.len == 0); - tr.llvm_pass_timings = try comp.gpa.dupe(u8, time_report_data); + if (log_opt_llvm_ir) std.log.scoped(.@"opt-llvm-ir").debug("{s}", .{ + llvm_ir_c_str orelse return error.OutOfMemory, + }); + if (options.time_report) |time_report| { + assert(time_report.llvm_pass_timings.len == 0); + time_report.llvm_pass_timings = try comp.gpa.dupe(u8, std.mem.span( + time_report_c_str orelse return error.OutOfMemory, + )); } } @@ -2184,10 +2191,10 @@ pub const Object = struct { .value = try o.builder.string(s), } }, &o.builder); } - if (owner_mod.resolved_target.llvm_cpu_features) |s| { + if (owner_mod.llvm_cpu_features) |s| { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("target-features"), - .value = try o.builder.string(std.mem.span(s)), + .value = try o.builder.string(s), } }, &o.builder); } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index a312285db3310ba0fc69dffe55d56e484c293e50..7ee71c237c9a07763b80d844e4c559cc88bf1934 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -51,7 +51,7 @@ pub const Module = opaque { }; pub const disposeMessage = LLVMDisposeMessage; -extern fn LLVMDisposeMessage(Message: [*:0]const u8) void; +extern fn LLVMDisposeMessage(Message: ?[*:0]const u8) void; pub const TargetMachine = opaque { pub const FloatABI = enum(c_int) { @@ -85,7 +85,7 @@ pub const TargetMachine = opaque { pub const EmitOptions = extern struct { is_debug: bool, is_small: bool, - time_report_out: ?*[*:0]u8, + time_report_out: ?*?[*:0]u8, tsan: bool, sancov: bool, lto: LtoPhase, @@ -95,6 +95,7 @@ pub const TargetMachine = opaque { bin_filename: ?[*:0]const u8, llvm_ir_filename: ?[*:0]const u8, bitcode_filename: ?[*:0]const u8, + llvm_ir_out: ?*?[*:0]u8, coverage: Coverage, pub const LtoPhase = enum(c_int) { diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig index 55a1e138d1ab01c5744f1ff7cb3f3c8181b8c715..c01390c2d9ec26d2da3f227eafc7ed7b98b30ba2 100644 --- a/src/codegen/riscv64/CodeGen.zig +++ b/src/codegen/riscv64/CodeGen.zig @@ -27,7 +27,7 @@ const Target = std.Target; const log = std.log.scoped(.riscv_codegen); const tracking_log = std.log.scoped(.tracking); const verbose_tracking_log = std.log.scoped(.verbose_tracking); -const wip_mir_log = std.log.scoped(.wip_mir); +const wip_mir_log = std.log.scoped(.@"wip-mir"); const Alignment = InternPool.Alignment; const bits = @import("bits.zig"); diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig index 4f9b9ef0b0a2c6d3df51f642256e83b7cdda8a24..e3cfed33b33300e71e703147362bf501873df1d6 100644 --- a/src/codegen/x86_64/CodeGen.zig +++ b/src/codegen/x86_64/CodeGen.zig @@ -5,7 +5,7 @@ const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); const tracking_log = std.log.scoped(.tracking); const verbose_tracking_log = std.log.scoped(.verbose_tracking); -const wip_mir_log = std.log.scoped(.wip_mir); +const wip_mir_log = std.log.scoped(.@"wip-mir"); const Writer = std.Io.Writer; const Air = @import("../../Air.zig"); diff --git a/src/dev.zig b/src/dev.zig index b9e448ef7e53668bfac8044f6dfd3cabf6ead9bd..aaed1c88140240a0794a8b4e94f73e5e219452cf 100644 --- a/src/dev.zig +++ b/src/dev.zig @@ -112,6 +112,7 @@ pub const Env = enum { .loongarch_backend, .spork8_backend, .lld_linker, + .llvm_linker, .coff_linker, .elf_linker, .elf2_linker, @@ -333,6 +334,7 @@ pub const Feature = enum { spork8_backend, lld_linker, + llvm_linker, coff_linker, elf_linker, elf2_linker, diff --git a/src/libs/freebsd.zig b/src/libs/freebsd.zig index 584a6b63b69adae9e3427334567bd41357056543..8f07d8fb8a6d12cc1f54b4f656a9d269008a82c7 100644 --- a/src/libs/freebsd.zig +++ b/src/libs/freebsd.zig @@ -1120,10 +1120,6 @@ fn buildSharedLib( .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .version = version, .version_script = .{ diff --git a/src/libs/glibc.zig b/src/libs/glibc.zig index 024588644a8c69c12fc323c4c812468abff565d1..4da85e7df75ac5db630273b364d8ee675b94738d 100644 --- a/src/libs/glibc.zig +++ b/src/libs/glibc.zig @@ -1268,10 +1268,6 @@ fn buildSharedLib( .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .version = version, .version_script = .{ diff --git a/src/libs/libcxx.zig b/src/libs/libcxx.zig index 993f9ee2f8277acb627dd28ab918fab6a7703c64..376887fd9ecedeaf9c1eb1096a9637ae930745cd 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -260,10 +260,6 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, .environ_map = comp.environ_map, @@ -445,10 +441,6 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, .environ_map = comp.environ_map, diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 7212a17e38e5dba06208c23feb698ad46527a20e..bc8cacd84cfb167591271a7ef0cd6753dd143923 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -295,10 +295,6 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = skip_linker_dependencies, .linker_allow_shlib_undefined = linker_allow_shlib_undefined, diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index fb3b20684bc90117d10bebd2082ac97367124be1..d8d3456d1572b84d0d742dc27b5f22d8375909db 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -157,10 +157,6 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr .c_source_files = &c_source_files, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, .environ_map = comp.environ_map, diff --git a/src/libs/musl.zig b/src/libs/musl.zig index eca461940ce84ad1aa8d26dd406b6c4c3100dbb3..346563b4ce2f6886f53f8222ccd249a4686fabf0 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -246,9 +246,6 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro .emit_bin = .yes_cache, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .c_source_files = &.{ .{ diff --git a/src/libs/netbsd.zig b/src/libs/netbsd.zig index 46a34b96a55943f84a92914521a730c27d0d19a7..3a31b8f8d577cd6c6827c13bb3abe9e3f4244f93 100644 --- a/src/libs/netbsd.zig +++ b/src/libs/netbsd.zig @@ -771,10 +771,6 @@ fn buildSharedLib( .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .version = version, .soname = soname, diff --git a/src/libs/openbsd.zig b/src/libs/openbsd.zig index 4bea660a65eed944eb93d48f27c5cc70a0369a8d..b16c0189f8a8fca0713f220c6dd41aaaadb2f940 100644 --- a/src/libs/openbsd.zig +++ b/src/libs/openbsd.zig @@ -691,10 +691,6 @@ fn buildSharedLib( .emit_bin = .{ .yes_path = try bin_directory.join(arena, &.{basename}) }, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, - .verbose_air = comp.verbose_air, - .verbose_llvm_ir = comp.verbose_llvm_ir, - .verbose_llvm_bc = comp.verbose_llvm_bc, - .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .soname = soname, .c_source_files = &c_source_files, diff --git a/src/link.zig b/src/link.zig index 5b1b0bbeb601c90013a603f4014b1c32254a8634..843cb395fa06130840298a7986d1ebf2653478e5 100644 --- a/src/link.zig +++ b/src/link.zig @@ -550,6 +550,7 @@ pub const File = struct { return &ptr.base; }, .lld => unreachable, // not known from ofmt + .llvm => |tag| return createDummy(arena, tag, comp, emit), } } @@ -573,9 +574,26 @@ pub const File = struct { return &ptr.base; }, .lld => unreachable, // not known from ofmt + .llvm => |tag| return createDummy(arena, tag, comp, emit), } } + fn createDummy(arena: Allocator, tag: Tag, comp: *Compilation, path: Path) Allocator.Error!*File { + const base = try arena.create(File); + base.* = .{ + .tag = tag, + .comp = comp, + .emit = path, + .file = null, + .gc_sections = false, + .print_gc_sections = false, + .build_id = .none, + .allow_shlib_undefined = false, + .stack_size = 0, + }; + return base; + } + pub fn cast(base: *File, comptime tag: Tag) if (dev.env.supports(tag.devFeature())) ?*tag.Type() else ?noreturn { return if (dev.env.supports(tag.devFeature()) and base.tag == tag) @fieldParentPtr("base", base) else null; } @@ -606,7 +624,7 @@ pub const File = struct { const gpa = comp.gpa; const io = comp.io; switch (base.tag) { - .lld => assert(base.file == null), + .lld, .llvm => assert(base.file == null), .elf, .macho, .wasm => { dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker }); if (base.file != null) return; @@ -706,7 +724,7 @@ pub const File = struct { if (!base.canMakeExecutable()) return; switch (base.tag) { - .lld => assert(base.file == null), + .lld, .llvm => assert(base.file == null), .elf => if (base.file) |f| { dev.check(.elf_linker); f.close(io); @@ -827,6 +845,7 @@ pub const File = struct { switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); @@ -862,6 +881,7 @@ pub const File = struct { assert(pt.zcu.llvm_object == null); switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); @@ -897,6 +917,7 @@ pub const File = struct { } switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .plan9 => unreachable, .spirv => {}, .coff => {}, @@ -941,6 +962,7 @@ pub const File = struct { base.releaseLock(); if (base.file) |f| f.close(io); switch (base.tag) { + .llvm => {}, .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); @@ -1003,6 +1025,7 @@ pub const File = struct { } assert(base.post_prelink); switch (base.tag) { + .llvm => {}, .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); @@ -1030,6 +1053,7 @@ pub const File = struct { switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .plan9 => unreachable, inline else => |tag| { dev.check(tag.devFeature()); @@ -1056,6 +1080,7 @@ pub const File = struct { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .c => unreachable, .spirv => unreachable, .wasm => unreachable, @@ -1078,6 +1103,7 @@ pub const File = struct { assert(pt.zcu.llvm_object == null); switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .c => unreachable, .spirv => unreachable, .wasm => unreachable, @@ -1095,6 +1121,7 @@ pub const File = struct { assert(base.comp.zcu.?.llvm_object == null); switch (base.tag) { .lld => unreachable, + .llvm => unreachable, .c => unreachable, .spirv => unreachable, .wasm => unreachable, @@ -1124,6 +1151,7 @@ pub const File = struct { .spirv, .plan9, .lld, + .llvm, .spork8, => return .unimplemented, inline else => |tag| { @@ -1240,7 +1268,7 @@ pub const File = struct { dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input); }, - .c, .spork8, .plan9, .lld => {}, + .c, .spork8, .plan9, .lld, .llvm => {}, } } @@ -1352,6 +1380,7 @@ pub const File = struct { coff, elf, elf2, + llvm, macho, macho2, c, @@ -1366,13 +1395,14 @@ pub const File = struct { .coff => Coff, .elf => Elf, .elf2 => Elf2, + .llvm => File, .macho => MachO, .macho2 => MachO2, .c => C, .wasm => Wasm, .spirv => Spirv, .lld => Lld, - .plan9 => comptime unreachable, + .plan9 => unreachable, .spork8 => Spork8, }; } @@ -1381,6 +1411,7 @@ pub const File = struct { return switch (ofmt) { .coff => .coff, .elf => if (use_new_linker) .elf2 else .elf, + .llvmbc, .llvmir => .llvm, .macho => if (use_new_linker) .macho2 else .macho, .wasm => .wasm, .plan9 => .plan9, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 828f707582aa3753709e623e1662641fd505358b..1067b9a50a525a99213d7a02fd988fb187561b5b 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -860,9 +860,7 @@ fn flushInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { // Dump the state for easy debugging. // State can be dumped via `--debug-log link_state`. - if (build_options.enable_logging) { - state_log.debug("{f}", .{self.dumpState()}); - } + state_log.debug("{f}", .{self.dumpState()}); // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations, and commit objects to file. diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index b0a9c43db2de32654acb8e52407103b766e54a11..08ba32f95e93d7a5c267137a9323187b7f12924b 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -291,7 +291,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed; } - if (build_options.enable_logging) { + if (build_options.enable_debug_extensions) { const active = elf_file.base.comp.zcu.?.activate(tid); defer active.deactivate(); for (self.navs.keys(), self.navs.values()) |nav_index, meta| { diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig index 29bdaae801109683381a5b3bbc18f05d7fb43a56..98941b5d73ff68f9e4220301c3246166f7c33414 100644 --- a/src/link/Elf/gc.zig +++ b/src/link/Elf/gc.zig @@ -98,7 +98,7 @@ fn markAtom(atom: *Atom) bool { } fn markLive(atom: *Atom, elf_file: *Elf) void { - if (@import("build_options").enable_logging) track_live_level.incr(); + if (@import("build_options").enable_debug_extensions) track_live_level.incr(); assert(atom.visited); const file = atom.file(elf_file).?; diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index e5a5e55b29f0edd8d4733e7f0fafede1e0da201c..d45995054da762491085cdff360bb32ac63f7a52 100644 --- a/src/link/Elf/relocatable.zig +++ b/src/link/Elf/relocatable.zig @@ -51,9 +51,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void { try allocateAllocSections(elf_file); try elf_file.allocateNonAllocSections(); - if (build_options.enable_logging) { - state_log.debug("{f}", .{elf_file.dumpState()}); - } + state_log.debug("{f}", .{elf_file.dumpState()}); try elf_file.writeMergeSections(); try writeSyntheticSections(elf_file); @@ -117,10 +115,8 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void { break :blk pos; }; - if (build_options.enable_logging) { - state_log.debug("ar_symtab\n{f}\n", .{ar_symtab.fmt(elf_file)}); - state_log.debug("ar_strtab\n{f}\n", .{ar_strtab}); - } + state_log.debug("ar_symtab\n{f}\n", .{ar_symtab.fmt(elf_file)}); + state_log.debug("ar_strtab\n{f}\n", .{ar_strtab}); const buffer = try gpa.alloc(u8, total_size); defer gpa.free(buffer); @@ -193,9 +189,7 @@ pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void { try allocateAllocSections(elf_file); try elf_file.allocateNonAllocSections(); - if (build_options.enable_logging) { - state_log.debug("{f}", .{elf_file.dumpState()}); - } + state_log.debug("{f}", .{elf_file.dumpState()}); try writeAtoms(elf_file); try elf_file.writeMergeSections(); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3f5f7951bf101c768beea37c920f216ccbd84daf..c1b76db1b10be51d584e54a48767a9b9d84d727f 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -456,9 +456,7 @@ pub fn flush( self.allocateSegments(); self.allocateSyntheticSymbols(); - if (build_options.enable_logging) { - state_log.debug("{f}", .{self.dumpState()}); - } + state_log.debug("{f}", .{self.dumpState()}); // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations, and commit objects to file. diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 3c17ead455821921e85cd5cbfd4d6e14828f8bba..f5091220876875394740038ee46f2e80e9fde370 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -123,8 +123,9 @@ fn markLive(atom: *Atom, macho_file: *MachO) void { atom.getName(macho_file), }); - if (build_options.enable_logging) + if (build_options.enable_debug_extensions) { track_live_level.incr(); + } for (atom.getRelocs(macho_file)) |rel| { const target_atom = switch (rel.tag) { diff --git a/src/link/MachO/relocatable.zig b/src/link/MachO/relocatable.zig index cd1231bebfa1cb97b082eacb705203afe8435daf..bf7080e31ea681a47ea35ef075f17f9ef8470645 100644 --- a/src/link/MachO/relocatable.zig +++ b/src/link/MachO/relocatable.zig @@ -61,9 +61,7 @@ pub fn flushObject(macho_file: *MachO, comp: *Compilation) link.Error!void { }; allocateSegment(macho_file); - if (build_options.enable_logging) { - state_log.debug("{f}", .{macho_file.dumpState()}); - } + state_log.debug("{f}", .{macho_file.dumpState()}); try writeSections(macho_file); sortRelocs(macho_file); @@ -107,9 +105,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void { return diags.fail("failed to allocate sections: {s}", .{@errorName(err)}); allocateSegment(macho_file); - if (build_options.enable_logging) { - state_log.debug("{f}", .{macho_file.dumpState()}); - } + state_log.debug("{f}", .{macho_file.dumpState()}); try writeSections(macho_file); sortRelocs(macho_file); @@ -185,9 +181,7 @@ pub fn flushStaticLib(macho_file: *MachO, comp: *Compilation) link.Error!void { break :blk pos; }; - if (build_options.enable_logging) { - state_log.debug("ar_symtab\n{f}\n", .{ar_symtab.fmt(macho_file)}); - } + state_log.debug("ar_symtab\n{f}\n", .{ar_symtab.fmt(macho_file)}); const buffer = try gpa.alloc(u8, total_size); defer gpa.free(buffer); diff --git a/src/main.zig b/src/main.zig index 607cd7b643a5a94d06d710dc25feea16c55568c7..541504cdb218af539235a5e1f6abca83d30b7234 100644 --- a/src/main.zig +++ b/src/main.zig @@ -42,11 +42,8 @@ const thread_stack_size = 60 << 20; pub const std_options: std.Options = .{ .logFn = log, - - .log_level = switch (builtin.mode) { - .debug => .debug, - .safe, .fast => .info, - .small => .err, + .log_level = switch (build_options.log_level) { + inline else => |log_level| @field(std.log.Level, @tagName(log_level)), }, }; pub const std_options_cwd = if (native_os == .wasi) wasi_cwd else null; @@ -104,7 +101,7 @@ const normal_usage = \\ help Print this help and exit \\ cache-cat Print a zig-cache manifest file as zon \\ std View standard library documentation in a browser - \\ libc Display native libc paths file or validate one + \\ libc Print native libc paths file or validate one \\ targets List available compilation targets \\ version Print version number and exit \\ zen Print Zen of Zig and exit @@ -126,7 +123,15 @@ const debug_usage = normal_usage ++ const usage = if (build_options.enable_debug_extensions) debug_usage else normal_usage; -var log_scopes: std.ArrayList([]const u8) = .empty; +var log_scopes: std.array_hash_map.String(void) = .empty; + +pub fn debugLogScopeEnabled(scope: []const u8) bool { + switch (build_options.log_level) { + else => {}, + .debug => if (log_scopes.contains(scope)) return true, + } + return false; +} pub fn log( comptime level: std.log.Level, @@ -135,18 +140,11 @@ pub fn log( args: anytype, ) void { // Hide debug messages unless: - // * logging enabled with `-Dlog`. + // * logging enabled with `-Ddebug-extensions` or `-Dlog=debug`. // * the --debug-log arg for the scope has been provided - if (@backingInt(level) > @backingInt(std.options.log_level) or - @backingInt(level) > @backingInt(std.log.Level.info)) - { - if (!build_options.enable_logging) return; - - const scope_name = @tagName(scope); - for (log_scopes.items) |log_scope| { - if (mem.eql(u8, log_scope, scope_name)) - break; - } else return; + switch (level) { + else => {}, + .debug => if (!debugLogScopeEnabled(@tagName(scope))) return, } // Otherwise, use the default implementation. @@ -557,11 +555,14 @@ const compile_usage = \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation \\ -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL \\ -fno-emit-implib Do not produce an import .lib when building a Windows DLL - \\ --show-builtin Output the source of @import("builtin") then exit \\ --cache-dir [path] Override the local cache directory \\ --global-cache-dir [path] Override the global cache directory \\ --zig-lib-dir [path] Override path to Zig installation lib directory \\ --build-root [path] Override path to project source files + \\ --show-builtin Output the source of @import("builtin") then exit + \\ --time-report Send timing diagnostics to '--listen' clients + \\ --verbose-cc Print C compiler commands before executing them + \\ --verbose-link Print linker commands before executing them \\ \\Global Compile Options: \\ --name [name] Compilation unit name (not a file path) @@ -593,7 +594,6 @@ const compile_usage = \\ -fpatchable-function-entry=[num] Add num NOPs of padding in function prologues \\ -mexec-model=[value] (WASI) Execution model \\ -municode (Windows) Use wmain/wWinMain as entry point - \\ --time-report Send timing diagnostics to '--listen' clients \\ \\Per-Module Compile Options: \\ --dep [[import=]name] Add an entry to the next module's import table @@ -604,13 +604,15 @@ const compile_usage = \\ safe Enable both safety checks and machine code optimizations \\ small Prioritize small binary size. Safety checks off. \\ -ofmt=[fmt] Override target object format - \\ elf Executable and Linking Format \\ c C source code - \\ wasm WebAssembly \\ coff Common Object File Format (Windows) + \\ elf Executable and Linking Format + \\ llvmir LLVM text intermediate representation + \\ llvmbc LLVM binary intermediate representation \\ macho macOS relocatables - \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) \\ plan9 Plan 9 from Bell Labs object format + \\ spirv Standard, Portable Intermediate Representation V (SPIR-V) + \\ wasm WebAssembly \\ hex (planned feature) Intel IHEX \\ raw (planned feature) Dump machine code directly \\ -mcpu [cpu] Specify target CPU and feature set @@ -791,15 +793,6 @@ const compile_usage = \\Debug Options (Zig Compiler Development): \\ -fopt-bisect-limit=[limit] Only run [limit] first LLVM optimization passes \\ -fstack-report Print stack size diagnostics - \\ --verbose-link Display linker invocations - \\ --verbose-cc Display C compiler invocations - \\ --verbose-air Enable compiler debug output for Zig AIR - \\ --verbose-intern-pool Enable compiler debug output for InternPool - \\ --verbose-generic-instances Enable compiler debug output for generic instance generation - \\ --verbose-llvm-ir[=path] Enable compiler debug output for unoptimized LLVM IR - \\ --verbose-llvm-bc=[path] Enable compiler debug output for unoptimized LLVM BC - \\ --verbose-cimport Enable compiler debug output for C imports - \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features \\ --debug-log [scope] Enable printing debug/info log messages for scope \\ --debug-compile-errors Crash with helpful diagnostics at the first compile error \\ --debug-link-snapshot Dump linker state and output file information for troubleshooting @@ -933,17 +926,11 @@ fn buildOutputType( var listen: Listen = .none; var debug_compile_errors = false; var debug_incremental = false; - var verbose_link = (native_os != .wasi or builtin.link_libc) and - EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map); var verbose_cc = (native_os != .wasi or builtin.link_libc) and EnvVar.ZIG_VERBOSE_CC.isSet(environ_map); - var verbose_air = false; - var verbose_intern_pool = false; - var verbose_generic_instances = false; - var verbose_llvm_ir: ?[]const u8 = null; - var verbose_llvm_bc: ?[]const u8 = null; + var verbose_link = (native_os != .wasi or builtin.link_libc) and + EnvVar.ZIG_VERBOSE_LINK.isSet(environ_map); var link_depfile: ?[]const u8 = null; - var verbose_llvm_cpu_features = false; var time_report = false; var stack_report = false; var show_builtin = false; @@ -1809,24 +1796,10 @@ fn buildOutputType( } else { warn("Zig was compiled without debug extensions. --debug-compile-errors has no effect.", .{}); } - } else if (mem.eql(u8, arg, "--verbose-link")) { - verbose_link = true; } else if (mem.eql(u8, arg, "--verbose-cc")) { verbose_cc = true; - } else if (mem.eql(u8, arg, "--verbose-air")) { - verbose_air = true; - } else if (mem.eql(u8, arg, "--verbose-intern-pool")) { - verbose_intern_pool = true; - } else if (mem.eql(u8, arg, "--verbose-generic-instances")) { - verbose_generic_instances = true; - } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { - verbose_llvm_ir = "-"; - } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| { - verbose_llvm_ir = rest; - } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| { - verbose_llvm_bc = rest; - } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { - verbose_llvm_cpu_features = true; + } else if (mem.eql(u8, arg, "--verbose-link")) { + verbose_link = true; } else if (mem.cutPrefix(u8, arg, "-T")) |rest| { linker_script = rest; } else if (mem.cutPrefix(u8, arg, "-L")) |rest| { @@ -3729,13 +3702,7 @@ fn buildOutputType( .libc_installation = if (create_module.libc_installation) |*lci| lci else null, .verbose_cc = verbose_cc, .verbose_link = verbose_link, - .verbose_air = verbose_air, - .verbose_intern_pool = verbose_intern_pool, - .verbose_generic_instances = verbose_generic_instances, - .verbose_llvm_ir = verbose_llvm_ir, - .verbose_llvm_bc = verbose_llvm_bc, .link_depfile = link_depfile, - .verbose_llvm_cpu_features = verbose_llvm_cpu_features, .time_report = time_report, .stack_report = stack_report, .build_id = build_id, @@ -3956,10 +3923,10 @@ fn buildOutputType( }); } - try test_exec_args.ensureUnusedCapacity(arena, 2 * log_scopes.items.len + @intFromBool(verbose_link) + @intFromBool(verbose_cc)); - for (log_scopes.items) |log_scope| test_exec_args.appendSliceAssumeCapacity(&.{ "--debug-log", log_scope }); - if (verbose_link) test_exec_args.appendAssumeCapacity("--verbose-link"); + try test_exec_args.ensureUnusedCapacity(arena, 2 * log_scopes.count() + @intFromBool(verbose_link) + @intFromBool(verbose_cc)); + for (log_scopes.keys()) |log_scope| test_exec_args.appendSliceAssumeCapacity(&.{ "--debug-log", log_scope }); if (verbose_cc) test_exec_args.appendAssumeCapacity("--verbose-cc"); + if (verbose_link) test_exec_args.appendAssumeCapacity("--verbose-link"); } try runOrTest( @@ -5627,17 +5594,17 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: } } - const display_path = zig_source_path orelse ""; + const print_path = zig_source_path orelse ""; const source: [:0]const u8 = s: { var f = if (zig_source_path) |p| file: { break :file Io.Dir.cwd().openFile(io, p, .{}) catch |err| { - fatal("unable to open file {q} for ast-check: {t}", .{ display_path, err }); + fatal("unable to open file {q} for ast-check: {t}", .{ print_path, err }); }; } else Io.File.stdin(); defer if (zig_source_path != null) f.close(io); var file_reader: Io.File.Reader = f.reader(io, &stdin_buffer); break :s std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err| { - fatal("unable to load file {q} for ast-check: {t}", .{ display_path, err }); + fatal("unable to load file {q} for ast-check: {t}", .{ print_path, err }); }; }; @@ -5661,7 +5628,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: if (zir.hasCompileErrors()) { var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); - try wip_errors.addZirErrorMessages(zir, tree, source, display_path); + try wip_errors.addZirErrorMessages(zir, tree, source, print_path); var error_bundle = try wip_errors.toOwnedBundle(""); try error_bundle.renderToStderr(io, .{}, color); if (zir.loweringFailed()) { @@ -5731,7 +5698,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map: const zoir = try ZonGen.generate(arena, tree, .{}); if (zoir.hasCompileErrors()) { var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena); - try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path); + try wip_errors.addZoirErrorMessages(zoir, tree, source, print_path); var error_bundle = try wip_errors.toOwnedBundle(""); error_bundle.renderToStderr(io, .{}, color) catch {}; process.exit(1); @@ -6254,9 +6221,8 @@ fn randInt(io: Io, comptime T: type) T { } fn addDebugLog(arena: Allocator, scope_name: []const u8) error{OutOfMemory}!void { - if (!build_options.enable_logging) { - warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{}); - } else { - try log_scopes.append(arena, scope_name); + switch (build_options.log_level) { + else => warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{}), + .debug => try log_scopes.put(arena, try arena.dupe(u8, scope_name), {}), } } diff --git a/src/target.zig b/src/target.zig index f3fe06954f6789aec3a18bb1d91f2fb1ef6642b2..3f6edf8c63673c36abcbbf2b0faf41397df5cfd2 100644 --- a/src/target.zig +++ b/src/target.zig @@ -193,6 +193,8 @@ pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat) .coff, .elf, .hex, + .llvmbc, + .llvmir, .macho, .spirv, .raw, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index aeb838d10f1040998696fc624b3222685ae6d834..56875833b3ff992cd058062b53bf2421a416aa25 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -410,6 +410,10 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi // Code generation phase codegen_pm.run(llvm_module); + if (options->llvm_ir_out != nullptr) { + *options->llvm_ir_out = LLVMPrintModuleToString(module_ref); + } + if (options->llvm_ir_filename) { if (LLVMPrintModuleToFile(module_ref, options->llvm_ir_filename, error_message)) { return true; @@ -430,9 +434,7 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi auto os = raw_string_ostream(out_str); TimerGroup::printAll(os); TimerGroup::clearAll(); - auto c_str = (char *)malloc(out_str.length() + 1); - strcpy(c_str, out_str.c_str()); - *options->time_report_out = c_str; + *options->time_report_out = LLVMCreateMessage(out_str.c_str()); } return false; } diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 79ba0272df2f73743705ac832f5d508b5099542d..c5e9492b8c244b0444fbf409d4be3f6c84149b18 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -67,8 +67,8 @@ struct ZigLLVMEmitOptions { bool is_debug; bool is_small; // If not null, and `ZigLLVMTargetMachineEmitToFile` returns `false` indicating success, this - // `char *` will be populated with a `malloc`-allocated string containing the serialized (as - // JSON) time report data. The caller is responsible for freeing that memory. + // `char *` will be populated with the serialized (as JSON) time report data. The caller is + // responsible for freeing that memory with `disposeMessage`. char **time_report_out; bool tsan; bool sancov; @@ -79,6 +79,10 @@ struct ZigLLVMEmitOptions { const char *bin_filename; const char *llvm_ir_filename; const char *bitcode_filename; + // If not null and `ZigLLVMTargetMachineEmitToFile` returns `false` indicating success, this + // `char *` will be populated with the optimized LLVM IR. The caller is responsible for freeing + // this memory with `disposeMessage`. + char **llvm_ir_out; ZigLLVMCoverageOptions coverage; }; diff --git a/stage1/config.zig.in b/stage1/config.zig.in index d975f286a0fd52360326971d73f3b4a294169df1..d75edde720b9336f7786ba814fa50dbcfbb59ad5 100644 --- a/stage1/config.zig.in +++ b/stage1/config.zig.in @@ -6,7 +6,7 @@ pub const llvm_has_xtensa = false; pub const version: [:0]const u8 = "@RESOLVED_ZIG_VERSION@"; pub const semver = @import("std").SemanticVersion.parse(version) catch unreachable; pub const enable_debug_extensions = false; -pub const enable_logging = false; +pub const log_level: @import("std").log.Level = .err; pub const enable_link_snapshots = false; pub const enable_tracy = false; pub const value_tracing = false; -- 2.54.0