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..ed8b16da95710fd2d401cc9fc0c59c08b3462369 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); @@ -494,12 +504,6 @@ pub fn build(b: *std.Build) !void { .skip_linux = skip_linux, .skip_llvm = skip_llvm, .skip_libc = skip_libc, - }, .{ - .enable_llvm = enable_llvm, - .llvm_has_m68k = llvm_has_m68k, - .llvm_has_csky = llvm_has_csky, - .llvm_has_arc = llvm_has_arc, - .llvm_has_xtensa = llvm_has_xtensa, }); test_step.dependOn(test_cases_step); @@ -702,11 +706,6 @@ pub fn build(b: *std.Build) !void { .skip_libc = skip_libc, })); test_step.dependOn(tests.addCliTests(b)); - if (tests.addLlvmIrTests(b, .{ - .enable_llvm = enable_llvm, - .test_filters = test_filters, - .test_target_filters = test_target_filters, - })) |test_llvm_ir_step| test_step.dependOn(test_llvm_ir_step); try addWasiUpdateStep(b, version); @@ -783,7 +782,7 @@ pub fn build(b: *std.Build) !void { const runner = b.addExecutable(.{ .name = "runner", .root_module = b.createModule(.{ - .root_source_file = b.path("test/src/Runner.zig"), + .root_source_file = b.path("test/Runner.zig"), .target = b.graph.host, }), }); @@ -799,21 +798,27 @@ pub fn build(b: *std.Build) !void { .skip_darwin = skip_darwin, .skip_linux = skip_linux, .skip_llvm = skip_llvm, + .llvm_has_m68k = llvm_has_m68k, + .llvm_has_csky = llvm_has_csky, + .llvm_has_arc = llvm_has_arc, + .llvm_has_xtensa = llvm_has_xtensa, .gdb = b.option([]const u8, "gdb", "path to gdb binary"), .lldb = b.option([]const u8, "lldb", "path to lldb binary"), }; - const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); - try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", tests.incremental_matrix, runner_opts); - test_step.dependOn(test_incremental_step); - const test_debugger_step = b.step("test-debugger", "Run the debugger tests"); - if (runner_opts.gdb == null and runner_opts.lldb == null) { - test_debugger_step.dependOn(&b.addFail("test-debugger requires -Dgdb and/or -Dlldb").step); - } else { - try tests.addRunnerTests(b, runner, test_debugger_step, "test/debugger", tests.debugger_matrix, runner_opts); + if (runner_opts.gdb != null or runner_opts.lldb != null) { + try tests.addRunnerTests(b, runner, test_debugger_step, "test/debugger", @import("test/debugger/matrix.zon"), runner_opts); test_step.dependOn(test_debugger_step); - } + } else test_debugger_step.dependOn(&b.addFail("test-debugger requires -Dgdb and/or -Dlldb").step); + + const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); + try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", @import("test/incremental/matrix.zon"), runner_opts); + test_step.dependOn(test_incremental_step); + + const test_llvm_step = b.step("test-llvm", "Run the LLVM IR tests"); + try tests.addRunnerTests(b, runner, test_llvm_step, "test/llvm", @import("test/llvm/matrix.zon"), runner_opts); + test_step.dependOn(test_llvm_step); } fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { @@ -837,7 +842,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/Package/Manifest.zig b/lib/compiler/Maker/Package/Manifest.zig index 7dd507a20c8ce9836b04ba2d8e4822e569ef5e91..dd7fbd86f8c6d1ae3f4e003921a42fdf6e2a67dc 100644 --- a/lib/compiler/Maker/Package/Manifest.zig +++ b/lib/compiler/Maker/Package/Manifest.zig @@ -494,95 +494,9 @@ const Parse = struct { }; switch (result) { .success => {}, - .failure => |err| try p.appendStrLitError(err, token, bytes, offset), - } - } - - /// TODO: try to DRY this with AstGen.failWithStrLitError - fn appendStrLitError( - p: *Parse, - err: std.zig.string_literal.Error, - token: Ast.TokenIndex, - bytes: []const u8, - offset: u32, - ) Allocator.Error!void { - const raw_string = bytes[offset..]; - switch (err) { - .invalid_escape_character => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "invalid escape character: '{c}'", - .{raw_string[bad_index]}, - ); - }, - .expected_hex_digit => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "expected hex digit, found '{c}'", - .{raw_string[bad_index]}, - ); - }, - .empty_unicode_escape_sequence => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "empty unicode escape sequence", - .{}, - ); - }, - .expected_hex_digit_or_rbrace => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "expected hex digit or '}}', found '{c}'", - .{raw_string[bad_index]}, - ); - }, - .invalid_unicode_codepoint => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "unicode escape does not correspond to a unicode code point", - .{}, - ); - }, - .expected_lbrace => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "expected '{{', found '{c}", - .{raw_string[bad_index]}, - ); - }, - .expected_rbrace => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "expected '}}', found '{c}", - .{raw_string[bad_index]}, - ); - }, - .expected_single_quote => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "expected single quote ('), found '{c}", - .{raw_string[bad_index]}, - ); - }, - .invalid_character => |bad_index| { - try p.appendErrorOff( - token, - offset + @as(u32, @intCast(bad_index)), - "invalid byte in string or character literal: '{c}'", - .{raw_string[bad_index]}, - ); - }, - .empty_char_literal => { - try p.appendErrorOff(token, offset, "empty character literal", .{}); - }, + .failure => |err| try p.appendErrorOff(token, @intCast(offset + err.offset()), "{f}", .{ + err.fmt(bytes[offset..]), + }), } } 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/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig index 73b36d1ec7ad1b592cce4ac4716193c5ac5249b5..ed55273d560d5f92f73488c7ced019414a85ac13 100644 --- a/lib/compiler/Maker/Step/Run.zig +++ b/lib/compiler/Maker/Step/Run.zig @@ -795,6 +795,7 @@ fn waitZigTest( multi_reader: *Io.File.MultiReader, opt_metadata: *?TestMetadata, results: *Step.TestResults, + eb_wip: *std.zig.ErrorBundle.Wip, ) !union(enum) { write_failed: anyerror, no_poll: struct { @@ -885,7 +886,7 @@ fn waitZigTest( .{ builtin.zig_version_string, body }, ); }, - .error_bundle => step.result_error_bundle = try std.zig.Server.allocErrorBundle(gpa, body), + .error_bundle => try eb_wip.addBundleAsRoots(try std.zig.Server.allocErrorBundle(arena, body)), .test_metadata => { // `metadata` would only be populated if we'd already seen a `test_metadata`, but we // only request it once (and importantly, we don't re-request it if we kill and @@ -1573,6 +1574,9 @@ fn evalZigTest( }; var test_metadata: ?TestMetadata = null; + var eb_wip: std.zig.ErrorBundle.Wip = try .init(gpa); + defer eb_wip.deinit(); + while (true) { var child = try process.spawn(io, spawn_options); var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; @@ -1602,6 +1606,7 @@ fn evalZigTest( &multi_reader, &test_metadata, &test_results, + &eb_wip, )) { .write_failed => |err| { // The runner unexpectedly closed a stdio pipe, which means a crash. Make sure we've captured @@ -1687,6 +1692,7 @@ fn evalZigTest( } } } + step.result_error_bundle = try eb_wip.toOwnedBundle(""); return; }, .timeout => |timeout| { 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/lldb/pretty_printers.py b/lib/lldb/pretty_printers.py index 8246b583607273384aec2d5779dd500acc45da65..54c5e91327d5a3c04a26d9bae606d1b14cab76e4 100644 --- a/lib/lldb/pretty_printers.py +++ b/lib/lldb/pretty_printers.py @@ -13,34 +13,73 @@ page_size = 1 << 12 def log2_int(i): return i.bit_length() - 1 -def create_struct(parent, name, struct_type, inits): - struct_bytes, struct_data = bytearray(struct_type.size), lldb.SBData() - for field in struct_type.fields: - field_size = field.type.size - field_init = inits[field.name] - if isinstance(field_init, int): - match struct_data.byte_order: - case lldb.eByteOrderLittle: - byte_order = 'little' - case lldb.eByteOrderBig: - byte_order = 'big' - field_bytes = field_init.to_bytes(field_size, byte_order, signed=field.type.GetTypeFlags() & lldb.eTypeIsSigned != 0) - elif isinstance(field_init, lldb.SBValue): - field_bytes = field_init.data.uint8 - else: return - match struct_data.byte_order: +class ValueInit: + def __init__(self, value: lldb.SBValue): self.value = value + def toBytes(self, parent: lldb.SBValue, value_type: lldb.SBType) -> bytes: + return self.value.data.uint8 + def create(self, parent: lldb.SBValue, name: str, value_type: lldb.SBType) -> lldb.SBValue: + return self.value.Clone(name) + +class FromBytes(ValueInit): + def __init__(self, value_bytes: bytes): self.value_bytes = value_bytes + def toBytes(self, parent: lldb.SBValue, value_type: lldb.SBType) -> bytes: return self.value_bytes + def create(self, parent: lldb.SBValue, name: str, value_type: lldb.SBType) -> lldb.SBValue: + data = lldb.SBData() + error = lldb.SBError() + value_bytes = self.toBytes(parent, value_type); + target = parent.target + data.SetData(error, value_bytes, target.byte_order, target.addr_size) + return parent.CreateValueFromData(name, data, value_type) + +class IntLiteral(FromBytes): + def __init__(self, int_value: int): self.int_value = int_value + def toBytes(self, parent: lldb.SBValue, int_type: lldb.SBType) -> bytes: + int_flags = int_type.GetTypeFlags() + assert int_flags & lldb.eTypeIsInteger != 0 + int_is_signed = int_flags & lldb.eTypeIsSigned != 0 + match parent.target.byte_order: case lldb.eByteOrderLittle: - field_bytes = field_bytes[:field_size] - field_start = field.byte_offset - struct_bytes[field_start:field_start + len(field_bytes)] = field_bytes + byte_order = 'little' case lldb.eByteOrderBig: - field_bytes = field_bytes[-field_size:] - field_end = field.byte_offset + field_size - struct_bytes[field_end - len(field_bytes):field_end] = field_bytes - struct_data.SetData(lldb.SBError(), struct_bytes, struct_data.byte_order, struct_data.GetAddressByteSize()) - return parent.CreateValueFromData(name, struct_data, struct_type) + byte_order = 'big' + return self.int_value.to_bytes(int_type.size, byte_order, signed=int_is_signed) -# Define Zig Language +class FromBackingInt(FromBytes): + def __init__(self, field_value: int): self.field_value = field_value + def fieldValue(self, enum_type: lldb.SBType) -> int: return self.field_value + def toBytes(self, parent: lldb.SBValue, enum_type: lldb.SBType) -> bytes: + assert enum_type.type == lldb.eTypeClassEnumeration + backing_int = IntLiteral(self.fieldValue(enum_type)); + return backing_int.toBytes(parent, enum_type.GetEnumerationIntegerType()) + +class EnumField(FromBackingInt): + def __init__(self, field_index: int): self.field_index = field_index + def fieldIndex(self, enum_type: lldb.SBType) -> int: return self.field_index + def fieldValue(self, enum_type: lldb.SBType) -> int: + enum_is_signed = enum_type.GetEnumerationIntegerType().GetTypeFlags() & lldb.eTypeIsSigned != 0 + field = enum_type.enum_members[self.fieldIndex(enum_type)] + return field.signed if enum_is_signed else field.unsigned + +class EnumLiteral(EnumField): + def __init__(self, field_name: str): self.field_name = field_name + def fieldName(self, enum_type: lldb.SBType) -> str: return self.field_name + def fieldIndex(self, enum_type: lldb.SBType) -> int: + field_name = self.fieldName(enum_type) + return next(field_index for field_index, field in enumerate(enum_type.enum_members) + if field.name == field_name) + +class StructInit(FromBytes): + def __init__(self, field_inits: dict[str, ValueInit]): self.field_inits = field_inits + def toBytes(self, parent: lldb.SBValue, struct_type: lldb.SBType) -> bytes: + assert struct_type.type == lldb.eTypeClassStruct + struct_bytes = bytearray(struct_type.size) + for field in struct_type.fields: + field_bytes = self.field_inits[field.name].toBytes(parent, field.type) + assert len(field_bytes) == field.type.size + struct_bytes[field.byte_offset:field.byte_offset + len(field_bytes)] = field_bytes + return bytes(struct_bytes) + +# Define Zig Language (for programs compiled with the llvm backend) zig_keywords = { 'addrspace', @@ -204,7 +243,7 @@ class zig_TaggedUnion_SynthProvider: except: return -1 def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index in range(2) else None -# Define Zig Standard Library +# Define Zig Standard Library (for programs compiled with the llvm backend) class std_MultiArrayList_SynthProvider: def __init__(self, value, _=None): self.value = value @@ -331,7 +370,7 @@ class std_Entry_SynthProvider: def get_child_index(self, name): return self.indices.get(name) def get_child_at_index(self, index): return self.children[index].deref if index in range(len(self.children)) else None -# Define Zig Compiler +# Define Zig Compiler (compiled with the llvm backend) class TagAndPayload_SynthProvider: def __init__(self, value, _=None): self.value = value @@ -435,8 +474,9 @@ class InternPool_Index_SynthProvider: def __init__(self, value, _=None): self.value = value def update(self): try: + target = self.value.target index_type = self.value.type - for helper in self.value.target.FindFunctions('%s.dbHelper' % index_type.name, lldb.eFunctionNameTypeFull): + for helper in target.FindFunctions('%s.dbHelper' % index_type.name, lldb.eFunctionNameTypeFull): ptr_self_type, ptr_tag_to_encoding_map_type = helper.function.type.GetFunctionArgumentTypes() if ptr_self_type.GetPointeeType() == index_type: break else: return @@ -465,7 +505,7 @@ class InternPool_Index_SynthProvider: for trailing_field in encoding_field.type.fields: trailing_data.Append(extra.GetChildAtIndex(extra_index).address_of.data) trailing_len = dynamic_values['trailing.%s.len' % trailing_field.name].unsigned - trailing_data.Append(lldb.SBData.CreateDataFromInt(trailing_len, trailing_data.GetAddressByteSize())) + trailing_data.Append(lldb.SBData.CreateDataFromInt(trailing_len, target.addr_size, target=target)) extra_index += trailing_len self.trailing = self.data.CreateValueFromData('trailing', trailing_data, encoding_field.type) else: @@ -685,8 +725,18 @@ value_tag_handlers = { class root_InternPool_Local_List_SynthProvider: def __init__(self, value, _=None): self.value = value def update(self): - capacity = self.value.EvaluateExpression('@as(*@This().Header, @alignCast(@ptrCast(@this().bytes - @This().bytes_offset))).capacity') - self.view = create_struct(self.value, '.view', self.value.type.FindDirectNestedType('View'), { 'bytes': self.value.GetChildMemberWithName('bytes'), 'len': capacity, 'capacity': capacity }).GetNonSyntheticValue() + capacity = IntLiteral(self.value.EvaluateExpression( + '@as(*@This().Header, @alignCast(@ptrCast(@this().bytes - @This().bytes_offset))).capacity', + ).unsigned) + View = self.value.type.FindDirectNestedType('View') + self.view = StructInit({ + 'bytes': ValueInit(self.value.GetChildMemberWithName('bytes')), + 'len': capacity, + 'capacity': capacity, + 'pointer_stability': StructInit({ + 'state': EnumField(0), + }), + }).create(self.value, '.view', View).GetNonSyntheticValue() def has_children(self): return True def num_children(self): return 1 def get_child_index(self, name): @@ -719,7 +769,10 @@ class root_InternPool_Index_SynthProvider: unwrapped_type = self.value.type.FindDirectNestedType('Unwrapped') ip = self.value.CreateValueFromType(unwrapped_type).GetChildMemberWithName('debug_state').GetChildMemberWithName('intern_pool').GetNonSyntheticValue().GetChildMemberWithName('?') tid_shift_30 = ip.GetChildMemberWithName('tid_shift_30').unsigned - self.unwrapped = create_struct(self.value, '.unwrapped', unwrapped_type, { 'tid': wrapped >> tid_shift_30, 'index': wrapped & (1 << tid_shift_30) - 1 }) + self.unwrapped = StructInit({ + 'tid': FromBackingInt(wrapped >> tid_shift_30), + 'index': IntLiteral(wrapped & (1 << tid_shift_30) - 1), + }).create(self.value, '.unwrapped', unwrapped_type) def has_children(self): return True def num_children(self): return 0 def get_child_index(self, name): @@ -732,6 +785,7 @@ class root_InternPool_Index_SynthProvider: class root_InternPool_Index_Unwrapped_SynthProvider: def __init__(self, value, _=None): self.value = value def update(self): + target = self.value.target self.tag, self.index, self.data, self.payload, self.trailing = None, None, None, None, None index = self.value.GetChildMemberWithName('index') ip = self.value.CreateValueFromType(self.value.type).GetChildMemberWithName('debug_state').GetChildMemberWithName('intern_pool').GetNonSyntheticValue().GetChildMemberWithName('?') @@ -742,36 +796,34 @@ class root_InternPool_Index_Unwrapped_SynthProvider: encoding_index, encoding_data, encoding_payload, encoding_trailing, encoding_config = encoding.GetChildMemberWithName('index'), encoding.GetChildMemberWithName('data'), encoding.GetChildMemberWithName('payload'), encoding.GetChildMemberWithName('trailing'), encoding.GetChildMemberWithName('config') if encoding_index: index_type = encoding_index.GetValueAsType() - index_bytes, index_data = index.data.uint8, lldb.SBData() - match index_data.byte_order: + index_bytes = index.data.uint8 + match target.byte_order: case lldb.eByteOrderLittle: index_bytes = bytes(index_bytes[:index_type.size]) case lldb.eByteOrderBig: index_bytes = bytes(index_bytes[-index_type.size:]) - index_data.SetData(lldb.SBError(), index_bytes, index_data.byte_order, index_data.GetAddressByteSize()) - self.index = self.value.CreateValueFromData('.index', index_data, index_type) + self.index = FromBytes(index_bytes).create(self.value, '.index', index_type) elif encoding_data: data_type = encoding_data.GetValueAsType() - data_bytes, data_data = item_data.data.uint8, lldb.SBData() - match data_data.byte_order: + data_bytes = item_data.data.uint8 + match target.byte_order: case lldb.eByteOrderLittle: data_bytes = bytes(data_bytes[:data_type.size]) case lldb.eByteOrderBig: data_bytes = bytes(data_bytes[-data_type.size:]) - data_data.SetData(lldb.SBError(), data_bytes, data_data.byte_order, data_data.GetAddressByteSize()) - self.data = self.value.CreateValueFromData('.data', data_data, data_type) + self.data = FromBytes(data_bytes).create(self.value, '.data', data_type) elif encoding_payload: extra = shared.GetChildMemberWithName('extra').GetChildMemberWithName('view').GetChildMemberWithName('0') extra_index = item_data.unsigned payload_type = encoding_payload.GetValueAsType() payload_fields = dict() for payload_field in payload_type.fields: - payload_fields[payload_field.name] = extra.child[extra_index] + payload_fields[payload_field.name] = ValueInit(extra.child[extra_index]) extra_index += 1 - self.payload = create_struct(self.value, '.payload', payload_type, payload_fields) + self.payload = StructInit(payload_fields).create(self.value, '.payload', payload_type) if encoding_trailing and encoding_config: trailing_type = encoding_trailing.GetValueAsType() - trailing_bytes, trailing_data = bytearray(trailing_type.size), lldb.SBData() + trailing_bytes = bytearray(trailing_type.size) def eval_config(config_name): expr = encoding_config.GetChildMemberWithName(config_name).value.removeprefix('.').removeprefix('@"').removesuffix('"').replace(r'\"', '"') if 'payload.' in expr: @@ -780,13 +832,11 @@ class root_InternPool_Index_Unwrapped_SynthProvider: field_type, field_byte_offset = trailing_type, 0 expr_parts = expr.split('.') for expr_part in expr_parts[1:]: - field = next(filter(lambda field: field.name == expr_part, field_type.fields)) + field = next(field for field in field_type.fields if field.name == expr_part) field_type = field.type field_byte_offset += field.byte_offset - field_data = lldb.SBData() field_bytes = trailing_bytes[field_byte_offset:field_byte_offset + field_type.size] - field_data.SetData(lldb.SBError(), field_bytes, field_data.byte_order, field_data.GetAddressByteSize()) - return self.value.CreateValueFromData('.%s' % expr_parts[-1], field_data, field_type) + return FromBytes(field_bytes).create(self.value, '.%s' % expr_parts[-1], field_type) else: return self.value.frame.EvaluateExpression(expr) for trailing_field in trailing_type.fields: @@ -812,7 +862,7 @@ class root_InternPool_Index_Unwrapped_SynthProvider: trailing_bytes[ptr_field_byte_offset:ptr_field_byte_offset + ptr_field.type.size] = extra.child[extra_index].address_of.data.uint8 len_field_value = eval_config('%s.len' % trailing_field_name) len_field_size = len_field.type.size - match trailing_data.byte_order: + match target.byte_order: case lldb.eByteOrderLittle: len_field_bytes = len_field_value.data.uint8[:len_field_size] trailing_bytes[len_field_byte_offset:len_field_byte_offset + len(len_field_bytes)] = len_field_bytes @@ -827,8 +877,7 @@ class root_InternPool_Index_Unwrapped_SynthProvider: trailing_bytes[trailing_field_byte_offset + offset:trailing_field_byte_offset + offset + 4] = extra.child[extra_index].data.uint8 extra_index += 1 break - trailing_data.SetData(lldb.SBError(), trailing_bytes, trailing_data.byte_order, trailing_data.GetAddressByteSize()) - self.trailing = self.value.CreateValueFromData('.trailing', trailing_data, trailing_type) + self.trailing = FromBytes(trailing_bytes).create(self.value, '.trailing', trailing_type) def has_children(self): return True def num_children(self): return 1 + ((self.index or self.data or self.payload) is not None) + (self.trailing is not None) def get_child_index(self, name): 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/Io/Threaded.zig b/lib/std/Io/Threaded.zig index c777c258127e4d72f38ff461f24c415d74b6e99b..44a716743d2a79ff675a4e39ccf630189acb1860 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -7191,9 +7191,9 @@ fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize { return n; }, .linux, .serenity, .illumos => { - var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined; + var proc_buf: [std.fmt.count("/proc/self/path/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}"; - const proc_path = std.mem.printSentinel(&procfs_buf, template, .{fd}, 0) catch unreachable; + const proc_path = std.mem.printSentinel(&proc_buf, template, .{fd}, 0) catch unreachable; const syscall: Syscall = try .start(); while (true) { const rc = posix.system.readlink(proc_path, out_buffer.ptr, out_buffer.len); @@ -7266,27 +7266,32 @@ fn fileHardLink( new_sub_path: []const u8, options: File.HardLinkOptions, ) File.HardLinkError!void { - _ = userdata; - if (native_os != .linux) return error.OperationUnsupported; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; var new_path_buffer: [posix.PATH_MAX]u8 = undefined; const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer); - const flags: u32 = if (options.follow_symlinks) - posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH - else - posix.AT.EMPTY_PATH; + switch (native_os) { + else => return error.OperationUnsupported, + .freebsd, .linux => { + const flags: u32 = if (options.follow_symlinks) + posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH + else + posix.AT.EMPTY_PATH; - return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags) catch |err| switch (err) { - error.FileNotFound => { - if (options.follow_symlinks) return error.FileNotFound; - var proc_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = std.mem.printSentinel(&proc_buf, "/proc/self/fd/{d}", .{file.handle}, 0) catch - unreachable; - return linkat(posix.AT.FDCWD, proc_path, new_dir.handle, new_sub_path_posix, posix.AT.SYMLINK_FOLLOW); + return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; }, - else => |e| return e, - }; + .netbsd, .openbsd => {}, + } + + if (options.follow_symlinks) return error.FileNotFound; + var dev_buf: [std.fmt.count("/dev/fd/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; + const dev_path = std.mem.printSentinel(&dev_buf, "/dev/fd/{d}", .{file.handle}, 0) catch unreachable; + return linkat(posix.AT.FDCWD, dev_path, new_dir.handle, new_sub_path_posix, posix.AT.SYMLINK_FOLLOW); } fn linkat( @@ -8732,12 +8737,12 @@ fn fchmodatFallback( ) Dir.SetFilePermissionsError!void { comptime assert(native_os == .linux); - // Fallback to changing permissions using procfs: + // Fallback to changing permissions using devfs: // // 1. Open `path` as a `PATH` descriptor. // 2. Stat the fd and check if it isn't a symbolic link. - // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`. - // 4. Pass the procfs path to `chmod` with the `mode`. + // 3. Generate the devfs reference to the fd via `/dev/fd/{fd}`. + // 4. Pass the devfs path to `chmod` with the `mode`. const path_fd: posix.fd_t = fd: { const syscall: Syscall = try .start(); while (true) { @@ -8808,11 +8813,11 @@ fn fchmodatFallback( if ((path_mode & posix.S.IFMT) == posix.S.IFLNK) return error.OperationUnsupported; - var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = std.mem.printSentinel(&procfs_buf, "/proc/self/fd/{d}", .{path_fd}, 0) catch unreachable; + var dev_buf: [std.fmt.count("/dev/fd/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; + const dev_path = std.mem.printSentinel(&dev_buf, "/dev/fd/{d}", .{path_fd}, 0) catch unreachable; const syscall: Syscall = try .start(); while (true) { - switch (posix.errno(posix.system.chmod(proc_path, mode))) { + switch (posix.errno(posix.system.chmod(dev_path, mode))) { .SUCCESS => return syscall.finish(), .INTR => { try syscall.checkCancel(); @@ -8821,7 +8826,7 @@ fn fchmodatFallback( else => |e| { syscall.finish(); switch (e) { - .NOENT => return error.OperationUnsupported, // procfs not mounted. + .NOENT => return error.OperationUnsupported, // devfs not mounted. .BADF => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), .INVAL => |err| return errnoBug(err), @@ -17446,9 +17451,6 @@ pub fn posixExecveat( ) process.ReplaceError { try Thread.checkCancel(); switch (err: { - if (path) |p| if (dir == posix.AT.FDCWD or Dir.path.isAbsoluteZ(p)) { - break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr)); - }; if (comptime native_os == .linux and (!builtin.link_libc or (builtin.target.isGnuLibC() and builtin.target.os.version_range.linux.glibc.order( .{ .major = 2, .minor = 34, .patch = 0 }, @@ -17462,34 +17464,57 @@ pub fn posixExecveat( .NOSYS => {}, } } - var path_buffer: [posix.PATH_MAX]u8 = undefined; - var path_len = realPathPosix(dir, &path_buffer) catch |err| switch (err) { - else => |e| return e, - error.InputOutput, - error.FileTooBig, - error.NoSpaceLeft, - error.PathAlreadyExists, - error.SymLinkLoop, - error.NetworkNotFound, - error.PipeBusy, - error.AntivirusInterference, - => return error.FileNotFound, - error.DeviceBusy, - error.NoDevice, - error.UnrecognizedVolume, - => return error.FileSystem, + if (path) |p| if (dir == posix.AT.FDCWD or Dir.path.isAbsoluteZ(p)) { + break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr)); }; - if (path) |p| { - const path_slice = std.mem.span(p); - path_buffer[path_len] = Dir.path.sep; - path_len += 1; - @memcpy(path_buffer[path_len..][0..path_slice.len], path_slice); - path_len += path_slice.len; + const exe: posix.fd_t = if (path) |p| exe: { + var flags: posix.O = .{}; + if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true; + if (@hasField(posix.O, "LARGEFILE")) flags.LARGEFILE = true; + if (@hasField(posix.O, "NOCTTY")) flags.NOCTTY = true; + if (@hasField(posix.O, "PATH")) flags.PATH = true; + if (@hasField(posix.O, "EXEC")) flags.EXEC = true; + const rc = openat_sym(dir, p, flags, @as(posix.mode_t, 0)); + while (true) switch (posix.errno(rc)) { + .SUCCESS => break :exe @intCast(rc), + .INTR, .AGAIN => {}, + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .FBIG => return error.InvalidExe, + .OVERFLOW => return error.InvalidExe, + .ISDIR => return error.IsDir, + .LOOP => return error.InvalidExe, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.FileSystem, + .NOENT => return error.FileNotFound, + .SRCH => return error.FileNotFound, // Linux when opening procfs files. + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .BUSY => return error.FileSystem, + .TXTBSY => return error.FileBusy, + .NXIO => return error.FileSystem, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + }; + } else dir; + defer closeFd(exe); + if (builtin.link_libc and @TypeOf(posix.system.fexecve) != void) { + break :err posix.errno(posix.system.fexecve(exe, argv, env_block.slice.ptr)); + } + switch (native_os) { + else => return error.OperationUnsupported, + .linux, .serenity, .illumos => { + var proc_buf: [std.fmt.count("/proc/self/path/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; + const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}"; + const proc_path = std.mem.printSentinel(&proc_buf, template, .{exe}, 0) catch unreachable; + break :err posix.errno(posix.system.execve(proc_path, argv, env_block.slice.ptr)); + }, } - path_buffer[path_len] = 0; - break :err posix.errno( - posix.system.execve(path_buffer[0..path_len :0], argv, env_block.slice.ptr), - ); }) { .FAULT => |err| return errnoBug(err), // Bad pointer parameter. .@"2BIG" => return error.SystemResources, 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/Target/Query.zig b/lib/std/Target/Query.zig index 91df353ef218342e4e790e2c64318dfef52ae797..bace539353e1fb9427d7e355a679ecf80275bd1b 100644 --- a/lib/std/Target/Query.zig +++ b/lib/std/Target/Query.zig @@ -261,13 +261,11 @@ pub fn parse(args: ParseOptions) !Query { if (abi_it.next() != null) { if (abi.isGnu()) { result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) { - error.Overflow => return error.InvalidAbiVersion, - error.InvalidVersion => return error.InvalidAbiVersion, + error.Overflow, error.InvalidVersion => return error.InvalidAbiVersion, }; } else if (abi.isAndroid()) { result.android_api_level = std.fmt.parseUnsigned(u32, abi_ver_text, 10) catch |err| switch (err) { - error.InvalidCharacter => return error.InvalidVersion, - error.Overflow => |e| return e, + error.InvalidCharacter, error.Overflow => return error.InvalidAbiVersion, }; } else { return error.InvalidAbiVersion; @@ -428,139 +426,105 @@ pub fn canDetectLibC(self: Query) bool { return false; } -/// Formats a version with the patch component omitted if it is zero, +/// Prints a version with the patch component omitted if it is zero, /// unlike SemanticVersion.format which formats all its version components regardless. -fn formatVersion(version: SemanticVersion, gpa: Allocator, list: *ArrayList(u8)) !void { - if (version.patch == 0) { - try list.print(gpa, "{d}.{d}", .{ version.major, version.minor }); - } else { - try list.print(gpa, "{d}.{d}.{d}", .{ version.major, version.minor, version.patch }); +pub const PrintVersion = struct { + version: SemanticVersion, + pub fn format(print: PrintVersion, w: *std.Io.Writer) std.Io.Writer.Error!void { + try w.print("{d}.{d}", .{ print.version.major, print.version.minor }); + switch (print.version.patch) { + 0 => {}, + else => |patch| try w.print(".{d}", .{patch}), + } } -} +}; -pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 { - if (self.isNativeTriple()) return gpa.dupe(u8, "native"); +pub fn printZigTriple(query: *const Query, w: *std.Io.Writer) std.Io.Writer.Error!void { + if (query.isNativeTriple()) return w.writeAll("native"); - const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native"; - const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native"; - - var result: ArrayList(u8) = .empty; - defer result.deinit(gpa); - - try result.print(gpa, "{s}-{s}", .{ arch_name, os_name }); + try w.print("{s}-{s}", .{ + if (query.cpu_arch) |arch| @tagName(arch) else "native", + if (query.os_tag) |os_tag| @tagName(os_tag) else "native", + }); // The zig target syntax does not allow specifying a max os version with no min, so // if either are present, we need the min. - if (self.os_version_min) |min| { + if (query.os_version_min) |min| { switch (min) { .none => {}, - .semver => |v| { - try result.appendSlice(gpa, "."); - try formatVersion(v, gpa, &result); - }, - .windows => |v| { - try result.print(gpa, "{f}", .{v}); - }, + .semver => |v| try w.print(".{f}", .{PrintVersion{ .version = v }}), + .windows => |v| try w.print("{f}", .{v}), } } - if (self.os_version_max) |max| { + if (query.os_version_max) |max| { switch (max) { .none => {}, - .semver => |v| { - try result.appendSlice(gpa, "..."); - try formatVersion(v, gpa, &result); - }, - .windows => |v| { - // This is counting on a custom format() function defined on `WindowsVersion` - // to add a prefix '.' and make there be a total of three dots. - try result.print(gpa, "..{f}", .{v}); - }, + .semver => |v| try w.print("...{f}", .{PrintVersion{ .version = v }}), + // This is counting on a custom format() function defined on `WindowsVersion` + // to add a prefix '.' and make there be a total of three dots. + .windows => |v| try w.print("..{f}", .{v}), } } - if (self.glibc_version) |v| { - const name = @tagName(self.abi.?); - try result.ensureUnusedCapacity(gpa, name.len + 2); - result.appendAssumeCapacity('-'); - result.appendSliceAssumeCapacity(name); - result.appendAssumeCapacity('.'); - try formatVersion(v, gpa, &result); - } else if (self.android_api_level) |lvl| { - const name = @tagName(self.abi.?); - try result.ensureUnusedCapacity(gpa, name.len + 2); - result.appendAssumeCapacity('-'); - result.appendSliceAssumeCapacity(name); - result.appendAssumeCapacity('.'); - try result.print(gpa, "{d}", .{lvl}); - } else if (self.abi) |abi| { - const name = @tagName(abi); - try result.ensureUnusedCapacity(gpa, name.len + 1); - result.appendAssumeCapacity('-'); - result.appendSliceAssumeCapacity(name); + if (query.glibc_version) |v| { + try w.print("-{t}.{f}", .{ query.abi.?, PrintVersion{ .version = v } }); + } else if (query.android_api_level) |lvl| { + try w.print("-{t}.{d}", .{ query.abi.?, lvl }); + } else if (query.abi) |abi| { + try w.print("-{t}", .{abi}); } - - return result.toOwnedSlice(gpa); } -/// Renders the query into a textual representation that can be parsed via the +/// Prints the query into a textual representation that can be parsed via the /// `-mcpu` flag passed to the Zig compiler. -/// Appends the result to `buffer`. -pub fn serializeCpu(q: Query, buffer: *std.array_list.Managed(u8)) Allocator.Error!void { - try buffer.ensureUnusedCapacity(8); - switch (q.cpu_model) { - .native => { - buffer.appendSliceAssumeCapacity("native"); - }, - .baseline => { - buffer.appendSliceAssumeCapacity("baseline"); - }, - .determined_by_arch_os => { - if (q.cpu_arch == null) { - buffer.appendSliceAssumeCapacity("native"); - } else { - buffer.appendSliceAssumeCapacity("baseline"); - } - }, - .explicit => |model| { - try buffer.appendSlice(model.name); - }, +pub fn printCpu(query: *const Query, w: *std.Io.Writer) std.Io.Writer.Error!void { + cpu_model: switch (query.cpu_model) { + .native, .baseline => |_, cpu_model| try w.print("{t}", .{cpu_model}), + .determined_by_arch_os => continue :cpu_model if (query.cpu_arch) |_| .baseline else .native, + .explicit => |model| try w.writeAll(model.name), } - if (q.cpu_features_add.isEmpty() and q.cpu_features_sub.isEmpty()) { + if (query.cpu_features_add.isEmpty() and query.cpu_features_sub.isEmpty()) { // The CPU name alone is sufficient. return; } - const cpu_arch = q.cpu_arch orelse builtin.cpu.arch; + const cpu_arch = query.cpu_arch orelse builtin.cpu.arch; const all_features = cpu_arch.allFeaturesList(); for (all_features, 0..) |feature, i_usize| { const i: Target.Cpu.Feature.Set.Index = @intCast(i_usize); - try buffer.ensureUnusedCapacity(feature.name.len + 1); - if (q.cpu_features_sub.isEnabled(i)) { - buffer.appendAssumeCapacity('-'); - buffer.appendSliceAssumeCapacity(feature.name); - } else if (q.cpu_features_add.isEnabled(i)) { - buffer.appendAssumeCapacity('+'); - buffer.appendSliceAssumeCapacity(feature.name); + if (query.cpu_features_sub.isEnabled(i)) { + try w.print("-{s}", .{feature.name}); + } else if (query.cpu_features_add.isEnabled(i)) { + try w.print("+{s}", .{feature.name}); } } } -pub fn serializeCpuAlloc(q: Query, ally: Allocator) Allocator.Error![]u8 { - var buffer = std.array_list.Managed(u8).init(ally); - try serializeCpu(q, &buffer); - return buffer.toOwnedSlice(); +/// Deprecated; use `printZigTriple` instead. Will be removed in 0.19.0. +pub fn zigTriple(query: Query, gpa: Allocator) Allocator.Error![]u8 { + return gpa.print("{f}", .{std.fmt.alt(&query, .printZigTriple)}); +} + +/// Deprecated; use `printCpu` instead. Will be removed in 0.19.0. +pub fn serializeCpu(query: Query, buffer: *std.array_list.Managed(u8)) Allocator.Error!void { + try buffer.print("{f}", .{std.fmt.alt(&query, .printCpu)}); +} + +/// Deprecated; use `printCpu` instead. Will be removed in 0.19.0. +pub fn serializeCpuAlloc(query: Query, gpa: Allocator) Allocator.Error![]u8 { + return gpa.print("{f}", .{std.fmt.alt(&query, .printCpu)}); } /// Deprecated; use `zigTriple` instead. Will be removed in 0.18.0. -pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 { - return self.zigTriple(allocator); +pub fn allocDescription(query: Query, allocator: Allocator) ![]u8 { + return query.zigTriple(allocator); } /// Deprecated; set `glibc_version` directly. Will be removed in 0.18.0. -pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void { - self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch }; +pub fn setGnuLibCVersion(query: *Query, major: u32, minor: u32, patch: u32) void { + query.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch }; } fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) !void { diff --git a/lib/std/c.zig b/lib/std/c.zig index 6969f4df8a4b1f3be8daf11923312c2f9a7c8a52..093cab3db16ad24142617d63aa02ad06c1a3e743 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -8387,6 +8387,8 @@ pub const AT = switch (native_os) { /// to determine the target of relative file paths in the openat() and /// similar syscalls. pub const FDCWD = -100; + /// Fail rename if target exists + pub const RENAME_NOREPLACE = 0x0001; /// Check access using effective user and group ID pub const EACCESS = 0x0100; /// Do not follow symbolic links @@ -8397,6 +8399,10 @@ pub const AT = switch (native_os) { pub const REMOVEDIR = 0x0800; /// Fail if not under dirfd pub const BENEATH = 0x1000; + /// Do not allow name resolution to walk out of dirfd + pub const RESOLVE_BENEATH = 0x2000; + /// Operate on dirfd if path is empty + pub const EMPTY_PATH = 0x4000; }, .netbsd => struct { /// Magic value that specify the use of the current working directory @@ -8552,7 +8558,11 @@ pub const O = switch (native_os) { DIRECTORY: bool = false, CLOEXEC: bool = false, SEARCH: bool = false, - _: u8 = 0, + NOSIGPIPE: bool = false, + REGULAR: bool = false, + EXEC: bool = false, + CLOFORK: bool = false, + _: u4 = 0, }, .openbsd => packed struct(u32) { ACCMODE: std.posix.ACCMODE = .RDONLY, @@ -10937,9 +10947,13 @@ pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: mode_t) c_int; pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int; pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; -pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int; +pub extern "c" fn renameat(olddirfd: fd_t, oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int; pub extern "c" fn chdir(path: [*:0]const u8) c_int; pub extern "c" fn fchdir(fd: fd_t) c_int; +pub const fexecve = switch (native_os) { + else => {}, + .freebsd, .linux, .netbsd => private.fexecve, +}; pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; pub extern "c" fn execveat(dirfd: fd_t, path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, flags: EXECVEAT) c_int; pub extern "c" fn dup(fd: fd_t) c_int; @@ -11791,34 +11805,35 @@ pub const setkeymap = serenity.setkeymap; /// External definitions shared by two or more operating systems. const private = struct { - pub extern "c" fn strdup(s: [*:0]const c_char) ?[*:0]c_char; - pub extern "c" fn _strdup(s: [*:0]const c_char) ?[*:0]c_char; - pub extern "c" fn wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; - pub extern "c" fn _wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; + extern "c" fn strdup(s: [*:0]const c_char) ?[*:0]c_char; + extern "c" fn _strdup(s: [*:0]const c_char) ?[*:0]c_char; + extern "c" fn wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; + extern "c" fn _wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; - pub extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; - pub extern "c" fn _swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; + extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; + extern "c" fn _swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; - extern "c" fn close(fd: fd_t) c_int; extern "c" fn clock_getres(clk_id: clockid_t, tp: *timespec) c_int; extern "c" fn clock_gettime(clk_id: clockid_t, tp: *timespec) c_int; + extern "c" fn clock_nanosleep(clockid: clockid_t, flags: TIMER, t: *const timespec, remain: ?*timespec) c_int; + extern "c" fn close(fd: fd_t) c_int; extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: c_uint) isize; + extern "c" fn fexecve(fd: fd_t, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; extern "c" fn flock(fd: fd_t, operation: c_int) c_int; extern "c" fn fork() c_int; extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, buf: *Stat, flag: u32) c_int; extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) switch (native_os) { + else => c_int, .freebsd => isize, .illumos => usize, - else => c_int, }; extern "c" fn getrusage(who: c_int, usage: *rusage) c_int; extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int; extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; - extern "c" fn clock_nanosleep(clockid: clockid_t, flags: TIMER, t: *const timespec, remain: ?*timespec) c_int; extern "c" fn pipe2(fds: *[2]fd_t, flags: O) c_int; extern "c" fn readdir(dir: *DIR) ?*dirent; extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; 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/posix.zig b/lib/std/posix.zig index 146a521224cd1a041b9200eed79b1d1815918bfb..de41ed68cbe7619c947803c5705e6026525e3a3b 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -797,7 +797,7 @@ pub fn dl_iterate_phdr( const dl = @import("dynamic_library.zig"); switch (builtin.object_format) { - .elf, .c => {}, + .c, .elf, .llvmbc, .llvmir => {}, else => @compileError("dl_iterate_phdr is not available for this target"), } 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/lib/std/zig/string_literal.zig b/lib/std/zig/string_literal.zig index c51c2ab77c62faf84057d25f2739e37ece9fd0d6..95a293c2b86d68f3de2ee292b4df3d50666f9709 100644 --- a/lib/std/zig/string_literal.zig +++ b/lib/std/zig/string_literal.zig @@ -38,6 +38,11 @@ pub const Error = union(enum) { invalid_character: usize, /// `''`. Not returned for string literals. empty_char_literal, + /// The input ended before the end of the literal. + end_of_input: usize, + /// The literal ended before the end of the input. There was an otherwise successful + /// parse of a prefix of the input of length given by this payload written to the writer. + end_of_literal: usize, const FormatMessage = struct { err: Error, @@ -83,6 +88,12 @@ pub const Error = union(enum) { .empty_char_literal => try writer.writeAll( "empty character literal", ), + .end_of_input => try writer.writeAll( + "expected end of string literal", + ), + .end_of_literal => try writer.writeAll( + "unexpected end of string literal", + ), } } @@ -104,6 +115,8 @@ pub const Error = union(enum) { .expected_rbrace, .expected_single_quote, .invalid_character, + .end_of_input, + .end_of_literal, => |n| n, .empty_char_literal => 0, }; @@ -339,13 +352,16 @@ pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result { try writer.writeAll(buf[0..len]); } else { try writer.writeByte(@as(u8, @intCast(codepoint))); + if (index == bytes.len) return .{ .failure = .{ .end_of_input = index } }; } }, .failure => |err| return .{ .failure = err }, } }, '\n' => return .{ .failure = .{ .invalid_character = index } }, - '"' => return .success, + '"' => return if (index < bytes.len - 1) .{ + .failure = .{ .end_of_literal = index }, + } else .success, else => { try writer.writeByte(b); index += 1; 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..94dad2b74fa251bafaa9dd8246d52d8e45fdf0e1 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -147,6 +147,7 @@ failed_win32_resources: if (dev.env.supports(.win32_resource)) std.array_hash_ma pub fn values(_: @This()) [0]void { return .{}; } + pub fn clearRetainingCapacity(_: @This()) void {} pub fn deinit(_: @This(), _: Allocator) void {} } = .{}, @@ -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, @@ -243,8 +238,6 @@ reference_trace: ?u32 = null, /// This mutex guards all `Compilation` mutable state. mutex: std.Io.Mutex = .init, -test_filters: []const []const u8, - link_prog_node: std.Progress.Node = .none, llvm_opt_bisect_limit: c_int, @@ -1544,13 +1537,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, @@ -1646,28 +1633,11 @@ pub const CreateOptions = struct { }; }; -fn addModuleTableToCacheHash(zcu: *Zcu, hash: *Cache.HashHelper) error{ OutOfMemory, Unexpected }!void { - assert(zcu.module_roots.count() != 0); // module_roots is populated - - for (zcu.module_roots.keys(), zcu.module_roots.values()) |mod, opt_mod_root_file| { - if (mod == zcu.std_mod) continue; // redundant - if (opt_mod_root_file.unwrap()) |mod_root_file| { - if (zcu.fileByIndex(mod_root_file).is_builtin) continue; // redundant - } - cache_helpers.addModule(hash, mod); - hash.add(mod.root.root); - hash.addBytes(mod.root.sub_path); - hash.addBytes(mod.root_src_path); - hash.addListOfBytes(mod.deps.keys()); - } -} - const RtStrat = enum { none, lib, obj, zcu }; pub const CreateDiagnostic = union(enum) { export_table_import_table_conflict, emit_h_without_zcu, - illegal_zig_import, cross_libc_unavailable, find_native_libc: std.zig.LibCInstallation.FindError, libc_installation_missing_cc_dir, @@ -1683,7 +1653,6 @@ pub const CreateDiagnostic = union(enum) { switch (diag) { .export_table_import_table_conflict => try w.writeAll("'--import-table' and '--export-table' cannot be used together"), .emit_h_without_zcu => try w.writeAll("cannot emit C header with no Zig source files"), - .illegal_zig_import => try w.writeAll("this compiler implementation does not support importing the root source file of a provided module"), .cross_libc_unavailable => try w.writeAll("unable to provide libc for this target"), .find_native_libc => |err| try w.print("failed to find libc installation: {t}", .{err}), .libc_installation_missing_cc_dir => try w.writeAll("libc installation is missing cc directory"), @@ -1731,7 +1700,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, const use_llvm = options.config.use_llvm; const target = &options.root_mod.resolved_target.result; - const comp: *Compilation = comp: { + const comp = comp: { // We put the `Compilation` itself in the arena. Freeing the arena will free the module. // It's initialized later after we prepare the initialization options. const root_name = try arena.dupeSentinel(u8, options.root_name, 0); @@ -1932,21 +1901,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; @@ -2014,7 +1968,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, // TODO audit this and make sure everything is in it const comp = try arena.create(Compilation); - const opt_zcu: ?*Zcu = if (have_zcu) blk: { + const opt_zcu = if (have_zcu) blk: { // Pre-open the directory handles for cached ZIR code so that it does not need // to redundantly happen for each AstGen operation. const zir_sub_dir = "z"; @@ -2067,19 +2021,95 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, zcu.* = .{ .gpa = gpa, .comp = comp, - .main_mod = main_mod, + .llvm_object = null, + .root_mod = options.root_mod, + .main_mod = main_mod, .std_mod = std_mod, + .sema_prog_node = .none, + .codegen_prog_node = .none, + .pending_codegen_jobs = .init(0), + + .cur_sema_prog_node = .none, + .global_zir_cache = global_zir_cache, .local_zir_cache = local_zir_cache, + + .all_exports = .empty, + .free_exports = .empty, + .single_exports = .empty, + .multi_exports = .empty, + + .module_roots = .empty, + .builtin_modules = .empty, + + .import_table = .empty, + .alive_files = .empty, + .embed_table = .empty, + + .intern_pool = .empty, + + .compile_logs = .empty, + .compile_log_lines = .empty, + .free_compile_log_lines = .empty, + + .illegal_module_root = null, + .multi_module_err = null, + .failed_imports = .empty, + .failed_files = .empty, + .skip_analysis_this_update = false, + .analysis_in_progress = .empty, + .failed_analysis = .empty, + .transitive_failed_analysis = .empty, + .dependency_loops = .empty, + .dependency_loop_nodes = .empty, + .failed_codegen = .empty, + .failed_types = .empty, + .failed_exports = .empty, + .error_limit = error_limit, - .llvm_object = null, + + .outdated_lock = if (std.debug.runtime_safety) .init, + .potentially_outdated = .empty, + .outdated = .empty, + .outdated_ready = .{ + .funcs = .empty, + .other = .empty, + }, + .retryable_failures = .empty, + .analysis_roots_buffer = undefined, .analysis_roots_len = 0, + .resolved_references = null, + + .test_filters = options.test_filters, + .test_functions = .empty, + + .global_assembly = .empty, + + .reference_table = .empty, + .all_references = .empty, + .free_references = .empty, + + .inline_reference_frames = .empty, + .free_inline_reference_frames = .empty, + + .type_reference_table = .empty, + .all_type_references = .empty, + .free_type_references = .empty, + + .std_lang_decl_values = .initFill(.none), + + .incremental_debug_state = if (build_options.enable_debug_extensions) .init, + + .cur_analysis_timer = null, + .codegen_task_pool = try .init(arena), + + .generation = 0, + .anon_name_counter = 0, }; - try zcu.init(gpa, io, options.thread_limit); break :blk zcu; } else blk: { if (options.emit_h != .no) return diag.fail(.emit_h_without_zcu); @@ -2111,19 +2141,12 @@ 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, .stack_report = options.stack_report, - .test_filters = options.test_filters, .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, .debug_compile_errors = options.debug_compile_errors, .debug_incremental = options.debug_incremental, @@ -2175,18 +2198,8 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, comp.config.any_sanitize_c = any_sanitize_c; comp.config.any_fuzz = any_fuzz; - if (opt_zcu) |zcu| { - // Finish initializing the `zcu` after the fields on `comp` have been initialized. - zcu.initAfterCompilation(); - - // Populate `zcu.module_roots`. - const active = zcu.acquire(); - defer active.release(); - active.pt.populateModuleRootTable() catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.IllegalZigImport => return diag.fail(.illegal_zig_import), - }; - } + // Initialize the `zcu` after the fields on `comp` have been initialized. + if (opt_zcu) |zcu| zcu.init(); const lf_open_opts: link.File.OpenOptions = .{ .linker_script = options.linker_script, @@ -2282,20 +2295,10 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, hash.add(options.config.is_test); hash.addListOfBytes(options.test_filters); hash.add(options.skip_linker_dependencies); - hash.add(options.emit_h != .no); + //hash.add(options.emit_h != .no); hash.add(error_limit); - // Here we put the root source file path name, but *not* with addFile. - // We want the hash to be the same regardless of the contents of the - // source file, because incremental compilation will handle it, but we - // do want to namespace different source file names because they are - // likely different compilations and therefore this would be likely to - // cause cache hits. - if (comp.zcu) |zcu| { - try addModuleTableToCacheHash(zcu, &hash); - } else { - cache_helpers.addModule(&hash, options.root_mod); - } + try comp.addModuleTableToCacheHash(&hash); // In the case of incremental cache mode, this `artifact_directory` // is computed based on a hash of non-linker inputs, and it is where all @@ -2347,21 +2350,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic, comp.cache_use = .{ .whole = whole }; }, } - - if (use_llvm and - (comp.emit_bin != null or - 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)) - { - if (opt_zcu) |zcu| { - dev.check(.llvm_backend); - zcu.llvm_object = try LlvmObject.create(arena, zcu); - } - } - break :comp comp; }; errdefer comp.destroy(); @@ -2566,19 +2554,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; @@ -2643,8 +2618,8 @@ pub fn destroy(comp: *Compilation) void { } comp.win32_resources.deinit(gpa); - for (comp.failed_win32_resources.values()) |*value| { - value.deinit(gpa); + for (comp.failed_win32_resources.values()) |*bundle| { + bundle.deinit(gpa); } comp.failed_win32_resources.deinit(gpa); @@ -2668,8 +2643,8 @@ pub fn clearMiscFailures(comp: *Compilation) void { comp.misc_failures = .{}; } -pub fn getTarget(self: *const Compilation) *const Target { - return &self.root_mod.resolved_target.result; +pub fn getTarget(comp: *const Compilation) *const Target { + return &comp.root_mod.resolved_target.result; } /// Only legal to call when cache mode is incremental and a link file is present. @@ -2688,6 +2663,18 @@ pub fn hotCodeSwap( fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { const io = comp.io; + if (comp.bin_file) |lf| { + lf.endProgress(); + comp.link_prog_node.end(); + comp.link_prog_node = .none; + switch (comp.cache_use) { + .none, .whole => { + lf.destroy(); + comp.bin_file = null; + }, + .incremental => {}, + } + } switch (comp.cache_use) { .none => |none| { if (none.tmp_artifact_directory) |*tmp_dir| { @@ -2704,17 +2691,14 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { // replicated in the output binary (the output instead contains a reference to that // file which debug tooling can look through). So, in that particular case, we need // to keep this directory around so that the output binary can be debugged. - if (comp.bin_file != null and comp.getTarget().ofmt == .macho and comp.config.debug_format != .strip) { - // We are emitting an unstripped Mach-O binary with the LLVM backend: the ZCU - // object file must remain on-disk for its debug info. - return; + if (comp.bin_file == null or comp.getTarget().ofmt != .macho or comp.config.debug_format == .strip) { + const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); + comp.dirs.local_cache.handle.deleteTree(io, tmp_dir_sub_path) catch |err| { + log.warn("failed to delete temporary directory '{s}{c}{s}': {t}", .{ + comp.dirs.local_cache.path orelse ".", fs.path.sep, tmp_dir_sub_path, err, + }); + }; } - const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int); - comp.dirs.local_cache.handle.deleteTree(io, tmp_dir_sub_path) catch |err| { - log.warn("failed to delete temporary directory '{s}{c}{s}': {t}", .{ - comp.dirs.local_cache.path orelse ".", fs.path.sep, tmp_dir_sub_path, err, - }); - }; } }, .incremental => return, @@ -2723,10 +2707,6 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { man.deinit(); whole.cache_manifest = null; } - if (comp.bin_file) |lf| { - lf.destroy(); - comp.bin_file = null; - } if (whole.tmp_artifact_directory) |*tmp_dir| { tmp_dir.handle.close(io); whole.tmp_artifact_directory = null; @@ -2739,6 +2719,13 @@ fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void { } }, } + assert(!comp.config.incremental); // always uses incremental cache mode + if (comp.zcu) |zcu| { + if (zcu.llvm_object) |llvm_object| { + llvm_object.deinit(); + zcu.llvm_object = null; + } + } } pub const UpdateError = error{ @@ -2760,11 +2747,24 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - comp.clearMiscFailures(); - comp.last_update_was_cache_hit = false; - if (comp.time_report) |*tr| { - tr.deinit(gpa); // this is information about an old update - tr.* = .init; + reset: { + comp.clearMiscFailures(); + comp.last_update_was_cache_hit = false; + if (comp.time_report) |*tr| { + tr.deinit(comp.gpa); + tr.* = .init; + } + if (comp.file_system_inputs) |file_system_inputs| file_system_inputs.clearRetainingCapacity(); + + if (comp.zcu) |zcu| { + zcu.skip_analysis_this_update = false; + if (zcu.generation > 0) break :reset; + try zcu.reset(arena); + } + for (comp.failed_c_objects.values()) |bundle| bundle.destroy(comp.gpa); + comp.failed_c_objects.clearRetainingCapacity(); + for (comp.failed_win32_resources.values()) |*bundle| bundle.deinit(comp.gpa); + comp.failed_win32_resources.clearRetainingCapacity(); } var tmp_dir_rand_int: u64 = undefined; @@ -2909,39 +2909,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE if (comp.zcu) |zcu| { assert(zcu.cur_analysis_timer == null); - zcu.skip_analysis_this_update = false; - // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate! for (zcu.embed_table.keys()) |embed_file| { try comp.appendFileSystemInput(embed_file.path); } - - zcu.analysis_roots_len = 0; - - zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod; - zcu.analysis_roots_len += 1; - - // Normally we rely on importing std to in turn import the root source file in the start code. - // However, the main module is distinct from the root module in tests, so that won't happen there. - if (comp.config.is_test and zcu.main_mod != zcu.std_mod) { - zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod; - zcu.analysis_roots_len += 1; - } - - if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { - zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod; - zcu.analysis_roots_len += 1; - } - - if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| { - zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod; - zcu.analysis_roots_len += 1; - } - - if (zcu.root_mod.deps.get("zigc")) |zigc_mod| { - zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zigc_mod; - zcu.analysis_roots_len += 1; - } } // The linker progress node is set up here instead of in `performAllTheWork`, because @@ -2963,11 +2934,6 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE comp.link_prog_node = main_progress_node.start("Linking", initial_estimated_total); lf.startProgress(comp.link_prog_node); } - defer if (comp.bin_file) |lf| { - lf.endProgress(); - comp.link_prog_node.end(); - comp.link_prog_node = .none; - }; try comp.performAllTheWork(main_progress_node, arena); @@ -2991,14 +2957,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; } } @@ -3008,7 +2979,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE comp.setMiscFailure(.link_depfile, "writing linker dependency file failed: {t}", .{err}); }; - if (anyErrors(comp)) { + if (comp.anyErrors()) { // Skip flushing and keep source files loaded for error reporting. return; } @@ -3022,7 +2993,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE } switch (comp.cache_use) { - .none, .incremental => try flush(comp, arena), + .none, .incremental => try comp.flush(arena), .whole => |whole| { if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf); if (comp.parent_whole_cache) |pwc| { @@ -3103,11 +3074,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE }; } - try flush(comp, arena); + try comp.flush(arena); // Calling `flush` may have produced errors, in which case the // cache manifest must not be written. - if (anyErrors(comp)) return; + if (comp.anyErrors()) return; // Release the lock and close the file before writing the manifest. // This avoids the potential of another process getting a cache hit and @@ -3207,7 +3178,6 @@ fn flush(comp: *Compilation, arena: Allocator) (Io.Cancelable || Allocator.Error defer tid.release(io); if (comp.zcu) |zcu| { if (zcu.llvm_object) |llvm_object| { - // Emit the ZCU object from LLVM now; it's required to flush the output file. // If there's an output file, it wants to decide where the LLVM object goes! const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0); @@ -3220,28 +3190,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 +3238,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| { @@ -3341,23 +3312,42 @@ fn renameTmpIntoCache( /// anything from the link cache manifest. pub const link_hash_implementation_version = 14; -fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { - comptime assert(link_hash_implementation_version == 14); +fn addModuleTableToCacheHash(comp: *Compilation, hash: *Cache.HashHelper) Allocator.Error!void { + const gpa = comp.gpa; + var mods: std.array_hash_map.Auto(*Module, void) = .empty; + defer mods.deinit(gpa); + + try mods.putNoClobber(gpa, comp.root_mod, {}); + if (comp.zcu) |zcu| try mods.put(gpa, zcu.main_mod, {}); + + var mod_index: usize = 0; + while (mod_index < mods.count()) : (mod_index += 1) { + const mod = mods.keys()[mod_index]; - if (comp.zcu) |zcu| { // No need to hash the actual file contents here because it is // redundant with the logic in `PerThread.update` which iterates over // `zcu.alive_files` and adds those files discovered via `@import` to // the whole cache manifest. - try addModuleTableToCacheHash(zcu, &man.hash); + cache_helpers.addModule(hash, mod); + hash.add(mod.root.root); + hash.addBytes(mod.root.sub_path); + hash.addBytes(mod.root_src_path); + hash.addListOfBytes(mod.deps.keys()); + for (mod.deps.values()) |dep_mod| try mods.put(gpa, dep_mod, {}); + } +} + +fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { + comptime assert(link_hash_implementation_version == 14); + + try comp.addModuleTableToCacheHash(&man.hash); + + if (comp.zcu) |zcu| { // Synchronize with other matching comments: ZigOnlyHashStuff - man.hash.addListOfBytes(comp.test_filters); - man.hash.add(comp.skip_linker_dependencies); + man.hash.addListOfBytes(zcu.test_filters); //man.hash.add(zcu.emit_h != .no); man.hash.add(zcu.error_limit); - } else { - cache_helpers.addModule(&man.hash, comp.root_mod); } try link.hashInputs(man, comp.link_inputs); @@ -3826,7 +3816,7 @@ fn addBuf(list: *std.array_list.Managed([]const u8), buf: []const u8) void { } /// This function is temporally single-threaded. -pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { +pub fn getAllErrorsAlloc(comp: *Compilation) Allocator.Error!ErrorBundle { const gpa = comp.gpa; const io = comp.io; @@ -3869,6 +3859,15 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle { } if (comp.zcu) |zcu| zcu_errors: { + if (zcu.illegal_module_root) |mod| { + try bundle.addRootErrorMessage(.{ + .msg = try bundle.printString( + "this compiler implementation does not support using '{f}/{s}' as the root of module '{s}'", + .{ mod.root.fmt(comp), mod.root_src_path, mod.fully_qualified_name }, + ), + .notes_len = 0, + }); + } if (zcu.multi_module_err != null) { try zcu.addFileInMultipleModulesError(&bundle); break :zcu_errors; @@ -4397,7 +4396,9 @@ fn performAllTheWork( defer if (comp.zcu) |zcu| zcu.codegen_task_pool.cancel(zcu); if (comp.zcu) |zcu| { // Regardless of errors, `comp.zcu` needs to update its generation number. - defer zcu.generation += 1; + defer if (comp.config.incremental) { + zcu.generation += 1; + }; const active = zcu.acquire(); defer active.release(); try active.pt.update(main_progress_node, &decl_work_timer); @@ -4903,12 +4904,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 +7410,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 +7541,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 88c454fc2f3b414710886a524c80f4ecc7b09134..77bf96a5f0a46a67a56e1d445d3a9d9e3ff4e5d7 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -5255,6 +5255,7 @@ pub const Tag = enum(u8) { type_hash: ?u64, captures_len: ?u32, captures: ?[]CaptureValue, + reified_field_name: ?[]NullTerminatedString, field_types: []Index, field_aligns: ?[]Alignment, }, @@ -5263,6 +5264,8 @@ pub const Tag = enum(u8) { .@"trailing.captures_len.?" = .@"payload.flags.any_captures == .true", .@"trailing.captures.?" = .@"payload.flags.any_captures == .true", .@"trailing.captures.?.len" = .@"trailing.captures_len.?", + .@"trailing.reified_field_name.?" = .@"payload.flags.any_captures == .reified", + .@"trailing.reified_field_name.?.len" = .@"payload.fields_len", .@"trailing.field_types.len" = .@"payload.fields_len", .@"trailing.field_aligns.?" = .@"payload.flags.any_field_aligns", .@"trailing.field_aligns.?.len" = .@"(payload.fields_len + 3) & ~@as(u32, 3)", @@ -10588,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(); @@ -10665,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; @@ -10965,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; @@ -11068,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 4904149cf0f1c47e59d0ee7e4d4c4e11307acf43..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 @@ -27920,7 +27918,25 @@ fn elemValSlice( } } - if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); + switch (elem_ty.classify(zcu)) { + .no_possible_value => switch (elem_ty.zigTypeTag(zcu)) { + .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{elem_ty.fmt(zcu)}), + else => { + // Loading an uninstantiable type always invokes Illegal Behavior. + if (block.isComptime()) { + return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{elem_ty.fmt(zcu)}); + } else if (block.wantSafety()) { + try sema.safetyPanic(block, src, .load_uninstantiable_type); + return .unreachable_value; + } else { + _ = try block.addNoOp(.unreach); + return .unreachable_value; + } + }, + }, + .one_possible_value => return .fromValue((try elem_ty.onePossibleValue(pt)).?), + .runtime, .partially_comptime, .fully_comptime => {}, + } try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_src); try sema.validateRuntimeValue(block, slice_src, slice); diff --git a/src/Zcu.zig b/src/Zcu.zig index 01b136b01514721616febf43f65827b36d0ced4f..fdae0d840a69e6bc19ebddb32788b1598fef9865 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -40,6 +40,7 @@ const AnalUnit = InternPool.AnalUnit; const BuiltinFn = std.zig.BuiltinFn; const codegen = @import("codegen.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; +const debugLogScopeEnabled = @import("main.zig").debugLogScopeEnabled; const dev = @import("dev.zig"); const Zoir = std.zig.Zoir; const ZonGen = std.zig.ZonGen; @@ -69,18 +70,18 @@ root_mod: *Module, /// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests. main_mod: *Module, std_mod: *Module, -sema_prog_node: std.Progress.Node = .none, -codegen_prog_node: std.Progress.Node = .none, +sema_prog_node: std.Progress.Node, +codegen_prog_node: std.Progress.Node, /// The number of codegen jobs which are pending or in-progress. Whichever thread drops this value /// to 0 is responsible for ending `codegen_prog_node`. While semantic analysis is happening, this /// value bottoms out at 1 instead of 0, to ensure that it can only drop to 0 after analysis is /// completed (since semantic analysis could trigger more codegen work). -pending_codegen_jobs: std.atomic.Value(u32) = .init(0), +pending_codegen_jobs: std.atomic.Value(u32), /// This is the progress node *under* `sema_prog_node` which is currently running. /// When we have to pause to analyze something else, we just temporarily rename this node. /// Eventually, when we thread semantic analysis, we will want one of these per thread. -cur_sema_prog_node: std.Progress.Node = .none, +cur_sema_prog_node: std.Progress.Node, /// Used by AstGen worker to load and store ZIR cache. global_zir_cache: Cache.Directory, @@ -89,27 +90,26 @@ local_zir_cache: Cache.Directory, /// This is where all `Export` values are stored. Not all values here are necessarily valid exports; /// to enumerate all exports, `single_exports` and `multi_exports` must be consulted. -all_exports: std.ArrayList(Export) = .empty, +all_exports: std.ArrayList(Export), /// This is a list of free indices in `all_exports`. These indices may be reused by exports from /// future semantic analysis. -free_exports: std.ArrayList(Export.Index) = .empty, +free_exports: std.ArrayList(Export.Index), /// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of /// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit` /// whose analysis triggered the export. -single_exports: std.array_hash_map.Auto(AnalUnit, Export.Index) = .empty, +single_exports: std.array_hash_map.Auto(AnalUnit, Export.Index), /// Like `single_exports`, but for `AnalUnit`s which perform multiple exports. /// The exports are `all_exports.items[index..][0..len]`. multi_exports: std.array_hash_map.Auto(AnalUnit, extern struct { index: u32, len: u32, -}) = .empty, - -/// Key is the digest returned by `Builtin.hash`; value is the corresponding module. -builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Module) = .empty, +}), /// Populated as soon as the `Compilation` is created. Guaranteed to contain all modules, even builtin ones. /// Modules whose root file is not a Zig or ZON file have the value `.none`. -module_roots: std.array_hash_map.Auto(*Module, File.Index.Optional) = .empty, +module_roots: std.array_hash_map.Auto(*Module, File.Index.Optional), +/// Key is the digest returned by `Builtin.hash`; value is the corresponding module. +builtin_modules: std.array_hash_map.Auto(Cache.BinDigest, *Module), /// The set of all the Zig source files in the Zig Compilation Unit. Tracked in /// order to iterate over it and check which source files have been modified on @@ -132,26 +132,14 @@ import_table: std.array_hash_map.Custom( pub const eql = @compileError("all accesses should be through ImportTableAdapter"); }, true, // This is necessary! Without it, the map tries to use its Context to rehash. #21918 -) = .empty, - +), /// The set of all files in `import_table` which are "alive" this update, meaning /// they are reachable by traversing imports starting from an analysis root. This /// is usually all files in `import_table`, but some could be omitted if an incremental /// update removes an import, or if a module specified on the CLI is never imported. /// Reconstructed on every update, after AstGen and before Sema. /// Value is why the file is alive. -alive_files: std.array_hash_map.Auto(File.Index, File.Reference) = .empty, - -/// If this is populated, a "file exists in multiple modules" error should be emitted. -/// This causes file errors to not be shown, because we don't really know which files -/// should be alive (because the user has messed up their imports somewhere!). -/// Cleared and recomputed every update, after AstGen and before Sema. -multi_module_err: ?struct { - file: File.Index, - modules: [2]*Module, - refs: [2]File.Reference, -} = null, - +alive_files: std.array_hash_map.Auto(File.Index, File.Reference), /// The set of all the files which have been loaded with `@embedFile` in the Module. /// We keep track of this in order to iterate over it and check which files have been /// modified on the file system when an update is requested, as well as to cache @@ -162,49 +150,19 @@ multi_module_err: ?struct { /// /// This table owns all of the `*EmbedFile` memory, which is allocated into gpa. embed_table: std.array_hash_map.Custom( - *EmbedFile, + EmbedFile, void, struct { pub const hash = @compileError("all accesses should be through EmbedTableAdapter"); pub const eql = @compileError("all accesses should be through EmbedTableAdapter"); }, true, // This is necessary! Without it, the map tries to use its Context to rehash. #21918 -) = .empty, +), /// Stores all Type and Value objects. /// The idea is that this will be periodically garbage-collected, but such logic /// is not yet implemented. -intern_pool: InternPool = .empty, - -/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis -/// (index 0), and non-`null` for all others. -analysis_in_progress: std.array_hash_map.Auto(AnalUnit, ?*const DependencyReason) = .empty, -/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. -failed_analysis: std.array_hash_map.Auto(AnalUnit, *ErrorMsg) = .empty, -/// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed. -transitive_failed_analysis: std.array_hash_map.Auto( - AnalUnit, - if (build_options.enable_debug_extensions) TransitiveFailureReason else void, -) = .empty, -/// This `Nav` succeeded analysis, but failed codegen. -/// This may be a simple "value" `Nav`, or it may be a function. -/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. -/// While multiple threads are active (most of the time!), this is guarded by `zcu.comp.mutex`, as -/// codegen and linking run on a separate thread. -failed_codegen: std.array_hash_map.Auto(InternPool.Nav.Index, *ErrorMsg) = .empty, -failed_types: std.array_hash_map.Auto(InternPool.Index, *ErrorMsg) = .empty, - -/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one -/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible -/// between compilations. So, instead of (for instance) defining where the dependency loop "starts", -/// this map simply exists to allow easily iterating all dependency loops exactly once. -dependency_loops: std.array_hash_map.Auto(AnalUnit, void) = .empty, -/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so. -/// All units in here form loops. To iterate loops, see `dependency_loops`. -dependency_loop_nodes: std.array_hash_map.Auto(AnalUnit, struct { - unit: AnalUnit, - reason: DependencyReason, -}) = .empty, +intern_pool: InternPool, /// Keep track of `@compileLog`s per `AnalUnit`. /// We track the source location of the first `@compileLog` call, and all logged lines as a linked list. @@ -220,17 +178,20 @@ compile_logs: std.array_hash_map.Auto(AnalUnit, struct { .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset), }; } -}) = .empty, -compile_log_lines: std.ArrayList(CompileLogLine) = .empty, -free_compile_log_lines: std.ArrayList(CompileLogLine.Index) = .empty, -/// This tracks files which triggered errors when generating AST/ZIR/ZOIR. -/// If not `null`, the value is a retryable error (the file status is guaranteed -/// to be `.retryable_failure`). Otherwise, the file status is `.astgen_failure` -/// or `.success`, and there are ZIR/ZOIR errors which should be printed. -/// We just store a `[]u8` instead of a full `*ErrorMsg`, because the source -/// location is always the entire file. The `[]u8` memory is owned by the map -/// and allocated into `gpa`. -failed_files: std.array_hash_map.Auto(File.Index, ?[]u8) = .empty, +}), +compile_log_lines: std.ArrayList(CompileLogLine), +free_compile_log_lines: std.ArrayList(CompileLogLine.Index), + +illegal_module_root: ?*Module, +/// If this is populated, a "file exists in multiple modules" error should be emitted. +/// This causes file errors to not be shown, because we don't really know which files +/// should be alive (because the user has messed up their imports somewhere!). +/// Cleared and recomputed every update, after AstGen and before Sema. +multi_module_err: ?struct { + file: File.Index, + modules: [2]*Module, + refs: [2]File.Reference, +}, /// AstGen is not aware of modules, and so cannot determine whether an import /// string makes sense. That is the job of a traversal after AstGen. /// @@ -257,11 +218,48 @@ failed_imports: std.ArrayList(struct { import_string: Zir.NullTerminatedString, import_token: Ast.TokenIndex, kind: enum { file_outside_module_root, illegal_zig_import }, -}) = .empty, -failed_exports: std.array_hash_map.Auto(Export.Index, *ErrorMsg) = .empty, -/// If analysis failed due to a cimport error, the corresponding Clang errors -/// are stored here. -cimport_errors: std.array_hash_map.Auto(AnalUnit, std.zig.ErrorBundle) = .empty, +}), +/// This tracks files which triggered errors when generating AST/ZIR/ZOIR. +/// If not `null`, the value is a retryable error (the file status is guaranteed +/// to be `.retryable_failure`). Otherwise, the file status is `.astgen_failure` +/// or `.success`, and there are ZIR/ZOIR errors which should be printed. +/// We just store a `[]u8` instead of a full `*ErrorMsg`, because the source +/// location is always the entire file. The `[]u8` memory is owned by the map +/// and allocated into `gpa`. +failed_files: std.array_hash_map.Auto(File.Index, ?[]u8), +/// If `true`, then semantic analysis must not occur on this update due to AstGen errors. +/// Essentially the entire pipeline after AstGen, including Sema, codegen, and link, is skipped. +/// Reset to `false` at the start of each update in `Compilation.update`. +skip_analysis_this_update: bool, +/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis +/// (index 0), and non-`null` for all others. +analysis_in_progress: std.array_hash_map.Auto(AnalUnit, ?*const DependencyReason), +/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. +failed_analysis: std.array_hash_map.Auto(AnalUnit, *ErrorMsg), +/// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed. +transitive_failed_analysis: std.array_hash_map.Auto( + AnalUnit, + if (build_options.enable_debug_extensions) TransitiveFailureReason else void, +), +/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one +/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible +/// between compilations. So, instead of (for instance) defining where the dependency loop "starts", +/// this map simply exists to allow easily iterating all dependency loops exactly once. +dependency_loops: std.array_hash_map.Auto(AnalUnit, void), +/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so. +/// All units in here form loops. To iterate loops, see `dependency_loops`. +dependency_loop_nodes: std.array_hash_map.Auto(AnalUnit, struct { + unit: AnalUnit, + reason: DependencyReason, +}), +/// This `Nav` succeeded analysis, but failed codegen. +/// This may be a simple "value" `Nav`, or it may be a function. +/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. +/// While multiple threads are active (most of the time!), this is guarded by `zcu.comp.mutex`, as +/// codegen and linking run on a separate thread. +failed_codegen: std.array_hash_map.Auto(InternPool.Nav.Index, *ErrorMsg), +failed_types: std.array_hash_map.Auto(InternPool.Index, *ErrorMsg), +failed_exports: std.array_hash_map.Auto(Export.Index, *ErrorMsg), /// Maximum amount of distinct error values, set by --error-limit error_limit: ErrorInt, @@ -269,14 +267,14 @@ error_limit: ErrorInt, /// In safe builds, `Type.assertHasLayout` may be called cross-thread, so this lock /// guards accesses to `outdated` and `potentially_outdated`. In unsafe builds, the /// lock is not needed and is compiled out. -outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void = if (std.debug.runtime_safety) .init, +outdated_lock: if (std.debug.runtime_safety) std.Io.RwLock else void, /// Value is the number of PO dependencies of this AnalUnit. /// This value will decrease as we perform semantic analysis to learn what is outdated. /// If any of these PO deps is outdated, this value will be moved to `outdated`. -potentially_outdated: std.array_hash_map.Auto(AnalUnit, u32) = .empty, +potentially_outdated: std.array_hash_map.Auto(AnalUnit, u32), /// Value is the number of PO dependencies of this AnalUnit. /// Once this value drops to 0, the AnalUnit is a candidate for re-analysis. -outdated: std.array_hash_map.Auto(AnalUnit, u32) = .empty, +outdated: std.array_hash_map.Auto(AnalUnit, u32), /// This is the set of all `AnalUnit`s in `outdated` whose PO dependency count is 0. /// Such `AnalUnit`s are ready for immediate re-analysis. /// See `findOutdatedToAnalyze` for details. @@ -286,69 +284,64 @@ outdated_ready: struct { funcs: std.array_hash_map.Auto(InternPool.Index, void), /// Does not contain `.func` units. other: std.array_hash_map.Auto(AnalUnit, void), -} = .{ .funcs = .empty, .other = .empty }, +}, /// This contains a list of AnalUnit whose analysis or codegen failed, but the /// failure was something like running out of disk space, and trying again may /// succeed. On the next update, we will flush this list, marking all members of /// it as outdated. -retryable_failures: std.ArrayList(AnalUnit) = .empty, +retryable_failures: std.ArrayList(AnalUnit), /// These are the modules which we initially queue for analysis in `Compilation.update`. /// `resolveReferences` will use these as the root of its reachability traversal. analysis_roots_buffer: [5]*Module, -analysis_roots_len: usize = 0, +analysis_roots_len: usize, /// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and /// reset to `null` when any semantic analysis occurs (since this invalidates the data). /// Allocated into `gpa`. -resolved_references: ?std.array_hash_map.Auto(AnalUnit, ?ResolvedReference) = null, +resolved_references: ?std.array_hash_map.Auto(AnalUnit, ?ResolvedReference), -/// If `true`, then semantic analysis must not occur on this update due to AstGen errors. -/// Essentially the entire pipeline after AstGen, including Sema, codegen, and link, is skipped. -/// Reset to `false` at the start of each update in `Compilation.update`. -skip_analysis_this_update: bool = false, +test_filters: []const []const u8, +test_functions: std.array_hash_map.Auto(InternPool.Nav.Index, void), -test_functions: std.array_hash_map.Auto(InternPool.Nav.Index, void) = .empty, - -global_assembly: std.array_hash_map.Auto(AnalUnit, []u8) = .empty, +global_assembly: std.array_hash_map.Auto(AnalUnit, []u8), /// Key is the `AnalUnit` *performing* the reference. This representation allows /// incremental updates to quickly delete references caused by a specific `AnalUnit`. /// Value is index into `all_references` of the first reference triggered by the unit. /// The `next` field on the `Reference` forms a linked list of all references /// triggered by the key `AnalUnit`. -reference_table: std.array_hash_map.Auto(AnalUnit, u32) = .empty, -all_references: std.ArrayList(Reference) = .empty, +reference_table: std.array_hash_map.Auto(AnalUnit, u32), +all_references: std.ArrayList(Reference), /// Freelist of indices in `all_references`. -free_references: std.ArrayList(u32) = .empty, +free_references: std.ArrayList(u32), -inline_reference_frames: std.ArrayList(InlineReferenceFrame) = .empty, -free_inline_reference_frames: std.ArrayList(InlineReferenceFrame.Index) = .empty, +inline_reference_frames: std.ArrayList(InlineReferenceFrame), +free_inline_reference_frames: std.ArrayList(InlineReferenceFrame.Index), /// Key is the `AnalUnit` *performing* the reference. This representation allows /// incremental updates to quickly delete references caused by a specific `AnalUnit`. /// Value is index into `all_type_reference` of the first reference triggered by the unit. /// The `next` field on the `TypeReference` forms a linked list of all type references /// triggered by the key `AnalUnit`. -type_reference_table: std.array_hash_map.Auto(AnalUnit, u32) = .empty, -all_type_references: std.ArrayList(TypeReference) = .empty, +type_reference_table: std.array_hash_map.Auto(AnalUnit, u32), +all_type_references: std.ArrayList(TypeReference), /// Freelist of indices in `all_type_references`. -free_type_references: std.ArrayList(u32) = .empty, +free_type_references: std.ArrayList(u32), /// Populated by analysis of `AnalUnit.wrap(.{ .memoized_state = s })`, where `s` depends on the element. -std_lang_decl_values: StdLangDecl.Memoized = .initFill(.none), +std_lang_decl_values: StdLangDecl.Memoized, -incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void = - if (build_options.enable_debug_extensions) .init else {}, +incremental_debug_state: if (build_options.enable_debug_extensions) IncrementalDebugState else void, /// Times semantic analysis of the current `AnalUnit`. When we pause to analyze a different unit, /// this timer must be temporarily paused and resumed later. -cur_analysis_timer: ?Compilation.Timer = null, +cur_analysis_timer: ?Compilation.Timer, codegen_task_pool: CodegenTaskPool, -generation: u32 = 0, +generation: u32, -/// Only access from the Sema thread. +/// Only accessed from the Sema thread. anon_name_counter: u32, pub const DependencyReason = struct { @@ -434,7 +427,7 @@ pub const EmbedTableAdapter = struct { _ = ctx; return @truncate(std.hash.Wyhash.hash(@backingInt(path.root), path.sub_path)); } - pub fn eql(ctx: EmbedTableAdapter, a_path: Compilation.Path, b_file: *EmbedFile, b_index: usize) bool { + pub fn eql(ctx: EmbedTableAdapter, a_path: Compilation.Path, b_file: EmbedFile, b_index: usize) bool { _ = ctx; _ = b_index; const b_path = b_file.path; @@ -1250,10 +1243,15 @@ pub const EmbedFile = struct { pub const Index = enum(u32) { _, - pub fn get(idx: Index, zcu: *const Zcu) *EmbedFile { - return zcu.embed_table.keys()[@backingInt(idx)]; + pub fn get(idx: Index, zcu: *const Zcu) *const EmbedFile { + return &zcu.embed_table.keys()[@backingInt(idx)]; } }; + + pub fn deinit(ef: *EmbedFile, gpa: Allocator) void { + ef.path.deinit(gpa); + ef.* = undefined; + } }; /// This struct holds data necessary to construct API-facing `AllErrors.Message`. @@ -2894,16 +2892,130 @@ pub const CompileError = error{ ComptimeBreak, }; -pub fn init(zcu: *Zcu, gpa: Allocator, io: Io, thread_count: usize) !void { - try zcu.intern_pool.init(gpa, io, thread_count); -} - /// It is valid to not call this function before `deinit` in error paths. /// Requires the fields on `zcu.comp` to already be initialized. -pub fn initAfterCompilation(zcu: *Zcu) void { +pub fn init(zcu: *Zcu) void { zcu.initTracyPlots(); } +pub fn reset(zcu: *Zcu, arena: Allocator) Allocator.Error!void { + const comp = zcu.comp; + const gpa = comp.gpa; + const io = comp.io; + + zcu.all_exports.clearRetainingCapacity(); + zcu.free_exports.clearRetainingCapacity(); + zcu.single_exports.clearRetainingCapacity(); + zcu.multi_exports.clearRetainingCapacity(); + + zcu.module_roots.clearRetainingCapacity(); + // builtin modules can still be reused from a previous non-incremental update + + for (zcu.import_table.keys()) |file| zcu.destroyFile(file); + zcu.import_table.clearRetainingCapacity(); + zcu.alive_files.clearRetainingCapacity(); + for (zcu.embed_table.keys()) |*ef| ef.deinit(gpa); + zcu.embed_table.clearRetainingCapacity(); + + zcu.compile_logs.clearRetainingCapacity(); + zcu.compile_log_lines.clearRetainingCapacity(); + zcu.free_compile_log_lines.clearRetainingCapacity(); + + zcu.illegal_module_root = null; + zcu.multi_module_err = null; + zcu.failed_imports.clearRetainingCapacity(); + for (zcu.failed_files.values()) |maybe_msg| if (maybe_msg) |msg| gpa.free(msg); + zcu.failed_files.clearRetainingCapacity(); + zcu.analysis_in_progress.clearRetainingCapacity(); + for (zcu.failed_analysis.values()) |msg| msg.destroy(gpa); + zcu.failed_analysis.clearRetainingCapacity(); + zcu.transitive_failed_analysis.clearRetainingCapacity(); + zcu.dependency_loops.clearRetainingCapacity(); + zcu.dependency_loop_nodes.clearRetainingCapacity(); + for (zcu.failed_codegen.values()) |msg| msg.destroy(gpa); + zcu.failed_codegen.clearRetainingCapacity(); + for (zcu.failed_types.values()) |msg| msg.destroy(gpa); + zcu.failed_types.clearRetainingCapacity(); + for (zcu.failed_exports.values()) |msg| msg.destroy(gpa); + zcu.failed_exports.clearRetainingCapacity(); + + assert(zcu.potentially_outdated.count() == 0); + assert(zcu.outdated.count() == 0); + assert(zcu.outdated_ready.funcs.count() == 0); + assert(zcu.outdated_ready.other.count() == 0); + zcu.retryable_failures.clearRetainingCapacity(); + + zcu.analysis_roots_len = 0; + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod; + zcu.analysis_roots_len += 1; + // Normally we rely on importing std to in turn import the root source file in the start code. + // However, the main module is distinct from the root module in tests, so that won't happen there. + if (comp.config.is_test and zcu.main_mod != zcu.std_mod) { + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod; + zcu.analysis_roots_len += 1; + } + if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod; + zcu.analysis_roots_len += 1; + } + if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| { + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod; + zcu.analysis_roots_len += 1; + } + if (zcu.root_mod.deps.get("zigc")) |zigc_mod| { + zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zigc_mod; + zcu.analysis_roots_len += 1; + } + zcu.clearCachedResolvedReferences(); + + zcu.test_functions.clearRetainingCapacity(); + + zcu.global_assembly.clearRetainingCapacity(); + + zcu.reference_table.clearRetainingCapacity(); + zcu.all_references.clearRetainingCapacity(); + zcu.free_references.clearRetainingCapacity(); + + zcu.inline_reference_frames.clearRetainingCapacity(); + zcu.free_inline_reference_frames.clearRetainingCapacity(); + + zcu.type_reference_table.clearRetainingCapacity(); + zcu.all_type_references.clearRetainingCapacity(); + zcu.free_type_references.clearRetainingCapacity(); + + zcu.std_lang_decl_values = .initFill(.none); + + for (zcu.codegen_task_pool.task_funcs) |task_func| assert(task_func == .none); + + assert(zcu.generation == 0); + + zcu.anon_name_counter = 0; + + zcu.intern_pool.deinit(gpa, io); + zcu.intern_pool = .empty; + try zcu.intern_pool.init(gpa, io, comp.thread_limit); + + { + const active = zcu.acquire(); + defer active.release(); + try active.pt.populateModuleRootTable(); + } + + assert(zcu.llvm_object == null); + if (comp.config.use_llvm and + (comp.emit_bin != null or + comp.emit_asm != null or + comp.emit_llvm_ir != null or + comp.emit_llvm_bc != null or + log_llvm_ir: { + break :log_llvm_ir debugLogScopeEnabled("llvm-ir") or debugLogScopeEnabled("opt-llvm-ir"); + })) + { + dev.check(.llvm_backend); + zcu.llvm_object = try .create(if (comp.config.incremental) comp.arena else arena, zcu); + } +} + pub fn deinit(zcu: *Zcu) void { const comp = zcu.comp; const io = comp.io; @@ -2911,70 +3023,54 @@ pub fn deinit(zcu: *Zcu) void { { if (zcu.llvm_object) |llvm_object| llvm_object.deinit(); - zcu.builtin_modules.deinit(gpa); + zcu.global_zir_cache.handle.close(io); + zcu.local_zir_cache.handle.close(io); + + zcu.all_exports.deinit(gpa); + zcu.free_exports.deinit(gpa); + zcu.single_exports.deinit(gpa); + zcu.multi_exports.deinit(gpa); + zcu.module_roots.deinit(gpa); - for (zcu.import_table.keys()) |file_index| { - zcu.destroyFile(file_index); - } + zcu.builtin_modules.deinit(gpa); + + for (zcu.import_table.keys()) |file| zcu.destroyFile(file); zcu.import_table.deinit(gpa); zcu.alive_files.deinit(gpa); - - for (zcu.embed_table.keys()) |embed_file| { - embed_file.path.deinit(gpa); - gpa.destroy(embed_file); - } + for (zcu.embed_table.keys()) |*ef| ef.deinit(gpa); zcu.embed_table.deinit(gpa); - zcu.local_zir_cache.handle.close(io); - zcu.global_zir_cache.handle.close(io); + zcu.compile_logs.deinit(gpa); + zcu.compile_log_lines.deinit(gpa); + zcu.free_compile_log_lines.deinit(gpa); - for (zcu.failed_analysis.values()) |value| value.destroy(gpa); - for (zcu.failed_codegen.values()) |value| value.destroy(gpa); - for (zcu.failed_types.values()) |value| value.destroy(gpa); + zcu.failed_imports.deinit(gpa); + for (zcu.failed_files.values()) |maybe_msg| if (maybe_msg) |msg| gpa.free(msg); + zcu.failed_files.deinit(gpa); zcu.analysis_in_progress.deinit(gpa); + for (zcu.failed_analysis.values()) |msg| msg.destroy(gpa); zcu.failed_analysis.deinit(gpa); zcu.transitive_failed_analysis.deinit(gpa); zcu.dependency_loops.deinit(gpa); zcu.dependency_loop_nodes.deinit(gpa); + for (zcu.failed_codegen.values()) |msg| msg.destroy(gpa); zcu.failed_codegen.deinit(gpa); + for (zcu.failed_types.values()) |msg| msg.destroy(gpa); zcu.failed_types.deinit(gpa); - - for (zcu.failed_files.values()) |value| { - if (value) |msg| gpa.free(msg); - } - zcu.failed_files.deinit(gpa); - zcu.failed_imports.deinit(gpa); - - for (zcu.failed_exports.values()) |value| { - value.destroy(gpa); - } + for (zcu.failed_exports.values()) |msg| msg.destroy(gpa); zcu.failed_exports.deinit(gpa); - for (zcu.cimport_errors.values()) |*errs| { - errs.deinit(gpa); - } - zcu.cimport_errors.deinit(gpa); - - zcu.compile_logs.deinit(gpa); - zcu.compile_log_lines.deinit(gpa); - zcu.free_compile_log_lines.deinit(gpa); - - zcu.all_exports.deinit(gpa); - zcu.free_exports.deinit(gpa); - zcu.single_exports.deinit(gpa); - zcu.multi_exports.deinit(gpa); - zcu.potentially_outdated.deinit(gpa); zcu.outdated.deinit(gpa); zcu.outdated_ready.funcs.deinit(gpa); zcu.outdated_ready.other.deinit(gpa); zcu.retryable_failures.deinit(gpa); + if (zcu.resolved_references) |*r| r.deinit(gpa); + zcu.test_functions.deinit(gpa); - for (zcu.global_assembly.values()) |s| { - gpa.free(s); - } + for (zcu.global_assembly.values()) |s| gpa.free(s); zcu.global_assembly.deinit(gpa); zcu.reference_table.deinit(gpa); @@ -2988,8 +3084,6 @@ pub fn deinit(zcu: *Zcu) void { zcu.all_type_references.deinit(gpa); zcu.free_type_references.deinit(gpa); - if (zcu.resolved_references) |*r| r.deinit(gpa); - if (comp.debugIncremental()) { zcu.incremental_debug_state.deinit(gpa); } @@ -4352,8 +4446,8 @@ fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.array_hash_map.Auto(Ana .unnamed_test => true, .@"test", .decltest => a: { const fqn_slice = nav.fqn.toSlice(ip); - if (comp.test_filters.len > 0) { - for (comp.test_filters) |test_filter| { + if (zcu.test_filters.len > 0) { + for (zcu.test_filters) |test_filter| { if (std.mem.find(u8, fqn_slice, test_filter) != null) break; } else break :a false; } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index ec764ddfdefc972b4ff9bfbc3d8a920cdabe9762..8c789491b5b3121c3ddebd99451ff0f169d5b157 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; @@ -147,6 +148,12 @@ pub fn update( comp.link_queue.finishZcuQueue(comp); } + if (zcu.illegal_module_root) |_| { + // We cannot run AstGen because we failed to set up the module roots. We can return early + // because this is a guaranteed compile error. + return; + } + { const tracy_trace = traceNamed(@src(), "astgen"); defer tracy_trace.end(); @@ -202,11 +209,9 @@ pub fn update( // On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here // because `workerUpdateEmbedFile` can't invalidate it. The different here is that one // `@embedFile` can't trigger analysis of a new `@embedFile`! - for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| { + for (0.., zcu.embed_table.keys()) |ef_index_usize, *ef| { const ef_index: Zcu.EmbedFile.Index = @fromBackingInt(@intCast(ef_index_usize)); - astgen_group.async(io, workerUpdateEmbedFile, .{ - comp, ef_index, ef, - }); + astgen_group.async(io, workerUpdateEmbedFile, .{ comp, ef_index, ef }); } try astgen_group.await(io); @@ -218,8 +223,8 @@ pub fn update( // they belong to. const any_fatal_files = try pt.computeAliveFiles(); - // If the cache mode is `whole`, add every alive source file to the manifest. switch (comp.cache_use) { + .none => {}, .whole => |whole| if (whole.cache_manifest) |man| { for (zcu.alive_files.keys()) |file_index| { const file = zcu.fileByIndex(file_index); @@ -249,7 +254,11 @@ pub fn update( }; } }, - .none, .incremental => {}, + .incremental => { + // Do not include file contents in the cache manifest because we + // want the hash to be the same regardless of the contents of the + // source files so that incremental can do its thing. + }, } if (comp.time_report) |*tr| { @@ -2303,10 +2312,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); @@ -2494,11 +2502,7 @@ pub fn doImport( } /// This is called once during `Compilation.create` and never again. "builtin" modules don't yet /// exist, so are not added to `module_roots` here. They must be added when they are created. -pub fn populateModuleRootTable(pt: Zcu.PerThread) error{ - OutOfMemory, - /// One of the specified modules had its root source file at an illegal path. - IllegalZigImport, -}!void { +pub fn populateModuleRootTable(pt: Zcu.PerThread) Allocator.Error!void { const zcu = pt.zcu; const comp = zcu.comp; const gpa = comp.gpa; @@ -2507,7 +2511,7 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{ // We'll initially add [mod, undefined] pairs, and when we reach the pair while // iterating, rewrite the undefined value. const roots = &zcu.module_roots; - roots.clearRetainingCapacity(); + assert(roots.count() == 0); // Start with: // * `std_mod`, which is the main root of analysis @@ -2542,7 +2546,9 @@ pub fn populateModuleRootTable(pt: Zcu.PerThread) error{ errdefer path.deinit(gpa); if (try path.isIllegalZigImport(gpa, zcu.comp.dirs)) { - return error.IllegalZigImport; + // One of the specified modules had its root source file at an illegal path. + zcu.illegal_module_root = mod; + return; } const gop = try zcu.import_table.getOrPutAdapted(gpa, path, Zcu.ImportTableAdapter{ .zcu = zcu }); @@ -2781,15 +2787,21 @@ fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!struct const gpa = comp.gpa; const io = comp.io; - const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash()); - if (gop.found_existing) return .{ // the `File` is up-to-date - .file = zcu.module_roots.get(gop.value_ptr.*).?.unwrap().?, - .module_root = gop.value_ptr.*, + const mod: *Module = mod: { + const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash()); + if (!gop.found_existing) { + errdefer _ = zcu.builtin_modules.pop(); + gop.value_ptr.* = try .createBuiltin(comp.arena, opts, comp.dirs); + } + const mod = gop.value_ptr.*; + assert(std.mem.eql(u8, &mod.getBuiltinOptions(comp.config).hash(), gop.key_ptr)); // builtin is its own builtin + break :mod mod; }; - errdefer _ = zcu.builtin_modules.pop(); - const mod: *Module = try .createBuiltin(comp.arena, opts, comp.dirs); - assert(std.mem.eql(u8, &mod.getBuiltinOptions(comp.config).hash(), gop.key_ptr)); // builtin is its own builtin + if (zcu.module_roots.get(mod)) |file| { + // We have already loaded this builtin.zig before! + return .{ .file = file.unwrap().?, .module_root = mod }; + } const path = try mod.root.join(gpa, comp.dirs, "builtin.zig"); errdefer path.deinit(gpa); @@ -2827,7 +2839,6 @@ fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!struct .root_type = .none, }); - gop.value_ptr.* = mod; file_gop.key_ptr.* = file_index; zcu.module_roots.putAssumeCapacityNoClobber(mod, file_index.toOptional()); try opts.populateFile(gpa, file); @@ -2883,22 +2894,19 @@ pub fn embedFile( return @fromBackingInt(@intCast(gop.index)); } errdefer _ = zcu.embed_table.pop(); - gop.key_ptr.* = try pt.newEmbedFile(path); + try pt.initEmbedFile(gop.key_ptr, path); return @fromBackingInt(@intCast(gop.index)); } - const embed_file: *Zcu.EmbedFile, const embed_file_idx: Zcu.EmbedFile.Index = ef: { + const embed_file, const embed_file_idx: Zcu.EmbedFile.Index = ef: { const path = try cur_file.path.upJoin(gpa, zcu.comp.dirs, import_string); errdefer path.deinit(gpa); const gop = try zcu.embed_table.getOrPutAdapted(gpa, path, Zcu.EmbedTableAdapter{}); - if (gop.found_existing) { - path.deinit(gpa); // we're not using this key - break :ef .{ gop.key_ptr.*, @fromBackingInt(@intCast(gop.index)) }; - } else { + if (!gop.found_existing) { errdefer _ = zcu.embed_table.pop(); - gop.key_ptr.* = try pt.newEmbedFile(path); - break :ef .{ gop.key_ptr.*, @fromBackingInt(@intCast(gop.index)) }; - } + try pt.initEmbedFile(gop.key_ptr, path); + } else path.deinit(gpa); // we're not using this key + break :ef .{ gop.key_ptr, @fromBackingInt(@intCast(gop.index)) }; }; switch (embed_file.path.isNested(cur_file.mod.?.root)) { @@ -2913,7 +2921,7 @@ pub fn updateEmbedFile( pt: Zcu.PerThread, ef: *Zcu.EmbedFile, /// If not `null`, the interned file data is stored here, if it was loaded. - /// `newEmbedFile` uses this to add the file to the `whole` cache manifest. + /// `initEmbedFile` uses this to add the file to the `whole` cache manifest. ip_str_out: ?*?InternPool.String, ) Allocator.Error!void { pt.updateEmbedFileInner(ef, ip_str_out) catch |err| switch (err) { @@ -3000,17 +3008,13 @@ fn updateEmbedFileInner( } /// Assumes that `path` is allocated into `gpa`. Takes ownership of `path` on success. -fn newEmbedFile(pt: Zcu.PerThread, path: Compilation.Path) EmbedFileError!*Zcu.EmbedFile { +fn initEmbedFile(pt: Zcu.PerThread, ef: *Zcu.EmbedFile, path: Compilation.Path) EmbedFileError!void { const zcu = pt.zcu; const comp = zcu.comp; const io = comp.io; - const gpa = comp.gpa; const ip = &zcu.intern_pool; - const new_file = try gpa.create(Zcu.EmbedFile); - errdefer gpa.destroy(new_file); - - new_file.* = .{ + ef.* = .{ .path = path, .val = .none, .err = null, @@ -3018,7 +3022,7 @@ fn newEmbedFile(pt: Zcu.PerThread, path: Compilation.Path) EmbedFileError!*Zcu.E }; var opt_ip_str: ?InternPool.String = null; - try pt.updateEmbedFile(new_file, &opt_ip_str); + try pt.updateEmbedFile(ef, &opt_ip_str); try comp.appendFileSystemInput(path); @@ -3031,19 +3035,17 @@ fn newEmbedFile(pt: Zcu.PerThread, path: Compilation.Path) EmbedFileError!*Zcu.E const man = whole.cache_manifest orelse break :cache; const ip_str = opt_ip_str orelse break :cache; // this will be a compile error - const array_len = Value.fromInterned(new_file.val).typeOf(zcu).childType(zcu).arrayLen(zcu); + const array_len = Value.fromInterned(ef.val).typeOf(zcu).childType(zcu).arrayLen(zcu); const contents = ip_str.toSlice(array_len, ip); try whole.cache_manifest_mutex.lock(io); defer whole.cache_manifest_mutex.unlock(io); - path.addToCacheManifestAsDiscovered(man, &comp.dirs, contents, new_file.stat) catch |err| switch (err) { + path.addToCacheManifestAsDiscovered(man, &comp.dirs, contents, ef.stat) catch |err| switch (err) { error.FileSystemFailure => unreachable, // contents and stat are both provided else => |e| return e, }; } - - return new_file; } pub fn scanNamespace( @@ -3242,9 +3244,9 @@ const ScanDeclIter = struct { // Perhaps we should add all test indiscriminately and filter at the end of the update. if (!comp.config.is_test) break :a false; if (file.mod != zcu.main_mod) break :a false; - if (is_named and comp.test_filters.len > 0) { + if (is_named and zcu.test_filters.len > 0) { const fqn_slice = fqn.toSlice(ip); - for (comp.test_filters) |test_filter| { + for (zcu.test_filters) |test_filter| { if (std.mem.find(u8, fqn_slice, test_filter) != null) break; } else break :a false; } @@ -4522,32 +4524,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 +4575,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..49838feb8beb6de697b763fc6852054050033a6e 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| { @@ -3395,7 +3368,7 @@ fn buildOutputType( const target = &main_mod.resolved_target.result; if (target.cpu.arch == .arc or target.cpu.arch.isNvptx()) { - if (emit_bin != .no and create_module.resolved_options.use_llvm) { + if (emit_bin != .no and target.ofmt != .llvmbc and target.ofmt != .llvmir and create_module.resolved_options.use_llvm) { fatal("cannot emit {s} binary with the LLVM backend; only '-femit-asm' is supported", .{ @tagName(target.cpu.arch), }); @@ -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( @@ -4446,13 +4413,12 @@ fn serve( switch (hdr.tag) { .exit => return cleanExit(io), .update => { - file_system_inputs.clearRetainingCapacity(); - if (arg_mode == .translate_c) { var arena_instance = std.heap.ArenaAllocator.init(gpa); defer arena_instance.deinit(); const arena = arena_instance.allocator(); var output: Compilation.TranslateCResult = undefined; + file_system_inputs.clearRetainingCapacity(); try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node, environ_map); defer output.deinit(gpa); @@ -4470,7 +4436,6 @@ fn serve( continue; } - try comp.makeBinFileWritable(); try comp.update(main_progress_node); try comp.makeBinFileExecutable(); @@ -4496,7 +4461,6 @@ fn serve( //); }, .hot_update => { - file_system_inputs.clearRetainingCapacity(); if (child_pid) |pid| { try comp.hotCodeSwap(main_progress_node, pid); try serveUpdateResults(&server, comp); @@ -5627,17 +5591,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 +5625,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 +5695,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 +6218,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; diff --git a/test/Runner.zig b/test/Runner.zig new file mode 100644 index 0000000000000000000000000000000000000000..1d838172a23bc9e182529cb6d37305def72f4c90 --- /dev/null +++ b/test/Runner.zig @@ -0,0 +1,2605 @@ +const assert = std.debug.assert; +const builtin = @import("builtin"); +const std = @import("std"); +const Runner = @This(); + +/// These are passed as `----` strings. +/// +/// If only one specific test is failing on a target, instead of entirely disabling the target here, you +/// can skip the target for that specific test only by adding a line like this to top of the manifest: +/// +/// #skip x86_64-linux-none-baseline-whole-selfhosted-new-pic +pub const Target = struct { + cpu: Cpu = .{ .native = .{} }, + os: Os = .native, + abi: Abi = .default, + ofmt: ObjectFormat = .default, + dynamic_linker: DynamicLinker = .default, + + cache_mode: enum { whole, incremental }, + backend: enum { selfhosted, llvm, cbe }, + linker: enum { lld, old, new }, + pic: enum { nopic, pic, pie }, + + pub const Cpu = Cpu: { + @setEvalBranchQuota(3_000); + const Arch = std.Target.Cpu.Arch; + const arch_info = @typeInfo(Arch).@"enum"; + const field_names = .{"native"} ++ arch_info.field_names; + var field_types: [field_names.len]type = undefined; + field_types[0] = struct { + model: Model = .native, + + pub const Model = enum { native, baseline }; + }; + for (field_types[1..], field_names[1..]) |*field_type, field_name| { + const arch = @field(Arch, field_name); + const models = arch.allCpuModels(); + const family = @field(std.Target, @tagName(arch.family())); + field_type.* = struct { + model: Model = .baseline, + features: struct { + add: []const Feature = &.{}, + remove: []const Feature = &.{}, + comptime { + _ = &family; // hack around compiler bug + } + } = .{}, + + pub const Model = Model: { + const explicit_models = @typeInfo(family.cpu).@"struct".decl_names; + var model_names: []const []const u8 = &.{"native"}; + if (!@hasDecl(family.cpu, "baseline")) model_names = model_names ++ .{"baseline"}; + model_names = model_names ++ explicit_models; + const TagInt = std.math.IntFittingRange(0, model_names.len - 1); + var model_values: [model_names.len]TagInt = undefined; + var model_index = 0; + { + var model_value = explicit_models.len; + model_values[model_index] = model_value; + model_index += 1; + model_value += 1; + if (!@hasDecl(family.cpu, "baseline")) { + model_values[model_index] = model_value; + model_index += 1; + model_value += 1; + } + } + for (model_values[model_index..], 0..) |*model_value, value| model_value.* = value; + break :Model @Enum(TagInt, .exhaustive, model_names, &model_values); + }; + + pub const Feature = family.Feature; + + pub fn updateQuery(cpu: *const @This(), query: *std.Target.Query) void { + query.cpu_model = switch (cpu.model) { + .native => .native, + .baseline => .baseline, + else => |model| .{ .explicit = models[@backingInt(model)] }, + }; + for (cpu.features.add) |add_feature| + query.cpu_features_add.addFeature(@backingInt(add_feature)); + for (cpu.features.remove) |remove_feature| + query.cpu_features_sub.addFeature(@backingInt(remove_feature)); + } + }; + } + const TagInt = std.math.IntFittingRange(0, field_names.len - 1); + var field_values: [field_names.len]TagInt = undefined; + field_values[0] = field_names.len - 1; + for (field_values[1..], arch_info.field_values) |*field_value, value| field_value.* = value; + const Tag = @Enum(TagInt, .exhaustive, field_names, &field_values); + break :Cpu @Union(.auto, Tag, field_names, &field_types, &@splat(.{})); + }; + + pub const Os = Os: { + const os_info = @typeInfo(std.Target.Os.Tag).@"enum"; + const field_names = .{"native"} ++ os_info.field_names; + var field_types: [field_names.len]type = undefined; + field_types[0] = void; + for (field_types[1..], field_names[1..]) |*field_type, field_name| { + const version: enum { + none, + semver, + windows, + } = switch (@field(std.Target.Os.Tag, field_name).versionRangeTag()) { + .none => .none, + .semver, .hurd, .linux => .semver, + .windows => .windows, + }; + field_type.* = struct { + version: ?Version = null, + + pub const Version = switch (version) { + .none => struct {}, + .semver => struct { + min: std.SemanticVersion, + max: ?std.SemanticVersion = null, + }, + .windows => struct { + min: std.Target.Os.WindowsVersion, + max: ?std.Target.Os.WindowsVersion = null, + }, + }; + + pub fn updateQuery(os: *const @This(), query: *std.Target.Query) void { + if (os.version) |v| switch (version) { + .none => {}, + .semver => { + query.os_version_min = .{ .semver = v.min }; + if (v.max) |max| query.os_version_max = .{ .semver = max }; + }, + .windows => { + query.os_version_min = .{ .windows = v.min }; + if (v.max) |max| query.os_version_max = .{ .windows = max }; + }, + }; + } + }; + } + const TagInt = std.math.IntFittingRange(0, field_names.len - 1); + var field_values: [field_names.len]TagInt = undefined; + field_values[0] = field_names.len - 1; + for (field_values[1..], os_info.field_values) |*field_value, value| field_value.* = value; + const Tag = @Enum(TagInt, .exhaustive, field_names, &field_values); + break :Os @Union(.auto, Tag, field_names, &field_types, &@splat(.{})); + }; + + pub const Abi = Abi: { + const abi_info = @typeInfo(std.Target.Abi).@"enum"; + const field_names = .{"default"} ++ abi_info.field_names; + var field_types: [field_names.len]type = undefined; + field_types[0] = void; + for (field_types[1..], field_names[1..]) |*field_type, field_name| { + const abi_tag = @field(std.Target.Abi, field_name); + const version: enum { none, libc, android } = if (abi_tag.isAndroid()) + .android + else if (abi_tag.isGnu() or abi_tag.isMusl()) + .libc + else + .none; + field_type.* = struct { + version: ?Version = null, + + pub const Version = switch (version) { + .none => struct {}, + .libc => std.SemanticVersion, + .android => u32, + }; + + pub fn updateQuery(abi: *const @This(), query: *std.Target.Query) void { + if (abi.version) |v| switch (version) { + .none => {}, + .libc => query.glibc_version = v, + .android => query.android_api_level = v, + }; + } + }; + } + const TagInt = std.math.IntFittingRange(0, field_names.len - 1); + var field_values: [field_names.len]TagInt = undefined; + field_values[0] = field_names.len - 1; + for (field_values[1..], abi_info.field_values) |*field_value, value| field_value.* = value; + const Tag = @Enum(TagInt, .exhaustive, field_names, &field_values); + break :Abi @Union(.auto, Tag, field_names, &field_types, &@splat(.{})); + }; + + pub const ObjectFormat = ObjectFormat: { + const ofmt_info = @typeInfo(std.Target.ObjectFormat).@"enum"; + const field_names = .{"default"} ++ ofmt_info.field_names; + const TagInt = std.math.IntFittingRange(0, field_names.len - 1); + var field_values: [field_names.len]TagInt = undefined; + field_values[0] = field_names.len - 1; + for (field_values[1..], ofmt_info.field_values) |*field_value, value| field_value.* = value; + break :ObjectFormat @Enum(TagInt, .exhaustive, field_names, &field_values); + }; + + pub const DynamicLinker = union(enum) { + default, + none, + explicit: []const u8, + }; + + pub fn toQuery(target: *const Target) std.Target.Query { + var query: std.Target.Query = .{}; + query.cpu_arch = switch (target.cpu) { + .native => null, + else => |_, arch| @fromBackingInt(@backingInt(arch)), + }; + switch (target.cpu) { + .native => |native| query.cpu_model = switch (native.model) { + .native => .native, + .baseline => .baseline, + }, + inline else => |cpu| cpu.updateQuery(&query), + } + query.os_tag = switch (target.os) { + .native => null, + else => |_, os| @fromBackingInt(@backingInt(os)), + }; + switch (target.os) { + .native => {}, + inline else => |os| os.updateQuery(&query), + } + query.abi = switch (target.abi) { + .default => null, + else => |_, abi| @fromBackingInt(@backingInt(abi)), + }; + switch (target.abi) { + .default => {}, + inline else => |abi| abi.updateQuery(&query), + } + query.ofmt = switch (target.ofmt) { + .default => null, + else => |ofmt| @fromBackingInt(@backingInt(ofmt)), + }; + query.dynamic_linker = switch (target.dynamic_linker) { + .default => null, + .none => .none, + .explicit => |dynamic_linker| .init(dynamic_linker), + }; + return query; + } + + pub fn format(target: *const Target, w: *std.Io.Writer) std.Io.Writer.Error!void { + try w.print("{f}-{f}-{t}-{t}-{t}-{t}", .{ + std.fmt.alt(&target.toQuery(), .printZigTriple), + std.fmt.alt(&target.toQuery(), .printCpu), + target.cache_mode, + target.backend, + target.linker, + target.pic, + }); + } +}; + +gpa: std.mem.Allocator, +arena: *std.heap.ArenaAllocator, +environ_map: std.process.Environ.Map, +io: std.Io, +prog_node: std.Progress.Node, +args: Args, +host: std.Target, +server: std.zig.Server, +eb_wip: std.zig.ErrorBundle.Wip, + +const Args = struct { + @"test": ?struct { + input_dir: std.zig.Server.Message.InputDir, + dir: std.Io.Dir, + }, + manifest_file: ?std.Io.File, + zig_exe: ?std.Io.File, + lib_dir: ?std.Io.Dir, + src_dir: ?std.Io.Dir, + targets: std.ArrayList(u32), + target_bytes: std.ArrayList(u8), + + libc_runtimes_dir: ?std.Io.Dir, + enable_darling: bool, + enable_qemu: bool, + enable_rosetta: bool, + enable_wasmtime: bool, + enable_wine: bool, + + gdb_exe: ?std.Io.File, + lldb_exe: ?std.Io.File, + + quiet: bool, + + fn deinit(args: *Args, gpa: std.mem.Allocator) void { + args.targets.deinit(gpa); + args.target_bytes.deinit(gpa); + args.* = undefined; + } + + fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { + try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); + try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); + args.target_bytes.appendSliceAssumeCapacity(target); + args.target_bytes.appendAssumeCapacity(0); + } +}; + +fn deinit(runner: *Runner) void { + runner.environ_map.deinit(); + runner.prog_node.end(); + runner.args.deinit(runner.gpa); + runner.eb_wip.deinit(); + runner.* = undefined; +} + +const FailError = error{AlreadyReported} || std.mem.Allocator.Error || std.Io.Cancelable; +fn fail(runner: *Runner, comptime fmt: []const u8, args: anytype) FailError { + return runner.failString(try runner.eb_wip.printString(fmt, args)); +} +fn failString(runner: *Runner, msg: std.zig.ErrorBundle.String) FailError { + try runner.eb_wip.addRootErrorMessage(.{ .msg = msg }); + return error.AlreadyReported; +} + +pub const ProtocolError = error{ + ServerReadFailed, + ServerWriteFailed, + ServerEndOfStream, +} || FailError; +pub fn runServer(runner: *Runner) ProtocolError { + runner.server.serveStringMessage(.zig_version, builtin.zig_version_string) catch |err| switch (err) { + error.WriteFailed => return error.ServerWriteFailed, + }; + while (true) { + _ = runner.arena.reset(.retain_capacity); + const arena = runner.arena.allocator(); + const hdr = runner.server.receiveMessage() catch |err| switch (err) { + error.ReadFailed => return error.ServerReadFailed, + error.EndOfStream => return error.ServerEndOfStream, + }; + switch (hdr.tag) { + else => |tag| return runner.fail("unsupported message: {t}", .{tag}), + .exit => std.process.exit(0), + .args => { + const args_body = try arena.alloc(u8, hdr.bytes_len); + runner.server.in.readSliceAll(args_body) catch unreachable; + const State = enum { + positional, + @"--zig", + @"--lib", + @"--src", + @"--target", + @"--libc-runtimes", + @"--gdb", + @"--lldb", + }; + var state: State = .positional; + const state_expected: std.enums.EnumArray( + State, + enum { path, dir, file, string }, + ) = .init(.{ + .positional = .path, + .@"--zig" = .file, + .@"--lib" = .dir, + .@"--src" = .dir, + .@"--target" = .string, + .@"--libc-runtimes" = .dir, + .@"--gdb" = .file, + .@"--lldb" = .file, + }); + var input_dir: std.zig.Server.Message.InputDir = .cwd; + var args_body_offset: usize = 0; + while (args_body.len - args_body_offset > 0) { + const arg: std.zig.Client.Message.Arg = + @fromBackingInt(args_body[args_body_offset]); + args_body_offset += 1; + arg: switch (arg) { + .string => { + const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; + const string = args_body[args_body_offset..end]; + args_body_offset = end + 1; + switch (state) { + .positional => if (std.mem.eql(u8, string, "--target")) { + state = .@"--target"; + continue; + } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { + try runner.args.addTarget(runner.gpa, target); + } else if (std.mem.eql(u8, string, "--libc-runtimes")) { + state = .@"--libc-runtimes"; + continue; + } else if (std.mem.eql(u8, string, "-fdarling")) { + runner.args.enable_darling = true; + } else if (std.mem.eql(u8, string, "-fqemu")) { + runner.args.enable_qemu = true; + } else if (std.mem.eql(u8, string, "-frosetta")) { + runner.args.enable_rosetta = true; + } else if (std.mem.eql(u8, string, "-fwasmtime")) { + runner.args.enable_wasmtime = true; + } else if (std.mem.eql(u8, string, "-fwine")) { + runner.args.enable_wine = true; + } else if (std.mem.eql(u8, string, "--gdb")) { + state = .@"--gdb"; + continue; + } else if (std.mem.eql(u8, string, "--lldb")) { + state = .@"--lldb"; + continue; + } else if (std.mem.eql(u8, string, "--quiet")) { + runner.args.quiet = true; + }, + .@"--target" => try runner.args.addTarget(runner.gpa, string), + else => return runner.fail( + "\"{t}\" expected {t}, got {t}", + .{ state, state_expected.get(state), arg }, + ), + } + }, + .prefix => { + const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; + const string = args_body[args_body_offset..end]; + args_body_offset = end + 1; + assert(state == .positional); + state = if (std.mem.eql(u8, string, "--zig=")) + .@"--zig" + else if (std.mem.eql(u8, string, "--lib=")) + .@"--lib" + else if (std.mem.eql(u8, string, "--src=")) + .@"--src" + else if (std.mem.eql(u8, string, "--libc-runtimes=")) + .@"--libc-runtimes" + else if (std.mem.eql(u8, string, "--gdb=")) + .@"--gdb" + else if (std.mem.eql(u8, string, "--lldb=")) + .@"--lldb" + else + return runner.fail("unsupported arg prefix: {q}", .{string}); + continue; + }, + .suffix => { + const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; + const string = args_body[args_body_offset..end]; + args_body_offset = end + 1; + return runner.fail("unsupported arg suffix: {q}", .{string}); + }, + .input_dir => { + input_dir = @fromBackingInt(@backingInt(input_dir) + 1); + continue :arg .output_dir; + }, + .output_dir => { + const dir_handle: *align(1) const std.Io.Dir.Handle = @ptrCast( + args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)], + ); + args_body_offset += @sizeOf(std.Io.Dir.Handle); + const dir = runner.io.vtable.inheritParentDir( + runner.io.userdata, + dir_handle.*, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return runner.fail( + "unable to inherit parent dir: {t}", + .{e}, + ), + }; + switch (state) { + .positional => { + if (runner.args.@"test" != null or + runner.args.manifest_file != null) return runner.fail( + "{t} specified multiple times", + .{state}, + ); + runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir }; + const manifest_path = "manifest"; + runner.args.manifest_file = dir.openFile( + runner.io, + manifest_path, + .{}, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return runner.fail( + "unable to open manifest file: {t}", + .{e}, + ), + }; + try runner.discoverInput(input_dir, manifest_path); + }, + .@"--lib" => { + if (runner.args.lib_dir != null) return runner.fail( + "\"{t}\" specified multiple times", + .{state}, + ); + runner.args.lib_dir = dir; + }, + .@"--src" => { + if (runner.args.src_dir != null) return runner.fail( + "\"{t}\" specified multiple times", + .{state}, + ); + runner.args.src_dir = dir; + }, + .@"--libc-runtimes" => { + if (runner.args.libc_runtimes_dir != null) return runner.fail( + "\"{t}\" specified multiple times", + .{state}, + ); + runner.args.libc_runtimes_dir = dir; + }, + else => return runner.fail( + "\"{t}\" expected {t}, got {t}", + .{ state, state_expected.get(state), arg }, + ), + } + }, + .input_file, .input_file_content, .output_file => { + const file_handle: *align(1) const std.Io.File.Handle = @ptrCast( + args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)], + ); + args_body_offset += @sizeOf(std.Io.File.Handle); + const file = runner.io.vtable.inheritParentFile( + runner.io.userdata, + file_handle.*, + .{ .nonblocking = false }, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return runner.fail( + "unable to inherit parent file: {t}", + .{e}, + ), + }; + switch (state) { + .positional => { + if (runner.args.@"test" != null or + runner.args.manifest_file != null) return runner.fail( + "{t} specified multiple times", + .{state}, + ); + runner.args.manifest_file = file; + }, + .@"--zig" => { + if (runner.args.zig_exe != null) return runner.fail( + "\"{t}\" specified multiple times", + .{state}, + ); + runner.args.zig_exe = file; + }, + .@"--gdb" => { + if (runner.args.gdb_exe != null) return runner.fail( + "\"{t}\" specified multiple times", + .{state}, + ); + runner.args.gdb_exe = file; + }, + .@"--lldb" => { + if (runner.args.lldb_exe != null) return runner.fail( + "\"{t}\" specified multiple times", + .{state}, + ); + runner.args.lldb_exe = file; + }, + else => return runner.fail( + "\"{t}\" expected {t}, got {t}", + .{ state, state_expected.get(state), arg }, + ), + } + }, + } + state = .positional; + } + }, + .query_test_metadata => { + const expected_panic_msgs = try arena.alloc(u32, runner.args.targets.items.len); + @memset(expected_panic_msgs, 0); + runner.server.serveTestMetadata(.{ + .names = runner.args.targets.items, + .expected_panic_msgs = expected_panic_msgs, + .string_bytes = runner.args.target_bytes.items, + }) catch |err| switch (err) { + error.WriteFailed => return error.ServerWriteFailed, + }; + }, + .run_test => { + const test_index = runner.server.receiveBody_u32() catch unreachable; + runner.server.serveBodylessMessage(.test_started) catch |err| switch (err) { + error.WriteFailed => return error.ServerWriteFailed, + }; + runner.server.serveTestResults(.{ + .index = test_index, + .flags = .{ + .status = if (runner.testOne(test_index)) + .pass + else |err| status: switch (err) { + error.Canceled, + error.OutOfMemory, + error.ServerWriteFailed, + => |e| return e, + error.AlreadyReported => { + var eb = try runner.eb_wip.toOwnedBundle(""); + defer eb.deinit(runner.gpa); + assert(eb.errorMessageCount() > 0); // already reported what? + runner.server.serveErrorBundle( + .error_bundle, + eb, + ) catch |server_err| switch (server_err) { + error.WriteFailed => return error.ServerWriteFailed, + }; + const eb_wip: std.zig.ErrorBundle.Wip = try .init(runner.gpa); + runner.eb_wip.deinit(); + runner.eb_wip = eb_wip; + break :status .fail; + }, + error.SkipTest => .skip, + }, + .fuzz = false, + .log_err_count = 0, + .leak_count = 0, + }, + }) catch |err| switch (err) { + error.WriteFailed => return error.ServerWriteFailed, + }; + assert(runner.eb_wip.root_list.items.len == 0); // failed to report + }, + } + } +} + +fn discoverInput( + runner: *Runner, + dir: std.zig.Server.Message.InputDir, + sub_path: []const u8, +) error{ServerWriteFailed}!void { + return runner.discoverInputInner(dir, sub_path) catch |err| switch (err) { + error.WriteFailed => return error.ServerWriteFailed, + }; +} +fn discoverInputInner( + runner: *Runner, + dir: std.zig.Server.Message.InputDir, + sub_path: []const u8, +) std.Io.Writer.Error!void { + try runner.server.serveMessageHeader(.{ + .tag = .discovered_inputs, + .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + + sub_path.len + 1), + }); + try runner.server.out.writeInt(u32, @backingInt(dir), .little); + try runner.server.out.writeAll(sub_path); + try runner.server.out.writeByte(0); + try runner.server.out.flush(); +} + +pub const SourceReader = struct { + const Io = std.Io; + + backing: *std.Io.Reader, + line_aw: std.Io.Writer.Allocating, + interface: std.Io.Reader, + line: u32, + eb_wip: *std.zig.ErrorBundle.Wip, + src_path: std.zig.ErrorBundle.String, + + pub fn init( + reader: *Io.Reader, + gpa: std.mem.Allocator, + eb_wip: *std.zig.ErrorBundle.Wip, + src_path: std.zig.ErrorBundle.String, + ) SourceReader { + return .{ + .backing = reader, + .line_aw = .init(gpa), + .interface = .{ + .vtable = &.{ + .stream = stream, + .discard = discard, + .readVec = readVec, + .rebase = rebase, + }, + .buffer = &.{}, + .seek = 0, + .end = 0, + }, + .line = 0, + .eb_wip = eb_wip, + .src_path = src_path, + }; + } + fn deinit(s: *SourceReader) void { + s.line_aw.deinit(); + } + fn stream(r: *Io.Reader, _: *Io.Writer, _: Io.Limit) Io.Reader.StreamError!usize { + const s: *SourceReader = @fieldParentPtr("interface", r); + try s.bufferLine(); + return 0; + } + fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { + const s: *SourceReader = @fieldParentPtr("interface", r); + const remaining = r.buffered(); + if (remaining.len > 0) { + const n = limit.minInt(remaining.len); + r.toss(n); + return n; + } + try s.bufferLine(); + return 0; + } + fn readVec(r: *Io.Reader, _: [][]u8) Io.Reader.Error!usize { + const s: *SourceReader = @fieldParentPtr("interface", r); + try s.bufferLine(); + return 0; + } + fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void { + const s: *SourceReader = @fieldParentPtr("interface", r); + defer s.updateBuffer(); + s.line_aw.ensureUnusedCapacity(capacity) catch |err| switch (err) { + error.OutOfMemory => return error.ReadFailed, + }; + } + fn updateBuffer(s: *SourceReader) void { + s.interface.buffer = s.line_aw.writer.buffer; + s.interface.end = s.line_aw.writer.end; + } + fn bufferLine(s: *SourceReader) Io.Reader.Error!void { + defer s.updateBuffer(); + { + const written = s.line_aw.written(); + const start = if (std.mem.findScalarLast(u8, written[0..s.interface.seek], '\n')) |newline| + newline + 1 + else + 0; + s.line += @intCast(std.mem.countScalar(u8, written[0..start], '\n')); + const preserve = written[start..]; + @memmove(written[0..preserve.len], preserve); + s.line_aw.shrinkRetainingCapacity(preserve.len); + s.interface.seek -= start; + } + const n = s.backing.streamDelimiterEnding(&s.line_aw.writer, '\n') catch |err| switch (err) { + error.ReadFailed => |e| return e, + error.WriteFailed => return error.ReadFailed, + }; + if (std.mem.endsWith(u8, s.line_aw.written(), "\r")) s.line_aw.writer.end -= 1; + const buffered = s.backing.buffered(); + if (n == 0 and buffered.len == 0) return error.EndOfStream; + if (std.mem.startsWith(u8, buffered, "\n")) { + s.line_aw.writer.writeByte('\n') catch |err| switch (err) { + error.WriteFailed => return error.ReadFailed, + }; + s.backing.toss(1); + } + } + fn fail( + s: *SourceReader, + src_loc: []const u8, + comptime fmt: []const u8, + args: anytype, + ) FailError { + return s.failString(src_loc, try s.eb_wip.printString(fmt, args)); + } + fn failString( + s: *SourceReader, + src_loc: []const u8, + msg: std.zig.ErrorBundle.String, + ) FailError { + try s.eb_wip.addRootErrorMessage(.{ .msg = msg, .src_loc = try s.srcLoc(src_loc) }); + return error.AlreadyReported; + } + fn srcLoc( + s: *SourceReader, + src_loc: []const u8, + ) std.mem.Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex { + const line: u32, const column: u32, const source_line = src_loc: { + const buffer = s.interface.buffer[0..s.interface.end]; + const pos = src_loc.ptr - buffer.ptr; + const end = std.mem.findScalarPos(u8, buffer, pos, '\n') orelse buffer.len; + const start = + if (std.mem.findScalarLast(u8, buffer[0..end], '\n')) |newline| newline + 1 else 0; + break :src_loc .{ + @intCast(s.line + std.mem.countScalar(u8, buffer[0..start], '\n')), + @intCast(pos - start), + buffer[start..end], + }; + }; + return s.eb_wip.addSourceLocation(.{ + .src_path = s.src_path, + .line = line, + .column = column, + .span_start = column, + .span_main = column, + .span_end = @intCast(column + src_loc.len), + .source_line = try s.eb_wip.addString(source_line), + }); + } +}; +pub const DelimitedReader = struct { + const Io = std.Io; + + unlimited: *Io.Reader, + remaining: Io.Limit, + delimiter: []const u8, + interface: Io.Reader, + + pub fn init(reader: *Io.Reader, delimiter: []const u8, buffer: []u8) DelimitedReader { + return .{ + .unlimited = reader, + .remaining = .nothing, + .delimiter = delimiter, + .interface = .{ + .vtable = &.{ + .stream = stream, + .discard = discard, + }, + .buffer = buffer, + .seek = 0, + .end = 0, + }, + }; + } + fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { + const d: *DelimitedReader = @fieldParentPtr("interface", r); + const block = try d.unlimited.peekGreedy(d.delimiter.len); + d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse + block.len - d.delimiter.len + 1); + if (d.remaining == .nothing) return error.EndOfStream; + const n = try d.unlimited.stream(w, limit.min(d.remaining)); + d.remaining = d.remaining.subtract(n).?; + return n; + } + fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { + const d: *DelimitedReader = @fieldParentPtr("interface", r); + const block = try d.unlimited.peekGreedy(d.delimiter.len); + d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse + block.len - d.delimiter.len + 1); + if (d.remaining == .nothing) return error.EndOfStream; + const n = try d.unlimited.discard(limit.min(d.remaining)); + d.remaining = d.remaining.subtract(n).?; + return n; + } +}; + +const Update = struct { + target_query: []const u8, + manifest_sr: SourceReader, + compiler: ?Compiler, + src_dir: std.Io.Dir, + eb_wip: *std.zig.ErrorBundle.Wip, + cmd_src_loc: std.zig.ErrorBundle.SourceLocationIndex, + src_loc: ?std.zig.ErrorBundle.SourceLocationIndex, + num: usize, + name: []const u8, + prog_node: std.Progress.Node, + mtime: std.Io.Timestamp, + allow_skip: bool, + + fn checkMatches( + update: *const Update, + what: []const u8, + expected: []const u8, + actual: []const u8, + ) FailError!void { + var expected_match_it = std.mem.splitSequence(u8, expected, "#..."); + var pos: usize = 0; + while (expected_match_it.next()) |expected_match| : (pos += expected_match.len) { + var context_len: usize = expected_match.len; + pos = while (true) : (context_len -= 1) break std.mem.findPos( + u8, + actual, + pos, + expected_match[0..context_len], + ) orelse continue; + const actual_match = actual[pos..]; + std.testing.expectEqualStrings( + expected_match, + actual_match[0..@min(actual_match.len, expected_match.len)], + ) catch |err| switch (err) { + error.TestExpectedEqual => { + std.testing.failPrint("\n=============== full output: =========\n", .{}); + std.testing.failPrintWithVisibleNewlines(actual_match); + std.testing.failPrint("\n======================================\n", .{}); + return update.fail("{s} did not match expected", .{what}); + }, + }; + } + } + + fn fail(update: *const Update, comptime fmt: []const u8, args: anytype) FailError { + return update.failString(try update.eb_wip.printString(fmt, args)); + } + fn failString(update: *const Update, msg: std.zig.ErrorBundle.String) FailError { + var notes_buffer: [1]std.zig.ErrorBundle.ErrorMessage = undefined; + var notes_len: u32 = 0; + if (update.src_loc) |src_loc| { + notes_buffer[notes_len] = .{ + .msg = try update.eb_wip.addString("after this update"), + .src_loc = src_loc, + }; + notes_len += 1; + } + try update.eb_wip.addRootErrorMessageWithNotes(.{ + .msg = msg, + .src_loc = update.cmd_src_loc, + .notes_len = 1, + }, notes_buffer[0..notes_len]); + return error.AlreadyReported; + } +}; + +const TestError = error{ + SkipTest, +} || FailError; +fn testOne(runner: *Runner, test_index: u32) (error{ServerWriteFailed} || TestError)!void { + const target_query = + std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0); + const manifest_file = runner.args.manifest_file orelse + return runner.fail("missing \"path/to/test\" arg", .{}); + var manifest_buffer: [512]u8 = undefined; + var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer); + var update: Update = .{ + .target_query = target_query, + .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: { + var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; + const src_path = src_path: { + break :src_path path_buffer[0 .. manifest_file.realPath( + runner.io, + &path_buffer, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => break :src_path "manifest", + }]; + }; + break :src_path_string try runner.eb_wip.addString(src_path); + }), + .compiler = null, + .src_dir = (runner.args.src_dir orelse + return runner.fail("missing \"--src=path/to/src\" arg", .{})).createDirPathOpen( + runner.io, + target_query, + .{}, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return runner.fail("unable to create {q}: {t}", .{ target_query, e }), + }, + .eb_wip = &runner.eb_wip, + .cmd_src_loc = undefined, + .src_loc = null, + .num = 0, + .name = undefined, + .prog_node = .none, + .mtime = .now(runner.io, .real), + .allow_skip = true, + }; + defer { + update.manifest_sr.deinit(); + if (update.compiler) |*comp| comp.deinit(); + update.src_dir.close(runner.io); + update.prog_node.end(); + } + + // hack around `std.Io.Reader` interface bugs by ensuring the + // buffer is larger than the longest supported manifest line + try update.manifest_sr.line_aw.ensureTotalCapacityPrecise(512); + update.manifest_sr.updateBuffer(); + + var skip_delimiter = false; + while (update.manifest_sr.interface.takeSentinel('\n')) |line| { + var line_it = std.mem.tokenizeScalar(u8, line, ' '); + const cmd_src_loc = line_it.next() orelse continue; + if (skip_delimiter) { + assert(std.mem.eql(u8, cmd_src_loc, "#}")); + skip_delimiter = false; + const unexpected = line_it.rest(); + if (unexpected.len > 0) + return update.manifest_sr.fail(unexpected, "unexpected argument {q}", .{unexpected}); + continue; + } + update.cmd_src_loc = try update.manifest_sr.srcLoc(cmd_src_loc); + const cmd = std.meta.stringToEnum(Command, std.mem.cutPrefix(u8, cmd_src_loc, "#") orelse + return update.manifest_sr.fail(cmd_src_loc, "expected command", .{})) orelse + return update.manifest_sr.fail(cmd_src_loc, "unknown command", .{}); + const arg = switch (cmd) { + .todo, .exe, .lib, .obj, .update => null, + .skip, .write, .delete, .check => line_it.next() orelse + return update.manifest_sr.fail(line_it.rest()[0..0], "missing argument", .{}), + }; + var contents_file: ?std.Io.File = null; + defer if (contents_file) |file| file.close(runner.io); + var contents_buffer: [512]u8 = undefined; + var contents_impl: union(enum) { + r: std.Io.Reader, + dr: DelimitedReader, + fr: std.Io.File.Reader, + } = undefined; + const contents_r: *std.Io.Reader = contents_r: switch (cmd) { + .skip, .delete => { + const unexpected = line_it.rest(); + if (unexpected.len > 0) return update.manifest_sr.fail( + unexpected, + "unexpected argument {q}", + .{unexpected}, + ); + contents_impl = .{ .r = .ending_instance }; + break :contents_r &contents_impl.r; + }, + else => { + const contents = line_it.rest(); + if (contents.len == 0) { + contents_impl = .{ .r = .ending_instance }; + break :contents_r &contents_impl.r; + } + contents: switch (contents[0]) { + else => return update.manifest_sr.fail(contents, "invalid contents syntax", .{}), + '"' => { + if (contents.len < 2 or contents[contents.len - 1] != '"') continue :contents 0; + var contents_fw: std.Io.Writer = .fixed(&contents_buffer); + switch (std.zig.string_literal.parseWrite( + &contents_fw, + contents, + ) catch |err| switch (err) { + error.WriteFailed => return update.manifest_sr.fail( + contents, + "contents string too long, use inline or file syntax instead", + .{}, + ), + }) { + .success => { + contents_impl = .{ .r = .fixed(contents_fw.buffered()) }; + break :contents_r &contents_impl.r; + }, + .failure => |err| return update.manifest_sr.fail( + contents[err.offset()..][0..0], + "{f}", + .{err.fmt(contents)}, + ), + } + }, + '{' => { + if (contents.len != 1) continue :contents 0; + + contents_impl = .{ + .dr = .init(&update.manifest_sr.interface, "#}", &contents_buffer), + }; + skip_delimiter = true; + break :contents_r &contents_impl.dr.interface; + }, + '@' => { + const args_test = runner.args.@"test" orelse return update.manifest_sr.fail( + contents, + "contents file requires directory test", + .{}, + ); + const contents_path = contents[1..]; + try runner.discoverInput(args_test.input_dir, contents_path); + + contents_file = args_test.dir.openFile( + runner.io, + contents_path, + .{}, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.manifest_sr.fail( + contents_path, + "unable to open contents file: {t}", + .{e}, + ), + }; + contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) }; + break :contents_r &contents_impl.fr.interface; + }, + } + }, + }; + + runner.handleCommand(&update, cmd_src_loc, cmd, arg, contents_r) catch |err| switch (err) { + error.Canceled, + error.OutOfMemory, + error.AlreadyReported, + error.SkipTest, + => |e| return e, + error.ContentsReadFailed => switch (contents_impl) { + .r => unreachable, + .dr => switch (manifest_fr.err orelse return error.OutOfMemory) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to read manifest: {t}", .{e}), + }, + .fr => |fr| switch (fr.err.?) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to read contents file: {t}", .{e}), + }, + }, + }; + } else |err| switch (err) { + error.ReadFailed => switch (manifest_fr.err orelse return error.OutOfMemory) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to read manifest: {t}", .{e}), + }, + error.StreamTooLong => unreachable, + error.EndOfStream => { + const final_line = update.manifest_sr.interface.buffered(); + switch (final_line.len) { + 0 => if (skip_delimiter) return update.manifest_sr.fail( + final_line[0..0], + "missing \"#}}\" delimiter", + .{}, + ), + else => return update.manifest_sr.fail( + final_line[final_line.len..], + "missing trailing newline", + .{}, + ), + } + }, + } + if (update.compiler) |*comp| { + if (comp.state != .idle) return update.fail("missing \"#check\"", .{}); + try comp.exit(); + } +} +const Command = enum { todo, skip, exe, lib, obj, write, delete, update, check }; +fn handleCommand( + runner: *Runner, + update: *Update, + cmd_src_loc: []const u8, + cmd: Command, + arg: ?[]const u8, + contents_r: *std.Io.Reader, +) (error{ContentsReadFailed} || TestError)!void { + var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; + const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t}{s}{s}", .{ + cmd, if (arg) |_| " " else "", arg orelse "", + }) catch &name_buffer, 0); + defer cmd_prog_node.end(); + + cmd: switch (cmd) { + .todo => _ = contents_r.discardRemaining() catch |err| switch (err) { + error.ReadFailed => {}, + }, + .skip => if (!update.allow_skip) return update.manifest_sr.fail( + cmd_src_loc, + "\"#skip\" must appear before other commands", + .{}, + ) else if (std.mem.eql(u8, update.target_query, arg.?)) return error.SkipTest, + .exe, .lib, .obj => runner.spawnCompiler(&update.compiler, update.src_dir, switch (cmd) { + else => unreachable, + .exe => .Exe, + .lib => .Lib, + .obj => .Obj, + }, contents_r, target: { + var component_it = std.mem.splitBackwardsScalar(u8, update.target_query, '-'); + const pic_str = component_it.next() orelse + return update.fail("target {q} missing pic", .{update.target_query}); + const pic = std.meta.stringToEnum(Compiler.Target.Pic, pic_str) orelse + return update.fail("target {q} unknown pic {q}", .{ update.target_query, pic_str }); + const linker_str = component_it.next() orelse + return update.fail("target {q} missing linker", .{update.target_query}); + const linker = std.meta.stringToEnum(Compiler.Target.Linker, linker_str) orelse + return update.fail("target {q} unknown linker {q}", .{ + update.target_query, linker_str, + }); + const backend_str = component_it.next() orelse + return update.fail("target {q} missing backend", .{update.target_query}); + const backend = std.meta.stringToEnum(Compiler.Target.Backend, backend_str) orelse + return update.fail("target {q} unknown backend {q}", .{ + update.target_query, backend_str, + }); + const cache_mode_str = component_it.next() orelse + return update.fail("target {q} missing cache mode", .{update.target_query}); + const cache_mode = std.meta.stringToEnum(Compiler.Target.CacheMode, cache_mode_str) orelse + return update.fail("target {q} unknown cache mode {q}", .{ + update.target_query, cache_mode_str, + }); + const triple_cpu = component_it.rest(); + var triple_len: usize = 0; + for (0..3) |_| { + const component_start = triple_len; + triple_len = if (std.mem.findScalarPos(u8, triple_cpu, triple_len, '-')) |dash_pos| + dash_pos + 1 + else + return update.fail("target {q} missing cpu", .{update.target_query}); + if (std.mem.eql(u8, triple_cpu[component_start..triple_len], "native-")) break; + } + const triple = triple_cpu[0 .. triple_len - 1]; + const cpu = triple_cpu[triple_len..]; + var query_parse_diags: std.Target.Query.ParseOptions.Diagnostics = undefined; + break :target .{ + .triple = triple, + .resolved = std.zig.system.resolveTargetQuery( + runner.io, + std.Target.Query.parse(.{ + .arch_os_abi = triple, + .cpu_features = cpu, + .object_format = switch (backend) { + .sema, .selfhosted, .llvm => null, + .cbe => "c", + }, + .diagnostics = &query_parse_diags, + }) catch |err| switch (err) { + error.UnknownArchitecture => return update.fail( + "unable to resolve target {q}: unknown architecture: {q}", + .{ update.target_query, query_parse_diags.unknown_architecture_name.? }, + ), + error.MissingOperatingSystem => return update.fail( + "unable to resolve target {q}: missing operating system", + .{update.target_query}, + ), + error.UnknownOperatingSystem => return update.fail( + "unable to resolve target {q}: unknown operating system: {q}", + .{ update.target_query, query_parse_diags.os_name.? }, + ), + error.InvalidOperatingSystemVersion => return update.fail( + "unable to resolve target {q}: invalid operating system version", + .{update.target_query}, + ), + error.UnknownApplicationBinaryInterface => return update.fail( + "unable to resolve target {q}: unknown abi", + .{update.target_query}, + ), + error.InvalidAbiVersion => return update.fail( + "unable to resolve target {q}: invalid abi version", + .{update.target_query}, + ), + error.UnexpectedExtraField => unreachable, // validated above + error.UnknownCpuModel => return update.fail( + "unable to resolve target {q}: unknown cpu: {q}", + .{ update.target_query, query_parse_diags.cpu_name.? }, + ), + error.UnknownCpuFeature => return update.fail( + "unable to resolve target {q}: unknown cpu feature: {q}", + .{ update.target_query, query_parse_diags.unknown_feature_name.? }, + ), + error.UnknownObjectFormat => return update.fail( + "unable to resolve target {q}: unknown object format", + .{update.target_query}, + ), + }, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to resolve target {q}: {t}", .{ triple, e }), + }, + .cache_mode = cache_mode, + .backend = backend, + .linker = linker, + .pic = pic, + }; + }) catch |err| switch (err) { + error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, + error.ReadFailed => return error.ContentsReadFailed, + }, + .write => { + const file = update.src_dir.createFile(runner.io, arg.?, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.manifest_sr.fail(arg.?, "unable to create file: {t}", .{e}), + }; + defer file.close(runner.io); + var fw_buffer: [512]u8 = undefined; + var fw = file.writer(runner.io, &fw_buffer); + _ = contents_r.streamRemaining(&fw.interface) catch |err| switch (err) { + error.ReadFailed => return error.ContentsReadFailed, + error.WriteFailed => switch (fw.err.?) { + error.Canceled => |e| return e, + else => |e| return update.manifest_sr.fail( + arg.?, + "unable to write file: {t}", + .{e}, + ), + }, + }; + fw.flush() catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.manifest_sr.fail( + arg.?, + "unable to write file: {t}", + .{e}, + ), + }; + file.setTimestamps(runner.io, .{ .modify_timestamp = .{ + .new = update.mtime, + } }) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.manifest_sr.fail( + arg.?, + "unable to update file timestamps: {t}", + .{e}, + ), + }; + }, + .delete => update.src_dir.deleteFile(runner.io, arg.?) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.manifest_sr.fail(arg.?, "unable to delete file: {t}", .{e}), + }, + .update => { + update.src_loc = update.cmd_src_loc; + const comp = &(update.compiler orelse return update.manifest_sr.fail( + cmd_src_loc, + "missing \"#exe\", \"#lib\", or \"#obj\"", + .{}, + )); + update.prog_node.end(); + const arena = runner.arena.allocator(); + update.name = switch (contents_r.readSliceShort(&name_buffer) catch |err| switch (err) { + error.ReadFailed => return error.ContentsReadFailed, + }) { + 0 => switch (update.num) { + 0 => "initial update", + else => |num| try arena.print("update {d}", .{num}), + }, + else => |name_len| try arena.dupe(u8, name_buffer[0..name_len]), + }; + update.prog_node = runner.prog_node.start(update.name, 0); + _ = contents_r.discardRemaining() catch |err| switch (err) { + error.ReadFailed => return error.ContentsReadFailed, + }; + if (comp.state != .idle) + return update.manifest_sr.fail(cmd_src_loc, "missing \"#check\"", .{}); + comp.client.serveBodylessMessage(.update) catch |err| switch (err) { + error.WriteFailed => switch (comp.fw.err.?) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to send message to compiler: {t}", .{e}), + }, + }; + comp.state = .update; + update.mtime = update.mtime.addDuration(.fromSeconds(2)); + update.num += 1; + }, + .check => { + const check = std.meta.stringToEnum(Compiler.Check, arg.?) orelse + return update.manifest_sr.fail(arg.?, "unknown check", .{}); + const expected = + contents_r.allocRemaining(runner.gpa, .unlimited) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.ReadFailed => return error.ContentsReadFailed, + error.StreamTooLong => unreachable, // .unlimited + }; + defer runner.gpa.free(expected); + + const comp = &(update.compiler orelse return update.manifest_sr.fail( + cmd_src_loc, + "missing \"#exe\", \"#lib\", or \"#obj\"", + .{}, + )); + if (comp.state != .update) + return update.manifest_sr.fail(cmd_src_loc, "missing \"#update\"", .{}); + const stderr = comp.mr.reader(1); + while (try comp.receiveMessage()) |message| switch (message.tag) { + else => {}, // Ignore other messages, + .config => { + var body_r: std.Io.Reader = .fixed(message.body); + comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch + unreachable; + }, + .emit_digest => { + var body_r: std.Io.Reader = .fixed(message.body); + _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch + unreachable; + const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch + unreachable; + try comp.checkSuccess(update, check, expected, digest); + }, + .error_bundle => { + const eb = std.zig.Server.allocErrorBundle( + runner.arena.allocator(), + message.body, + ) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.EndOfStream => return update.fail("error bundle truncated", .{}), + }; + switch (check) { + .errors => { + var error_aw: std.Io.Writer.Allocating = .init(runner.gpa); + defer error_aw.deinit(); + eb.renderToWriter(.{ + .include_reference_trace = false, + .include_source_line = false, + .include_log_text = true, + }, &error_aw.writer) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; + const actual = error_aw.written(); + std.mem.replaceScalar(u8, actual, '\\', '/'); + std.testing.expectEqualStrings(expected, actual) catch |err| switch (err) { + error.TestExpectedEqual => return update.fail( + "errors did not match expected", + .{}, + ), + }; + }, + .stdout, .exit, .llvm, .gdb, .lldb => if (eb.errorMessageCount() > 0) { + eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { + error.Canceled => {}, + else => {}, + }; + return update.fail("unexpected compile errors", .{}); + }, + } + comp.state = .idle; + break :cmd; + }, + }; + + const stderr_buffered = stderr.buffered(); + if (stderr_buffered.len > 0) { + if (comp.allow_compiler_stderr) { + std.log.info("#update {q} stderr:\n{s}", .{ update.name, stderr_buffered }); + } else return update.fail("unexpected stderr:\n{s}", .{stderr_buffered}); + } + + return update.fail("compiler failed to send terminating error_bundle", .{}); + }, + } + assert(contents_r.discardRemaining() catch |err| switch (err) { + error.ReadFailed => return error.ContentsReadFailed, + } == 0); + switch (cmd) { + .todo, .skip => {}, + else => update.allow_skip = false, + } +} + +fn spawnCompiler( + runner: *Runner, + compiler: *?Compiler, + src_dir: std.Io.Dir, + output_mode: std.lang.OutputMode, + args_r: *std.Io.Reader, + target: Compiler.Target, +) (std.Io.Reader.ShortError || TestError)!void { + const gpa = runner.gpa; + const arena = runner.arena.allocator(); + if (compiler.*) |*comp| { + comp.deinit(); + compiler.* = null; + } + errdefer compiler.* = null; + + compiler.* = .{ + .runner = runner, + .allow_compiler_stderr = true, + .output_mode = output_mode, + .state = .idle, + .child = undefined, + .fw = undefined, + .mr_buffer = undefined, + .mr = undefined, + .client = undefined, + .target = target, + .emit_name = null, + .prog_node = .none, + .config = null, + }; + const comp = &compiler.*.?; + + var argv: std.ArrayList([]const u8) = .empty; + defer argv.deinit(gpa); + try argv.appendSlice(gpa, &.{ + "zig", + switch (output_mode) { + .Obj => "build-obj", + .Exe => "build-exe", + .Lib => "build-lib", + }, + "-target", + comp.target.triple, + "--cache-dir", + ".zig-cache", + }); + switch (comp.target.cache_mode) { + .whole => {}, + .incremental => try argv.append(gpa, "-fincremental"), + } + var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; + if (comp.target.backend) |b| switch (b) { + .sema => try argv.append(gpa, "-fno-emit-bin"), + .selfhosted => try argv.append(gpa, "-fno-llvm"), + .llvm => try argv.append(gpa, "-fllvm"), + .cbe => try argv.append(gpa, "-ofmt=c"), + } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse + return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) + .realPath(runner.io, &path_buffer) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return runner.fail("unable to get lib path: {t}", .{e}), + }] }); + switch (comp.target.linker) { + .lld => try argv.appendSlice(gpa, &.{ "-flld", "-fno-new-linker" }), + .old => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fno-new-linker" }), + .new => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fnew-linker" }), + } + switch (comp.target.pic) { + .nopic => try argv.appendSlice(gpa, &.{ "-fno-PIC", "-fno-PIE" }), + .pic => try argv.appendSlice(gpa, &.{ "-fPIC", "-fno-PIE" }), + .pie => try argv.appendSlice(gpa, &.{ "-fPIC", "-fPIE" }), + } + + var need: usize = 1; + var state: enum { positional, @"--name" } = .positional; + var root_name: ?[]const u8 = null; + var emit: ?std.zig.EmitArtifact = .bin; + while (true) { + const done = if (args_r.fill(need)) false else |err| switch (err) { + error.ReadFailed => |e| return e, + error.EndOfStream => true, + }; + const buffered = args_r.buffered(); + const start = std.mem.findNone(u8, buffered, "\n ") orelse buffered.len; + const end = std.mem.findAnyPos(u8, buffered, start, "\n ") orelse buffered.len; + if (end - start > 0) { + const arg = try arena.dupe(u8, buffered[start..end]); + switch (state) { + .positional => if (std.mem.eql(u8, arg, "--name")) { + state = .@"--name"; + } else if (std.mem.cutPrefix(u8, arg, "-ofmt=")) |ofmt| { + comp.target.resolved.ofmt = + std.meta.stringToEnum(std.Target.ObjectFormat, ofmt) orelse + return runner.fail("invalid object format {q}", .{ofmt}); + } else if (std.mem.cutPrefix(u8, arg, "-M")) |module| { + var module_name_it = std.mem.splitScalar(u8, module, '='); + const module_name = module_name_it.next().?; + root_name = root_name orelse module_name; + } else if (std.mem.eql(u8, arg, "-femit-asm")) { + emit = .@"asm"; + } else if (std.mem.eql(u8, arg, "-femit-bin")) { + emit = .bin; + } else if (std.mem.eql(u8, arg, "-femit-docs")) { + emit = .docs; + } else if (std.mem.eql(u8, arg, "-femit-h")) { + emit = .h; + } else if (std.mem.eql(u8, arg, "-femit-implib")) { + emit = .implib; + } else if (std.mem.eql(u8, arg, "-femit-llvm-bc")) { + emit = .llvm_bc; + } else if (std.mem.eql(u8, arg, "-femit-llvm-ir")) { + emit = .llvm_ir; + } else if (std.mem.eql(u8, arg, "-fno-emit-asm")) { + if (emit == .@"asm") emit = null; + } else if (std.mem.eql(u8, arg, "-fno-emit-bin")) { + if (emit == .bin) emit = null; + } else if (std.mem.eql(u8, arg, "-fno-emit-docs")) { + if (emit == .docs) emit = null; + } else if (std.mem.eql(u8, arg, "-fno-emit-h")) { + if (emit == .h) emit = null; + } else if (std.mem.eql(u8, arg, "-fno-emit-implib")) { + if (emit == .implib) emit = null; + } else if (std.mem.eql(u8, arg, "-fno-emit-llvm-bc")) { + if (emit == .llvm_bc) emit = null; + } else if (std.mem.eql(u8, arg, "-fno-emit-llvm-ir")) { + if (emit == .llvm_ir) emit = null; + }, + .@"--name" => { + root_name = arg; + state = .positional; + }, + } + try argv.append(gpa, arg); + args_r.toss(end); + need = 1; + } else { + args_r.toss(start); + need = buffered.len - start + 1; + } + if (done) break; + } + try argv.append(gpa, "--listen=-"); + + comp.emit_name = if (emit) |e| try e.cacheName(arena, .{ + .root_name = root_name orelse "root", + .cpu_arch = comp.target.resolved.cpu.arch, + .os_tag = comp.target.resolved.os.tag, + .ofmt = comp.target.resolved.ofmt, + .abi = comp.target.resolved.abi, + .output_mode = output_mode, + }) else null; + + var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; + comp.prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "compile {s}", .{ + comp.emit_name orelse root_name orelse "root", + }) catch &name_buffer, 0); + errdefer comp.prog_node.end(); + + comp.child = std.process.spawn(runner.io, .{ + .exe = .{ .file = runner.args.zig_exe orelse + return runner.fail("missing \"--zig=path/to/zig\" arg", .{}) }, + .argv = argv.items, + .cwd = .{ .dir = src_dir }, + .progress_node = comp.prog_node, + .stdin = .pipe, + .stdout = .pipe, + .stderr = .pipe, + }) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, + error.OperationUnsupported => return error.SkipTest, + else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}), + }; + comp.fw = comp.child.stdin.?.writerStreaming(runner.io, &.{}); + comp.mr.init(gpa, runner.io, comp.mr_buffer.toStreams(), &.{ + comp.child.stdout.?, comp.child.stderr.?, + }); + comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface }; +} +const Compiler = struct { + runner: *Runner, + allow_compiler_stderr: bool, + output_mode: std.lang.OutputMode, + state: enum { idle, update }, + child: std.process.Child, + fw: std.Io.File.Writer, + mr_buffer: std.Io.File.MultiReader.Buffer(2), + mr: std.Io.File.MultiReader, + client: std.zig.Client, + target: Compiler.Target, + emit_name: ?[]const u8, + prog_node: std.Progress.Node, + config: ?std.zig.Server.Message.Config, + + const Target = struct { + triple: []const u8, + resolved: std.Target, + cache_mode: CacheMode, + backend: ?Backend, + linker: Linker, + pic: Pic, + + const CacheMode = enum { whole, incremental }; + + const Backend = enum { + /// Run semantic analysis only. Runtime output will not be tested, but we still verify + /// that compilation succeeds. Corresponds to `-fno-emit-bin`. + sema, + /// Use the self-hosted code generation backend for this target. + /// Corresponds to `-fno-llvm -fno-lld`. + selfhosted, + /// Use the LLVM backend. + /// Corresponds to `-fllvm -flld`. + llvm, + /// Use the C backend. The output is compiled with `zig cc`. + /// Corresponds to `-ofmt=c`. + cbe, + }; + + const Linker = enum { old, new, lld }; + + const Pic = enum { nopic, pic, pie }; + }; + + fn deinit(comp: *Compiler) void { + comp.child.kill(comp.runner.io); + comp.prog_node.end(); + comp.prog_node = .none; + } + + fn receiveMessage(comp: *Compiler) FailError!?struct { + tag: std.zig.Server.Message.Tag, + body: []const u8, + } { + const header = comp.client.receiveMessageWithMultiReader( + &comp.mr, + .none, + ) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, + error.Timeout => unreachable, + error.EndOfStream => return null, + else => |e| return comp.runner.fail("unable to receive message: {t}", .{e}), + }; + const body = comp.client.in.take(header.bytes_len) catch unreachable; + const stderr = comp.mr.reader(1); + if (stderr.bufferedLen() > 0) { + if (comp.allow_compiler_stderr) { + std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() }); + } else { + return comp.runner.fail("{t} unexpected stderr:\n{s}", .{ + header.tag, stderr.buffered(), + }); + } + stderr.tossBuffered(); + } + return .{ .tag = header.tag, .body = body }; + } + + const Check = enum { errors, stdout, exit, llvm, gdb, lldb }; + fn checkSuccess( + comp: *Compiler, + update: *const Update, + check: Check, + expected: []const u8, + digest: *const std.Build.Cache.BinDigest, + ) TestError!void { + const runner = comp.runner; + const gpa = runner.gpa; + const arena = runner.arena.allocator(); + const io = runner.io; + const target = &comp.target.resolved; + const emit_name = switch (check) { + else => comp.emit_name orelse + return update.fail("unable to perform {t} check without emitted file", .{check}), + .errors => return, // expect no emitted file + }; + const config = &comp.config.?; + var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; + + // After a failure while running a script, the debugger starts accepting commands from stdin, and + // because it is empty, the debugger exits normally with status 0. Choose a non-zero status to + // return from the debugger script instead to detect it running to completion and indicate success. + const debug_success = 99; + + const tmp_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ "tmp"; + update.src_dir.createDirPath(io, tmp_dir_path) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to create dir path {q}: {t}", .{ + tmp_dir_path, e, + }), + }; + + const emit_path = emit_path: { + const emit_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ + "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); + const emit_path = + try std.Io.Dir.path.join(arena, &.{ emit_dir_path, emit_name }); + break :emit_path switch (comp.target.backend.?) { + .sema => return, + .selfhosted, .llvm => emit_path, + .cbe => try comp.compileC(update.src_dir, emit_path), + }; + }; + + const emit_file = update.src_dir.openFile(io, emit_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to open {q}: {t}", .{ emit_path, e }), + }; + defer emit_file.close(io); + + var argv: std.ArrayList([]const u8) = try .initCapacity(arena, 5 + 4); + var environ_map: std.process.Environ.Map = .init(gpa); + defer environ_map.deinit(); + try environ_map.putAll(&runner.environ_map); + var script_buffer: [512]u8 = undefined; + const script_path, const script_file, var script_fw = script: switch (check) { + .errors => unreachable, + .stdout, .exit => .{ undefined, null, undefined }, + .llvm => { + var emit_fr = emit_file.reader(io, &.{}); + const actual = emit_fr.interface.readAllocAll(arena, @intCast( + emit_fr.getSize() catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return runner.fail("unable to get size of {q}: {t}", .{ emit_path, e }), + }, + )) catch |err| switch (switch (err) { + error.OutOfMemory => |e| return e, + error.EndOfStream => |e| e, + error.ReadFailed => emit_fr.err.?, + }) { + error.Canceled => |e| return e, + else => |e| return runner.fail("unable to read {q}: {t}", .{ emit_path, e }), + }; + return update.checkMatches("LLVM IR", expected, actual); + }, + .gdb => { + const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; + argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); + const script_file = + update.src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to create debugger script {q}: {t}", .{ + script_path, e, + }), + }; + errdefer script_file.close(io); + var script_fw = script_file.writer(io, &script_buffer); + script_fw.interface.writeAll( + \\set remotetimeout 0 + \\ + \\ + ) catch |err| switch (err) { + error.WriteFailed => switch (script_fw.err.?) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ + script_path, e, + }), + }, + }; + break :script .{ script_path, script_file, script_fw }; + }, + .lldb => { + const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "lldb"; + argv.appendSliceAssumeCapacity(&.{ "lldb", "--batch", "--source", script_path, "--" }); + try environ_map.put("LANG", "C.UTF-8"); // affects output formatting + const script_file = + update.src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to create debugger script {q}: {t}", .{ + script_path, e, + }), + }; + errdefer script_file.close(io); + var script_fw = script_file.writer(io, &script_buffer); + script_fw.interface.writeAll( + \\settings set frame-format 'frame #${frame.index}:{ ${module.file.basename}{\`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${line.file.basename}:${line.number}{:${line.column}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\n' + \\settings set plugin.process.gdb-remote.packet-timeout 0 + \\settings set stop-line-count-after 0 + \\settings set stop-line-count-before 0 + \\ + ) catch |err| switch (err) { + error.WriteFailed => switch (script_fw.err.?) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ + script_path, e, + }), + }, + }; + break :script .{ script_path, script_file, script_fw }; + }, + }; + defer if (script_file) |file| file.close(io); + + const need_cross_libc = + target.os.tag == .linux and config.flags.link_libc and config.flags.link_mode == .dynamic; + const use_executor = use_executor: switch (std.zig.system.getExternalExecutor(io, target, .{ + .host_cpu_arch = runner.host.cpu.arch, + .host_os_tag = runner.host.os.tag, + .qemu_fixes_dl = need_cross_libc and runner.args.libc_runtimes_dir != null, + .link_mode = config.flags.link_mode, + .link_libc = config.flags.link_libc, + })) { + .bad_dl, .bad_os_or_cpu => { + // This binary cannot be executed on this host. + if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++ + "execute binaries for foreign target {q}", .{ + try runner.host.zigTriple(arena), comp.target.triple, + }); + return; + }, + .native => false, + .darling => |executor| if (runner.args.enable_darling) { + argv.appendAssumeCapacity(executor); + break :use_executor true; + } else continue :use_executor .bad_os_or_cpu, + .qemu => |executor| if (runner.args.enable_qemu) { + argv.appendAssumeCapacity(executor); + if (need_cross_libc) { + const libc_runtimes_dir = runner.args.libc_runtimes_dir orelse + continue :use_executor .bad_os_or_cpu; + const libc_runtimes_path = path_buffer[0 .. libc_runtimes_dir.realPath( + io, + &path_buffer, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to get libc runtimes path: {t}", .{e}), + }]; + argv.appendSliceAssumeCapacity(&.{ "-L", try std.Io.Dir.path.join(arena, &.{ + libc_runtimes_path, + try if (target.isGnuLibC()) std.zig.target.glibcRuntimeTriple( + arena, + target.cpu.arch, + target.os.tag, + target.abi, + ) else if (target.isMuslLibC()) std.zig.target.muslRuntimeTriple( + arena, + target.cpu.arch, + target.abi, + ) else unreachable, + }) }); + } + break :use_executor true; + } else continue :use_executor .bad_os_or_cpu, + .rosetta => if (runner.args.enable_rosetta) + true + else + continue :use_executor .bad_os_or_cpu, + .wine => |executor| if (runner.args.enable_wine) { + if (!environ_map.contains("WINEDEBUG")) try environ_map.put("WINEDEBUG", "-all"); + argv.appendAssumeCapacity(executor); + break :use_executor true; + } else continue :use_executor .bad_os_or_cpu, + .wasmtime => |executor| if (runner.args.enable_wasmtime) { + if (!environ_map.contains("WASMTIME_BACKTRACE_DETAILS")) + try environ_map.put("WASMTIME_BACKTRACE_DETAILS", "1"); + argv.appendSliceAssumeCapacity(&.{ executor, "--dir=.", "-Sinherit-env" }); + break :use_executor true; + } else continue :use_executor .bad_os_or_cpu, + }; + argv.appendAssumeCapacity(emit_path); + + if (!runner.args.quiet) std.log.info("{f}", .{std.zig.SubprocessCommand{ + .argv = argv.items, + .cwd = cwd_path: { + break :cwd_path path_buffer[0 .. update.src_dir.realPath( + io, + &path_buffer, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => break :cwd_path null, + }]; + }, + .parent_env = &runner.environ_map, + .child_env = &environ_map, + }}); + + var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; + const run_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "run {s}", .{ + emit_name, + }) catch &name_buffer, 0); + defer run_prog_node.end(); + + const result = std.process.run(gpa, io, .{ + .exe = exe: switch (check) { + .errors, .llvm => unreachable, + .stdout, .exit => if (use_executor) .search else .{ .file = emit_file }, + .gdb, .lldb => { + if (use_executor) { + if (!runner.args.quiet) std.log.warn( + "skipping debugging of {q} via executor for foreign target {q}", + .{ emit_path, comp.target.triple }, + ); + return; + } + + var line_it = std.mem.splitScalar(u8, expected, '\n'); + while (line_it.next()) |line| if (std.mem.cutPrefix(u8, line, switch (check) { + else => unreachable, + .gdb => "(gdb) ", + .lldb => "(lldb) ", + })) |cmd| script_fw.interface.print( + \\{s} + \\ + , .{cmd}) catch |err| switch (err) { + error.WriteFailed => switch (script_fw.err.?) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ + script_path, e, + }), + }, + }; + script_fw.interface.print( + \\ + \\quit {d} + \\ + , .{debug_success}) catch |err| switch (err) { + error.WriteFailed => switch (script_fw.err.?) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ + script_path, e, + }), + }, + }; + script_fw.flush() catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ + script_path, e, + }), + }; + break :exe .{ .file = switch (check) { + else => unreachable, + .gdb => runner.args.gdb_exe, + .lldb => runner.args.lldb_exe, + } orelse { + if (!runner.args.quiet) std.log.warn( + "skipping debugging of {q} because of missing \"--{t}=path/to/{[1]t}\" arg", + .{ emit_path, check }, + ); + return; + } }; + }, + }, + .argv = argv.items, + .cwd = .{ .dir = update.src_dir }, + .environ_map = &environ_map, + .progress_node = run_prog_node, + }) catch |err| if (use_executor) { + // Chances are the foreign executor isn't available. Skip this evaluation. + if (!runner.args.quiet) std.log.warn( + "skipping execution of {q} via executor for foreign target {q}: {t}", + .{ emit_path, comp.target.triple, err }, + ); + return; + } else return update.fail("unable to run the generated executable {q}: {t}", .{ + emit_path, err, + }); + defer { + gpa.free(result.stdout); + gpa.free(result.stderr); + } + switch (result.term) { + .exited => |code| switch (check) { + .errors, .llvm => unreachable, + .stdout => { + if (code != 0) return update.fail( + "generated executable {q} failed with code {d}", + .{ emit_path, code }, + ); + std.testing.expectEqualStrings( + expected, + result.stdout, + ) catch |err| switch (err) { + error.TestExpectedEqual => return update.fail( + "stdout did not match expected", + .{}, + ), + }; + }, + .exit => { + var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = + undefined; + std.testing.expectEqualStrings( + expected, + std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable, + ) catch |err| switch (err) { + error.TestExpectedEqual => return update.fail( + "exit code did not match expected", + .{}, + ), + }; + }, + .gdb, .lldb => { + if (code != debug_success) return update.fail( + "debugging {q} failed with code {d}", + .{ emit_path, code }, + ); + try update.checkMatches("stdout", expected, result.stdout); + }, + }, + .signal => |sig| return update.fail( + "generated executable {q} terminated with signal {t}", + .{ emit_path, sig }, + ), + .stopped => |sig| return update.fail( + "generated executable {q} stopped with signal {t}", + .{ emit_path, sig }, + ), + .unknown => return update.fail( + "generated executable {q} terminated unexpectedly", + .{emit_path}, + ), + } + if (!use_executor and result.stderr.len > 0) { + std.log.err("#update {q} generated executable {q} had unexpected stderr:\n{s}", .{ + update.name, emit_path, result.stderr, + }); + } + } + + fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) TestError![]const u8 { + const runner = parent_comp.runner; + const arena = runner.arena.allocator(); + var compiler: ?Compiler = null; + var args_r: std.Io.Reader = .fixed(c_path); + parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{ + .triple = parent_comp.target.triple, + .resolved = resolved: { + var resolved = parent_comp.target.resolved; + resolved.ofmt = .default(resolved.os.tag, resolved.cpu.arch); + break :resolved resolved; + }, + .cache_mode = .whole, + .backend = null, + .linker = .lld, + .pic = parent_comp.target.pic, + }) catch |err| switch (err) { + error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, + error.ReadFailed => unreachable, // .fixed + }; + const comp = &compiler.?; + defer comp.deinit(); + comp.client.serveBodylessMessage(.update) catch |err| switch (err) { + error.WriteFailed => switch (comp.fw.err.?) { + error.Canceled => |e| return e, + else => |e| return runner.fail("unable to send message: {t}", .{e}), + }, + }; + comp.state = .update; + var emit_path: ?[]const u8 = null; + while (try comp.receiveMessage()) |message| switch (message.tag) { + else => {}, // Ignore other messages, + .config => { + var body_r: std.Io.Reader = .fixed(message.body); + comp.config = + body_r.takeStruct(std.zig.Server.Message.Config, .little) catch unreachable; + }, + .emit_digest => { + var body_r: std.Io.Reader = .fixed(message.body); + _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; + const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch unreachable; + + const emit_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ + "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); + emit_path = try std.Io.Dir.path.join(arena, &.{ emit_dir, comp.emit_name.? }); + }, + .error_bundle => { + const eb = std.zig.Server.allocErrorBundle( + runner.arena.allocator(), + message.body, + ) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.EndOfStream => return runner.fail("error bundle truncated", .{}), + }; + if (eb.errorMessageCount() > 0) { + try runner.eb_wip.addBundleAsRoots(eb); + return runner.fail("unexpected compile errors", .{}); + } + comp.state = .idle; + break; + }, + }; + try comp.exit(); + return emit_path.?; + } + + fn exit(comp: *Compiler) FailError!void { + const runner = comp.runner; + comp.client.serveBodylessMessage(.exit) catch |err| switch (err) { + error.WriteFailed => switch (comp.fw.err.?) { + error.Canceled => |e| return e, + error.BrokenPipe => {}, + else => |e| return comp.runner.fail("unable to send exit: {t}", .{e}), + }, + }; + comp.child.stdin.?.close(runner.io); + comp.child.stdin = null; + while (try comp.receiveMessage()) |_| {} + if (comp.client.in.bufferedLen() > 0) return runner.fail("unable to receive messages", .{}); + const term = comp.child.wait(runner.io) catch |err| + return runner.fail("child process failed: {t}", .{err}); + switch (term) { + .exited => |code| if (code != 0) + return runner.fail("compiler failed with code {d}", .{code}), + .signal => |sig| return runner.fail("compiler terminated with signal {t}", .{sig}), + .stopped => |sig| return runner.fail("compiler stopped unexpectedly with signal {t}", .{ + sig, + }), + .unknown => return runner.fail("compiler terminated unexpectedly", .{}), + } + } +}; + +const ClientArgs = struct { + test_file: std.Io.File, + test_kind: std.Io.File.Kind, + zig_exe: std.Io.File, + lib_dir: std.Io.Dir, + src_dir: std.Io.Dir, + targets: std.ArrayList([]const u8), + + libc_runtimes_dir: ?std.Io.Dir, + enable_darling: bool, + enable_qemu: bool, + enable_rosetta: bool, + enable_wine: bool, + enable_wasmtime: bool, + + gdb_exe: ?std.Io.File, + lldb_exe: ?std.Io.File, + + quiet: bool, + keep_src: bool, +}; +pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 { + const fatal = struct { + fn fatal(comptime fmt: []const u8, args: anytype) noreturn { + std.log.err(fmt, args); + std.process.exit(1); + } + }.fatal; + const arena = init.arena.allocator(); + + var test_path_arg: ?[]const u8 = null; + var zig_path_arg: ?[]const u8 = null; + var lib_path_arg: ?[]const u8 = null; + var libc_runtimes_path_arg: ?[]const u8 = null; + var gdb_path_arg: ?[]const u8 = null; + var lldb_path_arg: ?[]const u8 = null; + var args: ClientArgs = .{ + .test_file = undefined, + .test_kind = undefined, + .zig_exe = undefined, + .lib_dir = undefined, + .src_dir = undefined, + .targets = .empty, + + .libc_runtimes_dir = null, + .enable_darling = false, + .enable_qemu = false, + .enable_rosetta = false, + .enable_wine = false, + .enable_wasmtime = false, + + .gdb_exe = null, + .lldb_exe = null, + + .quiet = false, + .keep_src = false, + }; + defer args.targets.deinit(init.gpa); + + var arg_it = try init.minimal.args.iterateAllocator(arena); + const self = arg_it.next().?; + while (arg_it.next()) |arg| { + if (std.mem.eql(u8, arg, "--listen=-")) { + var stdin_buffer: [512]u8 = undefined; + var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer); + var stdout_buffer: [512]u8 = undefined; + var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); + var runner: Runner = .{ + .gpa = init.gpa, + .arena = init.arena, + .environ_map = init.minimal.environ.createMap(init.gpa) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => |e| fatal("unable to get environment: {t}", .{e}), + }, + .io = init.io, + .prog_node = std.Progress.start(init.io, .{}), + .args = .{ + .@"test" = null, + .manifest_file = null, + .zig_exe = null, + .lib_dir = null, + .src_dir = null, + .targets = .empty, + .target_bytes = .empty, + + .libc_runtimes_dir = null, + .enable_darling = false, + .enable_qemu = false, + .enable_rosetta = false, + .enable_wasmtime = false, + .enable_wine = false, + + .gdb_exe = null, + .lldb_exe = null, + + .quiet = false, + }, + .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to resolve host target: {t}", .{e}), + }, + .server = .{ + .in = &stdin.interface, + .out = &stdout.interface, + }, + .eb_wip = try .init(init.gpa), + }; + defer runner.deinit(); + switch (runner.runServer()) { + error.Canceled, error.OutOfMemory => |e| return e, + error.ServerReadFailed => switch (stdin.err.?) { + error.Canceled => |e| return e, + else => |e| fatal("unable to receive message: {t}", .{e}), + }, + error.ServerWriteFailed => switch (stdout.err.?) { + error.Canceled => |e| return e, + else => |e| fatal("unable to send message: {t}", .{e}), + }, + error.ServerEndOfStream => fatal("no more messages", .{}), + error.AlreadyReported => { + var eb = try runner.eb_wip.toOwnedBundle(""); + defer eb.deinit(init.gpa); + eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { + error.Canceled => |e| return e, + else => {}, + }; + std.process.exit(1); + }, + } + } else if (std.mem.eql(u8, arg, "--zig")) { + zig_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); + } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { + zig_path_arg = zig_path; + } else if (std.mem.eql(u8, arg, "--lib")) { + lib_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); + } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { + lib_path_arg = lib_path; + } else if (std.mem.eql(u8, arg, "--target")) { + try args.targets.append( + init.gpa, + try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after {q}", .{arg})), + ); + } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { + try args.targets.append(init.gpa, try arena.dupe(u8, target)); + } else if (std.mem.eql(u8, arg, "--libc-runtimes")) { + libc_runtimes_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); + } else if (std.mem.cutPrefix(u8, arg, "--libc-runtimes=")) |libc_runtimes_path| { + libc_runtimes_path_arg = libc_runtimes_path; + } else if (std.mem.eql(u8, arg, "-fdarling")) { + args.enable_darling = true; + } else if (std.mem.eql(u8, arg, "-fqemu")) { + args.enable_qemu = true; + } else if (std.mem.eql(u8, arg, "-frosetta")) { + args.enable_rosetta = true; + } else if (std.mem.eql(u8, arg, "-fwine")) { + args.enable_wine = true; + } else if (std.mem.eql(u8, arg, "-fwasmtime")) { + args.enable_wasmtime = true; + } else if (std.mem.eql(u8, arg, "--gdb")) { + gdb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); + } else if (std.mem.cutPrefix(u8, arg, "--gdb=")) |gdb_path| { + gdb_path_arg = gdb_path; + } else if (std.mem.eql(u8, arg, "--lldb")) { + lldb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); + } else if (std.mem.cutPrefix(u8, arg, "--lldb=")) |lldb_path| { + lldb_path_arg = lldb_path; + } else if (std.mem.eql(u8, arg, "--quiet")) { + args.quiet = true; + } else if (std.mem.eql(u8, arg, "--keep-src")) { + args.keep_src = true; + } else { + if (test_path_arg) |_| fatal("unexpected arg {q}", .{arg}); + test_path_arg = arg; + } + } + + const test_path = test_path_arg orelse fatal("missing \"path/to/test\" arg", .{}); + const zig_path = zig_path_arg orelse fatal("missing \"--zig=path/to/zig\" arg", .{}); + const lib_path = lib_path_arg orelse fatal("missing \"--lib=path/to/lib\" arg", .{}); + + const cwd: std.Io.Dir = .cwd(); + args.test_file = cwd.openFile(init.io, test_path, .{ + .allow_directory = true, + }) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to open {q}: {t}", .{ test_path, e }), + }; + defer args.test_file.close(init.io); + args.test_kind = (args.test_file.stat(init.io) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to stat {q}: {t}", .{ test_path, e }), + }).kind; + args.zig_exe = cwd.openFile(init.io, zig_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to open {q}: {t}", .{ zig_path, e }), + }; + defer args.zig_exe.close(init.io); + args.lib_dir = cwd.openDir(init.io, lib_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), + }; + defer args.lib_dir.close(init.io); + if (libc_runtimes_path_arg) |libc_runtimes_path| args.libc_runtimes_dir = cwd.openDir( + init.io, + libc_runtimes_path, + .{}, + ) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), + }; + defer if (args.libc_runtimes_dir) |libc_runtimes_dir| libc_runtimes_dir.close(init.io); + if (gdb_path_arg) |gdb_path| args.gdb_exe = cwd.openFile(init.io, gdb_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to open {q}: {t}", .{ gdb_path, e }), + }; + defer if (args.gdb_exe) |gdb_exe| gdb_exe.close(init.io); + if (lldb_path_arg) |lldb_path| args.lldb_exe = cwd.openFile(init.io, lldb_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to open {q}: {t}", .{ lldb_path, e }), + }; + defer if (args.lldb_exe) |lldb_exe| lldb_exe.close(init.io); + + const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { + var rand_int: u64 = undefined; + init.io.random(@ptrCast(&rand_int)); + break :rand_int rand_int; + }); + args.src_dir = cwd.createDirPathOpen(init.io, src_dir_path, .{}) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to create {q}: {t}", .{ src_dir_path, e }), + }; + defer { + args.src_dir.close(init.io); + if (!args.keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| { + std.log.warn("unable to delete tree {q}: {t}", .{ src_dir_path, err }); + }; + } + + var inherit_dirs: std.ArrayList(std.Io.Dir) = try .initCapacity(arena, 3); + var inherit_files: std.ArrayList(std.Io.File) = try .initCapacity(arena, 4); + inherit_files.appendSliceAssumeCapacity(&.{ args.test_file, args.zig_exe }); + inherit_dirs.appendSliceAssumeCapacity(&.{ args.lib_dir, args.src_dir }); + if (args.libc_runtimes_dir) |libc_runtimes_dir| inherit_dirs.appendAssumeCapacity(libc_runtimes_dir); + if (args.gdb_exe) |gdb_exe| inherit_files.appendAssumeCapacity(gdb_exe); + if (args.lldb_exe) |lldb_exe| inherit_files.appendAssumeCapacity(lldb_exe); + var child = std.process.spawn(init.io, .{ + .argv = &.{ self, "--listen=-" }, + .stdin = .pipe, + .stdout = .pipe, + .inherit_dirs = inherit_dirs.items, + .inherit_files = inherit_files.items, + }) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, + else => |e| fatal("unable to spawn runner: {t}", .{e}), + }; + + var child_reader_buffer: [512]u8 = undefined; + var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer); + var child_writer_buffer: [512]u8 = undefined; + var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer); + var client: std.zig.Client = .{ + .in = &child_reader.interface, + .out = &child_writer.interface, + }; + const success = runClient(init.gpa, arena, init.io, &client, &args) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.ReadFailed => switch (child_reader.err.?) { + error.Canceled => |e| return e, + else => |e| fatal("unable to receive message: {t}", .{e}), + }, + error.WriteFailed => switch (child_writer.err.?) { + error.Canceled => |e| return e, + else => |e| fatal("unable to send message: {t}", .{e}), + }, + error.EndOfStream => fatal("no more messages", .{}), + }; + _ = child.wait(init.io) catch |err| switch (err) { + error.Canceled => |e| return e, + else => |e| fatal("unable to wait for runner: {t}", .{e}), + }; + return if (success) 0 else 1; +} +fn runClient( + gpa: std.mem.Allocator, + arena: std.mem.Allocator, + io: std.Io, + client: *std.zig.Client, + args: *const ClientArgs, +) (std.mem.Allocator.Error || std.Io.Reader.Error || std.Io.Writer.Error)!bool { + var stdout_buffer: [512]u8 = undefined; + var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer); + const stdout = &stdout_fw.interface; + var stderr_buffer: [512]u8 = undefined; + var stderr_fw: std.Io.File.Writer = .initStreaming(.stderr(), io, &stderr_buffer); + const stderr = &stderr_fw.interface; + + var argv: std.ArrayList(u8) = .empty; + defer argv.deinit(gpa); + const Arg = std.zig.Client.Message.Arg; + + try argv.append(gpa, @backingInt(@as(Arg, switch (args.test_kind) { + else => unreachable, + .file => .input_file, + .directory => .input_dir, + }))); + try argv.appendSlice(gpa, @ptrCast(&args.test_file.handle)); + + try argv.append(gpa, @backingInt(Arg.prefix)); + try argv.appendSlice(gpa, "--zig="); + try argv.append(gpa, 0); + try argv.append(gpa, @backingInt(Arg.input_file)); + try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle)); + + try argv.append(gpa, @backingInt(Arg.prefix)); + try argv.appendSlice(gpa, "--lib="); + try argv.append(gpa, 0); + try argv.append(gpa, @backingInt(Arg.input_dir)); + try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle)); + + try argv.append(gpa, @backingInt(Arg.prefix)); + try argv.appendSlice(gpa, "--src="); + try argv.append(gpa, 0); + try argv.append(gpa, @backingInt(Arg.output_dir)); + try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle)); + + for (args.targets.items) |target| { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "--target"); + try argv.append(gpa, 0); + + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, target); + try argv.append(gpa, 0); + } + + if (args.libc_runtimes_dir) |libc_runtimes_dir| { + try argv.append(gpa, @backingInt(Arg.prefix)); + try argv.appendSlice(gpa, "--libc-runtimes="); + try argv.append(gpa, 0); + try argv.append(gpa, @backingInt(Arg.input_dir)); + try argv.appendSlice(gpa, @ptrCast(&libc_runtimes_dir.handle)); + } + if (args.enable_darling) { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "-fdarling"); + try argv.append(gpa, 0); + } + if (args.enable_qemu) { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "-fqemu"); + try argv.append(gpa, 0); + } + if (args.enable_rosetta) { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "-frosetta"); + try argv.append(gpa, 0); + } + if (args.enable_wine) { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "-fwine"); + try argv.append(gpa, 0); + } + if (args.enable_wasmtime) { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "-fwasmtime"); + try argv.append(gpa, 0); + } + + if (args.gdb_exe) |gdb_exe| { + try argv.append(gpa, @backingInt(Arg.prefix)); + try argv.appendSlice(gpa, "--gdb="); + try argv.append(gpa, 0); + try argv.append(gpa, @backingInt(Arg.input_file)); + try argv.appendSlice(gpa, @ptrCast(&gdb_exe.handle)); + } + if (args.lldb_exe) |lldb_exe| { + try argv.append(gpa, @backingInt(Arg.prefix)); + try argv.appendSlice(gpa, "--lldb="); + try argv.append(gpa, 0); + try argv.append(gpa, @backingInt(Arg.input_file)); + try argv.appendSlice(gpa, @ptrCast(&lldb_exe.handle)); + } + + if (args.quiet) { + try argv.append(gpa, @backingInt(Arg.string)); + try argv.appendSlice(gpa, "--quiet"); + try argv.append(gpa, 0); + } + + try client.serveMessageHeader(.{ + .tag = .args, + .bytes_len = @intCast(argv.items.len), + }); + try client.out.writeAll(argv.items); + + var success = true; + var metadata: struct { + index: u32, + len: u32, + names: []u32, + expected_panic_msgs: []u32, + string_bytes: []u8, + + fn name(m: *@This()) []const u8 { + return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0); + } + + fn expectedPanicMsg(m: *@This()) ?[:0]const u8 { + return switch (m.expected_panic_msgs[m.index - 1]) { + 0 => null, + else => |offset| m.string_bytes[offset..std.mem.findScalarPos( + u8, + m.string_bytes, + offset, + 0, + ).? :0], + }; + } + + fn next(m: *@This()) ?u32 { + if (m.len - m.index == 0) return null; + defer m.index += 1; + return m.index; + } + } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} }; + try client.serveBodylessMessage(.query_test_metadata); + while (true) { + const hdr = try client.receiveMessage(); + switch (hdr.tag) { + else => try client.in.discardAll(hdr.bytes_len), + .zig_version => { + const actual_version = try client.in.take(hdr.bytes_len); + if (!std.mem.eql(u8, builtin.zig_version_string, actual_version)) { + try stderr.print( + "error: zig version mismatch compiler test runner vs compiler: {q} vs {q}\n", + .{ builtin.zig_version_string, actual_version }, + ); + try stderr.flush(); + std.process.exit(1); + } + }, + .error_bundle => { + const body = try arena.alloc(u8, hdr.bytes_len); + try client.in.readSliceAll(body); + const eb = std.zig.Server.allocErrorBundle(arena, body) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.EndOfStream => { + try stderr.writeAll("error bundle truncated\n"); + try stderr.flush(); + std.process.exit(1); + }, + }; + success = false; + eb.renderToStderr(io, .{}, .auto) catch {}; + }, + .test_metadata => { + const tm_hdr = + try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); + metadata = .{ + .index = 0, + .len = tm_hdr.tests_len, + .names = try arena.alloc(u32, tm_hdr.tests_len), + .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len), + .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len), + }; + try client.in.readSliceEndian(u32, metadata.names, .little); + try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little); + try client.in.readSliceAll(metadata.string_bytes); + try client.serveRunTest(metadata.next() orelse break); + }, + .test_results => { + const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little); + switch (tr.flags.status) { + .pass, .skip => {}, + .fail => success = false, + } + try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() }); + try stdout.flush(); + try client.serveRunTest(metadata.next() orelse break); + }, + } + } + try client.serveBodylessMessage(.exit); + return success; +} diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index b1feb5433000f68a3d54c4e4555bb07cfa75f5dd..6f521a724349c176e5ff693c521394d0fca9d73b 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -1209,3 +1209,21 @@ test "modify slice through coerced array pointer" { assert(slice[2] == 0); } } + +test "iterate empty slice of uninstantiable types" { + const E = enum {}; + var slice_enum: []const E = undefined; + slice_enum = &.{}; + for (slice_enum) |elem| { + _ = elem; + return error.ReachedUnreachable; + } + + const U = union { foo: struct { a: u8, b: noreturn }, bar: E }; + var slice_union: []const U = undefined; + slice_union = &.{}; + for (slice_union) |elem| { + _ = elem; + return error.ReachedUnreachable; + } +} diff --git a/test/cases.zig b/test/cases.zig index 834855099ed17dd16b981c22c01935bd460a7906..573a10764e5530319655a46fa3f7ec5a8c319af4 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -9,7 +9,6 @@ pub const BuildOptions = struct { llvm_has_xtensa: bool, }; -pub fn addCases(cases: *Cases, build_options: BuildOptions, b: *std.Build) !void { +pub fn addCases(cases: *Cases, b: *std.Build) !void { try @import("compile_errors.zig").addCases(cases, b); - try @import("llvm_targets.zig").addCases(cases, build_options, b); } diff --git a/test/cases/compile_errors/load_uninstantiable_type.zig b/test/cases/compile_errors/load_uninstantiable_type.zig new file mode 100644 index 0000000000000000000000000000000000000000..7b68d2330c91356a1640a115ecfe53aad68a6762 --- /dev/null +++ b/test/cases/compile_errors/load_uninstantiable_type.zig @@ -0,0 +1,48 @@ +const E = enum {}; +const U = union { + foo: struct { a: u8, b: noreturn }, + bar: E, +}; +export fn loadSliceUninstantiableEnum() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + _ = comptime slice[0]; + comptime unreachable; +} +export fn iterateSliceUninstantiableEnum() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + comptime for (slice) |elem| { + _ = elem; + }; + comptime unreachable; +} +export fn loadSliceUninstantiableUnion() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + _ = comptime slice[0]; + comptime unreachable; +} +export fn iterateSliceUninstantiableUnion() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + comptime for (slice) |elem| { + _ = elem; + }; + comptime unreachable; +} + +// error +// +// :10:23: error: cannot load uninstantiable type 'tmp.E' +// :1:11: note: enum declared here +// :17:19: error: cannot load uninstantiable type 'tmp.E' +// :1:11: note: enum declared here +// :26:23: error: cannot load uninstantiable type 'tmp.U' +// :2:11: note: union declared here +// :33:19: error: cannot load uninstantiable type 'tmp.U' +// :2:11: note: union declared here diff --git a/test/cases/safety/iterate_slice_uninstantiable_enum.zig b/test/cases/safety/iterate_slice_uninstantiable_enum.zig new file mode 100644 index 0000000000000000000000000000000000000000..09093930e66113bfe3b51ab34633d59fcae74457 --- /dev/null +++ b/test/cases/safety/iterate_slice_uninstantiable_enum.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const E = enum {}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + for (slice) |elem| _ = elem; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/iterate_slice_uninstantiable_union.zig b/test/cases/safety/iterate_slice_uninstantiable_union.zig new file mode 100644 index 0000000000000000000000000000000000000000..b6841dbc7005fbd15e73deb183228ad6850e8f4f --- /dev/null +++ b/test/cases/safety/iterate_slice_uninstantiable_union.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const U = union { + foo: struct { a: u8, b: noreturn }, + bar: enum {}, +}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + for (slice) |elem| _ = elem; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_slice_uninstantiable_enum.zig b/test/cases/safety/load_slice_uninstantiable_enum.zig new file mode 100644 index 0000000000000000000000000000000000000000..57c308f8f6b15ca4f61eb36a3da0c246b6831dd8 --- /dev/null +++ b/test/cases/safety/load_slice_uninstantiable_enum.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const E = enum {}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + _ = slice[0]; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_slice_uninstantiable_union.zig b/test/cases/safety/load_slice_uninstantiable_union.zig new file mode 100644 index 0000000000000000000000000000000000000000..1f7264526ed4518198a3c797110515971ac2dbc1 --- /dev/null +++ b/test/cases/safety/load_slice_uninstantiable_union.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const U = union { + foo: struct { a: u8, b: noreturn }, + bar: enum {}, +}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + _ = slice[0]; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_uninstantiable_enum_from_slice.zig b/test/cases/safety/load_uninstantiable_enum_from_slice.zig deleted file mode 100644 index 57c308f8f6b15ca4f61eb36a3da0c246b6831dd8..0000000000000000000000000000000000000000 --- a/test/cases/safety/load_uninstantiable_enum_from_slice.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { - std.process.exit(0); - } - std.process.exit(1); -} - -const E = enum {}; -pub fn main() error{TestFailed}!void { - const bytes: [32]u8 = @splat(0); - const ptr: *const [1]E = @ptrCast(&bytes); - const slice: []const E = ptr; - _ = slice[0]; - return error.TestFailed; -} -// run -// backend=selfhosted,llvm -// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_uninstantiable_union.zig b/test/cases/safety/load_uninstantiable_union.zig index 87f84e07c7f624104d797b94037fc6148491265b..9e673d3958bd8ec11c39033f0d5e3688ddaca79e 100644 --- a/test/cases/safety/load_uninstantiable_union.zig +++ b/test/cases/safety/load_uninstantiable_union.zig @@ -9,7 +9,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usi } const U = union { - foo: struct { a: u8, b: noreturn, }, + foo: struct { a: u8, b: noreturn }, bar: enum {}, }; pub fn main() error{TestFailed}!void { diff --git a/test/cases/safety/load_uninstantiable_union_from_slice.zig b/test/cases/safety/load_uninstantiable_union_from_slice.zig deleted file mode 100644 index 844bfd0b7d766bfb9e5eb0f911061eda7bace0d6..0000000000000000000000000000000000000000 --- a/test/cases/safety/load_uninstantiable_union_from_slice.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { - std.process.exit(0); - } - std.process.exit(1); -} - -const U = union { - foo: struct { a: u8, b: noreturn, }, - bar: enum {}, -}; -pub fn main() error{TestFailed}!void { - const bytes: [32]u8 = @splat(0); - const ptr: *const [1]U = @ptrCast(&bytes); - const slice: []const U = ptr; - _ = slice[0]; - return error.TestFailed; -} -// run -// backend=selfhosted,llvm -// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/debugger/basic/manifest b/test/debugger/basic/manifest index bddaec73e0f6d7e5ab469b041e0b5f229937fefb..79beee569dc10bb57f7fd99675f8b33c29055a48 100644 --- a/test/debugger/basic/manifest +++ b/test/debugger/basic/manifest @@ -3,6 +3,6 @@ -Mroot=basic.zig #} -#write basic.zig basic.zig +#write basic.zig @basic.zig #update -#check lldb lldb +#check lldb @lldb diff --git a/test/debugger/enums b/test/debugger/enums index d6cbcf24334ee9ea2122cb88143139c22f59d701..10888bfe4ee60a51d7b04d3719c97c71f1dfa42a 100644 --- a/test/debugger/enums +++ b/test/debugger/enums @@ -1,10 +1,8 @@ -#todo { -passes, but prints errors -#} -#skip x86_64-linux-none-whole-selfhosted-new-nopic -#skip x86_64-linux-none-whole-selfhosted-new-pic -#skip x86_64-linux-none-incremental-selfhosted-new-nopic -#skip x86_64-linux-none-incremental-selfhosted-new-pic +#todo "passes, but prints errors" +#skip x86_64-linux-none-baseline-whole-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-whole-selfhosted-new-pic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-pic #exe { --name enums diff --git a/test/debugger/errors b/test/debugger/errors index 156118c3addabd6b5ce13fe8bbdcb3a3dd580c92..4d8eadbd4098275511016d87a7d6705264f973eb 100644 --- a/test/debugger/errors +++ b/test/debugger/errors @@ -1,10 +1,8 @@ -#todo { -passes, but prints errors -#} -#skip x86_64-linux-none-whole-selfhosted-new-nopic -#skip x86_64-linux-none-whole-selfhosted-new-pic -#skip x86_64-linux-none-incremental-selfhosted-new-nopic -#skip x86_64-linux-none-incremental-selfhosted-new-pic +#todo "passes, but prints errors" +#skip x86_64-linux-none-baseline-whole-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-whole-selfhosted-new-pic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-pic #exe { --name errors diff --git a/test/debugger/inline_call/manifest b/test/debugger/inline_call/manifest index 77b906f21de307fc4e2219ec965df5f08d3b5a19..ba99272bea5103cb01277ea4a4903a798487a24e 100644 --- a/test/debugger/inline_call/manifest +++ b/test/debugger/inline_call/manifest @@ -1,8 +1,6 @@ -#todo { -implement inline line info in lldb -#} -#skip x86_64-linux-none-incremental-selfhosted-new-nopic -#skip x86_64-linux-none-incremental-selfhosted-new-pic +#todo "implement inline line info in lldb" +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-pic #exe { --name inline_call @@ -10,9 +8,9 @@ implement inline line info in lldb -Mmodule=mod0.zig #} -#write root0.zig root0.zig -#write root1.zig root1.zig -#write mod0.zig mod0.zig -#write mod1.zig mod1.zig +#write root0.zig @root0.zig +#write root1.zig @root1.zig +#write mod0.zig @mod0.zig +#write mod1.zig @mod1.zig #update -#check lldb lldb +#check lldb @lldb diff --git a/test/debugger/matrix.zon b/test/debugger/matrix.zon new file mode 100644 index 0000000000000000000000000000000000000000..fb9bc41d4ba88ae453950a6fe5b21ba4efae67e2 --- /dev/null +++ b/test/debugger/matrix.zon @@ -0,0 +1,56 @@ +.{ + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .whole, + .backend = .selfhosted, + .linker = .old, + .pic = .nopic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .whole, + .backend = .selfhosted, + .linker = .old, + .pic = .pic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .whole, + .backend = .selfhosted, + .linker = .new, + .pic = .nopic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .whole, + .backend = .selfhosted, + .linker = .new, + .pic = .pic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .incremental, + .backend = .selfhosted, + .linker = .new, + .pic = .nopic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .incremental, + .backend = .selfhosted, + .linker = .new, + .pic = .pic, + }, +} diff --git a/test/debugger/multi_array_list/manifest b/test/debugger/multi_array_list/manifest index 2503522bee1531df3cd11ce44cfec94b2510def5..233cff5ffd14881cd2c1104ced977e0e8c05beb8 100644 --- a/test/debugger/multi_array_list/manifest +++ b/test/debugger/multi_array_list/manifest @@ -3,6 +3,6 @@ -Mroot=main.zig #} -#write main.zig main.zig +#write main.zig @main.zig #update -#check lldb lldb +#check lldb @lldb diff --git a/test/debugger/step_single_stmt_loops/manifest b/test/debugger/step_single_stmt_loops/manifest index d7284fe12466167a022443945763760b7655e352..abbda7d27e5fd6b8d34ac9da6922d5476a3bc4a7 100644 --- a/test/debugger/step_single_stmt_loops/manifest +++ b/test/debugger/step_single_stmt_loops/manifest @@ -3,6 +3,6 @@ -Mroot=step_single_stmt_loops.zig #} -#write step_single_stmt_loops.zig step_single_stmt_loops.zig +#write step_single_stmt_loops.zig @step_single_stmt_loops.zig #update -#check lldb lldb +#check lldb @lldb diff --git a/test/debugger/unions b/test/debugger/unions index 435b8ec7cf02c239392c7867d0489d8b7eb26e66..f999ee37135bcfcf027bcd1573b4d322a2fb12c3 100644 --- a/test/debugger/unions +++ b/test/debugger/unions @@ -1,10 +1,8 @@ -#todo { -passes, but prints errors -#} -#skip x86_64-linux-none-whole-selfhosted-new-nopic -#skip x86_64-linux-none-whole-selfhosted-new-pic -#skip x86_64-linux-none-incremental-selfhosted-new-nopic -#skip x86_64-linux-none-incremental-selfhosted-new-pic +#todo "passes, but prints errors" +#skip x86_64-linux-none-baseline-whole-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-whole-selfhosted-new-pic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-nopic +#skip x86_64-linux-none-baseline-incremental-selfhosted-new-pic #exe { --name unions diff --git a/test/incremental/add_decl b/test/incremental/add_decl index 5832015217c571a79a2bc5ca0ab45abf91d7a156..27a5e921dfccf9cd098f9aefee14ae7f6850ebfa 100644 --- a/test/incremental/add_decl +++ b/test/incremental/add_decl @@ -24,8 +24,7 @@ const foo = "good morning\n"; const bar = "good evening\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add new declaration#} +#update "add new declaration" #check stdout { good morning #} @@ -39,8 +38,7 @@ const foo = "good morning\n"; const bar = "good evening\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -reference new declaration#} +#update "reference new declaration" #check stdout { good evening #} @@ -54,8 +52,7 @@ const foo = "good morning\n"; const bar = "good evening\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -reference missing declaration#} +#update "reference missing declaration" #check errors { main.zig:3:52: error: use of undeclared identifier 'qux' #} @@ -70,8 +67,7 @@ const bar = "good evening\n"; const qux = "good night\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add missing declaration#} +#update "add missing declaration" #check stdout { good night #} @@ -84,8 +80,7 @@ pub fn main() !void { const qux = "good night\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove unused declarations#} +#update "remove unused declarations" #check stdout { good night #} diff --git a/test/incremental/add_decl_namespaced b/test/incremental/add_decl_namespaced index 30bf6a81e9abb3449ad1575a20022b7d6f0d279f..30314032b97331b5f6f3f7c362fb90a128f1a8ca 100644 --- a/test/incremental/add_decl_namespaced +++ b/test/incremental/add_decl_namespaced @@ -24,8 +24,7 @@ const foo = "good morning\n"; const bar = "good evening\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add new declaration#} +#update "add new declaration" #check stdout { good morning #} @@ -39,8 +38,7 @@ const foo = "good morning\n"; const bar = "good evening\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -reference new declaration#} +#update "reference new declaration" #check stdout { good evening #} @@ -54,8 +52,7 @@ const foo = "good morning\n"; const bar = "good evening\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -reference missing declaration#} +#update "reference missing declaration" #check errors { main.zig:3:59: error: root source file struct 'main' has no member named 'qux' main.zig:1:1: note: struct declared here @@ -71,8 +68,7 @@ const bar = "good evening\n"; const qux = "good night\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add missing declaration#} +#update "add missing declaration" #check stdout { good night #} @@ -85,8 +81,7 @@ pub fn main() !void { const qux = "good night\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove unused declarations#} +#update "remove unused declarations" #check stdout { good night #} diff --git a/test/incremental/add_decls_around_bad_field_expr b/test/incremental/add_decls_around_bad_field_expr index b084be50e0df8e4ee192f39c658595baff996a4d..539ba9bf3dd1d4cd7d276184cc3ad499af1bfca3 100644 --- a/test/incremental/add_decls_around_bad_field_expr +++ b/test/incremental/add_decls_around_bad_field_expr @@ -32,8 +32,7 @@ pub fn main() void { _ = @as(@This(), undefined); } #} -#update { -add more decls around error#} +#update "add more decls around error" #check errors { main.zig:4:15: error: root source file struct 'main' has no member named 'does_not_exist' main.zig:1:1: note: struct declared here diff --git a/test/incremental/add_field_and_nested_struct_uses_changed_decl b/test/incremental/add_field_and_nested_struct_uses_changed_decl index 731d34837bc82b9b3ffac774943f53ffad77d468..acabf972c4e3ba0470e2f2be83f262a69a70041b 100644 --- a/test/incremental/add_field_and_nested_struct_uses_changed_decl +++ b/test/incremental/add_field_and_nested_struct_uses_changed_decl @@ -14,8 +14,7 @@ const S = struct { }; #} #update -#check stdout { -#} +#check stdout #write main.zig { pub fn main() void { @@ -29,7 +28,5 @@ const S = struct { bar: u32, }; #} -#update { -add field to outer struct, change decl used by inner struct#} -#check stdout { -#} +#update "add field to outer struct, change decl used by inner struct" +#check stdout diff --git a/test/incremental/add_remove_struct_fields/manifest b/test/incremental/add_remove_struct_fields/manifest index 2099e73cef2d89db7446cfe1c9f3a82057332248..b0b067f9c212b7de14af1b54031ed03a62eecb05 100644 --- a/test/incremental/add_remove_struct_fields/manifest +++ b/test/incremental/add_remove_struct_fields/manifest @@ -2,30 +2,27 @@ -Mroot=main.zig #} -#write main.zig 0/main.zig +#write main.zig @0/main.zig #update #check stdout { 1 100 #} -#write main.zig 1/main.zig -#update { -add a field#} +#write main.zig @1/main.zig +#update "add a field" #check stdout { 2 100 #} -#write main.zig 2/main.zig -#update { -remove all fields#} +#write main.zig @2/main.zig +#update "remove all fields" #check errors { main.zig:15:24: error: no field named 'x' in struct 'main.S' main.zig:1:11: note: struct declared here #} -#write main.zig 3/main.zig -#update { -remove reference to non-existent field#} +#write main.zig @3/main.zig +#update "remove reference to non-existent field" #check stdout { 0 #} diff --git a/test/incremental/add_remove_toplevel_fields/manifest b/test/incremental/add_remove_toplevel_fields/manifest index 569ee7527b51e75d6a214d260b4206b1c5dd2974..c991cd84b336d667312b14ed9c39783a1a97d10b 100644 --- a/test/incremental/add_remove_toplevel_fields/manifest +++ b/test/incremental/add_remove_toplevel_fields/manifest @@ -2,30 +2,27 @@ -Mroot=main.zig #} -#write main.zig 0/main.zig +#write main.zig @0/main.zig #update #check stdout { 1 100 #} -#write main.zig 1/main.zig -#update { -add a field#} +#write main.zig @1/main.zig +#update "add a field" #check stdout { 2 100 #} -#write main.zig 2/main.zig -#update { -remove all fields#} +#write main.zig @2/main.zig +#update "remove all fields" #check errors { main.zig:15:24: error: no field named 'x' in struct 'main' main.zig:1:1: note: struct declared here #} -#write main.zig 3/main.zig -#update { -remove reference to non-existent field#} +#write main.zig @3/main.zig +#update "remove reference to non-existent field" #check stdout { 0 #} diff --git a/test/incremental/analysis_error_and_syntax_error b/test/incremental/analysis_error_and_syntax_error index 5a2246ee8a8cbc2da6512e70830d8927a676d75b..5f2d849420ca99419966333cbf999b33653cfacc 100644 --- a/test/incremental/analysis_error_and_syntax_error +++ b/test/incremental/analysis_error_and_syntax_error @@ -16,8 +16,7 @@ main.zig:2:5: error: uh oh pub fn main() !void { @compileError("uh oh"); #} -#update { -add parse error#} +#update "add parse error" #check errors { main.zig:3:1: error: expected statement, found 'EOF' #} @@ -27,8 +26,7 @@ pub fn main() !void { @compileError("uh oh"); } #} -#update { -fix parse error#} +#update "fix parse error" #check errors { main.zig:2:5: error: uh oh #} @@ -37,8 +35,7 @@ main.zig:2:5: error: uh oh pub fn main() !void { @compileError("uh oh"); #} -#update { -add parse error again#} +#update "add parse error again" #check errors { main.zig:3:1: error: expected statement, found 'EOF' #} @@ -47,8 +44,7 @@ main.zig:3:1: error: expected statement, found 'EOF' pub fn main() !void { //@compileError("uh oh"); #} -#update { -comment @compileError call#} +#update "comment @compileError call" #check errors { main.zig:3:1: error: expected statement, found 'EOF' #} @@ -58,7 +54,5 @@ pub fn main() !void { //@compileError("uh oh"); } #} -#update { -fix parse error again#} -#check stdout { -#} +#update "fix parse error again" +#check stdout diff --git a/test/incremental/bad_import b/test/incremental/bad_import index d93a9ed6309f963c310a82f0813d0905a564cc10..dcf0bbccd81a23ebab163ae4f6b342a890727504 100644 --- a/test/incremental/bad_import +++ b/test/incremental/bad_import @@ -26,8 +26,7 @@ comptime { _ = @import("this_is/not_real.zig"); } #} -#update { -change bad import#} +#update "change bad import" #check errors { this_is/not_real.zig:1:1: error: unable to load "not_real.zig": FileNotFound foo.zig:2:17: note: file imported here @@ -41,8 +40,7 @@ pub fn main() !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove import of 'foo.zig'#} +#update "remove import of 'foo.zig'" #check stdout { success #} diff --git a/test/incremental/change_bitpack_backing_int b/test/incremental/change_bitpack_backing_int index a5a2b81ce3c3df699043da2fdbd06e7718627ec8..6530ae9c66ae83e3b92085e5e9eca2d5cd3238fd 100644 --- a/test/incremental/change_bitpack_backing_int +++ b/test/incremental/change_bitpack_backing_int @@ -38,8 +38,7 @@ comptime { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -make backing types signed#} +#update "make backing types signed" #check errors { main.zig:5:5: error: found compile log statement main.zig:8:5: note: also here @@ -62,8 +61,7 @@ comptime { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -make backing types too small#} +#update "make backing types too small" #check errors { main.zig:1:20: error: backing integer bit width does not match total bit width of fields main.zig:1:27: note: backing integer 'i5' has bit width '5' @@ -87,8 +85,7 @@ comptime { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -make backing types too big#} +#update "make backing types too big" #check errors { main.zig:1:20: error: backing integer bit width does not match total bit width of fields main.zig:1:27: note: backing integer 'u10' has bit width '10' diff --git a/test/incremental/change_container_kind b/test/incremental/change_container_kind index b83abc6ed79414180e28bf393af94cb708bb377e..55b0f788db78ed1c470bca7020b4062da7f79a1b 100644 --- a/test/incremental/change_container_kind +++ b/test/incremental/change_container_kind @@ -12,8 +12,7 @@ pub fn main() void { #write foo.zig { pub const T = struct { x: u8 }; #} -#update { -T is struct#} +#update "T is struct" #check errors { main.zig:3:5: error: found compile log statement @@ -25,8 +24,7 @@ Compile Log Output: #write foo.zig { pub const T = union {}; #} -#update { -T is union#} +#update "T is union" #check errors { main.zig:3:5: error: found compile log statement @@ -38,8 +36,7 @@ Compile Log Output: #write foo.zig { pub const T = enum {}; #} -#update { -T is enum#} +#update "T is enum" #check errors { main.zig:3:5: error: found compile log statement @@ -51,8 +48,7 @@ Compile Log Output: #write foo.zig { pub const T = opaque {}; #} -#update { -T is opaque#} +#update "T is opaque" #check errors { main.zig:3:5: error: found compile log statement @@ -64,8 +60,7 @@ Compile Log Output: #write foo.zig { pub const T = packed struct {}; #} -#update { -T is packed struct#} +#update "T is packed struct" #check errors { main.zig:3:5: error: found compile log statement @@ -77,8 +72,7 @@ Compile Log Output: #write foo.zig { pub const T = packed union { x: void }; // packed unions need at least 1 field #} -#update { -T is packed union#} +#update "T is packed union" #check errors { main.zig:3:5: error: found compile log statement @@ -90,8 +84,7 @@ Compile Log Output: #write foo.zig { pub const T = extern struct {}; #} -#update { -T is extern struct#} +#update "T is extern struct" #check errors { main.zig:3:5: error: found compile log statement @@ -103,8 +96,7 @@ Compile Log Output: #write foo.zig { pub const T = extern union { x: void }; // extern unions need at least 1 field #} -#update { -T is extern union#} +#update "T is extern union" #check errors { main.zig:3:5: error: found compile log statement diff --git a/test/incremental/change_embed_file b/test/incremental/change_embed_file index d50101dc476973ab1974d4ae82fca3cebffed359..8ef936e7e6b96d8d967850c11e3748050628ccd4 100644 --- a/test/incremental/change_embed_file +++ b/test/incremental/change_embed_file @@ -21,15 +21,13 @@ Hello, World! #write string.txt { Hello again, World! #} -#update { -change file contents#} +#update "change file contents" #check stdout { Hello again, World! #} #delete string.txt -#update { -delete file#} +#update "delete file" #check errors { main.zig:2:27: error: failed opening "string.txt": FileNotFound #} @@ -42,8 +40,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove reference to file#} +#update "remove reference to file" #check stdout { a hardcoded string #} @@ -56,8 +53,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -re-introduce reference to file#} +#update "re-introduce reference to file" #check errors { main.zig:2:27: error: failed opening "string.txt": FileNotFound #} @@ -65,8 +61,7 @@ main.zig:2:27: error: failed opening "string.txt": FileNotFound #write string.txt { We're back, World! #} -#update { -recreate file#} +#update "recreate file" #check stdout { We're back, World! #} diff --git a/test/incremental/change_enum_tag_type/manifest b/test/incremental/change_enum_tag_type/manifest index 682c83a67ed4a3d9ced7ba413b3c8e2ddfab4559..e26e630b28561e1339b22813e0ea99e760c79d91 100644 --- a/test/incremental/change_enum_tag_type/manifest +++ b/test/incremental/change_enum_tag_type/manifest @@ -2,29 +2,26 @@ -Mroot=main.zig #} -#write main.zig 0/main.zig +#write main.zig @0/main.zig #update #check stdout { a #} -#write main.zig 1/main.zig -#update { -too many enum fields#} +#write main.zig @1/main.zig +#update "too many enum fields" #check errors { main.zig:7:5: error: enum tag value '4' too large for type 'u2' #} -#write main.zig 2/main.zig -#update { -increase tag size#} +#write main.zig @2/main.zig +#update "increase tag size" #check stdout { a #} -#write main.zig 3/main.zig -#update { -specify tag directly#} +#write main.zig @3/main.zig +#update "specify tag directly" #check errors { main.zig:9:5: error: found compile log statement @@ -32,9 +29,8 @@ Compile Log Output: @as(type, u3) #} -#write main.zig 4/main.zig -#update { -change directly-specified tag type#} +#write main.zig @4/main.zig +#update "change directly-specified tag type" #check errors { main.zig:9:5: error: found compile log statement diff --git a/test/incremental/change_exports/manifest b/test/incremental/change_exports/manifest index 41716cd32cadf13dca51971fbab9703bc1530ade..9d38a16b9d18a1a07c11f23b765a68eae510c7e0 100644 --- a/test/incremental/change_exports/manifest +++ b/test/incremental/change_exports/manifest @@ -2,50 +2,43 @@ -Mroot=main.zig #} -#write main.zig 0/main.zig +#write main.zig @0/main.zig #update #check stdout { 123 #} -#write main.zig 1/main.zig -#update { -add conflict#} +#write main.zig @1/main.zig +#update "add conflict" #check errors { main.zig:6:5: error: exported symbol collision: foo main.zig:1:1: note: other symbol here #} -#write main.zig 2/main.zig -#update { -resolve conflict#} +#write main.zig @2/main.zig +#update "resolve conflict" #check stdout { 123 456 #} -#write main.zig 3/main.zig -#update { -put exports in decl#} +#write main.zig @3/main.zig +#update "put exports in decl" #check stdout { 123 456 #} -#write main.zig 4/main.zig -#update { -remove reference to exporting decl#} -#check stdout { -#} +#write main.zig @4/main.zig +#update "remove reference to exporting decl" +#check stdout -#write main.zig 5/main.zig -#update { -mark consts as export#} +#write main.zig @5/main.zig +#update "mark consts as export" #check stdout { 123 456 #} -#write main.zig 6/main.zig -#update { -reintroduce reference to exporting decl, introducing conflict#} +#write main.zig @6/main.zig +#update "reintroduce reference to exporting decl, introducing conflict" #check errors { main.zig:5:5: error: exported symbol collision: bar main.zig:2:1: note: other symbol here diff --git a/test/incremental/change_fn_type b/test/incremental/change_fn_type index b16811fbc937aa183fd80bfe7b0eb59690e089b7..448050823cba202d0d4cc2b73f855472e7e53674 100644 --- a/test/incremental/change_fn_type +++ b/test/incremental/change_fn_type @@ -29,8 +29,7 @@ fn foo(x: i64) !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change function type#} +#update "change function type" #check stdout { 123 #} @@ -46,8 +45,7 @@ fn foo(x: i64) !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change function argument#} +#update "change function argument" #check stdout { -42 #} diff --git a/test/incremental/change_generic_line_number b/test/incremental/change_generic_line_number index cda09859f9c849649b5b40c66d9d16e94101cf34..561fc8b59cf326545a17288f8c96e59148d796f0 100644 --- a/test/incremental/change_generic_line_number +++ b/test/incremental/change_generic_line_number @@ -39,8 +39,7 @@ pub fn main() !void { try Printer("bar\n").print(); } #} -#update { -change line number#} +#update "change line number" #check stdout { foo bar diff --git a/test/incremental/change_line_number b/test/incremental/change_line_number index ed8e24b009356b452dd71d17d7ca510e0df285d2..a6ec89fb4f80da9697bf660fc65695abb31a7ad1 100644 --- a/test/incremental/change_line_number +++ b/test/incremental/change_line_number @@ -22,8 +22,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change line number#} +#update "change line number" #check stdout { foo #} diff --git a/test/incremental/change_module b/test/incremental/change_module index 4c24df6e4dd2c3f410d2d0a555e5a05a2d94d592..a1fc17b6777e5113d0f3bfbd169b9fb9b2627f01 100644 --- a/test/incremental/change_module +++ b/test/incremental/change_module @@ -41,8 +41,7 @@ comptime { //_ = @import("other.zig"); } #} -#update { -change module of other.zig#} +#update "change module of other.zig" #check errors { other.zig:2:5: error: found compile log statement @@ -61,8 +60,7 @@ comptime { _ = @import("other.zig"); } #} -#update { -put other.zig in both modules#} +#update "put other.zig in both modules" #check errors { other.zig:1:1: error: file exists in modules 'root' and 'foo' other.zig:1:1: note: files must belong to only one module @@ -81,7 +79,5 @@ comptime { //_ = @import("other.zig"); } #} -#update { -put other.zig in no modules#} -#check stdout { -#} +#update "put other.zig in no modules" +#check stdout diff --git a/test/incremental/change_panic_handler b/test/incremental/change_panic_handler index 3407de9bade926e20542b9342c91e2d223a019da..67abb5aac76dddfe33c6b6206107772227805f9d 100644 --- a/test/incremental/change_panic_handler +++ b/test/incremental/change_panic_handler @@ -39,8 +39,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change the panic handler body#} +#update "change the panic handler body" #check stdout { new panic message: integer overflow #} @@ -61,8 +60,7 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change the panic handler function value#} +#update "change the panic handler function value" #check stdout { third panic message: integer overflow #} diff --git a/test/incremental/change_panic_handler_explicit/manifest b/test/incremental/change_panic_handler_explicit/manifest index 3388f912ec3c0f6f111bf0750283c4c94a2b720f..201b78dc20cda57f266ddf66facd3bd359320d20 100644 --- a/test/incremental/change_panic_handler_explicit/manifest +++ b/test/incremental/change_panic_handler_explicit/manifest @@ -2,22 +2,20 @@ -Mroot=main.zig #} -#write main.zig 0/main.zig +#write main.zig @0/main.zig #update #check stdout { panic message: integer overflow #} -#write main.zig 1/main.zig -#update { -change the panic handler body#} +#write main.zig @1/main.zig +#update "change the panic handler body" #check stdout { new panic message: integer overflow #} -#write main.zig 2/main.zig -#update { -change the panic handler function value#} +#write main.zig @2/main.zig +#update "change the panic handler function value" #check stdout { third panic message: integer overflow #} diff --git a/test/incremental/change_reify_struct_field_type b/test/incremental/change_reify_struct_field_type index 92a41faee42808ef4b2338a1bdac241507e659fe..808fb39b701a603fb39d1c6f93f18585a51e1cf2 100644 --- a/test/incremental/change_reify_struct_field_type +++ b/test/incremental/change_reify_struct_field_type @@ -13,8 +13,7 @@ const Enum = enum { foo, bar }; const std = @import("std"); #} #update -#check stdout { -foo#} +#check stdout "foo" #write main.zig { fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{Enum}, &.{.{}})) Enum { @@ -26,7 +25,5 @@ pub fn main(init: std.process.Init) !void { const Enum = enum { foo, bar }; const std = @import("std"); #} -#update { -change field type#} -#check stdout { -bar#} +#update "change field type" +#check stdout "bar" diff --git a/test/incremental/change_shift_op b/test/incremental/change_shift_op index bcd0ff1d2f61b4aeb305a350bc64a8b64b76fbed..85160205dc368156fb1aa9860d7d03f1e6ce2038 100644 --- a/test/incremental/change_shift_op +++ b/test/incremental/change_shift_op @@ -29,8 +29,7 @@ fn foo(x: u16) !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change to right shift#} +#update "change to right shift" #check stdout { 0x130 #} diff --git a/test/incremental/change_struct_same_fields b/test/incremental/change_struct_same_fields index 348a443dd555223482c96f6ae0ccd7ed5a48c459..a8a4533ef420b1922dbf12322ee6e62e238ea497 100644 --- a/test/incremental/change_struct_same_fields +++ b/test/incremental/change_struct_same_fields @@ -39,8 +39,7 @@ fn foo(val: *const S) !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change struct layout#} +#update "change struct layout" #check stdout { 100 200 #} @@ -61,8 +60,7 @@ fn foo(val: *const S) !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change values#} +#update "change values" #check stdout { 1234 5678 #} diff --git a/test/incremental/change_union_tag_type b/test/incremental/change_union_tag_type index dbbbc94c1e9bebbfdd1b98743de8a85763e725a5..8ec3714735fd783b31a3e1e4e70f47e169b7b129 100644 --- a/test/incremental/change_union_tag_type +++ b/test/incremental/change_union_tag_type @@ -29,8 +29,7 @@ pub fn main(init: std.process.Init) !void { } const std = @import("std"); #} -#update { -change tag type#} +#update "change tag type" #check errors { main.zig:3:24: error: no field named 'a' in enum 'main.B' main.zig:2:11: note: enum declared here @@ -47,8 +46,7 @@ pub fn main(init: std.process.Init) !void { } const std = @import("std"); #} -#update { -change field name to match new tag type#} +#update "change field name to match new tag type" #check stdout { b #} diff --git a/test/incremental/change_zon_file b/test/incremental/change_zon_file index 53b7c82c84703475b71d4495a716041858d27cee..5082d01e796df554957f7c280aba8d55d8913e0e 100644 --- a/test/incremental/change_zon_file +++ b/test/incremental/change_zon_file @@ -21,15 +21,13 @@ Hello, World! #write message.zon { "Hello again, World!\n" #} -#update { -change ZON file contents#} +#update "change ZON file contents" #check stdout { Hello again, World! #} #delete message.zon -#update { -delete file#} +#update "delete file" #check errors { message.zon:1:1: error: unable to load "message.zon": FileNotFound main.zig:2:37: note: file imported here @@ -43,8 +41,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove reference to ZON file#} +#update "remove reference to ZON file" #check errors { message.zon:1:1: error: unable to load "message.zon": FileNotFound main.zig:2:37: note: file imported here @@ -53,8 +50,7 @@ main.zig:2:37: note: file imported here #write message.zon { "We're back, World!\n" #} -#update { -recreate ZON file#} +#update "recreate ZON file" #check stdout { a hardcoded string #} @@ -67,8 +63,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -re-introduce reference to ZON file#} +#update "re-introduce reference to ZON file" #check stdout { We're back, World! #} diff --git a/test/incremental/change_zon_file_no_result_type b/test/incremental/change_zon_file_no_result_type index dd63226cbaa21d113d2ff5f7f43484d616a5d956..201c5dfbde118d39f731ae3b3127431bfa25d868 100644 --- a/test/incremental/change_zon_file_no_result_type +++ b/test/incremental/change_zon_file_no_result_type @@ -26,8 +26,7 @@ Hello, World! .b_number = 0, } #} -#update { -change ZON file#} +#update "change ZON file" #check stdout { Hello again, World! #} diff --git a/test/incremental/compile_error_then_log b/test/incremental/compile_error_then_log index 44dd98a1734dc9fdc2c5618fa58d8fdcceea8ce9..867ad231eb1c8ba756007266ccbd374eaa30940c 100644 --- a/test/incremental/compile_error_then_log +++ b/test/incremental/compile_error_then_log @@ -11,8 +11,7 @@ comptime { @compileLog("this is a log"); } #} -#update { -compile error#} +#update "compile error" #check errors { main.zig:3:5: error: this is an error @@ -29,8 +28,7 @@ comptime { @compileLog("this is a log"); } #} -#update { -remove the compile error#} +#update "remove the compile error" #check errors { main.zig:6:5: error: found compile log statement diff --git a/test/incremental/compile_log b/test/incremental/compile_log index d4445f3ca80aaac2262e70b94ec2f8acaf751603..ec2dd9e1065f437e5d3a1f31d9b2744a31b78e7d 100644 --- a/test/incremental/compile_log +++ b/test/incremental/compile_log @@ -9,8 +9,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -no compile log#} +#update "no compile log" #check stdout { Hello, World! #} @@ -23,8 +22,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add compile log#} +#update "add compile log" #check errors { main.zig:4:5: error: found compile log statement @@ -39,8 +37,7 @@ pub fn main() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove compile log#} +#update "remove compile log" #check stdout { Hello, World! #} diff --git a/test/incremental/delete_comptime_decls b/test/incremental/delete_comptime_decls index ffae08f3d8d7a26173aefdb4c398895eb26d420a..ff13f6c8a77146890361297218d155eeedc47b67 100644 --- a/test/incremental/delete_comptime_decls +++ b/test/incremental/delete_comptime_decls @@ -44,8 +44,7 @@ comptime { _ = y; } #} -#update { -delete and modify comptime decls#} +#update "delete and modify comptime decls" #check errors { main.zig:6:16: error: slice of null pointer #} diff --git a/test/incremental/dependency_on_type_of_inferred_global b/test/incremental/dependency_on_type_of_inferred_global index 73d36a7254759c0f98720617aaf3848cc8703070..517424ed02ba2b4ebb3571797f381d68a673a4f8 100644 --- a/test/incremental/dependency_on_type_of_inferred_global +++ b/test/incremental/dependency_on_type_of_inferred_global @@ -15,8 +15,7 @@ comptime { pub fn main() void {} #} #update -#check stdout { -#} +#check stdout #write main.zig { const foo = @as(u16, 123); @@ -30,8 +29,7 @@ comptime { } pub fn main() void {} #} -#update { -change the type#} +#update "change the type" #check errors { main.zig:8:37: error: reached unreachable code #} diff --git a/test/incremental/do_nothing b/test/incremental/do_nothing index 213f7f1892d8fa96dbb9313054d862513956ce66..ace818eacc418576e6fb787f736a29070ebf0967 100644 --- a/test/incremental/do_nothing +++ b/test/incremental/do_nothing @@ -1,6 +1,4 @@ -#todo { -it'd be great if we could actually check that no analysis happened! -#} +#todo "it'd be great if we could actually check that no analysis happened!" #exe { -Mroot=main.zig #} @@ -25,8 +23,7 @@ const O = opaque { }; #} #update -#check stdout { -#} +#check stdout #write main.zig { pub fn main() void { @@ -47,7 +44,5 @@ const O = opaque { } }; #} -#update { -do literally nothing#} -#check stdout { -#} +#update "do literally nothing" +#check stdout diff --git a/test/incremental/fix_astgen_failure b/test/incremental/fix_astgen_failure index feb078359a513becff249d9e5e89a88a2e243352..df25736b059af01381b17f12e8d9f6eceba3a163 100644 --- a/test/incremental/fix_astgen_failure +++ b/test/incremental/fix_astgen_failure @@ -12,8 +12,7 @@ pub fn hello() !void { try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); } #} -#update { -error#} +#update "error" #check errors { foo.zig:2:9: error: use of undeclared identifier 'std' #} @@ -25,8 +24,7 @@ pub fn hello() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -fix the error#} +#update "fix the error" #check stdout { Hello, World! #} @@ -38,8 +36,7 @@ pub fn hello() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add new error#} +#update "add new error" #check errors { foo.zig:3:52: error: use of undeclared identifier 'hello_str' #} @@ -52,8 +49,7 @@ pub fn hello() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -fix the new error#} +#update "fix the new error" #check stdout { Hello, World! Again! #} diff --git a/test/incremental/function_becomes_inline b/test/incremental/function_becomes_inline index 7d64be329579442c284c149e6342bcc0d3105c36..0a68a41407d4dd311b3b969bc4c157cdf90deaa3 100644 --- a/test/incremental/function_becomes_inline +++ b/test/incremental/function_becomes_inline @@ -12,8 +12,7 @@ fn foo() !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -non-inline version#} +#update "non-inline version" #check stdout { Hello, World! #} @@ -28,8 +27,7 @@ inline fn foo() !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -make function inline#} +#update "make function inline" #check stdout { Hello, World! #} @@ -44,8 +42,7 @@ inline fn foo() !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change string#} +#update "change string" #check stdout { Hello, `inline` World! #} diff --git a/test/incremental/hello b/test/incremental/hello index 74a45d2a8b81afebe334968569949b6c82d4c397..8aab04829d520e382ed5bbbe156462a844a09585 100644 --- a/test/incremental/hello +++ b/test/incremental/hello @@ -21,8 +21,7 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); } #} -#update { -change the string#} +#update "change the string" #check stdout { おはようございます #} diff --git a/test/incremental/increase_array_field_length b/test/incremental/increase_array_field_length index 7b0eb9c165ff1a1b43206624569303b820d658ac..81d9d1ce8251658382c8c566241a84cc721f1e24 100644 --- a/test/incremental/increase_array_field_length +++ b/test/incremental/increase_array_field_length @@ -35,8 +35,7 @@ pub fn main(init: std.process.Init) !void { } const std = @import("std"); #} -#update { -increase array length#} +#update "increase array length" #check stdout { arr[999] = 234 #} diff --git a/test/incremental/make_decl_pub b/test/incremental/make_decl_pub index 59a54ece02b3850083fdd6c86c63d901cd346157..8055c5b7dfc9419740d46f19e50787c136eabc14 100644 --- a/test/incremental/make_decl_pub +++ b/test/incremental/make_decl_pub @@ -28,8 +28,7 @@ pub fn hello() !void { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -make hello pub#} +#update "make hello pub" #check stdout { Hello, World! #} diff --git a/test/incremental/matrix.zon b/test/incremental/matrix.zon new file mode 100644 index 0000000000000000000000000000000000000000..2f62c48bdbd44b68ab91628bd187e2321d3f2518 --- /dev/null +++ b/test/incremental/matrix.zon @@ -0,0 +1,51 @@ +.{ + // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others + // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang + // or LLVM). We just have a couple here to make sure that it works. + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .musl = .{} }, + .cache_mode = .incremental, + .backend = .cbe, + .linker = .old, + .pic = .nopic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .incremental, + .backend = .llvm, + .linker = .lld, + .pic = .nopic, + }, + + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .linux = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .incremental, + .backend = .selfhosted, + .linker = .new, + .pic = .nopic, + }, + .{ + .cpu = .{ .x86_64 = .{} }, + .os = .{ .windows = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .incremental, + .backend = .selfhosted, + .linker = .old, + .pic = .pic, + }, + .{ + .cpu = .{ .wasm32 = .{} }, + .os = .{ .wasi = .{} }, + .abi = .{ .none = .{} }, + .cache_mode = .incremental, + .backend = .selfhosted, + .linker = .old, + .pic = .nopic, + }, +} diff --git a/test/incremental/modify_inline_fn b/test/incremental/modify_inline_fn index 956a47a6da82a984edfb3cc9ebf788759c0c61a5..fba956d321e27b0d631e4473736c275f4e0f922c 100644 --- a/test/incremental/modify_inline_fn +++ b/test/incremental/modify_inline_fn @@ -29,8 +29,7 @@ inline fn getStr() []const u8 { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -change the string#} +#update "change the string" #check stdout { bar #} diff --git a/test/incremental/move_src b/test/incremental/move_src index b385a312dbb5d55b6f7605aee556abbda5f9ade4..1de952a6b9b31864ee273238aa3c5f177c30a4ef 100644 --- a/test/incremental/move_src +++ b/test/incremental/move_src @@ -36,8 +36,7 @@ fn bar() u32 { } const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -add newline#} +#update "add newline" #check stdout { 8 123 #} diff --git a/test/incremental/pointer_to_decl_with_astgen_error b/test/incremental/pointer_to_decl_with_astgen_error index 87d07ca1abe476a8bc0be5a2abfa2b127736127f..1ac7e90c460749db94e2e8e3209ee3921b75d20a 100644 --- a/test/incremental/pointer_to_decl_with_astgen_error +++ b/test/incremental/pointer_to_decl_with_astgen_error @@ -9,8 +9,7 @@ pub fn main() void { } #} #update -#check stdout { -#} +#check stdout #write main.zig { const foo = something; @@ -18,8 +17,7 @@ pub fn main() void { _ = &foo; } #} -#update { -introduce use of undeclared identifier#} +#update "introduce use of undeclared identifier" #check errors { main.zig:1:13: error: use of undeclared identifier 'something' #} @@ -30,7 +28,5 @@ pub fn main() void { _ = &foo; } #} -#update { -revert use of undeclared identifier#} -#check stdout { -#} +#update "revert use of undeclared identifier" +#check stdout diff --git a/test/incremental/recursive_function_becomes_non_recursive b/test/incremental/recursive_function_becomes_non_recursive index 442748a079c7bc0ca8a97fc726a661e86546acd1..df5bc83da9a5fd2a7376517f7a2737e51ae59d33 100644 --- a/test/incremental/recursive_function_becomes_non_recursive +++ b/test/incremental/recursive_function_becomes_non_recursive @@ -31,8 +31,7 @@ fn foo(recurse: bool) !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -eliminate recursion and change argument#} +#update "eliminate recursion and change argument" #check stdout { x==1 #} diff --git a/test/incremental/remove_enum_field b/test/incremental/remove_enum_field index 67b0db55b9d597270216eee728189fbd0a638833..049d98746163611f72e824f908016f466a5404b7 100644 --- a/test/incremental/remove_enum_field +++ b/test/incremental/remove_enum_field @@ -31,8 +31,7 @@ pub fn main() !void { const std = @import("std"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove enum field#} +#update "remove enum field" #check errors { main.zig:7:68: error: enum 'main.MyEnum' has no member named 'foo' main.zig:1:16: note: enum declared here diff --git a/test/incremental/remove_function_with_changed_callee b/test/incremental/remove_function_with_changed_callee index d0a04678922d9ef841393ad2903469baf6ed7ca6..3bfa9bca0705c4cbdfcad7690d08d81871a40c10 100644 --- a/test/incremental/remove_function_with_changed_callee +++ b/test/incremental/remove_function_with_changed_callee @@ -28,8 +28,7 @@ pub fn main() !void { } #} #update -#check stdout { -0#} +#check stdout "0" #write main.zig { //! This update must preserve `bar` `InternPool.Index` value, while changing its wasm function type. @@ -54,7 +53,5 @@ pub fn main() !void { try bar(.one); } #} -#update { -remove foo and change bar wasm function type#} -#check stdout { -1#} +#update "remove foo and change bar wasm function type" +#check stdout "1" diff --git a/test/incremental/remove_invalid_union_backing_enum b/test/incremental/remove_invalid_union_backing_enum index 8a2099afb4ebe92697ff76f773b53abb4eb8b871..f93c81b5b157b04e89d21506a6977cecfd44d0b1 100644 --- a/test/incremental/remove_invalid_union_backing_enum +++ b/test/incremental/remove_invalid_union_backing_enum @@ -33,7 +33,5 @@ pub fn main() void { _ = u; } #} -#update { -remove invalid backing enum#} -#check stdout { -#} +#update "remove invalid backing enum" +#check stdout diff --git a/test/incremental/struct_reification_becomes_anon_init b/test/incremental/struct_reification_becomes_anon_init index bd790cdeb9445aab9834c64f0a5fa683974b6d95..c317b0dd749874f22f175b79a7ce326067f21a6e 100644 --- a/test/incremental/struct_reification_becomes_anon_init +++ b/test/incremental/struct_reification_becomes_anon_init @@ -23,8 +23,7 @@ pub fn main(init: std.process.Init) !void { } const std = @import("std"); #} -#update { -change to `@TypeOf` anonymous init#} +#update "change to `@TypeOf` anonymous init" #check stdout { anon_field #} diff --git a/test/incremental/tag_name b/test/incremental/tag_name index 05991657cdbfd920ee24d094bb8db93ada706d17..d948d102f0ebdbec84de2460c29bea3d0f4f53e3 100644 --- a/test/incremental/tag_name +++ b/test/incremental/tag_name @@ -11,8 +11,7 @@ pub fn main() !void { } #} #update -#check stdout { -first#} +#check stdout "first" #write main.zig { const std = @import("std"); @@ -22,10 +21,8 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); } #} -#update { -no change#} -#check stdout { -first#} +#update "no change" +#check stdout "first" #write main.zig { const std = @import("std"); @@ -35,7 +32,5 @@ pub fn main() !void { try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); } #} -#update { -swap fields#} -#check stdout { -first#} +#update "swap fields" +#check stdout "first" diff --git a/test/incremental/temporary_analysis_error_in_generic_signature b/test/incremental/temporary_analysis_error_in_generic_signature index dbb3b6407eba276b9654775979353b53d9cea452..9b9292f4accc103d90dff97eaba552d8da9e522b 100644 --- a/test/incremental/temporary_analysis_error_in_generic_signature +++ b/test/incremental/temporary_analysis_error_in_generic_signature @@ -33,8 +33,7 @@ fn bar(comptime x: u8) @This().FooAlias { } const std = @import("std"); #} -#update { -change generic signature to use non-existent member#} +#update "change generic signature to use non-existent member" #check errors { main.zig:8:31: error: root source file struct 'main' has no member named 'FooAlias' main.zig:1:1: note: struct declared here @@ -54,8 +53,7 @@ fn bar(comptime x: u8) @This().FooAlias { } const std = @import("std"); #} -#update { -add that member, fixing the error#} +#update "add that member, fixing the error" #check stdout { Z #} diff --git a/test/incremental/temporary_parse_error b/test/incremental/temporary_parse_error index 9687ac4844b79133391f85addd09e71a44c8f6cd..033e5f581d207e24f12f000c4d35275368fbbca9 100644 --- a/test/incremental/temporary_parse_error +++ b/test/incremental/temporary_parse_error @@ -7,14 +7,12 @@ const std = @import("std"); pub fn main() !void {} #} #update -#check stdout { -#} +#check stdout #write main.zig { pub fn main() !void { #} -#update { -introduce parse error#} +#update "introduce parse error" #check errors { main.zig:2:1: error: expected statement, found 'EOF' #} @@ -22,7 +20,5 @@ main.zig:2:1: error: expected statement, found 'EOF' #write main.zig { pub fn main() !void {} #} -#update { -fix parse error#} -#check stdout { -#} +#update "fix parse error" +#check stdout diff --git a/test/incremental/type_becomes_comptime_only b/test/incremental/type_becomes_comptime_only index f174faacd788e3e5d4720c9badb1cb2e5f91c9ea..15a9d51176bca515225026f3ae85c0ef8197d6f6 100644 --- a/test/incremental/type_becomes_comptime_only +++ b/test/incremental/type_becomes_comptime_only @@ -14,8 +14,7 @@ pub fn main() void { } #} #update -#check stdout { -#} +#check stdout #write main.zig { const SomeType = comptime_int; @@ -28,10 +27,8 @@ pub fn main() void { s.foo(); } #} -#update { -make S comptime-only#} -#check stdout { -#} +#update "make S comptime-only" +#check stdout #write main.zig { const SomeType = u16; @@ -44,7 +41,5 @@ pub fn main() void { s.foo(); } #} -#update { -make S runtime again#} -#check stdout { -#} +#update "make S runtime again" +#check stdout diff --git a/test/incremental/type_dependency_loop b/test/incremental/type_dependency_loop index 85de8ae8391015b85194357423522046466cc042..1a5b1e2a49f467ebbea3420e3f8793db99628427 100644 --- a/test/incremental/type_dependency_loop +++ b/test/incremental/type_dependency_loop @@ -24,10 +24,8 @@ pub fn main() void { _ = B; } #} -#update { -remove reference to dependency loop#} -#check stdout { -#} +#update "remove reference to dependency loop" +#check stdout #write main.zig { pub const A = struct { b: B }; @@ -36,10 +34,8 @@ pub fn main() void { _ = B; } #} -#update { -change dependency loop without fixing it#} -#check stdout { -#} +#update "change dependency loop without fixing it" +#check stdout #write main.zig { pub const A = struct { b: B }; @@ -48,8 +44,7 @@ pub fn main() void { _ = @as(B, undefined); } #} -#update { -reference dependency loop again#} +#update "reference dependency loop again" #check errors { error: dependency loop with length 2 main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here @@ -64,7 +59,5 @@ pub fn main() void { _ = @as(B, undefined); } #} -#update { -fix dependency loop#} -#check stdout { -#} +#update "fix dependency loop" +#check stdout diff --git a/test/incremental/unreferenced_error b/test/incremental/unreferenced_error index 0f5f65feab5c98985d105d06db1e2086cd6f0470..fc63d66a96462524e35e8cba041fe4dd20cbc159 100644 --- a/test/incremental/unreferenced_error +++ b/test/incremental/unreferenced_error @@ -23,8 +23,7 @@ pub fn main() !void { const a = @compileError("bad a"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -introduce compile error#} +#update "introduce compile error" #check errors { main.zig:5:11: error: bad a #} @@ -38,8 +37,7 @@ const a = @compileError("bad a"); const b = "Hi there!\n"; const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -remove error reference#} +#update "remove error reference" #check stdout { Hi there! #} @@ -53,8 +51,7 @@ const a = "Back to a\n"; const b = @compileError("bad b"); const io = std.Io.Threaded.global_single_threaded.io(); #} -#update { -introduce and remove reference to error#} +#update "introduce and remove reference to error" #check stdout { Back to a #} diff --git a/test/llvm/allowzero b/test/llvm/allowzero new file mode 100644 index 0000000000000000000000000000000000000000..3883eb84115c94f32695c11c4fec99d8924c3497 --- /dev/null +++ b/test/llvm/allowzero @@ -0,0 +1,98 @@ +#skip amdgcn-amdhsa-none-baseline-whole-llvm-lld-pic +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip bpfeb-freestanding-none-baseline-whole-llvm-lld-pic + +#skip bpfel-freestanding-none-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#skip x86_64-linux-gnux32-baseline-whole-llvm-new-pic +#skip x86_64-linux-muslx32-baseline-whole-llvm-new-pic +#skip x86_64-linux-x32-baseline-whole-llvm-new-pic + +#obj { +--name test -fno-lld -fno-new-linker -ofmt=llvmir +-Mroot=test.zig +#} + +#write test.zig { +export fn entry(ptr: *allowzero i16) i16 { + return ptr.*; +} +#} +#update "load" +#check llvm { +define #...@test.entry(ptr #...%0) #...#0 #...{ +#...load i16, ptr %0#... +} + +#... +attributes #0 = {#... null_pointer_is_valid #...} +#} + +#write test.zig { +export fn entry(ptr: *allowzero i16) void { + ptr.* = 42; +} +#} +#update "store" +#check llvm { +define #...@test.entry(ptr #...%0) #...#0 #...{ +#...store i16 42, ptr %0#... +} + +#... +attributes #0 = {#... null_pointer_is_valid #...} +#} + +#write test.zig { +export fn entry(ptr: *allowzero i16) i16 { + return entry2(ptr[0..1]); +} +fn entry2(slice: []allowzero i16) i16 { + @setRuntimeSafety(false); + return slice[0]; +} +#} +#update "slice load" +#check llvm { +define #...@test.entry2(#..., ptr #...%1, #...) #...#2 #...{ +#...load i16, ptr %#... +} + +#... +attributes #2 = {#... null_pointer_is_valid #...} +#} + +#write test.zig { +export fn entry(ptr: *allowzero i16) void { + entry2(ptr[0..1]); +} +fn entry2(slice: []allowzero i16) void { + @setRuntimeSafety(false); + slice[0] = 42; +} +#} +#update "slice store" +#check llvm { +define #...@test.entry2(#..., ptr #...%1, #...) #...#2 #...{ +#...store i16 42, ptr %#... +} + +#... +attributes #2 = {#... null_pointer_is_valid #...} +#} diff --git a/test/llvm/array_to_vector b/test/llvm/array_to_vector new file mode 100644 index 0000000000000000000000000000000000000000..db4316af54b7fcd71b1f28fedd50aff961831252 --- /dev/null +++ b/test/llvm/array_to_vector @@ -0,0 +1,109 @@ +#skip amdgcn-amdhsa-none-baseline-whole-llvm-lld-pic +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip bpfeb-freestanding-none-baseline-whole-llvm-lld-pic + +#skip bpfel-freestanding-none-baseline-whole-llvm-lld-pic + +#skip loongarch32-freestanding-none-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-gnu-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-gnusf-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-none-baseline-whole-llvm-lld-pic +#skip loongarch32-uefi-none-baseline-whole-llvm-lld-pic + +#skip loongarch64-freestanding-none-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnu-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnusf-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-none-baseline-whole-llvm-lld-pic +#skip loongarch64-uefi-none-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip riscv32-freestanding-none-baseline-whole-llvm-lld-pic +#skip riscv32-linux-gnu-baseline-whole-llvm-lld-pic +#skip riscv32-linux-musl-baseline-whole-llvm-lld-pic +#skip riscv32-linux-none-baseline-whole-llvm-lld-pic +#skip riscv32-netbsd-none-baseline-whole-llvm-lld-pic +#skip riscv32-rtems-none-baseline-whole-llvm-lld-pic +#skip riscv32-uefi-none-baseline-whole-llvm-lld-pic +#skip riscv32be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip riscv64-freebsd-none-baseline-whole-llvm-lld-pic +#skip riscv64-freestanding-none-baseline-whole-llvm-lld-pic +#skip riscv64-fuchsia-none-baseline-whole-llvm-lld-pic +#skip riscv64-haiku-none-baseline-whole-llvm-lld-pic +#skip riscv64-hurd-gnu-baseline-whole-llvm-lld-pic +#skip riscv64-linux-android-baseline-whole-llvm-lld-pic +#skip riscv64-linux-gnu-baseline-whole-llvm-lld-pic +#skip riscv64-linux-musl-baseline-whole-llvm-lld-pic +#skip riscv64-linux-none-baseline-whole-llvm-lld-pic +#skip riscv64-netbsd-none-baseline-whole-llvm-lld-pic +#skip riscv64-openbsd-none-baseline-whole-llvm-lld-pic +#skip riscv64-rtems-none-baseline-whole-llvm-lld-pic +#skip riscv64-serenity-none-baseline-whole-llvm-lld-pic +#skip riscv64-uefi-none-baseline-whole-llvm-lld-pic +#skip riscv64be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#skip x86_64-linux-gnux32-baseline-whole-llvm-new-pic +#skip x86_64-linux-muslx32-baseline-whole-llvm-new-pic +#skip x86_64-linux-x32-baseline-whole-llvm-new-pic + +#obj { +--name test -fno-lld -fno-new-linker -ofmt=llvmir +-Mroot=test.zig +#} + +#write test.zig { +export fn entry(arr: *const [16]u8) @Vector(16, u8) { + return arr.*; +} +#} +#update "one vector load" +#check llvm { +define #...<16 x i8> @test.entry(#...) #...{ +#... + %1 = alloca [16 x i8], align 1#... + %3 = load <16 x i8>, ptr %1, align 1#... + ret <16 x i8> %3#... +#} + +#write test.zig { +export fn entry(a: *@Vector(8, u8), b: *@Vector(8, u8), src: [*]const u8) void { + const q: @Vector(16, u8) = src[0..16].*; + a.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 0, 1, 2, 3, 4, 5, 6, 7 }); + b.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 7, 8, 9, 10, 11, 12, 13, 14 }); +} +#} +#update "feeding overlapping shuffles" +#check llvm { +define #...void @test.entry(ptr #...%0, ptr #...%1, #...) #...{ +#... + %11 = load <16 x i8>, ptr %#... + %12 = shufflevector <16 x i8> %11, #... + store <8 x i8> %12, ptr %0, align 8#... + %13 = shufflevector <16 x i8> %11, #... + store <8 x i8> %13, ptr %1, align 8#... + ret void#... +} +#} diff --git a/test/llvm/dead_store b/test/llvm/dead_store new file mode 100644 index 0000000000000000000000000000000000000000..ec59117ad730d0cdc64af2411ab2ac0275e6c287 --- /dev/null +++ b/test/llvm/dead_store @@ -0,0 +1,65 @@ +#skip amdgcn-amdhsa-none-baseline-whole-llvm-lld-pic +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#skip x86_64-linux-gnux32-baseline-whole-llvm-new-pic +#skip x86_64-linux-muslx32-baseline-whole-llvm-new-pic +#skip x86_64-linux-x32-baseline-whole-llvm-new-pic + +#obj { +--name test -fno-lld -fno-new-linker -ofmt=llvmir +-Mroot=test.zig +#} + +#write test.zig { +export fn entry(ptr: *volatile i16) void { + ptr.* = 123; + ptr.* = 321; +} +#} +#update "volatile" +#check llvm { +define #...@test.entry(ptr #...%0) #...{ +#...store volatile i16 123, ptr %0#... +#...store volatile i16 321, ptr %0#... +} + +#} + +#write test.zig { +export fn entry(ptr: *volatile i16) void { + entry2(ptr[0..1]); +} +fn entry2(slice: []volatile i16) void { + @setRuntimeSafety(false); + slice[0] = 123; + slice[0] = 321; +} +#} +#update "volatile slice" +#check llvm { +define #...@test.entry(#...) #...{ +#... +} + +#... +define #...@test.entry2(#...) #...{ +#...store volatile i16 123, ptr %#... +#...store volatile i16 321, ptr %#... +} + +#} diff --git a/test/llvm/empty_analyze b/test/llvm/empty_analyze new file mode 100644 index 0000000000000000000000000000000000000000..83f8b9adfad2a1b79ec2adf06739cab3cee88900 --- /dev/null +++ b/test/llvm/empty_analyze @@ -0,0 +1,50 @@ +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip arm-uefi-eabi-baseline-whole-llvm-lld-pic +#skip arm-uefi-eabihf-baseline-whole-llvm-lld-pic + +#skip csky-freestanding-eabi-baseline-whole-llvm-lld-pic +#skip csky-freestanding-eabihf-baseline-whole-llvm-lld-pic +#skip csky-linux-eabi-baseline-whole-llvm-lld-pic +#skip csky-linux-eabihf-baseline-whole-llvm-lld-pic +#skip csky-linux-gnueabi-baseline-whole-llvm-lld-pic +#skip csky-linux-gnueabihf-baseline-whole-llvm-lld-pic + +#skip loongarch32-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch32-uefi-none-baseline-whole-llvm-lld-pic + +#skip loongarch64-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch64-uefi-none-baseline-whole-llvm-lld-pic + +#skip mipsel-psx-eabi-baseline-whole-llvm-lld-pic + +#skip riscv32-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv32be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip riscv64-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv64be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#obj { +--name test +-Mroot=test.zig +-fno-emit-asm -fno-emit-bin +#} + +#write test.zig +#update +#check errors diff --git a/test/llvm/empty_assembly b/test/llvm/empty_assembly new file mode 100644 index 0000000000000000000000000000000000000000..30a58733d045c6e1c9c6af10fbf8aae192e75754 --- /dev/null +++ b/test/llvm/empty_assembly @@ -0,0 +1,55 @@ +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip arm-uefi-eabi-baseline-whole-llvm-lld-pic +#skip arm-uefi-eabihf-baseline-whole-llvm-lld-pic + +#skip csky-freestanding-eabi-baseline-whole-llvm-lld-pic +#skip csky-freestanding-eabihf-baseline-whole-llvm-lld-pic +#skip csky-linux-eabi-baseline-whole-llvm-lld-pic +#skip csky-linux-eabihf-baseline-whole-llvm-lld-pic +#skip csky-linux-gnueabi-baseline-whole-llvm-lld-pic +#skip csky-linux-gnueabihf-baseline-whole-llvm-lld-pic + +#skip loongarch32-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch32-uefi-none-baseline-whole-llvm-lld-pic + +#skip loongarch64-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch64-uefi-none-baseline-whole-llvm-lld-pic + +#skip mipsel-psx-eabi-baseline-whole-llvm-lld-pic + +#skip riscv32-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv32be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip riscv64-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv64be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#skip xtensa-freestanding-call0-baseline-whole-llvm-lld-pic +#skip xtensa-freestanding-none-baseline-whole-llvm-lld-pic +#skip xtensa-linux-call0-baseline-whole-llvm-lld-pic +#skip xtensa-linux-none-baseline-whole-llvm-lld-pic + +#obj { +--name test +-Mroot=test.zig +-femit-asm -fno-emit-bin +#} + +#write test.zig +#update +#check errors diff --git a/test/llvm/empty_object b/test/llvm/empty_object new file mode 100644 index 0000000000000000000000000000000000000000..b42e86be09bd48d78ae38accc26574b3403981ac --- /dev/null +++ b/test/llvm/empty_object @@ -0,0 +1,67 @@ +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip arc-freestanding-none-baseline-whole-llvm-lld-pic +#skip arc-linux-gnu-baseline-whole-llvm-lld-pic +#skip arc-linux-none-baseline-whole-llvm-lld-pic + +#skip arm-uefi-eabi-baseline-whole-llvm-lld-pic +#skip arm-uefi-eabihf-baseline-whole-llvm-lld-pic + +#skip csky-freestanding-eabi-baseline-whole-llvm-lld-pic +#skip csky-freestanding-eabihf-baseline-whole-llvm-lld-pic +#skip csky-linux-eabi-baseline-whole-llvm-lld-pic +#skip csky-linux-eabihf-baseline-whole-llvm-lld-pic +#skip csky-linux-gnueabi-baseline-whole-llvm-lld-pic +#skip csky-linux-gnueabihf-baseline-whole-llvm-lld-pic + +#skip loongarch32-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch32-uefi-none-baseline-whole-llvm-lld-pic + +#skip loongarch64-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch64-uefi-none-baseline-whole-llvm-lld-pic + +#skip mipsel-psx-eabi-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip riscv32-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv32be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip riscv64-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv64be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#skip xcore-freestanding-none-baseline-whole-llvm-lld-pic + +#skip xtensa-freestanding-call0-baseline-whole-llvm-lld-pic +#skip xtensa-freestanding-none-baseline-whole-llvm-lld-pic +#skip xtensa-linux-call0-baseline-whole-llvm-lld-pic +#skip xtensa-linux-none-baseline-whole-llvm-lld-pic + +#obj { +--name test +-Mroot=test.zig +-fno-emit-asm -femit-bin +#} + +#write test.zig +#update +#check errors diff --git a/test/llvm/load_store_bool b/test/llvm/load_store_bool new file mode 100644 index 0000000000000000000000000000000000000000..b59421e5cf1b3b31fa26570e02c83dedc30aaeac --- /dev/null +++ b/test/llvm/load_store_bool @@ -0,0 +1,95 @@ +#skip amdgcn-amdhsa-none-baseline-whole-llvm-lld-pic +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip bpfeb-freestanding-none-baseline-whole-llvm-lld-pic + +#skip bpfel-freestanding-none-baseline-whole-llvm-lld-pic + +#skip loongarch32-freestanding-none-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-gnu-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-gnusf-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch32-linux-none-baseline-whole-llvm-lld-pic +#skip loongarch32-uefi-none-baseline-whole-llvm-lld-pic + +#skip loongarch64-freestanding-none-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnu-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnu-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnuf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-gnusf-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-musl-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslf32-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-muslsf-baseline-whole-llvm-lld-pic +#skip loongarch64-linux-none-baseline-whole-llvm-lld-pic +#skip loongarch64-uefi-none-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip riscv32-freestanding-none-baseline-whole-llvm-lld-pic +#skip riscv32-linux-gnu-baseline-whole-llvm-lld-pic +#skip riscv32-linux-musl-baseline-whole-llvm-lld-pic +#skip riscv32-linux-none-baseline-whole-llvm-lld-pic +#skip riscv32-netbsd-none-baseline-whole-llvm-lld-pic +#skip riscv32-rtems-none-baseline-whole-llvm-lld-pic +#skip riscv32-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv32be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip riscv64-freebsd-none-baseline-whole-llvm-lld-pic +#skip riscv64-freestanding-none-baseline-whole-llvm-lld-pic +#skip riscv64-fuchsia-none-baseline-whole-llvm-lld-pic +#skip riscv64-haiku-none-baseline-whole-llvm-lld-pic +#skip riscv64-hurd-gnu-baseline-whole-llvm-lld-pic +#skip riscv64-linux-android-baseline-whole-llvm-lld-pic +#skip riscv64-linux-gnu-baseline-whole-llvm-lld-pic +#skip riscv64-linux-musl-baseline-whole-llvm-lld-pic +#skip riscv64-linux-none-baseline-whole-llvm-lld-pic +#skip riscv64-netbsd-none-baseline-whole-llvm-lld-pic +#skip riscv64-openbsd-none-baseline-whole-llvm-lld-pic +#skip riscv64-rtems-none-baseline-whole-llvm-lld-pic +#skip riscv64-serenity-none-baseline-whole-llvm-lld-pic +#skip riscv64-uefi-none-baseline-whole-llvm-lld-pic + +#skip riscv64be-freestanding-none-baseline-whole-llvm-lld-pic + +#skip x86_64-linux-gnux32-baseline-whole-llvm-new-pic +#skip x86_64-linux-muslx32-baseline-whole-llvm-new-pic +#skip x86_64-linux-x32-baseline-whole-llvm-new-pic + +#obj { +--name test -fno-lld -fno-new-linker -ofmt=llvmir +-Mroot=test.zig +#} + +#write test.zig { +export fn foo(a: *bool, b: *align(2) bool) void { + const tmp = a.*; + a.* = b.*; + b.* = tmp; +} +#} +#update +#check llvm { +define #...void @test.foo(ptr #...%0, ptr #...%1) #...{ +#... + %5 = load i8, ptr %0, align 1#... + %6 = trunc nuw i8 %5 to i1#... + %8 = load i8, ptr %1, align 2#... + %9 = trunc nuw i8 %8 to i1#... + %10 = zext i1 %9 to i8#... + store i8 %10, ptr %0, align 1#... + %11 = zext i1 %6 to i8#... + store i8 %11, ptr %1, align 2#... + ret void#... +} + +#} diff --git a/test/llvm/matrix.zon b/test/llvm/matrix.zon new file mode 100644 index 0000000000000000000000000000000000000000..8dafd5efada10939b1ca29d4f9d16f166907b03c --- /dev/null +++ b/test/llvm/matrix.zon @@ -0,0 +1,355 @@ +.{ + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .driverkit = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .freebsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .fuchsia = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .haiku = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .hurd = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .ios = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .ios = .{} }, .abi = .{ .simulator = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .android = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .ohos = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .maccatalyst = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .macos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .serenity = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .@"switch" = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .tvos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .tvos = .{} }, .abi = .{ .simulator = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .visionos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .visionos = .{} }, .abi = .{ .simulator = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .watchos = .{} }, .abi = .{ .ilp32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .watchos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .watchos = .{} }, .abi = .{ .simulator = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .itanium = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .msvc = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .aarch64_be = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64_be = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64_be = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64_be = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .aarch64_be = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .amdgcn = .{} }, .os = .{ .amdhsa = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .amdgcn = .{} }, .os = .{ .amdpal = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .amdgcn = .{} }, .os = .{ .mesa3d = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .arc = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .arm = .{} }, .os = .{ .@"3ds" = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .freebsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .fuchsia = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .gba = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .haiku = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .androideabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .linux = .{} }, .abi = .{ .ohoseabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .arm = .{} }, .os = .{ .vita = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .armeb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .armeb = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .avr = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .avr = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .bpfeb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .bpfel = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .csky = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .csky = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .csky = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .csky = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .csky = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .csky = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .hexagon = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .hexagon = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .hexagon = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .lanai = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnuf32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnusf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslf32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslsf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch32 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnuf32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnusf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslf32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslsf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .loongarch64 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .m68k = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .m68k = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .m68k = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .m68k = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .m68k = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .m68k = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .mips = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnueabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .psx = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .psp = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mipsel = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .abin32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnuabi64 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnuabin32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslabi64 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslabin32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .linux = .{} }, .abi = .{ .abin32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnuabi64 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnuabin32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslabi64 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslabin32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .mips64el = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .msp430 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .nvptx = .{} }, .os = .{ .cuda = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .nvptx = .{} }, .os = .{ .nvcl = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .nvptx64 = .{} }, .os = .{ .cuda = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .nvptx64 = .{} }, .os = .{ .nvcl = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc = .{} }, .os = .{ .wiiu = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .powerpcle = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpcle = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .freebsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .ps3 = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .powerpc64le = .{} }, .os = .{ .freebsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64le = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64le = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64le = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .powerpc64le = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv32 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .riscv32be = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .freebsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .fuchsia = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .haiku = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .hurd = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .android = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .serenity = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .riscv64 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .riscv64be = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .s390x = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .s390x = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .s390x = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .s390x = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .sparc = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .sparc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .sparc = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .sparc = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .sparc = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .sparc64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .sparc64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .sparc64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .sparc64 = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .sparc64 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .sparc64 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + + .{ .cpu = .{ .spirv32 = .{} }, .os = .{ .opencl = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .spirv32 = .{} }, .os = .{ .opengl = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .spirv32 = .{} }, .os = .{ .vulkan = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .spirv64 = .{} }, .os = .{ .amdhsa = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .spirv64 = .{} }, .os = .{ .opencl = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .spirv64 = .{} }, .os = .{ .vulkan = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .thumb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .fuchsia = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .gba = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .vita = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .windows = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .windows = .{} }, .abi = .{ .itanium = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumb = .{} }, .os = .{ .windows = .{} }, .abi = .{ .msvc = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musleabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabi = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .thumbeb = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .eabihf = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .ve = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .ve = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .wasm32 = .{} }, .os = .{ .emscripten = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .wasm32 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .wasm32 = .{} }, .os = .{ .wasi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .wasm64 = .{} }, .os = .{ .emscripten = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .wasm64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .wasm64 = .{} }, .os = .{ .wasi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .x86 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .haiku = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .hurd = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .illumos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .android = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .itanium = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .msvc = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .freebsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{ .features = .{ .add = .{.soft_float}, .remove = .{ .mmx, .sse, .sse2, .avx, .avx2 } } } }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .fuchsia = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .dragonfly = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .driverkit = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .haiku = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .hurd = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .illumos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .android = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .gnux32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .musl = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .muslx32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .ohos = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .linux = .{} }, .abi = .{ .x32 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .maccatalyst = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .macos = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .old, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .netbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .openbsd = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .ps4 = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .ps5 = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .rtems = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .serenity = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .new, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .uefi = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .gnu = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .itanium = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .x86_64 = .{} }, .os = .{ .windows = .{} }, .abi = .{ .msvc = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .xcore = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + + .{ .cpu = .{ .xtensa = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .call0 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .xtensa = .{} }, .os = .{ .freestanding = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .xtensa = .{} }, .os = .{ .linux = .{} }, .abi = .{ .call0 = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, + .{ .cpu = .{ .xtensa = .{} }, .os = .{ .linux = .{} }, .abi = .{ .none = .{} }, .cache_mode = .whole, .backend = .llvm, .linker = .lld, .pic = .pic }, +} diff --git a/test/llvm/nonnull b/test/llvm/nonnull new file mode 100644 index 0000000000000000000000000000000000000000..90d904ba6df7befeace21f9bf6b27e4cc4fec35c --- /dev/null +++ b/test/llvm/nonnull @@ -0,0 +1,44 @@ +#skip amdgcn-amdhsa-none-baseline-whole-llvm-lld-pic +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip x86_64-linux-gnux32-baseline-whole-llvm-new-pic +#skip x86_64-linux-muslx32-baseline-whole-llvm-new-pic +#skip x86_64-linux-x32-baseline-whole-llvm-new-pic + +#obj { +--name test -fno-lld -fno-new-linker -ofmt=llvmir +-Mroot=test.zig +#} + +#write test.zig { +export fn entry(ptr: *i16) i16 { + return ptr.*; +} +#} +#update "store" +#check llvm { +define #...@test.entry(ptr #...nonnull #...%0) #...{ +#...load i16, ptr %0#... +} + +#} + +#write test.zig { +export fn entry(ptr: *i16) void { + ptr.* = 42; +} +#} +#update "load" +#check llvm { +define #...@test.entry(ptr #...nonnull #...%0) #...{ +#...store i16 42, ptr %0#... +} + +#} diff --git a/test/llvm/unused_load b/test/llvm/unused_load new file mode 100644 index 0000000000000000000000000000000000000000..b18d0e52d080e7c6769c3f562db251c4df2378bc --- /dev/null +++ b/test/llvm/unused_load @@ -0,0 +1,109 @@ +#skip amdgcn-amdhsa-none-baseline-whole-llvm-lld-pic +#skip amdgcn-amdpal-none-baseline-whole-llvm-lld-pic +#skip amdgcn-mesa3d-none-baseline-whole-llvm-lld-pic + +#skip nvptx-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx-nvcl-none-baseline-whole-llvm-lld-pic + +#skip nvptx64-cuda-none-baseline-whole-llvm-lld-pic +#skip nvptx64-nvcl-none-baseline-whole-llvm-lld-pic + +#skip spirv32-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv32-opengl-none-baseline-whole-llvm-lld-pic +#skip spirv32-vulkan-none-baseline-whole-llvm-lld-pic + +#skip spirv64-amdhsa-none-baseline-whole-llvm-lld-pic +#skip spirv64-opencl-none-baseline-whole-llvm-lld-pic +#skip spirv64-vulkan-none-baseline-whole-llvm-lld-pic + +#skip wasm32-emscripten-none-baseline-whole-llvm-lld-pic +#skip wasm32-freestanding-none-baseline-whole-llvm-lld-pic +#skip wasm32-wasi-none-baseline-whole-llvm-lld-pic + +#skip wasm64-emscripten-none-baseline-whole-llvm-lld-pic +#skip wasm64-freestanding-none-baseline-whole-llvm-lld-pic +#skip wasm64-wasi-none-baseline-whole-llvm-lld-pic + +#skip x86_64-linux-gnux32-baseline-whole-llvm-new-pic +#skip x86_64-linux-muslx32-baseline-whole-llvm-new-pic +#skip x86_64-linux-x32-baseline-whole-llvm-new-pic + +#obj { +--name test -fno-lld -fno-new-linker -ofmt=llvmir +-Mroot=test.zig +#} + +#write test.zig { +export fn entry(ptr: *i16) void { + _ = @atomicLoad(i16, ptr, .acquire); +} +#} +#update "atomic acquire" +#check llvm { +define #...@test.entry(ptr #...%0) #...{ +#...load atomic i16, ptr %0 acquire#... +} + +#} + +#write test.zig { +export fn entry(ptr: *volatile i16) void { + _ = @atomicLoad(i16, ptr, .unordered); +} +#} +#update "atomic volatile unordered" +#check llvm { +define #...@test.entry(ptr #...%0) #...{ +#...load atomic volatile i16, ptr %0 unordered#... +} + +#} + +#write test.zig { +export fn entry(ptr: *volatile i16) void { + _ = ptr.*; +} +#} +#update "volatile" +#check llvm { +define #...@test.entry(ptr #...%0) #...{ +#...load volatile i16, ptr %0#... +} + +#} + +#write test.zig { +export fn entry(ptr: *volatile i16) void { + entry2(ptr[0..1]); +} +fn entry2(slice: []volatile i16) void { + @setRuntimeSafety(false); + _ = slice[0]; +} +#} +#update "volatile slice" +#check llvm { +define #...@test.entry(#...) #...{ +#... +} + +#... +define #...@test.entry2(#...) #...{ +#...load volatile i16, ptr %#... +} + +#} + +#write test.zig { +const S = packed struct(u16) { a: u2, b: u8, c: u6 }; +export fn entry(ptr: *volatile S) void { + _ = ptr.b; +} +#} +#update "volatile packed" +#check llvm { +define #...@test.entry(ptr #...%0) #...{ +#...load volatile i16, ptr %#... +} + +#} diff --git a/test/llvm_ir.zig b/test/llvm_ir.zig deleted file mode 100644 index 9cb9dab42e85753e168fe7d9ca4f8c049754b19c..0000000000000000000000000000000000000000 --- a/test/llvm_ir.zig +++ /dev/null @@ -1,170 +0,0 @@ -pub fn addCases(cases: *tests.LlvmIrContext) void { - cases.addMatches("nonnull ptr load", - \\export fn entry(ptr: *i16) i16 { - \\ return ptr.*; - \\} - , &.{ - "ptr nonnull", - "load i16, ptr %0", - }, .{}); - - cases.addMatches("nonnull ptr store", - \\export fn entry(ptr: *i16) void { - \\ ptr.* = 42; - \\} - , &.{ - "ptr nonnull", - "store i16 42, ptr %0", - }, .{}); - - cases.addMatches("unused acquire atomic ptr load", - \\export fn entry(ptr: *i16) void { - \\ _ = @atomicLoad(i16, ptr, .acquire); - \\} - , &.{ - "load atomic i16, ptr %0 acquire", - }, .{}); - - cases.addMatches("unused unordered atomic volatile ptr load", - \\export fn entry(ptr: *volatile i16) void { - \\ _ = @atomicLoad(i16, ptr, .unordered); - \\} - , &.{ - "load atomic volatile i16, ptr %0 unordered", - }, .{}); - - cases.addMatches("unused volatile ptr load", - \\export fn entry(ptr: *volatile i16) void { - \\ _ = ptr.*; - \\} - , &.{ - "load volatile i16, ptr %0", - }, .{}); - - cases.addMatches("dead volatile ptr store", - \\export fn entry(ptr: *volatile i16) void { - \\ ptr.* = 123; - \\ ptr.* = 321; - \\} - , &.{ - "store volatile i16 123, ptr %0", - "store volatile i16 321, ptr %0", - }, .{}); - - cases.addMatches("unused volatile slice load", - \\export fn entry(ptr: *volatile i16) void { - \\ entry2(ptr[0..1]); - \\} - \\fn entry2(ptr: []volatile i16) void { - \\ _ = ptr[0]; - \\} - , &.{ - "load volatile i16, ptr", - }, .{}); - - cases.addMatches("dead volatile slice store", - \\export fn entry(ptr: *volatile i16) void { - \\ entry2(ptr[0..1]); - \\} - \\fn entry2(ptr: []volatile i16) void { - \\ ptr[0] = 123; - \\ ptr[0] = 321; - \\} - , &.{ - "store volatile i16 123, ptr", - "store volatile i16 321, ptr", - }, .{}); - - cases.addMatches("unused volatile packed pointer load", - \\const S = packed struct(u16) { a: u2, b: u8, c: u6 }; - \\export fn entry(ptr: *volatile S) void { - \\ _ = ptr.b; - \\} - , &.{ - "load volatile i16, ptr", - }, .{}); - - cases.addMatches("allowzero ptr load", - \\export fn entry(ptr: *allowzero i16) i16 { - \\ return ptr.*; - \\} - , &.{ - "null_pointer_is_valid", - "load i16, ptr %0", - }, .{}); - - cases.addMatches("allowzero ptr store", - \\export fn entry(ptr: *allowzero i16) void { - \\ ptr.* = 42; - \\} - , &.{ - "null_pointer_is_valid", - "store i16 42, ptr %0", - }, .{}); - - cases.addMatches("allowzero slice load", - \\export fn entry(ptr: *allowzero i16) i16 { - \\ return entry2(ptr[0..1]); - \\} - \\fn entry2(ptr: []allowzero i16) i16 { - \\ return ptr[0]; - \\} - , &.{ - "null_pointer_is_valid", - "load i16, ptr", - }, .{}); - - cases.addMatches("allowzero slice store", - \\export fn entry(ptr: *allowzero i16) void { - \\ entry2(ptr[0..1]); - \\} - \\fn entry2(ptr: []allowzero i16) void { - \\ ptr[0] = 42; - \\} - , &.{ - "null_pointer_is_valid", - "store i16 42, ptr", - }, .{}); - - cases.addMatches("load and store bool", - \\export fn foo(a: *bool, b: *align(2) bool) void { - \\ const tmp = a.*; - \\ a.* = b.*; - \\ b.* = tmp; - \\} - , &.{ - // TODO: this should all be one multiline string literal, but `-femit-llvm-ir` is currently - // emitting CRLF on Windows, which is a pain to handle here. In future that option will emit - // unoptimized LLVM IR emitted directly from Zig, so that bug will go away. - " %3 = load i8, ptr %0, align 1", - " %4 = trunc nuw i8 %3 to i1", - " %5 = load i8, ptr %1, align 2", - " %6 = trunc nuw i8 %5 to i1", - " %7 = zext i1 %6 to i8", - " store i8 %7, ptr %0, align 1", - " %8 = zext i1 %4 to i8", - " store i8 %8, ptr %1, align 2", - }, .{ .strip = true }); - - cases.addMatches("array to vector coercion is one vector load", - \\export fn entry(arr: *const [16]u8) @Vector(16, u8) { - \\ return arr.*; - \\} - , &.{ - "load <16 x i8>, ptr", - }, .{}); - - cases.addMatches("array to vector coercion feeding overlapping shuffles", - \\export fn entry(a: *@Vector(8, u8), b: *@Vector(8, u8), src: [*]const u8) void { - \\ const q: @Vector(16, u8) = src[0..16].*; - \\ a.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 0, 1, 2, 3, 4, 5, 6, 7 }); - \\ b.* = @shuffle(u8, q, undefined, @Vector(8, i32){ 7, 8, 9, 10, 11, 12, 13, 14 }); - \\} - , &.{ - "load <16 x i8>, ptr", - "shufflevector <16 x i8>", - }, .{}); -} - -const std = @import("std"); -const tests = @import("tests.zig"); diff --git a/test/llvm_targets.zig b/test/llvm_targets.zig deleted file mode 100644 index 0c758566fe37fc1af4862620f36dda3d17a9dc0d..0000000000000000000000000000000000000000 --- a/test/llvm_targets.zig +++ /dev/null @@ -1,382 +0,0 @@ -const std = @import("std"); -const Cases = @import("src/Cases.zig"); - -const targets = [_]std.Target.Query{ - .{ .cpu_arch = .aarch64, .os_tag = .driverkit, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .freebsd, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .fuchsia, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .haiku, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .hurd, .abi = .gnu }, - .{ .cpu_arch = .aarch64, .os_tag = .ios, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .ios, .abi = .simulator }, - .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .android }, - .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .ohos }, - .{ .cpu_arch = .aarch64, .os_tag = .maccatalyst, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .macos, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .openbsd, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .rtems, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .serenity, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .@"switch", .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .tvos, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .tvos, .abi = .simulator }, - .{ .cpu_arch = .aarch64, .os_tag = .uefi, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .visionos, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .visionos, .abi = .simulator }, - .{ .cpu_arch = .aarch64, .os_tag = .watchos, .abi = .ilp32 }, - .{ .cpu_arch = .aarch64, .os_tag = .watchos, .abi = .none }, - .{ .cpu_arch = .aarch64, .os_tag = .watchos, .abi = .simulator }, - .{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .gnu }, - .{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .itanium }, - .{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .msvc }, - - .{ .cpu_arch = .aarch64_be, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .aarch64_be, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .aarch64_be, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .aarch64_be, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .aarch64_be, .os_tag = .netbsd, .abi = .none }, - - .{ .cpu_arch = .amdgcn, .os_tag = .amdhsa, .abi = .none }, - // .{ .cpu_arch = .amdgcn, .os_tag = .amdpal, .abi = .none }, - // .{ .cpu_arch = .amdgcn, .os_tag = .mesa3d, .abi = .none }, - - .{ .cpu_arch = .arc, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .arc, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .arc, .os_tag = .linux, .abi = .none }, - - .{ .cpu_arch = .arm, .os_tag = .@"3ds", .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .freebsd, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .arm, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .fuchsia, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .gba, .abi = .eabi }, - .{ .cpu_arch = .arm, .os_tag = .haiku, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .androideabi }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .gnueabi }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .gnueabihf }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .ohoseabi }, - .{ .cpu_arch = .arm, .os_tag = .netbsd, .abi = .eabi }, - .{ .cpu_arch = .arm, .os_tag = .netbsd, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .openbsd, .abi = .eabi }, - .{ .cpu_arch = .arm, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .arm, .os_tag = .rtems, .abi = .eabihf }, - // .{ .cpu_arch = .arm, .os_tag = .uefi, .abi = .eabi }, - // .{ .cpu_arch = .arm, .os_tag = .uefi, .abi = .eabihf }, - .{ .cpu_arch = .arm, .os_tag = .vita, .abi = .eabihf }, - - .{ .cpu_arch = .armeb, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .armeb, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .gnueabi }, - .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .gnueabihf }, - .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .armeb, .os_tag = .netbsd, .abi = .eabi }, - .{ .cpu_arch = .armeb, .os_tag = .netbsd, .abi = .eabihf }, - .{ .cpu_arch = .armeb, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .armeb, .os_tag = .rtems, .abi = .eabihf }, - - .{ .cpu_arch = .avr, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .avr, .os_tag = .rtems, .abi = .none }, - - .{ .cpu_arch = .bpfeb, .os_tag = .freestanding, .abi = .none }, - - .{ .cpu_arch = .bpfel, .os_tag = .freestanding, .abi = .none }, - - // .{ .cpu_arch = .csky, .os_tag = .freestanding, .abi = .eabi }, - // .{ .cpu_arch = .csky, .os_tag = .freestanding, .abi = .eabihf }, - // .{ .cpu_arch = .csky, .os_tag = .linux, .abi = .eabi }, - // .{ .cpu_arch = .csky, .os_tag = .linux, .abi = .eabihf }, - // .{ .cpu_arch = .csky, .os_tag = .linux, .abi = .gnueabi }, - // .{ .cpu_arch = .csky, .os_tag = .linux, .abi = .gnueabihf }, - - .{ .cpu_arch = .hexagon, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .hexagon, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .hexagon, .os_tag = .linux, .abi = .none }, - - .{ .cpu_arch = .lanai, .os_tag = .freestanding, .abi = .none }, - - .{ .cpu_arch = .loongarch32, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .gnu }, - // .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .gnuf32 }, - .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .gnusf }, - // .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .musl }, - // .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .muslf32 }, - // .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .muslsf }, - .{ .cpu_arch = .loongarch32, .os_tag = .linux, .abi = .none }, - // .{ .cpu_arch = .loongarch32, .os_tag = .uefi, .abi = .none }, - - .{ .cpu_arch = .loongarch64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .gnu }, - // .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .gnuf32 }, - .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .gnusf }, - .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .musl }, - // .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .muslf32 }, - .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .muslsf }, - .{ .cpu_arch = .loongarch64, .os_tag = .linux, .abi = .none }, - // .{ .cpu_arch = .loongarch64, .os_tag = .uefi, .abi = .none }, - - .{ .cpu_arch = .m68k, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .m68k, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .m68k, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .m68k, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .m68k, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .m68k, .os_tag = .rtems, .abi = .none }, - - .{ .cpu_arch = .mips, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .mips, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .gnueabi }, - .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .gnueabihf }, - .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .mips, .os_tag = .netbsd, .abi = .eabi }, - .{ .cpu_arch = .mips, .os_tag = .netbsd, .abi = .eabihf }, - .{ .cpu_arch = .mips, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .mips, .os_tag = .rtems, .abi = .eabihf }, - - .{ .cpu_arch = .mipsel, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .mipsel, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .gnueabi }, - .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .gnueabihf }, - .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .mipsel, .os_tag = .netbsd, .abi = .eabi }, - .{ .cpu_arch = .mipsel, .os_tag = .netbsd, .abi = .eabihf }, - // .{ .cpu_arch = .mipsel, .os_tag = .psx, .abi = .eabi }, - .{ .cpu_arch = .mipsel, .os_tag = .psp, .abi = .eabihf }, - .{ .cpu_arch = .mipsel, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .mipsel, .os_tag = .rtems, .abi = .eabihf }, - - .{ .cpu_arch = .mips64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .abin32 }, - .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .gnuabi64 }, - .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .gnuabin32 }, - .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .muslabi64 }, - .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .muslabin32 }, - .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .mips64, .os_tag = .openbsd, .abi = .none }, - - .{ .cpu_arch = .mips64el, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .abin32 }, - .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .gnuabi64 }, - .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .gnuabin32 }, - .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .muslabi64 }, - .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .muslabin32 }, - .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .mips64el, .os_tag = .openbsd, .abi = .none }, - - .{ .cpu_arch = .msp430, .os_tag = .freestanding, .abi = .none }, - - .{ .cpu_arch = .nvptx, .os_tag = .cuda, .abi = .none }, - .{ .cpu_arch = .nvptx, .os_tag = .nvcl, .abi = .none }, - - .{ .cpu_arch = .nvptx64, .os_tag = .cuda, .abi = .none }, - .{ .cpu_arch = .nvptx64, .os_tag = .nvcl, .abi = .none }, - - .{ .cpu_arch = .powerpc, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .powerpc, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .powerpc, .os_tag = .netbsd, .abi = .eabihf }, - .{ .cpu_arch = .powerpc, .os_tag = .openbsd, .abi = .eabihf }, - .{ .cpu_arch = .powerpc, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .powerpc, .os_tag = .rtems, .abi = .eabihf }, - .{ .cpu_arch = .powerpc, .os_tag = .wiiu, .abi = .eabihf }, - - .{ .cpu_arch = .powerpcle, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .powerpcle, .os_tag = .freestanding, .abi = .eabihf }, - - .{ .cpu_arch = .powerpc64, .os_tag = .freebsd, .abi = .none }, - .{ .cpu_arch = .powerpc64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .powerpc64, .os_tag = .openbsd, .abi = .none }, - .{ .cpu_arch = .powerpc64, .os_tag = .ps3, .abi = .ilp32 }, - .{ .cpu_arch = .powerpc64, .os_tag = .rtems, .abi = .none }, - - .{ .cpu_arch = .powerpc64le, .os_tag = .freebsd, .abi = .none }, - .{ .cpu_arch = .powerpc64le, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .none }, - - .{ .cpu_arch = .riscv32, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .riscv32, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .riscv32, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .riscv32, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .riscv32, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .riscv32, .os_tag = .rtems, .abi = .none }, - // .{ .cpu_arch = .riscv32, .os_tag = .uefi, .abi = .none }, - - // .{ .cpu_arch = .riscv32be, .os_tag = .freestanding, .abi = .none }, - - .{ .cpu_arch = .riscv64, .os_tag = .freebsd, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .fuchsia, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .haiku, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .hurd, .abi = .gnu }, - .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .android }, - .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .openbsd, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .rtems, .abi = .none }, - .{ .cpu_arch = .riscv64, .os_tag = .serenity, .abi = .none }, - // .{ .cpu_arch = .riscv64, .os_tag = .uefi, .abi = .none }, - - // .{ .cpu_arch = .riscv64be, .os_tag = .freestanding, .abi = .none }, - - .{ .cpu_arch = .s390x, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .s390x, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .s390x, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .s390x, .os_tag = .linux, .abi = .none }, - - .{ .cpu_arch = .sparc, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .sparc, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .sparc, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .sparc, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .sparc, .os_tag = .rtems, .abi = .none }, - - .{ .cpu_arch = .sparc64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .sparc64, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .sparc64, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .sparc64, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .sparc64, .os_tag = .openbsd, .abi = .none }, - .{ .cpu_arch = .sparc64, .os_tag = .rtems, .abi = .none }, - - // .{ .cpu_arch = .spirv32, .os_tag = .opencl, .abi = .none }, - // .{ .cpu_arch = .spirv32, .os_tag = .opengl, .abi = .none }, - // .{ .cpu_arch = .spirv32, .os_tag = .vulkan, .abi = .none }, - // .{ .cpu_arch = .spirv64, .os_tag = .amdhsa, .abi = .none }, - // .{ .cpu_arch = .spirv64, .os_tag = .opencl, .abi = .none }, - // .{ .cpu_arch = .spirv64, .os_tag = .vulkan, .abi = .none }, - - .{ .cpu_arch = .thumb, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .thumb, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .thumb, .os_tag = .fuchsia, .abi = .eabihf }, - .{ .cpu_arch = .thumb, .os_tag = .gba, .abi = .eabi }, - .{ .cpu_arch = .thumb, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .thumb, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .thumb, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .thumb, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .thumb, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .thumb, .os_tag = .rtems, .abi = .eabihf }, - .{ .cpu_arch = .thumb, .os_tag = .vita, .abi = .eabihf }, - .{ .cpu_arch = .thumb, .os_tag = .windows, .abi = .gnu }, - .{ .cpu_arch = .thumb, .os_tag = .windows, .abi = .itanium }, - .{ .cpu_arch = .thumb, .os_tag = .windows, .abi = .msvc }, - - .{ .cpu_arch = .thumbeb, .os_tag = .freestanding, .abi = .eabi }, - .{ .cpu_arch = .thumbeb, .os_tag = .freestanding, .abi = .eabihf }, - .{ .cpu_arch = .thumbeb, .os_tag = .linux, .abi = .eabi }, - .{ .cpu_arch = .thumbeb, .os_tag = .linux, .abi = .eabihf }, - .{ .cpu_arch = .thumbeb, .os_tag = .linux, .abi = .musleabi }, - .{ .cpu_arch = .thumbeb, .os_tag = .linux, .abi = .musleabihf }, - .{ .cpu_arch = .thumbeb, .os_tag = .rtems, .abi = .eabi }, - .{ .cpu_arch = .thumbeb, .os_tag = .rtems, .abi = .eabihf }, - - .{ .cpu_arch = .ve, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .ve, .os_tag = .linux, .abi = .none }, - - .{ .cpu_arch = .wasm32, .os_tag = .emscripten, .abi = .none }, - .{ .cpu_arch = .wasm32, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .wasm32, .os_tag = .wasi, .abi = .none }, - - .{ .cpu_arch = .wasm64, .os_tag = .emscripten, .abi = .none }, - .{ .cpu_arch = .wasm64, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .wasm64, .os_tag = .wasi, .abi = .none }, - - .{ .cpu_arch = .x86, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .haiku, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .hurd, .abi = .gnu }, - .{ .cpu_arch = .x86, .os_tag = .illumos, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .android }, - .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .openbsd, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .rtems, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .uefi, .abi = .none }, - .{ .cpu_arch = .x86, .os_tag = .windows, .abi = .gnu }, - .{ .cpu_arch = .x86, .os_tag = .windows, .abi = .itanium }, - .{ .cpu_arch = .x86, .os_tag = .windows, .abi = .msvc }, - - .{ .cpu_arch = .x86_64, .os_tag = .freebsd, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .freestanding, .abi = .none }, - .{ - .cpu_arch = .x86_64, - .os_tag = .freestanding, - .abi = .none, - .cpu_features_add = std.Target.x86.featureSet(&.{.soft_float}), - .cpu_features_sub = std.Target.x86.featureSet(&.{ .mmx, .sse, .sse2, .avx, .avx2 }), - }, - .{ .cpu_arch = .x86_64, .os_tag = .fuchsia, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .dragonfly, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .driverkit, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .haiku, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .hurd, .abi = .gnu }, - .{ .cpu_arch = .x86_64, .os_tag = .illumos, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .android }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .gnu }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .gnux32 }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .muslx32 }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .ohos }, - .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .x32 }, - .{ .cpu_arch = .x86_64, .os_tag = .maccatalyst, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .macos, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .netbsd, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .openbsd, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .ps4, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .ps5, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .rtems, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .serenity, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .uefi, .abi = .none }, - .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }, - .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .itanium }, - .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .msvc }, - - .{ .cpu_arch = .xcore, .os_tag = .freestanding, .abi = .none }, - - .{ .cpu_arch = .xtensa, .os_tag = .freestanding, .abi = .call0 }, - .{ .cpu_arch = .xtensa, .os_tag = .freestanding, .abi = .none }, - .{ .cpu_arch = .xtensa, .os_tag = .linux, .abi = .call0 }, - .{ .cpu_arch = .xtensa, .os_tag = .linux, .abi = .none }, -}; - -pub fn addCases( - ctx: *Cases, - build_options: @import("cases.zig").BuildOptions, - b: *std.Build, -) !void { - if (!build_options.enable_llvm) return; - for (targets) |target_query| { - if (target_query.cpu_arch) |arch| switch (arch) { - .m68k => if (!build_options.llvm_has_m68k) continue, - .csky => if (!build_options.llvm_has_csky) continue, - .arc => if (!build_options.llvm_has_arc) continue, - .xtensa => if (!build_options.llvm_has_xtensa) continue, - else => {}, - }; - var case = ctx.addObjLlvm("llvm_targets", b.resolveTargetQuery(target_query)); - case.addCompile(""); - } -} diff --git a/test/src/Cases.zig b/test/src/Cases.zig index c6b3eb07d1ed6efdfd626952bac4ce484ca4efef..ac367a5813f9d644617aaa0c22259307105144c3 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -75,7 +75,6 @@ pub const Case = struct { emit_asm: bool = false, emit_bin: bool = true, - emit_h: bool = false, is_test: bool = false, expect_exact: bool = false, backend: Backend = .auto, @@ -165,38 +164,6 @@ pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target_query: std.Target. return &ctx.cases.items[ctx.cases.items.len - 1]; } -pub fn addObjLlvm(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { - const can_emit_asm = switch (target.result.cpu.arch) { - .csky, - .xtensa, - => false, - else => true, - }; - const can_emit_bin = switch (target.result.cpu.arch) { - .arc, - .csky, - .nvptx, - .nvptx64, - .xcore, - .xtensa, - => false, - else => true, - }; - - ctx.cases.append(.{ - .name = name, - .target = target, - .files = .init(ctx.arena), - .case = null, - .output_mode = .Obj, - .deps = std.array_list.Managed(DepModule).init(ctx.arena), - .backend = .llvm, - .emit_bin = can_emit_bin, - .emit_asm = can_emit_asm, - }) catch @panic("out of memory"); - return &ctx.cases.items[ctx.cases.items.len - 1]; -} - pub fn addObj( ctx: *Cases, name: []const u8, diff --git a/test/src/LlvmIr.zig b/test/src/LlvmIr.zig deleted file mode 100644 index 3dce66058b2c3eb21e9046f16091d348826d37a4..0000000000000000000000000000000000000000 --- a/test/src/LlvmIr.zig +++ /dev/null @@ -1,135 +0,0 @@ -b: *std.Build, -options: Options, -root_step: *std.Build.Step, - -pub const Options = struct { - enable_llvm: bool, - test_filters: []const []const u8, - test_target_filters: []const []const u8, -}; - -const TestCase = struct { - name: []const u8, - source: []const u8, - check: union(enum) { - matches: []const []const u8, - exact: []const u8, - }, - params: Params, - - pub const Params = struct { - code_model: std.builtin.CodeModel = .default, - dll_export_fns: ?bool = null, - dwarf_format: ?std.dwarf.Format = null, - error_tracing: ?bool = null, - no_builtin: ?bool = null, - omit_frame_pointer: ?bool = null, - // For most cases, we want to test the LLVM IR that we output; we don't want to be in the - // business of testing LLVM's optimization passes. `Debug` gets us the closest to that as it - // disables the vast majority of passes in LLVM. - optimize: std.builtin.Optimize = .debug, - pic: ?bool = null, - pie: ?bool = null, - red_zone: ?bool = null, - sanitize_thread: ?bool = null, - single_threaded: ?bool = null, - stack_check: ?bool = null, - stack_protector: ?bool = null, - strip: ?bool = null, - target: std.Target.Query = .{}, - unwind_tables: ?std.builtin.UnwindTables = null, - valgrind: ?bool = null, - }; -}; - -pub fn addMatches( - self: *LlvmIr, - name: []const u8, - source: []const u8, - matches: []const []const u8, - params: TestCase.Params, -) void { - self.addCase(.{ - .name = name, - .source = source, - .check = .{ .matches = matches }, - .params = params, - }); -} - -pub fn addExact( - self: *LlvmIr, - name: []const u8, - source: []const u8, - expected: []const []const u8, - params: TestCase.Params, -) void { - self.addCase(.{ - .name = name, - .source = source, - .check = .{ .exact = expected }, - .params = params, - }); -} - -pub fn addCase(self: *LlvmIr, case: TestCase) void { - const target = self.b.resolveTargetQuery(case.params.target); - if (self.options.test_target_filters.len > 0) { - const triple_txt = target.query.zigTriple(self.b.allocator) catch @panic("OOM"); - for (self.options.test_target_filters) |filter| { - if (std.mem.find(u8, triple_txt, filter) != null) break; - } else return; - } - - const name = std.fmt.allocPrint(self.b.allocator, "check llvm-ir {s}", .{case.name}) catch @panic("OOM"); - if (self.options.test_filters.len > 0) { - for (self.options.test_filters) |filter| { - if (std.mem.find(u8, name, filter) != null) break; - } else return; - } - - const obj = self.b.addObject(.{ - .name = "test", - .root_module = self.b.createModule(.{ - .root_source_file = self.b.addWriteFiles().add("test.zig", case.source), - - .code_model = case.params.code_model, - .error_tracing = case.params.error_tracing, - .omit_frame_pointer = case.params.omit_frame_pointer, - .optimize = case.params.optimize, - .pic = case.params.pic, - .sanitize_thread = case.params.sanitize_thread, - .single_threaded = case.params.single_threaded, - .strip = case.params.strip, - .target = target, - .unwind_tables = case.params.unwind_tables, - }), - .use_llvm = true, - }); - - obj.dll_export_fns = case.params.dll_export_fns; - obj.pie = case.params.pie; - - obj.root_module.dwarf_format = case.params.dwarf_format; - obj.root_module.no_builtin = case.params.no_builtin; - obj.root_module.red_zone = case.params.red_zone; - obj.root_module.stack_check = case.params.stack_check; - obj.root_module.stack_protector = case.params.stack_protector; - obj.root_module.valgrind = case.params.valgrind; - - // This is not very sophisticated at the moment. Eventually, we should move towards something - // like LLVM's `FileCheck` utility (https://llvm.org/docs/CommandGuide/FileCheck.html), though - // likely a more simplified version as we probably don't want a full-blown regex engine in the - // standard library... - const check = self.b.addCheckFile(obj.getEmittedLlvmIr(), switch (case.check) { - .matches => |m| .{ .expected_matches = m }, - .exact => |e| .{ .expected_exact = e }, - }); - check.setName(name); - check.max_bytes = 64 * 1024 * 1024; // allow fairly big LLVM IR files - - self.root_step.dependOn(&check.step); -} - -const LlvmIr = @This(); -const std = @import("std"); diff --git a/test/src/Runner.zig b/test/src/Runner.zig deleted file mode 100644 index 0598fbaecd48053c1f8a166bc0eaefb516d4a435..0000000000000000000000000000000000000000 --- a/test/src/Runner.zig +++ /dev/null @@ -1,2140 +0,0 @@ -const assert = std.debug.assert; -const builtin = @import("builtin"); -const std = @import("std"); -const Runner = @This(); - -gpa: std.mem.Allocator, -arena: *std.heap.ArenaAllocator, -environ_map: std.process.Environ.Map, -io: std.Io, -prog_node: std.Progress.Node, -args: Args, -host: std.Target, -server: std.zig.Server, -eb_wip: std.zig.ErrorBundle.Wip, - -const Args = struct { - @"test": ?struct { - input_dir: std.zig.Server.Message.InputDir, - dir: std.Io.Dir, - }, - manifest_file: ?std.Io.File, - zig_exe: ?std.Io.File, - lib_dir: ?std.Io.Dir, - src_dir: ?std.Io.Dir, - targets: std.ArrayList(u32), - target_bytes: std.ArrayList(u8), - - libc_runtimes_dir: ?std.Io.Dir, - enable_darling: bool, - enable_qemu: bool, - enable_rosetta: bool, - enable_wasmtime: bool, - enable_wine: bool, - - gdb_exe: ?std.Io.File, - lldb_exe: ?std.Io.File, - - quiet: bool, - - fn deinit(args: *Args, gpa: std.mem.Allocator) void { - args.targets.deinit(gpa); - args.target_bytes.deinit(gpa); - args.* = undefined; - } - - fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { - try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); - try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); - args.target_bytes.appendSliceAssumeCapacity(target); - args.target_bytes.appendAssumeCapacity(0); - } -}; - -fn deinit(runner: *Runner) void { - runner.environ_map.deinit(); - runner.prog_node.end(); - runner.args.deinit(runner.gpa); - runner.eb_wip.deinit(); - runner.* = undefined; -} - -const FailError = error{AlreadyReported} || std.mem.Allocator.Error || std.Io.Cancelable; -fn fail(runner: *Runner, comptime fmt: []const u8, args: anytype) FailError { - return runner.failString(try runner.eb_wip.printString(fmt, args)); -} -fn failString(runner: *Runner, msg: std.zig.ErrorBundle.String) FailError { - try runner.eb_wip.addRootErrorMessage(.{ - .msg = msg, - }); - return error.AlreadyReported; -} - -pub const ProtocolError = error{ - ServerReadFailed, - ServerWriteFailed, - ServerEndOfStream, -} || FailError; -pub fn runServer(runner: *Runner) ProtocolError { - runner.server.serveStringMessage(.zig_version, builtin.zig_version_string) catch |err| switch (err) { - error.WriteFailed => return error.ServerWriteFailed, - }; - while (true) { - _ = runner.arena.reset(.retain_capacity); - const arena = runner.arena.allocator(); - const hdr = runner.server.receiveMessage() catch |err| switch (err) { - error.ReadFailed => return error.ServerReadFailed, - error.EndOfStream => return error.ServerEndOfStream, - }; - switch (hdr.tag) { - else => |tag| return runner.fail("unsupported message: {t}", .{tag}), - .exit => std.process.exit(0), - .args => { - const args_body = try arena.alloc(u8, hdr.bytes_len); - runner.server.in.readSliceAll(args_body) catch unreachable; - const State = enum { - positional, - @"--zig", - @"--lib", - @"--src", - @"--target", - @"--libc-runtimes", - @"--gdb", - @"--lldb", - }; - var state: State = .positional; - const state_expected: std.enums.EnumArray( - State, - enum { path, dir, file, string }, - ) = .init(.{ - .positional = .path, - .@"--zig" = .file, - .@"--lib" = .dir, - .@"--src" = .dir, - .@"--target" = .string, - .@"--libc-runtimes" = .dir, - .@"--gdb" = .file, - .@"--lldb" = .file, - }); - var input_dir: std.zig.Server.Message.InputDir = .cwd; - var args_body_offset: usize = 0; - while (args_body.len - args_body_offset > 0) { - const arg: std.zig.Client.Message.Arg = - @fromBackingInt(args_body[args_body_offset]); - args_body_offset += 1; - arg: switch (arg) { - .string => { - const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; - const string = args_body[args_body_offset..end]; - args_body_offset = end + 1; - switch (state) { - .positional => if (std.mem.eql(u8, string, "--target")) { - state = .@"--target"; - continue; - } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { - try runner.args.addTarget(runner.gpa, target); - } else if (std.mem.eql(u8, string, "--libc-runtimes")) { - state = .@"--libc-runtimes"; - continue; - } else if (std.mem.eql(u8, string, "-fdarling")) { - runner.args.enable_darling = true; - } else if (std.mem.eql(u8, string, "-fqemu")) { - runner.args.enable_qemu = true; - } else if (std.mem.eql(u8, string, "-frosetta")) { - runner.args.enable_rosetta = true; - } else if (std.mem.eql(u8, string, "-fwasmtime")) { - runner.args.enable_wasmtime = true; - } else if (std.mem.eql(u8, string, "-fwine")) { - runner.args.enable_wine = true; - } else if (std.mem.eql(u8, string, "--gdb")) { - state = .@"--gdb"; - continue; - } else if (std.mem.eql(u8, string, "--lldb")) { - state = .@"--lldb"; - continue; - } else if (std.mem.eql(u8, string, "--quiet")) { - runner.args.quiet = true; - }, - .@"--target" => try runner.args.addTarget(runner.gpa, string), - else => return runner.fail( - "\"{t}\" expected {t}, got {t}", - .{ state, state_expected.get(state), arg }, - ), - } - }, - .prefix => { - const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; - const string = args_body[args_body_offset..end]; - args_body_offset = end + 1; - assert(state == .positional); - state = if (std.mem.eql(u8, string, "--zig=")) - .@"--zig" - else if (std.mem.eql(u8, string, "--lib=")) - .@"--lib" - else if (std.mem.eql(u8, string, "--src=")) - .@"--src" - else if (std.mem.eql(u8, string, "--libc-runtimes=")) - .@"--libc-runtimes" - else if (std.mem.eql(u8, string, "--gdb=")) - .@"--gdb" - else if (std.mem.eql(u8, string, "--lldb=")) - .@"--lldb" - else - return runner.fail("unsupported arg prefix: {q}", .{string}); - continue; - }, - .suffix => { - const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; - const string = args_body[args_body_offset..end]; - args_body_offset = end + 1; - return runner.fail("unsupported arg suffix: {q}", .{string}); - }, - .input_dir => { - input_dir = @fromBackingInt(@backingInt(input_dir) + 1); - continue :arg .output_dir; - }, - .output_dir => { - const dir_handle: *align(1) const std.Io.Dir.Handle = @ptrCast( - args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)], - ); - args_body_offset += @sizeOf(std.Io.Dir.Handle); - const dir = runner.io.vtable.inheritParentDir( - runner.io.userdata, - dir_handle.*, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail( - "unable to inherit parent dir: {t}", - .{e}, - ), - }; - switch (state) { - .positional => { - if (runner.args.@"test" != null or - runner.args.manifest_file != null) return runner.fail( - "{t} specified multiple times", - .{state}, - ); - runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir }; - const manifest_path = "manifest"; - runner.args.manifest_file = dir.openFile( - runner.io, - manifest_path, - .{}, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail( - "unable to open manifest file: {t}", - .{e}, - ), - }; - try runner.discoverInput(input_dir, manifest_path); - }, - .@"--lib" => { - if (runner.args.lib_dir != null) return runner.fail( - "\"{t}\" specified multiple times", - .{state}, - ); - runner.args.lib_dir = dir; - }, - .@"--src" => { - if (runner.args.src_dir != null) return runner.fail( - "\"{t}\" specified multiple times", - .{state}, - ); - runner.args.src_dir = dir; - }, - .@"--libc-runtimes" => { - if (runner.args.libc_runtimes_dir != null) return runner.fail( - "\"{t}\" specified multiple times", - .{state}, - ); - runner.args.libc_runtimes_dir = dir; - }, - else => return runner.fail( - "\"{t}\" expected {t}, got {t}", - .{ state, state_expected.get(state), arg }, - ), - } - }, - .input_file, .input_file_content, .output_file => { - const file_handle: *align(1) const std.Io.File.Handle = @ptrCast( - args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)], - ); - args_body_offset += @sizeOf(std.Io.File.Handle); - const file = runner.io.vtable.inheritParentFile( - runner.io.userdata, - file_handle.*, - .{ .nonblocking = false }, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail( - "unable to inherit parent file: {t}", - .{e}, - ), - }; - switch (state) { - .positional => { - if (runner.args.@"test" != null or - runner.args.manifest_file != null) return runner.fail( - "{t} specified multiple times", - .{state}, - ); - runner.args.manifest_file = file; - }, - .@"--zig" => { - if (runner.args.zig_exe != null) return runner.fail( - "\"{t}\" specified multiple times", - .{state}, - ); - runner.args.zig_exe = file; - }, - .@"--gdb" => { - if (runner.args.gdb_exe != null) return runner.fail( - "\"{t}\" specified multiple times", - .{state}, - ); - runner.args.gdb_exe = file; - }, - .@"--lldb" => { - if (runner.args.lldb_exe != null) return runner.fail( - "\"{t}\" specified multiple times", - .{state}, - ); - runner.args.lldb_exe = file; - }, - else => return runner.fail( - "\"{t}\" expected {t}, got {t}", - .{ state, state_expected.get(state), arg }, - ), - } - }, - } - state = .positional; - } - }, - .query_test_metadata => { - const expected_panic_msgs = try arena.alloc(u32, runner.args.targets.items.len); - @memset(expected_panic_msgs, 0); - runner.server.serveTestMetadata(.{ - .names = runner.args.targets.items, - .expected_panic_msgs = expected_panic_msgs, - .string_bytes = runner.args.target_bytes.items, - }) catch |err| switch (err) { - error.WriteFailed => return error.ServerWriteFailed, - }; - }, - .run_test => { - const test_index = runner.server.receiveBody_u32() catch unreachable; - runner.server.serveBodylessMessage(.test_started) catch |err| switch (err) { - error.WriteFailed => return error.ServerWriteFailed, - }; - runner.server.serveTestResults(.{ - .index = test_index, - .flags = .{ - .status = if (runner.testOne(test_index)) - .pass - else |err| status: switch (err) { - error.Canceled, - error.OutOfMemory, - error.ServerWriteFailed, - => |e| return e, - error.AlreadyReported => { - var eb = try runner.eb_wip.toOwnedBundle(""); - defer eb.deinit(runner.gpa); - assert(eb.errorMessageCount() > 0); // already reported what? - runner.server.serveErrorBundle( - .error_bundle, - eb, - ) catch |server_err| switch (server_err) { - error.WriteFailed => return error.ServerWriteFailed, - }; - const eb_wip: std.zig.ErrorBundle.Wip = try .init(runner.gpa); - runner.eb_wip.deinit(); - runner.eb_wip = eb_wip; - break :status .fail; - }, - error.SkipTest => .skip, - }, - .fuzz = false, - .log_err_count = 0, - .leak_count = 0, - }, - }) catch |err| switch (err) { - error.WriteFailed => return error.ServerWriteFailed, - }; - assert(runner.eb_wip.root_list.items.len == 0); // failed to report - }, - } - } -} - -fn discoverInput( - runner: *Runner, - dir: std.zig.Server.Message.InputDir, - sub_path: []const u8, -) error{ServerWriteFailed}!void { - return runner.discoverInputInner(dir, sub_path) catch |err| switch (err) { - error.WriteFailed => return error.ServerWriteFailed, - }; -} -fn discoverInputInner( - runner: *Runner, - dir: std.zig.Server.Message.InputDir, - sub_path: []const u8, -) std.Io.Writer.Error!void { - try runner.server.serveMessageHeader(.{ - .tag = .discovered_inputs, - .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + - sub_path.len + 1), - }); - try runner.server.out.writeInt(u32, @backingInt(dir), .little); - try runner.server.out.writeAll(sub_path); - try runner.server.out.writeByte(0); - try runner.server.out.flush(); -} - -pub const SourceReader = struct { - const Io = std.Io; - - backing: *std.Io.Reader, - line_aw: std.Io.Writer.Allocating, - interface: std.Io.Reader, - line: u32, - eb_wip: *std.zig.ErrorBundle.Wip, - src_path: std.zig.ErrorBundle.String, - - pub fn init( - reader: *Io.Reader, - gpa: std.mem.Allocator, - eb_wip: *std.zig.ErrorBundle.Wip, - src_path: std.zig.ErrorBundle.String, - ) SourceReader { - return .{ - .backing = reader, - .line_aw = .init(gpa), - .interface = .{ - .vtable = &.{ - .stream = stream, - .discard = discard, - .readVec = readVec, - .rebase = rebase, - }, - .buffer = &.{}, - .seek = 0, - .end = 0, - }, - .line = 0, - .eb_wip = eb_wip, - .src_path = src_path, - }; - } - fn deinit(s: *SourceReader) void { - s.line_aw.deinit(); - } - fn stream(r: *Io.Reader, _: *Io.Writer, _: Io.Limit) Io.Reader.StreamError!usize { - const s: *SourceReader = @fieldParentPtr("interface", r); - try s.bufferLine(); - return 0; - } - fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { - const s: *SourceReader = @fieldParentPtr("interface", r); - const remaining = r.buffered(); - if (remaining.len > 0) { - const n = limit.minInt(remaining.len); - r.toss(n); - return n; - } - try s.bufferLine(); - return 0; - } - fn readVec(r: *Io.Reader, _: [][]u8) Io.Reader.Error!usize { - const s: *SourceReader = @fieldParentPtr("interface", r); - try s.bufferLine(); - return 0; - } - fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void { - const s: *SourceReader = @fieldParentPtr("interface", r); - defer s.updateBuffer(); - s.line_aw.ensureUnusedCapacity(capacity) catch |err| switch (err) { - error.OutOfMemory => return error.ReadFailed, - }; - } - fn updateBuffer(s: *SourceReader) void { - s.interface.buffer = s.line_aw.writer.buffer; - s.interface.end = s.line_aw.writer.end; - } - fn bufferLine(s: *SourceReader) Io.Reader.Error!void { - defer s.updateBuffer(); - { - const written = s.line_aw.written(); - const start = if (std.mem.findScalarLast(u8, written[0..s.interface.seek], '\n')) |newline| - newline + 1 - else - 0; - s.line += @intCast(std.mem.countScalar(u8, written[0..start], '\n')); - const preserve = written[start..]; - @memmove(written[0..preserve.len], preserve); - s.line_aw.shrinkRetainingCapacity(preserve.len); - s.interface.seek -= start; - } - const n = s.backing.streamDelimiterEnding(&s.line_aw.writer, '\n') catch |err| switch (err) { - error.ReadFailed => |e| return e, - error.WriteFailed => return error.ReadFailed, - }; - if (std.mem.endsWith(u8, s.line_aw.written(), "\r")) s.line_aw.writer.end -= 1; - const buffered = s.backing.buffered(); - if (n == 0 and buffered.len == 0) return error.EndOfStream; - if (std.mem.startsWith(u8, buffered, "\n")) { - s.line_aw.writer.writeByte('\n') catch |err| switch (err) { - error.WriteFailed => return error.ReadFailed, - }; - s.backing.toss(1); - } - } - fn fail( - s: *SourceReader, - src_loc: []const u8, - comptime fmt: []const u8, - args: anytype, - ) FailError { - return s.failString(src_loc, try s.eb_wip.printString(fmt, args)); - } - fn failString( - s: *SourceReader, - src_loc: []const u8, - msg: std.zig.ErrorBundle.String, - ) FailError { - const line: u32, const column: u32, const source_line = src_loc: { - const pos = src_loc.ptr - s.interface.buffer.ptr; - const end = std.mem.findScalarPos(u8, s.interface.buffer, pos, '\n') orelse - s.interface.buffer.len; - const start = if (std.mem.findScalarLast(u8, s.interface.buffer[0..end], '\n')) |newline| - newline + 1 - else - 0; - break :src_loc .{ - @intCast(s.line + std.mem.countScalar(u8, s.interface.buffer[0..start], '\n')), - @intCast(pos - start), - s.interface.buffer[start..end], - }; - }; - try s.eb_wip.addRootErrorMessage(.{ - .msg = msg, - .src_loc = try s.eb_wip.addSourceLocation(.{ - .src_path = s.src_path, - .line = line, - .column = column, - .span_start = column, - .span_main = column, - .span_end = @intCast(column + src_loc.len), - .source_line = try s.eb_wip.addString(source_line), - }), - }); - return error.AlreadyReported; - } -}; -pub const DelimitedReader = struct { - const Io = std.Io; - - unlimited: *Io.Reader, - remaining: Io.Limit, - delimiter: []const u8, - interface: Io.Reader, - - pub fn init(reader: *Io.Reader, delimiter: []const u8, buffer: []u8) DelimitedReader { - return .{ - .unlimited = reader, - .remaining = .nothing, - .delimiter = delimiter, - .interface = .{ - .vtable = &.{ - .stream = stream, - .discard = discard, - }, - .buffer = buffer, - .seek = 0, - .end = 0, - }, - }; - } - fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { - const d: *DelimitedReader = @fieldParentPtr("interface", r); - const block = try d.unlimited.peekGreedy(d.delimiter.len); - d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse - block.len - d.delimiter.len + 1); - if (d.remaining == .nothing) return error.EndOfStream; - const n = try d.unlimited.stream(w, limit.min(d.remaining)); - d.remaining = d.remaining.subtract(n).?; - return n; - } - fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { - const d: *DelimitedReader = @fieldParentPtr("interface", r); - const block = try d.unlimited.peekGreedy(d.delimiter.len); - d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse - block.len - d.delimiter.len + 1); - if (d.remaining == .nothing) return error.EndOfStream; - const n = try d.unlimited.discard(limit.min(d.remaining)); - d.remaining = d.remaining.subtract(n).?; - return n; - } -}; - -const Update = struct { - target_query: []const u8, - manifest_sr: SourceReader, - compiler: ?Compiler, - src_dir: std.Io.Dir, - prog_node: std.Progress.Node, - mtime: std.Io.Timestamp, - num: usize, - allow_skip: bool, -}; - -const TestError = error{ - SkipTest, -} || FailError; -fn testOne(runner: *Runner, test_index: u32) (error{ServerWriteFailed} || TestError)!void { - const target_query = - std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0); - const manifest_file = runner.args.manifest_file orelse - return runner.fail("missing \"path/to/test\" arg", .{}); - var manifest_buffer: [512]u8 = undefined; - var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer); - var update: Update = .{ - .target_query = target_query, - .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: { - var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; - const src_path = src_path: { - break :src_path path_buffer[0 .. manifest_file.realPath( - runner.io, - &path_buffer, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => break :src_path "manifest", - }]; - }; - break :src_path_string try runner.eb_wip.addString(src_path); - }), - .compiler = null, - .src_dir = (runner.args.src_dir orelse - return runner.fail("missing \"--src=path/to/src\" arg", .{})).createDirPathOpen( - runner.io, - target_query, - .{}, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to create {q}: {t}", .{ target_query, e }), - }, - .prog_node = .none, - .mtime = .now(runner.io, .real), - .num = 0, - .allow_skip = true, - }; - defer { - update.manifest_sr.deinit(); - update.src_dir.close(runner.io); - update.prog_node.end(); - } - - // hack around `std.Io.Reader` interface bugs by ensuring the - // buffer is larger than the longest supported manifest line - try update.manifest_sr.line_aw.ensureTotalCapacityPrecise(512); - update.manifest_sr.updateBuffer(); - - var skip_delimiter = false; - while (update.manifest_sr.interface.takeSentinel('\n')) |line| { - var line_it = std.mem.tokenizeScalar(u8, line, ' '); - const cmd_src_loc = line_it.next() orelse continue; - if (skip_delimiter) { - assert(std.mem.eql(u8, cmd_src_loc, "#}")); - skip_delimiter = false; - const unexpected = line_it.rest(); - if (unexpected.len > 0) - return update.manifest_sr.fail(unexpected, "unexpected argument {q}", .{unexpected}); - continue; - } - const cmd = std.meta.stringToEnum(Command, std.mem.cutPrefix(u8, cmd_src_loc, "#") orelse - return update.manifest_sr.fail(cmd_src_loc, "expected command", .{})) orelse - return update.manifest_sr.fail(cmd_src_loc, "unknown command", .{}); - const arg = switch (cmd) { - .todo, .exe, .lib, .obj, .update => null, - .skip, .write, .delete, .check => line_it.next() orelse - return update.manifest_sr.fail(line_it.rest()[0..0], "missing argument", .{}), - }; - var contents_file: ?std.Io.File = null; - defer if (contents_file) |file| file.close(runner.io); - var contents_buffer: [512]u8 = undefined; - var contents_impl: union(enum) { - ending: std.Io.Reader, - dr: DelimitedReader, - fr: std.Io.File.Reader, - } = undefined; - const contents_r: *std.Io.Reader = contents_r: switch (cmd) { - .skip, .delete => { - const unexpected = line_it.rest(); - if (unexpected.len > 0) return update.manifest_sr.fail( - unexpected, - "unexpected argument {q}", - .{unexpected}, - ); - contents_impl = .{ .ending = .ending_instance }; - break :contents_r &contents_impl.ending; - }, - else => { - const contents_path = line_it.rest(); - if (contents_path.len == 0) { - contents_impl = .{ .ending = .ending_instance }; - break :contents_r &contents_impl.ending; - } - - if (std.mem.eql(u8, contents_path, "{")) { - contents_impl = .{ - .dr = .init(&update.manifest_sr.interface, "#}", &contents_buffer), - }; - skip_delimiter = true; - break :contents_r &contents_impl.dr.interface; - } - - const args_test = runner.args.@"test" orelse return update.manifest_sr.fail( - contents_path, - "contents file requires directory test", - .{}, - ); - try runner.discoverInput(args_test.input_dir, contents_path); - - contents_file = - args_test.dir.openFile(runner.io, contents_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return update.manifest_sr.fail( - contents_path, - "unable to open contents file: {t}", - .{e}, - ), - }; - contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) }; - break :contents_r &contents_impl.fr.interface; - }, - }; - - runner.handleCommand(&update, cmd_src_loc, cmd, arg, contents_r) catch |err| switch (err) { - error.Canceled, - error.OutOfMemory, - error.AlreadyReported, - error.SkipTest, - => |e| return e, - error.ContentsReadFailed => switch (contents_impl) { - .ending => unreachable, - .dr => switch (manifest_fr.err orelse return error.OutOfMemory) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to read manifest: {t}", .{e}), - }, - .fr => |fr| switch (fr.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to read contents file: {t}", .{e}), - }, - }, - }; - } else |err| switch (err) { - error.ReadFailed => switch (manifest_fr.err orelse return error.OutOfMemory) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to read manifest: {t}", .{e}), - }, - error.StreamTooLong => unreachable, - error.EndOfStream => if (skip_delimiter) - return runner.fail("manifest missing \"#}}\" delimiter", .{}), - } - if (update.compiler) |*comp| { - if (comp.state != .idle) return runner.fail("missing \"#check\"", .{}); - try comp.exit(); - } -} -const Command = enum { todo, skip, exe, lib, obj, write, delete, update, check }; -fn handleCommand( - runner: *Runner, - update: *Update, - cmd_src_loc: []const u8, - cmd: Command, - arg: ?[]const u8, - contents_r: *std.Io.Reader, -) (error{ContentsReadFailed} || TestError)!void { - var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; - const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t}{s}{s}", .{ - cmd, if (arg) |_| " " else "", arg orelse "", - }) catch &name_buffer, 0); - defer cmd_prog_node.end(); - - cmd: switch (cmd) { - .todo => _ = contents_r.discardRemaining() catch |err| switch (err) { - error.ReadFailed => {}, - }, - .skip => if (!update.allow_skip) return update.manifest_sr.fail( - cmd_src_loc, - "\"#skip\" must appear before other commands", - .{}, - ) else if (std.mem.eql(u8, update.target_query, arg.?)) return error.SkipTest, - .exe, .lib, .obj => runner.spawnCompiler(&update.compiler, update.src_dir, switch (cmd) { - else => unreachable, - .exe => .Exe, - .lib => .Lib, - .obj => .Obj, - }, contents_r, target: { - var component_it = std.mem.splitBackwardsScalar(u8, update.target_query, '-'); - const pic_str = component_it.next() orelse - return runner.fail("target {q} missing pic", .{update.target_query}); - const pic = std.meta.stringToEnum(Compiler.Target.Pic, pic_str) orelse - return runner.fail("target {q} unknown pic {q}", .{ update.target_query, pic_str }); - const linker_str = component_it.next() orelse - return runner.fail("target {q} missing linker", .{update.target_query}); - const linker = std.meta.stringToEnum(Compiler.Target.Linker, linker_str) orelse - return runner.fail("target {q} unknown linker {q}", .{ update.target_query, linker_str }); - const backend_str = component_it.next() orelse - return runner.fail("target {q} missing backend", .{update.target_query}); - const backend = std.meta.stringToEnum(Compiler.Target.Backend, backend_str) orelse - return runner.fail("target {q} unknown backend {q}", .{ update.target_query, backend_str }); - const cache_mode_str = component_it.next() orelse - return runner.fail("target {q} missing cache mode", .{update.target_query}); - const cache_mode = std.meta.stringToEnum(Compiler.Target.CacheMode, cache_mode_str) orelse - return runner.fail("target {q} unknown cache mode {q}", .{ update.target_query, cache_mode_str }); - const triple = component_it.rest(); - break :target .{ - .triple = triple, - .resolved = std.zig.system.resolveTargetQuery( - runner.io, - std.Build.parseTargetQuery(.{ - .arch_os_abi = triple, - .object_format = switch (backend) { - .sema, .selfhosted, .llvm => null, - .cbe => "c", - }, - }) catch |err| switch (err) { - error.ParseFailed => return runner.fail("unable to parse triple {q}", .{ - triple, - }), - }, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to resolve target {q}: {t}", .{ triple, e }), - }, - .cache_mode = cache_mode, - .backend = backend, - .linker = linker, - .pic = pic, - }; - }) catch |err| switch (err) { - error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e, - error.ReadFailed => return error.ContentsReadFailed, - }, - .write => { - const file = update.src_dir.createFile(runner.io, arg.?, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return update.manifest_sr.fail(arg.?, "unable to create file: {t}", .{e}), - }; - defer file.close(runner.io); - var fw_buffer: [512]u8 = undefined; - var fw = file.writer(runner.io, &fw_buffer); - _ = contents_r.streamRemaining(&fw.interface) catch |err| switch (err) { - error.ReadFailed => return error.ContentsReadFailed, - error.WriteFailed => switch (fw.err.?) { - error.Canceled => |e| return e, - else => |e| return update.manifest_sr.fail( - arg.?, - "unable to write file: {t}", - .{e}, - ), - }, - }; - fw.flush() catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return update.manifest_sr.fail( - arg.?, - "unable to write file: {t}", - .{e}, - ), - }; - file.setTimestamps(runner.io, .{ .modify_timestamp = .{ - .new = update.mtime, - } }) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return update.manifest_sr.fail( - arg.?, - "unable to update file timestamps: {t}", - .{e}, - ), - }; - }, - .delete => update.src_dir.deleteFile(runner.io, arg.?) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return update.manifest_sr.fail(arg.?, "unable to delete file: {t}", .{e}), - }, - .update => { - const comp = &(update.compiler orelse return update.manifest_sr.fail( - cmd_src_loc, - "missing \"#exe\", \"#lib\", or \"#obj\"", - .{}, - )); - update.prog_node.end(); - update.prog_node = runner.prog_node.start( - switch (contents_r.readSliceShort(&name_buffer) catch |err| switch (err) { - error.ReadFailed => return error.ContentsReadFailed, - }) { - 0 => switch (update.num) { - 0 => "initial update", - else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch - &name_buffer, - }, - else => |name_len| name_buffer[0..name_len], - }, - 0, - ); - _ = contents_r.discardRemaining() catch |err| switch (err) { - error.ReadFailed => return error.ContentsReadFailed, - }; - if (comp.state != .idle) - return update.manifest_sr.fail(cmd_src_loc, "missing \"#check\"", .{}); - comp.client.serveBodylessMessage(.update) catch |err| switch (err) { - error.WriteFailed => switch (comp.fw.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to send message to compiler: {t}", .{e}), - }, - }; - comp.state = .update; - update.mtime = update.mtime.addDuration(.fromSeconds(2)); - update.num += 1; - }, - .check => { - const check = std.meta.stringToEnum(Compiler.Check, arg.?) orelse - return update.manifest_sr.fail(arg.?, "unknown check", .{}); - const expected = - contents_r.allocRemaining(runner.gpa, .unlimited) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.ReadFailed => return error.ContentsReadFailed, - error.StreamTooLong => unreachable, // .unlimited - }; - defer runner.gpa.free(expected); - - const comp = &(update.compiler orelse return update.manifest_sr.fail( - cmd_src_loc, - "missing \"#exe\", \"#lib\", or \"#obj\"", - .{}, - )); - if (comp.state != .update) - return update.manifest_sr.fail(cmd_src_loc, "missing \"#update\"", .{}); - const stderr = comp.mr.reader(1); - while (try comp.receiveMessage()) |message| switch (message.tag) { - else => {}, // Ignore other messages, - .config => { - var body_r: std.Io.Reader = .fixed(message.body); - comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch - unreachable; - }, - .emit_digest => { - var body_r: std.Io.Reader = .fixed(message.body); - _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch - unreachable; - const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch - unreachable; - try comp.checkSuccess(check, expected, update.src_dir, digest); - }, - .error_bundle => { - const eb = std.zig.Server.allocErrorBundle( - runner.arena.allocator(), - message.body, - ) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.EndOfStream => return runner.fail("error bundle truncated", .{}), - }; - switch (check) { - .errors => { - var error_aw: std.Io.Writer.Allocating = .init(runner.gpa); - defer error_aw.deinit(); - eb.renderToWriter(.{ - .include_reference_trace = false, - .include_source_line = false, - .include_log_text = true, - }, &error_aw.writer) catch |err| switch (err) { - error.WriteFailed => return error.OutOfMemory, - }; - const actual = error_aw.written(); - std.mem.replaceScalar(u8, actual, '\\', '/'); - std.testing.expectEqualStrings(expected, actual) catch |err| switch (err) { - error.TestExpectedEqual => return runner.fail( - "errors did not match expected", - .{}, - ), - }; - }, - .stdout, .exit, .gdb, .lldb => if (eb.errorMessageCount() > 0) { - eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { - error.Canceled => {}, - else => {}, - }; - return runner.fail("unexpected compile errors", .{}); - }, - } - comp.state = .idle; - break :cmd; - }, - }; - - const buffered_stderr = stderr.buffered(); - if (buffered_stderr.len > 0) { - if (comp.allow_compiler_stderr) { - std.log.info("stderr:\n{s}", .{buffered_stderr}); - } else { - return runner.fail("unexpected stderr:\n{s}", .{buffered_stderr}); - } - } - - try comp.exit(); - return runner.fail("compiler failed to send terminating error_bundle", .{}); - }, - } - assert(contents_r.discardRemaining() catch |err| switch (err) { - error.ReadFailed => return error.ContentsReadFailed, - } == 0); - switch (cmd) { - .todo, .skip => {}, - else => update.allow_skip = false, - } -} - -fn spawnCompiler( - runner: *Runner, - compiler: *?Compiler, - src_dir: std.Io.Dir, - output_mode: std.lang.OutputMode, - args_r: *std.Io.Reader, - target: Compiler.Target, -) (std.Io.Reader.ShortError || FailError)!void { - const gpa = runner.gpa; - const arena = runner.arena.allocator(); - if (compiler.*) |*comp| { - try comp.exit(); - compiler.* = null; - } - - var argv: std.ArrayList([]const u8) = .empty; - defer argv.deinit(gpa); - try argv.appendSlice(gpa, &.{ - "zig", - switch (output_mode) { - .Obj => "build-obj", - .Exe => "build-exe", - .Lib => "build-lib", - }, - "-target", - target.triple, - "--cache-dir", - ".zig-cache", - }); - switch (target.cache_mode) { - .whole => {}, - .incremental => try argv.append(gpa, "-fincremental"), - } - var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; - if (target.backend) |b| switch (b) { - .sema => try argv.append(gpa, "-fno-emit-bin"), - .selfhosted => try argv.append(gpa, "-fno-llvm"), - .llvm => try argv.append(gpa, "-fllvm"), - .cbe => try argv.append(gpa, "-ofmt=c"), - } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse - return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) - .realPath(runner.io, &path_buffer) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to get lib path: {t}", .{e}), - }] }); - switch (target.linker) { - .lld => try argv.appendSlice(gpa, &.{ "-flld", "-fno-new-linker" }), - .old => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fno-new-linker" }), - .new => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fnew-linker" }), - } - switch (target.pic) { - .nopic => try argv.appendSlice(gpa, &.{ "-fno-PIC", "-fno-PIE" }), - .pic => try argv.appendSlice(gpa, &.{ "-fPIC", "-fno-PIE" }), - .pie => try argv.appendSlice(gpa, &.{ "-fPIC", "-fPIE" }), - } - - var need: usize = 1; - var state: enum { positional, @"--name" } = .positional; - var root_name: ?[]const u8 = null; - while (true) { - const done = if (args_r.fill(need)) false else |err| switch (err) { - error.ReadFailed => |e| return e, - error.EndOfStream => true, - }; - const buffered = args_r.buffered(); - const start = std.mem.findNone(u8, buffered, "\n ") orelse buffered.len; - const end = std.mem.findAnyPos(u8, buffered, start, "\n ") orelse buffered.len; - if (end - start > 0) { - const arg = try arena.dupe(u8, buffered[start..end]); - switch (state) { - .positional => if (std.mem.eql(u8, arg, "--name")) { - state = .@"--name"; - } else if (std.mem.cutPrefix(u8, arg, "-M")) |module| { - var module_name_it = std.mem.splitScalar(u8, module, '='); - const module_name = module_name_it.next().?; - root_name = root_name orelse module_name; - }, - .@"--name" => { - root_name = arg; - state = .positional; - }, - } - try argv.append(gpa, arg); - args_r.toss(end); - need = 1; - } else { - args_r.toss(start); - need = buffered.len - start + 1; - } - if (done) break; - } - try argv.append(gpa, "--listen=-"); - - const out_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ - .root_name = root_name orelse "root", - .cpu_arch = target.resolved.cpu.arch, - .os_tag = target.resolved.os.tag, - .ofmt = target.resolved.ofmt, - .abi = target.resolved.abi, - .output_mode = output_mode, - }); - - var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; - const comp_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "compile {s}", .{ - out_name, - }) catch &name_buffer, 0); - errdefer comp_prog_node.end(); - - const child = std.process.spawn(runner.io, .{ - .exe = .{ .file = runner.args.zig_exe orelse - return runner.fail("missing \"--zig=path/to/zig\" arg", .{}) }, - .argv = argv.items, - .cwd = .{ .dir = src_dir }, - .progress_node = comp_prog_node, - .stdin = .pipe, - .stdout = .pipe, - .stderr = .pipe, - }) catch |err| switch (err) { - error.Canceled, error.OutOfMemory => |e| return e, - else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}), - }; - compiler.* = .{ - .runner = runner, - .prog_node = comp_prog_node, - .allow_compiler_stderr = true, - .output_mode = output_mode, - .state = .idle, - .child = child, - .fw = undefined, - .mr_buffer = undefined, - .mr = undefined, - .client = undefined, - .target = target, - .out_name = out_name, - .config = null, - }; - const comp = &compiler.*.?; - comp.fw = child.stdin.?.writerStreaming(runner.io, &.{}); - comp.mr.init(gpa, runner.io, comp.mr_buffer.toStreams(), &.{ - child.stdout.?, child.stderr.?, - }); - comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface }; -} -const Compiler = struct { - runner: *Runner, - prog_node: std.Progress.Node, - allow_compiler_stderr: bool, - output_mode: std.lang.OutputMode, - state: enum { idle, update }, - child: std.process.Child, - fw: std.Io.File.Writer, - mr_buffer: std.Io.File.MultiReader.Buffer(2), - mr: std.Io.File.MultiReader, - client: std.zig.Client, - target: Target, - out_name: []const u8, - config: ?std.zig.Server.Message.Config, - - const Target = struct { - triple: []const u8, - resolved: std.Target, - cache_mode: CacheMode, - backend: ?Backend, - linker: Linker, - pic: Pic, - - const CacheMode = enum { whole, incremental }; - - const Backend = enum { - /// Run semantic analysis only. Runtime output will not be tested, but we still verify - /// that compilation succeeds. Corresponds to `-fno-emit-bin`. - sema, - /// Use the self-hosted code generation backend for this target. - /// Corresponds to `-fno-llvm -fno-lld`. - selfhosted, - /// Use the LLVM backend. - /// Corresponds to `-fllvm -flld`. - llvm, - /// Use the C backend. The output is compiled with `zig cc`. - /// Corresponds to `-ofmt=c`. - cbe, - }; - - const Linker = enum { old, new, lld }; - - const Pic = enum { nopic, pic, pie }; - }; - - fn receiveMessage(comp: *Compiler) FailError!?struct { - tag: std.zig.Server.Message.Tag, - body: []const u8, - } { - const header = comp.client.receiveMessageWithMultiReader( - &comp.mr, - .none, - ) catch |err| switch (err) { - error.Canceled, error.OutOfMemory => |e| return e, - error.Timeout => unreachable, - error.EndOfStream => return null, - else => |e| return comp.runner.fail("unable to receive message: {t}", .{e}), - }; - const body = comp.client.in.take(header.bytes_len) catch unreachable; - const stderr = comp.mr.reader(1); - if (stderr.bufferedLen() > 0) { - if (comp.allow_compiler_stderr) { - std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() }); - } else { - return comp.runner.fail("{t} unexpected stderr:\n{s}", .{ - header.tag, stderr.buffered(), - }); - } - stderr.tossBuffered(); - } - return .{ .tag = header.tag, .body = body }; - } - - const Check = enum { errors, stdout, exit, gdb, lldb }; - fn checkSuccess( - comp: *Compiler, - check: Check, - expected: []const u8, - src_dir: std.Io.Dir, - digest: *const std.Build.Cache.BinDigest, - ) FailError!void { - const runner = comp.runner; - const gpa = runner.gpa; - const arena = runner.arena.allocator(); - const io = runner.io; - const target = &comp.target.resolved; - const config = &comp.config.?; - var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; - - // After a failure while running a script, the debugger starts accepting commands from stdin, and - // because it is empty, the debugger exits normally with status 0. Choose a non-zero status to - // return from the debugger script instead to detect it running to completion and indicate success. - const debug_success = 99; - - const tmp_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ "tmp"; - src_dir.createDirPath(io, tmp_dir_path) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to create dir path {q}: {t}", .{ - tmp_dir_path, e, - }), - }; - - var argv: std.ArrayList([]const u8) = .initBuffer(try arena.alloc([]const u8, 5 + 4)); - var environ_map: std.process.Environ.Map = .init(gpa); - defer environ_map.deinit(); - try environ_map.putAll(&runner.environ_map); - var script_buffer: [512]u8 = undefined; - const script_path, const script_file, var script_fw = script: switch (check) { - .errors => return, - .stdout, .exit => .{ undefined, null, undefined }, - .gdb => { - const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; - argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); - const script_file = src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to create debugger script {q}: {t}", .{ - script_path, e, - }), - }; - errdefer script_file.close(io); - var script_fw = script_file.writer(io, &script_buffer); - script_fw.interface.writeAll( - \\set remotetimeout 0 - \\ - \\ - ) catch |err| switch (err) { - error.WriteFailed => switch (script_fw.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ script_path, e }), - }, - }; - break :script .{ script_path, script_file, script_fw }; - }, - .lldb => { - const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "lldb"; - argv.appendSliceAssumeCapacity(&.{ "lldb", "--batch", "--source", script_path, "--" }); - try environ_map.put("LANG", "C.UTF-8"); // affects output formatting - const script_file = src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to create debugger script {q}: {t}", .{ - script_path, - e, - }), - }; - errdefer script_file.close(io); - var script_fw = script_file.writer(io, &script_buffer); - script_fw.interface.writeAll( - \\settings set frame-format 'frame #${frame.index}:{ ${module.file.basename}{\`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${line.file.basename}:${line.number}{:${line.column}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\n' - \\settings set plugin.process.gdb-remote.packet-timeout 0 - \\settings set stop-line-count-after 0 - \\settings set stop-line-count-before 0 - \\ - ) catch |err| switch (err) { - error.WriteFailed => switch (script_fw.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ script_path, e }), - }, - }; - break :script .{ script_path, script_file, script_fw }; - }, - }; - defer if (script_file) |file| file.close(io); - - const out_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ - "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); - const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir_path, comp.out_name }); - const bin_path = switch (comp.target.backend.?) { - .sema => return, - .selfhosted, .llvm => out_path, - .cbe => try comp.compileC(src_dir, out_path), - }; - - const need_cross_libc = - target.os.tag == .linux and config.flags.link_libc and config.flags.link_mode == .dynamic; - const use_executor = use_executor: switch (std.zig.system.getExternalExecutor(io, target, .{ - .host_cpu_arch = runner.host.cpu.arch, - .host_os_tag = runner.host.os.tag, - .qemu_fixes_dl = need_cross_libc and runner.args.libc_runtimes_dir != null, - .link_mode = config.flags.link_mode, - .link_libc = config.flags.link_libc, - })) { - .bad_dl, .bad_os_or_cpu => { - // This binary cannot be executed on this host. - if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++ - "execute binaries for foreign target {q}", .{ - try runner.host.zigTriple(arena), comp.target.triple, - }); - return; - }, - .native => false, - .darling => |executor| if (runner.args.enable_darling) { - argv.appendAssumeCapacity(executor); - break :use_executor true; - } else continue :use_executor .bad_os_or_cpu, - .qemu => |executor| if (runner.args.enable_qemu) { - argv.appendAssumeCapacity(executor); - if (need_cross_libc) { - const libc_runtimes_dir = runner.args.libc_runtimes_dir orelse - continue :use_executor .bad_os_or_cpu; - const libc_runtimes_path = path_buffer[0 .. libc_runtimes_dir.realPath( - io, - &path_buffer, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to get libc runtimes path: {t}", .{e}), - }]; - argv.appendSliceAssumeCapacity(&.{ "-L", try std.Io.Dir.path.join(arena, &.{ - libc_runtimes_path, - try if (target.isGnuLibC()) std.zig.target.glibcRuntimeTriple( - arena, - target.cpu.arch, - target.os.tag, - target.abi, - ) else if (target.isMuslLibC()) std.zig.target.muslRuntimeTriple( - arena, - target.cpu.arch, - target.abi, - ) else unreachable, - }) }); - } - break :use_executor true; - } else continue :use_executor .bad_os_or_cpu, - .rosetta => if (runner.args.enable_rosetta) - true - else - continue :use_executor .bad_os_or_cpu, - .wine => |executor| if (runner.args.enable_wine) { - if (!environ_map.contains("WINEDEBUG")) try environ_map.put("WINEDEBUG", "-all"); - argv.appendAssumeCapacity(executor); - break :use_executor true; - } else continue :use_executor .bad_os_or_cpu, - .wasmtime => |executor| if (runner.args.enable_wasmtime) { - if (!environ_map.contains("WASMTIME_BACKTRACE_DETAILS")) - try environ_map.put("WASMTIME_BACKTRACE_DETAILS", "1"); - argv.appendSliceAssumeCapacity(&.{ executor, "--dir=.", "-Sinherit-env" }); - break :use_executor true; - } else continue :use_executor .bad_os_or_cpu, - }; - argv.appendAssumeCapacity(bin_path); - - if (!runner.args.quiet) std.log.info("{f}", .{std.zig.SubprocessCommand{ - .argv = argv.items, - .cwd = cwd_path: { - break :cwd_path path_buffer[0 .. src_dir.realPath(io, &path_buffer) catch |err| switch (err) { - error.Canceled => |e| return e, - else => break :cwd_path null, - }]; - }, - .parent_env = &runner.environ_map, - .child_env = &environ_map, - }}); - - var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; - const run_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "run {s}", .{ - comp.out_name, - }) catch &name_buffer, 0); - defer run_prog_node.end(); - - const bin_file = src_dir.openFile(io, bin_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to open {q}: {t}", .{ bin_path, e }), - }; - defer bin_file.close(io); - const result = std.process.run(gpa, io, .{ - .exe = exe: switch (check) { - .errors => unreachable, - .stdout, .exit => if (use_executor) .search else .{ .file = bin_file }, - .gdb, .lldb => { - if (use_executor) { - if (!runner.args.quiet) std.log.warn( - "skipping debugging of {q} via executor for foreign target {q}", - .{ bin_path, comp.target.triple }, - ); - return; - } - - var line_it = std.mem.splitScalar(u8, expected, '\n'); - while (line_it.next()) |line| if (std.mem.cutPrefix(u8, line, switch (check) { - else => unreachable, - .gdb => "(gdb) ", - .lldb => "(lldb) ", - })) |cmd| script_fw.interface.print( - \\{s} - \\ - , .{cmd}) catch |err| switch (err) { - error.WriteFailed => switch (script_fw.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ - script_path, e, - }), - }, - }; - script_fw.interface.print( - \\ - \\quit {d} - \\ - , .{debug_success}) catch |err| switch (err) { - error.WriteFailed => switch (script_fw.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ - script_path, e, - }), - }, - }; - script_fw.flush() catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ - script_path, e, - }), - }; - break :exe .{ .file = switch (check) { - else => unreachable, - .gdb => runner.args.gdb_exe, - .lldb => runner.args.lldb_exe, - } orelse { - if (!runner.args.quiet) std.log.warn( - "skipping debugging of {q} because of missing \"--{t}=path/to/{[1]t}\" arg", - .{ bin_path, check }, - ); - return; - } }; - }, - }, - .argv = argv.items, - .cwd = .{ .dir = src_dir }, - .environ_map = &environ_map, - .progress_node = run_prog_node, - }) catch |err| if (use_executor) { - // Chances are the foreign executor isn't available. Skip this evaluation. - if (!runner.args.quiet) std.log.warn( - "skipping execution of {q} via executor for foreign target {q}: {t}", - .{ bin_path, comp.target.triple, err }, - ); - return; - } else return runner.fail("unable to run the generated executable {q}: {t}", .{ - bin_path, err, - }); - defer { - gpa.free(result.stdout); - gpa.free(result.stderr); - } - switch (result.term) { - .exited => |code| switch (check) { - .errors => unreachable, - .stdout => { - if (code != 0) return runner.fail( - "generated executable {q} failed with code {d}", - .{ bin_path, code }, - ); - std.testing.expectEqualStrings( - expected, - result.stdout, - ) catch |err| switch (err) { - error.TestExpectedEqual => return runner.fail("stdout did not match expected", .{}), - }; - }, - .exit => { - var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = undefined; - std.testing.expectEqualStrings( - expected, - std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable, - ) catch |err| switch (err) { - error.TestExpectedEqual => return runner.fail( - "exit code did not match expected", - .{}, - ), - }; - }, - .gdb, .lldb => { - if (code != debug_success) return runner.fail( - "debugging {q} failed with code {d}", - .{ bin_path, code }, - ); - var expected_match_it = std.mem.splitSequence(u8, expected, "#..."); - var pos: usize = 0; - while (expected_match_it.next()) |expected_match| : (pos += expected_match.len) { - var context_len: usize = expected_match.len; - pos = while (true) : (context_len -= 1) break std.mem.findPos( - u8, - result.stdout, - pos, - expected_match[0..context_len], - ) orelse continue; - const actual_match = result.stdout[pos..]; - std.testing.expectEqualStrings( - expected_match, - actual_match[0..@min(actual_match.len, expected_match.len)], - ) catch |err| switch (err) { - error.TestExpectedEqual => { - std.testing.failPrint("\n=============== full output: =========\n", .{}); - std.testing.failPrintWithVisibleNewlines(actual_match); - std.testing.failPrint("\n======================================\n", .{}); - return runner.fail("stdout did not match expected", .{}); - }, - }; - } - }, - }, - .signal => |sig| return runner.fail( - "generated executable {q} terminated with signal {t}", - .{ bin_path, sig }, - ), - .stopped => |sig| return runner.fail( - "generated executable {q} stopped with signal {t}", - .{ bin_path, sig }, - ), - .unknown => return runner.fail( - "generated executable {q} terminated unexpectedly", - .{bin_path}, - ), - } - if (!use_executor and result.stderr.len > 0) { - std.log.err("generated executable {q} had unexpected stderr:\n{s}", .{ - bin_path, result.stderr, - }); - } - } - - fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) FailError![]const u8 { - const runner = parent_comp.runner; - const arena = runner.arena.allocator(); - var compiler: ?Compiler = null; - var args_r: std.Io.Reader = .fixed(c_path); - parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{ - .triple = parent_comp.target.triple, - .resolved = resolved: { - var resolved = parent_comp.target.resolved; - resolved.ofmt = .default(resolved.os.tag, resolved.cpu.arch); - break :resolved resolved; - }, - .cache_mode = .whole, - .backend = null, - .linker = .lld, - .pic = parent_comp.target.pic, - }) catch |err| switch (err) { - error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e, - error.ReadFailed => unreachable, // .fixed - }; - const comp = &compiler.?; - comp.client.serveBodylessMessage(.update) catch |err| switch (err) { - error.WriteFailed => switch (comp.fw.err.?) { - error.Canceled => |e| return e, - else => |e| return runner.fail("unable to send message: {t}", .{e}), - }, - }; - comp.state = .update; - var out_path: ?[]const u8 = null; - while (try comp.receiveMessage()) |message| switch (message.tag) { - else => {}, // Ignore other messages, - .config => { - var body_r: std.Io.Reader = .fixed(message.body); - comp.config = - body_r.takeStruct(std.zig.Server.Message.Config, .little) catch unreachable; - }, - .emit_digest => { - var body_r: std.Io.Reader = .fixed(message.body); - _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; - const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch unreachable; - - const out_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ - "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); - out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name }); - }, - .error_bundle => { - const eb = std.zig.Server.allocErrorBundle( - runner.arena.allocator(), - message.body, - ) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.EndOfStream => return runner.fail("error bundle truncated", .{}), - }; - if (eb.errorMessageCount() > 0) { - try runner.eb_wip.addBundleAsRoots(eb); - return runner.fail("unexpected compile errors", .{}); - } - comp.state = .idle; - break; - }, - }; - try comp.exit(); - return out_path.?; - } - - fn exit(comp: *Compiler) FailError!void { - const runner = comp.runner; - comp.client.serveBodylessMessage(.exit) catch |err| switch (err) { - error.WriteFailed => switch (comp.fw.err.?) { - error.Canceled => |e| return e, - error.BrokenPipe => {}, - else => |e| return comp.runner.fail("unable to send exit: {t}", .{e}), - }, - }; - comp.child.stdin.?.close(runner.io); - comp.child.stdin = null; - while (try comp.receiveMessage()) |_| {} - if (comp.client.in.bufferedLen() > 0) return runner.fail("unable to receive messages", .{}); - const term = comp.child.wait(runner.io) catch |err| - return runner.fail("child process failed: {t}", .{err}); - comp.prog_node.end(); - switch (term) { - .exited => |code| if (code != 0) - return runner.fail("compiler failed with code {d}", .{code}), - .signal => |sig| return runner.fail("compiler terminated with signal {t}", .{sig}), - .stopped => |sig| return runner.fail("compiler stopped unexpectedly with signal {t}", .{ - sig, - }), - .unknown => return runner.fail("compiler terminated unexpectedly", .{}), - } - } -}; - -const ClientArgs = struct { - test_file: std.Io.File, - test_kind: std.Io.File.Kind, - zig_exe: std.Io.File, - lib_dir: std.Io.Dir, - src_dir: std.Io.Dir, - targets: std.ArrayList([]const u8), - - libc_runtimes_dir: ?std.Io.Dir, - enable_darling: bool, - enable_qemu: bool, - enable_rosetta: bool, - enable_wine: bool, - enable_wasmtime: bool, - - gdb_exe: ?std.Io.File, - lldb_exe: ?std.Io.File, - - quiet: bool, - keep_src: bool, -}; -pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 { - const fatal = struct { - fn fatal(comptime fmt: []const u8, args: anytype) noreturn { - std.log.err(fmt, args); - std.process.exit(1); - } - }.fatal; - const arena = init.arena.allocator(); - - var test_path_arg: ?[]const u8 = null; - var zig_path_arg: ?[]const u8 = null; - var lib_path_arg: ?[]const u8 = null; - var libc_runtimes_path_arg: ?[]const u8 = null; - var gdb_path_arg: ?[]const u8 = null; - var lldb_path_arg: ?[]const u8 = null; - var args: ClientArgs = .{ - .test_file = undefined, - .test_kind = undefined, - .zig_exe = undefined, - .lib_dir = undefined, - .src_dir = undefined, - .targets = .empty, - - .libc_runtimes_dir = null, - .enable_darling = false, - .enable_qemu = false, - .enable_rosetta = false, - .enable_wine = false, - .enable_wasmtime = false, - - .gdb_exe = null, - .lldb_exe = null, - - .quiet = false, - .keep_src = false, - }; - defer args.targets.deinit(init.gpa); - - var arg_it = try init.minimal.args.iterateAllocator(arena); - const self = arg_it.next().?; - while (arg_it.next()) |arg| { - if (std.mem.eql(u8, arg, "--listen=-")) { - var stdin_buffer: [512]u8 = undefined; - var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer); - var stdout_buffer: [512]u8 = undefined; - var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); - var runner: Runner = .{ - .gpa = init.gpa, - .arena = init.arena, - .environ_map = init.minimal.environ.createMap(init.gpa) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - else => |e| fatal("unable to get environment: {t}", .{e}), - }, - .io = init.io, - .prog_node = std.Progress.start(init.io, .{}), - .args = .{ - .@"test" = null, - .manifest_file = null, - .zig_exe = null, - .lib_dir = null, - .src_dir = null, - .targets = .empty, - .target_bytes = .empty, - - .libc_runtimes_dir = null, - .enable_darling = false, - .enable_qemu = false, - .enable_rosetta = false, - .enable_wasmtime = false, - .enable_wine = false, - - .gdb_exe = null, - .lldb_exe = null, - - .quiet = false, - }, - .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to resolve host target: {t}", .{e}), - }, - .server = .{ - .in = &stdin.interface, - .out = &stdout.interface, - }, - .eb_wip = try .init(init.gpa), - }; - defer runner.deinit(); - switch (runner.runServer()) { - error.Canceled, error.OutOfMemory => |e| return e, - error.ServerReadFailed => switch (stdin.err.?) { - error.Canceled => |e| return e, - else => |e| fatal("unable to receive message: {t}", .{e}), - }, - error.ServerWriteFailed => switch (stdout.err.?) { - error.Canceled => |e| return e, - else => |e| fatal("unable to send message: {t}", .{e}), - }, - error.ServerEndOfStream => fatal("no more messages", .{}), - error.AlreadyReported => { - var eb = try runner.eb_wip.toOwnedBundle(""); - defer eb.deinit(init.gpa); - eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { - error.Canceled => |e| return e, - else => {}, - }; - std.process.exit(1); - }, - } - } else if (std.mem.eql(u8, arg, "--zig")) { - zig_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); - } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { - zig_path_arg = zig_path; - } else if (std.mem.eql(u8, arg, "--lib")) { - lib_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); - } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { - lib_path_arg = lib_path; - } else if (std.mem.eql(u8, arg, "--target")) { - try args.targets.append( - init.gpa, - try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after {q}", .{arg})), - ); - } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { - try args.targets.append(init.gpa, try arena.dupe(u8, target)); - } else if (std.mem.eql(u8, arg, "--libc-runtimes")) { - libc_runtimes_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); - } else if (std.mem.cutPrefix(u8, arg, "--libc-runtimes=")) |libc_runtimes_path| { - libc_runtimes_path_arg = libc_runtimes_path; - } else if (std.mem.eql(u8, arg, "-fdarling")) { - args.enable_darling = true; - } else if (std.mem.eql(u8, arg, "-fqemu")) { - args.enable_qemu = true; - } else if (std.mem.eql(u8, arg, "-frosetta")) { - args.enable_rosetta = true; - } else if (std.mem.eql(u8, arg, "-fwine")) { - args.enable_wine = true; - } else if (std.mem.eql(u8, arg, "-fwasmtime")) { - args.enable_wasmtime = true; - } else if (std.mem.eql(u8, arg, "--gdb")) { - gdb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); - } else if (std.mem.cutPrefix(u8, arg, "--gdb=")) |gdb_path| { - gdb_path_arg = gdb_path; - } else if (std.mem.eql(u8, arg, "--lldb")) { - lldb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); - } else if (std.mem.cutPrefix(u8, arg, "--lldb=")) |lldb_path| { - lldb_path_arg = lldb_path; - } else if (std.mem.eql(u8, arg, "--quiet")) { - args.quiet = true; - } else if (std.mem.eql(u8, arg, "--keep-src")) { - args.keep_src = true; - } else { - if (test_path_arg) |_| fatal("unexpected arg {q}", .{arg}); - test_path_arg = arg; - } - } - - const test_path = test_path_arg orelse fatal("missing \"path/to/test\" arg", .{}); - const zig_path = zig_path_arg orelse fatal("missing \"--zig=path/to/zig\" arg", .{}); - const lib_path = lib_path_arg orelse fatal("missing \"--lib=path/to/lib\" arg", .{}); - - const cwd: std.Io.Dir = .cwd(); - args.test_file = cwd.openFile(init.io, test_path, .{ - .allow_directory = true, - }) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to open {q}: {t}", .{ test_path, e }), - }; - defer args.test_file.close(init.io); - args.test_kind = (args.test_file.stat(init.io) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to stat {q}: {t}", .{ test_path, e }), - }).kind; - args.zig_exe = cwd.openFile(init.io, zig_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to open {q}: {t}", .{ zig_path, e }), - }; - defer args.zig_exe.close(init.io); - args.lib_dir = cwd.openDir(init.io, lib_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), - }; - defer args.lib_dir.close(init.io); - if (libc_runtimes_path_arg) |libc_runtimes_path| args.libc_runtimes_dir = cwd.openDir( - init.io, - libc_runtimes_path, - .{}, - ) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), - }; - defer if (args.libc_runtimes_dir) |libc_runtimes_dir| libc_runtimes_dir.close(init.io); - if (gdb_path_arg) |gdb_path| args.gdb_exe = cwd.openFile(init.io, gdb_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to open {q}: {t}", .{ gdb_path, e }), - }; - defer if (args.gdb_exe) |gdb_exe| gdb_exe.close(init.io); - if (lldb_path_arg) |lldb_path| args.lldb_exe = cwd.openFile(init.io, lldb_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to open {q}: {t}", .{ lldb_path, e }), - }; - defer if (args.lldb_exe) |lldb_exe| lldb_exe.close(init.io); - - const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { - var rand_int: u64 = undefined; - init.io.random(@ptrCast(&rand_int)); - break :rand_int rand_int; - }); - args.src_dir = cwd.createDirPathOpen(init.io, src_dir_path, .{}) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to create {q}: {t}", .{ src_dir_path, e }), - }; - defer { - args.src_dir.close(init.io); - if (!args.keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| { - std.log.warn("unable to delete tree {q}: {t}", .{ src_dir_path, err }); - }; - } - - var inherit_dirs: std.ArrayList(std.Io.Dir) = .initBuffer(try arena.alloc(std.Io.Dir, 3)); - var inherit_files: std.ArrayList(std.Io.File) = .initBuffer(try arena.alloc(std.Io.File, 4)); - inherit_files.appendSliceAssumeCapacity(&.{ args.test_file, args.zig_exe }); - inherit_dirs.appendSliceAssumeCapacity(&.{ args.lib_dir, args.src_dir }); - if (args.libc_runtimes_dir) |libc_runtimes_dir| inherit_dirs.appendAssumeCapacity(libc_runtimes_dir); - if (args.gdb_exe) |gdb_exe| inherit_files.appendAssumeCapacity(gdb_exe); - if (args.lldb_exe) |lldb_exe| inherit_files.appendAssumeCapacity(lldb_exe); - var child = std.process.spawn(init.io, .{ - .argv = &.{ self, "--listen=-" }, - .stdin = .pipe, - .stdout = .pipe, - .inherit_dirs = inherit_dirs.items, - .inherit_files = inherit_files.items, - }) catch |err| switch (err) { - error.Canceled, error.OutOfMemory => |e| return e, - else => |e| fatal("unable to spawn runner: {t}", .{e}), - }; - - var child_reader_buffer: [512]u8 = undefined; - var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer); - var child_writer_buffer: [512]u8 = undefined; - var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer); - var client: std.zig.Client = .{ - .in = &child_reader.interface, - .out = &child_writer.interface, - }; - const success = runClient(init.gpa, arena, init.io, &client, &args) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.ReadFailed => switch (child_reader.err.?) { - error.Canceled => |e| return e, - else => |e| fatal("unable to receive message: {t}", .{e}), - }, - error.WriteFailed => switch (child_writer.err.?) { - error.Canceled => |e| return e, - else => |e| fatal("unable to send message: {t}", .{e}), - }, - error.EndOfStream => fatal("no more messages", .{}), - }; - _ = child.wait(init.io) catch |err| switch (err) { - error.Canceled => |e| return e, - else => |e| fatal("unable to wait for runner: {t}", .{e}), - }; - return if (success) 0 else 1; -} -fn runClient( - gpa: std.mem.Allocator, - arena: std.mem.Allocator, - io: std.Io, - client: *std.zig.Client, - args: *const ClientArgs, -) (std.mem.Allocator.Error || std.Io.Reader.Error || std.Io.Writer.Error)!bool { - var stdout_buffer: [512]u8 = undefined; - var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer); - const stdout = &stdout_fw.interface; - var stderr_buffer: [512]u8 = undefined; - var stderr_fw: std.Io.File.Writer = .initStreaming(.stderr(), io, &stderr_buffer); - const stderr = &stderr_fw.interface; - - var argv: std.ArrayList(u8) = .empty; - defer argv.deinit(gpa); - const Arg = std.zig.Client.Message.Arg; - - try argv.append(gpa, @backingInt(@as(Arg, switch (args.test_kind) { - else => unreachable, - .file => .input_file, - .directory => .input_dir, - }))); - try argv.appendSlice(gpa, @ptrCast(&args.test_file.handle)); - - try argv.append(gpa, @backingInt(Arg.prefix)); - try argv.appendSlice(gpa, "--zig="); - try argv.append(gpa, 0); - try argv.append(gpa, @backingInt(Arg.input_file)); - try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle)); - - try argv.append(gpa, @backingInt(Arg.prefix)); - try argv.appendSlice(gpa, "--lib="); - try argv.append(gpa, 0); - try argv.append(gpa, @backingInt(Arg.input_dir)); - try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle)); - - try argv.append(gpa, @backingInt(Arg.prefix)); - try argv.appendSlice(gpa, "--src="); - try argv.append(gpa, 0); - try argv.append(gpa, @backingInt(Arg.output_dir)); - try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle)); - - for (args.targets.items) |target| { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "--target"); - try argv.append(gpa, 0); - - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, target); - try argv.append(gpa, 0); - } - - if (args.libc_runtimes_dir) |libc_runtimes_dir| { - try argv.append(gpa, @backingInt(Arg.prefix)); - try argv.appendSlice(gpa, "--libc-runtimes="); - try argv.append(gpa, 0); - try argv.append(gpa, @backingInt(Arg.input_dir)); - try argv.appendSlice(gpa, @ptrCast(&libc_runtimes_dir.handle)); - } - if (args.enable_darling) { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "-fdarling"); - try argv.append(gpa, 0); - } - if (args.enable_qemu) { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "-fqemu"); - try argv.append(gpa, 0); - } - if (args.enable_rosetta) { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "-frosetta"); - try argv.append(gpa, 0); - } - if (args.enable_wine) { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "-fwine"); - try argv.append(gpa, 0); - } - if (args.enable_wasmtime) { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "-fwasmtime"); - try argv.append(gpa, 0); - } - - if (args.gdb_exe) |gdb_exe| { - try argv.append(gpa, @backingInt(Arg.prefix)); - try argv.appendSlice(gpa, "--gdb="); - try argv.append(gpa, 0); - try argv.append(gpa, @backingInt(Arg.input_file)); - try argv.appendSlice(gpa, @ptrCast(&gdb_exe.handle)); - } - if (args.lldb_exe) |lldb_exe| { - try argv.append(gpa, @backingInt(Arg.prefix)); - try argv.appendSlice(gpa, "--lldb="); - try argv.append(gpa, 0); - try argv.append(gpa, @backingInt(Arg.input_file)); - try argv.appendSlice(gpa, @ptrCast(&lldb_exe.handle)); - } - - if (args.quiet) { - try argv.append(gpa, @backingInt(Arg.string)); - try argv.appendSlice(gpa, "--quiet"); - try argv.append(gpa, 0); - } - - try client.serveMessageHeader(.{ - .tag = .args, - .bytes_len = @intCast(argv.items.len), - }); - try client.out.writeAll(argv.items); - - var success = true; - var metadata: struct { - index: u32, - len: u32, - names: []u32, - expected_panic_msgs: []u32, - string_bytes: []u8, - - fn name(m: *@This()) []const u8 { - return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0); - } - - fn expectedPanicMsg(m: *@This()) ?[:0]const u8 { - return switch (m.expected_panic_msgs[m.index - 1]) { - 0 => null, - else => |offset| m.string_bytes[offset..std.mem.findScalarPos( - u8, - m.string_bytes, - offset, - 0, - ).? :0], - }; - } - - fn next(m: *@This()) ?u32 { - if (m.len - m.index == 0) return null; - defer m.index += 1; - return m.index; - } - } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} }; - try client.serveBodylessMessage(.query_test_metadata); - while (true) { - const hdr = try client.receiveMessage(); - switch (hdr.tag) { - else => try client.in.discardAll(hdr.bytes_len), - .zig_version => { - const actual_version = try client.in.take(hdr.bytes_len); - if (!std.mem.eql(u8, builtin.zig_version_string, actual_version)) { - try stderr.print( - "error: zig version mismatch compiler test runner vs compiler: {q} vs {q}\n", - .{ builtin.zig_version_string, actual_version }, - ); - try stderr.flush(); - std.process.exit(1); - } - }, - .error_bundle => { - const body = try arena.alloc(u8, hdr.bytes_len); - try client.in.readSliceAll(body); - const eb = std.zig.Server.allocErrorBundle(arena, body) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.EndOfStream => { - try stderr.writeAll("error bundle truncated\n"); - try stderr.flush(); - std.process.exit(1); - }, - }; - success = false; - eb.renderToStderr(io, .{}, .auto) catch {}; - }, - .test_metadata => { - const tm_hdr = - try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); - metadata = .{ - .index = 0, - .len = tm_hdr.tests_len, - .names = try arena.alloc(u32, tm_hdr.tests_len), - .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len), - .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len), - }; - try client.in.readSliceEndian(u32, metadata.names, .little); - try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little); - try client.in.readSliceAll(metadata.string_bytes); - try client.serveRunTest(metadata.next() orelse break); - }, - .test_results => { - const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little); - switch (tr.flags.status) { - .pass, .skip => {}, - .fail => success = false, - } - try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() }); - try stdout.flush(); - try client.serveRunTest(metadata.next() orelse break); - }, - } - } - try client.serveBodylessMessage(.exit); - return success; -} diff --git a/test/tests.zig b/test/tests.zig index ba0079b752ffb808a2cebe89fb5776e1663aa34f..3ed14aedd5f077e47896465829564134db178d7c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -6,14 +6,12 @@ const OptimizeMode = std.builtin.Optimize; const Step = std.Build.Step; // Cases -const llvm_ir = @import("llvm_ir.zig"); const libc = @import("libc.zig"); const link = @import("link.zig"); // Implementations pub const ErrorTracesContext = @import("src/ErrorTrace.zig"); pub const StackTracesContext = @import("src/StackTrace.zig"); -pub const LlvmIrContext = @import("src/LlvmIr.zig"); pub const LibcContext = @import("src/Libc.zig"); pub const LinkContext = @import("src/Link.zig"); @@ -2188,150 +2186,6 @@ const link_targets = blk: { }; }; -/// These are passed to `src/Runner.zig` as `----` strings. -/// -/// If only one specific test is failing on a target, instead of entirely disabling the target here, you -/// can skip the target for that specific test only by adding a line like this to top of the manifest: -/// #skip x86_64-linux-selfhosted -const RunnerTarget = struct { - target: std.Target.Query, - cache_mode: enum { whole, incremental }, - backend: enum { selfhosted, llvm, cbe }, - linker: enum { lld, old, new }, - pic: enum { nopic, pic, pie }, -}; - -pub const incremental_matrix: []const RunnerTarget = &.{ - // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others - // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang - // or LLVM). We just have a couple here to make sure that it works. - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .musl, - }, - .cache_mode = .incremental, - .backend = .cbe, - .linker = .old, - .pic = .nopic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .incremental, - .backend = .llvm, - .linker = .lld, - .pic = .nopic, - }, - - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .incremental, - .backend = .selfhosted, - .linker = .new, - .pic = .nopic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .windows, - .abi = .none, - }, - .cache_mode = .incremental, - .backend = .selfhosted, - .linker = .old, - .pic = .pic, - }, - .{ - .target = .{ - .cpu_arch = .wasm32, - .os_tag = .wasi, - .abi = .none, - }, - .cache_mode = .incremental, - .backend = .selfhosted, - .linker = .old, - .pic = .nopic, - }, -}; - -pub const debugger_matrix: []const RunnerTarget = &.{ - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .whole, - .backend = .selfhosted, - .linker = .old, - .pic = .nopic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .whole, - .backend = .selfhosted, - .linker = .old, - .pic = .pic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .whole, - .backend = .selfhosted, - .linker = .new, - .pic = .nopic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .whole, - .backend = .selfhosted, - .linker = .new, - .pic = .pic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .incremental, - .backend = .selfhosted, - .linker = .new, - .pic = .nopic, - }, - .{ - .target = .{ - .cpu_arch = .x86_64, - .os_tag = .linux, - .abi = .none, - }, - .cache_mode = .incremental, - .backend = .selfhosted, - .linker = .new, - .pic = .pic, - }, -}; - pub fn addStackTraceTests(b: *std.Build, options: StackTracesContext.Options) *Step { const step = b.step("test-stack-traces", "Run the stack trace tests"); @@ -3170,7 +3024,6 @@ pub fn addCases( b: *std.Build, parent_step: *Step, case_test_options: @import("src/Cases.zig").CaseTestOptions, - build_options: @import("cases.zig").BuildOptions, ) !void { const arena = b.allocator; const gpa = b.allocator; @@ -3184,7 +3037,7 @@ pub fn addCases( defer dir.close(io); cases.addFromDir(dir, "test/cases", b); - try @import("cases.zig").addCases(&cases, build_options, b); + try @import("cases.zig").addCases(&cases, b); cases.lowerToBuildSteps( b, @@ -3193,6 +3046,7 @@ pub fn addCases( ); } +pub const RunnerTarget = @import("Runner.zig").Target; pub const RunnerOptions = struct { test_filters: []const []const u8, test_target_filters: []const []const u8, @@ -3205,10 +3059,13 @@ pub const RunnerOptions = struct { skip_darwin: bool, skip_linux: bool, skip_llvm: bool, + llvm_has_m68k: bool, + llvm_has_csky: bool, + llvm_has_arc: bool, + llvm_has_xtensa: bool, gdb: ?[]const u8, lldb: ?[]const u8, }; - pub fn addRunnerTests( b: *std.Build, runner: *std.Build.Step.Compile, @@ -3224,21 +3081,24 @@ pub fn addRunnerTests( defer tests_dir.close(b.graph.io); var test_it = tests_dir.iterate(); while (try test_it.next(b.graph.io)) |@"test"| { + switch (@"test".kind) { + else => continue, + .file => { + if (std.mem.eql(u8, @"test".name, "matrix.zon")) continue; + if (isEditorFileName(@"test".name)) continue; + }, + .directory => {}, + } + for (options.test_filters) |filter| { if (std.mem.find(u8, @"test".name, filter) != null) break; } else if (options.test_filters.len > 0) continue; - const test_path = tests_path.path(b, @"test".name); - switch (@"test".kind) { - else => continue, - .file => if (isEditorFileName(@"test".name)) continue, - .directory => {}, - } - const run = b.addRunArtifact(runner); run.setName(@"test".name); run.enableProtocolMode(); + const test_path = tests_path.path(b, @"test".name); switch (@"test".kind) { else => continue, .file => run.addFileArg(test_path), @@ -3249,7 +3109,7 @@ pub fn addRunnerTests( _ = run.addPrefixedOutputDirectoryArg("--src=", "src"); for (matrix) |test_target| { - const resolved_target = b.resolveTargetQuery(test_target.target); + const resolved_target = b.resolveTargetQuery(test_target.toQuery()); if (options.skip_non_native and !std.zig.target.isNative(&resolved_target.query, &resolved_target.result, &b.graph.host.result)) continue; @@ -3265,15 +3125,18 @@ pub fn addRunnerTests( if (options.skip_darwin and target.os.tag.isDarwin()) continue; if (options.skip_linux and target.os.tag == .linux) continue; - if (options.skip_llvm and test_target.backend == .llvm) continue; + if (test_target.backend == .llvm) { + if (options.skip_llvm) continue; + switch (target.cpu.arch) { + else => {}, + .m68k => if (!options.llvm_has_m68k) continue, + .csky => if (!options.llvm_has_csky) continue, + .arc => if (!options.llvm_has_arc) continue, + .xtensa => if (!options.llvm_has_xtensa) continue, + } + } - const target_str = b.fmt("{s}-{t}-{t}-{t}-{t}", .{ - resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"), - test_target.cache_mode, - test_target.backend, - test_target.linker, - test_target.pic, - }); + const target_str = b.fmt("{f}", .{test_target}); for (options.test_target_filters) |filter| { if (std.mem.find(u8, target_str, filter) != null) break; @@ -3303,25 +3166,6 @@ fn isEditorFileName(name: []const u8) bool { std.mem.endsWith(u8, name, "~") or std.mem.endsWith(u8, name, ".swp"); } -pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step { - const step = b.step("test-llvm-ir", "Run the LLVM IR tests"); - - if (!options.enable_llvm) { - step.dependOn(&b.addFail("test-llvm-ir requires -Denable-llvm").step); - return null; - } - - var context: LlvmIrContext = .{ - .b = b, - .options = options, - .root_step = step, - }; - - llvm_ir.addCases(&context); - - return step; -} - const libc_test_nsz_targets: []const std.Target.Query = &.{ .{ .cpu_arch = .arm,