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 new file mode 100644 index 0000000000000000000000000000000000000000..cf61419fa35acd0d56d1c2b86b9f892f2c683864 --- /dev/null +++ b/src/Air/Print.zig @@ -0,0 +1,1059 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const build_options = @import("build_options"); +const Zcu = @import("../Zcu.zig"); +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.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. + (@sizeOf(Air.Inst.Tag) + 8); + const extra_bytes = air.extra.items.len * @sizeOf(u32); + const tomb_bytes = if (liveness) |l| l.tomb_bits.len * @sizeOf(usize) else 0; + const liveness_extra_bytes = if (liveness) |l| l.extra.len * @sizeOf(u32) else 0; + const liveness_special_bytes = if (liveness) |l| l.special.count() * 8 else 0; + const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes + + @sizeOf(Air.Liveness) + liveness_extra_bytes + + liveness_special_bytes + tomb_bytes; + + // zig fmt: off + try stream.print( + \\# Total AIR+Liveness bytes: {Bi} + \\# AIR Instructions: {d} ({Bi}) + \\# AIR Extra Data: {d} ({Bi}) + \\# Liveness tomb_bits: {Bi} + \\# Liveness Extra Data: {d} ({Bi}) + \\# Liveness special table: {d} ({Bi}) + \\ + , .{ + total_bytes, + air.instructions.len, instruction_bytes, + air.extra.items.len, extra_bytes, + tomb_bytes, + if (liveness) |l| l.extra.len else 0, liveness_extra_bytes, + if (liveness) |l| l.special.count() else 0, liveness_special_bytes, + }); + // zig fmt: on + + var writer: Writer = .{ + .zcu = zcu, + .gpa = zcu.comp.gpa, + .air = air, + .liveness = liveness, + .indent = 2, + .skip_body = false, + }; + try writer.writeBody(stream, air.getMainBody()); +} + +pub fn writeInst( + air: Air, + stream: *std.Io.Writer, + inst: Air.Inst.Index, + zcu: *Zcu, + liveness: ?Air.Liveness, +) void { + comptime assert(build_options.log_level == .debug); + var writer: Writer = .{ + .zcu = zcu, + .gpa = zcu.comp.gpa, + .air = air, + .liveness = liveness, + .indent = 2, + .skip_body = true, + }; + writer.writeInst(stream, inst) catch return; +} + +pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { + var buffer: [4096]u8 = undefined; + const stderr = std.debug.lockStderr(&buffer); + defer std.debug.unlockStderr(); + const w = &stderr.file_writer.interface; + air.write(w, pt, liveness) catch return; +} + +pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { + var buffer: [4096]u8 = undefined; + const stderr = std.debug.lockStderr(&buffer); + defer std.debug.unlockStderr(); + const w = &stderr.file_writer.interface; + air.writeInst(w, inst, pt, liveness) catch return; +} + +const Writer = struct { + zcu: *Zcu, + gpa: Allocator, + air: Air, + liveness: ?Air.Liveness, + indent: usize, + skip_body: bool, + + const Error = std.Io.Writer.Error; + + fn writeBody(w: *Writer, s: *std.Io.Writer, body: []const Air.Inst.Index) Error!void { + for (body) |inst| { + try w.writeInst(s, inst); + try s.writeByte('\n'); + } + } + + fn writeInst(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const tag = w.air.instructions.items(.tag)[@backingInt(inst)]; + try s.splatByteAll(' ', w.indent); + try s.print("{f}{c}= {s}(", .{ + inst, + @as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '), + @tagName(tag), + }); + switch (tag) { + .add, + .add_optimized, + .add_safe, + .add_wrap, + .add_sat, + .sub, + .sub_optimized, + .sub_safe, + .sub_wrap, + .sub_sat, + .mul, + .mul_optimized, + .mul_safe, + .mul_wrap, + .mul_sat, + .div_float, + .div_trunc, + .div_floor, + .div_ceil, + .div_exact, + .rem, + .mod, + .bit_and, + .bit_or, + .xor, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .store, + .store_safe, + .array_elem_val, + .slice_elem_val, + .ptr_elem_val, + .shl, + .shl_exact, + .shl_sat, + .shr, + .shr_exact, + .set_union_tag, + .min, + .max, + .div_float_optimized, + .div_trunc_optimized, + .div_floor_optimized, + .div_ceil_optimized, + .div_exact_optimized, + .rem_optimized, + .mod_optimized, + .cmp_lt_optimized, + .cmp_lte_optimized, + .cmp_eq_optimized, + .cmp_gte_optimized, + .cmp_gt_optimized, + .cmp_neq_optimized, + .memcpy, + .memmove, + .memset, + .memset_safe, + .legalize_vec_elem_val, + => try w.writeBinOp(s, inst), + + .is_null, + .is_non_null, + .is_null_ptr, + .is_non_null_ptr, + .is_err, + .is_non_err, + .is_err_ptr, + .is_non_err_ptr, + .ret, + .ret_safe, + .ret_load, + .is_named_enum_value, + .tag_name, + .error_name, + .sqrt, + .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .floor, + .ceil, + .round, + .trunc_float, + .neg, + .neg_optimized, + .cmp_lte_errors_len, + .set_err_return_trace, + .c_va_end, + => try w.writeUnOp(s, inst), + + .trap, + .breakpoint, + .dbg_empty_stmt, + .unreach, + .ret_addr, + .frame_addr, + .save_err_return_trace_index, + => try w.writeNoOp(s, inst), + + .alloc, + .ret_ptr, + .err_return_trace, + .c_va_start, + => try w.writeTy(s, inst), + + .arg => try w.writeArg(s, inst), + + .not, + .bit_cast, + .bit_cast_safe, + .ptr_cast, + .ptr_from_int, + .int_from_ptr, + .error_cast, + .error_from_int, + .int_from_error, + .union_from_enum, + .load, + .fptrunc, + .fpext, + .int_cast, + .int_cast_safe, + .trunc, + .optional_payload, + .optional_payload_ptr, + .optional_payload_ptr_set, + .errunion_payload_ptr_set, + .wrap_optional, + .unwrap_errunion_payload, + .unwrap_errunion_err, + .unwrap_errunion_payload_ptr, + .unwrap_errunion_err_ptr, + .wrap_errunion_payload, + .wrap_errunion_err, + .slice_ptr, + .slice_len, + .ptr_slice_len_ptr, + .ptr_slice_ptr_ptr, + .struct_field_ptr_index_0, + .struct_field_ptr_index_1, + .struct_field_ptr_index_2, + .struct_field_ptr_index_3, + .array_to_slice, + .array_to_vector, + .float_from_int, + .splat, + .int_from_float, + .int_from_float_optimized, + .int_from_float_safe, + .int_from_float_optimized_safe, + .get_union_tag, + .clz, + .ctz, + .popcount, + .byte_swap, + .bit_reverse, + .abs, + .error_set_has_value, + .addrspace_cast, + .c_va_arg, + .c_va_copy, + => try w.writeTyOp(s, inst), + + .block, .dbg_inline_block => try w.writeBlock(s, tag, inst), + + .loop => try w.writeLoop(s, inst), + + .slice, + .slice_elem_ptr, + .ptr_elem_ptr, + .ptr_add, + .ptr_sub, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, + => try w.writeTyPlBin(s, inst), + + .call, + .call_always_tail, + .call_never_tail, + .call_never_inline, + => try w.writeCall(s, inst), + + .dbg_var_ptr, + .dbg_var_val, + .dbg_arg_inline, + => try w.writeDbgVar(s, inst), + + .struct_field_ptr => try w.writeStructField(s, inst), + .agg_field_val => try w.writeStructField(s, inst), + .spirv_runtime_array_len => try w.writeStructField(s, inst), + .inferred_alloc => @panic("TODO"), + .inferred_alloc_comptime => @panic("TODO"), + .assembly => try w.writeAssembly(s, inst), + .dbg_stmt => try w.writeDbgStmt(s, inst), + + .aggregate_init => try w.writeAggregateInit(s, inst), + .union_init => try w.writeUnionInit(s, inst), + .br => try w.writeBr(s, inst), + .switch_dispatch => try w.writeBr(s, inst), + .repeat => try w.writeRepeat(s, inst), + .cond_br => try w.writeCondBr(s, inst), + .@"try", .try_cold => try w.writeTry(s, inst), + .try_ptr, .try_ptr_cold => try w.writeTryPtr(s, inst), + .loop_switch_br, .switch_br => try w.writeSwitchBr(s, inst), + .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), + .atomic_load => try w.writeAtomicLoad(s, inst), + .prefetch => try w.writePrefetch(s, inst), + .atomic_store_unordered => try w.writeAtomicStore(s, inst, .unordered), + .atomic_store_monotonic => try w.writeAtomicStore(s, inst, .monotonic), + .atomic_store_release => try w.writeAtomicStore(s, inst, .release), + .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .seq_cst), + .atomic_rmw => try w.writeAtomicRmw(s, inst), + .field_parent_ptr => try w.writeFieldParentPtr(s, inst), + .wasm_memory_size => try w.writeWasmMemorySize(s, inst), + .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), + .mul_add => try w.writeMulAdd(s, inst), + .select => try w.writeSelect(s, inst), + .shuffle_one => try w.writeShuffleOne(s, inst), + .shuffle_two => try w.writeShuffleTwo(s, inst), + .reduce, .reduce_optimized => try w.writeReduce(s, inst), + .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst), + .runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst), + .legalize_vec_store_elem => try w.writeLegalizeVecStoreElem(s, inst), + .legalize_compiler_rt_call => try w.writeLegalizeCompilerRtCall(s, inst), + + .work_item_id, + .work_group_size, + .work_group_id, + => try w.writeWorkDimension(s, inst), + } + try s.writeByte(')'); + } + + fn writeBinOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const bin_op = w.air.instructions.items(.data)[@backingInt(inst)].bin_op; + try w.writeOperand(s, inst, 0, bin_op.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, bin_op.rhs); + } + + fn writeUnOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const un_op = w.air.instructions.items(.data)[@backingInt(inst)].un_op; + try w.writeOperand(s, inst, 0, un_op); + } + + fn writeNoOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + _ = w; + _ = s; + _ = inst; + // no-op, no argument to write + } + + fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void { + return ty.print(s, w.zcu, null); + } + + fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty = w.air.instructions.items(.data)[@backingInt(inst)].ty; + try w.writeType(s, ty); + } + + fn writeArg(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const arg = w.air.instructions.items(.data)[@backingInt(inst)].arg; + try w.writeType(s, arg.ty); + try s.print(", {d}", .{arg.zir_param_index}); + } + + fn writeTyOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty_op = w.air.instructions.items(.data)[@backingInt(inst)].ty_op; + try w.writeType(s, ty_op.ty); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, ty_op.operand); + } + + fn writeBlock(w: *Writer, s: *std.Io.Writer, tag: Air.Inst.Tag, inst: Air.Inst.Index) Error!void { + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + try w.writeType(s, ty_pl.ty); + + const body = switch (tag) { + .block => w.air.unwrapBlock(inst).body, + .dbg_inline_block => body: { + const dbg_block = w.air.unwrapDbgBlock(inst); + try s.writeAll(", "); + try w.writeInstRef(s, Air.internedToRef(dbg_block.func), false); + break :body dbg_block.body; + }, + else => unreachable, + }; + if (w.skip_body) return s.writeAll(", ..."); + const liveness_block: Air.Liveness.BlockSlices = if (w.liveness) |liveness| + liveness.getBlock(inst) + else + .{ .deaths = &.{} }; + + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, body); + w.indent = old_indent; + try s.splatByteAll(' ', w.indent); + try s.writeAll("}"); + + for (liveness_block.deaths) |operand| { + try s.print(" {f}!", .{operand}); + } + } + + fn writeLoop(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const block = w.air.unwrapBlock(inst); + + try w.writeType(s, block.ty); + if (w.skip_body) return s.writeAll(", ..."); + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, block.body); + w.indent = old_indent; + try s.splatByteAll(' ', w.indent); + try s.writeAll("}"); + } + + fn writeAggregateInit(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const zcu = w.zcu; + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const vector_ty = ty_pl.ty; + const len = @as(usize, @intCast(vector_ty.arrayLen(zcu))); + const elements = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[ty_pl.payload..][0..len])); + + try w.writeType(s, vector_ty); + try s.writeAll(", ["); + for (elements, 0..) |elem, i| { + if (i != 0) try s.writeAll(", "); + try w.writeOperand(s, inst, i, elem); + } + try s.writeAll("]"); + } + + fn writeUnionInit(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const extra = w.air.extraData(Air.UnionInit, ty_pl.payload).data; + + try s.print("{d}, ", .{extra.field_index}); + try w.writeOperand(s, inst, 0, extra.init); + } + + fn writeStructField(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const extra = w.air.extraData(Air.StructField, ty_pl.payload).data; + + try w.writeOperand(s, inst, 0, extra.struct_operand); + try s.print(", {d}", .{extra.field_index}); + } + + fn writeTyPlBin(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const data = w.air.instructions.items(.data); + const ty_pl = data[@backingInt(inst)].ty_pl; + const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; + + const inst_ty = data[@backingInt(inst)].ty_pl.ty; + try w.writeType(s, inst_ty); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.rhs); + } + + fn writeCmpxchg(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data; + + try w.writeOperand(s, inst, 0, extra.ptr); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.expected_value); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, extra.new_value); + try s.print(", {s}, {s}", .{ + @tagName(extra.successOrder()), @tagName(extra.failureOrder()), + }); + } + + fn writeMulAdd(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + const extra = w.air.extraData(Air.Bin, pl_op.payload).data; + + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.rhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, pl_op.operand); + } + + fn writeLegalizeVecStoreElem(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + const bin = w.air.extraData(Air.Bin, pl_op.payload).data; + + try w.writeOperand(s, inst, 0, pl_op.operand); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, bin.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, bin.rhs); + } + + fn writeLegalizeCompilerRtCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const rt_call = w.air.unwrapCompilerRtCall(inst); + const args = rt_call.args; + + try s.print("{t}, [", .{rt_call.func}); + for (args, 0..) |arg, i| { + if (i != 0) try s.writeAll(", "); + try w.writeOperand(s, inst, i, arg); + } + try s.writeByte(']'); + } + + fn writeShuffleOne(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const unwrapped = w.air.unwrapShuffleOne(w.zcu, inst); + try w.writeType(s, unwrapped.result_ty); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, unwrapped.operand); + try s.writeAll(", ["); + for (unwrapped.mask, 0..) |mask_elem, mask_idx| { + if (mask_idx > 0) try s.writeAll(", "); + switch (mask_elem.unwrap()) { + .elem => |idx| try s.print("elem {d}", .{idx}), + .value => |val| try s.print("val {f}", .{Value.fromInterned(val).fmtValue(w.zcu)}), + } + } + try s.writeByte(']'); + } + + fn writeShuffleTwo(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const unwrapped = w.air.unwrapShuffleTwo(w.zcu, inst); + try w.writeType(s, unwrapped.result_ty); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, unwrapped.operand_a); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, unwrapped.operand_b); + try s.writeAll(", ["); + for (unwrapped.mask, 0..) |mask_elem, mask_idx| { + if (mask_idx > 0) try s.writeAll(", "); + switch (mask_elem.unwrap()) { + .a_elem => |idx| try s.print("a_elem {d}", .{idx}), + .b_elem => |idx| try s.print("b_elem {d}", .{idx}), + .undef => try s.writeAll("undef"), + } + } + try s.writeByte(']'); + } + + fn writeSelect(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const zcu = w.zcu; + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + const extra = w.air.extraData(Air.Bin, pl_op.payload).data; + + const elem_ty = w.typeOfIndex(inst).childType(zcu); + try w.writeType(s, elem_ty); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, pl_op.operand); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, extra.rhs); + } + + fn writeReduce(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const reduce = w.air.instructions.items(.data)[@backingInt(inst)].reduce; + + try w.writeOperand(s, inst, 0, reduce.operand); + try s.print(", {s}", .{@tagName(reduce.operation)}); + } + + fn writeCmpVector(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const extra = w.air.extraData(Air.VectorCmp, ty_pl.payload).data; + + try s.print("{s}, ", .{@tagName(extra.compareOperator())}); + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.rhs); + } + + fn writeRuntimeNavPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ip = &w.zcu.intern_pool; + const ty_nav = w.air.instructions.items(.data)[@backingInt(inst)].ty_nav; + try w.writeType(s, ty_nav.ty); + try s.print(", '{f}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); + } + + fn writeAtomicLoad(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const atomic_load = w.air.instructions.items(.data)[@backingInt(inst)].atomic_load; + + try w.writeOperand(s, inst, 0, atomic_load.ptr); + try s.print(", {s}", .{@tagName(atomic_load.order)}); + } + + fn writePrefetch(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const prefetch = w.air.instructions.items(.data)[@backingInt(inst)].prefetch; + + try w.writeOperand(s, inst, 0, prefetch.ptr); + try s.print(", {s}, {d}, {s}", .{ + @tagName(prefetch.rw), prefetch.locality, @tagName(prefetch.cache), + }); + } + + fn writeAtomicStore( + w: *Writer, + s: *std.Io.Writer, + inst: Air.Inst.Index, + order: std.lang.AtomicOrder, + ) Error!void { + const bin_op = w.air.instructions.items(.data)[@backingInt(inst)].bin_op; + try w.writeOperand(s, inst, 0, bin_op.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, bin_op.rhs); + try s.print(", {s}", .{@tagName(order)}); + } + + fn writeAtomicRmw(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data; + + try w.writeOperand(s, inst, 0, pl_op.operand); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.operand); + try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); + } + + fn writeFieldParentPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; + const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; + + try w.writeOperand(s, inst, 0, extra.field_ptr); + try s.print(", {d}", .{extra.field_index}); + } + + fn writeAssembly(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const unwrapped_asm = w.air.unwrapAsm(inst); + const is_volatile = unwrapped_asm.is_volatile; + var op_index: usize = 0; + + const ret_ty = w.typeOfIndex(inst); + try w.writeType(s, ret_ty); + + if (is_volatile) { + try s.writeAll(", volatile"); + } + + var it = unwrapped_asm.iterateOutputs(); + while (it.next()) |out| { + const name = out.name; + const constraint = out.constraint; + if (out.operand == .none) { + try s.print(", [{s}] -> {s}", .{ name, constraint }); + } else { + try s.print(", [{s}] out {s} = (", .{ name, constraint }); + try w.writeOperand(s, inst, op_index, out.operand); + op_index += 1; + try s.writeByte(')'); + } + } + + it = unwrapped_asm.iterateInputs(); + while (it.next()) |in| { + const name = in.name; + const constraint = in.constraint; + try s.print(", [{s}] in {s} = (", .{ name, constraint }); + try w.writeOperand(s, inst, op_index, in.operand); + op_index += 1; + try s.writeByte(')'); + } + + const zcu = w.zcu; + const ip = &zcu.intern_pool; + const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); + const clobbers_ty = clobbers_val.typeOf(zcu); + var clobbers_bigint_buf: Value.BigIntSpace = undefined; + const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); + for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { + assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); + const limb_bits = @bitSizeOf(std.math.big.Limb); + if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false + switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { + 0 => continue, // field is false + 1 => {}, // field is true + } + const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; + assert(clobber.len != 0); + try s.writeAll(", ~{"); + try s.writeAll(clobber); + try s.writeAll("}"); + } + const asm_source = unwrapped_asm.source; + try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)}); + } + + fn writeDbgStmt(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const dbg_stmt = w.air.instructions.items(.data)[@backingInt(inst)].dbg_stmt; + try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); + } + + fn writeDbgVar(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + try w.writeOperand(s, inst, 0, pl_op.operand); + const name: Air.NullTerminatedString = @fromBackingInt(@intCast(pl_op.payload)); + try s.print(", \"{f}\"", .{std.zig.fmtString(name.toSlice(w.air))}); + } + + fn writeCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const call = w.air.unwrapCall(inst); + const args = call.args; + try w.writeOperand(s, inst, 0, call.callee); + try s.writeAll(", ["); + for (args, 0..) |arg, i| { + if (i != 0) try s.writeAll(", "); + try w.writeOperand(s, inst, 1 + i, arg); + } + try s.writeAll("]"); + } + + fn writeBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const br = w.air.instructions.items(.data)[@backingInt(inst)].br; + try w.writeInstIndex(s, br.block_inst, false); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, br.operand); + } + + fn writeRepeat(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const repeat = w.air.instructions.items(.data)[@backingInt(inst)].repeat; + try w.writeInstIndex(s, repeat.loop_inst, false); + } + + fn writeTry(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const unwrapped_try = w.air.unwrapTry(inst); + const body = unwrapped_try.else_body; + const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| + liveness.getCondBr(inst) + else + .{ .then_deaths = &.{}, .else_deaths = &.{} }; + + try w.writeOperand(s, inst, 0, unwrapped_try.error_union); + if (w.skip_body) return s.writeAll(", ..."); + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + + if (liveness_condbr.else_deaths.len != 0) { + try s.splatByteAll(' ', w.indent); + for (liveness_condbr.else_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{f}!", .{operand}); + } + try s.writeAll("\n"); + } + try w.writeBody(s, body); + + w.indent = old_indent; + try s.splatByteAll(' ', w.indent); + try s.writeAll("}"); + + for (liveness_condbr.then_deaths) |operand| { + try s.print(" {f}!", .{operand}); + } + } + + fn writeTryPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const unwrapped_try = w.air.unwrapTryPtr(inst); + const body = unwrapped_try.else_body; + const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| + liveness.getCondBr(inst) + else + .{ .then_deaths = &.{}, .else_deaths = &.{} }; + + try w.writeOperand(s, inst, 0, unwrapped_try.error_union_ptr); + + try s.writeAll(", "); + try w.writeType(s, unwrapped_try.error_union_payload_ptr_ty); + if (w.skip_body) return s.writeAll(", ..."); + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + + if (liveness_condbr.else_deaths.len != 0) { + try s.splatByteAll(' ', w.indent); + for (liveness_condbr.else_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{f}!", .{operand}); + } + try s.writeAll("\n"); + } + try w.writeBody(s, body); + + w.indent = old_indent; + try s.splatByteAll(' ', w.indent); + try s.writeAll("}"); + + for (liveness_condbr.then_deaths) |operand| { + try s.print(" {f}!", .{operand}); + } + } + + fn writeCondBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const cond_br = w.air.unwrapCondBr(inst); + const then_body = cond_br.then_body; + const else_body = cond_br.else_body; + const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| + liveness.getCondBr(inst) + else + .{ .then_deaths = &.{}, .else_deaths = &.{} }; + + try w.writeOperand(s, inst, 0, cond_br.condition); + if (w.skip_body) return s.writeAll(", ..."); + try s.writeAll(","); + if (cond_br.branch_hints.true != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.true)}); + } + if (cond_br.branch_hints.then_cov != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.then_cov)}); + } + try s.writeAll(" {\n"); + const old_indent = w.indent; + w.indent += 2; + + if (liveness_condbr.then_deaths.len != 0) { + try s.splatByteAll(' ', w.indent); + for (liveness_condbr.then_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{f}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, then_body); + try s.splatByteAll(' ', old_indent); + try s.writeAll("},"); + if (cond_br.branch_hints.false != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.false)}); + } + if (cond_br.branch_hints.else_cov != .none) { + try s.print(" {s}", .{@tagName(cond_br.branch_hints.else_cov)}); + } + try s.writeAll(" {\n"); + + if (liveness_condbr.else_deaths.len != 0) { + try s.splatByteAll(' ', w.indent); + for (liveness_condbr.else_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{f}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, else_body); + w.indent = old_indent; + + try s.splatByteAll(' ', old_indent); + try s.writeAll("}"); + } + + fn writeSwitchBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const switch_br = w.air.unwrapSwitch(inst); + + const liveness: Air.Liveness.SwitchBrTable = if (w.liveness) |liveness| + liveness.getSwitchBr(w.gpa, inst, switch_br.cases_len + 1) catch + @panic("out of memory") + else blk: { + const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.cases_len + 1) catch + @panic("out of memory"); + @memset(slice, &.{}); + break :blk .{ .deaths = slice }; + }; + defer w.gpa.free(liveness.deaths); + + try w.writeOperand(s, inst, 0, switch_br.operand); + if (w.skip_body) return s.writeAll(", ..."); + const old_indent = w.indent; + w.indent += 2; + + var it = switch_br.iterateCases(); + while (it.next()) |case| { + try s.writeAll(", ["); + for (case.items, 0..) |item, item_i| { + if (item_i != 0) try s.writeAll(", "); + try w.writeInstRef(s, item, false); + } + for (case.ranges, 0..) |range, range_i| { + if (range_i != 0 or case.items.len != 0) try s.writeAll(", "); + try w.writeInstRef(s, range[0], false); + try s.writeAll("..."); + try w.writeInstRef(s, range[1], false); + } + try s.writeAll("] "); + const hint = switch_br.getHint(case.idx); + if (hint != .none) { + try s.print(".{s} ", .{@tagName(hint)}); + } + try s.writeAll("=> {\n"); + w.indent += 2; + + const deaths = liveness.deaths[case.idx]; + if (deaths.len != 0) { + try s.splatByteAll(' ', w.indent); + for (deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{f}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, case.body); + w.indent -= 2; + try s.splatByteAll(' ', w.indent); + try s.writeAll("}"); + } + + const else_body = it.elseBody(); + if (else_body.len != 0) { + try s.writeAll(", else "); + const hint = switch_br.getElseHint(); + if (hint != .none) { + try s.print(".{s} ", .{@tagName(hint)}); + } + try s.writeAll("=> {\n"); + w.indent += 2; + + const deaths = liveness.deaths[liveness.deaths.len - 1]; + if (deaths.len != 0) { + try s.splatByteAll(' ', w.indent); + for (deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{f}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, else_body); + w.indent -= 2; + try s.splatByteAll(' ', w.indent); + try s.writeAll("}"); + } + + try s.writeAll("\n"); + try s.splatByteAll(' ', old_indent); + } + + fn writeWasmMemorySize(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + try s.print("{d}", .{pl_op.payload}); + } + + fn writeWasmMemoryGrow(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + try s.print("{d}, ", .{pl_op.payload}); + try w.writeOperand(s, inst, 0, pl_op.operand); + } + + fn writeWorkDimension(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { + const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; + try s.print("{d}", .{pl_op.payload}); + } + + fn writeOperand( + w: *Writer, + s: *std.Io.Writer, + inst: Air.Inst.Index, + op_index: usize, + operand: Air.Inst.Ref, + ) Error!void { + const small_tomb_bits = Air.Liveness.bpi - 1; + const dies = if (w.liveness) |liveness| blk: { + if (op_index < small_tomb_bits) + break :blk liveness.operandDies(inst, @intCast(op_index)); + var extra_index = liveness.special.get(inst).?; + var tomb_op_index: usize = small_tomb_bits; + while (true) { + const bits = liveness.extra[extra_index]; + if (op_index < tomb_op_index + 31) { + break :blk @as(u1, @truncate(bits >> @as(u5, @intCast(op_index - tomb_op_index)))) != 0; + } + if ((bits >> 31) != 0) break :blk false; + extra_index += 1; + tomb_op_index += 31; + } + } else false; + return w.writeInstRef(s, operand, dies); + } + + fn writeInstRef( + w: *Writer, + s: *std.Io.Writer, + operand: Air.Inst.Ref, + dies: bool, + ) Error!void { + if (@backingInt(operand) < InternPool.static_len) { + return s.print("@{}", .{operand}); + } else if (operand.toInterned()) |ip_index| { + const zcu = w.zcu; + const ty = Type.fromInterned(zcu.intern_pool.indexToKey(ip_index).typeOf()); + try s.print("<{f}, {f}>", .{ + ty.fmt(zcu), + Value.fromInterned(ip_index).fmtValue(w.zcu), + }); + } else { + return w.writeInstIndex(s, operand.toIndex().?, dies); + } + } + + fn writeInstIndex( + w: *Writer, + s: *std.Io.Writer, + inst: Air.Inst.Index, + dies: bool, + ) Error!void { + _ = w; + try s.print("{f}", .{inst}); + if (dies) try s.writeByte('!'); + } + + fn typeOfIndex(w: *Writer, inst: Air.Inst.Index) Type { + const zcu = w.zcu; + return w.air.typeOfIndex(inst, &zcu.intern_pool); + } +}; diff --git a/src/Air/print.zig b/src/Air/print.zig deleted file mode 100644 index 6f43909740609da4d5ce53040307dadf3d386e1b..0000000000000000000000000000000000000000 --- a/src/Air/print.zig +++ /dev/null @@ -1,1048 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; - -const build_options = @import("build_options"); -const Zcu = @import("../Zcu.zig"); -const Value = @import("../Value.zig"); -const Type = @import("../Type.zig"); -const Air = @import("../Air.zig"); -const InternPool = @import("../InternPool.zig"); - -pub fn write(air: Air, stream: *std.Io.Writer, zcu: *Zcu, liveness: ?Air.Liveness) !void { - comptime assert(build_options.enable_debug_extensions); - 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. - (@sizeOf(Air.Inst.Tag) + 8); - const extra_bytes = air.extra.items.len * @sizeOf(u32); - const tomb_bytes = if (liveness) |l| l.tomb_bits.len * @sizeOf(usize) else 0; - const liveness_extra_bytes = if (liveness) |l| l.extra.len * @sizeOf(u32) else 0; - const liveness_special_bytes = if (liveness) |l| l.special.count() * 8 else 0; - const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes + - @sizeOf(Air.Liveness) + liveness_extra_bytes + - liveness_special_bytes + tomb_bytes; - - // zig fmt: off - try stream.print( - \\# Total AIR+Liveness bytes: {Bi} - \\# AIR Instructions: {d} ({Bi}) - \\# AIR Extra Data: {d} ({Bi}) - \\# Liveness tomb_bits: {Bi} - \\# Liveness Extra Data: {d} ({Bi}) - \\# Liveness special table: {d} ({Bi}) - \\ - , .{ - total_bytes, - air.instructions.len, instruction_bytes, - air.extra.items.len, extra_bytes, - tomb_bytes, - if (liveness) |l| l.extra.len else 0, liveness_extra_bytes, - if (liveness) |l| l.special.count() else 0, liveness_special_bytes, - }); - // zig fmt: on - - var writer: Writer = .{ - .zcu = zcu, - .gpa = zcu.comp.gpa, - .air = air, - .liveness = liveness, - .indent = 2, - .skip_body = false, - }; - try writer.writeBody(stream, air.getMainBody()); -} - -pub fn writeInst( - air: Air, - stream: *std.Io.Writer, - inst: Air.Inst.Index, - zcu: *Zcu, - liveness: ?Air.Liveness, -) void { - comptime assert(build_options.enable_debug_extensions); - var writer: Writer = .{ - .zcu = zcu, - .gpa = zcu.comp.gpa, - .air = air, - .liveness = liveness, - .indent = 2, - .skip_body = true, - }; - writer.writeInst(stream, inst) catch return; -} - -pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - var buffer: [4096]u8 = undefined; - const stderr = std.debug.lockStderr(&buffer); - defer std.debug.unlockStderr(); - const w = &stderr.file_writer.interface; - air.write(w, pt, liveness) catch return; -} - -pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { - var buffer: [4096]u8 = undefined; - const stderr = std.debug.lockStderr(&buffer); - defer std.debug.unlockStderr(); - const w = &stderr.file_writer.interface; - air.writeInst(w, inst, pt, liveness) catch return; -} - -const Writer = struct { - zcu: *Zcu, - gpa: Allocator, - air: Air, - liveness: ?Air.Liveness, - indent: usize, - skip_body: bool, - - const Error = std.Io.Writer.Error; - - fn writeBody(w: *Writer, s: *std.Io.Writer, body: []const Air.Inst.Index) Error!void { - for (body) |inst| { - try w.writeInst(s, inst); - try s.writeByte('\n'); - } - } - - fn writeInst(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const tag = w.air.instructions.items(.tag)[@backingInt(inst)]; - try s.splatByteAll(' ', w.indent); - try s.print("{f}{c}= {s}(", .{ - inst, - @as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '), - @tagName(tag), - }); - switch (tag) { - .add, - .add_optimized, - .add_safe, - .add_wrap, - .add_sat, - .sub, - .sub_optimized, - .sub_safe, - .sub_wrap, - .sub_sat, - .mul, - .mul_optimized, - .mul_safe, - .mul_wrap, - .mul_sat, - .div_float, - .div_trunc, - .div_floor, - .div_ceil, - .div_exact, - .rem, - .mod, - .bit_and, - .bit_or, - .xor, - .cmp_lt, - .cmp_lte, - .cmp_eq, - .cmp_gte, - .cmp_gt, - .cmp_neq, - .store, - .store_safe, - .array_elem_val, - .slice_elem_val, - .ptr_elem_val, - .shl, - .shl_exact, - .shl_sat, - .shr, - .shr_exact, - .set_union_tag, - .min, - .max, - .div_float_optimized, - .div_trunc_optimized, - .div_floor_optimized, - .div_ceil_optimized, - .div_exact_optimized, - .rem_optimized, - .mod_optimized, - .cmp_lt_optimized, - .cmp_lte_optimized, - .cmp_eq_optimized, - .cmp_gte_optimized, - .cmp_gt_optimized, - .cmp_neq_optimized, - .memcpy, - .memmove, - .memset, - .memset_safe, - .legalize_vec_elem_val, - => try w.writeBinOp(s, inst), - - .is_null, - .is_non_null, - .is_null_ptr, - .is_non_null_ptr, - .is_err, - .is_non_err, - .is_err_ptr, - .is_non_err_ptr, - .ret, - .ret_safe, - .ret_load, - .is_named_enum_value, - .tag_name, - .error_name, - .sqrt, - .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .floor, - .ceil, - .round, - .trunc_float, - .neg, - .neg_optimized, - .cmp_lte_errors_len, - .set_err_return_trace, - .c_va_end, - => try w.writeUnOp(s, inst), - - .trap, - .breakpoint, - .dbg_empty_stmt, - .unreach, - .ret_addr, - .frame_addr, - .save_err_return_trace_index, - => try w.writeNoOp(s, inst), - - .alloc, - .ret_ptr, - .err_return_trace, - .c_va_start, - => try w.writeTy(s, inst), - - .arg => try w.writeArg(s, inst), - - .not, - .bit_cast, - .bit_cast_safe, - .ptr_cast, - .ptr_from_int, - .int_from_ptr, - .error_cast, - .error_from_int, - .int_from_error, - .union_from_enum, - .load, - .fptrunc, - .fpext, - .int_cast, - .int_cast_safe, - .trunc, - .optional_payload, - .optional_payload_ptr, - .optional_payload_ptr_set, - .errunion_payload_ptr_set, - .wrap_optional, - .unwrap_errunion_payload, - .unwrap_errunion_err, - .unwrap_errunion_payload_ptr, - .unwrap_errunion_err_ptr, - .wrap_errunion_payload, - .wrap_errunion_err, - .slice_ptr, - .slice_len, - .ptr_slice_len_ptr, - .ptr_slice_ptr_ptr, - .struct_field_ptr_index_0, - .struct_field_ptr_index_1, - .struct_field_ptr_index_2, - .struct_field_ptr_index_3, - .array_to_slice, - .array_to_vector, - .float_from_int, - .splat, - .int_from_float, - .int_from_float_optimized, - .int_from_float_safe, - .int_from_float_optimized_safe, - .get_union_tag, - .clz, - .ctz, - .popcount, - .byte_swap, - .bit_reverse, - .abs, - .error_set_has_value, - .addrspace_cast, - .c_va_arg, - .c_va_copy, - => try w.writeTyOp(s, inst), - - .block, .dbg_inline_block => try w.writeBlock(s, tag, inst), - - .loop => try w.writeLoop(s, inst), - - .slice, - .slice_elem_ptr, - .ptr_elem_ptr, - .ptr_add, - .ptr_sub, - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => try w.writeTyPlBin(s, inst), - - .call, - .call_always_tail, - .call_never_tail, - .call_never_inline, - => try w.writeCall(s, inst), - - .dbg_var_ptr, - .dbg_var_val, - .dbg_arg_inline, - => try w.writeDbgVar(s, inst), - - .struct_field_ptr => try w.writeStructField(s, inst), - .agg_field_val => try w.writeStructField(s, inst), - .spirv_runtime_array_len => try w.writeStructField(s, inst), - .inferred_alloc => @panic("TODO"), - .inferred_alloc_comptime => @panic("TODO"), - .assembly => try w.writeAssembly(s, inst), - .dbg_stmt => try w.writeDbgStmt(s, inst), - - .aggregate_init => try w.writeAggregateInit(s, inst), - .union_init => try w.writeUnionInit(s, inst), - .br => try w.writeBr(s, inst), - .switch_dispatch => try w.writeBr(s, inst), - .repeat => try w.writeRepeat(s, inst), - .cond_br => try w.writeCondBr(s, inst), - .@"try", .try_cold => try w.writeTry(s, inst), - .try_ptr, .try_ptr_cold => try w.writeTryPtr(s, inst), - .loop_switch_br, .switch_br => try w.writeSwitchBr(s, inst), - .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), - .atomic_load => try w.writeAtomicLoad(s, inst), - .prefetch => try w.writePrefetch(s, inst), - .atomic_store_unordered => try w.writeAtomicStore(s, inst, .unordered), - .atomic_store_monotonic => try w.writeAtomicStore(s, inst, .monotonic), - .atomic_store_release => try w.writeAtomicStore(s, inst, .release), - .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .seq_cst), - .atomic_rmw => try w.writeAtomicRmw(s, inst), - .field_parent_ptr => try w.writeFieldParentPtr(s, inst), - .wasm_memory_size => try w.writeWasmMemorySize(s, inst), - .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), - .mul_add => try w.writeMulAdd(s, inst), - .select => try w.writeSelect(s, inst), - .shuffle_one => try w.writeShuffleOne(s, inst), - .shuffle_two => try w.writeShuffleTwo(s, inst), - .reduce, .reduce_optimized => try w.writeReduce(s, inst), - .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst), - .runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst), - .legalize_vec_store_elem => try w.writeLegalizeVecStoreElem(s, inst), - .legalize_compiler_rt_call => try w.writeLegalizeCompilerRtCall(s, inst), - - .work_item_id, - .work_group_size, - .work_group_id, - => try w.writeWorkDimension(s, inst), - } - try s.writeByte(')'); - } - - fn writeBinOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const bin_op = w.air.instructions.items(.data)[@backingInt(inst)].bin_op; - try w.writeOperand(s, inst, 0, bin_op.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, bin_op.rhs); - } - - fn writeUnOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const un_op = w.air.instructions.items(.data)[@backingInt(inst)].un_op; - try w.writeOperand(s, inst, 0, un_op); - } - - fn writeNoOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - _ = w; - _ = s; - _ = inst; - // no-op, no argument to write - } - - fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void { - return ty.print(s, w.zcu, null); - } - - fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty = w.air.instructions.items(.data)[@backingInt(inst)].ty; - try w.writeType(s, ty); - } - - fn writeArg(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const arg = w.air.instructions.items(.data)[@backingInt(inst)].arg; - try w.writeType(s, arg.ty); - try s.print(", {d}", .{arg.zir_param_index}); - } - - fn writeTyOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_op = w.air.instructions.items(.data)[@backingInt(inst)].ty_op; - try w.writeType(s, ty_op.ty); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, ty_op.operand); - } - - fn writeBlock(w: *Writer, s: *std.Io.Writer, tag: Air.Inst.Tag, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - try w.writeType(s, ty_pl.ty); - - const body = switch (tag) { - .block => w.air.unwrapBlock(inst).body, - .dbg_inline_block => body: { - const dbg_block = w.air.unwrapDbgBlock(inst); - try s.writeAll(", "); - try w.writeInstRef(s, Air.internedToRef(dbg_block.func), false); - break :body dbg_block.body; - }, - else => unreachable, - }; - if (w.skip_body) return s.writeAll(", ..."); - const liveness_block: Air.Liveness.BlockSlices = if (w.liveness) |liveness| - liveness.getBlock(inst) - else - .{ .deaths = &.{} }; - - try s.writeAll(", {\n"); - const old_indent = w.indent; - w.indent += 2; - try w.writeBody(s, body); - w.indent = old_indent; - try s.splatByteAll(' ', w.indent); - try s.writeAll("}"); - - for (liveness_block.deaths) |operand| { - try s.print(" {f}!", .{operand}); - } - } - - fn writeLoop(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const block = w.air.unwrapBlock(inst); - - try w.writeType(s, block.ty); - if (w.skip_body) return s.writeAll(", ..."); - try s.writeAll(", {\n"); - const old_indent = w.indent; - w.indent += 2; - try w.writeBody(s, block.body); - w.indent = old_indent; - try s.splatByteAll(' ', w.indent); - try s.writeAll("}"); - } - - fn writeAggregateInit(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const zcu = w.zcu; - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const vector_ty = ty_pl.ty; - const len = @as(usize, @intCast(vector_ty.arrayLen(zcu))); - const elements = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[ty_pl.payload..][0..len])); - - try w.writeType(s, vector_ty); - try s.writeAll(", ["); - for (elements, 0..) |elem, i| { - if (i != 0) try s.writeAll(", "); - try w.writeOperand(s, inst, i, elem); - } - try s.writeAll("]"); - } - - fn writeUnionInit(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const extra = w.air.extraData(Air.UnionInit, ty_pl.payload).data; - - try s.print("{d}, ", .{extra.field_index}); - try w.writeOperand(s, inst, 0, extra.init); - } - - fn writeStructField(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const extra = w.air.extraData(Air.StructField, ty_pl.payload).data; - - try w.writeOperand(s, inst, 0, extra.struct_operand); - try s.print(", {d}", .{extra.field_index}); - } - - fn writeTyPlBin(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const data = w.air.instructions.items(.data); - const ty_pl = data[@backingInt(inst)].ty_pl; - const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; - - const inst_ty = data[@backingInt(inst)].ty_pl.ty; - try w.writeType(s, inst_ty); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.rhs); - } - - fn writeCmpxchg(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data; - - try w.writeOperand(s, inst, 0, extra.ptr); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.expected_value); - try s.writeAll(", "); - try w.writeOperand(s, inst, 2, extra.new_value); - try s.print(", {s}, {s}", .{ - @tagName(extra.successOrder()), @tagName(extra.failureOrder()), - }); - } - - fn writeMulAdd(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - const extra = w.air.extraData(Air.Bin, pl_op.payload).data; - - try w.writeOperand(s, inst, 0, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.rhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 2, pl_op.operand); - } - - fn writeLegalizeVecStoreElem(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - const bin = w.air.extraData(Air.Bin, pl_op.payload).data; - - try w.writeOperand(s, inst, 0, pl_op.operand); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, bin.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 2, bin.rhs); - } - - fn writeLegalizeCompilerRtCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const rt_call = w.air.unwrapCompilerRtCall(inst); - const args = rt_call.args; - - try s.print("{t}, [", .{rt_call.func}); - for (args, 0..) |arg, i| { - if (i != 0) try s.writeAll(", "); - try w.writeOperand(s, inst, i, arg); - } - try s.writeByte(']'); - } - - fn writeShuffleOne(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const unwrapped = w.air.unwrapShuffleOne(w.zcu, inst); - try w.writeType(s, unwrapped.result_ty); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, unwrapped.operand); - try s.writeAll(", ["); - for (unwrapped.mask, 0..) |mask_elem, mask_idx| { - if (mask_idx > 0) try s.writeAll(", "); - switch (mask_elem.unwrap()) { - .elem => |idx| try s.print("elem {d}", .{idx}), - .value => |val| try s.print("val {f}", .{Value.fromInterned(val).fmtValue(w.zcu)}), - } - } - try s.writeByte(']'); - } - - fn writeShuffleTwo(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const unwrapped = w.air.unwrapShuffleTwo(w.zcu, inst); - try w.writeType(s, unwrapped.result_ty); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, unwrapped.operand_a); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, unwrapped.operand_b); - try s.writeAll(", ["); - for (unwrapped.mask, 0..) |mask_elem, mask_idx| { - if (mask_idx > 0) try s.writeAll(", "); - switch (mask_elem.unwrap()) { - .a_elem => |idx| try s.print("a_elem {d}", .{idx}), - .b_elem => |idx| try s.print("b_elem {d}", .{idx}), - .undef => try s.writeAll("undef"), - } - } - try s.writeByte(']'); - } - - fn writeSelect(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const zcu = w.zcu; - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - const extra = w.air.extraData(Air.Bin, pl_op.payload).data; - - const elem_ty = w.typeOfIndex(inst).childType(zcu); - try w.writeType(s, elem_ty); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, pl_op.operand); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 2, extra.rhs); - } - - fn writeReduce(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const reduce = w.air.instructions.items(.data)[@backingInt(inst)].reduce; - - try w.writeOperand(s, inst, 0, reduce.operand); - try s.print(", {s}", .{@tagName(reduce.operation)}); - } - - fn writeCmpVector(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const extra = w.air.extraData(Air.VectorCmp, ty_pl.payload).data; - - try s.print("{s}, ", .{@tagName(extra.compareOperator())}); - try w.writeOperand(s, inst, 0, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.rhs); - } - - fn writeRuntimeNavPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ip = &w.zcu.intern_pool; - const ty_nav = w.air.instructions.items(.data)[@backingInt(inst)].ty_nav; - try w.writeType(s, ty_nav.ty); - try s.print(", '{f}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); - } - - fn writeAtomicLoad(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const atomic_load = w.air.instructions.items(.data)[@backingInt(inst)].atomic_load; - - try w.writeOperand(s, inst, 0, atomic_load.ptr); - try s.print(", {s}", .{@tagName(atomic_load.order)}); - } - - fn writePrefetch(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const prefetch = w.air.instructions.items(.data)[@backingInt(inst)].prefetch; - - try w.writeOperand(s, inst, 0, prefetch.ptr); - try s.print(", {s}, {d}, {s}", .{ - @tagName(prefetch.rw), prefetch.locality, @tagName(prefetch.cache), - }); - } - - fn writeAtomicStore( - w: *Writer, - s: *std.Io.Writer, - inst: Air.Inst.Index, - order: std.lang.AtomicOrder, - ) Error!void { - const bin_op = w.air.instructions.items(.data)[@backingInt(inst)].bin_op; - try w.writeOperand(s, inst, 0, bin_op.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, bin_op.rhs); - try s.print(", {s}", .{@tagName(order)}); - } - - fn writeAtomicRmw(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data; - - try w.writeOperand(s, inst, 0, pl_op.operand); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.operand); - try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); - } - - fn writeFieldParentPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const ty_pl = w.air.instructions.items(.data)[@backingInt(inst)].ty_pl; - const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; - - try w.writeOperand(s, inst, 0, extra.field_ptr); - try s.print(", {d}", .{extra.field_index}); - } - - fn writeAssembly(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const unwrapped_asm = w.air.unwrapAsm(inst); - const is_volatile = unwrapped_asm.is_volatile; - var op_index: usize = 0; - - const ret_ty = w.typeOfIndex(inst); - try w.writeType(s, ret_ty); - - if (is_volatile) { - try s.writeAll(", volatile"); - } - - var it = unwrapped_asm.iterateOutputs(); - while (it.next()) |out| { - const name = out.name; - const constraint = out.constraint; - if (out.operand == .none) { - try s.print(", [{s}] -> {s}", .{ name, constraint }); - } else { - try s.print(", [{s}] out {s} = (", .{ name, constraint }); - try w.writeOperand(s, inst, op_index, out.operand); - op_index += 1; - try s.writeByte(')'); - } - } - - it = unwrapped_asm.iterateInputs(); - while (it.next()) |in| { - const name = in.name; - const constraint = in.constraint; - try s.print(", [{s}] in {s} = (", .{ name, constraint }); - try w.writeOperand(s, inst, op_index, in.operand); - op_index += 1; - try s.writeByte(')'); - } - - const zcu = w.zcu; - const ip = &zcu.intern_pool; - const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); - const clobbers_ty = clobbers_val.typeOf(zcu); - var clobbers_bigint_buf: Value.BigIntSpace = undefined; - const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu); - for (0..clobbers_ty.structFieldCount(zcu)) |field_index| { - assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type); - const limb_bits = @bitSizeOf(std.math.big.Limb); - if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false - switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) { - 0 => continue, // field is false - 1 => {}, // field is true - } - const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?; - assert(clobber.len != 0); - try s.writeAll(", ~{"); - try s.writeAll(clobber); - try s.writeAll("}"); - } - const asm_source = unwrapped_asm.source; - try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)}); - } - - fn writeDbgStmt(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const dbg_stmt = w.air.instructions.items(.data)[@backingInt(inst)].dbg_stmt; - try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); - } - - fn writeDbgVar(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - try w.writeOperand(s, inst, 0, pl_op.operand); - const name: Air.NullTerminatedString = @fromBackingInt(@intCast(pl_op.payload)); - try s.print(", \"{f}\"", .{std.zig.fmtString(name.toSlice(w.air))}); - } - - fn writeCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const call = w.air.unwrapCall(inst); - const args = call.args; - try w.writeOperand(s, inst, 0, call.callee); - try s.writeAll(", ["); - for (args, 0..) |arg, i| { - if (i != 0) try s.writeAll(", "); - try w.writeOperand(s, inst, 1 + i, arg); - } - try s.writeAll("]"); - } - - fn writeBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const br = w.air.instructions.items(.data)[@backingInt(inst)].br; - try w.writeInstIndex(s, br.block_inst, false); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, br.operand); - } - - fn writeRepeat(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const repeat = w.air.instructions.items(.data)[@backingInt(inst)].repeat; - try w.writeInstIndex(s, repeat.loop_inst, false); - } - - fn writeTry(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const unwrapped_try = w.air.unwrapTry(inst); - const body = unwrapped_try.else_body; - const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| - liveness.getCondBr(inst) - else - .{ .then_deaths = &.{}, .else_deaths = &.{} }; - - try w.writeOperand(s, inst, 0, unwrapped_try.error_union); - if (w.skip_body) return s.writeAll(", ..."); - try s.writeAll(", {\n"); - const old_indent = w.indent; - w.indent += 2; - - if (liveness_condbr.else_deaths.len != 0) { - try s.splatByteAll(' ', w.indent); - for (liveness_condbr.else_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{f}!", .{operand}); - } - try s.writeAll("\n"); - } - try w.writeBody(s, body); - - w.indent = old_indent; - try s.splatByteAll(' ', w.indent); - try s.writeAll("}"); - - for (liveness_condbr.then_deaths) |operand| { - try s.print(" {f}!", .{operand}); - } - } - - fn writeTryPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const unwrapped_try = w.air.unwrapTryPtr(inst); - const body = unwrapped_try.else_body; - const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| - liveness.getCondBr(inst) - else - .{ .then_deaths = &.{}, .else_deaths = &.{} }; - - try w.writeOperand(s, inst, 0, unwrapped_try.error_union_ptr); - - try s.writeAll(", "); - try w.writeType(s, unwrapped_try.error_union_payload_ptr_ty); - if (w.skip_body) return s.writeAll(", ..."); - try s.writeAll(", {\n"); - const old_indent = w.indent; - w.indent += 2; - - if (liveness_condbr.else_deaths.len != 0) { - try s.splatByteAll(' ', w.indent); - for (liveness_condbr.else_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{f}!", .{operand}); - } - try s.writeAll("\n"); - } - try w.writeBody(s, body); - - w.indent = old_indent; - try s.splatByteAll(' ', w.indent); - try s.writeAll("}"); - - for (liveness_condbr.then_deaths) |operand| { - try s.print(" {f}!", .{operand}); - } - } - - fn writeCondBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const cond_br = w.air.unwrapCondBr(inst); - const then_body = cond_br.then_body; - const else_body = cond_br.else_body; - const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| - liveness.getCondBr(inst) - else - .{ .then_deaths = &.{}, .else_deaths = &.{} }; - - try w.writeOperand(s, inst, 0, cond_br.condition); - if (w.skip_body) return s.writeAll(", ..."); - try s.writeAll(","); - if (cond_br.branch_hints.true != .none) { - try s.print(" {s}", .{@tagName(cond_br.branch_hints.true)}); - } - if (cond_br.branch_hints.then_cov != .none) { - try s.print(" {s}", .{@tagName(cond_br.branch_hints.then_cov)}); - } - try s.writeAll(" {\n"); - const old_indent = w.indent; - w.indent += 2; - - if (liveness_condbr.then_deaths.len != 0) { - try s.splatByteAll(' ', w.indent); - for (liveness_condbr.then_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{f}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, then_body); - try s.splatByteAll(' ', old_indent); - try s.writeAll("},"); - if (cond_br.branch_hints.false != .none) { - try s.print(" {s}", .{@tagName(cond_br.branch_hints.false)}); - } - if (cond_br.branch_hints.else_cov != .none) { - try s.print(" {s}", .{@tagName(cond_br.branch_hints.else_cov)}); - } - try s.writeAll(" {\n"); - - if (liveness_condbr.else_deaths.len != 0) { - try s.splatByteAll(' ', w.indent); - for (liveness_condbr.else_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{f}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, else_body); - w.indent = old_indent; - - try s.splatByteAll(' ', old_indent); - try s.writeAll("}"); - } - - fn writeSwitchBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const switch_br = w.air.unwrapSwitch(inst); - - const liveness: Air.Liveness.SwitchBrTable = if (w.liveness) |liveness| - liveness.getSwitchBr(w.gpa, inst, switch_br.cases_len + 1) catch - @panic("out of memory") - else blk: { - const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.cases_len + 1) catch - @panic("out of memory"); - @memset(slice, &.{}); - break :blk .{ .deaths = slice }; - }; - defer w.gpa.free(liveness.deaths); - - try w.writeOperand(s, inst, 0, switch_br.operand); - if (w.skip_body) return s.writeAll(", ..."); - const old_indent = w.indent; - w.indent += 2; - - var it = switch_br.iterateCases(); - while (it.next()) |case| { - try s.writeAll(", ["); - for (case.items, 0..) |item, item_i| { - if (item_i != 0) try s.writeAll(", "); - try w.writeInstRef(s, item, false); - } - for (case.ranges, 0..) |range, range_i| { - if (range_i != 0 or case.items.len != 0) try s.writeAll(", "); - try w.writeInstRef(s, range[0], false); - try s.writeAll("..."); - try w.writeInstRef(s, range[1], false); - } - try s.writeAll("] "); - const hint = switch_br.getHint(case.idx); - if (hint != .none) { - try s.print(".{s} ", .{@tagName(hint)}); - } - try s.writeAll("=> {\n"); - w.indent += 2; - - const deaths = liveness.deaths[case.idx]; - if (deaths.len != 0) { - try s.splatByteAll(' ', w.indent); - for (deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{f}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, case.body); - w.indent -= 2; - try s.splatByteAll(' ', w.indent); - try s.writeAll("}"); - } - - const else_body = it.elseBody(); - if (else_body.len != 0) { - try s.writeAll(", else "); - const hint = switch_br.getElseHint(); - if (hint != .none) { - try s.print(".{s} ", .{@tagName(hint)}); - } - try s.writeAll("=> {\n"); - w.indent += 2; - - const deaths = liveness.deaths[liveness.deaths.len - 1]; - if (deaths.len != 0) { - try s.splatByteAll(' ', w.indent); - for (deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{f}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, else_body); - w.indent -= 2; - try s.splatByteAll(' ', w.indent); - try s.writeAll("}"); - } - - try s.writeAll("\n"); - try s.splatByteAll(' ', old_indent); - } - - fn writeWasmMemorySize(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - try s.print("{d}", .{pl_op.payload}); - } - - fn writeWasmMemoryGrow(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - try s.print("{d}, ", .{pl_op.payload}); - try w.writeOperand(s, inst, 0, pl_op.operand); - } - - fn writeWorkDimension(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void { - const pl_op = w.air.instructions.items(.data)[@backingInt(inst)].pl_op; - try s.print("{d}", .{pl_op.payload}); - } - - fn writeOperand( - w: *Writer, - s: *std.Io.Writer, - inst: Air.Inst.Index, - op_index: usize, - operand: Air.Inst.Ref, - ) Error!void { - const small_tomb_bits = Air.Liveness.bpi - 1; - const dies = if (w.liveness) |liveness| blk: { - if (op_index < small_tomb_bits) - break :blk liveness.operandDies(inst, @intCast(op_index)); - var extra_index = liveness.special.get(inst).?; - var tomb_op_index: usize = small_tomb_bits; - while (true) { - const bits = liveness.extra[extra_index]; - if (op_index < tomb_op_index + 31) { - break :blk @as(u1, @truncate(bits >> @as(u5, @intCast(op_index - tomb_op_index)))) != 0; - } - if ((bits >> 31) != 0) break :blk false; - extra_index += 1; - tomb_op_index += 31; - } - } else false; - return w.writeInstRef(s, operand, dies); - } - - fn writeInstRef( - w: *Writer, - s: *std.Io.Writer, - operand: Air.Inst.Ref, - dies: bool, - ) Error!void { - if (@backingInt(operand) < InternPool.static_len) { - return s.print("@{}", .{operand}); - } else if (operand.toInterned()) |ip_index| { - const zcu = w.zcu; - const ty = Type.fromInterned(zcu.intern_pool.indexToKey(ip_index).typeOf()); - try s.print("<{f}, {f}>", .{ - ty.fmt(zcu), - Value.fromInterned(ip_index).fmtValue(w.zcu), - }); - } else { - return w.writeInstIndex(s, operand.toIndex().?, dies); - } - } - - fn writeInstIndex( - w: *Writer, - s: *std.Io.Writer, - inst: Air.Inst.Index, - dies: bool, - ) Error!void { - _ = w; - try s.print("{f}", .{inst}); - if (dies) try s.writeByte('!'); - } - - fn typeOfIndex(w: *Writer, inst: Air.Inst.Index) Type { - const zcu = w.zcu; - return w.air.typeOfIndex(inst, &zcu.intern_pool); - } -}; 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;