From 0bf8617d963dc432ed0204b10285cc414becf2fd Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 4 Jun 2025 21:45:31 -0400 Subject: x86_64: add support for pie executables --- CMakeLists.txt | 2 +- lib/std/builtin.zig | 15 +- lib/std/dynamic_library.zig | 13 +- lib/std/pie.zig | 343 +++---- lib/std/start.zig | 12 +- lib/std/zig/llvm/Builder.zig | 12 + lib/zig.h | 9 + src/Air.zig | 14 +- src/Air/Legalize.zig | 2 +- src/Air/Liveness.zig | 4 +- src/Air/Liveness/Verify.zig | 2 +- src/Air/print.zig | 1041 ++++++++++++++++++++ src/Air/types_resolved.zig | 2 +- src/Compilation/Config.zig | 31 +- src/InternPool.zig | 120 ++- src/Sema.zig | 53 +- src/Zcu.zig | 12 +- src/Zcu/PerThread.zig | 50 +- src/arch/aarch64/CodeGen.zig | 2 +- src/arch/arm/CodeGen.zig | 2 +- src/arch/riscv64/CodeGen.zig | 13 +- src/arch/sparc64/CodeGen.zig | 2 +- src/arch/wasm/CodeGen.zig | 4 +- src/arch/x86_64/CodeGen.zig | 215 +++- src/arch/x86_64/Emit.zig | 7 +- src/arch/x86_64/Lower.zig | 21 +- src/arch/x86_64/Mir.zig | 5 +- src/arch/x86_64/bits.zig | 1 + src/arch/x86_64/encoder.zig | 7 +- src/codegen.zig | 81 +- src/codegen/c.zig | 34 +- src/codegen/llvm.zig | 123 ++- src/link/Dwarf.zig | 190 +--- src/link/Elf.zig | 42 +- src/link/Elf/Atom.zig | 6 +- src/link/Elf/ZigObject.zig | 12 +- src/link/Elf/relocation.zig | 3 +- src/link/MachO/ZigObject.zig | 4 +- src/link/Wasm.zig | 2 +- src/print_air.zig | 1038 ------------------- test/cases/compile_errors/@import_zon_bad_type.zig | 6 +- .../anytype_param_requires_comptime.zig | 2 +- .../compile_errors/bogus_method_call_on_slice.zig | 2 +- test/cases/compile_errors/coerce_anon_struct.zig | 2 +- test/cases/compile_errors/redundant_try.zig | 4 +- test/tests.zig | 9 +- 46 files changed, 1910 insertions(+), 1666 deletions(-) create mode 100644 src/Air/print.zig delete mode 100644 src/print_air.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 2baf4dcbd8..671a6ad236 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -519,6 +519,7 @@ set(ZIG_STAGE2_SOURCES src/Air/Legalize.zig src/Air/Liveness.zig src/Air/Liveness/Verify.zig + src/Air/print.zig src/Air/types_resolved.zig src/Builtin.zig src/Compilation.zig @@ -675,7 +676,6 @@ set(ZIG_STAGE2_SOURCES src/libs/mingw.zig src/libs/musl.zig src/mutable_value.zig - src/print_air.zig src/print_env.zig src/print_targets.zig src/print_value.zig diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 1683cc500b..88e2304332 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -61,7 +61,7 @@ pub const StackTrace = struct { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. -pub const GlobalLinkage = enum { +pub const GlobalLinkage = enum(u2) { internal, strong, weak, @@ -70,7 +70,7 @@ pub const GlobalLinkage = enum { /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. -pub const SymbolVisibility = enum { +pub const SymbolVisibility = enum(u2) { default, hidden, protected, @@ -1030,8 +1030,19 @@ pub const ExternOptions = struct { name: []const u8, library_name: ?[]const u8 = null, linkage: GlobalLinkage = .strong, + visibility: SymbolVisibility = .default, + /// Setting this to `true` makes the `@extern` a runtime value. is_thread_local: bool = false, is_dll_import: bool = false, + relocation: Relocation = .any, + + pub const Relocation = enum(u1) { + /// Any type of relocation is allowed. + any, + /// A program-counter-relative relocation is required. + /// Using this value makes the `@extern` a runtime value. + pcrel, + }; }; /// This data structure is used by the Zig language code generation and diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index 4b1faa87ba..bac31f5760 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -83,13 +83,16 @@ const RDebug = extern struct { r_ldbase: usize, }; -/// TODO make it possible to reference this same external symbol 2x so we don't need this -/// helper function. -pub fn get_DYNAMIC() ?[*]elf.Dyn { - return @extern([*]elf.Dyn, .{ .name = "_DYNAMIC", .linkage = .weak }); +/// TODO fix comparisons of extern symbol pointers so we don't need this helper function. +pub fn get_DYNAMIC() ?[*]const elf.Dyn { + return @extern([*]const elf.Dyn, .{ + .name = "_DYNAMIC", + .linkage = .weak, + .visibility = .hidden, + }); } -pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator { +pub fn linkmap_iterator(phdrs: []const elf.Phdr) error{InvalidExe}!LinkMap.Iterator { _ = phdrs; const _DYNAMIC = get_DYNAMIC() orelse { // No PT_DYNAMIC means this is either a statically-linked program or a diff --git a/lib/std/pie.zig b/lib/std/pie.zig index 572f3ddf96..0f4b4ff4b3 100644 --- a/lib/std/pie.zig +++ b/lib/std/pie.zig @@ -39,167 +39,175 @@ const R_RELATIVE = switch (builtin.cpu.arch) { // Obtain a pointer to the _DYNAMIC array. // We have to compute its address as a PC-relative quantity not to require a // relocation that, at this point, is not yet applied. -inline fn getDynamicSymbol() [*]elf.Dyn { - return switch (builtin.cpu.arch) { - .x86 => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ call 1f - \\ 1: pop %[ret] - \\ lea _DYNAMIC-1b(%[ret]), %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - ), - .x86_64 => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ lea _DYNAMIC(%%rip), %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - ), - .arc => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ add %[ret], pcl, _DYNAMIC@pcl - : [ret] "=r" (-> [*]elf.Dyn), - ), - // Work around the limited offset range of `ldr` - .arm, .armeb, .thumb, .thumbeb => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ ldr %[ret], 1f - \\ add %[ret], pc - \\ b 2f - \\ 1: .word _DYNAMIC-1b - \\ 2: - : [ret] "=r" (-> [*]elf.Dyn), - ), - // A simple `adr` is not enough as it has a limited offset range - .aarch64, .aarch64_be => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ adrp %[ret], _DYNAMIC - \\ add %[ret], %[ret], #:lo12:_DYNAMIC - : [ret] "=r" (-> [*]elf.Dyn), - ), - // The CSKY ABI requires the gb register to point to the GOT. Additionally, the first - // entry in the GOT is defined to hold the address of _DYNAMIC. - .csky => asm volatile ( - \\ mov %[ret], gb - \\ ldw %[ret], %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - ), - .hexagon => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ jump 1f - \\ .word _DYNAMIC - . - \\ 1: - \\ r1 = pc - \\ r1 = add(r1, #-4) - \\ %[ret] = memw(r1) - \\ %[ret] = add(r1, %[ret]) - : [ret] "=r" (-> [*]elf.Dyn), - : - : "r1" - ), - .loongarch32, .loongarch64 => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ la.local %[ret], _DYNAMIC - : [ret] "=r" (-> [*]elf.Dyn), - ), - // Note that the - 8 is needed because pc in the second lea instruction points into the - // middle of that instruction. (The first lea is 6 bytes, the second is 4 bytes.) - .m68k => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ lea _DYNAMIC - . - 8, %[ret] - \\ lea (%[ret], %%pc), %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - ), - .mips, .mipsel => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ bal 1f - \\ .gpword _DYNAMIC - \\ 1: - \\ lw %[ret], 0($ra) - \\ addu %[ret], %[ret], $gp - : [ret] "=r" (-> [*]elf.Dyn), - : - : "lr" - ), - .mips64, .mips64el => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ .balign 8 - \\ bal 1f - \\ .gpdword _DYNAMIC - \\ 1: - \\ ld %[ret], 0($ra) - \\ daddu %[ret], %[ret], $gp - : [ret] "=r" (-> [*]elf.Dyn), - : - : "lr" - ), - .powerpc, .powerpcle => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ bl 1f - \\ .long _DYNAMIC - . - \\ 1: - \\ mflr %[ret] - \\ lwz 4, 0(%[ret]) - \\ add %[ret], 4, %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - : - : "lr", "r4" - ), - .powerpc64, .powerpc64le => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ bl 1f - \\ .quad _DYNAMIC - . - \\ 1: - \\ mflr %[ret] - \\ ld 4, 0(%[ret]) - \\ add %[ret], 4, %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - : - : "lr", "r4" - ), - .riscv32, .riscv64 => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ lla %[ret], _DYNAMIC - : [ret] "=r" (-> [*]elf.Dyn), - ), - .s390x => asm volatile ( - \\ .weak _DYNAMIC - \\ .hidden _DYNAMIC - \\ larl %[ret], 1f - \\ ag %[ret], 0(%[ret]) - \\ jg 2f - \\ 1: .quad _DYNAMIC - . - \\ 2: - : [ret] "=r" (-> [*]elf.Dyn), - ), - // The compiler does not necessarily have any obligation to load the `l7` register (pointing - // to the GOT), so do it ourselves just in case. - .sparc, .sparc64 => asm volatile ( - \\ sethi %%hi(_GLOBAL_OFFSET_TABLE_ - 4), %%l7 - \\ call 1f - \\ add %%l7, %%lo(_GLOBAL_OFFSET_TABLE_ + 4), %%l7 - \\ 1: - \\ add %%l7, %%o7, %[ret] - : [ret] "=r" (-> [*]elf.Dyn), - ), - else => { - @compileError("PIE startup is not yet supported for this target!"); +inline fn getDynamicSymbol() [*]const elf.Dyn { + return switch (builtin.zig_backend) { + else => switch (builtin.cpu.arch) { + .x86 => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ call 1f + \\ 1: pop %[ret] + \\ lea _DYNAMIC-1b(%[ret]), %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + ), + .x86_64 => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ lea _DYNAMIC(%%rip), %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + ), + .arc => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ add %[ret], pcl, _DYNAMIC@pcl + : [ret] "=r" (-> [*]const elf.Dyn), + ), + // Work around the limited offset range of `ldr` + .arm, .armeb, .thumb, .thumbeb => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ ldr %[ret], 1f + \\ add %[ret], pc + \\ b 2f + \\ 1: .word _DYNAMIC-1b + \\ 2: + : [ret] "=r" (-> [*]const elf.Dyn), + ), + // A simple `adr` is not enough as it has a limited offset range + .aarch64, .aarch64_be => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ adrp %[ret], _DYNAMIC + \\ add %[ret], %[ret], #:lo12:_DYNAMIC + : [ret] "=r" (-> [*]const elf.Dyn), + ), + // The CSKY ABI requires the gb register to point to the GOT. Additionally, the first + // entry in the GOT is defined to hold the address of _DYNAMIC. + .csky => asm volatile ( + \\ mov %[ret], gb + \\ ldw %[ret], %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + ), + .hexagon => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ jump 1f + \\ .word _DYNAMIC - . + \\ 1: + \\ r1 = pc + \\ r1 = add(r1, #-4) + \\ %[ret] = memw(r1) + \\ %[ret] = add(r1, %[ret]) + : [ret] "=r" (-> [*]const elf.Dyn), + : + : "r1" + ), + .loongarch32, .loongarch64 => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ la.local %[ret], _DYNAMIC + : [ret] "=r" (-> [*]const elf.Dyn), + ), + // Note that the - 8 is needed because pc in the second lea instruction points into the + // middle of that instruction. (The first lea is 6 bytes, the second is 4 bytes.) + .m68k => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ lea _DYNAMIC - . - 8, %[ret] + \\ lea (%[ret], %%pc), %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + ), + .mips, .mipsel => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ bal 1f + \\ .gpword _DYNAMIC + \\ 1: + \\ lw %[ret], 0($ra) + \\ addu %[ret], %[ret], $gp + : [ret] "=r" (-> [*]const elf.Dyn), + : + : "lr" + ), + .mips64, .mips64el => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ .balign 8 + \\ bal 1f + \\ .gpdword _DYNAMIC + \\ 1: + \\ ld %[ret], 0($ra) + \\ daddu %[ret], %[ret], $gp + : [ret] "=r" (-> [*]const elf.Dyn), + : + : "lr" + ), + .powerpc, .powerpcle => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ bl 1f + \\ .long _DYNAMIC - . + \\ 1: + \\ mflr %[ret] + \\ lwz 4, 0(%[ret]) + \\ add %[ret], 4, %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + : + : "lr", "r4" + ), + .powerpc64, .powerpc64le => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ bl 1f + \\ .quad _DYNAMIC - . + \\ 1: + \\ mflr %[ret] + \\ ld 4, 0(%[ret]) + \\ add %[ret], 4, %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + : + : "lr", "r4" + ), + .riscv32, .riscv64 => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ lla %[ret], _DYNAMIC + : [ret] "=r" (-> [*]const elf.Dyn), + ), + .s390x => asm volatile ( + \\ .weak _DYNAMIC + \\ .hidden _DYNAMIC + \\ larl %[ret], 1f + \\ ag %[ret], 0(%[ret]) + \\ jg 2f + \\ 1: .quad _DYNAMIC - . + \\ 2: + : [ret] "=r" (-> [*]const elf.Dyn), + ), + // The compiler does not necessarily have any obligation to load the `l7` register (pointing + // to the GOT), so do it ourselves just in case. + .sparc, .sparc64 => asm volatile ( + \\ sethi %%hi(_GLOBAL_OFFSET_TABLE_ - 4), %%l7 + \\ call 1f + \\ add %%l7, %%lo(_GLOBAL_OFFSET_TABLE_ + 4), %%l7 + \\ 1: + \\ add %%l7, %%o7, %[ret] + : [ret] "=r" (-> [*]const elf.Dyn), + ), + else => { + @compileError("PIE startup is not yet supported for this target!"); + }, }, + .stage2_x86_64 => @extern([*]const elf.Dyn, .{ + .name = "_DYNAMIC", + .linkage = .weak, + .visibility = .hidden, + .relocation = .pcrel, + }).?, }; } -pub fn relocate(phdrs: []elf.Phdr) void { +pub fn relocate(phdrs: []const elf.Phdr) void { @setRuntimeSafety(false); @disableInstrumentation(); @@ -256,10 +264,9 @@ pub fn relocate(phdrs: []elf.Phdr) void { const rel = sorted_dynv[elf.DT_REL]; if (rel != 0) { - const rels = @call(.always_inline, std.mem.bytesAsSlice, .{ - elf.Rel, - @as([*]u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]], - }); + const rels: []const elf.Rel = @alignCast(@ptrCast( + @as([*]align(@alignOf(elf.Rel)) const u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]], + )); for (rels) |r| { if (r.r_type() != R_RELATIVE) continue; @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr; @@ -268,10 +275,9 @@ pub fn relocate(phdrs: []elf.Phdr) void { const rela = sorted_dynv[elf.DT_RELA]; if (rela != 0) { - const relas = @call(.always_inline, std.mem.bytesAsSlice, .{ - elf.Rela, - @as([*]u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]], - }); + const relas: []const elf.Rela = @alignCast(@ptrCast( + @as([*]align(@alignOf(elf.Rela)) const u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]], + )); for (relas) |r| { if (r.r_type() != R_RELATIVE) continue; @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend)); @@ -280,10 +286,9 @@ pub fn relocate(phdrs: []elf.Phdr) void { const relr = sorted_dynv[elf.DT_RELR]; if (relr != 0) { - const relrs = @call(.always_inline, std.mem.bytesAsSlice, .{ - elf.Relr, - @as([*]u8, @ptrFromInt(base_addr + relr))[0..sorted_dynv[elf.DT_RELRSZ]], - }); + const relrs: []const elf.Relr = @ptrCast( + @as([*]align(@alignOf(elf.Relr)) const u8, @ptrFromInt(base_addr + relr))[0..sorted_dynv[elf.DT_RELRSZ]], + ); var current: [*]usize = undefined; for (relrs) |r| { if ((r & 1) == 0) { diff --git a/lib/std/start.zig b/lib/std/start.zig index 2280bea9e7..9b4897260e 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -163,7 +163,7 @@ fn exit2(code: usize) noreturn { // exits(0) .plan9 => std.os.plan9.exits(null), .windows => { - std.os.windows.ntdll.RtlExitUserProcess(@as(u32, @truncate(code))); + std.os.windows.ntdll.RtlExitUserProcess(@truncate(code)); }, else => @compileError("TODO"), } @@ -511,7 +511,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { // Code coverage instrumentation might try to use thread local variables. @disableInstrumentation(); const argc = argc_argv_ptr[0]; - const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1)); + const argv: [*][*:0]u8 = @ptrCast(argc_argv_ptr + 1); const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1)); var envp_count: usize = 0; @@ -573,11 +573,11 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn { expandStackSize(phdrs); } - const opt_init_array_start = @extern([*]*const fn () callconv(.c) void, .{ + const opt_init_array_start = @extern([*]const *const fn () callconv(.c) void, .{ .name = "__init_array_start", .linkage = .weak, }); - const opt_init_array_end = @extern([*]*const fn () callconv(.c) void, .{ + const opt_init_array_end = @extern([*]const *const fn () callconv(.c) void, .{ .name = "__init_array_end", .linkage = .weak, }); @@ -651,7 +651,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal } fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { - std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@as(usize, @intCast(c_argc))]; + std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)]; return callMain(); } @@ -701,7 +701,7 @@ pub inline fn callMain() u8 { pub fn call_wWinMain() std.os.windows.INT { const peb = std.os.windows.peb(); const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?; - const hInstance = @as(MAIN_HINSTANCE, @ptrCast(peb.ImageBaseAddress)); + const hInstance: MAIN_HINSTANCE = @ptrCast(peb.ImageBaseAddress); const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer); // There are various types used for the 'show window' variable through the Win32 APIs: diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index d8d5ff19c7..865e1efa6d 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -1823,6 +1823,14 @@ pub const Visibility = enum(u2) { hidden = 1, protected = 2, + pub fn fromSymbolVisibility(sv: std.builtin.SymbolVisibility) Visibility { + return switch (sv) { + .default => .default, + .hidden => .hidden, + .protected => .protected, + }; + } + pub fn format( self: Visibility, comptime _: []const u8, @@ -2555,6 +2563,10 @@ pub const Variable = struct { return self.ptrConst(builder).global.setLinkage(linkage, builder); } + pub fn setVisibility(self: Index, visibility: Visibility, builder: *Builder) void { + return self.ptrConst(builder).global.setVisibility(visibility, builder); + } + pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { return self.ptrConst(builder).global.setDllStorageClass(class, builder); } diff --git a/lib/zig.h b/lib/zig.h index 229d6a7973..188800cde4 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -272,6 +272,15 @@ #define zig_linksection_fn zig_linksection #endif +#if zig_has_attribute(visibility) +#define zig_visibility(name) __attribute__((visibility(#name))) +#else +#define zig_visibility(name) zig_visibility_##name +#define zig_visibility_default +#define zig_visibility_hidden zig_visibility_hidden_unavailable +#define zig_visibility_protected zig_visibility_protected_unavailable +#endif + #if zig_has_builtin(unreachable) || defined(zig_gcc) || defined(zig_tinyc) #define zig_unreachable() __builtin_unreachable() #elif defined(zig_msvc) diff --git a/src/Air.zig b/src/Air.zig index b315acecce..4810766e71 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -13,6 +13,7 @@ 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"); const types_resolved = @import("Air/types_resolved.zig"); pub const Legalize = @import("Air/Legalize.zig"); @@ -863,16 +864,17 @@ pub const Inst = struct { /// Uses the `vector_store_elem` field. vector_store_elem, - /// Compute a pointer to a threadlocal or dllimport `Nav`, meaning one of: + /// Compute a pointer to a `Nav` at runtime, always one of: /// /// * `threadlocal var` /// * `extern threadlocal var` (or corresponding `@extern`) /// * `@extern` with `.is_dll_import = true` + /// * `@extern` with `.relocation = .pcrel` /// /// Such pointers are runtime values, so cannot be represented with an InternPool index. /// /// Uses the `ty_nav` field. - tlv_dllimport_ptr, + runtime_nav_ptr, /// Implements @cVaArg builtin. /// Uses the `ty_op` field. @@ -1708,7 +1710,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) return .fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type); }, - .tlv_dllimport_ptr => return .fromInterned(datas[@intFromEnum(inst)].ty_nav.ty), + .runtime_nav_ptr => return .fromInterned(datas[@intFromEnum(inst)].ty_nav.ty), .work_item_id, .work_group_size, @@ -1983,7 +1985,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .err_return_trace, .addrspace_cast, .save_err_return_trace_index, - .tlv_dllimport_ptr, + .runtime_nav_ptr, .work_item_id, .work_group_size, .work_group_id, @@ -2141,6 +2143,10 @@ pub const typesFullyResolved = types_resolved.typesFullyResolved; pub const typeFullyResolved = types_resolved.checkType; pub const valFullyResolved = types_resolved.checkVal; 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 CoveragePoint = enum(u1) { /// Indicates the block is not a place of interest corresponding to diff --git a/src/Air/Legalize.zig b/src/Air/Legalize.zig index b5d579c2d3..01304d78e8 100644 --- a/src/Air/Legalize.zig +++ b/src/Air/Legalize.zig @@ -622,7 +622,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void { .addrspace_cast, .save_err_return_trace_index, .vector_store_elem, - .tlv_dllimport_ptr, + .runtime_nav_ptr, .c_va_arg, .c_va_copy, .c_va_end, diff --git a/src/Air/Liveness.zig b/src/Air/Liveness.zig index 7acba48ed0..94efaf114c 100644 --- a/src/Air/Liveness.zig +++ b/src/Air/Liveness.zig @@ -339,7 +339,7 @@ pub fn categorizeOperand( .wasm_memory_size, .err_return_trace, .save_err_return_trace_index, - .tlv_dllimport_ptr, + .runtime_nav_ptr, .c_va_start, .work_item_id, .work_group_size, @@ -972,7 +972,7 @@ fn analyzeInst( .wasm_memory_size, .err_return_trace, .save_err_return_trace_index, - .tlv_dllimport_ptr, + .runtime_nav_ptr, .c_va_start, .work_item_id, .work_group_size, diff --git a/src/Air/Liveness/Verify.zig b/src/Air/Liveness/Verify.zig index 4ad24cf924..2fafc44ab7 100644 --- a/src/Air/Liveness/Verify.zig +++ b/src/Air/Liveness/Verify.zig @@ -63,7 +63,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .wasm_memory_size, .err_return_trace, .save_err_return_trace_index, - .tlv_dllimport_ptr, + .runtime_nav_ptr, .c_va_start, .work_item_id, .work_group_size, diff --git a/src/Air/print.zig b/src/Air/print.zig new file mode 100644 index 0000000000..343c640a63 --- /dev/null +++ b/src/Air/print.zig @@ -0,0 +1,1041 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const fmtIntSizeBin = std.fmt.fmtIntSizeBin; + +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: anytype, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { + comptime std.debug.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 + stream.print( + \\# Total AIR+Liveness bytes: {} + \\# AIR Instructions: {d} ({}) + \\# AIR Extra Data: {d} ({}) + \\# Liveness tomb_bits: {} + \\# Liveness Extra Data: {d} ({}) + \\# Liveness special table: {d} ({}) + \\ + , .{ + fmtIntSizeBin(total_bytes), + air.instructions.len, fmtIntSizeBin(instruction_bytes), + air.extra.items.len, fmtIntSizeBin(extra_bytes), + fmtIntSizeBin(tomb_bytes), + if (liveness) |l| l.extra.len else 0, fmtIntSizeBin(liveness_extra_bytes), + if (liveness) |l| l.special.count() else 0, fmtIntSizeBin(liveness_special_bytes), + }) catch return; + // zig fmt: on + + var writer: Writer = .{ + .pt = pt, + .gpa = pt.zcu.gpa, + .air = air, + .liveness = liveness, + .indent = 2, + .skip_body = false, + }; + writer.writeBody(stream, air.getMainBody()) catch return; +} + +pub fn writeInst( + air: Air, + stream: anytype, + inst: Air.Inst.Index, + pt: Zcu.PerThread, + liveness: ?Air.Liveness, +) void { + comptime std.debug.assert(build_options.enable_debug_extensions); + var writer: Writer = .{ + .pt = pt, + .gpa = pt.zcu.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 { + air.write(std.io.getStdErr().writer(), pt, liveness); +} + +pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { + air.writeInst(std.io.getStdErr().writer(), inst, pt, liveness); +} + +const Writer = struct { + pt: Zcu.PerThread, + gpa: Allocator, + air: Air, + liveness: ?Air.Liveness, + indent: usize, + skip_body: bool, + + fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void { + for (body) |inst| { + try w.writeInst(s, inst); + try s.writeByte('\n'); + } + } + + fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const tag = w.air.instructions.items(.tag)[@intFromEnum(inst)]; + try s.writeByteNTimes(' ', w.indent); + try s.print("{}{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_exact, + .rem, + .mod, + .bit_and, + .bit_or, + .xor, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .bool_and, + .bool_or, + .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_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, + => 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_lt_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, + .bitcast, + .load, + .fptrunc, + .fpext, + .intcast, + .intcast_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, + .float_from_int, + .splat, + .int_from_float, + .int_from_float_optimized, + .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), + .struct_field_val => 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), + .vector_store_elem => try w.writeVectorStoreElem(s, inst), + .runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst), + + .work_item_id, + .work_group_size, + .work_group_id, + => try w.writeWorkDimension(s, inst), + } + try s.writeByte(')'); + } + + fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const bin_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const un_op = w.air.instructions.items(.data)[@intFromEnum(inst)].un_op; + try w.writeOperand(s, inst, 0, un_op); + } + + fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + _ = w; + _ = inst; + // no-op, no argument to write + } + + fn writeType(w: *Writer, s: anytype, ty: Type) !void { + return ty.print(s, w.pt); + } + + fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty = w.air.instructions.items(.data)[@intFromEnum(inst)].ty; + try w.writeType(s, ty); + } + + fn writeArg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg; + try w.writeType(s, arg.ty.toType()); + switch (arg.name) { + .none => {}, + _ => try s.print(", \"{}\"", .{std.zig.fmtEscapes(arg.name.toSlice(w.air))}), + } + } + + fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_op = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; + try w.writeType(s, ty_op.ty.toType()); + try s.writeAll(", "); + try w.writeOperand(s, inst, 0, ty_op.operand); + } + + fn writeBlock(w: *Writer, s: anytype, tag: Air.Inst.Tag, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + try w.writeType(s, ty_pl.ty.toType()); + const body: []const Air.Inst.Index = @ptrCast(switch (tag) { + inline .block, .dbg_inline_block => |comptime_tag| body: { + const extra = w.air.extraData(switch (comptime_tag) { + .block => Air.Block, + .dbg_inline_block => Air.DbgInlineBlock, + else => unreachable, + }, ty_pl.payload); + switch (comptime_tag) { + .block => {}, + .dbg_inline_block => { + try s.writeAll(", "); + try w.writeInstRef(s, Air.internedToRef(extra.data.func), false); + }, + else => unreachable, + } + break :body w.air.extra.items[extra.end..][0..extra.data.body_len]; + }, + 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.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + + for (liveness_block.deaths) |operand| { + try s.print(" {}!", .{operand}); + } + } + + fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = w.air.extraData(Air.Block, ty_pl.payload); + const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); + + try w.writeType(s, ty_pl.ty.toType()); + if (w.skip_body) return s.writeAll(", ..."); + try s.writeAll(", {\n"); + const old_indent = w.indent; + w.indent += 2; + try w.writeBody(s, body); + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + } + + fn writeAggregateInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const zcu = w.pt.zcu; + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const vector_ty = ty_pl.ty.toType(); + 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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const data = w.air.instructions.items(.data); + const ty_pl = data[@intFromEnum(inst)].ty_pl; + const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; + + const inst_ty = data[@intFromEnum(inst)].ty_pl.ty.toType(); + 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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(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 writeShuffleOne(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const unwrapped = w.air.unwrapShuffleOne(w.pt.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 {}", .{Value.fromInterned(val).fmtValue(w.pt)}), + } + } + try s.writeByte(']'); + } + + fn writeShuffleTwo(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const unwrapped = w.air.unwrapShuffleTwo(w.pt.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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const zcu = w.pt.zcu; + const pl_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const reduce = w.air.instructions.items(.data)[@intFromEnum(inst)].reduce; + + try w.writeOperand(s, inst, 0, reduce.operand); + try s.print(", {s}", .{@tagName(reduce.operation)}); + } + + fn writeCmpVector(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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 writeVectorStoreElem(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const data = w.air.instructions.items(.data)[@intFromEnum(inst)].vector_store_elem; + const extra = w.air.extraData(Air.VectorCmp, data.payload).data; + + try w.writeOperand(s, inst, 0, data.vector_ptr); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 2, extra.rhs); + } + + fn writeRuntimeNavPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ip = &w.pt.zcu.intern_pool; + const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; + try w.writeType(s, .fromInterned(ty_nav.ty)); + try s.print(", '{}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); + } + + fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const atomic_load = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const prefetch = w.air.instructions.items(.data)[@intFromEnum(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: anytype, + inst: Air.Inst.Index, + order: std.builtin.AtomicOrder, + ) @TypeOf(s).Error!void { + const bin_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = w.air.extraData(Air.Asm, ty_pl.payload); + const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; + const clobbers_len = @as(u31, @truncate(extra.data.flags)); + var extra_i: usize = extra.end; + var op_index: usize = 0; + + const ret_ty = w.typeOfIndex(inst); + try w.writeType(s, ret_ty); + + if (is_volatile) { + try s.writeAll(", volatile"); + } + + const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.outputs_len])); + extra_i += outputs.len; + const inputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.inputs_len])); + extra_i += inputs.len; + + for (outputs) |output| { + const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the strings and their null terminators, we still use the next u32 + // for the null terminator. + extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (output == .none) { + try s.print(", [{s}] -> {s}", .{ name, constraint }); + } else { + try s.print(", [{s}] out {s} = (", .{ name, constraint }); + try w.writeOperand(s, inst, op_index, output); + op_index += 1; + try s.writeByte(')'); + } + } + + for (inputs) |input| { + const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); + const constraint = std.mem.sliceTo(extra_bytes, 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the strings and their null terminators, we still use the next u32 + // for the null terminator. + extra_i += (constraint.len + name.len + 1) / 4 + 1; + + try s.print(", [{s}] in {s} = (", .{ name, constraint }); + try w.writeOperand(s, inst, op_index, input); + op_index += 1; + try s.writeByte(')'); + } + + { + var clobber_i: u32 = 0; + while (clobber_i < clobbers_len) : (clobber_i += 1) { + const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); + const clobber = std.mem.sliceTo(extra_bytes, 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; + + try s.writeAll(", ~{"); + try s.writeAll(clobber); + try s.writeAll("}"); + } + } + const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len]; + try s.print(", \"{}\"", .{std.zig.fmtEscapes(asm_source)}); + } + + fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const dbg_stmt = w.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; + try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); + } + + fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + try w.writeOperand(s, inst, 0, pl_op.operand); + const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); + try s.print(", \"{}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))}); + } + + fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + const extra = w.air.extraData(Air.Call, pl_op.payload); + const args = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len])); + try w.writeOperand(s, inst, 0, pl_op.operand); + 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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const br = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const repeat = w.air.instructions.items(.data)[@intFromEnum(inst)].repeat; + try w.writeInstIndex(s, repeat.loop_inst, false); + } + + fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + const extra = w.air.extraData(Air.Try, pl_op.payload); + const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); + const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| + liveness.getCondBr(inst) + else + .{ .then_deaths = &.{}, .else_deaths = &.{} }; + + try w.writeOperand(s, inst, 0, pl_op.operand); + 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.writeByteNTimes(' ', w.indent); + for (liveness_condbr.else_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{}!", .{operand}); + } + try s.writeAll("\n"); + } + try w.writeBody(s, body); + + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + + for (liveness_condbr.then_deaths) |operand| { + try s.print(" {}!", .{operand}); + } + } + + fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; + const extra = w.air.extraData(Air.TryPtr, ty_pl.payload); + const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); + const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| + liveness.getCondBr(inst) + else + .{ .then_deaths = &.{}, .else_deaths = &.{} }; + + try w.writeOperand(s, inst, 0, extra.data.ptr); + + try s.writeAll(", "); + try w.writeType(s, ty_pl.ty.toType()); + 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.writeByteNTimes(' ', w.indent); + for (liveness_condbr.else_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{}!", .{operand}); + } + try s.writeAll("\n"); + } + try w.writeBody(s, body); + + w.indent = old_indent; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + + for (liveness_condbr.then_deaths) |operand| { + try s.print(" {}!", .{operand}); + } + } + + fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + const extra = w.air.extraData(Air.CondBr, pl_op.payload); + const then_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.then_body_len]); + const else_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| + liveness.getCondBr(inst) + else + .{ .then_deaths = &.{}, .else_deaths = &.{} }; + + try w.writeOperand(s, inst, 0, pl_op.operand); + if (w.skip_body) return s.writeAll(", ..."); + try s.writeAll(","); + if (extra.data.branch_hints.true != .none) { + try s.print(" {s}", .{@tagName(extra.data.branch_hints.true)}); + } + if (extra.data.branch_hints.then_cov != .none) { + try s.print(" {s}", .{@tagName(extra.data.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.writeByteNTimes(' ', w.indent); + for (liveness_condbr.then_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, then_body); + try s.writeByteNTimes(' ', old_indent); + try s.writeAll("},"); + if (extra.data.branch_hints.false != .none) { + try s.print(" {s}", .{@tagName(extra.data.branch_hints.false)}); + } + if (extra.data.branch_hints.else_cov != .none) { + try s.print(" {s}", .{@tagName(extra.data.branch_hints.else_cov)}); + } + try s.writeAll(" {\n"); + + if (liveness_condbr.else_deaths.len != 0) { + try s.writeByteNTimes(' ', w.indent); + for (liveness_condbr.else_deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, else_body); + w.indent = old_indent; + + try s.writeByteNTimes(' ', old_indent); + try s.writeAll("}"); + } + + fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).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.writeByteNTimes(' ', w.indent); + for (deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, case.body); + w.indent -= 2; + try s.writeByteNTimes(' ', 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.writeByteNTimes(' ', w.indent); + for (deaths, 0..) |operand, i| { + if (i != 0) try s.writeAll(" "); + try s.print("{}!", .{operand}); + } + try s.writeAll("\n"); + } + + try w.writeBody(s, else_body); + w.indent -= 2; + try s.writeByteNTimes(' ', w.indent); + try s.writeAll("}"); + } + + try s.writeAll("\n"); + try s.writeByteNTimes(' ', old_indent); + } + + fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + try s.print("{d}", .{pl_op.payload}); + } + + fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + try s.print("{d}, ", .{pl_op.payload}); + try w.writeOperand(s, inst, 0, pl_op.operand); + } + + fn writeWorkDimension(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; + try s.print("{d}", .{pl_op.payload}); + } + + fn writeOperand( + w: *Writer, + s: anytype, + inst: Air.Inst.Index, + op_index: usize, + operand: Air.Inst.Ref, + ) @TypeOf(s).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: anytype, + operand: Air.Inst.Ref, + dies: bool, + ) @TypeOf(s).Error!void { + if (@intFromEnum(operand) < InternPool.static_len) { + return s.print("@{}", .{operand}); + } else if (operand.toInterned()) |ip_index| { + const pt = w.pt; + const ty = Type.fromInterned(pt.zcu.intern_pool.indexToKey(ip_index).typeOf()); + try s.print("<{}, {}>", .{ + ty.fmt(pt), + Value.fromInterned(ip_index).fmtValue(pt), + }); + } else { + return w.writeInstIndex(s, operand.toIndex().?, dies); + } + } + + fn writeInstIndex( + w: *Writer, + s: anytype, + inst: Air.Inst.Index, + dies: bool, + ) @TypeOf(s).Error!void { + _ = w; + try s.print("{}", .{inst}); + if (dies) try s.writeByte('!'); + } + + fn typeOfIndex(w: *Writer, inst: Air.Inst.Index) Type { + const zcu = w.pt.zcu; + return w.air.typeOfIndex(inst, &zcu.intern_pool); + } +}; diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig index eb17402ebe..1def679794 100644 --- a/src/Air/types_resolved.zig +++ b/src/Air/types_resolved.zig @@ -321,7 +321,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { if (!checkRef(bin.rhs, zcu)) return false; }, - .tlv_dllimport_ptr => { + .runtime_nav_ptr => { if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false; }, diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 626b2c3631..637a4bb280 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -345,11 +345,19 @@ pub fn resolve(options: Options) ResolveError!Config { } else false; }; + const is_dyn_lib = switch (options.output_mode) { + .Obj, .Exe => false, + .Lib => link_mode == .dynamic, + }; + // Make a decision on whether to use LLVM backend for machine code generation. // Note that using the LLVM backend does not necessarily mean using LLVM libraries. // For example, Zig can emit .bc and .ll files directly, and this is still considered // using "the LLVM backend". - const prefer_llvm = b: { + const use_llvm = b: { + // If we have no zig code to compile, no need for LLVM. + 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 (options.use_llvm == false) @@ -382,9 +390,9 @@ pub fn resolve(options: Options) ResolveError!Config { // Prefer LLVM for release builds. if (root_optimize_mode != .Debug) break :b true; - // Self-hosted backends can't handle the inline assembly in std.pie yet - // https://github.com/ziglang/zig/issues/24046 - if (pie) break :b true; + // load_dynamic_library standalone test not passing on this combination + // https://github.com/ziglang/zig/issues/24080 + if (target.os.tag == .macos and is_dyn_lib) break :b true; // At this point we would prefer to use our own self-hosted backend, // because the compilation speed is better than LLVM. But only do it if @@ -392,13 +400,6 @@ pub fn resolve(options: Options) ResolveError!Config { break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target); }; - const use_llvm = b: { - // If we have no zig code to compile, no need for LLVM. - if (!options.have_zcu) break :b false; - - break :b prefer_llvm; - }; - if (options.emit_bin and options.have_zcu) { if (!use_lib_llvm and use_llvm) { // Explicit request to use LLVM to produce an object file, but without @@ -435,7 +436,13 @@ pub fn resolve(options: Options) ResolveError!Config { } if (options.use_lld) |x| break :b x; - break :b prefer_llvm; + + // If we have no zig code to compile, no need for the self-hosted linker. + if (!options.have_zcu) break :b true; + + // If we do have zig code, match the decision for whether to use the llvm backend, + // so that the llvm backend defaults to lld and the self-hosted backends do not. + break :b use_llvm; }; const lto: std.zig.LtoMode = b: { diff --git a/src/InternPool.zig b/src/InternPool.zig index 44f19e3e29..de1a434c02 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -526,10 +526,10 @@ pub const Nav = struct { /// The type of this `Nav` is resolved; the value is queued for resolution. type_resolved: struct { type: InternPool.Index, + is_const: bool, alignment: Alignment, @"linksection": OptionalNullTerminatedString, @"addrspace": std.builtin.AddressSpace, - is_const: bool, is_threadlocal: bool, /// This field is whether this `Nav` is a literal `extern` definition. /// It does *not* tell you whether this might alias an extern fn (see #21027). @@ -538,6 +538,7 @@ pub const Nav = struct { /// The value of this `Nav` is resolved. fully_resolved: struct { val: InternPool.Index, + is_const: bool, alignment: Alignment, @"linksection": OptionalNullTerminatedString, @"addrspace": std.builtin.AddressSpace, @@ -727,12 +728,12 @@ pub const Nav = struct { const Bits = packed struct(u16) { status: enum(u2) { unresolved, type_resolved, fully_resolved, type_resolved_extern_decl }, /// Populated only if `bits.status != .unresolved`. + is_const: bool, + /// Populated only if `bits.status != .unresolved`. alignment: Alignment, /// Populated only if `bits.status != .unresolved`. @"addrspace": std.builtin.AddressSpace, /// Populated only if `bits.status == .type_resolved`. - is_const: bool, - /// Populated only if `bits.status == .type_resolved`. is_threadlocal: bool, is_usingnamespace: bool, }; @@ -753,15 +754,16 @@ pub const Nav = struct { .unresolved => .unresolved, .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{ .type = repr.type_or_val, + .is_const = repr.bits.is_const, .alignment = repr.bits.alignment, .@"linksection" = repr.@"linksection", .@"addrspace" = repr.bits.@"addrspace", - .is_const = repr.bits.is_const, .is_threadlocal = repr.bits.is_threadlocal, .is_extern_decl = repr.bits.status == .type_resolved_extern_decl, } }, .fully_resolved => .{ .fully_resolved = .{ .val = repr.type_or_val, + .is_const = repr.bits.is_const, .alignment = repr.bits.alignment, .@"linksection" = repr.@"linksection", .@"addrspace" = repr.bits.@"addrspace", @@ -792,26 +794,26 @@ pub const Nav = struct { .bits = switch (nav.status) { .unresolved => .{ .status = .unresolved, + .is_const = false, .alignment = .none, .@"addrspace" = .generic, .is_usingnamespace = nav.is_usingnamespace, - .is_const = false, .is_threadlocal = false, }, .type_resolved => |r| .{ .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved, + .is_const = r.is_const, .alignment = r.alignment, .@"addrspace" = r.@"addrspace", .is_usingnamespace = nav.is_usingnamespace, - .is_const = r.is_const, .is_threadlocal = r.is_threadlocal, }, .fully_resolved => |r| .{ .status = .fully_resolved, + .is_const = r.is_const, .alignment = r.alignment, .@"addrspace" = r.@"addrspace", .is_usingnamespace = nav.is_usingnamespace, - .is_const = false, .is_threadlocal = false, }, }, @@ -2221,7 +2223,6 @@ pub const Key = union(enum) { init: Index, owner_nav: Nav.Index, is_threadlocal: bool, - is_weak_linkage: bool, }; pub const Extern = struct { @@ -2234,10 +2235,12 @@ pub const Key = union(enum) { /// For example `extern "c" fn write(...) usize` would have 'c' as library name. /// Index into the string table bytes. lib_name: OptionalNullTerminatedString, - is_const: bool, + linkage: std.builtin.GlobalLinkage, + visibility: std.builtin.SymbolVisibility, is_threadlocal: bool, - is_weak_linkage: bool, is_dll_import: bool, + relocation: std.builtin.ExternOptions.Relocation, + is_const: bool, alignment: Alignment, @"addrspace": std.builtin.AddressSpace, /// The ZIR instruction which created this extern; used only for source locations. @@ -2844,9 +2847,10 @@ pub const Key = union(enum) { .@"extern" => |e| Hash.hash(seed, asBytes(&e.name) ++ asBytes(&e.ty) ++ asBytes(&e.lib_name) ++ - asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++ - asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++ - asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++ + asBytes(&e.linkage) ++ asBytes(&e.visibility) ++ + asBytes(&e.is_threadlocal) ++ asBytes(&e.is_dll_import) ++ + asBytes(&e.relocation) ++ + asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)), }; } @@ -2928,21 +2932,22 @@ pub const Key = union(enum) { .variable => |a_info| { const b_info = b.variable; - return a_info.owner_nav == b_info.owner_nav and - a_info.ty == b_info.ty and + return a_info.ty == b_info.ty and a_info.init == b_info.init and - a_info.is_threadlocal == b_info.is_threadlocal and - a_info.is_weak_linkage == b_info.is_weak_linkage; + a_info.owner_nav == b_info.owner_nav and + a_info.is_threadlocal == b_info.is_threadlocal; }, .@"extern" => |a_info| { const b_info = b.@"extern"; return a_info.name == b_info.name and a_info.ty == b_info.ty and a_info.lib_name == b_info.lib_name and - a_info.is_const == b_info.is_const and + a_info.linkage == b_info.linkage and + a_info.visibility == b_info.visibility and a_info.is_threadlocal == b_info.is_threadlocal and - a_info.is_weak_linkage == b_info.is_weak_linkage and a_info.is_dll_import == b_info.is_dll_import and + a_info.relocation == b_info.relocation and + a_info.is_const == b_info.is_const and a_info.alignment == b_info.alignment and a_info.@"addrspace" == b_info.@"addrspace" and a_info.zir_index == b_info.zir_index; @@ -4889,6 +4894,7 @@ pub const Index = enum(u32) { float_c_longdouble_f128: struct { data: *Float128 }, float_comptime_float: struct { data: *Float128 }, variable: struct { data: *Tag.Variable }, + threadlocal_variable: struct { data: *Tag.Variable }, @"extern": struct { data: *Tag.Extern }, func_decl: struct { const @"data.analysis.inferred_error_set" = opaque {}; @@ -5548,6 +5554,9 @@ pub const Tag = enum(u8) { /// A global variable. /// data is extra index to Variable. variable, + /// A global threadlocal variable. + /// data is extra index to Variable. + threadlocal_variable, /// An extern function or variable. /// data is extra index to Extern. /// Some parts of the key are stored in `owner_nav`. @@ -5863,6 +5872,7 @@ pub const Tag = enum(u8) { .float_c_longdouble_f128 = .{ .summary = .@"@as(c_longdouble, {.payload%value})", .payload = f128 }, .float_comptime_float = .{ .summary = .@"{.payload%value}", .payload = f128 }, .variable = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Variable }, + .threadlocal_variable = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Variable }, .@"extern" = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Extern }, .func_decl = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", @@ -5913,24 +5923,24 @@ pub const Tag = enum(u8) { /// May be `none`. init: Index, owner_nav: Nav.Index, - flags: Flags, - - pub const Flags = packed struct(u32) { - is_const: bool, - is_threadlocal: bool, - is_weak_linkage: bool, - is_dll_import: bool, - _: u28 = 0, - }; }; pub const Extern = struct { - // name, alignment, addrspace come from `owner_nav`. + // name, is_const, alignment, addrspace come from `owner_nav`. ty: Index, lib_name: OptionalNullTerminatedString, - flags: Variable.Flags, + flags: Flags, owner_nav: Nav.Index, zir_index: TrackedInst.Index, + + pub const Flags = packed struct(u32) { + linkage: std.builtin.GlobalLinkage, + visibility: std.builtin.SymbolVisibility, + is_threadlocal: bool, + is_dll_import: bool, + relocation: std.builtin.ExternOptions.Relocation, + _: u25 = 0, + }; }; /// Trailing: @@ -7248,14 +7258,17 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .ty = .comptime_float_type, .storage = .{ .f128 = extraData(unwrapped_index.getExtra(ip), Float128, data).get() }, } }, - .variable => { + .variable, .threadlocal_variable => { const extra = extraData(unwrapped_index.getExtra(ip), Tag.Variable, data); return .{ .variable = .{ .ty = extra.ty, .init = extra.init, .owner_nav = extra.owner_nav, - .is_threadlocal = extra.flags.is_threadlocal, - .is_weak_linkage = extra.flags.is_weak_linkage, + .is_threadlocal = switch (item.tag) { + else => unreachable, + .variable => false, + .threadlocal_variable => true, + }, } }; }, .@"extern" => { @@ -7265,10 +7278,12 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .name = nav.name, .ty = extra.ty, .lib_name = extra.lib_name, - .is_const = extra.flags.is_const, + .linkage = extra.flags.linkage, + .visibility = extra.flags.visibility, .is_threadlocal = extra.flags.is_threadlocal, - .is_weak_linkage = extra.flags.is_weak_linkage, .is_dll_import = extra.flags.is_dll_import, + .relocation = extra.flags.relocation, + .is_const = nav.status.fully_resolved.is_const, .alignment = nav.status.fully_resolved.alignment, .@"addrspace" = nav.status.fully_resolved.@"addrspace", .zir_index = extra.zir_index, @@ -7895,17 +7910,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All const has_init = variable.init != .none; if (has_init) assert(variable.ty == ip.typeOf(variable.init)); items.appendAssumeCapacity(.{ - .tag = .variable, + .tag = switch (variable.is_threadlocal) { + false => .variable, + true => .threadlocal_variable, + }, .data = try addExtra(extra, Tag.Variable{ .ty = variable.ty, .init = variable.init, .owner_nav = variable.owner_nav, - .flags = .{ - .is_const = false, - .is_threadlocal = variable.is_threadlocal, - .is_weak_linkage = variable.is_weak_linkage, - .is_dll_import = false, - }, }), }); }, @@ -9128,6 +9140,7 @@ pub fn getExtern( .name = key.name, .fqn = key.name, .val = extern_index, + .is_const = key.is_const, .alignment = key.alignment, .@"linksection" = .none, .@"addrspace" = key.@"addrspace", @@ -9136,10 +9149,11 @@ pub fn getExtern( .ty = key.ty, .lib_name = key.lib_name, .flags = .{ - .is_const = key.is_const, + .linkage = key.linkage, + .visibility = key.visibility, .is_threadlocal = key.is_threadlocal, - .is_weak_linkage = key.is_weak_linkage, .is_dll_import = key.is_dll_import, + .relocation = key.relocation, }, .zir_index = key.zir_index, .owner_nav = owner_nav, @@ -9714,6 +9728,7 @@ fn finishFuncInstance( .name = nav_name, .fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name), .val = func_index, + .is_const = fn_owner_nav.status.fully_resolved.is_const, .alignment = fn_owner_nav.status.fully_resolved.alignment, .@"linksection" = fn_owner_nav.status.fully_resolved.@"linksection", .@"addrspace" = fn_owner_nav.status.fully_resolved.@"addrspace", @@ -10300,13 +10315,13 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 { u32, i32, FuncAnalysis, + Tag.Extern.Flags, Tag.TypePointer.Flags, Tag.TypeFunction.Flags, Tag.TypePointer.PackedOffset, Tag.TypeUnion.Flags, Tag.TypeStruct.Flags, Tag.TypeStructPacked.Flags, - Tag.Variable.Flags, => @bitCast(@field(item, field.name)), else => @compileError("bad field type: " ++ @typeName(field.type)), @@ -10361,13 +10376,13 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat u32, i32, + Tag.Extern.Flags, Tag.TypePointer.Flags, Tag.TypeFunction.Flags, Tag.TypePointer.PackedOffset, Tag.TypeUnion.Flags, Tag.TypeStruct.Flags, Tag.TypeStructPacked.Flags, - Tag.Variable.Flags, FuncAnalysis, => @bitCast(extra_item), @@ -11162,7 +11177,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { .float_c_longdouble_f80 => @sizeOf(Float80), .float_c_longdouble_f128 => @sizeOf(Float128), .float_comptime_float => @sizeOf(Float128), - .variable => @sizeOf(Tag.Variable), + .variable, .threadlocal_variable => @sizeOf(Tag.Variable), .@"extern" => @sizeOf(Tag.Extern), .func_decl => @sizeOf(Tag.FuncDecl), .func_instance => b: { @@ -11282,6 +11297,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { .float_c_longdouble_f128, .float_comptime_float, .variable, + .threadlocal_variable, .@"extern", .func_decl, .func_instance, @@ -11414,6 +11430,7 @@ pub fn createNav( name: NullTerminatedString, fqn: NullTerminatedString, val: InternPool.Index, + is_const: bool, alignment: Alignment, @"linksection": OptionalNullTerminatedString, @"addrspace": std.builtin.AddressSpace, @@ -11430,6 +11447,7 @@ pub fn createNav( .analysis = null, .status = .{ .fully_resolved = .{ .val = opts.val, + .is_const = opts.is_const, .alignment = opts.alignment, .@"linksection" = opts.@"linksection", .@"addrspace" = opts.@"addrspace", @@ -11482,10 +11500,10 @@ pub fn resolveNavType( nav: Nav.Index, resolved: struct { type: InternPool.Index, + is_const: bool, alignment: Alignment, @"linksection": OptionalNullTerminatedString, @"addrspace": std.builtin.AddressSpace, - is_const: bool, is_threadlocal: bool, is_extern_decl: bool, }, @@ -11512,9 +11530,9 @@ pub fn resolveNavType( var bits = nav_bits[unwrapped.index]; bits.status = if (resolved.is_extern_decl) .type_resolved_extern_decl else .type_resolved; + bits.is_const = resolved.is_const; bits.alignment = resolved.alignment; bits.@"addrspace" = resolved.@"addrspace"; - bits.is_const = resolved.is_const; bits.is_threadlocal = resolved.is_threadlocal; @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release); } @@ -11526,6 +11544,7 @@ pub fn resolveNavValue( nav: Nav.Index, resolved: struct { val: InternPool.Index, + is_const: bool, alignment: Alignment, @"linksection": OptionalNullTerminatedString, @"addrspace": std.builtin.AddressSpace, @@ -11553,6 +11572,7 @@ pub fn resolveNavValue( var bits = nav_bits[unwrapped.index]; bits.status = .fully_resolved; + bits.is_const = resolved.is_const; bits.alignment = resolved.alignment; bits.@"addrspace" = resolved.@"addrspace"; @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release); @@ -12007,6 +12027,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .error_union_error, .enum_tag, .variable, + .threadlocal_variable, .@"extern", .func_decl, .func_instance, @@ -12391,6 +12412,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId { .float_c_longdouble_f128, .float_comptime_float, .variable, + .threadlocal_variable, .@"extern", .func_decl, .func_instance, diff --git a/src/Sema.zig b/src/Sema.zig index c732a283a0..e20fb17f26 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -26089,10 +26089,12 @@ fn resolveExternOptions( zir_ref: Zir.Inst.Ref, ) CompileError!struct { name: InternPool.NullTerminatedString, - library_name: InternPool.OptionalNullTerminatedString = .none, - linkage: std.builtin.GlobalLinkage = .strong, - is_thread_local: bool = false, - is_dll_import: bool = false, + library_name: InternPool.OptionalNullTerminatedString, + linkage: std.builtin.GlobalLinkage, + visibility: std.builtin.SymbolVisibility, + is_thread_local: bool, + is_dll_import: bool, + relocation: std.builtin.ExternOptions.Relocation, } { const pt = sema.pt; const zcu = pt.zcu; @@ -26105,8 +26107,10 @@ fn resolveExternOptions( const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); + const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node }); + const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src); const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options }); @@ -26118,6 +26122,10 @@ fn resolveExternOptions( const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options }); const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage); + const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src); + const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options }); + const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility); + const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src); const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options }); @@ -26133,6 +26141,10 @@ fn resolveExternOptions( const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src); const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options }); + const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src); + const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options }); + const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation); + if (name.len == 0) { return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); } @@ -26145,8 +26157,10 @@ fn resolveExternOptions( .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls), .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls), .linkage = linkage, + .visibility = visibility, .is_thread_local = is_thread_local_val.toBool(), .is_dll_import = is_dll_import_val.toBool(), + .relocation = relocation, }; } @@ -26178,6 +26192,17 @@ fn zirBuiltinExtern( } const options = try sema.resolveExternOptions(block, options_src, extra.rhs); + switch (options.linkage) { + .internal => if (options.visibility != .default) { + return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{}); + }, + .strong, .weak => {}, + .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}), + } + switch (options.relocation) { + .any => {}, + .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}), + } // TODO: error for threadlocal functions, non-const functions, etc @@ -26190,10 +26215,12 @@ fn zirBuiltinExtern( .name = options.name, .ty = ptr_info.child, .lib_name = options.library_name, - .is_const = ptr_info.flags.is_const, + .linkage = options.linkage, + .visibility = options.visibility, .is_threadlocal = options.is_thread_local, - .is_weak_linkage = options.linkage == .weak, .is_dll_import = options.is_dll_import, + .relocation = options.relocation, + .is_const = ptr_info.flags.is_const, .alignment = ptr_info.flags.alignment, .@"addrspace" = ptr_info.flags.address_space, // This instruction is just for source locations. @@ -31685,12 +31712,15 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde const nav_status = ip.getNav(nav_index).status; - const is_tlv_or_dllimport = switch (nav_status) { + const is_runtime = switch (nav_status) { .unresolved => unreachable, // dllimports go straight to `fully_resolved`; the only option is threadlocal .type_resolved => |r| r.is_threadlocal, .fully_resolved => |r| switch (ip.indexToKey(r.val)) { - .@"extern" => |e| e.is_threadlocal or e.is_dll_import, + .@"extern" => |e| e.is_threadlocal or e.is_dll_import or switch (e.relocation) { + .any => false, + .pcrel => true, + }, .variable => |v| v.is_threadlocal, else => false, }, @@ -31699,7 +31729,7 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) { .unresolved => unreachable, .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, - .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) }, + .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const }, }; const ptr_ty = try pt.ptrTypeSema(.{ .child = ty, @@ -31710,10 +31740,10 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde }, }); - if (is_tlv_or_dllimport) { + if (is_runtime) { // This pointer is runtime-known; we need to emit an AIR instruction to create it. return block.addInst(.{ - .tag = .tlv_dllimport_ptr, + .tag = .runtime_nav_ptr, .data = .{ .ty_nav = .{ .ty = ptr_ty.toIntern(), .nav = nav_index, @@ -36432,6 +36462,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .float_c_longdouble_f128, .float_comptime_float, .variable, + .threadlocal_variable, .@"extern", .func_decl, .func_instance, diff --git a/src/Zcu.zig b/src/Zcu.zig index d4a0adf284..7223e5a55e 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -2047,6 +2047,7 @@ pub const SrcLoc = struct { .init_field_library, .init_field_thread_local, .init_field_dll_import, + .init_field_relocation, => |builtin_call_node| { const wanted = switch (src_loc.lazy) { .init_field_name => "name", @@ -2059,6 +2060,7 @@ pub const SrcLoc = struct { .init_field_library => "library", .init_field_thread_local => "thread_local", .init_field_dll_import => "dll_import", + .init_field_relocation => "relocation", else => unreachable, }; const tree = try src_loc.file_scope.getTree(zcu); @@ -2506,6 +2508,7 @@ pub const LazySrcLoc = struct { init_field_library: Ast.Node.Offset, init_field_thread_local: Ast.Node.Offset, init_field_dll_import: Ast.Node.Offset, + init_field_relocation: Ast.Node.Offset, /// The source location points to the value of an item in a specific /// case of a `switch`. switch_case_item: SwitchItem, @@ -4562,15 +4565,6 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu return .ok; } -/// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer. -pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool { - return switch (zcu.intern_pool.indexToKey(val)) { - .variable => false, - .@"extern" => |e| e.is_const, - else => true, - }; -} - pub const CodegenFailError = error{ /// Indicates the error message has been already stored at `Zcu.failed_codegen`. CodegenFail, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 8e3d07627f..8b35d8d799 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1153,18 +1153,23 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr // First, we must resolve the declaration's type. To do this, we analyze the type body if available, // or otherwise, we analyze the value body, populating `early_val` in the process. - switch (zir_decl.kind) { + const is_const = is_const: switch (zir_decl.kind) { .@"comptime" => unreachable, // this is not a Nav - .unnamed_test, .@"test", .decltest => assert(nav_ty.zigTypeTag(zcu) == .@"fn"), - .@"usingnamespace" => {}, - .@"const" => {}, - .@"var" => try sema.validateVarType( - &block, - if (zir_decl.type_body != null) ty_src else init_src, - nav_ty, - zir_decl.linkage == .@"extern", - ), - } + .unnamed_test, .@"test", .decltest => { + assert(nav_ty.zigTypeTag(zcu) == .@"fn"); + break :is_const true; + }, + .@"usingnamespace", .@"const" => true, + .@"var" => { + try sema.validateVarType( + &block, + if (zir_decl.type_body != null) ty_src else init_src, + nav_ty, + zir_decl.linkage == .@"extern", + ); + break :is_const false; + }, + }; // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine // the full pointer type of this declaration. @@ -1195,7 +1200,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr .init = final_val.?.toIntern(), .owner_nav = nav_id, .is_threadlocal = zir_decl.is_threadlocal, - .is_weak_linkage = false, } })), else => final_val.?, }, @@ -1212,10 +1216,12 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr .name = old_nav.name, .ty = nav_ty.toIntern(), .lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls), - .is_const = zir_decl.kind == .@"const", .is_threadlocal = zir_decl.is_threadlocal, - .is_weak_linkage = false, + .linkage = .strong, + .visibility = .default, .is_dll_import = false, + .relocation = .any, + .is_const = is_const, .alignment = modifiers.alignment, .@"addrspace" = modifiers.@"addrspace", .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction @@ -1243,6 +1249,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr } ip.resolveNavValue(nav_id, .{ .val = nav_val.toIntern(), + .is_const = is_const, .alignment = .none, .@"linksection" = .none, .@"addrspace" = .generic, @@ -1286,6 +1293,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr ip.resolveNavValue(nav_id, .{ .val = nav_val.toIntern(), + .is_const = is_const, .alignment = modifiers.alignment, .@"linksection" = modifiers.@"linksection", .@"addrspace" = modifiers.@"addrspace", @@ -1515,8 +1523,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr // the pointer modifiers, i.e. alignment, linksection, addrspace. const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty); - // Usually, we can infer this information from the resolved `Nav` value; see `Zcu.navValIsConst`. - // However, since we don't have one, we need to quickly check the ZIR to figure this out. const is_const = switch (zir_decl.kind) { .@"comptime" => unreachable, .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true, @@ -1542,7 +1548,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr r.alignment != modifiers.alignment or r.@"linksection" != modifiers.@"linksection" or r.@"addrspace" != modifiers.@"addrspace" or - zcu.navValIsConst(r.val) != is_const or + r.is_const != is_const or (old_nav.getExtern(ip) != null) != is_extern_decl, }; @@ -1550,10 +1556,10 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr ip.resolveNavType(nav_id, .{ .type = resolved_ty.toIntern(), + .is_const = is_const, .alignment = modifiers.alignment, .@"linksection" = modifiers.@"linksection", .@"addrspace" = modifiers.@"addrspace", - .is_const = is_const, .is_threadlocal = zir_decl.is_threadlocal, .is_extern_decl = is_extern_decl, }); @@ -1750,7 +1756,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: *A if (build_options.enable_debug_extensions and comp.verbose_air) { std.debug.print("# Begin Function AIR: {}:\n", .{nav.fqn.fmt(ip)}); - @import("../print_air.zig").dump(pt, air.*, liveness); + air.dump(pt, liveness); std.debug.print("# End Function AIR: {}\n\n", .{nav.fqn.fmt(ip)}); } @@ -3577,8 +3583,10 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V .lib_name = e.lib_name, .is_const = e.is_const, .is_threadlocal = e.is_threadlocal, - .is_weak_linkage = e.is_weak_linkage, + .linkage = e.linkage, + .visibility = e.visibility, .is_dll_import = e.is_dll_import, + .relocation = e.relocation, .alignment = e.alignment, .@"addrspace" = e.@"addrspace", .zir_index = e.zir_index, @@ -3954,7 +3962,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) { .unresolved => unreachable, .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, - .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) }, + .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const }, }; return pt.ptrType(.{ .child = ty, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index c01fa24ecc..00cceb0c67 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -880,7 +880,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), - .tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}), + .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 9c7793df27..421ba7d753 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -869,7 +869,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), - .tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}), + .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 530af98a0d..9fc51bd2d3 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1041,12 +1041,7 @@ fn formatAir( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - @import("../../print_air.zig").dumpInst( - data.inst, - data.func.pt, - data.func.air, - data.func.liveness, - ); + data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness); } fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { return .{ .data = .{ .func = func, .inst = inst } }; @@ -1656,7 +1651,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .wrap_errunion_payload => try func.airWrapErrUnionPayload(inst), .wrap_errunion_err => try func.airWrapErrUnionErr(inst), - .tlv_dllimport_ptr => try func.airTlvDllimportPtr(inst), + .runtime_nav_ptr => try func.airRuntimeNavPtr(inst), .add_optimized, .sub_optimized, @@ -3626,7 +3621,7 @@ fn airWrapErrUnionErr(func: *Func, inst: Air.Inst.Index) !void { return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airTlvDllimportPtr(func: *Func, inst: Air.Inst.Index) !void { +fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void { const zcu = func.pt.zcu; const ip = &zcu.intern_pool; const ty_nav = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; @@ -3641,7 +3636,7 @@ fn airTlvDllimportPtr(func: *Func, inst: Air.Inst.Index) !void { break :sym sym; } break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); - } else return func.fail("TODO tlv_dllimport_ptr on {}", .{func.bin_file.tag}); + } else return func.fail("TODO runtime_nav_ptr on {}", .{func.bin_file.tag}); const dest_mcv = try func.allocRegOrMem(ptr_ty, inst, true); if (dest_mcv.isRegister()) { diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 439e5e6dbb..ad9884dcdb 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_named_enum_value => @panic("TODO implement is_named_enum_value"), .error_set_has_value => @panic("TODO implement error_set_has_value"), .vector_store_elem => @panic("TODO implement vector_store_elem"), - .tlv_dllimport_ptr => @panic("TODO implement tlv_dllimport_ptr"), + .runtime_nav_ptr => @panic("TODO implement runtime_nav_ptr"), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c4f72dd6c3..264b1e732d 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2057,7 +2057,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { .error_set_has_value => cg.airErrorSetHasValue(inst), .frame_addr => cg.airFrameAddress(inst), - .tlv_dllimport_ptr => cg.airTlvDllimportPtr(inst), + .runtime_nav_ptr => cg.airRuntimeNavPtr(inst), .assembly, .is_err_ptr, @@ -7616,7 +7616,7 @@ fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { return cg.finishAir(inst, .stack, &.{}); } -fn airTlvDllimportPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { +fn airRuntimeNavPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ty_nav = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const mod = cg.pt.zcu.navFileScope(cg.owner_nav).mod.?; if (mod.single_threaded) { diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index cd12bf72d5..396c0f7318 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -274,6 +274,12 @@ pub const MCValue = union(enum) { load_symbol: bits.SymbolOffset, /// The address of the memory location not-yet-allocated by the linker. lea_symbol: bits.SymbolOffset, + /// The value is in memory at an address not-yet-allocated by the linker. + /// This must use a non-got pc-relative relocation. + load_pcrel: bits.SymbolOffset, + /// The address of the memory location not-yet-allocated by the linker. + /// This must use a non-got pc-relative relocation. + lea_pcrel: bits.SymbolOffset, /// The value is in memory at a constant offset from the address in a register. indirect: bits.RegisterOffset, /// The value is in memory. @@ -314,6 +320,7 @@ pub const MCValue = union(enum) { .eflags, .register_overflow, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -327,6 +334,7 @@ pub const MCValue = union(enum) { .register_quadruple, .memory, .load_symbol, + .load_pcrel, .load_got, .load_direct, .indirect, @@ -429,6 +437,7 @@ pub const MCValue = union(enum) { .register_overflow, .register_mask, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -445,6 +454,7 @@ pub const MCValue = union(enum) { .load_got => |sym_index| .{ .lea_got = sym_index }, .load_frame => |frame_addr| .{ .lea_frame = frame_addr }, .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, + .load_pcrel => |sym_off| .{ .lea_pcrel = sym_off }, }; } @@ -466,6 +476,7 @@ pub const MCValue = union(enum) { .load_got, .load_frame, .load_symbol, + .load_pcrel, .elementwise_args, .reserved_frame, .air_ref, @@ -477,6 +488,7 @@ pub const MCValue = union(enum) { .lea_got => |sym_index| .{ .load_got = sym_index }, .lea_frame => |frame_addr| .{ .load_frame = frame_addr }, .lea_symbol => |sym_index| .{ .load_symbol = sym_index }, + .lea_pcrel => |sym_index| .{ .load_pcrel = sym_index }, }; } @@ -505,6 +517,8 @@ pub const MCValue = union(enum) { .load_frame, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, => switch (off) { 0 => mcv, else => unreachable, // not offsettable @@ -543,6 +557,7 @@ pub const MCValue = union(enum) { .elementwise_args, .reserved_frame, .lea_symbol, + .lea_pcrel, => unreachable, .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .{ .base = .{ .reg = .ds }, @@ -583,6 +598,18 @@ pub const MCValue = union(enum) { } }, }; }, + .load_pcrel => |sym_off| { + assert(sym_off.off == 0); + return .{ + .base = .{ .pcrel = sym_off.sym_index }, + .mod = .{ .rm = .{ + .size = mod_rm.size, + .index = mod_rm.index, + .scale = mod_rm.scale, + .disp = sym_off.off + mod_rm.disp, + } }, + }; + }, .air_ref => |ref| (try function.resolveInst(ref)).mem(function, mod_rm), }; } @@ -618,6 +645,8 @@ pub const MCValue = union(enum) { }), .load_symbol => |pl| try writer.print("[sym:{} + 0x{x}]", .{ pl.sym_index, pl.off }), .lea_symbol => |pl| try writer.print("sym:{} + 0x{x}", .{ pl.sym_index, pl.off }), + .load_pcrel => |pl| try writer.print("[sym@pcrel:{} + 0x{x}]", .{ pl.sym_index, pl.off }), + .lea_pcrel => |pl| try writer.print("sym@pcrel:{} + 0x{x}", .{ pl.sym_index, pl.off }), .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}), .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), @@ -655,6 +684,8 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, => result, .dead, .elementwise_args, @@ -755,6 +786,8 @@ const InstTracking = struct { .lea_frame, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, => assert(std.meta.eql(self.long, target.long)), .dead, .eflags, @@ -1228,12 +1261,7 @@ fn formatAir( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - @import("../../print_air.zig").dumpInst( - data.inst, - data.self.pt, - data.self.air, - data.self.liveness, - ); + data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness); } fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { return .{ .data = .{ .self = self, .inst = inst } }; @@ -163487,31 +163515,49 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { }; for (ops) |op| try op.die(cg); }, - .tlv_dllimport_ptr => switch (cg.bin_file.tag) { + .runtime_nav_ptr => switch (cg.bin_file.tag) { .elf, .macho => { const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; const nav = ip.getNav(ty_nav.nav); - const tlv_sym_index = sym: { + const sym_index, const relocation = sym: { if (cg.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; if (nav.getExtern(ip)) |e| { const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); - zo.symbol(sym).flags.is_extern_ptr = true; - break :sym sym; - } - break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); - } - if (cg.bin_file.cast(.macho)) |macho_file| { + linkage: switch (e.linkage) { + .internal => {}, + .strong => switch (e.visibility) { + .default => zo.symbol(sym).flags.is_extern_ptr = true, + .hidden, .protected => {}, + }, + .weak => { + zo.symbol(sym).flags.weak = true; + continue :linkage .strong; + }, + .link_once => unreachable, + } + break :sym .{ sym, e.relocation }; + } else break :sym .{ try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav), .any }; + } else if (cg.bin_file.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; if (nav.getExtern(ip)) |e| { const sym = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); - zo.symbols.items[sym].flags.is_extern_ptr = true; - break :sym sym; - } - break :sym try zo.getOrCreateMetadataForNav(macho_file, ty_nav.nav); - } - unreachable; + linkage: switch (e.linkage) { + .internal => {}, + .strong => switch (e.visibility) { + .default => zo.symbols.items[sym].flags.is_extern_ptr = true, + .hidden, .protected => {}, + }, + .weak => { + zo.symbols.items[sym].flags.weak = true; + continue :linkage .strong; + }, + .link_once => unreachable, + } + break :sym .{ sym, e.relocation }; + } else break :sym .{ try zo.getOrCreateMetadataForNav(macho_file, ty_nav.nav), .any }; + } else unreachable; }; if (cg.mod.pic) { @@ -163520,13 +163566,14 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { try cg.spillRegisters(&.{.rax}); } - var slot = try cg.tempInit(.usize, .{ .lea_symbol = .{ - .sym_index = tlv_sym_index, - } }); + var slot = try cg.tempInit(.usize, switch (relocation) { + .any => .{ .lea_symbol = .{ .sym_index = sym_index } }, + .pcrel => .{ .lea_pcrel = .{ .sym_index = sym_index } }, + }); while (try slot.toRegClass(true, .general_purpose, cg)) {} try slot.finish(inst, &.{}, &.{}, cg); }, - else => return cg.fail("TODO implement tlv/dllimport on {}", .{cg.bin_file.tag}), + else => return cg.fail("TODO implement runtime_nav_ptr on {}", .{cg.bin_file.tag}), }, .c_va_arg => try cg.airVaArg(inst), .c_va_copy => try cg.airVaCopy(inst), @@ -169189,6 +169236,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE .register, .register_offset, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -169196,6 +169244,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE .memory, .indirect, .load_symbol, + .load_pcrel, .load_direct, .load_got, .load_frame, @@ -169407,6 +169456,7 @@ fn store( .register, .register_offset, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -169414,6 +169464,7 @@ fn store( .memory, .indirect, .load_symbol, + .load_pcrel, .load_direct, .load_got, .load_frame, @@ -169883,6 +169934,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: .register_overflow, .register_mask, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -169892,7 +169944,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: => unreachable, // unmodifiable destination .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented - .memory, .load_symbol, .load_got, .load_direct => { + .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => { const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); @@ -171552,6 +171604,8 @@ fn genBinOp( .register_mask, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -172740,6 +172794,7 @@ fn genBinOpMir( .lea_got, .lea_frame, .lea_symbol, + .lea_pcrel, .elementwise_args, .reserved_frame, .air_ref, @@ -172831,6 +172886,8 @@ fn genBinOpMir( .indirect, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -172906,7 +172963,7 @@ fn genBinOpMir( } } }, - .memory, .indirect, .load_symbol, .load_got, .load_direct, .load_frame => { + .memory, .indirect, .load_symbol, .load_pcrel, .load_got, .load_direct, .load_frame => { const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; const limb_abi_size: u32 = @min(abi_size, 8); @@ -172953,8 +173010,9 @@ fn genBinOpMir( .load_frame, .lea_frame, .lea_symbol, + .lea_pcrel, => null, - .memory, .load_symbol, .load_got, .load_direct => src: { + .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => src: { switch (resolved_src_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr))) != null and std.math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null) @@ -173093,6 +173151,8 @@ fn genBinOpMir( .indirect, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -173160,6 +173220,7 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv .register_overflow, .register_mask, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -173222,6 +173283,8 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv .eflags, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -173281,7 +173344,7 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv } }, .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented - .memory, .indirect, .load_symbol, .load_direct, .load_got, .load_frame => { + .memory, .indirect, .load_symbol, .load_pcrel, .load_direct, .load_got, .load_frame => { const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); const tmp_mcv = MCValue{ .register = tmp_reg }; const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); @@ -173450,7 +173513,8 @@ fn genLocalDebugInfo( .disp = frame_addr.off, } }, }), - .lea_symbol => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{ + // debug info should explicitly ignore pcrel requirements + .lea_symbol, .lea_pcrel => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{ .base = .{ .reloc = sym_off.sym_index }, .mod = .{ .rm = .{ .size = .qword, @@ -174108,12 +174172,13 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .lea_got, .lea_frame, .lea_symbol, + .lea_pcrel, .elementwise_args, .reserved_frame, .air_ref, => unreachable, .register, .register_pair, .register_triple, .register_quadruple, .load_frame => null, - .memory, .load_symbol, .load_got, .load_direct => dst: { + .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => dst: { switch (resolved_dst_mcv) { .memory => |addr| if (std.math.cast( i32, @@ -174122,7 +174187,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :dst null, - .load_symbol, .load_got, .load_direct => {}, + .load_symbol, .load_pcrel, .load_got, .load_direct => {}, else => unreachable, } @@ -174160,6 +174225,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .register_mask, .indirect, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -174168,7 +174234,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v .air_ref, => unreachable, .register_pair, .register_triple, .register_quadruple, .load_frame => null, - .memory, .load_symbol, .load_got, .load_direct => src: { + .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => src: { switch (resolved_src_mcv) { .memory => |addr| if (std.math.cast( i32, @@ -174177,7 +174243,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v i32, @as(i64, @bitCast(addr)) + abi_size - 8, ) != null) break :src null, - .load_symbol, .load_got, .load_direct => {}, + .load_symbol, .load_pcrel, .load_got, .load_direct => {}, else => unreachable, } @@ -174568,6 +174634,7 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) .lea_direct, .lea_got, .lea_symbol, + .lea_pcrel, .elementwise_args, .reserved_frame, .air_ref, @@ -174616,6 +174683,7 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) .memory, .load_symbol, + .load_pcrel, .load_got, .load_direct, => { @@ -176625,6 +176693,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C .lea_got, .lea_frame, .lea_symbol, + .lea_pcrel, .elementwise_args, .reserved_frame, .air_ref, @@ -176719,7 +176788,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C } return; }, - .load_symbol, .load_direct, .load_got => { + .load_symbol, .load_pcrel, .load_direct, .load_got => { const src_addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); @@ -176752,7 +176821,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C .undef => if (opts.safety and part_i > 0) .{ .register = dst_regs[0] } else .undef, dst_tag => |src_regs| .{ .register = src_regs[part_i] }, .memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(), - .load_symbol, .load_direct, .load_got => .{ .indirect = .{ + .load_symbol, .load_pcrel, .load_direct, .load_got => .{ .indirect = .{ .reg = src_info.?.addr_reg, .off = part_disp, } }, @@ -176773,11 +176842,11 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C src_mcv, opts, ), - .memory, .load_symbol, .load_direct, .load_got => { + .memory, .load_symbol, .load_pcrel, .load_direct, .load_got => { switch (dst_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv, opts), - .load_symbol, .load_direct, .load_got => {}, + .load_symbol, .load_pcrel, .load_direct, .load_got => {}, else => unreachable, } @@ -177234,7 +177303,7 @@ fn genSetReg( if (src_reg_mask.info.inverted) try self.asmRegister(.{ ._, .not }, registerAlias(bits_reg, abi_size)); try self.genSetReg(dst_reg, ty, .{ .register = bits_reg }, .{}); }, - .memory, .load_symbol, .load_direct, .load_got => { + .memory, .load_symbol, .load_pcrel, .load_direct, .load_got => { switch (src_mcv) { .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| return (try self.moveStrategy( @@ -177263,6 +177332,21 @@ fn genSetReg( .segment, .mmx, .ip, .cr, .dr => unreachable, .x87, .sse => {}, }, + .load_pcrel => |sym_off| switch (dst_reg.class()) { + .general_purpose, .gphi => { + assert(sym_off.off == 0); + try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ + .base = .{ .pcrel = sym_off.sym_index }, + .mod = .{ .rm = .{ + .size = self.memSize(ty), + .disp = sym_off.off, + } }, + }); + return; + }, + .segment, .mmx, .ip, .cr, .dr => unreachable, + .x87, .sse => {}, + }, .load_direct => |sym_index| switch (dst_reg.class()) { .general_purpose, .gphi => { _ = try self.addInst(.{ @@ -177313,6 +177397,28 @@ fn genSetReg( @tagName(self.bin_file.tag), }), }, + .lea_pcrel => |sym_off| switch (self.bin_file.tag) { + .elf, .macho => { + try self.asmRegisterMemory( + .{ ._, .lea }, + dst_reg.to64(), + .{ + .base = .{ .pcrel = sym_off.sym_index }, + }, + ); + if (sym_off.off != 0) try self.asmRegisterMemory( + .{ ._, .lea }, + dst_reg.to64(), + .{ + .base = .{ .reg = dst_reg.to64() }, + .mod = .{ .rm = .{ .disp = sym_off.off } }, + }, + ); + }, + else => return self.fail("TODO emit symbol sequence on {s}", .{ + @tagName(self.bin_file.tag), + }), + }, .lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{ .tag = switch (src_mcv) { .lea_direct => .lea, @@ -177350,6 +177456,7 @@ fn genSetMem( .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, .table, .rip_inst => unreachable, .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } }, + .pcrel => |sym_index| .{ .lea_pcrel = .{ .sym_index = sym_index, .off = disp } }, }; switch (src_mcv) { .none, @@ -177466,7 +177573,7 @@ fn genSetMem( .off = disp, }).compare(.gte, src_align), .table, .rip_inst => unreachable, - .reloc => false, + .reloc, .pcrel => false, })).write( self, .{ .base = base, .mod = .{ .rm = .{ @@ -177557,6 +177664,8 @@ fn genSetMem( .lea_frame, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, => switch (abi_size) { 0 => {}, 1, 2, 4, 8 => { @@ -178110,7 +178219,7 @@ fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void { .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), } const ptr_lock = switch (ptr_mem.base) { - .none, .frame, .reloc => null, + .none, .frame, .reloc, .pcrel => null, .reg => |reg| self.register_manager.lockReg(reg), .table, .rip_inst => unreachable, }; @@ -178193,7 +178302,7 @@ fn atomicOp( .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), } const mem_lock = switch (ptr_mem.base) { - .none, .frame, .reloc => null, + .none, .frame, .reloc, .pcrel => null, .reg => |reg| self.register_manager.lockReg(reg), .table, .rip_inst => unreachable, }; @@ -182266,6 +182375,8 @@ const Temp = struct { .memory, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .indirect, .load_direct, .lea_direct, @@ -182427,6 +182538,22 @@ const Temp = struct { assert(limb_index == 0); new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = sym_off }); }, + .load_pcrel => |sym_off| { + const new_reg = + try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); + new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); + try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ + .base = .{ .pcrel = sym_off.sym_index }, + .mod = .{ .rm = .{ + .size = .qword, + .disp = sym_off.off + @as(u31, limb_index) * 8, + } }, + }); + }, + .lea_pcrel => |sym_off| { + assert(limb_index == 0); + new_temp_index.tracking(cg).* = .init(.{ .lea_pcrel = sym_off }); + }, .load_frame => |frame_addr| { const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); @@ -182721,11 +182848,12 @@ const Temp = struct { .memory, .indirect, .load_symbol, + .load_pcrel, .load_direct, .load_got, .load_frame, => return temp.toRegClass(true, .general_purpose, cg), - .lea_symbol => |sym_off| { + .lea_symbol, .lea_pcrel => |sym_off| { const off = sym_off.off; // hack around linker relocation bugs if (false and off == 0) return false; @@ -187464,6 +187592,8 @@ const Temp = struct { .memory, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .indirect, .load_direct, .lea_direct, @@ -190044,6 +190174,7 @@ const Select = struct { .register => |base_reg| .{ .reg = base_reg.toSize(.ptr, s.cg.target) }, .register_offset => |base_reg_off| .{ .reg = base_reg_off.reg.toSize(.ptr, s.cg.target) }, .lea_symbol => |base_sym_off| .{ .reloc = base_sym_off.sym_index }, + .lea_pcrel => |base_sym_off| .{ .pcrel = base_sym_off.sym_index }, }, .mod = .{ .rm = .{ .size = op.flags.base.size, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 92114095bf..d4116974cf 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -189,12 +189,12 @@ pub fn emitMir(emit: *Emit) Error!void { .r_addend = lowered_relocs[0].off, }, zo); }, - .linker_reloc => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { + .linker_reloc, .linker_pcrel => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const sym = zo.symbol(sym_index); if (emit.lower.pic) { - const r_type: u32 = if (sym.flags.is_extern_ptr) + const r_type: u32 = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel) @intFromEnum(std.elf.R_X86_64.GOTPCREL) else @intFromEnum(std.elf.R_X86_64.PC32); @@ -218,7 +218,7 @@ pub fn emitMir(emit: *Emit) Error!void { const zo = macho_file.getZigObject().?; const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; const sym = &zo.symbols.items[sym_index]; - const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr) + const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel) .got_load else if (sym.flags.tlv) .tlv @@ -438,6 +438,7 @@ pub fn emitMir(emit: *Emit) Error!void { .reg => |reg| .{ .breg = reg.dwarfNum() }, .frame, .table, .rip_inst => unreachable, .reloc => |sym_index| .{ .addr_reloc = sym_index }, + .pcrel => unreachable, }; break :base &loc_buf[0]; }, diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 7a69d58abc..838f155d10 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -66,6 +66,7 @@ pub const Reloc = struct { inst: Mir.Inst.Index, table, linker_reloc: u32, + linker_pcrel: u32, linker_tlsld: u32, linker_dtpoff: u32, linker_extern_fn: u32, @@ -421,9 +422,9 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) for (emit_ops, ops, 0..) |*emit_op, op, op_index| { emit_op.* = switch (op) { else => op, - .mem => |mem_op| switch (mem_op.base()) { + .mem => |mem_op| op: switch (mem_op.base()) { else => op, - .reloc => |sym_index| op: { + .reloc => |sym_index| { assert(prefix == .none); assert(mem_op.sib.disp == 0); assert(mem_op.sib.scale_index.scale == 0); @@ -559,6 +560,22 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) return lower.fail("TODO: bin format '{s}'", .{@tagName(lower.bin_file.tag)}); } }, + .pcrel => |sym_index| { + assert(prefix == .none); + assert(mem_op.sib.disp == 0); + assert(mem_op.sib.scale_index.scale == 0); + + _ = lower.reloc(@intCast(op_index), .{ .linker_pcrel = sym_index }, 0); + break :op switch (lower.bin_file.tag) { + .elf => op, + .macho => switch (mnemonic) { + .lea => .{ .mem = Memory.initRip(.none, 0) }, + .mov => .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) }, + else => unreachable, + }, + else => |tag| return lower.fail("TODO: bin format '{s}'", .{@tagName(tag)}), + }; + }, }, }; } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 83dbc855ec..8d202e6bae 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -1866,7 +1866,7 @@ pub const Memory = struct { .none, .table => undefined, .reg => |reg| @intFromEnum(reg), .frame => |frame_index| @intFromEnum(frame_index), - .reloc => |sym_index| sym_index, + .reloc, .pcrel => |sym_index| sym_index, .rip_inst => |inst_index| inst_index, }, .off = switch (mem.mod) { @@ -1895,6 +1895,7 @@ pub const Memory = struct { .frame => .{ .frame = @enumFromInt(mem.base) }, .table => .table, .reloc => .{ .reloc = mem.base }, + .pcrel => .{ .pcrel = mem.base }, .rip_inst => .{ .rip_inst = mem.base }, }, .scale_index = switch (mem.info.index) { @@ -1959,7 +1960,7 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory { return switch (mem.info.base) { - .none, .reg, .table, .reloc, .rip_inst => mem, + .none, .reg, .table, .reloc, .pcrel, .rip_inst => mem, .frame => if (mir.frame_locs.len > 0) .{ .info = .{ .base = .reg, diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 062a57981c..63b5d4b238 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -762,6 +762,7 @@ pub const Memory = struct { frame: FrameIndex, table, reloc: u32, + pcrel: u32, rip_inst: Mir.Inst.Index, pub const Tag = @typeInfo(Base).@"union".tag_type.?; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 9cacd955d7..cb1272fba0 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -138,7 +138,7 @@ pub const Instruction = struct { .moffs => true, .rip => false, .sib => |s| switch (s.base) { - .none, .frame, .table, .reloc, .rip_inst => false, + .none, .frame, .table, .reloc, .pcrel, .rip_inst => false, .reg => |reg| reg.isClass(.segment), }, }; @@ -211,7 +211,7 @@ pub const Instruction = struct { .none, .imm => 0b00, .reg => |reg| @truncate(reg.enc() >> 3), .mem => |mem| switch (mem.base()) { - .none, .frame, .table, .reloc, .rip_inst => 0b00, // rsp, rbp, and rip are not extended + .none, .frame, .table, .reloc, .pcrel, .rip_inst => 0b00, // rsp, rbp, and rip are not extended .reg => |reg| @truncate(reg.enc() >> 3), }, .bytes => unreachable, @@ -282,6 +282,7 @@ pub const Instruction = struct { .frame => |frame_index| try writer.print("{}", .{frame_index}), .table => try writer.print("Table", .{}), .reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}), + .pcrel => |sym_index| try writer.print("PcRelSymbol({d})", .{sym_index}), .rip_inst => |inst_index| try writer.print("RipInst({d})", .{inst_index}), } if (mem.scaleIndex()) |si| { @@ -721,7 +722,7 @@ pub const Instruction = struct { try encoder.modRm_indirectDisp32(operand_enc, 0); try encoder.disp32(undefined); } else return error.CannotEncode, - .rip_inst => { + .pcrel, .rip_inst => { try encoder.modRm_RIPDisp32(operand_enc); try encoder.disp32(sib.disp); }, diff --git a/src/codegen.zig b/src/codegen.zig index 1f794bbeea..a2de3e2d01 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -921,41 +921,74 @@ fn genNavRef( const nav = ip.getNav(nav_index); assert(!nav.isThreadlocal(ip)); - const is_extern, const lib_name = if (nav.getExtern(ip)) |e| - .{ true, e.lib_name } + const lib_name, const linkage, const visibility = if (nav.getExtern(ip)) |e| + .{ e.lib_name, e.linkage, e.visibility } else - .{ false, .none }; + .{ .none, .internal, .default }; const name = nav.name; if (lf.cast(.elf)) |elf_file| { const zo = elf_file.zigObjectPtr().?; - if (is_extern) { - const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); - zo.symbol(sym_index).flags.is_extern_ptr = true; - return .{ .mcv = .{ .lea_symbol = sym_index } }; + switch (linkage) { + .internal => { + const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); + return .{ .mcv = .{ .lea_symbol = sym_index } }; + }, + .strong, .weak => { + const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); + switch (linkage) { + .internal => unreachable, + .strong => {}, + .weak => zo.symbol(sym_index).flags.weak = true, + .link_once => unreachable, + } + switch (visibility) { + .default => zo.symbol(sym_index).flags.is_extern_ptr = true, + .hidden, .protected => {}, + } + return .{ .mcv = .{ .lea_symbol = sym_index } }; + }, + .link_once => unreachable, } - const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); - return .{ .mcv = .{ .lea_symbol = sym_index } }; } else if (lf.cast(.macho)) |macho_file| { const zo = macho_file.getZigObject().?; - if (is_extern) { - const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); - zo.symbols.items[sym_index].flags.is_extern_ptr = true; - return .{ .mcv = .{ .lea_symbol = sym_index } }; + switch (linkage) { + .internal => { + const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); + const sym = zo.symbols.items[sym_index]; + return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } }; + }, + .strong, .weak => { + const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); + switch (linkage) { + .internal => unreachable, + .strong => {}, + .weak => zo.symbols.items[sym_index].flags.weak = true, + .link_once => unreachable, + } + switch (visibility) { + .default => zo.symbols.items[sym_index].flags.is_extern_ptr = true, + .hidden, .protected => {}, + } + return .{ .mcv = .{ .lea_symbol = sym_index } }; + }, + .link_once => unreachable, } - const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); - const sym = zo.symbols.items[sym_index]; - return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } }; } else if (lf.cast(.coff)) |coff_file| { - if (is_extern) { - // TODO audit this - const global_index = try coff_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); - try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT - return .{ .mcv = .{ .load_got = link.File.Coff.global_symbol_bit | global_index } }; + // TODO audit this + switch (linkage) { + .internal => { + const atom_index = try coff_file.getOrCreateAtomForNav(nav_index); + const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; + return .{ .mcv = .{ .load_got = sym_index } }; + }, + .strong, .weak => { + const global_index = try coff_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); + try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT + return .{ .mcv = .{ .load_got = link.File.Coff.global_symbol_bit | global_index } }; + }, + .link_once => unreachable, } - const atom_index = try coff_file.getOrCreateAtomForNav(nav_index); - const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; - return .{ .mcv = .{ .load_got = sym_index } }; } else if (lf.cast(.plan9)) |p9| { const atom_index = try p9.seeNav(pt, nav_index); const atom = p9.getAtom(atom_index); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c68abc06ce..3b8ab52982 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2255,19 +2255,30 @@ pub const DeclGen = struct { fn renderFwdDecl( dg: *DeclGen, nav_index: InternPool.Nav.Index, - flags: struct { - is_extern: bool, + flags: packed struct { is_const: bool, is_threadlocal: bool, - is_weak_linkage: bool, + linkage: std.builtin.GlobalLinkage, + visibility: std.builtin.SymbolVisibility, }, ) !void { const zcu = dg.pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); const fwd = dg.fwdDeclWriter(); - try fwd.writeAll(if (flags.is_extern) "zig_extern " else "static "); - if (flags.is_weak_linkage) try fwd.writeAll("zig_weak_linkage "); + try fwd.writeAll(switch (flags.linkage) { + .internal => "static ", + .strong, .weak, .link_once => "zig_extern ", + }); + switch (flags.linkage) { + .internal, .strong => {}, + .weak => try fwd.writeAll("zig_weak_linkage "), + .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}), + } + switch (flags.linkage) { + .internal => {}, + .strong, .weak, .link_once => try fwd.print("zig_visibility({s}) ", .{@tagName(flags.visibility)}), + } if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal "); try dg.renderTypeAndName( fwd, @@ -2994,10 +3005,10 @@ pub fn genDecl(o: *Object) !void { switch (ip.indexToKey(nav.status.fully_resolved.val)) { .@"extern" => |@"extern"| { if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{ - .is_extern = true, .is_const = @"extern".is_const, .is_threadlocal = @"extern".is_threadlocal, - .is_weak_linkage = @"extern".is_weak_linkage, + .linkage = @"extern".linkage, + .visibility = @"extern".visibility, }); const fwd = o.dg.fwdDeclWriter(); @@ -3016,13 +3027,12 @@ pub fn genDecl(o: *Object) !void { }, .variable => |variable| { try o.dg.renderFwdDecl(o.dg.pass.nav, .{ - .is_extern = false, .is_const = false, .is_threadlocal = variable.is_threadlocal, - .is_weak_linkage = variable.is_weak_linkage, + .linkage = .internal, + .visibility = .default, }); const w = o.writer(); - if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage "); if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s| try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); @@ -3467,7 +3477,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), - .tlv_dllimport_ptr => try airTlvDllimportPtr(f, inst), + .runtime_nav_ptr => try airRuntimeNavPtr(f, inst), .c_va_start => try airCVaStart(f, inst), .c_va_arg => try airCVaArg(f, inst), @@ -7672,7 +7682,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { return local; } -fn airTlvDllimportPtr(f: *Function, inst: Air.Inst.Index) !CValue { +fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const writer = f.object.writer(); const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7790840d20..3fc6250d3f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2979,36 +2979,49 @@ pub const Object = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; const nav = ip.getNav(nav_index); - const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) { + const linkage: std.builtin.GlobalLinkage, const visibility: Builder.Visibility, const is_threadlocal, const is_dll_import = switch (nav.status) { .unresolved => unreachable, .fully_resolved => |r| switch (ip.indexToKey(r.val)) { - .variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false }, - .@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import }, - else => .{ false, false, false, false }, + .variable => |variable| .{ .internal, .default, variable.is_threadlocal, false }, + .@"extern" => |@"extern"| .{ @"extern".linkage, .fromSymbolVisibility(@"extern".visibility), @"extern".is_threadlocal, @"extern".is_dll_import }, + else => .{ .internal, .default, false, false }, }, // This means it's a source declaration which is not `extern`! - .type_resolved => |r| .{ false, r.is_threadlocal, false, false }, + .type_resolved => |r| .{ .internal, .default, r.is_threadlocal, false }, }; const variable_index = try o.builder.addVariable( - try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)), + try o.builder.strtabString(switch (linkage) { + .internal => nav.fqn, + .strong, .weak => nav.name, + .link_once => unreachable, + }.toSlice(ip)), try o.lowerType(Type.fromInterned(nav.typeOf(ip))), toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()), ); gop.value_ptr.* = variable_index.ptrConst(&o.builder).global; // This is needed for declarations created by `@extern`. - if (is_extern) { - variable_index.setLinkage(.external, &o.builder); - variable_index.setUnnamedAddr(.default, &o.builder); - if (is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded) - variable_index.setThreadLocal(.generaldynamic, &o.builder); - if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder); - if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder); - } else { - variable_index.setLinkage(.internal, &o.builder); - variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); - } + switch (linkage) { + .internal => { + variable_index.setLinkage(.internal, &o.builder); + variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); + }, + .strong, .weak => { + variable_index.setLinkage(switch (linkage) { + .internal => unreachable, + .strong => .external, + .weak => .extern_weak, + .link_once => unreachable, + }, &o.builder); + variable_index.setUnnamedAddr(.default, &o.builder); + if (is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded) + variable_index.setThreadLocal(.generaldynamic, &o.builder); + if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder); + }, + .link_once => unreachable, + } + variable_index.setVisibility(visibility, &o.builder); return variable_index; } @@ -4530,14 +4543,14 @@ pub const NavGen = struct { const nav = ip.getNav(nav_index); const resolved = nav.status.fully_resolved; - const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) { - .variable => |variable| .{ false, .none, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav }, - .@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav }, - else => .{ false, .none, false, false, false, true, resolved.val, nav_index }, + const lib_name, const linkage, const visibility: Builder.Visibility, const is_threadlocal, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) { + .variable => |variable| .{ .none, .internal, .default, variable.is_threadlocal, false, false, variable.init, variable.owner_nav }, + .@"extern" => |@"extern"| .{ @"extern".lib_name, @"extern".linkage, .fromSymbolVisibility(@"extern".visibility), @"extern".is_threadlocal, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav }, + else => .{ .none, .internal, .default, false, false, true, resolved.val, nav_index }, }; const ty = Type.fromInterned(nav.typeOf(ip)); - if (is_extern and ip.isFunctionType(ty.toIntern())) { + if (linkage != .internal and ip.isFunctionType(ty.toIntern())) { _ = try o.resolveLlvmFunction(owner_nav); } else { const variable_index = try o.resolveGlobalNav(nav_index); @@ -4549,6 +4562,7 @@ pub const NavGen = struct { .none => .no_init, else => try o.lowerValue(init_val), }, &o.builder); + variable_index.setVisibility(visibility, &o.builder); const file_scope = zcu.navFileScopeIndex(nav_index); const mod = zcu.fileByIndex(file_scope).mod.?; @@ -4568,7 +4582,7 @@ pub const NavGen = struct { line_number, try o.lowerDebugType(ty), variable_index, - .{ .local = !is_extern }, + .{ .local = linkage == .internal }, ); const debug_expression = try o.builder.debugExpression(&.{}); @@ -4583,38 +4597,47 @@ pub const NavGen = struct { } } - if (is_extern) { - const global_index = o.nav_map.get(nav_index).?; + switch (linkage) { + .internal => {}, + .strong, .weak => { + const global_index = o.nav_map.get(nav_index).?; - const decl_name = decl_name: { - if (zcu.getTarget().cpu.arch.isWasm() and ty.zigTypeTag(zcu) == .@"fn") { - if (lib_name.toSlice(ip)) |lib_name_slice| { - if (!std.mem.eql(u8, lib_name_slice, "c")) { - break :decl_name try o.builder.strtabStringFmt("{}|{s}", .{ nav.name.fmt(ip), lib_name_slice }); + const decl_name = decl_name: { + if (zcu.getTarget().cpu.arch.isWasm() and ty.zigTypeTag(zcu) == .@"fn") { + if (lib_name.toSlice(ip)) |lib_name_slice| { + if (!std.mem.eql(u8, lib_name_slice, "c")) { + break :decl_name try o.builder.strtabStringFmt("{}|{s}", .{ nav.name.fmt(ip), lib_name_slice }); + } } } - } - break :decl_name try o.builder.strtabString(nav.name.toSlice(ip)); - }; + break :decl_name try o.builder.strtabString(nav.name.toSlice(ip)); + }; - if (o.builder.getGlobal(decl_name)) |other_global| { - if (other_global != global_index) { - // Another global already has this name; just use it in place of this global. - try global_index.replace(other_global, &o.builder); - return; + if (o.builder.getGlobal(decl_name)) |other_global| { + if (other_global != global_index) { + // Another global already has this name; just use it in place of this global. + try global_index.replace(other_global, &o.builder); + return; + } } - } - try global_index.rename(decl_name, &o.builder); - global_index.setLinkage(.external, &o.builder); - global_index.setUnnamedAddr(.default, &o.builder); - if (is_dll_import) { - global_index.setDllStorageClass(.dllimport, &o.builder); - } else if (zcu.comp.config.dll_export_fns) { - global_index.setDllStorageClass(.default, &o.builder); - } + try global_index.rename(decl_name, &o.builder); + global_index.setUnnamedAddr(.default, &o.builder); + if (is_dll_import) { + global_index.setDllStorageClass(.dllimport, &o.builder); + } else if (zcu.comp.config.dll_export_fns) { + global_index.setDllStorageClass(.default, &o.builder); + } - if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder); + global_index.setLinkage(switch (linkage) { + .internal => unreachable, + .strong => .external, + .weak => .extern_weak, + .link_once => unreachable, + }, &o.builder); + global_index.setVisibility(visibility, &o.builder); + }, + .link_once => unreachable, } } }; @@ -5023,7 +5046,7 @@ pub const FuncGen = struct { .vector_store_elem => try self.airVectorStoreElem(inst), - .tlv_dllimport_ptr => try self.airTlvDllimportPtr(inst), + .runtime_nav_ptr => try self.airRuntimeNavPtr(inst), .inferred_alloc, .inferred_alloc_comptime => unreachable, @@ -8122,7 +8145,7 @@ pub const FuncGen = struct { return .none; } - fn airTlvDllimportPtr(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + fn airRuntimeNavPtr(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = fg.ng.object; const ty_nav = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const llvm_ptr_const = try o.lowerNavRefValue(ty_nav.nav); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index d65646744f..70405d8b5e 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -785,7 +785,6 @@ const Entry = struct { } const Index = enum(u32) { - //got_proc, _, const Optional = enum(u32) { @@ -1041,7 +1040,7 @@ const Entry = struct { const symbol = zo.symbol(reloc.target_sym); try dwarf.resolveReloc( entry_off + reloc.source_off, - @bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(@intFromEnum(reloc.target_off))) - + @bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(reloc.target_off)) - if (symbol.flags.is_tls) elf_file.dtpAddress() else 0), @intFromEnum(dwarf.address_size), ); @@ -1052,7 +1051,7 @@ const Entry = struct { const ref = zo.getSymbolRef(reloc.target_sym, macho_file); try dwarf.resolveReloc( entry_off + reloc.source_off, - ref.getSymbol(macho_file).?.getAddress(.{}, macho_file) + @as(i64, @intCast(@intFromEnum(reloc.target_off))), + ref.getSymbol(macho_file).?.getAddress(.{}, macho_file) + @as(i64, @intCast(reloc.target_off)), @intFromEnum(dwarf.address_size), ); } @@ -1081,27 +1080,12 @@ const CrossSectionReloc = struct { const ExternalReloc = struct { source_off: u32 = 0, target_sym: u32, - target_off: enum(u64) { - none = 0, - got = std.math.maxInt(i64) + 1, - _, - - pub fn rel(off: u64) @This() { - const res: @This() = @enumFromInt(off); - switch (res) { - .none => {}, - _ => {}, - .got => unreachable, // assertion failure - } - return res; - } - } = .none, + target_off: u64 = 0, }; pub const Loc = union(enum) { empty, addr_reloc: u32, - got_reloc: u32, deref: *const Loc, constu: u64, consts: i64, @@ -1166,10 +1150,6 @@ pub const Loc = union(enum) { try addr.write(adapter); try writer.writeByte(DW.OP.deref); }, - .got_reloc => |sym_index| { - try writer.writeByte(DW.OP.const4s); - try adapter.gotSym(sym_index); - }, .constu => |constu| if (std.math.cast(u5, constu)) |lit| { try writer.writeByte(@as(u8, DW.OP.lit0) + lit); } else if (std.math.cast(u8, constu)) |const1u| { @@ -1766,9 +1746,6 @@ pub const WipNav = struct { fn endian(_: ExprLocCounter) std.builtin.Endian { return @import("builtin").cpu.arch.endian(); } - fn gotSym(counter: *ExprLocCounter, _: u32) error{}!void { - counter.stream.bytes_written += 4; - } fn addrSym(counter: *ExprLocCounter, _: u32) error{}!void { counter.stream.bytes_written += @intFromEnum(counter.address_size); } @@ -1789,14 +1766,6 @@ pub const WipNav = struct { fn endian(ctx: @This()) std.builtin.Endian { return ctx.wip_nav.dwarf.endian; } - fn gotSym(ctx: @This(), sym_index: u32) UpdateError!void { - try ctx.wip_nav.infoExternalReloc(.{ - .source_off = @intCast(ctx.wip_nav.debug_info.items.len), - .target_sym = sym_index, - .target_off = .got, - }); - try ctx.wip_nav.debug_info.appendNTimes(ctx.wip_nav.dwarf.gpa, 0, 4); - } fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void { try ctx.wip_nav.infoAddrSym(sym_index, 0); } @@ -1812,7 +1781,7 @@ pub const WipNav = struct { try wip_nav.infoExternalReloc(.{ .source_off = @intCast(wip_nav.debug_info.items.len), .target_sym = sym_index, - .target_off = .rel(sym_off), + .target_off = sym_off, }); try wip_nav.debug_info.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size)); } @@ -1829,14 +1798,6 @@ pub const WipNav = struct { fn endian(ctx: @This()) std.builtin.Endian { return ctx.wip_nav.dwarf.endian; } - fn gotSym(ctx: @This(), sym_index: u32) UpdateError!void { - try ctx.wip_nav.frameExternalReloc(.{ - .source_off = @intCast(ctx.wip_nav.debug_frame.items.len), - .target_sym = sym_index, - .target_off = .got, - }); - try ctx.wip_nav.debug_frame.appendNTimes(ctx.wip_nav.dwarf.gpa, 0, 4); - } fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void { try ctx.wip_nav.frameAddrSym(sym_index, 0); } @@ -1852,7 +1813,7 @@ pub const WipNav = struct { try wip_nav.frameExternalReloc(.{ .source_off = @intCast(wip_nav.debug_frame.items.len), .target_sym = sym_index, - .target_off = .rel(sym_off), + .target_off = sym_off, }); try wip_nav.debug_frame.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size)); } @@ -2338,81 +2299,50 @@ fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index { const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); const unit: Unit.Index = @enumFromInt(mod_gop.index); if (!mod_gop.found_existing) { - { - errdefer _ = dwarf.mods.pop(); - mod_gop.value_ptr.* = .{ - .root_dir_path = undefined, - .dirs = .empty, - .files = .empty, - }; - errdefer mod_gop.value_ptr.dirs.deinit(dwarf.gpa); - try mod_gop.value_ptr.dirs.putNoClobber(dwarf.gpa, unit, {}); - assert(try dwarf.debug_aranges.section.addUnit( - DebugAranges.headerBytes(dwarf), - DebugAranges.trailerBytes(dwarf), - dwarf, - ) == unit); - errdefer dwarf.debug_aranges.section.popUnit(dwarf.gpa); - assert(try dwarf.debug_frame.section.addUnit( - DebugFrame.headerBytes(dwarf), - DebugFrame.trailerBytes(dwarf), - dwarf, - ) == unit); - errdefer dwarf.debug_frame.section.popUnit(dwarf.gpa); - assert(try dwarf.debug_info.section.addUnit( - DebugInfo.headerBytes(dwarf), - DebugInfo.trailer_bytes, - dwarf, - ) == unit); - errdefer dwarf.debug_info.section.popUnit(dwarf.gpa); - assert(try dwarf.debug_line.section.addUnit( - DebugLine.headerBytes(dwarf, 5, 25), - DebugLine.trailer_bytes, - dwarf, - ) == unit); - errdefer dwarf.debug_line.section.popUnit(dwarf.gpa); - assert(try dwarf.debug_loclists.section.addUnit( - DebugLocLists.headerBytes(dwarf), - DebugLocLists.trailer_bytes, - dwarf, - ) == unit); - errdefer dwarf.debug_loclists.section.popUnit(dwarf.gpa); - assert(try dwarf.debug_rnglists.section.addUnit( - DebugRngLists.headerBytes(dwarf), - DebugRngLists.trailer_bytes, - dwarf, - ) == unit); - errdefer dwarf.debug_rnglists.section.popUnit(dwarf.gpa); - } - //if (dwarf.bin_file.cast(.elf)) |elf_file| { - // if (unit == .main) assert(try dwarf.addCommonEntry(unit) == .got_proc); - // if (mod.pic and dwarf.debug_info.section.getUnit(.main).getEntry(.got_proc).len == 0) { - // var wip_nav: WipNav = .{ - // .dwarf = dwarf, - // .pt = undefined, - // .unit = .main, - // .entry = .got_proc, - // .any_children = false, - // .func = .none, - // .func_sym_index = undefined, - // .func_high_pc = undefined, - // .blocks = undefined, - // .cfi = undefined, - // .debug_frame = .empty, - // .debug_info = .empty, - // .debug_line = .empty, - // .debug_loclists = .empty, - // .pending_lazy = .empty, - // }; - // defer wip_nav.deinit(); - // try wip_nav.abbrevCode(.proc); - // try wip_nav.infoExprLoc(.{ .deref = &.{ .plus = .{ - // &.empty, - // &.{ .addr_reloc = try elf_file.zigObjectPtr().?.getGlobalSymbol(elf_file, "_GLOBAL_OFFSET_TABLE_", null) }, - // } } }); - // try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items); - // } - //} + errdefer _ = dwarf.mods.pop(); + mod_gop.value_ptr.* = .{ + .root_dir_path = undefined, + .dirs = .empty, + .files = .empty, + }; + errdefer mod_gop.value_ptr.dirs.deinit(dwarf.gpa); + try mod_gop.value_ptr.dirs.putNoClobber(dwarf.gpa, unit, {}); + assert(try dwarf.debug_aranges.section.addUnit( + DebugAranges.headerBytes(dwarf), + DebugAranges.trailerBytes(dwarf), + dwarf, + ) == unit); + errdefer dwarf.debug_aranges.section.popUnit(dwarf.gpa); + assert(try dwarf.debug_frame.section.addUnit( + DebugFrame.headerBytes(dwarf), + DebugFrame.trailerBytes(dwarf), + dwarf, + ) == unit); + errdefer dwarf.debug_frame.section.popUnit(dwarf.gpa); + assert(try dwarf.debug_info.section.addUnit( + DebugInfo.headerBytes(dwarf), + DebugInfo.trailer_bytes, + dwarf, + ) == unit); + errdefer dwarf.debug_info.section.popUnit(dwarf.gpa); + assert(try dwarf.debug_line.section.addUnit( + DebugLine.headerBytes(dwarf, 5, 25), + DebugLine.trailer_bytes, + dwarf, + ) == unit); + errdefer dwarf.debug_line.section.popUnit(dwarf.gpa); + assert(try dwarf.debug_loclists.section.addUnit( + DebugLocLists.headerBytes(dwarf), + DebugLocLists.trailer_bytes, + dwarf, + ) == unit); + errdefer dwarf.debug_loclists.section.popUnit(dwarf.gpa); + assert(try dwarf.debug_rnglists.section.addUnit( + DebugRngLists.headerBytes(dwarf), + DebugRngLists.trailer_bytes, + dwarf, + ) == unit); + errdefer dwarf.debug_rnglists.section.popUnit(dwarf.gpa); } return unit; } @@ -2510,16 +2440,7 @@ fn initWipNavInner( try wip_nav.strp(nav.fqn.toSlice(ip)); const ty: Type = nav_val.typeOf(zcu); const addr: Loc = .{ .addr_reloc = sym_index }; - const loc: Loc = loc: { - if (dwarf.bin_file.cast(.elf)) |elf_file| if (decl.linkage == .@"extern" and mod.pic) - // TODO: lldb doesn't support call :( - //.{ .call = .{ .args = &.{.{ .got_reloc = sym_index }}, .unit = .main, .entry = .got_proc } } - break :loc .{ .deref = &.{ .plus = .{ - &.{ .addr_reloc = try elf_file.zigObjectPtr().?.getGlobalSymbol(elf_file, "_GLOBAL_OFFSET_TABLE_", null) }, - &.{ .got_reloc = sym_index }, - } } }; - break :loc if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr; - }; + const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr; switch (decl.kind) { .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable, .@"const" => { @@ -2605,7 +2526,7 @@ fn initWipNavInner( try wip_nav.infoAddrSym(sym_index, 0); wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len); try diw.writeInt(u32, 0, dwarf.endian); - const target = file.mod.?.resolved_target.result; + const target = mod.resolved_target.result; try uleb128(diw, switch (nav.status.fully_resolved.alignment) { .none => target_info.defaultFunctionAlignment(target), else => |a| a.maxStrict(target_info.minFunctionAlignment(target)), @@ -2742,7 +2663,7 @@ pub fn finishWipNavFunc( .{ .source_off = 1 + @intFromEnum(dwarf.address_size), .target_sym = wip_nav.func_sym_index, - .target_off = .rel(code_size), + .target_off = code_size, }, }); try dwarf.debug_rnglists.section.replaceEntry( @@ -4981,7 +4902,6 @@ const AbbrevCode = enum { comptime_value_field_comptime_state, comptime_value_elem_runtime_bits, comptime_value_elem_comptime_state, - //proc, const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_instance_func_generic)); comptime { @@ -5851,12 +5771,6 @@ const AbbrevCode = enum { .{ .ZIG_comptime_value, .ref_addr }, }, }, - //.proc = .{ - // .tag = .dwarf_procedure, - // .attrs = &.{ - // .{ .location, .exprloc }, - // }, - //}, .null = undefined, }); }; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d947f1219b..1516993c74 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -959,6 +959,12 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { self.rela_plt.clearRetainingCapacity(); if (self.zigObjectPtr()) |zo| { + var undefs: std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)) = .init(gpa); + defer { + for (undefs.values()) |*refs| refs.deinit(); + undefs.deinit(); + } + var has_reloc_errors = false; for (zo.atoms_indexes.items) |atom_index| { const atom_ptr = zo.atom(atom_index) orelse continue; @@ -969,7 +975,10 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { const code = try zo.codeAlloc(self, atom_index); defer gpa.free(code); const file_offset = atom_ptr.offset(self); - atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) { + (if (shdr.sh_flags & elf.SHF_ALLOC == 0) + atom_ptr.resolveRelocsNonAlloc(self, code, &undefs) + else + atom_ptr.resolveRelocsAlloc(self, code)) catch |err| switch (err) { error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, error.UnsupportedCpuArch => { try self.reportUnsupportedCpuArch(); @@ -980,6 +989,8 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void { try self.pwriteAll(code, file_offset); } + try self.reportUndefinedSymbols(&undefs); + if (has_reloc_errors) return error.LinkFailure; } @@ -1392,11 +1403,9 @@ fn scanRelocs(self: *Elf) !void { const gpa = self.base.comp.gpa; const shared_objects = self.shared_objects.values(); - var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); + var undefs: std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)) = .init(gpa); defer { - for (undefs.values()) |*refs| { - refs.deinit(); - } + for (undefs.values()) |*refs| refs.deinit(); undefs.deinit(); } @@ -2702,15 +2711,16 @@ fn initSyntheticSections(self: *Elf) !void { }); } - const needs_interp = blk: { - // On Ubuntu with musl-gcc, we get a weird combo of options looking like this: - // -dynamic-linker= -static - // In this case, if we do generate .interp section and segment, we will get - // a segfault in the dynamic linker trying to load a binary that is static - // and doesn't contain .dynamic section. - if (self.base.isStatic() and !comp.config.pie) break :blk false; - break :blk target.dynamic_linker.get() != null; + const is_exe_or_dyn_lib = switch (comp.config.output_mode) { + .Exe => true, + .Lib => comp.config.link_mode == .dynamic, + .Obj => false, }; + const have_dynamic_linker = comp.config.link_mode == .dynamic and is_exe_or_dyn_lib and !target.dynamic_linker.eql(.none); + + const needs_interp = have_dynamic_linker and + (comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker); + if (needs_interp and self.section_indexes.interp == null) { self.section_indexes.interp = try self.addSection(.{ .name = try self.insertShString(".interp"), @@ -3707,11 +3717,9 @@ fn allocateSpecialPhdrs(self: *Elf) void { fn writeAtoms(self: *Elf) !void { const gpa = self.base.comp.gpa; - var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); + var undefs: std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)) = .init(gpa); defer { - for (undefs.values()) |*refs| { - refs.deinit(); - } + for (undefs.values()) |*refs| refs.deinit(); undefs.deinit(); } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index cb145f772c..0869d6582e 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -497,14 +497,14 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { } fn outputType(elf_file: *Elf) u2 { - const comp = elf_file.base.comp; assert(!elf_file.base.isRelocatable()); - return switch (elf_file.base.comp.config.output_mode) { + const config = &elf_file.base.comp.config; + return switch (config.output_mode) { .Obj => unreachable, .Lib => 0, .Exe => switch (elf_file.getTarget().os.tag) { .haiku => 0, - else => if (comp.config.pie) 1 else 2, + else => if (config.pie) 1 else 2, }, }; } diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index d1b1512828..13816940fe 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -463,11 +463,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void { for (entry.external_relocs.items) |reloc| { const target_sym = self.symbol(reloc.target_sym); const r_offset = entry_off + reloc.source_off; - const r_addend: i64 = switch (reloc.target_off) { - .none, .got => 0, - else => |off| @intCast(@intFromEnum(off)), - }; - const r_type = relocation.dwarf.externalRelocType(target_sym.*, reloc.target_off == .got, sect_index, dwarf.address_size, cpu_arch); + const r_addend: i64 = @intCast(reloc.target_off); + const r_type = relocation.dwarf.externalRelocType(target_sym.*, sect_index, dwarf.address_size, cpu_arch); atom_ptr.addRelocAssumeCapacity(.{ .r_offset = r_offset, .r_addend = r_addend, @@ -660,6 +657,7 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { const atom_ptr = self.atom(atom_index) orelse continue; if (!atom_ptr.alive) continue; const shdr = atom_ptr.inputShdr(elf_file); + if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; if (atom_ptr.scanRelocsRequiresCode(elf_file)) { // TODO ideally we don't have to fetch the code here. @@ -950,7 +948,7 @@ pub fn getNavVAddr( .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .source_off = @intCast(reloc_info.offset), .target_sym = this_sym_index, - .target_off = .rel(reloc_info.addend), + .target_off = reloc_info.addend, }), .plan9 => unreachable, .none => unreachable, @@ -983,7 +981,7 @@ pub fn getUavVAddr( .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .source_off = @intCast(reloc_info.offset), .target_sym = sym_index, - .target_off = .rel(reloc_info.addend), + .target_off = reloc_info.addend, }), .plan9 => unreachable, .none => unreachable, diff --git a/src/link/Elf/relocation.zig b/src/link/Elf/relocation.zig index 9af4453948..047312cd68 100644 --- a/src/link/Elf/relocation.zig +++ b/src/link/Elf/relocation.zig @@ -108,13 +108,12 @@ pub const dwarf = struct { pub fn externalRelocType( target: Symbol, - is_got: bool, source_section: Dwarf.Section.Index, address_size: Dwarf.AddressSize, cpu_arch: std.Target.Cpu.Arch, ) u32 { return switch (cpu_arch) { - .x86_64 => @intFromEnum(@as(elf.R_X86_64, if (is_got) .GOT32 else switch (source_section) { + .x86_64 => @intFromEnum(@as(elf.R_X86_64, switch (source_section) { else => switch (address_size) { .@"32" => if (target.flags.is_tls) .DTPOFF32 else .@"32", .@"64" => if (target.flags.is_tls) .DTPOFF64 else .@"64", diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index aebfe6275f..a0de866544 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -648,7 +648,7 @@ pub fn getNavVAddr( .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .source_off = @intCast(reloc_info.offset), .target_sym = sym_index, - .target_off = .rel(reloc_info.addend), + .target_off = reloc_info.addend, }), .plan9 => unreachable, .none => unreachable, @@ -688,7 +688,7 @@ pub fn getUavVAddr( .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .source_off = @intCast(reloc_info.offset), .target_sym = sym_index, - .target_off = .rel(reloc_info.addend), + .target_off = reloc_info.addend, }), .plan9 => unreachable, .none => unreachable, diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 338eff4e7b..69684724a5 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -2376,7 +2376,7 @@ pub const FunctionImportId = enum(u32) { const zcu = wasm.base.comp.zcu.?; const ip = &zcu.intern_pool; const ext = ip.getNav(i.ptr(wasm).*).getResolvedExtern(ip).?; - return !ext.is_weak_linkage and ext.lib_name != .none; + return ext.linkage != .weak and ext.lib_name != .none; }, }; } diff --git a/src/print_air.zig b/src/print_air.zig deleted file mode 100644 index 6085adbcdc..0000000000 --- a/src/print_air.zig +++ /dev/null @@ -1,1038 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const fmtIntSizeBin = std.fmt.fmtIntSizeBin; - -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(stream: anytype, pt: Zcu.PerThread, air: Air, liveness: ?Air.Liveness) void { - 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 - stream.print( - \\# Total AIR+Liveness bytes: {} - \\# AIR Instructions: {d} ({}) - \\# AIR Extra Data: {d} ({}) - \\# Liveness tomb_bits: {} - \\# Liveness Extra Data: {d} ({}) - \\# Liveness special table: {d} ({}) - \\ - , .{ - fmtIntSizeBin(total_bytes), - air.instructions.len, fmtIntSizeBin(instruction_bytes), - air.extra.items.len, fmtIntSizeBin(extra_bytes), - fmtIntSizeBin(tomb_bytes), - if (liveness) |l| l.extra.len else 0, fmtIntSizeBin(liveness_extra_bytes), - if (liveness) |l| l.special.count() else 0, fmtIntSizeBin(liveness_special_bytes), - }) catch return; - // zig fmt: on - - var writer: Writer = .{ - .pt = pt, - .gpa = pt.zcu.gpa, - .air = air, - .liveness = liveness, - .indent = 2, - .skip_body = false, - }; - writer.writeBody(stream, air.getMainBody()) catch return; -} - -pub fn writeInst( - stream: anytype, - inst: Air.Inst.Index, - pt: Zcu.PerThread, - air: Air, - liveness: ?Air.Liveness, -) void { - var writer: Writer = .{ - .pt = pt, - .gpa = pt.zcu.gpa, - .air = air, - .liveness = liveness, - .indent = 2, - .skip_body = true, - }; - writer.writeInst(stream, inst) catch return; -} - -pub fn dump(pt: Zcu.PerThread, air: Air, liveness: ?Air.Liveness) void { - write(std.io.getStdErr().writer(), pt, air, liveness); -} - -pub fn dumpInst(inst: Air.Inst.Index, pt: Zcu.PerThread, air: Air, liveness: ?Air.Liveness) void { - writeInst(std.io.getStdErr().writer(), inst, pt, air, liveness); -} - -const Writer = struct { - pt: Zcu.PerThread, - gpa: Allocator, - air: Air, - liveness: ?Air.Liveness, - indent: usize, - skip_body: bool, - - fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void { - for (body) |inst| { - try w.writeInst(s, inst); - try s.writeByte('\n'); - } - } - - fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const tag = w.air.instructions.items(.tag)[@intFromEnum(inst)]; - try s.writeByteNTimes(' ', w.indent); - try s.print("{}{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_exact, - .rem, - .mod, - .bit_and, - .bit_or, - .xor, - .cmp_lt, - .cmp_lte, - .cmp_eq, - .cmp_gte, - .cmp_gt, - .cmp_neq, - .bool_and, - .bool_or, - .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_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, - => 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_lt_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, - .bitcast, - .load, - .fptrunc, - .fpext, - .intcast, - .intcast_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, - .float_from_int, - .splat, - .int_from_float, - .int_from_float_optimized, - .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), - .struct_field_val => 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), - .vector_store_elem => try w.writeVectorStoreElem(s, inst), - .tlv_dllimport_ptr => try w.writeTlvDllimportPtr(s, inst), - - .work_item_id, - .work_group_size, - .work_group_id, - => try w.writeWorkDimension(s, inst), - } - try s.writeByte(')'); - } - - fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const bin_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const un_op = w.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - try w.writeOperand(s, inst, 0, un_op); - } - - fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - _ = w; - _ = inst; - // no-op, no argument to write - } - - fn writeType(w: *Writer, s: anytype, ty: Type) !void { - return ty.print(s, w.pt); - } - - fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty = w.air.instructions.items(.data)[@intFromEnum(inst)].ty; - try w.writeType(s, ty); - } - - fn writeArg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg; - try w.writeType(s, arg.ty.toType()); - switch (arg.name) { - .none => {}, - _ => try s.print(", \"{}\"", .{std.zig.fmtEscapes(arg.name.toSlice(w.air))}), - } - } - - fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_op = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - try w.writeType(s, ty_op.ty.toType()); - try s.writeAll(", "); - try w.writeOperand(s, inst, 0, ty_op.operand); - } - - fn writeBlock(w: *Writer, s: anytype, tag: Air.Inst.Tag, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - try w.writeType(s, ty_pl.ty.toType()); - const body: []const Air.Inst.Index = @ptrCast(switch (tag) { - inline .block, .dbg_inline_block => |comptime_tag| body: { - const extra = w.air.extraData(switch (comptime_tag) { - .block => Air.Block, - .dbg_inline_block => Air.DbgInlineBlock, - else => unreachable, - }, ty_pl.payload); - switch (comptime_tag) { - .block => {}, - .dbg_inline_block => { - try s.writeAll(", "); - try w.writeInstRef(s, Air.internedToRef(extra.data.func), false); - }, - else => unreachable, - } - break :body w.air.extra.items[extra.end..][0..extra.data.body_len]; - }, - 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.writeByteNTimes(' ', w.indent); - try s.writeAll("}"); - - for (liveness_block.deaths) |operand| { - try s.print(" {}!", .{operand}); - } - } - - fn writeLoop(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.Block, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); - - try w.writeType(s, ty_pl.ty.toType()); - if (w.skip_body) return s.writeAll(", ..."); - try s.writeAll(", {\n"); - const old_indent = w.indent; - w.indent += 2; - try w.writeBody(s, body); - w.indent = old_indent; - try s.writeByteNTimes(' ', w.indent); - try s.writeAll("}"); - } - - fn writeAggregateInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const zcu = w.pt.zcu; - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const vector_ty = ty_pl.ty.toType(); - 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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const data = w.air.instructions.items(.data); - const ty_pl = data[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; - - const inst_ty = data[@intFromEnum(inst)].ty_pl.ty.toType(); - 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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(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 writeShuffleOne(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const unwrapped = w.air.unwrapShuffleOne(w.pt.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 {}", .{Value.fromInterned(val).fmtValue(w.pt)}), - } - } - try s.writeByte(']'); - } - - fn writeShuffleTwo(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const unwrapped = w.air.unwrapShuffleTwo(w.pt.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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const zcu = w.pt.zcu; - const pl_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const reduce = w.air.instructions.items(.data)[@intFromEnum(inst)].reduce; - - try w.writeOperand(s, inst, 0, reduce.operand); - try s.print(", {s}", .{@tagName(reduce.operation)}); - } - - fn writeCmpVector(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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 writeVectorStoreElem(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const data = w.air.instructions.items(.data)[@intFromEnum(inst)].vector_store_elem; - const extra = w.air.extraData(Air.VectorCmp, data.payload).data; - - try w.writeOperand(s, inst, 0, data.vector_ptr); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 2, extra.rhs); - } - - fn writeTlvDllimportPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ip = &w.pt.zcu.intern_pool; - const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; - try w.writeType(s, .fromInterned(ty_nav.ty)); - try s.print(", '{}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)}); - } - - fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const atomic_load = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const prefetch = w.air.instructions.items(.data)[@intFromEnum(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: anytype, - inst: Air.Inst.Index, - order: std.builtin.AtomicOrder, - ) @TypeOf(s).Error!void { - const bin_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.Asm, ty_pl.payload); - const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; - const clobbers_len = @as(u31, @truncate(extra.data.flags)); - var extra_i: usize = extra.end; - var op_index: usize = 0; - - const ret_ty = w.typeOfIndex(inst); - try w.writeType(s, ret_ty); - - if (is_volatile) { - try s.writeAll(", volatile"); - } - - const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.outputs_len])); - extra_i += outputs.len; - const inputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.inputs_len])); - extra_i += inputs.len; - - for (outputs) |output| { - const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the strings and their null terminators, we still use the next u32 - // for the null terminator. - extra_i += (constraint.len + name.len + (2 + 3)) / 4; - - if (output == .none) { - try s.print(", [{s}] -> {s}", .{ name, constraint }); - } else { - try s.print(", [{s}] out {s} = (", .{ name, constraint }); - try w.writeOperand(s, inst, op_index, output); - op_index += 1; - try s.writeByte(')'); - } - } - - for (inputs) |input| { - const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); - const constraint = std.mem.sliceTo(extra_bytes, 0); - const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the strings and their null terminators, we still use the next u32 - // for the null terminator. - extra_i += (constraint.len + name.len + 1) / 4 + 1; - - try s.print(", [{s}] in {s} = (", .{ name, constraint }); - try w.writeOperand(s, inst, op_index, input); - op_index += 1; - try s.writeByte(')'); - } - - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]); - const clobber = std.mem.sliceTo(extra_bytes, 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - - try s.writeAll(", ~{"); - try s.writeAll(clobber); - try s.writeAll("}"); - } - } - const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len]; - try s.print(", \"{}\"", .{std.zig.fmtEscapes(asm_source)}); - } - - fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const dbg_stmt = w.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; - try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); - } - - fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - try w.writeOperand(s, inst, 0, pl_op.operand); - const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload); - try s.print(", \"{}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))}); - } - - fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = w.air.extraData(Air.Call, pl_op.payload); - const args = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len])); - try w.writeOperand(s, inst, 0, pl_op.operand); - 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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const br = w.air.instructions.items(.data)[@intFromEnum(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: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const repeat = w.air.instructions.items(.data)[@intFromEnum(inst)].repeat; - try w.writeInstIndex(s, repeat.loop_inst, false); - } - - fn writeTry(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = w.air.extraData(Air.Try, pl_op.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); - const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| - liveness.getCondBr(inst) - else - .{ .then_deaths = &.{}, .else_deaths = &.{} }; - - try w.writeOperand(s, inst, 0, pl_op.operand); - 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.writeByteNTimes(' ', w.indent); - for (liveness_condbr.else_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{}!", .{operand}); - } - try s.writeAll("\n"); - } - try w.writeBody(s, body); - - w.indent = old_indent; - try s.writeByteNTimes(' ', w.indent); - try s.writeAll("}"); - - for (liveness_condbr.then_deaths) |operand| { - try s.print(" {}!", .{operand}); - } - } - - fn writeTryPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; - const extra = w.air.extraData(Air.TryPtr, ty_pl.payload); - const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]); - const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| - liveness.getCondBr(inst) - else - .{ .then_deaths = &.{}, .else_deaths = &.{} }; - - try w.writeOperand(s, inst, 0, extra.data.ptr); - - try s.writeAll(", "); - try w.writeType(s, ty_pl.ty.toType()); - 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.writeByteNTimes(' ', w.indent); - for (liveness_condbr.else_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{}!", .{operand}); - } - try s.writeAll("\n"); - } - try w.writeBody(s, body); - - w.indent = old_indent; - try s.writeByteNTimes(' ', w.indent); - try s.writeAll("}"); - - for (liveness_condbr.then_deaths) |operand| { - try s.print(" {}!", .{operand}); - } - } - - fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const extra = w.air.extraData(Air.CondBr, pl_op.payload); - const then_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.then_body_len]); - const else_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]); - const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness| - liveness.getCondBr(inst) - else - .{ .then_deaths = &.{}, .else_deaths = &.{} }; - - try w.writeOperand(s, inst, 0, pl_op.operand); - if (w.skip_body) return s.writeAll(", ..."); - try s.writeAll(","); - if (extra.data.branch_hints.true != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.true)}); - } - if (extra.data.branch_hints.then_cov != .none) { - try s.print(" {s}", .{@tagName(extra.data.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.writeByteNTimes(' ', w.indent); - for (liveness_condbr.then_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, then_body); - try s.writeByteNTimes(' ', old_indent); - try s.writeAll("},"); - if (extra.data.branch_hints.false != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.false)}); - } - if (extra.data.branch_hints.else_cov != .none) { - try s.print(" {s}", .{@tagName(extra.data.branch_hints.else_cov)}); - } - try s.writeAll(" {\n"); - - if (liveness_condbr.else_deaths.len != 0) { - try s.writeByteNTimes(' ', w.indent); - for (liveness_condbr.else_deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, else_body); - w.indent = old_indent; - - try s.writeByteNTimes(' ', old_indent); - try s.writeAll("}"); - } - - fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).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.writeByteNTimes(' ', w.indent); - for (deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, case.body); - w.indent -= 2; - try s.writeByteNTimes(' ', 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.writeByteNTimes(' ', w.indent); - for (deaths, 0..) |operand, i| { - if (i != 0) try s.writeAll(" "); - try s.print("{}!", .{operand}); - } - try s.writeAll("\n"); - } - - try w.writeBody(s, else_body); - w.indent -= 2; - try s.writeByteNTimes(' ', w.indent); - try s.writeAll("}"); - } - - try s.writeAll("\n"); - try s.writeByteNTimes(' ', old_indent); - } - - fn writeWasmMemorySize(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - try s.print("{d}", .{pl_op.payload}); - } - - fn writeWasmMemoryGrow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - try s.print("{d}, ", .{pl_op.payload}); - try w.writeOperand(s, inst, 0, pl_op.operand); - } - - fn writeWorkDimension(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - try s.print("{d}", .{pl_op.payload}); - } - - fn writeOperand( - w: *Writer, - s: anytype, - inst: Air.Inst.Index, - op_index: usize, - operand: Air.Inst.Ref, - ) @TypeOf(s).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: anytype, - operand: Air.Inst.Ref, - dies: bool, - ) @TypeOf(s).Error!void { - if (@intFromEnum(operand) < InternPool.static_len) { - return s.print("@{}", .{operand}); - } else if (operand.toInterned()) |ip_index| { - const pt = w.pt; - const ty = Type.fromInterned(pt.zcu.intern_pool.indexToKey(ip_index).typeOf()); - try s.print("<{}, {}>", .{ - ty.fmt(pt), - Value.fromInterned(ip_index).fmtValue(pt), - }); - } else { - return w.writeInstIndex(s, operand.toIndex().?, dies); - } - } - - fn writeInstIndex( - w: *Writer, - s: anytype, - inst: Air.Inst.Index, - dies: bool, - ) @TypeOf(s).Error!void { - _ = w; - try s.print("{}", .{inst}); - if (dies) try s.writeByte('!'); - } - - fn typeOfIndex(w: *Writer, inst: Air.Inst.Index) Type { - const zcu = w.pt.zcu; - return w.air.typeOfIndex(inst, &zcu.intern_pool); - } -}; diff --git a/test/cases/compile_errors/@import_zon_bad_type.zig b/test/cases/compile_errors/@import_zon_bad_type.zig index d1ccfc312c..88bf6205dd 100644 --- a/test/cases/compile_errors/@import_zon_bad_type.zig +++ b/test/cases/compile_errors/@import_zon_bad_type.zig @@ -117,9 +117,9 @@ export fn testMutablePointer() void { // tmp.zig:37:38: note: imported here // neg_inf.zon:1:1: error: expected type '?u8' // tmp.zig:57:28: note: imported here -// neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_522' +// neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_525' // tmp.zig:62:39: note: imported here -// neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_524' +// neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_527' // tmp.zig:67:44: note: imported here -// neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_527' +// neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_530' // tmp.zig:72:50: note: imported here diff --git a/test/cases/compile_errors/anytype_param_requires_comptime.zig b/test/cases/compile_errors/anytype_param_requires_comptime.zig index c0990a9ed3..7bbb1d3c6e 100644 --- a/test/cases/compile_errors/anytype_param_requires_comptime.zig +++ b/test/cases/compile_errors/anytype_param_requires_comptime.zig @@ -15,6 +15,6 @@ pub export fn entry() void { // error // // :7:25: error: unable to resolve comptime value -// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_496.C' must be comptime-known +// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_499.C' must be comptime-known // :4:16: note: struct requires comptime because of this field // :4:16: note: types are not available at runtime diff --git a/test/cases/compile_errors/bogus_method_call_on_slice.zig b/test/cases/compile_errors/bogus_method_call_on_slice.zig index 4e35c4264e..b5ffe15549 100644 --- a/test/cases/compile_errors/bogus_method_call_on_slice.zig +++ b/test/cases/compile_errors/bogus_method_call_on_slice.zig @@ -16,5 +16,5 @@ pub export fn entry2() void { // // :3:6: error: no field or member function named 'copy' in '[]const u8' // :9:8: error: no field or member function named 'bar' in '@TypeOf(.{})' -// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_500' +// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_503' // :12:6: note: struct declared here diff --git a/test/cases/compile_errors/coerce_anon_struct.zig b/test/cases/compile_errors/coerce_anon_struct.zig index 9b515762b1..2de29e8aae 100644 --- a/test/cases/compile_errors/coerce_anon_struct.zig +++ b/test/cases/compile_errors/coerce_anon_struct.zig @@ -6,6 +6,6 @@ export fn foo() void { // error // -// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_489' +// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_492' // :3:16: note: struct declared here // :1:11: note: struct declared here diff --git a/test/cases/compile_errors/redundant_try.zig b/test/cases/compile_errors/redundant_try.zig index b6d030686b..19d8070f80 100644 --- a/test/cases/compile_errors/redundant_try.zig +++ b/test/cases/compile_errors/redundant_try.zig @@ -44,9 +44,9 @@ comptime { // // :5:23: error: expected error union type, found 'comptime_int' // :10:23: error: expected error union type, found '@TypeOf(.{})' -// :15:23: error: expected error union type, found 'tmp.test2__struct_526' +// :15:23: error: expected error union type, found 'tmp.test2__struct_529' // :15:23: note: struct declared here -// :20:27: error: expected error union type, found 'tmp.test3__struct_528' +// :20:27: error: expected error union type, found 'tmp.test3__struct_531' // :20:27: note: struct declared here // :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }' // :31:13: error: expected error union type, found 'u32' diff --git a/test/tests.zig b/test/tests.zig index c45e3ef561..ab42dbba26 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1598,7 +1598,9 @@ const c_abi_targets = blk: { break :blk [_]CAbiTarget{ // Native Targets - .{}, + .{ + .use_llvm = true, + }, // Linux Targets @@ -1837,7 +1839,6 @@ const c_abi_targets = blk: { .abi = .musl, }, .use_llvm = false, - .use_lld = false, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ @@ -1848,7 +1849,6 @@ const c_abi_targets = blk: { .abi = .musl, }, .use_llvm = false, - .use_lld = false, .strip = true, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, @@ -1860,7 +1860,6 @@ const c_abi_targets = blk: { .abi = .musl, }, .use_llvm = false, - .use_lld = false, .pic = true, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, @@ -1870,6 +1869,7 @@ const c_abi_targets = blk: { .os_tag = .linux, .abi = .musl, }, + .use_llvm = true, }, .{ .target = .{ @@ -1877,6 +1877,7 @@ const c_abi_targets = blk: { .os_tag = .linux, .abi = .muslx32, }, + .use_llvm = true, }, // WASI Targets -- cgit v1.2.3