diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6e3ab5bd5729..fd07b9add4c8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,6 +20,14 @@ jobs: uses: actions/checkout@v4 - name: Build and Test run: sh ci/x86_64-linux-debug.sh + x86_64-linux-debug-llvm: + timeout-minutes: 540 + runs-on: [self-hosted, Linux, x86_64] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build and Test + run: sh ci/x86_64-linux-debug-llvm.sh x86_64-linux-release: timeout-minutes: 540 runs-on: [self-hosted, Linux, x86_64] diff --git a/CMakeLists.txt b/CMakeLists.txt index 2baf4dcbd844..671a6ad23644 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/build.zig b/build.zig index 6487cc615d89..ce1c89aa1984 100644 --- a/build.zig +++ b/build.zig @@ -92,6 +92,12 @@ pub fn build(b: *std.Build) !void { const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false; const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false; const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; + const skip_freebsd = b.option(bool, "skip-freebsd", "Main test suite skips targets with freebsd OS") orelse false; + const skip_netbsd = b.option(bool, "skip-netbsd", "Main test suite skips targets with netbsd OS") orelse false; + const skip_windows = b.option(bool, "skip-windows", "Main test suite skips targets with windows OS") orelse false; + const skip_macos = b.option(bool, "skip-macos", "Main test suite skips targets with macos OS") orelse false; + const skip_linux = b.option(bool, "skip-linux", "Main test suite skips targets with linux OS") orelse false; + const skip_llvm = b.option(bool, "skip-llvm", "Main test suite skips targets that use LLVM backend") orelse false; const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; @@ -435,10 +441,15 @@ pub fn build(b: *std.Build) !void { .include_paths = &.{}, .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, .skip_libc = skip_libc, - .use_llvm = use_llvm, - // 2520100864 was observed on an x86_64-linux-gnu host. - .max_rss = 2772110950, + // 2923515904 was observed on an x86_64-linux-gnu host. + .max_rss = 3100000000, })); test_modules_step.dependOn(tests.addModuleTests(b, .{ @@ -452,8 +463,13 @@ pub fn build(b: *std.Build) !void { .include_paths = &.{"test/c_import"}, .skip_single_threaded = true, .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, .skip_libc = skip_libc, - .use_llvm = use_llvm, })); test_modules_step.dependOn(tests.addModuleTests(b, .{ @@ -467,8 +483,13 @@ pub fn build(b: *std.Build) !void { .include_paths = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, .skip_libc = true, - .use_llvm = use_llvm, .no_builtin = true, })); @@ -483,8 +504,13 @@ pub fn build(b: *std.Build) !void { .include_paths = &.{}, .skip_single_threaded = true, .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, .skip_libc = true, - .use_llvm = use_llvm, .no_builtin = true, })); @@ -499,8 +525,13 @@ pub fn build(b: *std.Build) !void { .include_paths = &.{}, .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, .skip_libc = skip_libc, - .use_llvm = use_llvm, // I observed a value of 5605064704 on the M2 CI. .max_rss = 6165571174, })); @@ -536,6 +567,12 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(tests.addCAbiTests(b, .{ .test_target_filters = test_target_filters, .skip_non_native = skip_non_native, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_windows = skip_windows, + .skip_macos = skip_macos, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, .skip_release = skip_release, })); test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows)); @@ -549,7 +586,6 @@ pub fn build(b: *std.Build) !void { .lldb = b.option([]const u8, "lldb", "path to lldb binary"), .optimize_modes = optimization_modes, .skip_single_threaded = skip_single_threaded, - .skip_non_native = skip_non_native, .skip_libc = skip_libc, })) |test_debugger_step| test_step.dependOn(test_debugger_step); if (tests.addLlvmIrTests(b, .{ diff --git a/ci/x86_64-linux-debug-llvm.sh b/ci/x86_64-linux-debug-llvm.sh new file mode 100644 index 000000000000..dfb440573f1a --- /dev/null +++ b/ci/x86_64-linux-debug-llvm.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# Requires cmake ninja-build + +set -x +set -e + +ARCH="$(uname -m)" +TARGET="$ARCH-linux-musl" +MCPU="baseline" +CACHE_BASENAME="zig+llvm+lld+clang-$TARGET-0.15.0-dev.233+7c85dc460" +PREFIX="$HOME/deps/$CACHE_BASENAME" +ZIG="$PREFIX/bin/zig" + +export PATH="$HOME/deps/wasmtime-v29.0.0-$ARCH-linux:$HOME/deps/qemu-linux-x86_64-9.2.0-rc1/bin:$HOME/local/bin:$PATH" + +# Make the `zig version` number consistent. +# This will affect the cmake command below. +git fetch --unshallow || true +git fetch --tags + +# Override the cache directories because they won't actually help other CI runs +# which will be testing alternate versions of zig, and ultimately would just +# fill up space on the hard drive for no reason. +export ZIG_GLOBAL_CACHE_DIR="$PWD/zig-global-cache" +export ZIG_LOCAL_CACHE_DIR="$PWD/zig-local-cache" + +mkdir build-debug-llvm +cd build-debug-llvm + +export CC="$ZIG cc -target $TARGET -mcpu=$MCPU" +export CXX="$ZIG c++ -target $TARGET -mcpu=$MCPU" + +cmake .. \ + -DCMAKE_INSTALL_PREFIX="stage3-debug" \ + -DCMAKE_PREFIX_PATH="$PREFIX" \ + -DCMAKE_BUILD_TYPE=Debug \ + -DZIG_TARGET_TRIPLE="$TARGET" \ + -DZIG_TARGET_MCPU="$MCPU" \ + -DZIG_STATIC=ON \ + -DZIG_NO_LIB=ON \ + -DZIG_EXTRA_BUILD_ARGS="-Duse-llvm=true" \ + -GNinja + +# Now cmake will use zig as the C/C++ compiler. We reset the environment variables +# so that installation and testing do not get affected by them. +unset CC +unset CXX + +ninja install + +# simultaneously test building self-hosted without LLVM and with 32-bit arm +stage3-debug/bin/zig build \ + -Dtarget=arm-linux-musleabihf \ + -Dno-lib + +stage3-debug/bin/zig build test docs \ + --maxrss 21000000000 \ + -Dlldb=$HOME/deps/lldb-zig/Debug-e0a42bb34/bin/lldb \ + -fqemu \ + -fwasmtime \ + -Dstatic-llvm \ + -Dskip-freebsd \ + -Dskip-netbsd \ + -Dskip-windows \ + -Dskip-macos \ + -Dtarget=native-native-musl \ + --search-prefix "$PREFIX" \ + --zig-lib-dir "$PWD/../lib" \ + -Denable-superhtml diff --git a/ci/x86_64-linux-debug.sh b/ci/x86_64-linux-debug.sh index 3974f07a9550..395a6770a5d4 100755 --- a/ci/x86_64-linux-debug.sh +++ b/ci/x86_64-linux-debug.sh @@ -25,12 +25,6 @@ git fetch --tags export ZIG_GLOBAL_CACHE_DIR="$PWD/zig-global-cache" export ZIG_LOCAL_CACHE_DIR="$PWD/zig-local-cache" -# Test building from source without LLVM. -cc -o bootstrap bootstrap.c -./bootstrap -./zig2 build -Dno-lib -./zig-out/bin/zig test test/behavior.zig - mkdir build-debug cd build-debug @@ -65,39 +59,12 @@ stage3-debug/bin/zig build test docs \ -fqemu \ -fwasmtime \ -Dstatic-llvm \ + -Dskip-freebsd \ + -Dskip-netbsd \ + -Dskip-windows \ + -Dskip-macos \ + -Dskip-llvm \ -Dtarget=native-native-musl \ --search-prefix "$PREFIX" \ --zig-lib-dir "$PWD/../lib" \ -Denable-superhtml - -# Ensure that updating the wasm binary from this commit will result in a viable build. -stage3-debug/bin/zig build update-zig1 - -mkdir ../build-new -cd ../build-new - -export CC="$ZIG cc -target $TARGET -mcpu=$MCPU" -export CXX="$ZIG c++ -target $TARGET -mcpu=$MCPU" - -cmake .. \ - -DCMAKE_PREFIX_PATH="$PREFIX" \ - -DCMAKE_BUILD_TYPE=Debug \ - -DZIG_TARGET_TRIPLE="$TARGET" \ - -DZIG_TARGET_MCPU="$MCPU" \ - -DZIG_STATIC=ON \ - -DZIG_NO_LIB=ON \ - -GNinja - -unset CC -unset CXX - -ninja install - -stage3/bin/zig test ../test/behavior.zig -stage3/bin/zig build -p stage4 \ - -Dstatic-llvm \ - -Dtarget=native-native-musl \ - -Dno-lib \ - --search-prefix "$PREFIX" \ - --zig-lib-dir "$PWD/../lib" -stage4/bin/zig test ../test/behavior.zig diff --git a/doc/langref/test_global_assembly.zig b/doc/langref/test_global_assembly.zig index ec07d23ee0a9..a5d5f3ca5f13 100644 --- a/doc/langref/test_global_assembly.zig +++ b/doc/langref/test_global_assembly.zig @@ -19,3 +19,4 @@ test "global assembly" { // test // target=x86_64-linux +// llvm=true diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 084344535e2a..347b3357f594 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -2581,12 +2581,16 @@ pub fn standardDynamicLinkerPath(target: Target) DynamicLinker { } pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 { + return ptrBitWidth_arch_abi(cpu.arch, abi); +} + +pub fn ptrBitWidth_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) u16 { switch (abi) { .gnux32, .muslx32, .gnuabin32, .muslabin32, .ilp32 => return 32, .gnuabi64, .muslabi64 => return 64, else => {}, } - return switch (cpu.arch) { + return switch (cpu_arch) { .avr, .msp430, => 16, diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 1683cc500b60..88e230433251 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 4b1faa87baa5..bac31f57601c 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 572f3ddf96b0..0f4b4ff4b37f 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 2280bea9e74e..9b4897260eff 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 d8d5ff19c701..865e1efa6d1c 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 229d6a797312..188800cde497 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 b315acecce94..4810766e710d 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 b5d579c2d380..01304d78e8a5 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 7acba48ed06f..94efaf114c53 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 4ad24cf92494..2fafc44ab7ae 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/print_air.zig b/src/Air/print.zig similarity index 97% rename from src/print_air.zig rename to src/Air/print.zig index 6085adbcdc00..343c640a6312 100644 --- a/src/print_air.zig +++ b/src/Air/print.zig @@ -2,13 +2,15 @@ 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 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. @@ -52,12 +54,13 @@ pub fn write(stream: anytype, pt: Zcu.PerThread, air: Air, liveness: ?Air.Livene } pub fn writeInst( + air: Air, stream: anytype, inst: Air.Inst.Index, pt: Zcu.PerThread, - air: Air, liveness: ?Air.Liveness, ) void { + comptime std.debug.assert(build_options.enable_debug_extensions); var writer: Writer = .{ .pt = pt, .gpa = pt.zcu.gpa, @@ -69,12 +72,12 @@ pub fn writeInst( 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 dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void { + air.write(std.io.getStdErr().writer(), pt, 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); +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 { @@ -320,7 +323,7 @@ const Writer = struct { .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), + .runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst), .work_item_id, .work_group_size, @@ -578,7 +581,7 @@ const Writer = struct { try w.writeOperand(s, inst, 2, extra.rhs); } - fn writeTlvDllimportPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + 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)); diff --git a/src/Air/types_resolved.zig b/src/Air/types_resolved.zig index eb17402ebe3f..1def679794a7 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.zig b/src/Compilation.zig index ba599d0207e3..81d150b03fea 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -774,7 +774,7 @@ pub const Directories = struct { /// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller, /// preventing debugging features from making it into release builds of the compiler. pub inline fn debugIncremental(comp: *const Compilation) bool { - if (!build_options.enable_debug_extensions) return false; + if (!build_options.enable_debug_extensions or builtin.single_threaded) return false; return comp.debug_incremental; } @@ -7225,7 +7225,7 @@ fn buildOutputFromZig( assert(out.* == null); out.* = crt_file; - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); } pub const CrtFileOptions = struct { @@ -7349,7 +7349,7 @@ pub fn build_crt_file( try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); { comp.mutex.lock(); @@ -7359,11 +7359,14 @@ pub fn build_crt_file( } } -pub fn queueLinkTaskMode(comp: *Compilation, path: Cache.Path, output_mode: std.builtin.OutputMode) void { - comp.queueLinkTasks(switch (output_mode) { +pub fn queueLinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) void { + comp.queueLinkTasks(switch (config.output_mode) { .Exe => unreachable, .Obj => &.{.{ .load_object = path }}, - .Lib => &.{.{ .load_archive = path }}, + .Lib => &.{switch (config.link_mode) { + .static => .{ .load_archive = path }, + .dynamic => .{ .load_dso = path }, + }}, }); } diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 49b5328a3a51..637a4bb280db 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -191,91 +191,6 @@ pub fn resolve(options: Options) ResolveError!Config { const root_optimize_mode = options.root_optimize_mode orelse .Debug; - // 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 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) - return error.EmittingLlvmModuleRequiresLlvmBackend; - if (!target_util.hasLlvmSupport(target, target.ofmt)) - return error.LlvmLacksTargetSupport; - - break :b true; - } - - // If LLVM does not support the target, then we can't use it. - if (!target_util.hasLlvmSupport(target, target.ofmt)) { - if (options.use_llvm == true) return error.LlvmLacksTargetSupport; - break :b false; - } - - // If Zig does not support the target, then we can't use it. - if (target_util.zigBackend(target, false) == .other) { - if (options.use_llvm == false) return error.ZigLacksTargetSupport; - break :b true; - } - - if (options.use_llvm) |x| break :b x; - - // If we cannot use LLVM libraries, then our own backends will be a - // better default since the LLVM backend can only produce bitcode - // and not an object file or executable. - if (!use_lib_llvm and options.emit_bin) break :b false; - - // Prefer LLVM for release builds. - if (root_optimize_mode != .Debug) 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 - // we are confident in the robustness of the backend. - break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target); - }; - - 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 - // using LLVM libraries. Impossible. - return error.EmittingBinaryRequiresLlvmLibrary; - } - - if (target_util.zigBackend(target, use_llvm) == .other) { - // There is no compiler backend available for this target. - return error.ZigLacksTargetSupport; - } - } - - // Make a decision on whether to use LLD or our own linker. - const use_lld = b: { - if (!target_util.hasLldSupport(target.ofmt)) { - if (options.use_lld == true) return error.LldIncompatibleObjectFormat; - break :b false; - } - - if (!build_options.have_llvm) { - if (options.use_lld == true) return error.LldUnavailable; - break :b false; - } - - if (options.lto != null and options.lto != .none) { - if (options.use_lld == false) return error.LtoRequiresLld; - break :b true; - } - - if (options.use_llvm == false) { - if (options.use_lld == true) return error.LldCannotIncrementallyLink; - break :b false; - } - - if (options.use_lld) |x| break :b x; - break :b true; - }; - // Make a decision on whether to use Clang or Aro for translate-c and compiling C files. const c_frontend: CFrontend = b: { if (!build_options.have_llvm) { @@ -288,19 +203,6 @@ pub fn resolve(options: Options) ResolveError!Config { break :b .clang; }; - const lto: std.zig.LtoMode = b: { - if (!use_lld) { - // zig ld LTO support is tracked by - // https://github.com/ziglang/zig/issues/8680 - if (options.lto != null and options.lto != .none) return error.LtoRequiresLld; - break :b .none; - } - - if (options.lto) |x| break :b x; - - break :b .none; - }; - const link_libcpp = b: { if (options.link_libcpp == true) break :b true; if (options.any_sanitize_thread) { @@ -314,14 +216,6 @@ pub fn resolve(options: Options) ResolveError!Config { break :b false; }; - var link_libunwind = b: { - if (link_libcpp and target_util.libCxxNeedsLibUnwind(target)) { - if (options.link_libunwind == false) return error.LibCppRequiresLibUnwind; - break :b true; - } - break :b options.link_libunwind orelse false; - }; - const link_libc = b: { if (target_util.osRequiresLibC(target)) { if (options.link_libc == false) return error.OsRequiresLibC; @@ -331,7 +225,7 @@ pub fn resolve(options: Options) ResolveError!Config { if (options.link_libc == false) return error.LibCppRequiresLibC; break :b true; } - if (link_libunwind) { + if (options.link_libunwind == true) { if (options.link_libc == false) return error.LibUnwindRequiresLibC; break :b true; } @@ -402,12 +296,17 @@ pub fn resolve(options: Options) ResolveError!Config { break :b .static; }; - // This is done here to avoid excessive duplicated logic due to the complex dependencies between these options. - if (options.output_mode == .Exe and link_libc and target_util.libCNeedsLibUnwind(target, link_mode)) { - if (options.link_libunwind == false) return error.LibCRequiresLibUnwind; - - link_libunwind = true; - } + const link_libunwind = b: { + if (options.output_mode == .Exe and link_libc and target_util.libCNeedsLibUnwind(target, link_mode)) { + if (options.link_libunwind == false) return error.LibCRequiresLibUnwind; + break :b true; + } + if (link_libcpp and target_util.libCxxNeedsLibUnwind(target)) { + if (options.link_libunwind == false) return error.LibCppRequiresLibUnwind; + break :b true; + } + break :b options.link_libunwind orelse false; + }; const import_memory = options.import_memory orelse (options.output_mode == .Obj); const export_memory = b: { @@ -446,6 +345,119 @@ 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 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) + return error.EmittingLlvmModuleRequiresLlvmBackend; + if (!target_util.hasLlvmSupport(target, target.ofmt)) + return error.LlvmLacksTargetSupport; + + break :b true; + } + + // If LLVM does not support the target, then we can't use it. + if (!target_util.hasLlvmSupport(target, target.ofmt)) { + if (options.use_llvm == true) return error.LlvmLacksTargetSupport; + break :b false; + } + + // If Zig does not support the target, then we can't use it. + if (target_util.zigBackend(target, false) == .other) { + if (options.use_llvm == false) return error.ZigLacksTargetSupport; + break :b true; + } + + if (options.use_llvm) |x| break :b x; + + // If we cannot use LLVM libraries, then our own backends will be a + // better default since the LLVM backend can only produce bitcode + // and not an object file or executable. + if (!use_lib_llvm and options.emit_bin) break :b false; + + // Prefer LLVM for release builds. + if (root_optimize_mode != .Debug) 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 + // we are confident in the robustness of the backend. + break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target); + }; + + 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 + // using LLVM libraries. Impossible. + return error.EmittingBinaryRequiresLlvmLibrary; + } + + if (target_util.zigBackend(target, use_llvm) == .other) { + // There is no compiler backend available for this target. + return error.ZigLacksTargetSupport; + } + } + + // Make a decision on whether to use LLD or our own linker. + const use_lld = b: { + if (!target_util.hasLldSupport(target.ofmt)) { + if (options.use_lld == true) return error.LldIncompatibleObjectFormat; + break :b false; + } + + if (!build_options.have_llvm) { + if (options.use_lld == true) return error.LldUnavailable; + break :b false; + } + + if (options.lto != null and options.lto != .none) { + if (options.use_lld == false) return error.LtoRequiresLld; + break :b true; + } + + if (options.use_llvm == false) { + if (options.use_lld == true) return error.LldCannotIncrementallyLink; + break :b false; + } + + if (options.use_lld) |x| break :b x; + + // 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: { + if (!use_lld) { + // zig ld LTO support is tracked by + // https://github.com/ziglang/zig/issues/8680 + if (options.lto != null and options.lto != .none) return error.LtoRequiresLld; + break :b .none; + } + + if (options.lto) |x| break :b x; + + break :b .none; + }; + const root_strip = b: { if (options.root_strip) |x| break :b x; if (root_optimize_mode == .ReleaseSmall) break :b true; diff --git a/src/IncrementalDebugServer.zig b/src/IncrementalDebugServer.zig index 69ebc3752e28..531b71b4e824 100644 --- a/src/IncrementalDebugServer.zig +++ b/src/IncrementalDebugServer.zig @@ -10,7 +10,7 @@ comptime { // This file should only be referenced when debug extensions are enabled. - std.debug.assert(@import("build_options").enable_debug_extensions); + std.debug.assert(@import("build_options").enable_debug_extensions and !@import("builtin").single_threaded); } zcu: *Zcu, diff --git a/src/InternPool.zig b/src/InternPool.zig index 44f19e3e29e5..de1a434c029e 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 37aa3fb6e862..e20fb17f2626 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, @@ -32508,11 +32538,11 @@ fn analyzeSlice( const actual_len = if (array_ty.zigTypeTag(zcu) == .array) try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) else if (slice_ty.isSlice(zcu)) l: { - const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice); + const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice); break :l if (slice_ty.sentinel(zcu) == null) - slice_len_inst + slice_len else - try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); + try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true); } else break :bounds_check; const actual_end = if (slice_sentinel != null) @@ -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 d4a0adf28498..7223e5a55e04 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 8e3d07627f51..8b35d8d7999f 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 c01fa24ecc93..00cceb0c6775 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 9c7793df2724..421ba7d75338 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 530af98a0d6f..9fc51bd2d3d2 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 439e5e6dbb20..ad9884dcdb85 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 c4f72dd6c38e..264b1e732d9e 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 cd12bf72d550..b38492d500d3 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 } }; @@ -161477,10 +161505,12 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { for (elems, 0..) |elem_ref, field_index| { const elem_dies = bt.feed(); if (loaded_struct.fieldIsComptime(ip, field_index)) continue; - var elem = try cg.tempFromOperand(elem_ref, elem_dies); - try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg); - try elem.die(cg); - try cg.resetTemps(reset_index); + if (!hack_around_sema_opv_bugs or Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]).hasRuntimeBitsIgnoreComptime(zcu)) { + var elem = try cg.tempFromOperand(elem_ref, elem_dies); + try res.write(&elem, .{ .disp = @intCast(loaded_struct.offsets.get(ip)[field_index]) }, cg); + try elem.die(cg); + try cg.resetTemps(reset_index); + } } }, .@"packed" => return cg.fail("failed to select {s} {}", .{ @@ -163487,31 +163517,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 +163568,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 +169238,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 +169246,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 +169458,7 @@ fn store( .register, .register_offset, .lea_symbol, + .lea_pcrel, .lea_direct, .lea_got, .lea_frame, @@ -169414,6 +169466,7 @@ fn store( .memory, .indirect, .load_symbol, + .load_pcrel, .load_direct, .load_got, .load_frame, @@ -169883,6 +169936,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 +169946,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 +171606,8 @@ fn genBinOp( .register_mask, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -172740,6 +172796,7 @@ fn genBinOpMir( .lea_got, .lea_frame, .lea_symbol, + .lea_pcrel, .elementwise_args, .reserved_frame, .air_ref, @@ -172831,6 +172888,8 @@ fn genBinOpMir( .indirect, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -172906,7 +172965,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 +173012,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 +173153,8 @@ fn genBinOpMir( .indirect, .load_symbol, .lea_symbol, + .load_pcrel, + .lea_pcrel, .load_direct, .lea_direct, .load_got, @@ -173160,6 +173222,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 +173285,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 +173346,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 +173515,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 +174174,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 +174189,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 +174227,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 +174236,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 +174245,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 +174636,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 +174685,7 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) .memory, .load_symbol, + .load_pcrel, .load_got, .load_direct, => { @@ -174947,8 +175017,7 @@ fn lowerSwitchBr( ) !void { const zcu = cg.pt.zcu; const condition_ty = cg.typeOf(switch_br.operand); - const condition_int_info = cg.intInfo(condition_ty).?; - const condition_int_ty = try cg.pt.intType(condition_int_info.signedness, condition_int_info.bits); + const unsigned_condition_ty = try cg.pt.intType(.unsigned, cg.intInfo(condition_ty).?.bits); const ExpectedContents = extern struct { liveness_deaths: [1 << 8 | 1]Air.Inst.Index, @@ -175019,8 +175088,8 @@ fn lowerSwitchBr( .{ .air_ref = Air.internedToRef(min.?.toIntern()) }, ); const else_reloc = if (switch_br.else_body_len > 0) else_reloc: { - var cond_temp = try cg.tempInit(condition_ty, condition_index); - var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(condition_int_ty, table_len - 1)); + var cond_temp = try cg.tempInit(unsigned_condition_ty, condition_index); + var table_max_temp = try cg.tempFromValue(try cg.pt.intValue(unsigned_condition_ty, table_len - 1)); const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, cg) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, @@ -175348,8 +175417,7 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void { if (self.loop_switches.getPtr(br.block_inst)) |table| { const condition_ty = self.typeOf(br.operand); - const condition_int_info = self.intInfo(condition_ty).?; - const condition_int_ty = try self.pt.intType(condition_int_info.signedness, condition_int_info.bits); + const unsigned_condition_ty = try self.pt.intType(.unsigned, self.intInfo(condition_ty).?.bits); const condition_mcv = block_tracking.short; try self.spillEflagsIfOccupied(); if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir( @@ -175361,8 +175429,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void { switch (table.else_relocs) { .@"unreachable" => {}, .forward => |*else_relocs| { - var cond_temp = try self.tempInit(condition_ty, condition_mcv); - var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1)); + var cond_temp = try self.tempInit(unsigned_condition_ty, condition_mcv); + var table_max_temp = try self.tempFromValue(try self.pt.intValue(unsigned_condition_ty, table.len - 1)); const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, @@ -175373,8 +175441,8 @@ fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void { try cc_temp.die(self); }, .backward => |else_reloc| { - var cond_temp = try self.tempInit(condition_ty, condition_mcv); - var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1)); + var cond_temp = try self.tempInit(unsigned_condition_ty, condition_mcv); + var table_max_temp = try self.tempFromValue(try self.pt.intValue(unsigned_condition_ty, table.len - 1)); const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) { error.SelectFailed => unreachable, else => |e| return e, @@ -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 8ea392897193..d4116974cf77 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 @@ -378,9 +378,9 @@ pub fn emitMir(emit: *Emit) Error!void { }; break :stack_value &loc_buf[0]; } } }, - .pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ .addr = .{ - .sym = mir_inst.data.as.sym_index, - } } }, + .pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ + .addr_reloc = mir_inst.data.as.sym_index, + } }, .pseudo_dbg_local_aso => loc: { const sym_off = emit.lower.mir.extraData( bits.SymbolOffset, @@ -388,7 +388,7 @@ pub fn emitMir(emit: *Emit) Error!void { ).data; break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{ sym: { - loc_buf[0] = .{ .addr = .{ .sym = sym_off.sym_index } }; + loc_buf[0] = .{ .addr_reloc = sym_off.sym_index }; break :sym &loc_buf[0]; }, off: { @@ -437,7 +437,8 @@ pub fn emitMir(emit: *Emit) Error!void { .none => .{ .constu = 0 }, .reg => |reg| .{ .breg = reg.dwarfNum() }, .frame, .table, .rip_inst => unreachable, - .reloc => |sym_index| .{ .addr = .{ .sym = sym_index } }, + .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 7a69d58abc24..838f155d10db 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 83dbc855ec61..8d202e6baeb3 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 062a57981c4c..63b5d4b238c2 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 9cacd955d7b5..cb1272fba0a4 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 1f794bbeeae0..a2de3e2d01de 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 c68abc06cef0..3b8ab5298287 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 7790840d201e..3fc6250d3f32 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/libs/libcxx.zig b/src/libs/libcxx.zig index 43acb0e93a80..17a7d3d29ea6 100644 --- a/src/libs/libcxx.zig +++ b/src/libs/libcxx.zig @@ -308,7 +308,7 @@ pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError! assert(comp.libcxx_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxx_static_lib = crt_file; - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); } pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void { @@ -504,7 +504,7 @@ pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildErr assert(comp.libcxxabi_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxxabi_static_lib = crt_file; - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); } pub fn addCxxArgs( diff --git a/src/libs/libtsan.zig b/src/libs/libtsan.zig index 73c2c49f721c..8a5ffd2eab5b 100644 --- a/src/libs/libtsan.zig +++ b/src/libs/libtsan.zig @@ -325,7 +325,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: std.Progress.Node) BuildError!vo }; const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); assert(comp.tsan_lib == null); comp.tsan_lib = crt_file; } diff --git a/src/libs/libunwind.zig b/src/libs/libunwind.zig index 3c2e14bfc570..945689ebab16 100644 --- a/src/libs/libunwind.zig +++ b/src/libs/libunwind.zig @@ -195,7 +195,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: std.Progress.Node) BuildErr }; const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); assert(comp.libunwind_static_lib == null); comp.libunwind_static_lib = crt_file; } diff --git a/src/libs/musl.zig b/src/libs/musl.zig index 25f49eb91bf8..d208b0982747 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -278,7 +278,7 @@ pub fn buildCrtFile(comp: *Compilation, in_crt_file: CrtFile, prog_node: std.Pro errdefer comp.gpa.free(basename); const crt_file = try sub_compilation.toCrtFile(); - comp.queueLinkTaskMode(crt_file.full_object_path, output_mode); + comp.queueLinkTaskMode(crt_file.full_object_path, &config); { comp.mutex.lock(); defer comp.mutex.unlock(); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index ca90f016a60b..70405d8b5ec1 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1051,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), + ref.getSymbol(macho_file).?.getAddress(.{}, macho_file) + @as(i64, @intCast(reloc.target_off)), @intFromEnum(dwarf.address_size), ); } @@ -1085,13 +1085,19 @@ const ExternalReloc = struct { pub const Loc = union(enum) { empty, - addr: union(enum) { sym: u32 }, + addr_reloc: u32, + deref: *const Loc, constu: u64, consts: i64, plus: Bin, reg: u32, breg: u32, push_object_address, + call: struct { + args: []const Loc = &.{}, + unit: Unit.Index, + entry: Entry.Index, + }, form_tls_address: *const Loc, implicit_value: []const u8, stack_value: *const Loc, @@ -1136,11 +1142,13 @@ pub const Loc = union(enum) { const writer = adapter.writer(); switch (loc) { .empty => {}, - .addr => |addr| { + .addr_reloc => |sym_index| { try writer.writeByte(DW.OP.addr); - switch (addr) { - .sym => |sym_index| try adapter.addrSym(sym_index), - } + try adapter.addrSym(sym_index); + }, + .deref => |addr| { + try addr.write(adapter); + try writer.writeByte(DW.OP.deref); }, .constu => |constu| if (std.math.cast(u5, constu)) |lit| { try writer.writeByte(@as(u8, DW.OP.lit0) + lit); @@ -1225,6 +1233,11 @@ pub const Loc = union(enum) { try sleb128(writer, 0); }, .push_object_address => try writer.writeByte(DW.OP.push_object_address), + .call => |call| { + for (call.args) |arg| try arg.write(adapter); + try writer.writeByte(DW.OP.call_ref); + try adapter.infoEntry(call.unit, call.entry); + }, .form_tls_address => |addr| { try addr.write(adapter); try writer.writeByte(DW.OP.form_tls_address); @@ -1385,12 +1398,12 @@ pub const Cfa = union(enum) { }, .def_cfa_expression => |expr| { try writer.writeByte(DW.CFA.def_cfa_expression); - try wip_nav.frameExprloc(expr); + try wip_nav.frameExprLoc(expr); }, .expression => |reg_expr| { try writer.writeByte(DW.CFA.expression); try uleb128(writer, reg_expr.reg); - try wip_nav.frameExprloc(reg_expr.expr); + try wip_nav.frameExprLoc(reg_expr.expr); }, .val_offset => |reg_off| { const factored_off = @divExact(reg_off.off, wip_nav.dwarf.debug_frame.header.data_alignment_factor); @@ -1407,7 +1420,7 @@ pub const Cfa = union(enum) { .val_expression => |reg_expr| { try writer.writeByte(DW.CFA.val_expression); try uleb128(writer, reg_expr.reg); - try wip_nav.frameExprloc(reg_expr.expr); + try wip_nav.frameExprLoc(reg_expr.expr); }, .escape => |bytes| try writer.writeAll(bytes), } @@ -1471,7 +1484,7 @@ pub const WipNav = struct { }); try wip_nav.strp(name); try wip_nav.refType(ty); - try wip_nav.infoExprloc(loc); + try wip_nav.infoExprLoc(loc); wip_nav.any_children = true; } @@ -1741,7 +1754,7 @@ pub const WipNav = struct { } }; - fn infoExprloc(wip_nav: *WipNav, loc: Loc) UpdateError!void { + fn infoExprLoc(wip_nav: *WipNav, loc: Loc) UpdateError!void { var counter: ExprLocCounter = .init(wip_nav.dwarf); try loc.write(&counter); @@ -1773,7 +1786,7 @@ pub const WipNav = struct { try wip_nav.debug_info.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size)); } - fn frameExprloc(wip_nav: *WipNav, loc: Loc) UpdateError!void { + fn frameExprLoc(wip_nav: *WipNav, loc: Loc) UpdateError!void { var counter: ExprLocCounter = .init(wip_nav.dwarf); try loc.write(&counter); @@ -2384,7 +2397,8 @@ fn initWipNavInner( else => {}, } - const unit = try dwarf.getUnit(file.mod.?); + const mod = file.mod.?; + const unit = try dwarf.getUnit(mod); const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index); errdefer _ = if (!nav_gop.found_existing) dwarf.navs.pop(); if (nav_gop.found_existing) { @@ -2425,13 +2439,13 @@ fn initWipNavInner( }, &nav, inst_info.file, &decl); try wip_nav.strp(nav.fqn.toSlice(ip)); const ty: Type = nav_val.typeOf(zcu); - const addr: Loc = .{ .addr = .{ .sym = sym_index } }; + const addr: Loc = .{ .addr_reloc = sym_index }; const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr; switch (decl.kind) { .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable, .@"const" => { const const_ty_reloc_index = try wip_nav.refForward(); - try wip_nav.infoExprloc(loc); + try wip_nav.infoExprLoc(loc); try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse ty.abiAlignment(zcu).toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); @@ -2441,7 +2455,7 @@ fn initWipNavInner( }, .@"var" => { try wip_nav.refType(ty); - try wip_nav.infoExprloc(loc); + try wip_nav.infoExprLoc(loc); try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse ty.abiAlignment(zcu).toByteUnits().?); try diw.writeByte(@intFromBool(decl.linkage != .normal)); @@ -2512,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)), @@ -3838,7 +3852,7 @@ fn updateLazyValue( byte_offset += base_ptr.byte_offset; }; try wip_nav.abbrevCode(.location_comptime_value); - try wip_nav.infoExprloc(.{ .implicit_pointer = .{ + try wip_nav.infoExprLoc(.{ .implicit_pointer = .{ .unit = base_unit, .entry = base_entry, .offset = byte_offset, @@ -4360,7 +4374,7 @@ fn refAbbrevCode(dwarf: *Dwarf, abbrev_code: AbbrevCode) UpdateError!@typeInfo(A assert(abbrev_code != .null); const entry: Entry.Index = @enumFromInt(@intFromEnum(abbrev_code)); if (dwarf.debug_abbrev.section.getUnit(DebugAbbrev.unit).getEntry(entry).len > 0) return @intFromEnum(abbrev_code); - var debug_abbrev = std.ArrayList(u8).init(dwarf.gpa); + var debug_abbrev: std.ArrayList(u8) = .init(dwarf.gpa); defer debug_abbrev.deinit(); const daw = debug_abbrev.writer(); const abbrev = AbbrevCode.abbrevs.get(abbrev_code); @@ -4422,7 +4436,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { mod_info.root_dir_path = try dwarf.debug_line_str.addString(dwarf, root_dir_path); } - var header = std.ArrayList(u8).init(dwarf.gpa); + var header: std.ArrayList(u8) = .init(dwarf.gpa); defer header.deinit(); if (dwarf.debug_aranges.section.dirty) { for (dwarf.debug_aranges.section.units.items, 0..) |*unit_ptr, unit_index| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d947f1219b2e..1516993c7489 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 cb145f772c5d..0869d6582e2f 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 af7167095a06..13816940fe15 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -657,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. diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 338eff4e7bbf..69684724a5b4 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/main.zig b/src/main.zig index ade23312b7cc..20ccf4b7ec1c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -39,7 +39,7 @@ test { _ = Package; } -const thread_stack_size = 50 << 20; +const thread_stack_size = 60 << 20; pub const std_options: std.Options = .{ .wasiCwd = wasi_cwd, @@ -4208,7 +4208,7 @@ fn serve( const main_progress_node = std.Progress.start(.{}); const file_system_inputs = comp.file_system_inputs.?; - const IncrementalDebugServer = if (build_options.enable_debug_extensions) + const IncrementalDebugServer = if (build_options.enable_debug_extensions and !builtin.single_threaded) @import("IncrementalDebugServer.zig") else void; diff --git a/src/target.zig b/src/target.zig index bd12ca9e709b..247b783439fb 100644 --- a/src/target.zig +++ b/src/target.zig @@ -223,6 +223,10 @@ pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { /// than or equal to the number of behavior tests as the respective LLVM backend. pub fn selfHostedBackendIsAsRobustAsLlvm(target: std.Target) bool { if (target.cpu.arch.isSpirV()) return true; + if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) return switch (target.ofmt) { + .elf, .macho => true, + else => false, + }; return false; } diff --git a/test/behavior/math.zig b/test/behavior/math.zig index d77603f53744..d6d169b0ccf4 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1722,6 +1722,7 @@ test "signed zeros are represented properly" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; const S = struct { fn doTheTest() !void { diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index b518766d971d..4ab6362a4934 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -1056,3 +1056,12 @@ test "unlabeled break ignores switch" { }; try expect(result == 123); } + +test "switch on a signed value smaller than the smallest prong value" { + var v: i32 = undefined; + v = -1; + switch (v) { + inline 0...10 => return error.TestFailed, + else => {}, + } +} diff --git a/test/cases/compile_errors/@import_zon_bad_type.zig b/test/cases/compile_errors/@import_zon_bad_type.zig index d1ccfc312c9e..88bf6205dde4 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 c0990a9ed341..7bbb1d3c6e1d 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 4e35c4264e91..b5ffe15549c3 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 9b515762b115..2de29e8aaecd 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 b6d030686beb..19d8070f8092 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/link/bss/main.zig b/test/link/bss/main.zig index d2ecffe9822b..aaf865a0c82c 100644 --- a/test/link/bss/main.zig +++ b/test/link/bss/main.zig @@ -6,8 +6,9 @@ var buffer: [0x1000000]u64 = [1]u64{0} ** 0x1000000; pub fn main() anyerror!void { buffer[0x10] = 1; try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{ - buffer[0], - buffer[0x10], - buffer[0x1000000 - 1], + // workaround the dreaded decl_val + (&buffer)[0], + (&buffer)[0x10], + (&buffer)[0x1000000 - 1], }); } diff --git a/test/src/Debugger.zig b/test/src/Debugger.zig index ff0f2ae69cfe..08202f3130d3 100644 --- a/test/src/Debugger.zig +++ b/test/src/Debugger.zig @@ -9,7 +9,6 @@ pub const Options = struct { lldb: ?[]const u8, optimize_modes: []const std.builtin.OptimizeMode, skip_single_threaded: bool, - skip_non_native: bool, skip_libc: bool, }; diff --git a/test/standalone/mix_c_files/build.zig b/test/standalone/mix_c_files/build.zig index 32b1d57b5cb8..a7415b4f01db 100644 --- a/test/standalone/mix_c_files/build.zig +++ b/test/standalone/mix_c_files/build.zig @@ -24,6 +24,8 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .optimize = optimize, .link_libc = true, }), + // extern threadlocals are not implemented in the self-hosted linker + .use_llvm = true, }); exe.root_module.addCSourceFile(.{ .file = b.path("test.c"), .flags = &[_][]const u8{"-std=c11"} }); diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index b76cb6cecd4b..486eb96439cd 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -52,6 +52,8 @@ pub fn build(b: *std.Build) void { .unwind_tables = .@"async", .omit_frame_pointer = true, }), + // self-hosted lacks omit_frame_pointer support + .use_llvm = true, }); const run_cmd = b.addRunArtifact(exe); @@ -97,6 +99,8 @@ pub fn build(b: *std.Build) void { .unwind_tables = if (target.result.os.tag.isDarwin()) .@"async" else null, .omit_frame_pointer = true, }), + // zig objcopy doesn't support incremental binaries + .use_llvm = true, }); exe.linkLibrary(c_shared_lib); @@ -137,6 +141,8 @@ pub fn build(b: *std.Build) void { .unwind_tables = null, .omit_frame_pointer = false, }), + // self-hosted lacks omit_frame_pointer support + .use_llvm = true, }); // This "freestanding" binary is runnable because it invokes the diff --git a/test/tests.zig b/test/tests.zig index 810ed02bc183..ef0251482acb 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1113,8 +1113,6 @@ const test_targets = blk: { .os_tag = .linux, .abi = .none, }, - .use_llvm = false, - .use_lld = false, }, .{ .target = .{ @@ -1123,8 +1121,6 @@ const test_targets = blk: { .os_tag = .linux, .abi = .none, }, - .use_llvm = false, - .use_lld = false, .pic = true, }, .{ @@ -1134,8 +1130,6 @@ const test_targets = blk: { .os_tag = .linux, .abi = .none, }, - .use_llvm = false, - .use_lld = false, .strip = true, }, .{ @@ -1144,6 +1138,8 @@ const test_targets = blk: { .os_tag = .linux, .abi = .none, }, + .use_llvm = true, + .use_lld = true, }, .{ .target = .{ @@ -1602,7 +1598,9 @@ const c_abi_targets = blk: { break :blk [_]CAbiTarget{ // Native Targets - .{}, + .{ + .use_llvm = true, + }, // Linux Targets @@ -1841,7 +1839,6 @@ const c_abi_targets = blk: { .abi = .musl, }, .use_llvm = false, - .use_lld = false, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ @@ -1852,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"}, }, @@ -1864,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"}, }, @@ -1874,6 +1869,7 @@ const c_abi_targets = blk: { .os_tag = .linux, .abi = .musl, }, + .use_llvm = true, }, .{ .target = .{ @@ -1881,6 +1877,7 @@ const c_abi_targets = blk: { .os_tag = .linux, .abi = .muslx32, }, + .use_llvm = true, }, // WASI Targets @@ -2276,8 +2273,13 @@ const ModuleTestOptions = struct { include_paths: []const []const u8, skip_single_threaded: bool, skip_non_native: bool, + skip_freebsd: bool, + skip_netbsd: bool, + skip_windows: bool, + skip_macos: bool, + skip_linux: bool, + skip_llvm: bool, skip_libc: bool, - use_llvm: ?bool = null, max_rss: usize = 0, no_builtin: bool = false, build_options: ?*std.Build.Step.Options = null, @@ -2298,6 +2300,15 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { if (options.skip_non_native and !test_target.target.isNative()) continue; + if (options.skip_freebsd and test_target.target.os_tag == .freebsd) continue; + if (options.skip_netbsd and test_target.target.os_tag == .netbsd) continue; + if (options.skip_windows and test_target.target.os_tag == .windows) continue; + if (options.skip_macos and test_target.target.os_tag == .macos) continue; + if (options.skip_linux and test_target.target.os_tag == .linux) continue; + + const would_use_llvm = wouldUseLlvm(test_target.use_llvm, test_target.target, test_target.optimize_mode); + if (options.skip_llvm and would_use_llvm) continue; + const resolved_target = b.resolveTargetQuery(test_target.target); const target = resolved_target.result; const triple_txt = target.zigTriple(b.allocator) catch @panic("OOM"); @@ -2318,10 +2329,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { if (options.skip_single_threaded and test_target.single_threaded == true) continue; - if (options.use_llvm) |use_llvm| { - if (test_target.use_llvm != use_llvm) continue; - } - // TODO get compiler-rt tests passing for self-hosted backends. if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) @@ -2501,9 +2508,31 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { return step; } +fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool { + if (use_llvm) |x| return x; + if (query.ofmt == .c) return false; + switch (optimize_mode) { + .Debug => {}, + else => return true, + } + const cpu_arch = query.cpu_arch orelse builtin.cpu.arch; + switch (cpu_arch) { + .x86_64 => if (std.Target.ptrBitWidth_arch_abi(cpu_arch, query.abi orelse .none) != 64) return true, + .spirv, .spirv32, .spirv64 => return false, + else => return true, + } + return false; +} + const CAbiTestOptions = struct { test_target_filters: []const []const u8, skip_non_native: bool, + skip_freebsd: bool, + skip_netbsd: bool, + skip_windows: bool, + skip_macos: bool, + skip_linux: bool, + skip_llvm: bool, skip_release: bool, }; @@ -2517,6 +2546,14 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step { for (c_abi_targets) |c_abi_target| { if (options.skip_non_native and !c_abi_target.target.isNative()) continue; + if (options.skip_freebsd and c_abi_target.target.os_tag == .freebsd) continue; + if (options.skip_netbsd and c_abi_target.target.os_tag == .netbsd) continue; + if (options.skip_windows and c_abi_target.target.os_tag == .windows) continue; + if (options.skip_macos and c_abi_target.target.os_tag == .macos) continue; + if (options.skip_linux and c_abi_target.target.os_tag == .linux) continue; + + const would_use_llvm = wouldUseLlvm(c_abi_target.use_llvm, c_abi_target.target, .Debug); + if (options.skip_llvm and would_use_llvm) continue; const resolved_target = b.resolveTargetQuery(c_abi_target.target); const target = resolved_target.result; diff --git a/tools/doctest.zig b/tools/doctest.zig index fe0fb7c0df6a..86eb2009bde7 100644 --- a/tools/doctest.zig +++ b/tools/doctest.zig @@ -168,6 +168,15 @@ fn printOutput( try build_args.appendSlice(&[_][]const u8{ "-target", triple }); try shell_out.print("-target {s} ", .{triple}); } + if (code.use_llvm) |use_llvm| { + if (use_llvm) { + try build_args.append("-fllvm"); + try shell_out.print("-fllvm", .{}); + } else { + try build_args.append("-fno-llvm"); + try shell_out.print("-fno-llvm", .{}); + } + } if (code.verbose_cimport) { try build_args.append("--verbose-cimport"); try shell_out.print("--verbose-cimport ", .{}); @@ -224,7 +233,6 @@ fn printOutput( break :code_block; } } - const target_query = try std.Target.Query.parse(.{ .arch_os_abi = code.target_str orelse "native", }); @@ -319,6 +327,16 @@ fn printOutput( }, } } + if (code.use_llvm) |use_llvm| { + if (use_llvm) { + try test_args.append("-fllvm"); + try shell_out.print("-fllvm", .{}); + } else { + try test_args.append("-fno-llvm"); + try shell_out.print("-fno-llvm", .{}); + } + } + const result = run(arena, &env_map, tmp_dir_path, test_args.items) catch fatal("test failed", .{}); const escaped_stderr = try escapeHtml(arena, result.stderr); @@ -469,6 +487,15 @@ fn printOutput( try build_args.appendSlice(&[_][]const u8{ "-target", triple }); try shell_out.print("-target {s} ", .{triple}); } + if (code.use_llvm) |use_llvm| { + if (use_llvm) { + try build_args.append("-fllvm"); + try shell_out.print("-fllvm", .{}); + } else { + try build_args.append("-fno-llvm"); + try shell_out.print("-fno-llvm", .{}); + } + } for (code.additional_options) |option| { try build_args.append(option); try shell_out.print("{s} ", .{option}); @@ -538,6 +565,15 @@ fn printOutput( try test_args.appendSlice(&[_][]const u8{ "-target", triple }); try shell_out.print("-target {s} ", .{triple}); } + if (code.use_llvm) |use_llvm| { + if (use_llvm) { + try test_args.append("-fllvm"); + try shell_out.print("-fllvm", .{}); + } else { + try test_args.append("-fno-llvm"); + try shell_out.print("-fno-llvm", .{}); + } + } if (code.link_mode) |link_mode| { switch (link_mode) { .static => { @@ -827,6 +863,7 @@ const Code = struct { verbose_cimport: bool, just_check_syntax: bool, additional_options: []const []const u8, + use_llvm: ?bool, const Id = union(enum) { @"test", @@ -886,6 +923,7 @@ fn parseManifest(arena: Allocator, source_bytes: []const u8) !Code { var link_libc = false; var disable_cache = false; var verbose_cimport = false; + var use_llvm: ?bool = null; while (it.next()) |prefixed_line| { const line = skipPrefix(prefixed_line); @@ -901,6 +939,10 @@ fn parseManifest(arena: Allocator, source_bytes: []const u8) !Code { try additional_options.append(arena, line["additional_option=".len..]); } else if (mem.startsWith(u8, line, "target=")) { target_str = line["target=".len..]; + } else if (mem.eql(u8, line, "llvm=true")) { + use_llvm = true; + } else if (mem.eql(u8, line, "llvm=false")) { + use_llvm = false; } else if (mem.eql(u8, line, "link_libc")) { link_libc = true; } else if (mem.eql(u8, line, "disable_cache")) { @@ -923,6 +965,7 @@ fn parseManifest(arena: Allocator, source_bytes: []const u8) !Code { .disable_cache = disable_cache, .verbose_cimport = verbose_cimport, .just_check_syntax = just_check_syntax, + .use_llvm = use_llvm, }; }