| author | |
| committer | |
| log | 1e21876de25f5725dd093f2243bcfdb3f6c6c44e |
| tree | 1482b1d8b7cb025006c02b9705295d3e50cf44e1 |
| parent | 1cb8065a52c9bf74f913c46a011ab0118c92f0fa |
| parent | f559ea95b1c37fd6ede8fff6ffb2d74d5c2abc4e |
58 files changed, 2924 insertions(+), 1635 deletions(-)
CMakeLists.txt+3| ... | @@ -753,6 +753,9 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -753,6 +753,9 @@ set(ZIG_STAGE2_SOURCES |
| 753 | "${CMAKE_SOURCE_DIR}/src/link.zig" | 753 | "${CMAKE_SOURCE_DIR}/src/link.zig" |
| 754 | "${CMAKE_SOURCE_DIR}/src/link/C.zig" | 754 | "${CMAKE_SOURCE_DIR}/src/link/C.zig" |
| 755 | "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" | 755 | "${CMAKE_SOURCE_DIR}/src/link/Coff.zig" |
| 756 | "${CMAKE_SOURCE_DIR}/src/link/Coff/Atom.zig" | ||
| 757 | "${CMAKE_SOURCE_DIR}/src/link/Coff/Object.zig" | ||
| 758 | "${CMAKE_SOURCE_DIR}/src/link/Coff/lld.zig" | ||
| 756 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" | 759 | "${CMAKE_SOURCE_DIR}/src/link/Elf.zig" |
| 757 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" | 760 | "${CMAKE_SOURCE_DIR}/src/link/MachO.zig" |
| 758 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" | 761 | "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig" |
ci/azure/macos_script+1| ... | @@ -49,6 +49,7 @@ unset CXX | ... | @@ -49,6 +49,7 @@ unset CXX |
| 49 | make $JOBS install | 49 | make $JOBS install |
| 50 | 50 | ||
| 51 | stage3-release/bin/zig build test docs \ | 51 | stage3-release/bin/zig build test docs \ |
| 52 | --zig-lib-dir "$(pwd)/../lib" \ | ||
| 52 | -Denable-macos-sdk \ | 53 | -Denable-macos-sdk \ |
| 53 | -Dstatic-llvm \ | 54 | -Dstatic-llvm \ |
| 54 | --search-prefix "$PREFIX" | 55 | --search-prefix "$PREFIX" |
ci/drone/test_linux_behavior+5-4| ... | @@ -7,7 +7,8 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" | ... | @@ -7,7 +7,8 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" |
| 7 | ZIG="$INSTALL_PREFIX/bin/zig" | 7 | ZIG="$INSTALL_PREFIX/bin/zig" |
| 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 9 | 9 | ||
| 10 | $ZIG build test-behavior -Dskip-non-native | 10 | # https://github.com/ziglang/zig/issues/12689 |
| 11 | $ZIG build test-compiler-rt -Dskip-non-native | 11 | # $ZIG build test-behavior -Dskip-non-native --zig-lib-dir lib |
| 12 | $ZIG build test-fmt | 12 | $ZIG build test-compiler-rt -Dskip-non-native --zig-lib-dir lib |
| 13 | $ZIG build docs | 13 | $ZIG build test-fmt --zig-lib-dir lib |
| 14 | $ZIG build docs --zig-lib-dir lib |
ci/drone/test_linux_cases+3-2| ... | @@ -7,5 +7,6 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" | ... | @@ -7,5 +7,6 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" |
| 7 | ZIG="$INSTALL_PREFIX/bin/zig" | 7 | ZIG="$INSTALL_PREFIX/bin/zig" |
| 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 9 | 9 | ||
| 10 | $ZIG build -Dskip-non-native # test building self-hosted without LLVM | 10 | # test building self-hosted without LLVM |
| 11 | $ZIG build -Dskip-non-native test-cases | 11 | $ZIG build -Dskip-non-native --zig-lib-dir lib |
| 12 | $ZIG build test-cases -Dskip-non-native --zig-lib-dir lib |
ci/drone/test_linux_misc+8-7| ... | @@ -7,10 +7,11 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" | ... | @@ -7,10 +7,11 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" |
| 7 | ZIG="$INSTALL_PREFIX/bin/zig" | 7 | ZIG="$INSTALL_PREFIX/bin/zig" |
| 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 9 | 9 | ||
| 10 | $ZIG build test-universal-libc -Dskip-non-native | 10 | $ZIG build test-universal-libc -Dskip-non-native --zig-lib-dir lib |
| 11 | $ZIG build test-compare-output -Dskip-non-native | 11 | # https://github.com/ziglang/zig/issues/12689 |
| 12 | $ZIG build test-standalone -Dskip-non-native -Dskip-release-safe | 12 | # $ZIG build test-compare-output -Dskip-non-native --zig-lib-dir lib |
| 13 | $ZIG build test-stack-traces -Dskip-non-native | 13 | $ZIG build test-standalone -Dskip-non-native --zig-lib-dir lib -Dskip-release-safe |
| 14 | $ZIG build test-cli -Dskip-non-native | 14 | $ZIG build test-stack-traces -Dskip-non-native --zig-lib-dir lib |
| 15 | $ZIG build test-asm-link -Dskip-non-native | 15 | $ZIG build test-cli -Dskip-non-native --zig-lib-dir lib |
| 16 | $ZIG build test-translate-c -Dskip-non-native | 16 | $ZIG build test-asm-link -Dskip-non-native --zig-lib-dir lib |
| 17 | $ZIG build test-translate-c -Dskip-non-native --zig-lib-dir lib |
ci/drone/test_linux_std_Debug+6-1| ... | @@ -7,4 +7,9 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" | ... | @@ -7,4 +7,9 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" |
| 7 | ZIG="$INSTALL_PREFIX/bin/zig" | 7 | ZIG="$INSTALL_PREFIX/bin/zig" |
| 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 9 | 9 | ||
| 10 | $ZIG build test-std -Dskip-release-safe -Dskip-release-fast -Dskip-release-small -Dskip-non-native | 10 | $ZIG build test-std \ |
| 11 | --zig-lib-dir lib \ | ||
| 12 | -Dskip-release-safe \ | ||
| 13 | -Dskip-release-fast \ | ||
| 14 | -Dskip-release-small \ | ||
| 15 | -Dskip-non-native |
ci/drone/test_linux_std_ReleaseFast+7-1| ... | @@ -7,4 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" | ... | @@ -7,4 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" |
| 7 | ZIG="$INSTALL_PREFIX/bin/zig" | 7 | ZIG="$INSTALL_PREFIX/bin/zig" |
| 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 9 | 9 | ||
| 10 | $ZIG build test-std -Dskip-debug -Dskip-release-safe -Dskip-release-small -Dskip-non-native -Dskip-single-threaded | 10 | $ZIG build test-std \ |
| 11 | --zig-lib-dir lib \ | ||
| 12 | -Dskip-debug \ | ||
| 13 | -Dskip-release-safe \ | ||
| 14 | -Dskip-release-small \ | ||
| 15 | -Dskip-non-native \ | ||
| 16 | -Dskip-single-threaded |
ci/drone/test_linux_std_ReleaseSafe+7-1| ... | @@ -7,4 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" | ... | @@ -7,4 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" |
| 7 | ZIG="$INSTALL_PREFIX/bin/zig" | 7 | ZIG="$INSTALL_PREFIX/bin/zig" |
| 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | 8 | export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 9 | 9 | ||
| 10 | $ZIG build test-std -Dskip-debug -Dskip-release-fast -Dskip-release-small -Dskip-non-native -Dskip-single-threaded | 10 | $ZIG build test-std \ |
| 11 | --zig-lib-dir lib \ | ||
| 12 | -Dskip-debug \ | ||
| 13 | -Dskip-release-fast \ | ||
| 14 | -Dskip-release-small \ | ||
| 15 | -Dskip-non-native \ | ||
| 16 | -Dskip-single-threaded |
ci/drone/test_linux_std_ReleaseSmall+2-2| ... | @@ -12,5 +12,5 @@ export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" | ... | @@ -12,5 +12,5 @@ export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" |
| 12 | # of ReleaseSmall std lib tests. | 12 | # of ReleaseSmall std lib tests. |
| 13 | # $ZIG build test-std -Dskip-debug -Dskip-release-safe -Dskip-release-fast -Dskip-non-native | 13 | # $ZIG build test-std -Dskip-debug -Dskip-release-safe -Dskip-release-fast -Dskip-non-native |
| 14 | 14 | ||
| 15 | $ZIG test lib/std/std.zig -OReleaseSmall | 15 | $ZIG test lib/std/std.zig -OReleaseSmall --zig-lib-dir lib |
| 16 | $ZIG test lib/std/std.zig -OReleaseSmall -lc | 16 | $ZIG test lib/std/std.zig -OReleaseSmall -lc --zig-lib-dir lib |
ci/srht/freebsd_script+1| ... | @@ -56,6 +56,7 @@ ZIG_LIBC="$ZIG_LIBC_TXT" samu install | ... | @@ -56,6 +56,7 @@ ZIG_LIBC="$ZIG_LIBC_TXT" samu install |
| 56 | 56 | ||
| 57 | # Here we skip some tests to save time. | 57 | # Here we skip some tests to save time. |
| 58 | stage3/bin/zig build test docs \ | 58 | stage3/bin/zig build test docs \ |
| 59 | --zig-lib-dir "$(pwd)/../lib" \ | ||
| 59 | -Dstatic-llvm \ | 60 | -Dstatic-llvm \ |
| 60 | --search-prefix "$PREFIX" \ | 61 | --search-prefix "$PREFIX" \ |
| 61 | -Dskip-stage1 \ | 62 | -Dskip-stage1 \ |
ci/zinc/build_aarch64_macos+1-1| ... | @@ -16,5 +16,5 @@ SEARCH_PREFIX="/deps/$TARGET" | ... | @@ -16,5 +16,5 @@ SEARCH_PREFIX="/deps/$TARGET" |
| 16 | -Drelease \ | 16 | -Drelease \ |
| 17 | -Dstrip \ | 17 | -Dstrip \ |
| 18 | -Dtarget="$TARGET" \ | 18 | -Dtarget="$TARGET" \ |
| 19 | -Dmcpu="$MCPU" \ | 19 | -Dcpu="$MCPU" \ |
| 20 | -Denable-stage1 | 20 | -Denable-stage1 |
ci/zinc/linux_test_stage3_debug+2-1| ... | @@ -55,7 +55,8 @@ stage3/bin/zig build test \ | ... | @@ -55,7 +55,8 @@ stage3/bin/zig build test \ |
| 55 | -fwasmtime \ | 55 | -fwasmtime \ |
| 56 | -Dstatic-llvm \ | 56 | -Dstatic-llvm \ |
| 57 | -Dtarget=native-native-musl \ | 57 | -Dtarget=native-native-musl \ |
| 58 | --search-prefix "$DEPS_LOCAL" | 58 | --search-prefix "$DEPS_LOCAL" \ |
| 59 | --zig-lib-dir "$(pwd)/../lib" | ||
| 59 | 60 | ||
| 60 | # Explicit exit helps show last command duration. | 61 | # Explicit exit helps show last command duration. |
| 61 | exit | 62 | exit |
ci/zinc/linux_test_stage3_release+4-2| ... | @@ -40,13 +40,15 @@ ninja install | ... | @@ -40,13 +40,15 @@ ninja install |
| 40 | -fwasmtime \ | 40 | -fwasmtime \ |
| 41 | -Dstatic-llvm \ | 41 | -Dstatic-llvm \ |
| 42 | -Dtarget=native-native-musl \ | 42 | -Dtarget=native-native-musl \ |
| 43 | --search-prefix "$DEPS_LOCAL" | 43 | --search-prefix "$DEPS_LOCAL" \ |
| 44 | --zig-lib-dir "$(pwd)/../lib" | ||
| 44 | 45 | ||
| 45 | # Produce the experimental std lib documentation. | 46 | # Produce the experimental std lib documentation. |
| 46 | mkdir -p "$RELEASE_STAGING/docs/std" | 47 | mkdir -p "$RELEASE_STAGING/docs/std" |
| 47 | "$RELEASE_STAGING/bin/zig" test ../lib/std/std.zig \ | 48 | "$RELEASE_STAGING/bin/zig" test ../lib/std/std.zig \ |
| 48 | -femit-docs=$RELEASE_STAGING/docs/std \ | 49 | -femit-docs=$RELEASE_STAGING/docs/std \ |
| 49 | -fno-emit-bin | 50 | -fno-emit-bin \ |
| 51 | --zig-lib-dir "$(pwd)/../lib" | ||
| 50 | 52 | ||
| 51 | cp ../LICENSE $RELEASE_STAGING/ | 53 | cp ../LICENSE $RELEASE_STAGING/ |
| 52 | cp ../zig-cache/langref.html $RELEASE_STAGING/docs/ | 54 | cp ../zig-cache/langref.html $RELEASE_STAGING/docs/ |
lib/std/coff.zig+212-123| ... | @@ -256,20 +256,89 @@ pub const OptionalHeaderPE64 = extern struct { | ... | @@ -256,20 +256,89 @@ pub const OptionalHeaderPE64 = extern struct { |
| 256 | number_of_rva_and_sizes: u32, | 256 | number_of_rva_and_sizes: u32, |
| 257 | }; | 257 | }; |
| 258 | 258 | ||
| 259 | pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | ||
| 260 | |||
| 261 | pub const DirectoryEntry = enum(u16) { | ||
| 262 | /// Export Directory | ||
| 263 | EXPORT = 0, | ||
| 264 | |||
| 265 | /// Import Directory | ||
| 266 | IMPORT = 1, | ||
| 267 | |||
| 268 | /// Resource Directory | ||
| 269 | RESOURCE = 2, | ||
| 270 | |||
| 271 | /// Exception Directory | ||
| 272 | EXCEPTION = 3, | ||
| 273 | |||
| 274 | /// Security Directory | ||
| 275 | SECURITY = 4, | ||
| 276 | |||
| 277 | /// Base Relocation Table | ||
| 278 | BASERELOC = 5, | ||
| 279 | |||
| 280 | /// Debug Directory | ||
| 281 | DEBUG = 6, | ||
| 282 | |||
| 283 | /// Architecture Specific Data | ||
| 284 | ARCHITECTURE = 7, | ||
| 285 | |||
| 286 | /// RVA of GP | ||
| 287 | GLOBALPTR = 8, | ||
| 288 | |||
| 289 | /// TLS Directory | ||
| 290 | TLS = 9, | ||
| 291 | |||
| 292 | /// Load Configuration Directory | ||
| 293 | LOAD_CONFIG = 10, | ||
| 294 | |||
| 295 | /// Bound Import Directory in headers | ||
| 296 | BOUND_IMPORT = 11, | ||
| 297 | |||
| 298 | /// Import Address Table | ||
| 299 | IAT = 12, | ||
| 300 | |||
| 301 | /// Delay Load Import Descriptors | ||
| 302 | DELAY_IMPORT = 13, | ||
| 303 | |||
| 304 | /// COM Runtime descriptor | ||
| 305 | COM_DESCRIPTOR = 14, | ||
| 306 | }; | ||
| 307 | |||
| 308 | pub const ImageDataDirectory = extern struct { | ||
| 309 | virtual_address: u32, | ||
| 310 | size: u32, | ||
| 311 | }; | ||
| 312 | |||
| 259 | pub const DebugDirectoryEntry = extern struct { | 313 | pub const DebugDirectoryEntry = extern struct { |
| 260 | characteristiccs: u32, | 314 | characteristics: u32, |
| 261 | time_date_stamp: u32, | 315 | time_date_stamp: u32, |
| 262 | major_version: u16, | 316 | major_version: u16, |
| 263 | minor_version: u16, | 317 | minor_version: u16, |
| 264 | @"type": u32, | 318 | @"type": DebugType, |
| 265 | size_of_data: u32, | 319 | size_of_data: u32, |
| 266 | address_of_raw_data: u32, | 320 | address_of_raw_data: u32, |
| 267 | pointer_to_raw_data: u32, | 321 | pointer_to_raw_data: u32, |
| 268 | }; | 322 | }; |
| 269 | 323 | ||
| 270 | pub const ImageDataDirectory = extern struct { | 324 | pub const DebugType = enum(u32) { |
| 271 | virtual_address: u32, | 325 | UNKNOWN = 0, |
| 272 | size: u32, | 326 | COFF = 1, |
| 327 | CODEVIEW = 2, | ||
| 328 | FPO = 3, | ||
| 329 | MISC = 4, | ||
| 330 | EXCEPTION = 5, | ||
| 331 | FIXUP = 6, | ||
| 332 | OMAP_TO_SRC = 7, | ||
| 333 | OMAP_FROM_SRC = 8, | ||
| 334 | BORLAND = 9, | ||
| 335 | RESERVED10 = 10, | ||
| 336 | VC_FEATURE = 12, | ||
| 337 | POGO = 13, | ||
| 338 | ILTCG = 14, | ||
| 339 | MPX = 15, | ||
| 340 | REPRO = 16, | ||
| 341 | EX_DLLCHARACTERISTICS = 20, | ||
| 273 | }; | 342 | }; |
| 274 | 343 | ||
| 275 | pub const SectionHeader = extern struct { | 344 | pub const SectionHeader = extern struct { |
| ... | @@ -303,6 +372,15 @@ pub const SectionHeader = extern struct { | ... | @@ -303,6 +372,15 @@ pub const SectionHeader = extern struct { |
| 303 | return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable; | 372 | return std.math.powi(u16, 2, self.flags.ALIGN - 1) catch unreachable; |
| 304 | } | 373 | } |
| 305 | 374 | ||
| 375 | pub fn setAlignment(self: *SectionHeader, new_alignment: u16) void { | ||
| 376 | assert(new_alignment > 0 and new_alignment <= 8192); | ||
| 377 | self.flags.ALIGN = std.math.log2(new_alignment); | ||
| 378 | } | ||
| 379 | |||
| 380 | pub fn isCode(self: SectionHeader) bool { | ||
| 381 | return self.flags.CNT_CODE == 0b1; | ||
| 382 | } | ||
| 383 | |||
| 306 | pub fn isComdat(self: SectionHeader) bool { | 384 | pub fn isComdat(self: SectionHeader) bool { |
| 307 | return self.flags.LNK_COMDAT == 0b1; | 385 | return self.flags.LNK_COMDAT == 0b1; |
| 308 | } | 386 | } |
| ... | @@ -778,6 +856,21 @@ pub const MachineType = enum(u16) { | ... | @@ -778,6 +856,21 @@ pub const MachineType = enum(u16) { |
| 778 | /// MIPS little-endian WCE v2 | 856 | /// MIPS little-endian WCE v2 |
| 779 | WCEMIPSV2 = 0x169, | 857 | WCEMIPSV2 = 0x169, |
| 780 | 858 | ||
| 859 | pub fn fromTargetCpuArch(arch: std.Target.Cpu.Arch) MachineType { | ||
| 860 | return switch (arch) { | ||
| 861 | .arm => .ARM, | ||
| 862 | .powerpc => .POWERPC, | ||
| 863 | .riscv32 => .RISCV32, | ||
| 864 | .thumb => .Thumb, | ||
| 865 | .i386 => .I386, | ||
| 866 | .aarch64 => .ARM64, | ||
| 867 | .riscv64 => .RISCV64, | ||
| 868 | .x86_64 => .X64, | ||
| 869 | // there's cases we don't (yet) handle | ||
| 870 | else => unreachable, | ||
| 871 | }; | ||
| 872 | } | ||
| 873 | |||
| 781 | pub fn toTargetCpuArch(machine_type: MachineType) ?std.Target.Cpu.Arch { | 874 | pub fn toTargetCpuArch(machine_type: MachineType) ?std.Target.Cpu.Arch { |
| 782 | return switch (machine_type) { | 875 | return switch (machine_type) { |
| 783 | .ARM => .arm, | 876 | .ARM => .arm, |
| ... | @@ -794,10 +887,6 @@ pub const MachineType = enum(u16) { | ... | @@ -794,10 +887,6 @@ pub const MachineType = enum(u16) { |
| 794 | } | 887 | } |
| 795 | }; | 888 | }; |
| 796 | 889 | ||
| 797 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | ||
| 798 | const IMAGE_DEBUG_TYPE_CODEVIEW = 2; | ||
| 799 | const DEBUG_DIRECTORY = 6; | ||
| 800 | |||
| 801 | pub const CoffError = error{ | 890 | pub const CoffError = error{ |
| 802 | InvalidPEMagic, | 891 | InvalidPEMagic, |
| 803 | InvalidPEHeader, | 892 | InvalidPEHeader, |
| ... | @@ -831,7 +920,7 @@ pub const Coff = struct { | ... | @@ -831,7 +920,7 @@ pub const Coff = struct { |
| 831 | var stream = std.io.fixedBufferStream(self.data); | 920 | var stream = std.io.fixedBufferStream(self.data); |
| 832 | const reader = stream.reader(); | 921 | const reader = stream.reader(); |
| 833 | try stream.seekTo(pe_pointer_offset); | 922 | try stream.seekTo(pe_pointer_offset); |
| 834 | const coff_header_offset = try reader.readByte(); | 923 | const coff_header_offset = try reader.readIntLittle(u32); |
| 835 | try stream.seekTo(coff_header_offset); | 924 | try stream.seekTo(coff_header_offset); |
| 836 | var buf: [4]u8 = undefined; | 925 | var buf: [4]u8 = undefined; |
| 837 | try reader.readNoEof(&buf); | 926 | try reader.readNoEof(&buf); |
| ... | @@ -862,7 +951,7 @@ pub const Coff = struct { | ... | @@ -862,7 +951,7 @@ pub const Coff = struct { |
| 862 | }; | 951 | }; |
| 863 | 952 | ||
| 864 | const data_dirs = self.getDataDirectories(); | 953 | const data_dirs = self.getDataDirectories(); |
| 865 | const debug_dir = data_dirs[DEBUG_DIRECTORY]; | 954 | const debug_dir = data_dirs[@enumToInt(DirectoryEntry.DEBUG)]; |
| 866 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; | 955 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; |
| 867 | 956 | ||
| 868 | var stream = std.io.fixedBufferStream(self.data); | 957 | var stream = std.io.fixedBufferStream(self.data); |
| ... | @@ -875,7 +964,7 @@ pub const Coff = struct { | ... | @@ -875,7 +964,7 @@ pub const Coff = struct { |
| 875 | var i: u32 = 0; | 964 | var i: u32 = 0; |
| 876 | blk: while (i < debug_dir_entry_count) : (i += 1) { | 965 | blk: while (i < debug_dir_entry_count) : (i += 1) { |
| 877 | const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry); | 966 | const debug_dir_entry = try reader.readStruct(DebugDirectoryEntry); |
| 878 | if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) { | 967 | if (debug_dir_entry.type == .CODEVIEW) { |
| 879 | for (self.getSectionHeaders()) |*section| { | 968 | for (self.getSectionHeaders()) |*section| { |
| 880 | const section_start = section.virtual_address; | 969 | const section_start = section.virtual_address; |
| 881 | const section_size = section.virtual_size; | 970 | const section_size = section.virtual_size; |
| ... | @@ -1011,132 +1100,132 @@ pub const Coff = struct { | ... | @@ -1011,132 +1100,132 @@ pub const Coff = struct { |
| 1011 | mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]); | 1100 | mem.copy(u8, out_buff, self.data[sec.pointer_to_raw_data..][0..sec.virtual_size]); |
| 1012 | return out_buff; | 1101 | return out_buff; |
| 1013 | } | 1102 | } |
| 1103 | }; | ||
| 1014 | 1104 | ||
| 1015 | pub const Symtab = struct { | 1105 | pub const Symtab = struct { |
| 1016 | buffer: []const u8, | 1106 | buffer: []const u8, |
| 1017 | |||
| 1018 | fn len(self: Symtab) usize { | ||
| 1019 | return @divExact(self.buffer.len, Symbol.sizeOf()); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | const Tag = enum { | ||
| 1023 | symbol, | ||
| 1024 | func_def, | ||
| 1025 | debug_info, | ||
| 1026 | weak_ext, | ||
| 1027 | file_def, | ||
| 1028 | sect_def, | ||
| 1029 | }; | ||
| 1030 | 1107 | ||
| 1031 | const Record = union(Tag) { | 1108 | pub fn len(self: Symtab) usize { |
| 1032 | symbol: Symbol, | 1109 | return @divExact(self.buffer.len, Symbol.sizeOf()); |
| 1033 | debug_info: DebugInfoDefinition, | 1110 | } |
| 1034 | func_def: FunctionDefinition, | ||
| 1035 | weak_ext: WeakExternalDefinition, | ||
| 1036 | file_def: FileDefinition, | ||
| 1037 | sect_def: SectionDefinition, | ||
| 1038 | }; | ||
| 1039 | 1111 | ||
| 1040 | /// Lives as long as Symtab instance. | 1112 | pub const Tag = enum { |
| 1041 | fn at(self: Symtab, index: usize, tag: Tag) Record { | 1113 | symbol, |
| 1042 | const offset = index * Symbol.sizeOf(); | 1114 | func_def, |
| 1043 | const raw = self.buffer[offset..][0..Symbol.sizeOf()]; | 1115 | debug_info, |
| 1044 | return switch (tag) { | 1116 | weak_ext, |
| 1045 | .symbol => .{ .symbol = asSymbol(raw) }, | 1117 | file_def, |
| 1046 | .debug_info => .{ .debug_info = asDebugInfo(raw) }, | 1118 | sect_def, |
| 1047 | .func_def => .{ .func_def = asFuncDef(raw) }, | 1119 | }; |
| 1048 | .weak_ext => .{ .weak_ext = asWeakExtDef(raw) }, | ||
| 1049 | .file_def => .{ .file_def = asFileDef(raw) }, | ||
| 1050 | .sect_def => .{ .sect_def = asSectDef(raw) }, | ||
| 1051 | }; | ||
| 1052 | } | ||
| 1053 | 1120 | ||
| 1054 | fn asSymbol(raw: []const u8) Symbol { | 1121 | pub const Record = union(Tag) { |
| 1055 | return .{ | 1122 | symbol: Symbol, |
| 1056 | .name = raw[0..8].*, | 1123 | debug_info: DebugInfoDefinition, |
| 1057 | .value = mem.readIntLittle(u32, raw[8..12]), | 1124 | func_def: FunctionDefinition, |
| 1058 | .section_number = @intToEnum(SectionNumber, mem.readIntLittle(u16, raw[12..14])), | 1125 | weak_ext: WeakExternalDefinition, |
| 1059 | .@"type" = @bitCast(SymType, mem.readIntLittle(u16, raw[14..16])), | 1126 | file_def: FileDefinition, |
| 1060 | .storage_class = @intToEnum(StorageClass, raw[16]), | 1127 | sect_def: SectionDefinition, |
| 1061 | .number_of_aux_symbols = raw[17], | 1128 | }; |
| 1062 | }; | ||
| 1063 | } | ||
| 1064 | 1129 | ||
| 1065 | fn asDebugInfo(raw: []const u8) DebugInfoDefinition { | 1130 | /// Lives as long as Symtab instance. |
| 1066 | return .{ | 1131 | pub fn at(self: Symtab, index: usize, tag: Tag) Record { |
| 1067 | .unused_1 = raw[0..4].*, | 1132 | const offset = index * Symbol.sizeOf(); |
| 1068 | .linenumber = mem.readIntLittle(u16, raw[4..6]), | 1133 | const raw = self.buffer[offset..][0..Symbol.sizeOf()]; |
| 1069 | .unused_2 = raw[6..12].*, | 1134 | return switch (tag) { |
| 1070 | .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]), | 1135 | .symbol => .{ .symbol = asSymbol(raw) }, |
| 1071 | .unused_3 = raw[16..18].*, | 1136 | .debug_info => .{ .debug_info = asDebugInfo(raw) }, |
| 1072 | }; | 1137 | .func_def => .{ .func_def = asFuncDef(raw) }, |
| 1073 | } | 1138 | .weak_ext => .{ .weak_ext = asWeakExtDef(raw) }, |
| 1139 | .file_def => .{ .file_def = asFileDef(raw) }, | ||
| 1140 | .sect_def => .{ .sect_def = asSectDef(raw) }, | ||
| 1141 | }; | ||
| 1142 | } | ||
| 1074 | 1143 | ||
| 1075 | fn asFuncDef(raw: []const u8) FunctionDefinition { | 1144 | fn asSymbol(raw: []const u8) Symbol { |
| 1076 | return .{ | 1145 | return .{ |
| 1077 | .tag_index = mem.readIntLittle(u32, raw[0..4]), | 1146 | .name = raw[0..8].*, |
| 1078 | .total_size = mem.readIntLittle(u32, raw[4..8]), | 1147 | .value = mem.readIntLittle(u32, raw[8..12]), |
| 1079 | .pointer_to_linenumber = mem.readIntLittle(u32, raw[8..12]), | 1148 | .section_number = @intToEnum(SectionNumber, mem.readIntLittle(u16, raw[12..14])), |
| 1080 | .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]), | 1149 | .@"type" = @bitCast(SymType, mem.readIntLittle(u16, raw[14..16])), |
| 1081 | .unused = raw[16..18].*, | 1150 | .storage_class = @intToEnum(StorageClass, raw[16]), |
| 1082 | }; | 1151 | .number_of_aux_symbols = raw[17], |
| 1083 | } | 1152 | }; |
| 1153 | } | ||
| 1084 | 1154 | ||
| 1085 | fn asWeakExtDef(raw: []const u8) WeakExternalDefinition { | 1155 | fn asDebugInfo(raw: []const u8) DebugInfoDefinition { |
| 1086 | return .{ | 1156 | return .{ |
| 1087 | .tag_index = mem.readIntLittle(u32, raw[0..4]), | 1157 | .unused_1 = raw[0..4].*, |
| 1088 | .flag = @intToEnum(WeakExternalFlag, mem.readIntLittle(u32, raw[4..8])), | 1158 | .linenumber = mem.readIntLittle(u16, raw[4..6]), |
| 1089 | .unused = raw[8..18].*, | 1159 | .unused_2 = raw[6..12].*, |
| 1090 | }; | 1160 | .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]), |
| 1091 | } | 1161 | .unused_3 = raw[16..18].*, |
| 1162 | }; | ||
| 1163 | } | ||
| 1092 | 1164 | ||
| 1093 | fn asFileDef(raw: []const u8) FileDefinition { | 1165 | fn asFuncDef(raw: []const u8) FunctionDefinition { |
| 1094 | return .{ | 1166 | return .{ |
| 1095 | .file_name = raw[0..18].*, | 1167 | .tag_index = mem.readIntLittle(u32, raw[0..4]), |
| 1096 | }; | 1168 | .total_size = mem.readIntLittle(u32, raw[4..8]), |
| 1097 | } | 1169 | .pointer_to_linenumber = mem.readIntLittle(u32, raw[8..12]), |
| 1170 | .pointer_to_next_function = mem.readIntLittle(u32, raw[12..16]), | ||
| 1171 | .unused = raw[16..18].*, | ||
| 1172 | }; | ||
| 1173 | } | ||
| 1098 | 1174 | ||
| 1099 | fn asSectDef(raw: []const u8) SectionDefinition { | 1175 | fn asWeakExtDef(raw: []const u8) WeakExternalDefinition { |
| 1100 | return .{ | 1176 | return .{ |
| 1101 | .length = mem.readIntLittle(u32, raw[0..4]), | 1177 | .tag_index = mem.readIntLittle(u32, raw[0..4]), |
| 1102 | .number_of_relocations = mem.readIntLittle(u16, raw[4..6]), | 1178 | .flag = @intToEnum(WeakExternalFlag, mem.readIntLittle(u32, raw[4..8])), |
| 1103 | .number_of_linenumbers = mem.readIntLittle(u16, raw[6..8]), | 1179 | .unused = raw[8..18].*, |
| 1104 | .checksum = mem.readIntLittle(u32, raw[8..12]), | 1180 | }; |
| 1105 | .number = mem.readIntLittle(u16, raw[12..14]), | 1181 | } |
| 1106 | .selection = @intToEnum(ComdatSelection, raw[14]), | ||
| 1107 | .unused = raw[15..18].*, | ||
| 1108 | }; | ||
| 1109 | } | ||
| 1110 | 1182 | ||
| 1111 | const Slice = struct { | 1183 | fn asFileDef(raw: []const u8) FileDefinition { |
| 1112 | buffer: []const u8, | 1184 | return .{ |
| 1113 | num: usize, | 1185 | .file_name = raw[0..18].*, |
| 1114 | count: usize = 0, | ||
| 1115 | |||
| 1116 | /// Lives as long as Symtab instance. | ||
| 1117 | fn next(self: *Slice) ?Symbol { | ||
| 1118 | if (self.count >= self.num) return null; | ||
| 1119 | const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]); | ||
| 1120 | self.count += 1; | ||
| 1121 | self.buffer = self.buffer[Symbol.sizeOf()..]; | ||
| 1122 | return sym; | ||
| 1123 | } | ||
| 1124 | }; | 1186 | }; |
| 1187 | } | ||
| 1125 | 1188 | ||
| 1126 | fn slice(self: Symtab, start: usize, end: ?usize) Slice { | 1189 | fn asSectDef(raw: []const u8) SectionDefinition { |
| 1127 | const offset = start * Symbol.sizeOf(); | 1190 | return .{ |
| 1128 | const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len; | 1191 | .length = mem.readIntLittle(u32, raw[0..4]), |
| 1129 | const num = @divExact(llen - offset, Symbol.sizeOf()); | 1192 | .number_of_relocations = mem.readIntLittle(u16, raw[4..6]), |
| 1130 | return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num }; | 1193 | .number_of_linenumbers = mem.readIntLittle(u16, raw[6..8]), |
| 1131 | } | 1194 | .checksum = mem.readIntLittle(u32, raw[8..12]), |
| 1132 | }; | 1195 | .number = mem.readIntLittle(u16, raw[12..14]), |
| 1196 | .selection = @intToEnum(ComdatSelection, raw[14]), | ||
| 1197 | .unused = raw[15..18].*, | ||
| 1198 | }; | ||
| 1199 | } | ||
| 1133 | 1200 | ||
| 1134 | pub const Strtab = struct { | 1201 | pub const Slice = struct { |
| 1135 | buffer: []const u8, | 1202 | buffer: []const u8, |
| 1203 | num: usize, | ||
| 1204 | count: usize = 0, | ||
| 1136 | 1205 | ||
| 1137 | fn get(self: Strtab, off: u32) []const u8 { | 1206 | /// Lives as long as Symtab instance. |
| 1138 | assert(off < self.buffer.len); | 1207 | pub fn next(self: *Slice) ?Symbol { |
| 1139 | return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0); | 1208 | if (self.count >= self.num) return null; |
| 1209 | const sym = asSymbol(self.buffer[0..Symbol.sizeOf()]); | ||
| 1210 | self.count += 1; | ||
| 1211 | self.buffer = self.buffer[Symbol.sizeOf()..]; | ||
| 1212 | return sym; | ||
| 1140 | } | 1213 | } |
| 1141 | }; | 1214 | }; |
| 1215 | |||
| 1216 | pub fn slice(self: Symtab, start: usize, end: ?usize) Slice { | ||
| 1217 | const offset = start * Symbol.sizeOf(); | ||
| 1218 | const llen = if (end) |e| e * Symbol.sizeOf() else self.buffer.len; | ||
| 1219 | const num = @divExact(llen - offset, Symbol.sizeOf()); | ||
| 1220 | return Slice{ .buffer = self.buffer[offset..][0..llen], .num = num }; | ||
| 1221 | } | ||
| 1222 | }; | ||
| 1223 | |||
| 1224 | pub const Strtab = struct { | ||
| 1225 | buffer: []const u8, | ||
| 1226 | |||
| 1227 | pub fn get(self: Strtab, off: u32) []const u8 { | ||
| 1228 | assert(off < self.buffer.len); | ||
| 1229 | return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.ptr + off), 0); | ||
| 1230 | } | ||
| 1142 | }; | 1231 | }; |
lib/std/rand.zig+1-1| ... | @@ -343,7 +343,7 @@ pub const Random = struct { | ... | @@ -343,7 +343,7 @@ pub const Random = struct { |
| 343 | /// | 343 | /// |
| 344 | /// This is useful for selecting an item from a slice where weights are not equal. | 344 | /// This is useful for selecting an item from a slice where weights are not equal. |
| 345 | /// `T` must be a numeric type capable of holding the sum of `proportions`. | 345 | /// `T` must be a numeric type capable of holding the sum of `proportions`. |
| 346 | pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []T) usize { | 346 | pub fn weightedIndex(r: std.rand.Random, comptime T: type, proportions: []const T) usize { |
| 347 | // This implementation works by summing the proportions and picking a random | 347 | // This implementation works by summing the proportions and picking a random |
| 348 | // point in [0, sum). We then loop over the proportions, accumulating | 348 | // point in [0, sum). We then loop over the proportions, accumulating |
| 349 | // until our accumulator is greater than the random point. | 349 | // until our accumulator is greater than the random point. |
lib/std/rand/test.zig+1-1| ... | @@ -452,7 +452,7 @@ test "Random weightedIndex" { | ... | @@ -452,7 +452,7 @@ test "Random weightedIndex" { |
| 452 | var prng = DefaultPrng.init(0); | 452 | var prng = DefaultPrng.init(0); |
| 453 | const random = prng.random(); | 453 | const random = prng.random(); |
| 454 | 454 | ||
| 455 | var proportions = [_]T{ 2, 1, 1, 2 }; | 455 | const proportions = [_]T{ 2, 1, 1, 2 }; |
| 456 | var counts = [_]f64{ 0, 0, 0, 0 }; | 456 | var counts = [_]f64{ 0, 0, 0, 0 }; |
| 457 | 457 | ||
| 458 | const n_trials: u64 = 10_000; | 458 | const n_trials: u64 = 10_000; |
lib/std/start.zig-2| ... | @@ -36,8 +36,6 @@ comptime { | ... | @@ -36,8 +36,6 @@ comptime { |
| 36 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { | 36 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { |
| 37 | @export(main2, .{ .name = "main" }); | 37 | @export(main2, .{ .name = "main" }); |
| 38 | } | 38 | } |
| 39 | } else if (builtin.os.tag == .windows) { | ||
| 40 | @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); | ||
| 41 | } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { | 39 | } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) { |
| 42 | @export(wasiMain2, .{ .name = "_start" }); | 40 | @export(wasiMain2, .{ .name = "_start" }); |
| 43 | } else { | 41 | } else { |
src/AstGen.zig+22-11| ... | @@ -226,6 +226,8 @@ pub const ResultLoc = union(enum) { | ... | @@ -226,6 +226,8 @@ pub const ResultLoc = union(enum) { |
| 226 | ref, | 226 | ref, |
| 227 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. | 227 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. |
| 228 | ty: Zir.Inst.Ref, | 228 | ty: Zir.Inst.Ref, |
| 229 | /// Same as `ty` but for shift operands. | ||
| 230 | ty_shift_operand: Zir.Inst.Ref, | ||
| 229 | /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, | 231 | /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion, |
| 230 | /// so no `as` instruction needs to be emitted. | 232 | /// so no `as` instruction needs to be emitted. |
| 231 | coerced_ty: Zir.Inst.Ref, | 233 | coerced_ty: Zir.Inst.Ref, |
| ... | @@ -259,7 +261,7 @@ pub const ResultLoc = union(enum) { | ... | @@ -259,7 +261,7 @@ pub const ResultLoc = union(enum) { |
| 259 | fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy { | 261 | fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy { |
| 260 | switch (rl) { | 262 | switch (rl) { |
| 261 | // In this branch there will not be any store_to_block_ptr instructions. | 263 | // In this branch there will not be any store_to_block_ptr instructions. |
| 262 | .none, .ty, .coerced_ty, .ref => return .{ | 264 | .none, .ty, .ty_shift_operand, .coerced_ty, .ref => return .{ |
| 263 | .tag = .break_operand, | 265 | .tag = .break_operand, |
| 264 | .elide_store_to_block_ptr_instructions = false, | 266 | .elide_store_to_block_ptr_instructions = false, |
| 265 | }, | 267 | }, |
| ... | @@ -302,6 +304,14 @@ pub const ResultLoc = union(enum) { | ... | @@ -302,6 +304,14 @@ pub const ResultLoc = union(enum) { |
| 302 | else => rl, | 304 | else => rl, |
| 303 | }; | 305 | }; |
| 304 | } | 306 | } |
| 307 | |||
| 308 | fn zirTag(rl: ResultLoc) Zir.Inst.Tag { | ||
| 309 | return switch (rl) { | ||
| 310 | .ty => .as_node, | ||
| 311 | .ty_shift_operand => .as_shift_operand, | ||
| 312 | else => unreachable, | ||
| 313 | }; | ||
| 314 | } | ||
| 305 | }; | 315 | }; |
| 306 | 316 | ||
| 307 | pub const align_rl: ResultLoc = .{ .ty = .u29_type }; | 317 | pub const align_rl: ResultLoc = .{ .ty = .u29_type }; |
| ... | @@ -1385,7 +1395,7 @@ fn arrayInitExpr( | ... | @@ -1385,7 +1395,7 @@ fn arrayInitExpr( |
| 1385 | const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; | 1395 | const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; |
| 1386 | return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); | 1396 | return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); |
| 1387 | }, | 1397 | }, |
| 1388 | .ty, .coerced_ty => { | 1398 | .ty, .ty_shift_operand, .coerced_ty => { |
| 1389 | const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; | 1399 | const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon; |
| 1390 | const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); | 1400 | const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag); |
| 1391 | return rvalue(gz, rl, result, node); | 1401 | return rvalue(gz, rl, result, node); |
| ... | @@ -1631,7 +1641,7 @@ fn structInitExpr( | ... | @@ -1631,7 +1641,7 @@ fn structInitExpr( |
| 1631 | return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); | 1641 | return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); |
| 1632 | } | 1642 | } |
| 1633 | }, | 1643 | }, |
| 1634 | .ty, .coerced_ty => |ty_inst| { | 1644 | .ty, .ty_shift_operand, .coerced_ty => |ty_inst| { |
| 1635 | if (struct_init.ast.type_expr == 0) { | 1645 | if (struct_init.ast.type_expr == 0) { |
| 1636 | const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon); | 1646 | const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon); |
| 1637 | return rvalue(gz, rl, result, node); | 1647 | return rvalue(gz, rl, result, node); |
| ... | @@ -2327,6 +2337,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2327,6 +2337,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2327 | .anyframe_type, | 2337 | .anyframe_type, |
| 2328 | .as, | 2338 | .as, |
| 2329 | .as_node, | 2339 | .as_node, |
| 2340 | .as_shift_operand, | ||
| 2330 | .bit_and, | 2341 | .bit_and, |
| 2331 | .bitcast, | 2342 | .bitcast, |
| 2332 | .bit_or, | 2343 | .bit_or, |
| ... | @@ -2497,7 +2508,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2497,7 +2508,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2497 | .field_parent_ptr, | 2508 | .field_parent_ptr, |
| 2498 | .maximum, | 2509 | .maximum, |
| 2499 | .minimum, | 2510 | .minimum, |
| 2500 | .builtin_async_call, | ||
| 2501 | .c_import, | 2511 | .c_import, |
| 2502 | .@"resume", | 2512 | .@"resume", |
| 2503 | .@"await", | 2513 | .@"await", |
| ... | @@ -7278,7 +7288,7 @@ fn as( | ... | @@ -7278,7 +7288,7 @@ fn as( |
| 7278 | ) InnerError!Zir.Inst.Ref { | 7288 | ) InnerError!Zir.Inst.Ref { |
| 7279 | const dest_type = try typeExpr(gz, scope, lhs); | 7289 | const dest_type = try typeExpr(gz, scope, lhs); |
| 7280 | switch (rl) { | 7290 | switch (rl) { |
| 7281 | .none, .discard, .ref, .ty, .coerced_ty => { | 7291 | .none, .discard, .ref, .ty, .ty_shift_operand, .coerced_ty => { |
| 7282 | const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node); | 7292 | const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node); |
| 7283 | return rvalue(gz, rl, result, node); | 7293 | return rvalue(gz, rl, result, node); |
| 7284 | }, | 7294 | }, |
| ... | @@ -7959,7 +7969,8 @@ fn builtinCall( | ... | @@ -7959,7 +7969,8 @@ fn builtinCall( |
| 7959 | return rvalue(gz, rl, result, node); | 7969 | return rvalue(gz, rl, result, node); |
| 7960 | }, | 7970 | }, |
| 7961 | .async_call => { | 7971 | .async_call => { |
| 7962 | const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{ | 7972 | const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{ |
| 7973 | .node = gz.nodeIndexToRelative(node), | ||
| 7963 | .frame_buffer = try expr(gz, scope, .none, params[0]), | 7974 | .frame_buffer = try expr(gz, scope, .none, params[0]), |
| 7964 | .result_ptr = try expr(gz, scope, .none, params[1]), | 7975 | .result_ptr = try expr(gz, scope, .none, params[1]), |
| 7965 | .fn_ptr = try expr(gz, scope, .none, params[2]), | 7976 | .fn_ptr = try expr(gz, scope, .none, params[2]), |
| ... | @@ -8178,7 +8189,7 @@ fn shiftOp( | ... | @@ -8178,7 +8189,7 @@ fn shiftOp( |
| 8178 | ) InnerError!Zir.Inst.Ref { | 8189 | ) InnerError!Zir.Inst.Ref { |
| 8179 | const lhs = try expr(gz, scope, .none, lhs_node); | 8190 | const lhs = try expr(gz, scope, .none, lhs_node); |
| 8180 | const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); | 8191 | const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node); |
| 8181 | const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, rhs_node); | 8192 | const rhs = try expr(gz, scope, .{ .ty_shift_operand = log2_int_type }, rhs_node); |
| 8182 | const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ | 8193 | const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ |
| 8183 | .lhs = lhs, | 8194 | .lhs = lhs, |
| 8184 | .rhs = rhs, | 8195 | .rhs = rhs, |
| ... | @@ -9409,7 +9420,7 @@ fn rvalue( | ... | @@ -9409,7 +9420,7 @@ fn rvalue( |
| 9409 | } | 9420 | } |
| 9410 | return indexToRef(gop.value_ptr.*); | 9421 | return indexToRef(gop.value_ptr.*); |
| 9411 | }, | 9422 | }, |
| 9412 | .ty => |ty_inst| { | 9423 | .ty, .ty_shift_operand => |ty_inst| { |
| 9413 | // Quickly eliminate some common, unnecessary type coercion. | 9424 | // Quickly eliminate some common, unnecessary type coercion. |
| 9414 | const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32; | 9425 | const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32; |
| 9415 | const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32; | 9426 | const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32; |
| ... | @@ -9470,7 +9481,7 @@ fn rvalue( | ... | @@ -9470,7 +9481,7 @@ fn rvalue( |
| 9470 | => return result, // type of result is already correct | 9481 | => return result, // type of result is already correct |
| 9471 | 9482 | ||
| 9472 | // Need an explicit type coercion instruction. | 9483 | // Need an explicit type coercion instruction. |
| 9473 | else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{ | 9484 | else => return gz.addPlNode(rl.zirTag(), src_node, Zir.Inst.As{ |
| 9474 | .dest_type = ty_inst, | 9485 | .dest_type = ty_inst, |
| 9475 | .operand = result, | 9486 | .operand = result, |
| 9476 | }), | 9487 | }), |
| ... | @@ -10350,7 +10361,7 @@ const GenZir = struct { | ... | @@ -10350,7 +10361,7 @@ const GenZir = struct { |
| 10350 | // we emit ZIR for the block break instructions to have the result values, | 10361 | // we emit ZIR for the block break instructions to have the result values, |
| 10351 | // and then rvalue() on that to pass the value to the result location. | 10362 | // and then rvalue() on that to pass the value to the result location. |
| 10352 | switch (parent_rl) { | 10363 | switch (parent_rl) { |
| 10353 | .ty, .coerced_ty => |ty_inst| { | 10364 | .ty, .ty_shift_operand, .coerced_ty => |ty_inst| { |
| 10354 | gz.rl_ty_inst = ty_inst; | 10365 | gz.rl_ty_inst = ty_inst; |
| 10355 | gz.break_result_loc = parent_rl; | 10366 | gz.break_result_loc = parent_rl; |
| 10356 | }, | 10367 | }, |
| ... | @@ -11506,7 +11517,7 @@ const GenZir = struct { | ... | @@ -11506,7 +11517,7 @@ const GenZir = struct { |
| 11506 | fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void { | 11517 | fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void { |
| 11507 | switch (rl) { | 11518 | switch (rl) { |
| 11508 | .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node), | 11519 | .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node), |
| 11509 | .ty => _ = try gz.addUnNode(.ret_node, operand, node), | 11520 | .ty, .ty_shift_operand => _ = try gz.addUnNode(.ret_node, operand, node), |
| 11510 | else => unreachable, | 11521 | else => unreachable, |
| 11511 | } | 11522 | } |
| 11512 | } | 11523 | } |
src/Autodoc.zig+1-1| ... | @@ -1888,7 +1888,7 @@ fn walkInstruction( | ... | @@ -1888,7 +1888,7 @@ fn walkInstruction( |
| 1888 | .expr = .{ .typeInfo = operand_index }, | 1888 | .expr = .{ .typeInfo = operand_index }, |
| 1889 | }; | 1889 | }; |
| 1890 | }, | 1890 | }, |
| 1891 | .as_node => { | 1891 | .as_node, .as_shift_operand => { |
| 1892 | const pl_node = data[inst_index].pl_node; | 1892 | const pl_node = data[inst_index].pl_node; |
| 1893 | const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index); | 1893 | const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index); |
| 1894 | const dest_type_walk = try self.walkRef( | 1894 | const dest_type_walk = try self.walkRef( |
src/Compilation.zig+19-18| ... | @@ -1127,7 +1127,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1127,7 +1127,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1127 | link_eh_frame_hdr or | 1127 | link_eh_frame_hdr or |
| 1128 | options.link_emit_relocs or | 1128 | options.link_emit_relocs or |
| 1129 | options.output_mode == .Lib or | 1129 | options.output_mode == .Lib or |
| 1130 | options.image_base_override != null or | ||
| 1131 | options.linker_script != null or options.version_script != null or | 1130 | options.linker_script != null or options.version_script != null or |
| 1132 | options.emit_implib != null or | 1131 | options.emit_implib != null or |
| 1133 | build_id) | 1132 | build_id) |
| ... | @@ -4767,6 +4766,24 @@ pub fn dump_argv(argv: []const []const u8) void { | ... | @@ -4767,6 +4766,24 @@ pub fn dump_argv(argv: []const []const u8) void { |
| 4767 | std.debug.print("{s}\n", .{argv[argv.len - 1]}); | 4766 | std.debug.print("{s}\n", .{argv[argv.len - 1]}); |
| 4768 | } | 4767 | } |
| 4769 | 4768 | ||
| 4769 | pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend { | ||
| 4770 | const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1; | ||
| 4771 | if (use_stage1) return .stage1; | ||
| 4772 | if (build_options.have_llvm and comp.bin_file.options.use_llvm) return .stage2_llvm; | ||
| 4773 | const target = comp.bin_file.options.target; | ||
| 4774 | if (target.ofmt == .c) return .stage2_c; | ||
| 4775 | return switch (target.cpu.arch) { | ||
| 4776 | .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm, | ||
| 4777 | .arm, .armeb, .thumb, .thumbeb => .stage2_arm, | ||
| 4778 | .x86_64 => .stage2_x86_64, | ||
| 4779 | .i386 => .stage2_x86, | ||
| 4780 | .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, | ||
| 4781 | .riscv64 => .stage2_riscv64, | ||
| 4782 | .sparc64 => .stage2_sparc64, | ||
| 4783 | else => .other, | ||
| 4784 | }; | ||
| 4785 | } | ||
| 4786 | |||
| 4770 | pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 { | 4787 | pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Allocator.Error![:0]u8 { |
| 4771 | const tracy_trace = trace(@src()); | 4788 | const tracy_trace = trace(@src()); |
| 4772 | defer tracy_trace.end(); | 4789 | defer tracy_trace.end(); |
| ... | @@ -4776,23 +4793,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca | ... | @@ -4776,23 +4793,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca |
| 4776 | 4793 | ||
| 4777 | const target = comp.getTarget(); | 4794 | const target = comp.getTarget(); |
| 4778 | const generic_arch_name = target.cpu.arch.genericName(); | 4795 | const generic_arch_name = target.cpu.arch.genericName(); |
| 4779 | const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1; | 4796 | const zig_backend = comp.getZigBackend(); |
| 4780 | |||
| 4781 | const zig_backend: std.builtin.CompilerBackend = blk: { | ||
| 4782 | if (use_stage1) break :blk .stage1; | ||
| 4783 | if (build_options.have_llvm and comp.bin_file.options.use_llvm) break :blk .stage2_llvm; | ||
| 4784 | if (target.ofmt == .c) break :blk .stage2_c; | ||
| 4785 | break :blk switch (target.cpu.arch) { | ||
| 4786 | .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm, | ||
| 4787 | .arm, .armeb, .thumb, .thumbeb => .stage2_arm, | ||
| 4788 | .x86_64 => .stage2_x86_64, | ||
| 4789 | .i386 => .stage2_x86, | ||
| 4790 | .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64, | ||
| 4791 | .riscv64 => .stage2_riscv64, | ||
| 4792 | .sparc64 => .stage2_sparc64, | ||
| 4793 | else => .other, | ||
| 4794 | }; | ||
| 4795 | }; | ||
| 4796 | 4797 | ||
| 4797 | @setEvalBranchQuota(4000); | 4798 | @setEvalBranchQuota(4000); |
| 4798 | try buffer.writer().print( | 4799 | try buffer.writer().print( |
src/Module.zig+7-4| ... | @@ -5259,9 +5259,9 @@ pub fn clearDecl( | ... | @@ -5259,9 +5259,9 @@ pub fn clearDecl( |
| 5259 | // TODO instead of a union, put this memory trailing Decl objects, | 5259 | // TODO instead of a union, put this memory trailing Decl objects, |
| 5260 | // and allow it to be variably sized. | 5260 | // and allow it to be variably sized. |
| 5261 | decl.link = switch (mod.comp.bin_file.tag) { | 5261 | decl.link = switch (mod.comp.bin_file.tag) { |
| 5262 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, | 5262 | .coff => .{ .coff = link.File.Coff.Atom.empty }, |
| 5263 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, | 5263 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 5264 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, | 5264 | .macho => .{ .macho = link.File.MachO.Atom.empty }, |
| 5265 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, | 5265 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, |
| 5266 | .c => .{ .c = {} }, | 5266 | .c => .{ .c = {} }, |
| 5267 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, | 5267 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, |
| ... | @@ -5391,6 +5391,9 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { | ... | @@ -5391,6 +5391,9 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { |
| 5391 | if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| { | 5391 | if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| { |
| 5392 | wasm.deleteExport(exp.link.wasm); | 5392 | wasm.deleteExport(exp.link.wasm); |
| 5393 | } | 5393 | } |
| 5394 | if (mod.comp.bin_file.cast(link.File.Coff)) |coff| { | ||
| 5395 | coff.deleteExport(exp.link.coff); | ||
| 5396 | } | ||
| 5394 | if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { | 5397 | if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { |
| 5395 | failed_kv.value.destroy(mod.gpa); | 5398 | failed_kv.value.destroy(mod.gpa); |
| 5396 | } | 5399 | } |
| ... | @@ -5680,9 +5683,9 @@ pub fn allocateNewDecl( | ... | @@ -5680,9 +5683,9 @@ pub fn allocateNewDecl( |
| 5680 | .zir_decl_index = 0, | 5683 | .zir_decl_index = 0, |
| 5681 | .src_scope = src_scope, | 5684 | .src_scope = src_scope, |
| 5682 | .link = switch (mod.comp.bin_file.tag) { | 5685 | .link = switch (mod.comp.bin_file.tag) { |
| 5683 | .coff => .{ .coff = link.File.Coff.TextBlock.empty }, | 5686 | .coff => .{ .coff = link.File.Coff.Atom.empty }, |
| 5684 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, | 5687 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 5685 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, | 5688 | .macho => .{ .macho = link.File.MachO.Atom.empty }, |
| 5686 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, | 5689 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, |
| 5687 | .c => .{ .c = {} }, | 5690 | .c => .{ .c = {} }, |
| 5688 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, | 5691 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, |
src/Sema.zig+135-40| ... | @@ -712,6 +712,7 @@ fn analyzeBodyInner( | ... | @@ -712,6 +712,7 @@ fn analyzeBodyInner( |
| 712 | .vector_type => try sema.zirVectorType(block, inst), | 712 | .vector_type => try sema.zirVectorType(block, inst), |
| 713 | .as => try sema.zirAs(block, inst), | 713 | .as => try sema.zirAs(block, inst), |
| 714 | .as_node => try sema.zirAsNode(block, inst), | 714 | .as_node => try sema.zirAsNode(block, inst), |
| 715 | .as_shift_operand => try sema.zirAsShiftOperand(block, inst), | ||
| 715 | .bit_and => try sema.zirBitwise(block, inst, .bit_and), | 716 | .bit_and => try sema.zirBitwise(block, inst, .bit_and), |
| 716 | .bit_not => try sema.zirBitNot(block, inst), | 717 | .bit_not => try sema.zirBitNot(block, inst), |
| 717 | .bit_or => try sema.zirBitwise(block, inst, .bit_or), | 718 | .bit_or => try sema.zirBitwise(block, inst, .bit_or), |
| ... | @@ -848,7 +849,6 @@ fn analyzeBodyInner( | ... | @@ -848,7 +849,6 @@ fn analyzeBodyInner( |
| 848 | .mul_add => try sema.zirMulAdd(block, inst), | 849 | .mul_add => try sema.zirMulAdd(block, inst), |
| 849 | .builtin_call => try sema.zirBuiltinCall(block, inst), | 850 | .builtin_call => try sema.zirBuiltinCall(block, inst), |
| 850 | .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), | 851 | .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), |
| 851 | .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), | ||
| 852 | .@"resume" => try sema.zirResume(block, inst), | 852 | .@"resume" => try sema.zirResume(block, inst), |
| 853 | .@"await" => try sema.zirAwait(block, inst), | 853 | .@"await" => try sema.zirAwait(block, inst), |
| 854 | .array_base_ptr => try sema.zirArrayBasePtr(block, inst), | 854 | .array_base_ptr => try sema.zirArrayBasePtr(block, inst), |
| ... | @@ -956,6 +956,7 @@ fn analyzeBodyInner( | ... | @@ -956,6 +956,7 @@ fn analyzeBodyInner( |
| 956 | .error_to_int => try sema.zirErrorToInt( block, extended), | 956 | .error_to_int => try sema.zirErrorToInt( block, extended), |
| 957 | .int_to_error => try sema.zirIntToError( block, extended), | 957 | .int_to_error => try sema.zirIntToError( block, extended), |
| 958 | .reify => try sema.zirReify( block, extended, inst), | 958 | .reify => try sema.zirReify( block, extended, inst), |
| 959 | .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), | ||
| 959 | // zig fmt: on | 960 | // zig fmt: on |
| 960 | .fence => { | 961 | .fence => { |
| 961 | try sema.zirFence(block, extended); | 962 | try sema.zirFence(block, extended); |
| ... | @@ -5076,7 +5077,7 @@ pub fn analyzeExport( | ... | @@ -5076,7 +5077,7 @@ pub fn analyzeExport( |
| 5076 | }, | 5077 | }, |
| 5077 | .src = src, | 5078 | .src = src, |
| 5078 | .link = switch (mod.comp.bin_file.tag) { | 5079 | .link = switch (mod.comp.bin_file.tag) { |
| 5079 | .coff => .{ .coff = {} }, | 5080 | .coff => .{ .coff = .{} }, |
| 5080 | .elf => .{ .elf = .{} }, | 5081 | .elf => .{ .elf = .{} }, |
| 5081 | .macho => .{ .macho = .{} }, | 5082 | .macho => .{ .macho = .{} }, |
| 5082 | .plan9 => .{ .plan9 = null }, | 5083 | .plan9 => .{ .plan9 = null }, |
| ... | @@ -6152,9 +6153,30 @@ fn analyzeCall( | ... | @@ -6152,9 +6153,30 @@ fn analyzeCall( |
| 6152 | if (ensure_result_used) { | 6153 | if (ensure_result_used) { |
| 6153 | try sema.ensureResultUsed(block, result, call_src); | 6154 | try sema.ensureResultUsed(block, result, call_src); |
| 6154 | } | 6155 | } |
| 6156 | if (call_tag == .call_always_tail) { | ||
| 6157 | return sema.handleTailCall(block, call_src, func_ty, result); | ||
| 6158 | } | ||
| 6155 | return result; | 6159 | return result; |
| 6156 | } | 6160 | } |
| 6157 | 6161 | ||
| 6162 | fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { | ||
| 6163 | const target = sema.mod.getTarget(); | ||
| 6164 | const backend = sema.mod.comp.getZigBackend(); | ||
| 6165 | if (!target_util.supportsTailCall(target, backend)) { | ||
| 6166 | return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{ | ||
| 6167 | @tagName(backend), @tagName(target.cpu.arch), | ||
| 6168 | }); | ||
| 6169 | } | ||
| 6170 | const func_decl = sema.mod.declPtr(sema.owner_func.?.owner_decl); | ||
| 6171 | if (!func_ty.eql(func_decl.ty, sema.mod)) { | ||
| 6172 | return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{}' does not match type of calling function '{}'", .{ | ||
| 6173 | func_ty.fmt(sema.mod), func_decl.ty.fmt(sema.mod), | ||
| 6174 | }); | ||
| 6175 | } | ||
| 6176 | _ = try block.addUnOp(.ret, result); | ||
| 6177 | return Air.Inst.Ref.unreachable_value; | ||
| 6178 | } | ||
| 6179 | |||
| 6158 | fn analyzeInlineCallArg( | 6180 | fn analyzeInlineCallArg( |
| 6159 | sema: *Sema, | 6181 | sema: *Sema, |
| 6160 | arg_block: *Block, | 6182 | arg_block: *Block, |
| ... | @@ -6670,7 +6692,8 @@ fn instantiateGenericCall( | ... | @@ -6670,7 +6692,8 @@ fn instantiateGenericCall( |
| 6670 | try sema.requireFunctionBlock(block, call_src); | 6692 | try sema.requireFunctionBlock(block, call_src); |
| 6671 | 6693 | ||
| 6672 | const comptime_args = callee.comptime_args.?; | 6694 | const comptime_args = callee.comptime_args.?; |
| 6673 | const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo(); | 6695 | const func_ty = mod.declPtr(callee.owner_decl).ty; |
| 6696 | const new_fn_info = func_ty.fnInfo(); | ||
| 6674 | const runtime_args_len = @intCast(u32, new_fn_info.param_types.len); | 6697 | const runtime_args_len = @intCast(u32, new_fn_info.param_types.len); |
| 6675 | const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); | 6698 | const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len); |
| 6676 | { | 6699 | { |
| ... | @@ -6717,7 +6740,7 @@ fn instantiateGenericCall( | ... | @@ -6717,7 +6740,7 @@ fn instantiateGenericCall( |
| 6717 | 6740 | ||
| 6718 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + | 6741 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 6719 | runtime_args_len); | 6742 | runtime_args_len); |
| 6720 | const func_inst = try block.addInst(.{ | 6743 | const result = try block.addInst(.{ |
| 6721 | .tag = call_tag, | 6744 | .tag = call_tag, |
| 6722 | .data = .{ .pl_op = .{ | 6745 | .data = .{ .pl_op = .{ |
| 6723 | .operand = callee_inst, | 6746 | .operand = callee_inst, |
| ... | @@ -6729,9 +6752,12 @@ fn instantiateGenericCall( | ... | @@ -6729,9 +6752,12 @@ fn instantiateGenericCall( |
| 6729 | sema.appendRefsAssumeCapacity(runtime_args); | 6752 | sema.appendRefsAssumeCapacity(runtime_args); |
| 6730 | 6753 | ||
| 6731 | if (ensure_result_used) { | 6754 | if (ensure_result_used) { |
| 6732 | try sema.ensureResultUsed(block, func_inst, call_src); | 6755 | try sema.ensureResultUsed(block, result, call_src); |
| 6733 | } | 6756 | } |
| 6734 | return func_inst; | 6757 | if (call_tag == .call_always_tail) { |
| 6758 | return sema.handleTailCall(block, call_src, func_ty, result); | ||
| 6759 | } | ||
| 6760 | return result; | ||
| 6735 | } | 6761 | } |
| 6736 | 6762 | ||
| 6737 | fn emitDbgInline( | 6763 | fn emitDbgInline( |
| ... | @@ -8177,8 +8203,22 @@ fn zirParam( | ... | @@ -8177,8 +8203,22 @@ fn zirParam( |
| 8177 | .is_comptime = comptime_syntax, | 8203 | .is_comptime = comptime_syntax, |
| 8178 | .name = param_name, | 8204 | .name = param_name, |
| 8179 | }); | 8205 | }); |
| 8180 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); | 8206 | |
| 8181 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); | 8207 | if (is_comptime) { |
| 8208 | // If this is a comptime parameter we can add a constant generic_poison | ||
| 8209 | // since this is also a generic parameter. | ||
| 8210 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); | ||
| 8211 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); | ||
| 8212 | } else { | ||
| 8213 | // Otherwise we need a dummy runtime instruction. | ||
| 8214 | const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len); | ||
| 8215 | try sema.air_instructions.append(sema.gpa, .{ | ||
| 8216 | .tag = .alloc, | ||
| 8217 | .data = .{ .ty = param_ty }, | ||
| 8218 | }); | ||
| 8219 | const result = Air.indexToRef(result_index); | ||
| 8220 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); | ||
| 8221 | } | ||
| 8182 | } | 8222 | } |
| 8183 | 8223 | ||
| 8184 | fn zirParamAnytype( | 8224 | fn zirParamAnytype( |
| ... | @@ -8225,7 +8265,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst | ... | @@ -8225,7 +8265,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst |
| 8225 | defer tracy.end(); | 8265 | defer tracy.end(); |
| 8226 | 8266 | ||
| 8227 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | 8267 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; |
| 8228 | return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs); | 8268 | return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false); |
| 8229 | } | 8269 | } |
| 8230 | 8270 | ||
| 8231 | fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8271 | fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -8235,7 +8275,17 @@ fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -8235,7 +8275,17 @@ fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 8235 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 8275 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 8236 | const src = inst_data.src(); | 8276 | const src = inst_data.src(); |
| 8237 | const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; | 8277 | const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; |
| 8238 | return sema.analyzeAs(block, src, extra.dest_type, extra.operand); | 8278 | return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false); |
| 8279 | } | ||
| 8280 | |||
| 8281 | fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 8282 | const tracy = trace(@src()); | ||
| 8283 | defer tracy.end(); | ||
| 8284 | |||
| 8285 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 8286 | const src = inst_data.src(); | ||
| 8287 | const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; | ||
| 8288 | return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true); | ||
| 8239 | } | 8289 | } |
| 8240 | 8290 | ||
| 8241 | fn analyzeAs( | 8291 | fn analyzeAs( |
| ... | @@ -8244,6 +8294,7 @@ fn analyzeAs( | ... | @@ -8244,6 +8294,7 @@ fn analyzeAs( |
| 8244 | src: LazySrcLoc, | 8294 | src: LazySrcLoc, |
| 8245 | zir_dest_type: Zir.Inst.Ref, | 8295 | zir_dest_type: Zir.Inst.Ref, |
| 8246 | zir_operand: Zir.Inst.Ref, | 8296 | zir_operand: Zir.Inst.Ref, |
| 8297 | no_cast_to_comptime_int: bool, | ||
| 8247 | ) CompileError!Air.Inst.Ref { | 8298 | ) CompileError!Air.Inst.Ref { |
| 8248 | const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index| | 8299 | const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index| |
| 8249 | sema.code.instructions.items(.tag)[ptr_index] == .ret_type | 8300 | sema.code.instructions.items(.tag)[ptr_index] == .ret_type |
| ... | @@ -8255,7 +8306,7 @@ fn analyzeAs( | ... | @@ -8255,7 +8306,7 @@ fn analyzeAs( |
| 8255 | if (dest_ty.zigTypeTag() == .NoReturn) { | 8306 | if (dest_ty.zigTypeTag() == .NoReturn) { |
| 8256 | return sema.fail(block, src, "cannot cast to noreturn", .{}); | 8307 | return sema.fail(block, src, "cannot cast to noreturn", .{}); |
| 8257 | } | 8308 | } |
| 8258 | return sema.coerceExtra(block, dest_ty, operand, src, true, is_ret) catch |err| switch (err) { | 8309 | return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) { |
| 8259 | error.NotCoercible => unreachable, | 8310 | error.NotCoercible => unreachable, |
| 8260 | else => |e| return e, | 8311 | else => |e| return e, |
| 8261 | }; | 8312 | }; |
| ... | @@ -10459,7 +10510,12 @@ fn zirShl( | ... | @@ -10459,7 +10510,12 @@ fn zirShl( |
| 10459 | 10510 | ||
| 10460 | const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { | 10511 | const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { |
| 10461 | if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); | 10512 | if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); |
| 10462 | const rhs_val = maybe_rhs_val orelse break :rs rhs_src; | 10513 | const rhs_val = maybe_rhs_val orelse { |
| 10514 | if (scalar_ty.zigTypeTag() == .ComptimeInt) { | ||
| 10515 | return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{}); | ||
| 10516 | } | ||
| 10517 | break :rs rhs_src; | ||
| 10518 | }; | ||
| 10463 | 10519 | ||
| 10464 | const val = switch (air_tag) { | 10520 | const val = switch (air_tag) { |
| 10465 | .shl_exact => val: { | 10521 | .shl_exact => val: { |
| ... | @@ -10583,7 +10639,10 @@ fn zirShr( | ... | @@ -10583,7 +10639,10 @@ fn zirShr( |
| 10583 | const target = sema.mod.getTarget(); | 10639 | const target = sema.mod.getTarget(); |
| 10584 | const scalar_ty = lhs_ty.scalarType(); | 10640 | const scalar_ty = lhs_ty.scalarType(); |
| 10585 | 10641 | ||
| 10586 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: { | 10642 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); |
| 10643 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); | ||
| 10644 | |||
| 10645 | const runtime_src = if (maybe_rhs_val) |rhs_val| rs: { | ||
| 10587 | if (rhs_val.isUndef()) { | 10646 | if (rhs_val.isUndef()) { |
| 10588 | return sema.addConstUndef(lhs_ty); | 10647 | return sema.addConstUndef(lhs_ty); |
| 10589 | } | 10648 | } |
| ... | @@ -10615,7 +10674,7 @@ fn zirShr( | ... | @@ -10615,7 +10674,7 @@ fn zirShr( |
| 10615 | }); | 10674 | }); |
| 10616 | } | 10675 | } |
| 10617 | } | 10676 | } |
| 10618 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | 10677 | if (maybe_lhs_val) |lhs_val| { |
| 10619 | if (lhs_val.isUndef()) { | 10678 | if (lhs_val.isUndef()) { |
| 10620 | return sema.addConstUndef(lhs_ty); | 10679 | return sema.addConstUndef(lhs_ty); |
| 10621 | } | 10680 | } |
| ... | @@ -10633,6 +10692,10 @@ fn zirShr( | ... | @@ -10633,6 +10692,10 @@ fn zirShr( |
| 10633 | } | 10692 | } |
| 10634 | } else rhs_src; | 10693 | } else rhs_src; |
| 10635 | 10694 | ||
| 10695 | if (maybe_rhs_val == null and scalar_ty.zigTypeTag() == .ComptimeInt) { | ||
| 10696 | return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be a comptime known", .{}); | ||
| 10697 | } | ||
| 10698 | |||
| 10636 | try sema.requireRuntimeBlock(block, src, runtime_src); | 10699 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 10637 | const result = try block.addBinOp(air_tag, lhs, rhs); | 10700 | const result = try block.addBinOp(air_tag, lhs, rhs); |
| 10638 | if (block.wantSafety()) { | 10701 | if (block.wantSafety()) { |
| ... | @@ -15353,7 +15416,7 @@ fn analyzeRet( | ... | @@ -15353,7 +15416,7 @@ fn analyzeRet( |
| 15353 | if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) { | 15416 | if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) { |
| 15354 | try sema.addToInferredErrorSet(uncasted_operand); | 15417 | try sema.addToInferredErrorSet(uncasted_operand); |
| 15355 | } | 15418 | } |
| 15356 | const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, true, true) catch |err| switch (err) { | 15419 | const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, .{ .is_ret = true }) catch |err| switch (err) { |
| 15357 | error.NotCoercible => unreachable, | 15420 | error.NotCoercible => unreachable, |
| 15358 | else => |e| return e, | 15421 | else => |e| return e, |
| 15359 | }; | 15422 | }; |
| ... | @@ -19262,7 +19325,7 @@ fn resolveCallOptions( | ... | @@ -19262,7 +19325,7 @@ fn resolveCallOptions( |
| 19262 | return wanted_modifier; | 19325 | return wanted_modifier; |
| 19263 | }, | 19326 | }, |
| 19264 | // These can be upgraded to comptime. nosuspend bit can be safely ignored. | 19327 | // These can be upgraded to comptime. nosuspend bit can be safely ignored. |
| 19265 | .always_tail, .always_inline, .compile_time => { | 19328 | .always_inline, .compile_time => { |
| 19266 | _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { | 19329 | _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { |
| 19267 | return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)}); | 19330 | return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)}); |
| 19268 | }; | 19331 | }; |
| ... | @@ -19272,6 +19335,12 @@ fn resolveCallOptions( | ... | @@ -19272,6 +19335,12 @@ fn resolveCallOptions( |
| 19272 | } | 19335 | } |
| 19273 | return wanted_modifier; | 19336 | return wanted_modifier; |
| 19274 | }, | 19337 | }, |
| 19338 | .always_tail => { | ||
| 19339 | if (is_comptime) { | ||
| 19340 | return .compile_time; | ||
| 19341 | } | ||
| 19342 | return wanted_modifier; | ||
| 19343 | }, | ||
| 19275 | .async_kw => { | 19344 | .async_kw => { |
| 19276 | if (is_nosuspend) { | 19345 | if (is_nosuspend) { |
| 19277 | return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); | 19346 | return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{}); |
| ... | @@ -19614,9 +19683,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -19614,9 +19683,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 19614 | }); | 19683 | }); |
| 19615 | } | 19684 | } |
| 19616 | 19685 | ||
| 19617 | fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 19686 | fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 19618 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 19687 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 19619 | const src = inst_data.src(); | 19688 | const src = LazySrcLoc.nodeOffset(extra.node); |
| 19620 | return sema.failWithUseOfAsync(block, src); | 19689 | return sema.failWithUseOfAsync(block, src); |
| 19621 | } | 19690 | } |
| 19622 | 19691 | ||
| ... | @@ -21871,6 +21940,7 @@ fn unionFieldPtr( | ... | @@ -21871,6 +21940,7 @@ fn unionFieldPtr( |
| 21871 | .mutable = union_ptr_ty.ptrIsMutable(), | 21940 | .mutable = union_ptr_ty.ptrIsMutable(), |
| 21872 | .@"addrspace" = union_ptr_ty.ptrAddressSpace(), | 21941 | .@"addrspace" = union_ptr_ty.ptrAddressSpace(), |
| 21873 | }); | 21942 | }); |
| 21943 | const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); | ||
| 21874 | 21944 | ||
| 21875 | if (initializing and field.ty.zigTypeTag() == .NoReturn) { | 21945 | if (initializing and field.ty.zigTypeTag() == .NoReturn) { |
| 21876 | const msg = msg: { | 21946 | const msg = msg: { |
| ... | @@ -21892,11 +21962,10 @@ fn unionFieldPtr( | ... | @@ -21892,11 +21962,10 @@ fn unionFieldPtr( |
| 21892 | if (union_val.isUndef()) { | 21962 | if (union_val.isUndef()) { |
| 21893 | return sema.failWithUseOfUndef(block, src); | 21963 | return sema.failWithUseOfUndef(block, src); |
| 21894 | } | 21964 | } |
| 21895 | const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?; | ||
| 21896 | const tag_and_val = union_val.castTag(.@"union").?.data; | 21965 | const tag_and_val = union_val.castTag(.@"union").?.data; |
| 21897 | var field_tag_buf: Value.Payload.U32 = .{ | 21966 | var field_tag_buf: Value.Payload.U32 = .{ |
| 21898 | .base = .{ .tag = .enum_field_index }, | 21967 | .base = .{ .tag = .enum_field_index }, |
| 21899 | .data = @intCast(u32, enum_field_index), | 21968 | .data = enum_field_index, |
| 21900 | }; | 21969 | }; |
| 21901 | const field_tag = Value.initPayload(&field_tag_buf.base); | 21970 | const field_tag = Value.initPayload(&field_tag_buf.base); |
| 21902 | const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); | 21971 | const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); |
| ... | @@ -21928,7 +21997,7 @@ fn unionFieldPtr( | ... | @@ -21928,7 +21997,7 @@ fn unionFieldPtr( |
| 21928 | if (!initializing and union_obj.layout == .Auto and block.wantSafety() and | 21997 | if (!initializing and union_obj.layout == .Auto and block.wantSafety() and |
| 21929 | union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) | 21998 | union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) |
| 21930 | { | 21999 | { |
| 21931 | const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); | 22000 | const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); |
| 21932 | const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); | 22001 | const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); |
| 21933 | // TODO would it be better if get_union_tag supported pointers to unions? | 22002 | // TODO would it be better if get_union_tag supported pointers to unions? |
| 21934 | const union_val = try block.addTyOp(.load, union_ty, union_ptr); | 22003 | const union_val = try block.addTyOp(.load, union_ty, union_ptr); |
| ... | @@ -21958,15 +22027,15 @@ fn unionFieldVal( | ... | @@ -21958,15 +22027,15 @@ fn unionFieldVal( |
| 21958 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; | 22027 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; |
| 21959 | const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); | 22028 | const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); |
| 21960 | const field = union_obj.fields.values()[field_index]; | 22029 | const field = union_obj.fields.values()[field_index]; |
| 22030 | const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); | ||
| 21961 | 22031 | ||
| 21962 | if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| { | 22032 | if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| { |
| 21963 | if (union_val.isUndef()) return sema.addConstUndef(field.ty); | 22033 | if (union_val.isUndef()) return sema.addConstUndef(field.ty); |
| 21964 | 22034 | ||
| 21965 | const tag_and_val = union_val.castTag(.@"union").?.data; | 22035 | const tag_and_val = union_val.castTag(.@"union").?.data; |
| 21966 | const enum_field_index = union_obj.tag_ty.enumFieldIndex(field_name).?; | ||
| 21967 | var field_tag_buf: Value.Payload.U32 = .{ | 22036 | var field_tag_buf: Value.Payload.U32 = .{ |
| 21968 | .base = .{ .tag = .enum_field_index }, | 22037 | .base = .{ .tag = .enum_field_index }, |
| 21969 | .data = @intCast(u32, enum_field_index), | 22038 | .data = enum_field_index, |
| 21970 | }; | 22039 | }; |
| 21971 | const field_tag = Value.initPayload(&field_tag_buf.base); | 22040 | const field_tag = Value.initPayload(&field_tag_buf.base); |
| 21972 | const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); | 22041 | const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod); |
| ... | @@ -22002,7 +22071,7 @@ fn unionFieldVal( | ... | @@ -22002,7 +22071,7 @@ fn unionFieldVal( |
| 22002 | if (union_obj.layout == .Auto and block.wantSafety() and | 22071 | if (union_obj.layout == .Auto and block.wantSafety() and |
| 22003 | union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) | 22072 | union_ty.unionTagTypeSafety() != null and union_obj.fields.count() > 1) |
| 22004 | { | 22073 | { |
| 22005 | const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index); | 22074 | const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); |
| 22006 | const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); | 22075 | const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); |
| 22007 | const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); | 22076 | const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); |
| 22008 | const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag); | 22077 | const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag); |
| ... | @@ -22504,7 +22573,7 @@ fn coerce( | ... | @@ -22504,7 +22573,7 @@ fn coerce( |
| 22504 | inst: Air.Inst.Ref, | 22573 | inst: Air.Inst.Ref, |
| 22505 | inst_src: LazySrcLoc, | 22574 | inst_src: LazySrcLoc, |
| 22506 | ) CompileError!Air.Inst.Ref { | 22575 | ) CompileError!Air.Inst.Ref { |
| 22507 | return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true, false) catch |err| switch (err) { | 22576 | return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { |
| 22508 | error.NotCoercible => unreachable, | 22577 | error.NotCoercible => unreachable, |
| 22509 | else => |e| return e, | 22578 | else => |e| return e, |
| 22510 | }; | 22579 | }; |
| ... | @@ -22516,14 +22585,22 @@ const CoersionError = CompileError || error{ | ... | @@ -22516,14 +22585,22 @@ const CoersionError = CompileError || error{ |
| 22516 | NotCoercible, | 22585 | NotCoercible, |
| 22517 | }; | 22586 | }; |
| 22518 | 22587 | ||
| 22588 | const CoerceOpts = struct { | ||
| 22589 | /// Should coerceExtra emit error messages. | ||
| 22590 | report_err: bool = true, | ||
| 22591 | /// Ignored if `report_err == false`. | ||
| 22592 | is_ret: bool = false, | ||
| 22593 | /// Should coercion to comptime_int ermit an error message. | ||
| 22594 | no_cast_to_comptime_int: bool = false, | ||
| 22595 | }; | ||
| 22596 | |||
| 22519 | fn coerceExtra( | 22597 | fn coerceExtra( |
| 22520 | sema: *Sema, | 22598 | sema: *Sema, |
| 22521 | block: *Block, | 22599 | block: *Block, |
| 22522 | dest_ty_unresolved: Type, | 22600 | dest_ty_unresolved: Type, |
| 22523 | inst: Air.Inst.Ref, | 22601 | inst: Air.Inst.Ref, |
| 22524 | inst_src: LazySrcLoc, | 22602 | inst_src: LazySrcLoc, |
| 22525 | report_err: bool, | 22603 | opts: CoerceOpts, |
| 22526 | is_ret: bool, | ||
| 22527 | ) CoersionError!Air.Inst.Ref { | 22604 | ) CoersionError!Air.Inst.Ref { |
| 22528 | switch (dest_ty_unresolved.tag()) { | 22605 | switch (dest_ty_unresolved.tag()) { |
| 22529 | .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src), | 22606 | .var_args_param => return sema.coerceVarArgParam(block, inst, inst_src), |
| ... | @@ -22575,7 +22652,7 @@ fn coerceExtra( | ... | @@ -22575,7 +22652,7 @@ fn coerceExtra( |
| 22575 | 22652 | ||
| 22576 | // T to ?T | 22653 | // T to ?T |
| 22577 | const child_type = try dest_ty.optionalChildAlloc(sema.arena); | 22654 | const child_type = try dest_ty.optionalChildAlloc(sema.arena); |
| 22578 | const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false, is_ret) catch |err| switch (err) { | 22655 | const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { |
| 22579 | error.NotCoercible => { | 22656 | error.NotCoercible => { |
| 22580 | if (in_memory_result == .no_match) { | 22657 | if (in_memory_result == .no_match) { |
| 22581 | // Try to give more useful notes | 22658 | // Try to give more useful notes |
| ... | @@ -22691,7 +22768,7 @@ fn coerceExtra( | ... | @@ -22691,7 +22768,7 @@ fn coerceExtra( |
| 22691 | return sema.addConstant(dest_ty, Value.@"null"); | 22768 | return sema.addConstant(dest_ty, Value.@"null"); |
| 22692 | }, | 22769 | }, |
| 22693 | .ComptimeInt => { | 22770 | .ComptimeInt => { |
| 22694 | const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false, is_ret) catch |err| switch (err) { | 22771 | const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { |
| 22695 | error.NotCoercible => break :pointer, | 22772 | error.NotCoercible => break :pointer, |
| 22696 | else => |e| return e, | 22773 | else => |e| return e, |
| 22697 | }; | 22774 | }; |
| ... | @@ -22702,7 +22779,7 @@ fn coerceExtra( | ... | @@ -22702,7 +22779,7 @@ fn coerceExtra( |
| 22702 | .signed => Type.isize, | 22779 | .signed => Type.isize, |
| 22703 | .unsigned => Type.usize, | 22780 | .unsigned => Type.usize, |
| 22704 | }; | 22781 | }; |
| 22705 | const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false, is_ret) catch |err| switch (err) { | 22782 | const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { |
| 22706 | error.NotCoercible => { | 22783 | error.NotCoercible => { |
| 22707 | // Try to give more useful notes | 22784 | // Try to give more useful notes |
| 22708 | in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src); | 22785 | in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src); |
| ... | @@ -22828,7 +22905,13 @@ fn coerceExtra( | ... | @@ -22828,7 +22905,13 @@ fn coerceExtra( |
| 22828 | }, | 22905 | }, |
| 22829 | .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) { | 22906 | .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) { |
| 22830 | .Float, .ComptimeFloat => float: { | 22907 | .Float, .ComptimeFloat => float: { |
| 22831 | const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float; | 22908 | const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse { |
| 22909 | if (dest_ty.zigTypeTag() == .ComptimeInt) { | ||
| 22910 | if (!opts.report_err) return error.NotCoercible; | ||
| 22911 | return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known"); | ||
| 22912 | } | ||
| 22913 | break :float; | ||
| 22914 | }; | ||
| 22832 | 22915 | ||
| 22833 | if (val.floatHasFraction()) { | 22916 | if (val.floatHasFraction()) { |
| 22834 | return sema.fail( | 22917 | return sema.fail( |
| ... | @@ -22845,11 +22928,16 @@ fn coerceExtra( | ... | @@ -22845,11 +22928,16 @@ fn coerceExtra( |
| 22845 | if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { | 22928 | if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { |
| 22846 | // comptime known integer to other number | 22929 | // comptime known integer to other number |
| 22847 | if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) { | 22930 | if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) { |
| 22848 | if (!report_err) return error.NotCoercible; | 22931 | if (!opts.report_err) return error.NotCoercible; |
| 22849 | return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); | 22932 | return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) }); |
| 22850 | } | 22933 | } |
| 22851 | return try sema.addConstant(dest_ty, val); | 22934 | return try sema.addConstant(dest_ty, val); |
| 22852 | } | 22935 | } |
| 22936 | if (dest_ty.zigTypeTag() == .ComptimeInt) { | ||
| 22937 | if (!opts.report_err) return error.NotCoercible; | ||
| 22938 | if (opts.no_cast_to_comptime_int) return inst; | ||
| 22939 | return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime known"); | ||
| 22940 | } | ||
| 22853 | 22941 | ||
| 22854 | // integer widening | 22942 | // integer widening |
| 22855 | const dst_info = dest_ty.intInfo(target); | 22943 | const dst_info = dest_ty.intInfo(target); |
| ... | @@ -22886,6 +22974,7 @@ fn coerceExtra( | ... | @@ -22886,6 +22974,7 @@ fn coerceExtra( |
| 22886 | } | 22974 | } |
| 22887 | return try sema.addConstant(dest_ty, result_val); | 22975 | return try sema.addConstant(dest_ty, result_val); |
| 22888 | } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { | 22976 | } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { |
| 22977 | if (!opts.report_err) return error.NotCoercible; | ||
| 22889 | return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known"); | 22978 | return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known"); |
| 22890 | } | 22979 | } |
| 22891 | 22980 | ||
| ... | @@ -22898,7 +22987,13 @@ fn coerceExtra( | ... | @@ -22898,7 +22987,13 @@ fn coerceExtra( |
| 22898 | } | 22987 | } |
| 22899 | }, | 22988 | }, |
| 22900 | .Int, .ComptimeInt => int: { | 22989 | .Int, .ComptimeInt => int: { |
| 22901 | const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int; | 22990 | const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse { |
| 22991 | if (dest_ty.zigTypeTag() == .ComptimeFloat) { | ||
| 22992 | if (!opts.report_err) return error.NotCoercible; | ||
| 22993 | return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known"); | ||
| 22994 | } | ||
| 22995 | break :int; | ||
| 22996 | }; | ||
| 22902 | const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); | 22997 | const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); |
| 22903 | // TODO implement this compile error | 22998 | // TODO implement this compile error |
| 22904 | //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); | 22999 | //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); |
| ... | @@ -23050,9 +23145,9 @@ fn coerceExtra( | ... | @@ -23050,9 +23145,9 @@ fn coerceExtra( |
| 23050 | return sema.addConstUndef(dest_ty); | 23145 | return sema.addConstUndef(dest_ty); |
| 23051 | } | 23146 | } |
| 23052 | 23147 | ||
| 23053 | if (!report_err) return error.NotCoercible; | 23148 | if (!opts.report_err) return error.NotCoercible; |
| 23054 | 23149 | ||
| 23055 | if (is_ret and dest_ty.zigTypeTag() == .NoReturn) { | 23150 | if (opts.is_ret and dest_ty.zigTypeTag() == .NoReturn) { |
| 23056 | const msg = msg: { | 23151 | const msg = msg: { |
| 23057 | const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{}); | 23152 | const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{}); |
| 23058 | errdefer msg.destroy(sema.gpa); | 23153 | errdefer msg.destroy(sema.gpa); |
| ... | @@ -23089,7 +23184,7 @@ fn coerceExtra( | ... | @@ -23089,7 +23184,7 @@ fn coerceExtra( |
| 23089 | try in_memory_result.report(sema, block, inst_src, msg); | 23184 | try in_memory_result.report(sema, block, inst_src, msg); |
| 23090 | 23185 | ||
| 23091 | // Add notes about function return type | 23186 | // Add notes about function return type |
| 23092 | if (is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) { | 23187 | if (opts.is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) { |
| 23093 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; | 23188 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; |
| 23094 | const src_decl = sema.mod.declPtr(sema.func.?.owner_decl); | 23189 | const src_decl = sema.mod.declPtr(sema.func.?.owner_decl); |
| 23095 | if (inst_ty.isError() and !dest_ty.isError()) { | 23190 | if (inst_ty.isError() and !dest_ty.isError()) { |
| ... | @@ -24050,7 +24145,7 @@ fn storePtr2( | ... | @@ -24050,7 +24145,7 @@ fn storePtr2( |
| 24050 | // https://github.com/ziglang/zig/issues/11154 | 24145 | // https://github.com/ziglang/zig/issues/11154 |
| 24051 | if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { | 24146 | if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| { |
| 24052 | const vector_ty = sema.typeOf(vector_ptr).childType(); | 24147 | const vector_ty = sema.typeOf(vector_ptr).childType(); |
| 24053 | const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) { | 24148 | const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { |
| 24054 | error.NotCoercible => unreachable, | 24149 | error.NotCoercible => unreachable, |
| 24055 | else => |e| return e, | 24150 | else => |e| return e, |
| 24056 | }; | 24151 | }; |
| ... | @@ -24058,7 +24153,7 @@ fn storePtr2( | ... | @@ -24058,7 +24153,7 @@ fn storePtr2( |
| 24058 | return; | 24153 | return; |
| 24059 | } | 24154 | } |
| 24060 | 24155 | ||
| 24061 | const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) { | 24156 | const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { |
| 24062 | error.NotCoercible => unreachable, | 24157 | error.NotCoercible => unreachable, |
| 24063 | else => |e| return e, | 24158 | else => |e| return e, |
| 24064 | }; | 24159 | }; |
| ... | @@ -26793,7 +26888,7 @@ fn wrapErrorUnionPayload( | ... | @@ -26793,7 +26888,7 @@ fn wrapErrorUnionPayload( |
| 26793 | inst_src: LazySrcLoc, | 26888 | inst_src: LazySrcLoc, |
| 26794 | ) !Air.Inst.Ref { | 26889 | ) !Air.Inst.Ref { |
| 26795 | const dest_payload_ty = dest_ty.errorUnionPayload(); | 26890 | const dest_payload_ty = dest_ty.errorUnionPayload(); |
| 26796 | const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, false, false); | 26891 | const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); |
| 26797 | if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| { | 26892 | if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| { |
| 26798 | return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val)); | 26893 | return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val)); |
| 26799 | } | 26894 | } |
src/Zir.zig+9-6| ... | @@ -242,6 +242,8 @@ pub const Inst = struct { | ... | @@ -242,6 +242,8 @@ pub const Inst = struct { |
| 242 | /// Type coercion to the function's return type. | 242 | /// Type coercion to the function's return type. |
| 243 | /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. | 243 | /// Uses the `pl_node` field. Payload is `As`. AST node could be many things. |
| 244 | as_node, | 244 | as_node, |
| 245 | /// Same as `as_node` but ignores runtime to comptime int error. | ||
| 246 | as_shift_operand, | ||
| 245 | /// Bitwise AND. `&` | 247 | /// Bitwise AND. `&` |
| 246 | bit_and, | 248 | bit_and, |
| 247 | /// Reinterpret the memory representation of a value as a different type. | 249 | /// Reinterpret the memory representation of a value as a different type. |
| ... | @@ -942,9 +944,6 @@ pub const Inst = struct { | ... | @@ -942,9 +944,6 @@ pub const Inst = struct { |
| 942 | /// Implements the `@maximum` builtin. | 944 | /// Implements the `@maximum` builtin. |
| 943 | /// Uses the `pl_node` union field with payload `Bin` | 945 | /// Uses the `pl_node` union field with payload `Bin` |
| 944 | maximum, | 946 | maximum, |
| 945 | /// Implements the `@asyncCall` builtin. | ||
| 946 | /// Uses the `pl_node` union field with payload `AsyncCall`. | ||
| 947 | builtin_async_call, | ||
| 948 | /// Implements the `@cImport` builtin. | 947 | /// Implements the `@cImport` builtin. |
| 949 | /// Uses the `pl_node` union field with payload `Block`. | 948 | /// Uses the `pl_node` union field with payload `Block`. |
| 950 | c_import, | 949 | c_import, |
| ... | @@ -1029,6 +1028,7 @@ pub const Inst = struct { | ... | @@ -1029,6 +1028,7 @@ pub const Inst = struct { |
| 1029 | .anyframe_type, | 1028 | .anyframe_type, |
| 1030 | .as, | 1029 | .as, |
| 1031 | .as_node, | 1030 | .as_node, |
| 1031 | .as_shift_operand, | ||
| 1032 | .bit_and, | 1032 | .bit_and, |
| 1033 | .bitcast, | 1033 | .bitcast, |
| 1034 | .bit_or, | 1034 | .bit_or, |
| ... | @@ -1231,7 +1231,6 @@ pub const Inst = struct { | ... | @@ -1231,7 +1231,6 @@ pub const Inst = struct { |
| 1231 | .memcpy, | 1231 | .memcpy, |
| 1232 | .memset, | 1232 | .memset, |
| 1233 | .minimum, | 1233 | .minimum, |
| 1234 | .builtin_async_call, | ||
| 1235 | .c_import, | 1234 | .c_import, |
| 1236 | .@"resume", | 1235 | .@"resume", |
| 1237 | .@"await", | 1236 | .@"await", |
| ... | @@ -1339,6 +1338,7 @@ pub const Inst = struct { | ... | @@ -1339,6 +1338,7 @@ pub const Inst = struct { |
| 1339 | .anyframe_type, | 1338 | .anyframe_type, |
| 1340 | .as, | 1339 | .as, |
| 1341 | .as_node, | 1340 | .as_node, |
| 1341 | .as_shift_operand, | ||
| 1342 | .bit_and, | 1342 | .bit_and, |
| 1343 | .bitcast, | 1343 | .bitcast, |
| 1344 | .bit_or, | 1344 | .bit_or, |
| ... | @@ -1513,7 +1513,6 @@ pub const Inst = struct { | ... | @@ -1513,7 +1513,6 @@ pub const Inst = struct { |
| 1513 | .field_parent_ptr, | 1513 | .field_parent_ptr, |
| 1514 | .maximum, | 1514 | .maximum, |
| 1515 | .minimum, | 1515 | .minimum, |
| 1516 | .builtin_async_call, | ||
| 1517 | .c_import, | 1516 | .c_import, |
| 1518 | .@"resume", | 1517 | .@"resume", |
| 1519 | .@"await", | 1518 | .@"await", |
| ... | @@ -1577,6 +1576,7 @@ pub const Inst = struct { | ... | @@ -1577,6 +1576,7 @@ pub const Inst = struct { |
| 1577 | .anyframe_type = .un_node, | 1576 | .anyframe_type = .un_node, |
| 1578 | .as = .bin, | 1577 | .as = .bin, |
| 1579 | .as_node = .pl_node, | 1578 | .as_node = .pl_node, |
| 1579 | .as_shift_operand = .pl_node, | ||
| 1580 | .bit_and = .pl_node, | 1580 | .bit_and = .pl_node, |
| 1581 | .bitcast = .pl_node, | 1581 | .bitcast = .pl_node, |
| 1582 | .bit_not = .un_node, | 1582 | .bit_not = .un_node, |
| ... | @@ -1801,7 +1801,6 @@ pub const Inst = struct { | ... | @@ -1801,7 +1801,6 @@ pub const Inst = struct { |
| 1801 | .memcpy = .pl_node, | 1801 | .memcpy = .pl_node, |
| 1802 | .memset = .pl_node, | 1802 | .memset = .pl_node, |
| 1803 | .minimum = .pl_node, | 1803 | .minimum = .pl_node, |
| 1804 | .builtin_async_call = .pl_node, | ||
| 1805 | .c_import = .pl_node, | 1804 | .c_import = .pl_node, |
| 1806 | 1805 | ||
| 1807 | .alloc = .un_node, | 1806 | .alloc = .un_node, |
| ... | @@ -1972,6 +1971,9 @@ pub const Inst = struct { | ... | @@ -1972,6 +1971,9 @@ pub const Inst = struct { |
| 1972 | /// `operand` is payload index to `UnNode`. | 1971 | /// `operand` is payload index to `UnNode`. |
| 1973 | /// `small` contains `NameStrategy | 1972 | /// `small` contains `NameStrategy |
| 1974 | reify, | 1973 | reify, |
| 1974 | /// Implements the `@asyncCall` builtin. | ||
| 1975 | /// `operand` is payload index to `AsyncCall`. | ||
| 1976 | builtin_async_call, | ||
| 1975 | 1977 | ||
| 1976 | pub const InstData = struct { | 1978 | pub const InstData = struct { |
| 1977 | opcode: Extended, | 1979 | opcode: Extended, |
| ... | @@ -3454,6 +3456,7 @@ pub const Inst = struct { | ... | @@ -3454,6 +3456,7 @@ pub const Inst = struct { |
| 3454 | }; | 3456 | }; |
| 3455 | 3457 | ||
| 3456 | pub const AsyncCall = struct { | 3458 | pub const AsyncCall = struct { |
| 3459 | node: i32, | ||
| 3457 | frame_buffer: Ref, | 3460 | frame_buffer: Ref, |
| 3458 | result_ptr: Ref, | 3461 | result_ptr: Ref, |
| 3459 | fn_ptr: Ref, | 3462 | fn_ptr: Ref, |
src/arch/aarch64/CodeGen.zig+7-9| ... | @@ -3466,19 +3466,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3466,19 +3466,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3466 | // on linking. | 3466 | // on linking. |
| 3467 | const mod = self.bin_file.options.module.?; | 3467 | const mod = self.bin_file.options.module.?; |
| 3468 | if (self.air.value(callee)) |func_value| { | 3468 | if (self.air.value(callee)) |func_value| { |
| 3469 | if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { | 3469 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 3470 | if (func_value.castTag(.function)) |func_payload| { | 3470 | if (func_value.castTag(.function)) |func_payload| { |
| 3471 | const func = func_payload.data; | 3471 | const func = func_payload.data; |
| 3472 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 3472 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 3473 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 3473 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 3474 | const fn_owner_decl = mod.declPtr(func.owner_decl); | 3474 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 3475 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 3475 | const got_addr = blk: { |
| 3476 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 3476 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 3477 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | 3477 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); |
| 3478 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| | 3478 | }; |
| 3479 | coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes | ||
| 3480 | else | ||
| 3481 | unreachable; | ||
| 3482 | 3479 | ||
| 3483 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); | 3480 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); |
| 3484 | 3481 | ||
| ... | @@ -3546,6 +3543,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3546,6 +3543,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3546 | } else { | 3543 | } else { |
| 3547 | return self.fail("TODO implement calling bitcasted functions", .{}); | 3544 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 3548 | } | 3545 | } |
| 3546 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | ||
| 3547 | return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch}); | ||
| 3549 | } else unreachable; | 3548 | } else unreachable; |
| 3550 | } else { | 3549 | } else { |
| 3551 | assert(ty.zigTypeTag() == .Pointer); | 3550 | assert(ty.zigTypeTag() == .Pointer); |
| ... | @@ -5109,9 +5108,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -5109,9 +5108,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 5109 | // the linker has enough info to perform relocations. | 5108 | // the linker has enough info to perform relocations. |
| 5110 | assert(decl.link.macho.sym_index != 0); | 5109 | assert(decl.link.macho.sym_index != 0); |
| 5111 | return MCValue{ .got_load = decl.link.macho.sym_index }; | 5110 | return MCValue{ .got_load = decl.link.macho.sym_index }; |
| 5112 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 5111 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 5113 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | 5112 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 5114 | return MCValue{ .memory = got_addr }; | ||
| 5115 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 5113 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 5116 | try p9.seeDecl(decl_index); | 5114 | try p9.seeDecl(decl_index); |
| 5117 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 5115 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; |
src/arch/arm/CodeGen.zig+5-9| ... | @@ -3698,7 +3698,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3698,7 +3698,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3698 | // Due to incremental compilation, how function calls are generated depends | 3698 | // Due to incremental compilation, how function calls are generated depends |
| 3699 | // on linking. | 3699 | // on linking. |
| 3700 | switch (self.bin_file.tag) { | 3700 | switch (self.bin_file.tag) { |
| 3701 | .elf, .coff => { | 3701 | .elf => { |
| 3702 | if (self.air.value(callee)) |func_value| { | 3702 | if (self.air.value(callee)) |func_value| { |
| 3703 | if (func_value.castTag(.function)) |func_payload| { | 3703 | if (func_value.castTag(.function)) |func_payload| { |
| 3704 | const func = func_payload.data; | 3704 | const func = func_payload.data; |
| ... | @@ -3709,11 +3709,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3709,11 +3709,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3709 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 3709 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { |
| 3710 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 3710 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 3711 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | 3711 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); |
| 3712 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| | 3712 | } else unreachable; |
| 3713 | coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes | ||
| 3714 | else | ||
| 3715 | unreachable; | ||
| 3716 | |||
| 3717 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); | 3713 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); |
| 3718 | } else if (func_value.castTag(.extern_fn)) |_| { | 3714 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 3719 | return self.fail("TODO implement calling extern functions", .{}); | 3715 | return self.fail("TODO implement calling extern functions", .{}); |
| ... | @@ -3751,6 +3747,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3751,6 +3747,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3751 | } | 3747 | } |
| 3752 | }, | 3748 | }, |
| 3753 | .macho => unreachable, // unsupported architecture for MachO | 3749 | .macho => unreachable, // unsupported architecture for MachO |
| 3750 | .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}), | ||
| 3754 | .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}), | 3751 | .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}), |
| 3755 | else => unreachable, | 3752 | else => unreachable, |
| 3756 | } | 3753 | } |
| ... | @@ -5548,9 +5545,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -5548,9 +5545,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 5548 | return MCValue{ .memory = got_addr }; | 5545 | return MCValue{ .memory = got_addr }; |
| 5549 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 5546 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 5550 | unreachable; // unsupported architecture for MachO | 5547 | unreachable; // unsupported architecture for MachO |
| 5551 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 5548 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 5552 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | 5549 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 5553 | return MCValue{ .memory = got_addr }; | ||
| 5554 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 5550 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 5555 | try p9.seeDecl(decl_index); | 5551 | try p9.seeDecl(decl_index); |
| 5556 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 5552 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; |
src/arch/riscv64/CodeGen.zig+7-9| ... | @@ -1718,7 +1718,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -1718,7 +1718,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1718 | 1718 | ||
| 1719 | // Due to incremental compilation, how function calls are generated depends | 1719 | // Due to incremental compilation, how function calls are generated depends |
| 1720 | // on linking. | 1720 | // on linking. |
| 1721 | if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { | 1721 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 1722 | for (info.args) |mc_arg, arg_i| { | 1722 | for (info.args) |mc_arg, arg_i| { |
| 1723 | const arg = args[arg_i]; | 1723 | const arg = args[arg_i]; |
| 1724 | const arg_ty = self.air.typeOf(arg); | 1724 | const arg_ty = self.air.typeOf(arg); |
| ... | @@ -1752,13 +1752,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -1752,13 +1752,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1752 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 1752 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 1753 | const mod = self.bin_file.options.module.?; | 1753 | const mod = self.bin_file.options.module.?; |
| 1754 | const fn_owner_decl = mod.declPtr(func.owner_decl); | 1754 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 1755 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 1755 | const got_addr = blk: { |
| 1756 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 1756 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 1757 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | 1757 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); |
| 1758 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| | 1758 | }; |
| 1759 | coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes | ||
| 1760 | else | ||
| 1761 | unreachable; | ||
| 1762 | 1759 | ||
| 1763 | try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); | 1760 | try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); |
| 1764 | _ = try self.addInst(.{ | 1761 | _ = try self.addInst(.{ |
| ... | @@ -1777,6 +1774,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -1777,6 +1774,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1777 | } else { | 1774 | } else { |
| 1778 | return self.fail("TODO implement calling runtime known function pointer", .{}); | 1775 | return self.fail("TODO implement calling runtime known function pointer", .{}); |
| 1779 | } | 1776 | } |
| 1777 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | ||
| 1778 | return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch}); | ||
| 1780 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 1779 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 1781 | unreachable; // unsupported architecture for MachO | 1780 | unreachable; // unsupported architecture for MachO |
| 1782 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { | 1781 | } else if (self.bin_file.cast(link.File.Plan9)) |_| { |
| ... | @@ -2591,9 +2590,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -2591,9 +2590,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 2591 | // TODO I'm hacking my way through here by repurposing .memory for storing | 2590 | // TODO I'm hacking my way through here by repurposing .memory for storing |
| 2592 | // index to the GOT target symbol index. | 2591 | // index to the GOT target symbol index. |
| 2593 | return MCValue{ .memory = decl.link.macho.sym_index }; | 2592 | return MCValue{ .memory = decl.link.macho.sym_index }; |
| 2594 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 2593 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 2595 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | 2594 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 2596 | return MCValue{ .memory = got_addr }; | ||
| 2597 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 2595 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 2598 | try p9.seeDecl(decl_index); | 2596 | try p9.seeDecl(decl_index); |
| 2599 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 2597 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; |
src/arch/x86_64/CodeGen.zig+49-17| ... | @@ -2657,22 +2657,26 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue | ... | @@ -2657,22 +2657,26 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2657 | .direct_load, | 2657 | .direct_load, |
| 2658 | => |sym_index| { | 2658 | => |sym_index| { |
| 2659 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); | 2659 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2660 | const mod = self.bin_file.options.module.?; | ||
| 2661 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); | ||
| 2662 | const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag) | ||
| 2663 | fn_owner_decl.link.macho.sym_index | ||
| 2664 | else | ||
| 2665 | fn_owner_decl.link.coff.sym_index; | ||
| 2660 | const flags: u2 = switch (ptr) { | 2666 | const flags: u2 = switch (ptr) { |
| 2661 | .got_load => 0b00, | 2667 | .got_load => 0b00, |
| 2662 | .direct_load => 0b01, | 2668 | .direct_load => 0b01, |
| 2663 | else => unreachable, | 2669 | else => unreachable, |
| 2664 | }; | 2670 | }; |
| 2665 | const mod = self.bin_file.options.module.?; | ||
| 2666 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); | ||
| 2667 | _ = try self.addInst(.{ | 2671 | _ = try self.addInst(.{ |
| 2668 | .tag = .lea_pie, | 2672 | .tag = .lea_pic, |
| 2669 | .ops = Mir.Inst.Ops.encode(.{ | 2673 | .ops = Mir.Inst.Ops.encode(.{ |
| 2670 | .reg1 = registerAlias(reg, abi_size), | 2674 | .reg1 = registerAlias(reg, abi_size), |
| 2671 | .flags = flags, | 2675 | .flags = flags, |
| 2672 | }), | 2676 | }), |
| 2673 | .data = .{ | 2677 | .data = .{ |
| 2674 | .relocation = .{ | 2678 | .relocation = .{ |
| 2675 | .atom_index = fn_owner_decl.link.macho.sym_index, | 2679 | .atom_index = atom_index, |
| 2676 | .sym_index = sym_index, | 2680 | .sym_index = sym_index, |
| 2677 | }, | 2681 | }, |
| 2678 | }, | 2682 | }, |
| ... | @@ -3961,20 +3965,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3961,20 +3965,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3961 | // Due to incremental compilation, how function calls are generated depends | 3965 | // Due to incremental compilation, how function calls are generated depends |
| 3962 | // on linking. | 3966 | // on linking. |
| 3963 | const mod = self.bin_file.options.module.?; | 3967 | const mod = self.bin_file.options.module.?; |
| 3964 | if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { | 3968 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 3965 | if (self.air.value(callee)) |func_value| { | 3969 | if (self.air.value(callee)) |func_value| { |
| 3966 | if (func_value.castTag(.function)) |func_payload| { | 3970 | if (func_value.castTag(.function)) |func_payload| { |
| 3967 | const func = func_payload.data; | 3971 | const func = func_payload.data; |
| 3968 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 3972 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 3969 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 3973 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 3970 | const fn_owner_decl = mod.declPtr(func.owner_decl); | 3974 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 3971 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 3975 | const got_addr = blk: { |
| 3972 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 3976 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 3973 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | 3977 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); |
| 3974 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| | 3978 | }; |
| 3975 | @intCast(u32, coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes) | ||
| 3976 | else | ||
| 3977 | unreachable; | ||
| 3978 | _ = try self.addInst(.{ | 3979 | _ = try self.addInst(.{ |
| 3979 | .tag = .call, | 3980 | .tag = .call, |
| 3980 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | 3981 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), |
| ... | @@ -3998,14 +3999,47 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3998,14 +3999,47 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3998 | .data = undefined, | 3999 | .data = undefined, |
| 3999 | }); | 4000 | }); |
| 4000 | } | 4001 | } |
| 4001 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 4002 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 4002 | if (self.air.value(callee)) |func_value| { | 4003 | if (self.air.value(callee)) |func_value| { |
| 4003 | if (func_value.castTag(.function)) |func_payload| { | 4004 | if (func_value.castTag(.function)) |func_payload| { |
| 4004 | const func = func_payload.data; | 4005 | const func = func_payload.data; |
| 4005 | const fn_owner_decl = mod.declPtr(func.owner_decl); | 4006 | const fn_owner_decl = mod.declPtr(func.owner_decl); |
| 4006 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | 4007 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4007 | .got_load = fn_owner_decl.link.macho.sym_index, | 4008 | .got_load = fn_owner_decl.link.coff.sym_index, |
| 4008 | }); | 4009 | }); |
| 4010 | _ = try self.addInst(.{ | ||
| 4011 | .tag = .call, | ||
| 4012 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4013 | .reg1 = .rax, | ||
| 4014 | .flags = 0b01, | ||
| 4015 | }), | ||
| 4016 | .data = undefined, | ||
| 4017 | }); | ||
| 4018 | } else if (func_value.castTag(.extern_fn)) |_| { | ||
| 4019 | return self.fail("TODO implement calling extern functions", .{}); | ||
| 4020 | } else { | ||
| 4021 | return self.fail("TODO implement calling bitcasted functions", .{}); | ||
| 4022 | } | ||
| 4023 | } else { | ||
| 4024 | assert(ty.zigTypeTag() == .Pointer); | ||
| 4025 | const mcv = try self.resolveInst(callee); | ||
| 4026 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | ||
| 4027 | _ = try self.addInst(.{ | ||
| 4028 | .tag = .call, | ||
| 4029 | .ops = Mir.Inst.Ops.encode(.{ | ||
| 4030 | .reg1 = .rax, | ||
| 4031 | .flags = 0b01, | ||
| 4032 | }), | ||
| 4033 | .data = undefined, | ||
| 4034 | }); | ||
| 4035 | } | ||
| 4036 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | ||
| 4037 | if (self.air.value(callee)) |func_value| { | ||
| 4038 | if (func_value.castTag(.function)) |func_payload| { | ||
| 4039 | const func = func_payload.data; | ||
| 4040 | const fn_owner_decl = mod.declPtr(func.owner_decl); | ||
| 4041 | const sym_index = fn_owner_decl.link.macho.sym_index; | ||
| 4042 | try self.genSetReg(Type.initTag(.usize), .rax, .{ .got_load = sym_index }); | ||
| 4009 | // callq *%rax | 4043 | // callq *%rax |
| 4010 | _ = try self.addInst(.{ | 4044 | _ = try self.addInst(.{ |
| 4011 | .tag = .call, | 4045 | .tag = .call, |
| ... | @@ -6842,13 +6876,11 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -6842,13 +6876,11 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6842 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 6876 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; |
| 6843 | return MCValue{ .memory = got_addr }; | 6877 | return MCValue{ .memory = got_addr }; |
| 6844 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6878 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6845 | // Because MachO is PIE-always-on, we defer memory address resolution until | ||
| 6846 | // the linker has enough info to perform relocations. | ||
| 6847 | assert(decl.link.macho.sym_index != 0); | 6879 | assert(decl.link.macho.sym_index != 0); |
| 6848 | return MCValue{ .got_load = decl.link.macho.sym_index }; | 6880 | return MCValue{ .got_load = decl.link.macho.sym_index }; |
| 6849 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 6881 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 6850 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | 6882 | assert(decl.link.coff.sym_index != 0); |
| 6851 | return MCValue{ .memory = got_addr }; | 6883 | return MCValue{ .got_load = decl.link.coff.sym_index }; |
| 6852 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 6884 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6853 | try p9.seeDecl(decl_index); | 6885 | try p9.seeDecl(decl_index); |
| 6854 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 6886 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; |
src/arch/x86_64/Emit.zig+29-11| ... | @@ -137,7 +137,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { | ... | @@ -137,7 +137,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { |
| 137 | .fld => try emit.mirFld(inst), | 137 | .fld => try emit.mirFld(inst), |
| 138 | 138 | ||
| 139 | .lea => try emit.mirLea(inst), | 139 | .lea => try emit.mirLea(inst), |
| 140 | .lea_pie => try emit.mirLeaPie(inst), | 140 | .lea_pic => try emit.mirLeaPic(inst), |
| 141 | 141 | ||
| 142 | .shl => try emit.mirShift(.shl, inst), | 142 | .shl => try emit.mirShift(.shl, inst), |
| 143 | .sal => try emit.mirShift(.sal, inst), | 143 | .sal => try emit.mirShift(.sal, inst), |
| ... | @@ -338,7 +338,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -338,7 +338,7 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 338 | .base = ops.reg1, | 338 | .base = ops.reg1, |
| 339 | }), emit.code); | 339 | }), emit.code); |
| 340 | }, | 340 | }, |
| 341 | 0b11 => return emit.fail("TODO unused JMP/CALL variant 0b11", .{}), | 341 | 0b11 => return emit.fail("TODO unused variant jmp/call 0b11", .{}), |
| 342 | } | 342 | } |
| 343 | } | 343 | } |
| 344 | 344 | ||
| ... | @@ -784,7 +784,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -784,7 +784,7 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 784 | // FD | 784 | // FD |
| 785 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); | 785 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); |
| 786 | }, | 786 | }, |
| 787 | else => return emit.fail("TODO unused variant: movabs 0b{b}", .{ops.flags}), | 787 | else => return emit.fail("TODO unused movabs variant", .{}), |
| 788 | } | 788 | } |
| 789 | } | 789 | } |
| 790 | 790 | ||
| ... | @@ -978,12 +978,17 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -978,12 +978,17 @@ fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 978 | } | 978 | } |
| 979 | } | 979 | } |
| 980 | 980 | ||
| 981 | fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | 981 | fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 982 | const tag = emit.mir.instructions.items(.tag)[inst]; | 982 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 983 | assert(tag == .lea_pie); | 983 | assert(tag == .lea_pic); |
| 984 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); | 984 | const ops = emit.mir.instructions.items(.ops)[inst].decode(); |
| 985 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; | 985 | const relocation = emit.mir.instructions.items(.data)[inst].relocation; |
| 986 | 986 | ||
| 987 | switch (ops.flags) { | ||
| 988 | 0b00, 0b01 => {}, | ||
| 989 | else => return emit.fail("TODO unused LEA PIC variants 0b10 and 0b11", .{}), | ||
| 990 | } | ||
| 991 | |||
| 987 | // lea reg1, [rip + reloc] | 992 | // lea reg1, [rip + reloc] |
| 988 | // RM | 993 | // RM |
| 989 | try lowerToRmEnc( | 994 | try lowerToRmEnc( |
| ... | @@ -994,16 +999,17 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -994,16 +999,17 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 994 | ); | 999 | ); |
| 995 | 1000 | ||
| 996 | const end_offset = emit.code.items.len; | 1001 | const end_offset = emit.code.items.len; |
| 1002 | const gpa = emit.bin_file.allocator; | ||
| 997 | 1003 | ||
| 998 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { | 1004 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 999 | const reloc_type = switch (ops.flags) { | 1005 | const reloc_type = switch (ops.flags) { |
| 1000 | 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), | 1006 | 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), |
| 1001 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), | 1007 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 1002 | else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), | 1008 | else => unreachable, |
| 1003 | }; | 1009 | }; |
| 1004 | const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?; | 1010 | const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?; |
| 1005 | log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index }); | 1011 | log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index }); |
| 1006 | try atom.relocs.append(emit.bin_file.allocator, .{ | 1012 | try atom.relocs.append(gpa, .{ |
| 1007 | .offset = @intCast(u32, end_offset - 4), | 1013 | .offset = @intCast(u32, end_offset - 4), |
| 1008 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | 1014 | .target = .{ .sym_index = relocation.sym_index, .file = null }, |
| 1009 | .addend = 0, | 1015 | .addend = 0, |
| ... | @@ -1012,11 +1018,23 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1012,11 +1018,23 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1012 | .length = 2, | 1018 | .length = 2, |
| 1013 | .@"type" = reloc_type, | 1019 | .@"type" = reloc_type, |
| 1014 | }); | 1020 | }); |
| 1021 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | ||
| 1022 | const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?; | ||
| 1023 | try atom.addRelocation(coff_file, .{ | ||
| 1024 | .@"type" = switch (ops.flags) { | ||
| 1025 | 0b00 => .got, | ||
| 1026 | 0b01 => .direct, | ||
| 1027 | else => unreachable, | ||
| 1028 | }, | ||
| 1029 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | ||
| 1030 | .offset = @intCast(u32, end_offset - 4), | ||
| 1031 | .addend = 0, | ||
| 1032 | .pcrel = true, | ||
| 1033 | .length = 2, | ||
| 1034 | .prev_vaddr = atom.getSymbol(coff_file).value, | ||
| 1035 | }); | ||
| 1015 | } else { | 1036 | } else { |
| 1016 | return emit.fail( | 1037 | return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); |
| 1017 | "TODO implement lea reg, [rip + reloc] for linking backends different than MachO", | ||
| 1018 | .{}, | ||
| 1019 | ); | ||
| 1020 | } | 1038 | } |
| 1021 | } | 1039 | } |
| 1022 | 1040 |
src/arch/x86_64/Mir.zig+6-7| ... | @@ -178,11 +178,11 @@ pub const Inst = struct { | ... | @@ -178,11 +178,11 @@ pub const Inst = struct { |
| 178 | lea, | 178 | lea, |
| 179 | 179 | ||
| 180 | /// ops flags: form: | 180 | /// ops flags: form: |
| 181 | /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation | 181 | /// 0b00 reg1, [rip + reloc] // via GOT PIC |
| 182 | /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation | 182 | /// 0b01 reg1, [rip + reloc] // direct load PIC |
| 183 | /// Notes: | 183 | /// Notes: |
| 184 | /// * `Data` contains `relocation` | 184 | /// * `Data` contains `relocation` |
| 185 | lea_pie, | 185 | lea_pic, |
| 186 | 186 | ||
| 187 | /// ops flags: form: | 187 | /// ops flags: form: |
| 188 | /// 0b00 reg1, 1 | 188 | /// 0b00 reg1, 1 |
| ... | @@ -242,15 +242,14 @@ pub const Inst = struct { | ... | @@ -242,15 +242,14 @@ pub const Inst = struct { |
| 242 | imul_complex, | 242 | imul_complex, |
| 243 | 243 | ||
| 244 | /// ops flags: form: | 244 | /// ops flags: form: |
| 245 | /// 0bX0 reg1, imm64 | 245 | /// 0b00 reg1, imm64 |
| 246 | /// 0bX1 rax, moffs64 | 246 | /// 0b01 rax, moffs64 |
| 247 | /// Notes: | 247 | /// Notes: |
| 248 | /// * If reg1 is 64-bit, the immediate is 64-bit and stored | 248 | /// * If reg1 is 64-bit, the immediate is 64-bit and stored |
| 249 | /// within extra data `Imm64`. | 249 | /// within extra data `Imm64`. |
| 250 | /// * For 0bX1, reg1 (or reg2) need to be | 250 | /// * For 0b01, reg1 (or reg2) need to be |
| 251 | /// a version of rax. If reg1 == .none, then reg2 == .rax, | 251 | /// a version of rax. If reg1 == .none, then reg2 == .rax, |
| 252 | /// or vice versa. | 252 | /// or vice versa. |
| 253 | /// TODO handle scaling | ||
| 254 | movabs, | 253 | movabs, |
| 255 | 254 | ||
| 256 | /// ops flags: form: | 255 | /// ops flags: form: |
src/codegen/llvm.zig+133-4| ... | @@ -198,6 +198,122 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { | ... | @@ -198,6 +198,122 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { |
| 198 | return llvm_triple.toOwnedSliceSentinel(0); | 198 | return llvm_triple.toOwnedSliceSentinel(0); |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | pub fn targetOs(os_tag: std.Target.Os.Tag) llvm.OSType { | ||
| 202 | return switch (os_tag) { | ||
| 203 | .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS, | ||
| 204 | .windows, .uefi => .Win32, | ||
| 205 | .ananas => .Ananas, | ||
| 206 | .cloudabi => .CloudABI, | ||
| 207 | .dragonfly => .DragonFly, | ||
| 208 | .freebsd => .FreeBSD, | ||
| 209 | .fuchsia => .Fuchsia, | ||
| 210 | .ios => .IOS, | ||
| 211 | .kfreebsd => .KFreeBSD, | ||
| 212 | .linux => .Linux, | ||
| 213 | .lv2 => .Lv2, | ||
| 214 | .macos => .MacOSX, | ||
| 215 | .netbsd => .NetBSD, | ||
| 216 | .openbsd => .OpenBSD, | ||
| 217 | .solaris => .Solaris, | ||
| 218 | .zos => .ZOS, | ||
| 219 | .haiku => .Haiku, | ||
| 220 | .minix => .Minix, | ||
| 221 | .rtems => .RTEMS, | ||
| 222 | .nacl => .NaCl, | ||
| 223 | .aix => .AIX, | ||
| 224 | .cuda => .CUDA, | ||
| 225 | .nvcl => .NVCL, | ||
| 226 | .amdhsa => .AMDHSA, | ||
| 227 | .ps4 => .PS4, | ||
| 228 | .ps5 => .PS5, | ||
| 229 | .elfiamcu => .ELFIAMCU, | ||
| 230 | .tvos => .TvOS, | ||
| 231 | .watchos => .WatchOS, | ||
| 232 | .mesa3d => .Mesa3D, | ||
| 233 | .contiki => .Contiki, | ||
| 234 | .amdpal => .AMDPAL, | ||
| 235 | .hermit => .HermitCore, | ||
| 236 | .hurd => .Hurd, | ||
| 237 | .wasi => .WASI, | ||
| 238 | .emscripten => .Emscripten, | ||
| 239 | .driverkit => .DriverKit, | ||
| 240 | .shadermodel => .ShaderModel, | ||
| 241 | }; | ||
| 242 | } | ||
| 243 | |||
| 244 | pub fn targetArch(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { | ||
| 245 | return switch (arch_tag) { | ||
| 246 | .arm => .arm, | ||
| 247 | .armeb => .armeb, | ||
| 248 | .aarch64 => .aarch64, | ||
| 249 | .aarch64_be => .aarch64_be, | ||
| 250 | .aarch64_32 => .aarch64_32, | ||
| 251 | .arc => .arc, | ||
| 252 | .avr => .avr, | ||
| 253 | .bpfel => .bpfel, | ||
| 254 | .bpfeb => .bpfeb, | ||
| 255 | .csky => .csky, | ||
| 256 | .dxil => .dxil, | ||
| 257 | .hexagon => .hexagon, | ||
| 258 | .loongarch32 => .loongarch32, | ||
| 259 | .loongarch64 => .loongarch64, | ||
| 260 | .m68k => .m68k, | ||
| 261 | .mips => .mips, | ||
| 262 | .mipsel => .mipsel, | ||
| 263 | .mips64 => .mips64, | ||
| 264 | .mips64el => .mips64el, | ||
| 265 | .msp430 => .msp430, | ||
| 266 | .powerpc => .ppc, | ||
| 267 | .powerpcle => .ppcle, | ||
| 268 | .powerpc64 => .ppc64, | ||
| 269 | .powerpc64le => .ppc64le, | ||
| 270 | .r600 => .r600, | ||
| 271 | .amdgcn => .amdgcn, | ||
| 272 | .riscv32 => .riscv32, | ||
| 273 | .riscv64 => .riscv64, | ||
| 274 | .sparc => .sparc, | ||
| 275 | .sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9. | ||
| 276 | .sparcel => .sparcel, | ||
| 277 | .s390x => .systemz, | ||
| 278 | .tce => .tce, | ||
| 279 | .tcele => .tcele, | ||
| 280 | .thumb => .thumb, | ||
| 281 | .thumbeb => .thumbeb, | ||
| 282 | .i386 => .x86, | ||
| 283 | .x86_64 => .x86_64, | ||
| 284 | .xcore => .xcore, | ||
| 285 | .nvptx => .nvptx, | ||
| 286 | .nvptx64 => .nvptx64, | ||
| 287 | .le32 => .le32, | ||
| 288 | .le64 => .le64, | ||
| 289 | .amdil => .amdil, | ||
| 290 | .amdil64 => .amdil64, | ||
| 291 | .hsail => .hsail, | ||
| 292 | .hsail64 => .hsail64, | ||
| 293 | .spir => .spir, | ||
| 294 | .spir64 => .spir64, | ||
| 295 | .kalimba => .kalimba, | ||
| 296 | .shave => .shave, | ||
| 297 | .lanai => .lanai, | ||
| 298 | .wasm32 => .wasm32, | ||
| 299 | .wasm64 => .wasm64, | ||
| 300 | .renderscript32 => .renderscript32, | ||
| 301 | .renderscript64 => .renderscript64, | ||
| 302 | .ve => .ve, | ||
| 303 | .spu_2, .spirv32, .spirv64 => .UnknownArch, | ||
| 304 | }; | ||
| 305 | } | ||
| 306 | |||
| 307 | pub fn supportsTailCall(target: std.Target) bool { | ||
| 308 | switch (target.cpu.arch) { | ||
| 309 | .wasm32, .wasm64 => return std.Target.wasm.featureSetHas(target.cpu.features, .tail_call), | ||
| 310 | // Although these ISAs support tail calls, LLVM does not support tail calls on them. | ||
| 311 | .mips, .mipsel, .mips64, .mips64el => return false, | ||
| 312 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => return false, | ||
| 313 | else => return true, | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 201 | pub const Object = struct { | 317 | pub const Object = struct { |
| 202 | gpa: Allocator, | 318 | gpa: Allocator, |
| 203 | module: *Module, | 319 | module: *Module, |
| ... | @@ -4559,7 +4675,7 @@ pub const FuncGen = struct { | ... | @@ -4559,7 +4675,7 @@ pub const FuncGen = struct { |
| 4559 | "", | 4675 | "", |
| 4560 | ); | 4676 | ); |
| 4561 | 4677 | ||
| 4562 | if (return_type.isNoReturn()) { | 4678 | if (return_type.isNoReturn() and attr != .AlwaysTail) { |
| 4563 | _ = self.builder.buildUnreachable(); | 4679 | _ = self.builder.buildUnreachable(); |
| 4564 | return null; | 4680 | return null; |
| 4565 | } | 4681 | } |
| ... | @@ -8644,12 +8760,26 @@ pub const FuncGen = struct { | ... | @@ -8644,12 +8760,26 @@ pub const FuncGen = struct { |
| 8644 | const union_llvm_ty = try self.dg.lowerType(union_ty); | 8760 | const union_llvm_ty = try self.dg.lowerType(union_ty); |
| 8645 | const target = self.dg.module.getTarget(); | 8761 | const target = self.dg.module.getTarget(); |
| 8646 | const layout = union_ty.unionGetLayout(target); | 8762 | const layout = union_ty.unionGetLayout(target); |
| 8763 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; | ||
| 8764 | const tag_int = blk: { | ||
| 8765 | const tag_ty = union_ty.unionTagTypeHypothetical(); | ||
| 8766 | const union_field_name = union_obj.fields.keys()[extra.field_index]; | ||
| 8767 | const enum_field_index = tag_ty.enumFieldIndex(union_field_name).?; | ||
| 8768 | var tag_val_payload: Value.Payload.U32 = .{ | ||
| 8769 | .base = .{ .tag = .enum_field_index }, | ||
| 8770 | .data = @intCast(u32, enum_field_index), | ||
| 8771 | }; | ||
| 8772 | const tag_val = Value.initPayload(&tag_val_payload.base); | ||
| 8773 | var int_payload: Value.Payload.U64 = undefined; | ||
| 8774 | const tag_int_val = tag_val.enumToInt(tag_ty, &int_payload); | ||
| 8775 | break :blk tag_int_val.toUnsignedInt(target); | ||
| 8776 | }; | ||
| 8647 | if (layout.payload_size == 0) { | 8777 | if (layout.payload_size == 0) { |
| 8648 | if (layout.tag_size == 0) { | 8778 | if (layout.tag_size == 0) { |
| 8649 | return null; | 8779 | return null; |
| 8650 | } | 8780 | } |
| 8651 | assert(!isByRef(union_ty)); | 8781 | assert(!isByRef(union_ty)); |
| 8652 | return union_llvm_ty.constInt(extra.field_index, .False); | 8782 | return union_llvm_ty.constInt(tag_int, .False); |
| 8653 | } | 8783 | } |
| 8654 | assert(isByRef(union_ty)); | 8784 | assert(isByRef(union_ty)); |
| 8655 | // The llvm type of the alloca will the the named LLVM union type, which will not | 8785 | // The llvm type of the alloca will the the named LLVM union type, which will not |
| ... | @@ -8658,7 +8788,6 @@ pub const FuncGen = struct { | ... | @@ -8658,7 +8788,6 @@ pub const FuncGen = struct { |
| 8658 | // then set the fields appropriately. | 8788 | // then set the fields appropriately. |
| 8659 | const result_ptr = self.buildAlloca(union_llvm_ty); | 8789 | const result_ptr = self.buildAlloca(union_llvm_ty); |
| 8660 | const llvm_payload = try self.resolveInst(extra.init); | 8790 | const llvm_payload = try self.resolveInst(extra.init); |
| 8661 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; | ||
| 8662 | assert(union_obj.haveFieldTypes()); | 8791 | assert(union_obj.haveFieldTypes()); |
| 8663 | const field = union_obj.fields.values()[extra.field_index]; | 8792 | const field = union_obj.fields.values()[extra.field_index]; |
| 8664 | const field_llvm_ty = try self.dg.lowerType(field.ty); | 8793 | const field_llvm_ty = try self.dg.lowerType(field.ty); |
| ... | @@ -8742,7 +8871,7 @@ pub const FuncGen = struct { | ... | @@ -8742,7 +8871,7 @@ pub const FuncGen = struct { |
| 8742 | }; | 8871 | }; |
| 8743 | const field_ptr = self.builder.buildInBoundsGEP(llvm_union_ty, casted_ptr, &indices, indices.len, ""); | 8872 | const field_ptr = self.builder.buildInBoundsGEP(llvm_union_ty, casted_ptr, &indices, indices.len, ""); |
| 8744 | const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); | 8873 | const tag_llvm_ty = try self.dg.lowerType(union_obj.tag_ty); |
| 8745 | const llvm_tag = tag_llvm_ty.constInt(extra.field_index, .False); | 8874 | const llvm_tag = tag_llvm_ty.constInt(tag_int, .False); |
| 8746 | const store_inst = self.builder.buildStore(llvm_tag, field_ptr); | 8875 | const store_inst = self.builder.buildStore(llvm_tag, field_ptr); |
| 8747 | store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target)); | 8876 | store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target)); |
| 8748 | } | 8877 | } |
src/link.zig+7-6| ... | @@ -245,8 +245,8 @@ pub const File = struct { | ... | @@ -245,8 +245,8 @@ pub const File = struct { |
| 245 | 245 | ||
| 246 | pub const LinkBlock = union { | 246 | pub const LinkBlock = union { |
| 247 | elf: Elf.TextBlock, | 247 | elf: Elf.TextBlock, |
| 248 | coff: Coff.TextBlock, | 248 | coff: Coff.Atom, |
| 249 | macho: MachO.TextBlock, | 249 | macho: MachO.Atom, |
| 250 | plan9: Plan9.DeclBlock, | 250 | plan9: Plan9.DeclBlock, |
| 251 | c: void, | 251 | c: void, |
| 252 | wasm: Wasm.DeclBlock, | 252 | wasm: Wasm.DeclBlock, |
| ... | @@ -267,7 +267,7 @@ pub const File = struct { | ... | @@ -267,7 +267,7 @@ pub const File = struct { |
| 267 | 267 | ||
| 268 | pub const Export = union { | 268 | pub const Export = union { |
| 269 | elf: Elf.Export, | 269 | elf: Elf.Export, |
| 270 | coff: void, | 270 | coff: Coff.Export, |
| 271 | macho: MachO.Export, | 271 | macho: MachO.Export, |
| 272 | plan9: Plan9.Export, | 272 | plan9: Plan9.Export, |
| 273 | c: void, | 273 | c: void, |
| ... | @@ -934,9 +934,10 @@ pub const File = struct { | ... | @@ -934,9 +934,10 @@ pub const File = struct { |
| 934 | std.debug.print("\n", .{}); | 934 | std.debug.print("\n", .{}); |
| 935 | } | 935 | } |
| 936 | 936 | ||
| 937 | const llvm = @import("codegen/llvm/bindings.zig"); | 937 | const llvm_bindings = @import("codegen/llvm/bindings.zig"); |
| 938 | const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag); | 938 | const llvm = @import("codegen/llvm.zig"); |
| 939 | const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type); | 939 | const os_tag = llvm.targetOs(base.options.target.os.tag); |
| 940 | const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag); | ||
| 940 | if (bad) return error.UnableToWriteArchive; | 941 | if (bad) return error.UnableToWriteArchive; |
| 941 | 942 | ||
| 942 | if (!base.options.disable_lld_caching) { | 943 | if (!base.options.disable_lld_caching) { |
src/link/Coff.zig+1247-1186| ... | @@ -1,131 +1,179 @@ | ... | @@ -1,131 +1,179 @@ |
| 1 | const Coff = @This(); | 1 | const Coff = @This(); |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | ||
| 4 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 5 | const log = std.log.scoped(.link); | ||
| 6 | const Allocator = std.mem.Allocator; | ||
| 7 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 8 | const fs = std.fs; | 7 | const coff = std.coff; |
| 9 | const allocPrint = std.fmt.allocPrint; | 8 | const fmt = std.fmt; |
| 9 | const log = std.log.scoped(.link); | ||
| 10 | const math = std.math; | ||
| 10 | const mem = std.mem; | 11 | const mem = std.mem; |
| 11 | 12 | ||
| 12 | const lldMain = @import("../main.zig").lldMain; | 13 | const Allocator = std.mem.Allocator; |
| 13 | const trace = @import("../tracy.zig").trace; | 14 | |
| 14 | const Module = @import("../Module.zig"); | ||
| 15 | const Compilation = @import("../Compilation.zig"); | ||
| 16 | const codegen = @import("../codegen.zig"); | 15 | const codegen = @import("../codegen.zig"); |
| 17 | const link = @import("../link.zig"); | 16 | const link = @import("../link.zig"); |
| 18 | const build_options = @import("build_options"); | 17 | const lld = @import("Coff/lld.zig"); |
| 19 | const Cache = @import("../Cache.zig"); | 18 | const trace = @import("../tracy.zig").trace; |
| 20 | const mingw = @import("../mingw.zig"); | 19 | |
| 21 | const Air = @import("../Air.zig"); | 20 | const Air = @import("../Air.zig"); |
| 21 | pub const Atom = @import("Coff/Atom.zig"); | ||
| 22 | const Compilation = @import("../Compilation.zig"); | ||
| 22 | const Liveness = @import("../Liveness.zig"); | 23 | const Liveness = @import("../Liveness.zig"); |
| 23 | const LlvmObject = @import("../codegen/llvm.zig").Object; | 24 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 25 | const Module = @import("../Module.zig"); | ||
| 26 | const Object = @import("Coff/Object.zig"); | ||
| 27 | const StringTable = @import("strtab.zig").StringTable; | ||
| 24 | const TypedValue = @import("../TypedValue.zig"); | 28 | const TypedValue = @import("../TypedValue.zig"); |
| 25 | 29 | ||
| 26 | const allocation_padding = 4 / 3; | ||
| 27 | const minimum_text_block_size = 64 * allocation_padding; | ||
| 28 | |||
| 29 | const section_alignment = 4096; | ||
| 30 | const file_alignment = 512; | ||
| 31 | const default_image_base = 0x400_000; | ||
| 32 | const section_table_size = 2 * 40; | ||
| 33 | comptime { | ||
| 34 | assert(mem.isAligned(default_image_base, section_alignment)); | ||
| 35 | } | ||
| 36 | |||
| 37 | pub const base_tag: link.File.Tag = .coff; | 30 | pub const base_tag: link.File.Tag = .coff; |
| 38 | 31 | ||
| 39 | const msdos_stub = @embedFile("msdos-stub.bin"); | 32 | const msdos_stub = @embedFile("msdos-stub.bin"); |
| 33 | const N_DATA_DIRS: u5 = 16; | ||
| 40 | 34 | ||
| 41 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. | 35 | /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. |
| 42 | llvm_object: ?*LlvmObject = null, | 36 | llvm_object: ?*LlvmObject = null, |
| 43 | 37 | ||
| 44 | base: link.File, | 38 | base: link.File, |
| 45 | ptr_width: PtrWidth, | ||
| 46 | error_flags: link.File.ErrorFlags = .{}, | 39 | error_flags: link.File.ErrorFlags = .{}, |
| 47 | 40 | ||
| 48 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, | 41 | ptr_width: PtrWidth, |
| 49 | last_text_block: ?*TextBlock = null, | 42 | page_size: u32, |
| 50 | 43 | ||
| 51 | /// Section table file pointer. | 44 | objects: std.ArrayListUnmanaged(Object) = .{}, |
| 52 | section_table_offset: u32 = 0, | 45 | |
| 53 | /// Section data file pointer. | 46 | sections: std.MultiArrayList(Section) = .{}, |
| 54 | section_data_offset: u32 = 0, | 47 | data_directories: [N_DATA_DIRS]coff.ImageDataDirectory, |
| 55 | /// Optional header file pointer. | 48 | |
| 56 | optional_header_offset: u32 = 0, | 49 | text_section_index: ?u16 = null, |
| 57 | 50 | got_section_index: ?u16 = null, | |
| 58 | /// Absolute virtual address of the offset table when the executable is loaded in memory. | 51 | rdata_section_index: ?u16 = null, |
| 59 | offset_table_virtual_address: u32 = 0, | 52 | data_section_index: ?u16 = null, |
| 60 | /// Current size of the offset table on disk, must be a multiple of `file_alignment` | 53 | |
| 61 | offset_table_size: u32 = 0, | 54 | locals: std.ArrayListUnmanaged(coff.Symbol) = .{}, |
| 62 | /// Contains absolute virtual addresses | 55 | globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, |
| 63 | offset_table: std.ArrayListUnmanaged(u64) = .{}, | 56 | |
| 64 | /// Free list of offset table indices | 57 | locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 65 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, | 58 | |
| 59 | strtab: StringTable(.strtab) = .{}, | ||
| 60 | strtab_offset: ?u32 = null, | ||
| 61 | |||
| 62 | got_entries: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, | ||
| 63 | got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, | ||
| 66 | 64 | ||
| 67 | /// Virtual address of the entry point procedure relative to image base. | 65 | /// Virtual address of the entry point procedure relative to image base. |
| 68 | entry_addr: ?u32 = null, | 66 | entry_addr: ?u32 = null, |
| 69 | 67 | ||
| 70 | /// Absolute virtual address of the text section when the executable is loaded in memory. | 68 | /// Table of Decls that are currently alive. |
| 71 | text_section_virtual_address: u32 = 0, | 69 | /// We store them here so that we can properly dispose of any allocated |
| 72 | /// Current size of the `.text` section on disk, must be a multiple of `file_alignment` | 70 | /// memory within the atom in the incremental linker. |
| 73 | text_section_size: u32 = 0, | 71 | /// TODO consolidate this. |
| 72 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{}, | ||
| 73 | |||
| 74 | /// List of atoms that are either synthetic or map directly to the Zig source program. | ||
| 75 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | ||
| 76 | |||
| 77 | /// Table of atoms indexed by the symbol index. | ||
| 78 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, | ||
| 79 | |||
| 80 | /// Table of unnamed constants associated with a parent `Decl`. | ||
| 81 | /// We store them here so that we can free the constants whenever the `Decl` | ||
| 82 | /// needs updating or is freed. | ||
| 83 | /// | ||
| 84 | /// For example, | ||
| 85 | /// | ||
| 86 | /// ```zig | ||
| 87 | /// const Foo = struct{ | ||
| 88 | /// a: u8, | ||
| 89 | /// }; | ||
| 90 | /// | ||
| 91 | /// pub fn main() void { | ||
| 92 | /// var foo = Foo{ .a = 1 }; | ||
| 93 | /// _ = foo; | ||
| 94 | /// } | ||
| 95 | /// ``` | ||
| 96 | /// | ||
| 97 | /// value assigned to label `foo` is an unnamed constant belonging/associated | ||
| 98 | /// with `Decl` `main`, and lives as long as that `Decl`. | ||
| 99 | unnamed_const_atoms: UnnamedConstTable = .{}, | ||
| 100 | |||
| 101 | /// A table of relocations indexed by the owning them `TextBlock`. | ||
| 102 | /// Note that once we refactor `TextBlock`'s lifetime and ownership rules, | ||
| 103 | /// this will be a table indexed by index into the list of Atoms. | ||
| 104 | relocs: RelocTable = .{}, | ||
| 105 | |||
| 106 | pub const Reloc = struct { | ||
| 107 | @"type": enum { | ||
| 108 | got, | ||
| 109 | direct, | ||
| 110 | }, | ||
| 111 | target: SymbolWithLoc, | ||
| 112 | offset: u32, | ||
| 113 | addend: u32, | ||
| 114 | pcrel: bool, | ||
| 115 | length: u2, | ||
| 116 | prev_vaddr: u32, | ||
| 117 | }; | ||
| 74 | 118 | ||
| 75 | offset_table_size_dirty: bool = false, | 119 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Reloc)); |
| 76 | text_section_size_dirty: bool = false, | 120 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); |
| 77 | /// This flag is set when the virtual size of the whole image file when loaded in memory has changed | 121 | |
| 78 | /// and needs to be updated in the optional header. | 122 | const default_file_alignment: u16 = 0x200; |
| 79 | size_of_image_dirty: bool = false, | 123 | const default_image_base_dll: u64 = 0x10000000; |
| 124 | const default_image_base_exe: u64 = 0x400000; | ||
| 125 | const default_size_of_stack_reserve: u32 = 0x1000000; | ||
| 126 | const default_size_of_stack_commit: u32 = 0x1000; | ||
| 127 | const default_size_of_heap_reserve: u32 = 0x100000; | ||
| 128 | const default_size_of_heap_commit: u32 = 0x1000; | ||
| 129 | |||
| 130 | const Section = struct { | ||
| 131 | header: coff.SectionHeader, | ||
| 132 | |||
| 133 | last_atom: ?*Atom = null, | ||
| 134 | |||
| 135 | /// A list of atoms that have surplus capacity. This list can have false | ||
| 136 | /// positives, as functions grow and shrink over time, only sometimes being added | ||
| 137 | /// or removed from the freelist. | ||
| 138 | /// | ||
| 139 | /// An atom has surplus capacity when its overcapacity value is greater than | ||
| 140 | /// padToIdeal(minimum_atom_size). That is, when it has so | ||
| 141 | /// much extra capacity, that we could fit a small new symbol in it, itself with | ||
| 142 | /// ideal_capacity or more. | ||
| 143 | /// | ||
| 144 | /// Ideal capacity is defined by size + (size / ideal_factor). | ||
| 145 | /// | ||
| 146 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that | ||
| 147 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | ||
| 148 | /// allocate a fresh atom, which will have ideal capacity, and then grow it | ||
| 149 | /// by 1 byte. It will then have -1 overcapacity. | ||
| 150 | free_list: std.ArrayListUnmanaged(*Atom) = .{}, | ||
| 151 | }; | ||
| 80 | 152 | ||
| 81 | pub const PtrWidth = enum { p32, p64 }; | 153 | pub const PtrWidth = enum { p32, p64 }; |
| 154 | pub const SrcFn = void; | ||
| 82 | 155 | ||
| 83 | pub const TextBlock = struct { | 156 | pub const Export = struct { |
| 84 | /// Offset of the code relative to the start of the text section | 157 | sym_index: ?u32 = null, |
| 85 | text_offset: u32, | 158 | }; |
| 86 | /// Used size of the text block | ||
| 87 | size: u32, | ||
| 88 | /// This field is undefined for symbols with size = 0. | ||
| 89 | offset_table_index: u32, | ||
| 90 | /// Points to the previous and next neighbors, based on the `text_offset`. | ||
| 91 | /// This can be used to find, for example, the capacity of this `TextBlock`. | ||
| 92 | prev: ?*TextBlock, | ||
| 93 | next: ?*TextBlock, | ||
| 94 | |||
| 95 | pub const empty = TextBlock{ | ||
| 96 | .text_offset = 0, | ||
| 97 | .size = 0, | ||
| 98 | .offset_table_index = undefined, | ||
| 99 | .prev = null, | ||
| 100 | .next = null, | ||
| 101 | }; | ||
| 102 | |||
| 103 | /// Returns how much room there is to grow in virtual address space. | ||
| 104 | fn capacity(self: TextBlock) u64 { | ||
| 105 | if (self.next) |next| { | ||
| 106 | return next.text_offset - self.text_offset; | ||
| 107 | } | ||
| 108 | // This is the last block, the capacity is only limited by the address space. | ||
| 109 | return std.math.maxInt(u32) - self.text_offset; | ||
| 110 | } | ||
| 111 | 159 | ||
| 112 | fn freeListEligible(self: TextBlock) bool { | 160 | pub const SymbolWithLoc = struct { |
| 113 | // No need to keep a free list node for the last block. | 161 | // Index into the respective symbol table. |
| 114 | const next = self.next orelse return false; | 162 | sym_index: u32, |
| 115 | const cap = next.text_offset - self.text_offset; | ||
| 116 | const ideal_cap = self.size * allocation_padding; | ||
| 117 | if (cap <= ideal_cap) return false; | ||
| 118 | const surplus = cap - ideal_cap; | ||
| 119 | return surplus >= minimum_text_block_size; | ||
| 120 | } | ||
| 121 | 163 | ||
| 122 | /// Absolute virtual address of the text block when the file is loaded in memory. | 164 | // null means it's a synthetic global or Zig source. |
| 123 | fn getVAddr(self: TextBlock, coff: Coff) u32 { | 165 | file: ?u32 = null, |
| 124 | return coff.text_section_virtual_address + self.text_offset; | ||
| 125 | } | ||
| 126 | }; | 166 | }; |
| 127 | 167 | ||
| 128 | pub const SrcFn = void; | 168 | /// When allocating, the ideal_capacity is calculated by |
| 169 | /// actual_capacity + (actual_capacity / ideal_factor) | ||
| 170 | const ideal_factor = 3; | ||
| 171 | |||
| 172 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at | ||
| 173 | /// it as a possible place to put new symbols, it must have enough room for this many bytes | ||
| 174 | /// (plus extra for reserved capacity). | ||
| 175 | const minimum_text_block_size = 64; | ||
| 176 | pub const min_text_capacity = padToIdeal(minimum_text_block_size); | ||
| 129 | 177 | ||
| 130 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff { | 178 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff { |
| 131 | assert(options.target.ofmt == .coff); | 179 | assert(options.target.ofmt == .coff); |
| ... | @@ -144,257 +192,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -144,257 +192,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 144 | }); | 192 | }); |
| 145 | self.base.file = file; | 193 | self.base.file = file; |
| 146 | 194 | ||
| 147 | // TODO Write object specific relocations, COFF symbol table, then enable object file output. | 195 | try self.populateMissingMetadata(); |
| 148 | switch (options.output_mode) { | ||
| 149 | .Exe => {}, | ||
| 150 | .Obj => return error.TODOImplementWritingObjFiles, | ||
| 151 | .Lib => return error.TODOImplementWritingLibFiles, | ||
| 152 | } | ||
| 153 | |||
| 154 | var coff_file_header_offset: u32 = 0; | ||
| 155 | if (options.output_mode == .Exe) { | ||
| 156 | // Write the MS-DOS stub and the PE signature | ||
| 157 | try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0); | ||
| 158 | coff_file_header_offset = msdos_stub.len + 4; | ||
| 159 | } | ||
| 160 | |||
| 161 | // COFF file header | ||
| 162 | const data_directory_count = 0; | ||
| 163 | var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined; | ||
| 164 | var index: usize = 0; | ||
| 165 | |||
| 166 | const machine = self.base.options.target.cpu.arch.toCoffMachine(); | ||
| 167 | if (machine == .Unknown) { | ||
| 168 | return error.UnsupportedCOFFArchitecture; | ||
| 169 | } | ||
| 170 | mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine)); | ||
| 171 | index += 2; | ||
| 172 | |||
| 173 | // Number of sections (we only use .got, .text) | ||
| 174 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); | ||
| 175 | index += 2; | ||
| 176 | // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32) | ||
| 177 | mem.set(u8, hdr_data[index..][0..12], 0); | ||
| 178 | index += 12; | ||
| 179 | |||
| 180 | const optional_header_size = switch (options.output_mode) { | ||
| 181 | .Exe => data_directory_count * 8 + switch (self.ptr_width) { | ||
| 182 | .p32 => @as(u16, 96), | ||
| 183 | .p64 => 112, | ||
| 184 | }, | ||
| 185 | else => 0, | ||
| 186 | }; | ||
| 187 | |||
| 188 | const section_table_offset = coff_file_header_offset + 20 + optional_header_size; | ||
| 189 | const default_offset_table_size = file_alignment; | ||
| 190 | const default_size_of_code = 0; | ||
| 191 | |||
| 192 | self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); | ||
| 193 | const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); | ||
| 194 | self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address; | ||
| 195 | self.offset_table_size = default_offset_table_size; | ||
| 196 | self.section_table_offset = section_table_offset; | ||
| 197 | self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment; | ||
| 198 | self.text_section_size = default_size_of_code; | ||
| 199 | |||
| 200 | // Size of file when loaded in memory | ||
| 201 | const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment); | ||
| 202 | |||
| 203 | mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); | ||
| 204 | index += 2; | ||
| 205 | |||
| 206 | // Characteristics | ||
| 207 | var characteristics: std.coff.CoffHeaderFlags = .{ | ||
| 208 | .DEBUG_STRIPPED = 1, // TODO remove debug info stripped flag when necessary | ||
| 209 | .RELOCS_STRIPPED = 1, | ||
| 210 | }; | ||
| 211 | if (options.output_mode == .Exe) { | ||
| 212 | characteristics.EXECUTABLE_IMAGE = 1; | ||
| 213 | } | ||
| 214 | switch (self.ptr_width) { | ||
| 215 | .p32 => characteristics.@"32BIT_MACHINE" = 1, | ||
| 216 | .p64 => characteristics.LARGE_ADDRESS_AWARE = 1, | ||
| 217 | } | ||
| 218 | mem.writeIntLittle(u16, hdr_data[index..][0..2], @bitCast(u16, characteristics)); | ||
| 219 | index += 2; | ||
| 220 | |||
| 221 | assert(index == 20); | ||
| 222 | try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset); | ||
| 223 | |||
| 224 | if (options.output_mode == .Exe) { | ||
| 225 | self.optional_header_offset = coff_file_header_offset + 20; | ||
| 226 | // Optional header | ||
| 227 | index = 0; | ||
| 228 | mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { | ||
| 229 | .p32 => @as(u16, 0x10b), | ||
| 230 | .p64 => 0x20b, | ||
| 231 | }); | ||
| 232 | index += 2; | ||
| 233 | |||
| 234 | // Linker version (u8 + u8) | ||
| 235 | mem.set(u8, hdr_data[index..][0..2], 0); | ||
| 236 | index += 2; | ||
| 237 | |||
| 238 | // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32) | ||
| 239 | mem.set(u8, hdr_data[index..][0..20], 0); | ||
| 240 | index += 20; | ||
| 241 | |||
| 242 | if (self.ptr_width == .p32) { | ||
| 243 | // Base of data relative to the image base (UNUSED) | ||
| 244 | mem.set(u8, hdr_data[index..][0..4], 0); | ||
| 245 | index += 4; | ||
| 246 | |||
| 247 | // Image base address | ||
| 248 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base); | ||
| 249 | index += 4; | ||
| 250 | } else { | ||
| 251 | // Image base address | ||
| 252 | mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base); | ||
| 253 | index += 8; | ||
| 254 | } | ||
| 255 | |||
| 256 | // Section alignment | ||
| 257 | mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment); | ||
| 258 | index += 4; | ||
| 259 | // File alignment | ||
| 260 | mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment); | ||
| 261 | index += 4; | ||
| 262 | // Required OS version, 6.0 is vista | ||
| 263 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); | ||
| 264 | index += 2; | ||
| 265 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); | ||
| 266 | index += 2; | ||
| 267 | // Image version | ||
| 268 | mem.set(u8, hdr_data[index..][0..4], 0); | ||
| 269 | index += 4; | ||
| 270 | // Required subsystem version, same as OS version | ||
| 271 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); | ||
| 272 | index += 2; | ||
| 273 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); | ||
| 274 | index += 2; | ||
| 275 | // Reserved zeroes (u32) | ||
| 276 | mem.set(u8, hdr_data[index..][0..4], 0); | ||
| 277 | index += 4; | ||
| 278 | mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image); | ||
| 279 | index += 4; | ||
| 280 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); | ||
| 281 | index += 4; | ||
| 282 | // CheckSum (u32) | ||
| 283 | mem.set(u8, hdr_data[index..][0..4], 0); | ||
| 284 | index += 4; | ||
| 285 | // Subsystem, TODO: Let users specify the subsystem, always CUI for now | ||
| 286 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); | ||
| 287 | index += 2; | ||
| 288 | // DLL characteristics | ||
| 289 | mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0); | ||
| 290 | index += 2; | ||
| 291 | |||
| 292 | switch (self.ptr_width) { | ||
| 293 | .p32 => { | ||
| 294 | // Size of stack reserve + commit | ||
| 295 | mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); | ||
| 296 | index += 4; | ||
| 297 | mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); | ||
| 298 | index += 4; | ||
| 299 | // Size of heap reserve + commit | ||
| 300 | mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); | ||
| 301 | index += 4; | ||
| 302 | mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); | ||
| 303 | index += 4; | ||
| 304 | }, | ||
| 305 | .p64 => { | ||
| 306 | // Size of stack reserve + commit | ||
| 307 | mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); | ||
| 308 | index += 8; | ||
| 309 | mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); | ||
| 310 | index += 8; | ||
| 311 | // Size of heap reserve + commit | ||
| 312 | mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); | ||
| 313 | index += 8; | ||
| 314 | mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); | ||
| 315 | index += 8; | ||
| 316 | }, | ||
| 317 | } | ||
| 318 | |||
| 319 | // Reserved zeroes | ||
| 320 | mem.set(u8, hdr_data[index..][0..4], 0); | ||
| 321 | index += 4; | ||
| 322 | |||
| 323 | // Number of data directories | ||
| 324 | mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); | ||
| 325 | index += 4; | ||
| 326 | // Initialize data directories to zero | ||
| 327 | mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0); | ||
| 328 | index += data_directory_count * 8; | ||
| 329 | |||
| 330 | assert(index == optional_header_size); | ||
| 331 | } | ||
| 332 | |||
| 333 | // Write section table. | ||
| 334 | // First, the .got section | ||
| 335 | hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*; | ||
| 336 | index += 8; | ||
| 337 | if (options.output_mode == .Exe) { | ||
| 338 | // Virtual size (u32) | ||
| 339 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); | ||
| 340 | index += 4; | ||
| 341 | // Virtual address (u32) | ||
| 342 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base); | ||
| 343 | index += 4; | ||
| 344 | } else { | ||
| 345 | mem.set(u8, hdr_data[index..][0..8], 0); | ||
| 346 | index += 8; | ||
| 347 | } | ||
| 348 | // Size of raw data (u32) | ||
| 349 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); | ||
| 350 | index += 4; | ||
| 351 | // File pointer to the start of the section | ||
| 352 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); | ||
| 353 | index += 4; | ||
| 354 | // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) | ||
| 355 | mem.set(u8, hdr_data[index..][0..12], 0); | ||
| 356 | index += 12; | ||
| 357 | // Section flags | ||
| 358 | mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{ | ||
| 359 | .CNT_INITIALIZED_DATA = 1, | ||
| 360 | .MEM_READ = 1, | ||
| 361 | })); | ||
| 362 | index += 4; | ||
| 363 | // Then, the .text section | ||
| 364 | hdr_data[index..][0..8].* = ".text\x00\x00\x00".*; | ||
| 365 | index += 8; | ||
| 366 | if (options.output_mode == .Exe) { | ||
| 367 | // Virtual size (u32) | ||
| 368 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); | ||
| 369 | index += 4; | ||
| 370 | // Virtual address (u32) | ||
| 371 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base); | ||
| 372 | index += 4; | ||
| 373 | } else { | ||
| 374 | mem.set(u8, hdr_data[index..][0..8], 0); | ||
| 375 | index += 8; | ||
| 376 | } | ||
| 377 | // Size of raw data (u32) | ||
| 378 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); | ||
| 379 | index += 4; | ||
| 380 | // File pointer to the start of the section | ||
| 381 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size); | ||
| 382 | index += 4; | ||
| 383 | // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) | ||
| 384 | mem.set(u8, hdr_data[index..][0..12], 0); | ||
| 385 | index += 12; | ||
| 386 | // Section flags | ||
| 387 | mem.writeIntLittle(u32, hdr_data[index..][0..4], @bitCast(u32, std.coff.SectionHeaderFlags{ | ||
| 388 | .CNT_CODE = 1, | ||
| 389 | .MEM_EXECUTE = 1, | ||
| 390 | .MEM_READ = 1, | ||
| 391 | .MEM_WRITE = 1, | ||
| 392 | })); | ||
| 393 | index += 4; | ||
| 394 | |||
| 395 | assert(index == optional_header_size + section_table_size); | ||
| 396 | try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset); | ||
| 397 | try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code); | ||
| 398 | 196 | ||
| 399 | return self; | 197 | return self; |
| 400 | } | 198 | } |
| ... | @@ -405,6 +203,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { | ... | @@ -405,6 +203,9 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { |
| 405 | 33...64 => .p64, | 203 | 33...64 => .p64, |
| 406 | else => return error.UnsupportedCOFFArchitecture, | 204 | else => return error.UnsupportedCOFFArchitecture, |
| 407 | }; | 205 | }; |
| 206 | const page_size: u32 = switch (options.target.cpu.arch) { | ||
| 207 | else => 0x1000, | ||
| 208 | }; | ||
| 408 | const self = try gpa.create(Coff); | 209 | const self = try gpa.create(Coff); |
| 409 | errdefer gpa.destroy(self); | 210 | errdefer gpa.destroy(self); |
| 410 | self.* = .{ | 211 | self.* = .{ |
| ... | @@ -415,6 +216,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { | ... | @@ -415,6 +216,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { |
| 415 | .file = null, | 216 | .file = null, |
| 416 | }, | 217 | }, |
| 417 | .ptr_width = ptr_width, | 218 | .ptr_width = ptr_width, |
| 219 | .page_size = page_size, | ||
| 220 | .data_directories = comptime mem.zeroes([N_DATA_DIRS]coff.ImageDataDirectory), | ||
| 418 | }; | 221 | }; |
| 419 | 222 | ||
| 420 | const use_llvm = build_options.have_llvm and options.use_llvm; | 223 | const use_llvm = build_options.have_llvm and options.use_llvm; |
| ... | @@ -425,245 +228,530 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { | ... | @@ -425,245 +228,530 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { |
| 425 | return self; | 228 | return self; |
| 426 | } | 229 | } |
| 427 | 230 | ||
| 428 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { | 231 | pub fn deinit(self: *Coff) void { |
| 429 | if (self.llvm_object) |_| return; | 232 | const gpa = self.base.allocator; |
| 233 | |||
| 234 | if (build_options.have_llvm) { | ||
| 235 | if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); | ||
| 236 | } | ||
| 430 | 237 | ||
| 431 | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); | 238 | for (self.objects.items) |*object| { |
| 239 | object.deinit(gpa); | ||
| 240 | } | ||
| 241 | self.objects.deinit(gpa); | ||
| 432 | 242 | ||
| 433 | const decl = self.base.options.module.?.declPtr(decl_index); | 243 | for (self.sections.items(.free_list)) |*free_list| { |
| 434 | if (self.offset_table_free_list.popOrNull()) |i| { | 244 | free_list.deinit(gpa); |
| 435 | decl.link.coff.offset_table_index = i; | 245 | } |
| 436 | } else { | 246 | self.sections.deinit(gpa); |
| 437 | decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len); | 247 | |
| 438 | _ = self.offset_table.addOneAssumeCapacity(); | 248 | for (self.managed_atoms.items) |atom| { |
| 249 | gpa.destroy(atom); | ||
| 250 | } | ||
| 251 | self.managed_atoms.deinit(gpa); | ||
| 439 | 252 | ||
| 440 | const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; | 253 | self.locals.deinit(gpa); |
| 441 | if (self.offset_table.items.len > self.offset_table_size / entry_size) { | 254 | self.globals.deinit(gpa); |
| 442 | self.offset_table_size_dirty = true; | 255 | self.locals_free_list.deinit(gpa); |
| 256 | self.strtab.deinit(gpa); | ||
| 257 | self.got_entries.deinit(gpa); | ||
| 258 | self.got_entries_free_list.deinit(gpa); | ||
| 259 | self.decls.deinit(gpa); | ||
| 260 | self.atom_by_index_table.deinit(gpa); | ||
| 261 | |||
| 262 | { | ||
| 263 | var it = self.unnamed_const_atoms.valueIterator(); | ||
| 264 | while (it.next()) |atoms| { | ||
| 265 | atoms.deinit(gpa); | ||
| 443 | } | 266 | } |
| 267 | self.unnamed_const_atoms.deinit(gpa); | ||
| 444 | } | 268 | } |
| 445 | 269 | ||
| 446 | self.offset_table.items[decl.link.coff.offset_table_index] = 0; | 270 | { |
| 271 | var it = self.relocs.valueIterator(); | ||
| 272 | while (it.next()) |relocs| { | ||
| 273 | relocs.deinit(gpa); | ||
| 274 | } | ||
| 275 | self.relocs.deinit(gpa); | ||
| 276 | } | ||
| 447 | } | 277 | } |
| 448 | 278 | ||
| 449 | fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | 279 | fn populateMissingMetadata(self: *Coff) !void { |
| 450 | const new_block_min_capacity = new_block_size * allocation_padding; | 280 | assert(self.llvm_object == null); |
| 281 | const gpa = self.base.allocator; | ||
| 282 | |||
| 283 | if (self.text_section_index == null) { | ||
| 284 | self.text_section_index = @intCast(u16, self.sections.slice().len); | ||
| 285 | const file_size = @intCast(u32, self.base.options.program_code_size_hint); | ||
| 286 | const off = self.findFreeSpace(file_size, self.page_size); // TODO we are over-aligning in file; we should track both in file and in memory pointers | ||
| 287 | log.debug("found .text free space 0x{x} to 0x{x}", .{ off, off + file_size }); | ||
| 288 | var header = coff.SectionHeader{ | ||
| 289 | .name = undefined, | ||
| 290 | .virtual_size = file_size, | ||
| 291 | .virtual_address = off, | ||
| 292 | .size_of_raw_data = file_size, | ||
| 293 | .pointer_to_raw_data = off, | ||
| 294 | .pointer_to_relocations = 0, | ||
| 295 | .pointer_to_linenumbers = 0, | ||
| 296 | .number_of_relocations = 0, | ||
| 297 | .number_of_linenumbers = 0, | ||
| 298 | .flags = .{ | ||
| 299 | .CNT_CODE = 1, | ||
| 300 | .MEM_EXECUTE = 1, | ||
| 301 | .MEM_READ = 1, | ||
| 302 | }, | ||
| 303 | }; | ||
| 304 | try self.setSectionName(&header, ".text"); | ||
| 305 | try self.sections.append(gpa, .{ .header = header }); | ||
| 306 | } | ||
| 307 | |||
| 308 | if (self.got_section_index == null) { | ||
| 309 | self.got_section_index = @intCast(u16, self.sections.slice().len); | ||
| 310 | const file_size = @intCast(u32, self.base.options.symbol_count_hint); | ||
| 311 | const off = self.findFreeSpace(file_size, self.page_size); | ||
| 312 | log.debug("found .got free space 0x{x} to 0x{x}", .{ off, off + file_size }); | ||
| 313 | var header = coff.SectionHeader{ | ||
| 314 | .name = undefined, | ||
| 315 | .virtual_size = file_size, | ||
| 316 | .virtual_address = off, | ||
| 317 | .size_of_raw_data = file_size, | ||
| 318 | .pointer_to_raw_data = off, | ||
| 319 | .pointer_to_relocations = 0, | ||
| 320 | .pointer_to_linenumbers = 0, | ||
| 321 | .number_of_relocations = 0, | ||
| 322 | .number_of_linenumbers = 0, | ||
| 323 | .flags = .{ | ||
| 324 | .CNT_INITIALIZED_DATA = 1, | ||
| 325 | .MEM_READ = 1, | ||
| 326 | }, | ||
| 327 | }; | ||
| 328 | try self.setSectionName(&header, ".got"); | ||
| 329 | try self.sections.append(gpa, .{ .header = header }); | ||
| 330 | } | ||
| 331 | |||
| 332 | if (self.rdata_section_index == null) { | ||
| 333 | self.rdata_section_index = @intCast(u16, self.sections.slice().len); | ||
| 334 | const file_size: u32 = 1024; | ||
| 335 | const off = self.findFreeSpace(file_size, self.page_size); | ||
| 336 | log.debug("found .rdata free space 0x{x} to 0x{x}", .{ off, off + file_size }); | ||
| 337 | var header = coff.SectionHeader{ | ||
| 338 | .name = undefined, | ||
| 339 | .virtual_size = file_size, | ||
| 340 | .virtual_address = off, | ||
| 341 | .size_of_raw_data = file_size, | ||
| 342 | .pointer_to_raw_data = off, | ||
| 343 | .pointer_to_relocations = 0, | ||
| 344 | .pointer_to_linenumbers = 0, | ||
| 345 | .number_of_relocations = 0, | ||
| 346 | .number_of_linenumbers = 0, | ||
| 347 | .flags = .{ | ||
| 348 | .CNT_INITIALIZED_DATA = 1, | ||
| 349 | .MEM_READ = 1, | ||
| 350 | }, | ||
| 351 | }; | ||
| 352 | try self.setSectionName(&header, ".rdata"); | ||
| 353 | try self.sections.append(gpa, .{ .header = header }); | ||
| 354 | } | ||
| 355 | |||
| 356 | if (self.data_section_index == null) { | ||
| 357 | self.data_section_index = @intCast(u16, self.sections.slice().len); | ||
| 358 | const file_size: u32 = 1024; | ||
| 359 | const off = self.findFreeSpace(file_size, self.page_size); | ||
| 360 | log.debug("found .data free space 0x{x} to 0x{x}", .{ off, off + file_size }); | ||
| 361 | var header = coff.SectionHeader{ | ||
| 362 | .name = undefined, | ||
| 363 | .virtual_size = file_size, | ||
| 364 | .virtual_address = off, | ||
| 365 | .size_of_raw_data = file_size, | ||
| 366 | .pointer_to_raw_data = off, | ||
| 367 | .pointer_to_relocations = 0, | ||
| 368 | .pointer_to_linenumbers = 0, | ||
| 369 | .number_of_relocations = 0, | ||
| 370 | .number_of_linenumbers = 0, | ||
| 371 | .flags = .{ | ||
| 372 | .CNT_INITIALIZED_DATA = 1, | ||
| 373 | .MEM_READ = 1, | ||
| 374 | .MEM_WRITE = 1, | ||
| 375 | }, | ||
| 376 | }; | ||
| 377 | try self.setSectionName(&header, ".data"); | ||
| 378 | try self.sections.append(gpa, .{ .header = header }); | ||
| 379 | } | ||
| 451 | 380 | ||
| 452 | // We use these to indicate our intention to update metadata, placing the new block, | 381 | if (self.strtab_offset == null) { |
| 382 | try self.strtab.buffer.append(gpa, 0); | ||
| 383 | self.strtab_offset = self.findFreeSpace(@intCast(u32, self.strtab.len()), 1); | ||
| 384 | log.debug("found strtab free space 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + self.strtab.len() }); | ||
| 385 | } | ||
| 386 | |||
| 387 | // Index 0 is always a null symbol. | ||
| 388 | try self.locals.append(gpa, .{ | ||
| 389 | .name = [_]u8{0} ** 8, | ||
| 390 | .value = 0, | ||
| 391 | .section_number = @intToEnum(coff.SectionNumber, 0), | ||
| 392 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | ||
| 393 | .storage_class = .NULL, | ||
| 394 | .number_of_aux_symbols = 0, | ||
| 395 | }); | ||
| 396 | |||
| 397 | { | ||
| 398 | // We need to find out what the max file offset is according to section headers. | ||
| 399 | // Otherwise, we may end up with an COFF binary with file size not matching the final section's | ||
| 400 | // offset + it's filesize. | ||
| 401 | // TODO I don't like this here one bit | ||
| 402 | var max_file_offset: u64 = 0; | ||
| 403 | for (self.sections.items(.header)) |header| { | ||
| 404 | if (header.pointer_to_raw_data + header.size_of_raw_data > max_file_offset) { | ||
| 405 | max_file_offset = header.pointer_to_raw_data + header.size_of_raw_data; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { | ||
| 413 | if (self.llvm_object) |_| return; | ||
| 414 | const decl = self.base.options.module.?.declPtr(decl_index); | ||
| 415 | if (decl.link.coff.sym_index != 0) return; | ||
| 416 | decl.link.coff.sym_index = try self.allocateSymbol(); | ||
| 417 | const gpa = self.base.allocator; | ||
| 418 | try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff); | ||
| 419 | try self.decls.putNoClobber(gpa, decl_index, null); | ||
| 420 | } | ||
| 421 | |||
| 422 | fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { | ||
| 423 | const tracy = trace(@src()); | ||
| 424 | defer tracy.end(); | ||
| 425 | |||
| 426 | const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1; | ||
| 427 | const header = &self.sections.items(.header)[sect_id]; | ||
| 428 | const free_list = &self.sections.items(.free_list)[sect_id]; | ||
| 429 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | ||
| 430 | const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size; | ||
| 431 | |||
| 432 | // We use these to indicate our intention to update metadata, placing the new atom, | ||
| 453 | // and possibly removing a free list node. | 433 | // and possibly removing a free list node. |
| 454 | // It would be simpler to do it inside the for loop below, but that would cause a | 434 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 455 | // problem if an error was returned later in the function. So this action | 435 | // problem if an error was returned later in the function. So this action |
| 456 | // is actually carried out at the end of the function, when errors are no longer possible. | 436 | // is actually carried out at the end of the function, when errors are no longer possible. |
| 457 | var block_placement: ?*TextBlock = null; | 437 | var atom_placement: ?*Atom = null; |
| 458 | var free_list_removal: ?usize = null; | 438 | var free_list_removal: ?usize = null; |
| 459 | 439 | ||
| 460 | const vaddr = blk: { | 440 | // First we look for an appropriately sized free list node. |
| 441 | // The list is unordered. We'll just take the first thing that works. | ||
| 442 | var vaddr = blk: { | ||
| 461 | var i: usize = 0; | 443 | var i: usize = 0; |
| 462 | while (i < self.text_block_free_list.items.len) { | 444 | while (i < free_list.items.len) { |
| 463 | const free_block = self.text_block_free_list.items[i]; | 445 | const big_atom = free_list.items[i]; |
| 464 | 446 | // We now have a pointer to a live atom that has too much capacity. | |
| 465 | const next_block_text_offset = free_block.text_offset + free_block.capacity(); | 447 | // Is it enough that we could fit this new atom? |
| 466 | const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address; | 448 | const sym = big_atom.getSymbol(self); |
| 467 | if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) { | 449 | const capacity = big_atom.capacity(self); |
| 468 | block_placement = free_block; | 450 | const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity; |
| 469 | 451 | const ideal_capacity_end_vaddr = math.add(u32, sym.value, ideal_capacity) catch ideal_capacity; | |
| 470 | const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity; | 452 | const capacity_end_vaddr = sym.value + capacity; |
| 471 | if (remaining_capacity < minimum_text_block_size) { | 453 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; |
| 472 | free_list_removal = i; | 454 | const new_start_vaddr = mem.alignBackwardGeneric(u32, new_start_vaddr_unaligned, alignment); |
| 473 | } | 455 | if (new_start_vaddr < ideal_capacity_end_vaddr) { |
| 474 | 456 | // Additional bookkeeping here to notice if this free list node | |
| 475 | break :blk new_block_text_offset + self.text_section_virtual_address; | 457 | // should be deleted because the atom that it points to has grown to take up |
| 476 | } else { | 458 | // more of the extra capacity. |
| 477 | if (!free_block.freeListEligible()) { | 459 | if (!big_atom.freeListEligible(self)) { |
| 478 | _ = self.text_block_free_list.swapRemove(i); | 460 | _ = free_list.swapRemove(i); |
| 479 | } else { | 461 | } else { |
| 480 | i += 1; | 462 | i += 1; |
| 481 | } | 463 | } |
| 482 | continue; | 464 | continue; |
| 483 | } | 465 | } |
| 484 | } else if (self.last_text_block) |last| { | 466 | // At this point we know that we will place the new atom here. But the |
| 485 | const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment); | 467 | // remaining question is whether there is still yet enough capacity left |
| 486 | block_placement = last; | 468 | // over for there to still be a free list node. |
| 487 | break :blk new_block_vaddr; | 469 | const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr; |
| 470 | const keep_free_list_node = remaining_capacity >= min_text_capacity; | ||
| 471 | |||
| 472 | // Set up the metadata to be updated, after errors are no longer possible. | ||
| 473 | atom_placement = big_atom; | ||
| 474 | if (!keep_free_list_node) { | ||
| 475 | free_list_removal = i; | ||
| 476 | } | ||
| 477 | break :blk new_start_vaddr; | ||
| 478 | } else if (maybe_last_atom.*) |last| { | ||
| 479 | const last_symbol = last.getSymbol(self); | ||
| 480 | const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size; | ||
| 481 | const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity; | ||
| 482 | const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment); | ||
| 483 | atom_placement = last; | ||
| 484 | break :blk new_start_vaddr; | ||
| 488 | } else { | 485 | } else { |
| 489 | break :blk self.text_section_virtual_address; | 486 | break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment); |
| 490 | } | 487 | } |
| 491 | }; | 488 | }; |
| 492 | 489 | ||
| 493 | const expand_text_section = block_placement == null or block_placement.?.next == null; | 490 | const expand_section = atom_placement == null or atom_placement.?.next == null; |
| 494 | if (expand_text_section) { | 491 | if (expand_section) { |
| 495 | const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment)); | 492 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 496 | if (needed_size > self.text_section_size) { | 493 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; |
| 497 | const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment); | 494 | if (needed_size > sect_capacity) { |
| 498 | const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment); | 495 | @panic("TODO move section"); |
| 499 | if (current_text_section_virtual_size != new_text_section_virtual_size) { | ||
| 500 | self.size_of_image_dirty = true; | ||
| 501 | // Write new virtual size | ||
| 502 | var buf: [4]u8 = undefined; | ||
| 503 | mem.writeIntLittle(u32, &buf, new_text_section_virtual_size); | ||
| 504 | try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8); | ||
| 505 | } | ||
| 506 | |||
| 507 | self.text_section_size = needed_size; | ||
| 508 | self.text_section_size_dirty = true; | ||
| 509 | } | 496 | } |
| 510 | self.last_text_block = text_block; | 497 | maybe_last_atom.* = atom; |
| 498 | // header.virtual_size = needed_size; | ||
| 499 | // header.size_of_raw_data = mem.alignForwardGeneric(u32, needed_size, default_file_alignment); | ||
| 511 | } | 500 | } |
| 512 | text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address); | ||
| 513 | text_block.size = @intCast(u32, new_block_size); | ||
| 514 | 501 | ||
| 515 | // This function can also reallocate a text block. | 502 | // if (header.getAlignment().? < alignment) { |
| 516 | // In this case we need to "unplug" it from its previous location before | 503 | // header.setAlignment(alignment); |
| 517 | // plugging it in to its new location. | 504 | // } |
| 518 | if (text_block.prev) |prev| { | 505 | atom.size = new_atom_size; |
| 519 | prev.next = text_block.next; | 506 | atom.alignment = alignment; |
| 507 | |||
| 508 | if (atom.prev) |prev| { | ||
| 509 | prev.next = atom.next; | ||
| 520 | } | 510 | } |
| 521 | if (text_block.next) |next| { | 511 | if (atom.next) |next| { |
| 522 | next.prev = text_block.prev; | 512 | next.prev = atom.prev; |
| 523 | } | 513 | } |
| 524 | 514 | ||
| 525 | if (block_placement) |big_block| { | 515 | if (atom_placement) |big_atom| { |
| 526 | text_block.prev = big_block; | 516 | atom.prev = big_atom; |
| 527 | text_block.next = big_block.next; | 517 | atom.next = big_atom.next; |
| 528 | big_block.next = text_block; | 518 | big_atom.next = atom; |
| 529 | } else { | 519 | } else { |
| 530 | text_block.prev = null; | 520 | atom.prev = null; |
| 531 | text_block.next = null; | 521 | atom.next = null; |
| 532 | } | 522 | } |
| 533 | if (free_list_removal) |i| { | 523 | if (free_list_removal) |i| { |
| 534 | _ = self.text_block_free_list.swapRemove(i); | 524 | _ = free_list.swapRemove(i); |
| 535 | } | 525 | } |
| 526 | |||
| 536 | return vaddr; | 527 | return vaddr; |
| 537 | } | 528 | } |
| 538 | 529 | ||
| 539 | fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | 530 | fn allocateSymbol(self: *Coff) !u32 { |
| 540 | const block_vaddr = text_block.getVAddr(self.*); | 531 | const gpa = self.base.allocator; |
| 541 | const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr; | 532 | try self.locals.ensureUnusedCapacity(gpa, 1); |
| 542 | const need_realloc = !align_ok or new_block_size > text_block.capacity(); | 533 | |
| 543 | if (!need_realloc) return @as(u64, block_vaddr); | 534 | const index = blk: { |
| 544 | return self.allocateTextBlock(text_block, new_block_size, alignment); | 535 | if (self.locals_free_list.popOrNull()) |index| { |
| 536 | log.debug(" (reusing symbol index {d})", .{index}); | ||
| 537 | break :blk index; | ||
| 538 | } else { | ||
| 539 | log.debug(" (allocating symbol index {d})", .{self.locals.items.len}); | ||
| 540 | const index = @intCast(u32, self.locals.items.len); | ||
| 541 | _ = self.locals.addOneAssumeCapacity(); | ||
| 542 | break :blk index; | ||
| 543 | } | ||
| 544 | }; | ||
| 545 | |||
| 546 | self.locals.items[index] = .{ | ||
| 547 | .name = [_]u8{0} ** 8, | ||
| 548 | .value = 0, | ||
| 549 | .section_number = @intToEnum(coff.SectionNumber, 0), | ||
| 550 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | ||
| 551 | .storage_class = .NULL, | ||
| 552 | .number_of_aux_symbols = 0, | ||
| 553 | }; | ||
| 554 | |||
| 555 | return index; | ||
| 545 | } | 556 | } |
| 546 | 557 | ||
| 547 | fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void { | 558 | pub fn allocateGotEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 548 | text_block.size = @intCast(u32, new_block_size); | 559 | const gpa = self.base.allocator; |
| 549 | if (text_block.capacity() - text_block.size >= minimum_text_block_size) { | 560 | try self.got_entries.ensureUnusedCapacity(gpa, 1); |
| 550 | self.text_block_free_list.append(self.base.allocator, text_block) catch {}; | 561 | const index: u32 = blk: { |
| 562 | if (self.got_entries_free_list.popOrNull()) |index| { | ||
| 563 | log.debug(" (reusing GOT entry index {d})", .{index}); | ||
| 564 | if (self.got_entries.getIndex(target)) |existing| { | ||
| 565 | assert(existing == index); | ||
| 566 | } | ||
| 567 | break :blk index; | ||
| 568 | } else { | ||
| 569 | log.debug(" (allocating GOT entry at index {d})", .{self.got_entries.keys().len}); | ||
| 570 | const index = @intCast(u32, self.got_entries.keys().len); | ||
| 571 | self.got_entries.putAssumeCapacityNoClobber(target, 0); | ||
| 572 | break :blk index; | ||
| 573 | } | ||
| 574 | }; | ||
| 575 | self.got_entries.keys()[index] = target; | ||
| 576 | return index; | ||
| 577 | } | ||
| 578 | |||
| 579 | fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | ||
| 580 | const gpa = self.base.allocator; | ||
| 581 | const atom = try gpa.create(Atom); | ||
| 582 | errdefer gpa.destroy(atom); | ||
| 583 | atom.* = Atom.empty; | ||
| 584 | atom.sym_index = try self.allocateSymbol(); | ||
| 585 | atom.size = @sizeOf(u64); | ||
| 586 | atom.alignment = @alignOf(u64); | ||
| 587 | |||
| 588 | try self.managed_atoms.append(gpa, atom); | ||
| 589 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | ||
| 590 | self.got_entries.getPtr(target).?.* = atom.sym_index; | ||
| 591 | |||
| 592 | const sym = atom.getSymbolPtr(self); | ||
| 593 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); | ||
| 594 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | ||
| 595 | |||
| 596 | log.debug("allocated GOT atom at 0x{x}", .{sym.value}); | ||
| 597 | |||
| 598 | try atom.addRelocation(self, .{ | ||
| 599 | .@"type" = .direct, | ||
| 600 | .target = target, | ||
| 601 | .offset = 0, | ||
| 602 | .addend = 0, | ||
| 603 | .pcrel = false, | ||
| 604 | .length = 3, | ||
| 605 | .prev_vaddr = sym.value, | ||
| 606 | }); | ||
| 607 | |||
| 608 | return atom; | ||
| 609 | } | ||
| 610 | |||
| 611 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { | ||
| 612 | const sym = atom.getSymbol(self); | ||
| 613 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; | ||
| 614 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); | ||
| 615 | if (!need_realloc) return sym.value; | ||
| 616 | return self.allocateAtom(atom, new_atom_size, alignment); | ||
| 617 | } | ||
| 618 | |||
| 619 | fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void { | ||
| 620 | _ = self; | ||
| 621 | _ = atom; | ||
| 622 | _ = new_block_size; | ||
| 623 | // TODO check the new capacity, and if it crosses the size threshold into a big enough | ||
| 624 | // capacity, insert a free list node for it. | ||
| 625 | } | ||
| 626 | |||
| 627 | fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void { | ||
| 628 | const sym = atom.getSymbol(self); | ||
| 629 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); | ||
| 630 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; | ||
| 631 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); | ||
| 632 | try self.base.file.?.pwriteAll(code, file_offset); | ||
| 633 | try self.resolveRelocs(atom); | ||
| 634 | } | ||
| 635 | |||
| 636 | fn writeGotAtom(self: *Coff, atom: *Atom) !void { | ||
| 637 | switch (self.ptr_width) { | ||
| 638 | .p32 => { | ||
| 639 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); | ||
| 640 | try self.writeAtom(atom, &buffer); | ||
| 641 | }, | ||
| 642 | .p64 => { | ||
| 643 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | ||
| 644 | try self.writeAtom(atom, &buffer); | ||
| 645 | }, | ||
| 551 | } | 646 | } |
| 552 | } | 647 | } |
| 553 | 648 | ||
| 554 | fn freeTextBlock(self: *Coff, text_block: *TextBlock) void { | 649 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 650 | const relocs = self.relocs.get(atom) orelse return; | ||
| 651 | const source_sym = atom.getSymbol(self); | ||
| 652 | const source_section = self.sections.get(@enumToInt(source_sym.section_number) - 1).header; | ||
| 653 | const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address; | ||
| 654 | |||
| 655 | log.debug("relocating '{s}'", .{atom.getName(self)}); | ||
| 656 | |||
| 657 | for (relocs.items) |*reloc| { | ||
| 658 | const target_vaddr = switch (reloc.@"type") { | ||
| 659 | .got => blk: { | ||
| 660 | const got_atom = self.getGotAtomForSymbol(reloc.target) orelse continue; | ||
| 661 | break :blk got_atom.getSymbol(self).value; | ||
| 662 | }, | ||
| 663 | .direct => self.getSymbol(reloc.target).value, | ||
| 664 | }; | ||
| 665 | const target_vaddr_with_addend = target_vaddr + reloc.addend; | ||
| 666 | |||
| 667 | if (target_vaddr_with_addend == reloc.prev_vaddr) continue; | ||
| 668 | |||
| 669 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ | ||
| 670 | reloc.offset, | ||
| 671 | target_vaddr_with_addend, | ||
| 672 | self.getSymbolName(reloc.target), | ||
| 673 | @tagName(reloc.@"type"), | ||
| 674 | }); | ||
| 675 | |||
| 676 | if (reloc.pcrel) { | ||
| 677 | const source_vaddr = source_sym.value + reloc.offset; | ||
| 678 | const disp = target_vaddr_with_addend - source_vaddr - 4; | ||
| 679 | try self.base.file.?.pwriteAll(mem.asBytes(&@intCast(u32, disp)), file_offset + reloc.offset); | ||
| 680 | return; | ||
| 681 | } | ||
| 682 | |||
| 683 | switch (self.ptr_width) { | ||
| 684 | .p32 => try self.base.file.?.pwriteAll( | ||
| 685 | mem.asBytes(&@intCast(u32, target_vaddr_with_addend + default_image_base_exe)), | ||
| 686 | file_offset + reloc.offset, | ||
| 687 | ), | ||
| 688 | .p64 => switch (reloc.length) { | ||
| 689 | 2 => try self.base.file.?.pwriteAll( | ||
| 690 | mem.asBytes(&@truncate(u32, target_vaddr_with_addend + default_image_base_exe)), | ||
| 691 | file_offset + reloc.offset, | ||
| 692 | ), | ||
| 693 | 3 => try self.base.file.?.pwriteAll( | ||
| 694 | mem.asBytes(&(target_vaddr_with_addend + default_image_base_exe)), | ||
| 695 | file_offset + reloc.offset, | ||
| 696 | ), | ||
| 697 | else => unreachable, | ||
| 698 | }, | ||
| 699 | } | ||
| 700 | |||
| 701 | reloc.prev_vaddr = target_vaddr_with_addend; | ||
| 702 | } | ||
| 703 | } | ||
| 704 | |||
| 705 | fn freeAtom(self: *Coff, atom: *Atom) void { | ||
| 706 | log.debug("freeAtom {*}", .{atom}); | ||
| 707 | |||
| 708 | const sym = atom.getSymbol(self); | ||
| 709 | const sect_id = @enumToInt(sym.section_number) - 1; | ||
| 710 | const free_list = &self.sections.items(.free_list)[sect_id]; | ||
| 555 | var already_have_free_list_node = false; | 711 | var already_have_free_list_node = false; |
| 556 | { | 712 | { |
| 557 | var i: usize = 0; | 713 | var i: usize = 0; |
| 558 | // TODO turn text_block_free_list into a hash map | 714 | // TODO turn free_list into a hash map |
| 559 | while (i < self.text_block_free_list.items.len) { | 715 | while (i < free_list.items.len) { |
| 560 | if (self.text_block_free_list.items[i] == text_block) { | 716 | if (free_list.items[i] == atom) { |
| 561 | _ = self.text_block_free_list.swapRemove(i); | 717 | _ = free_list.swapRemove(i); |
| 562 | continue; | 718 | continue; |
| 563 | } | 719 | } |
| 564 | if (self.text_block_free_list.items[i] == text_block.prev) { | 720 | if (free_list.items[i] == atom.prev) { |
| 565 | already_have_free_list_node = true; | 721 | already_have_free_list_node = true; |
| 566 | } | 722 | } |
| 567 | i += 1; | 723 | i += 1; |
| 568 | } | 724 | } |
| 569 | } | 725 | } |
| 570 | if (self.last_text_block == text_block) { | 726 | |
| 571 | self.last_text_block = text_block.prev; | 727 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; |
| 728 | if (maybe_last_atom.*) |last_atom| { | ||
| 729 | if (last_atom == atom) { | ||
| 730 | if (atom.prev) |prev| { | ||
| 731 | // TODO shrink the section size here | ||
| 732 | maybe_last_atom.* = prev; | ||
| 733 | } else { | ||
| 734 | maybe_last_atom.* = null; | ||
| 735 | } | ||
| 736 | } | ||
| 572 | } | 737 | } |
| 573 | if (text_block.prev) |prev| { | ||
| 574 | prev.next = text_block.next; | ||
| 575 | 738 | ||
| 576 | if (!already_have_free_list_node and prev.freeListEligible()) { | 739 | if (atom.prev) |prev| { |
| 740 | prev.next = atom.next; | ||
| 741 | |||
| 742 | if (!already_have_free_list_node and prev.freeListEligible(self)) { | ||
| 577 | // The free list is heuristics, it doesn't have to be perfect, so we can | 743 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 578 | // ignore the OOM here. | 744 | // ignore the OOM here. |
| 579 | self.text_block_free_list.append(self.base.allocator, prev) catch {}; | 745 | free_list.append(self.base.allocator, prev) catch {}; |
| 580 | } | 746 | } |
| 747 | } else { | ||
| 748 | atom.prev = null; | ||
| 581 | } | 749 | } |
| 582 | 750 | ||
| 583 | if (text_block.next) |next| { | 751 | if (atom.next) |next| { |
| 584 | next.prev = text_block.prev; | 752 | next.prev = atom.prev; |
| 585 | } | 753 | } else { |
| 586 | } | 754 | atom.next = null; |
| 587 | |||
| 588 | fn writeOffsetTableEntry(self: *Coff, index: usize) !void { | ||
| 589 | const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; | ||
| 590 | const endian = self.base.options.target.cpu.arch.endian(); | ||
| 591 | |||
| 592 | const offset_table_start = self.section_data_offset; | ||
| 593 | if (self.offset_table_size_dirty) { | ||
| 594 | const current_raw_size = self.offset_table_size; | ||
| 595 | const new_raw_size = self.offset_table_size * 2; | ||
| 596 | log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size }); | ||
| 597 | |||
| 598 | // Move the text section to a new place in the executable | ||
| 599 | const current_text_section_start = self.section_data_offset + current_raw_size; | ||
| 600 | const new_text_section_start = self.section_data_offset + new_raw_size; | ||
| 601 | |||
| 602 | const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size); | ||
| 603 | if (amt != self.text_section_size) return error.InputOutput; | ||
| 604 | |||
| 605 | // Write the new raw size in the .got header | ||
| 606 | var buf: [8]u8 = undefined; | ||
| 607 | mem.writeIntLittle(u32, buf[0..4], new_raw_size); | ||
| 608 | try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16); | ||
| 609 | // Write the new .text section file offset in the .text section header | ||
| 610 | mem.writeIntLittle(u32, buf[0..4], new_text_section_start); | ||
| 611 | try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20); | ||
| 612 | |||
| 613 | const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); | ||
| 614 | const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment); | ||
| 615 | // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section | ||
| 616 | // and the virtual size of the `.got` section | ||
| 617 | |||
| 618 | if (new_virtual_size != current_virtual_size) { | ||
| 619 | log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size }); | ||
| 620 | self.size_of_image_dirty = true; | ||
| 621 | const va_offset = new_virtual_size - current_virtual_size; | ||
| 622 | |||
| 623 | // Write .got virtual size | ||
| 624 | mem.writeIntLittle(u32, buf[0..4], new_virtual_size); | ||
| 625 | try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8); | ||
| 626 | |||
| 627 | // Write .text new virtual address | ||
| 628 | self.text_section_virtual_address = self.text_section_virtual_address + va_offset; | ||
| 629 | mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base); | ||
| 630 | try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); | ||
| 631 | |||
| 632 | // Fix the VAs in the offset table | ||
| 633 | for (self.offset_table.items) |*va, idx| { | ||
| 634 | if (va.* != 0) { | ||
| 635 | va.* += va_offset; | ||
| 636 | |||
| 637 | switch (entry_size) { | ||
| 638 | 4 => { | ||
| 639 | mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian); | ||
| 640 | try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size); | ||
| 641 | }, | ||
| 642 | 8 => { | ||
| 643 | mem.writeInt(u64, &buf, va.*, endian); | ||
| 644 | try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size); | ||
| 645 | }, | ||
| 646 | else => unreachable, | ||
| 647 | } | ||
| 648 | } | ||
| 649 | } | ||
| 650 | } | ||
| 651 | self.offset_table_size = new_raw_size; | ||
| 652 | self.offset_table_size_dirty = false; | ||
| 653 | } | ||
| 654 | // Write the new entry | ||
| 655 | switch (entry_size) { | ||
| 656 | 4 => { | ||
| 657 | var buf: [4]u8 = undefined; | ||
| 658 | mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); | ||
| 659 | try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); | ||
| 660 | }, | ||
| 661 | 8 => { | ||
| 662 | var buf: [8]u8 = undefined; | ||
| 663 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | ||
| 664 | try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); | ||
| 665 | }, | ||
| 666 | else => unreachable, | ||
| 667 | } | 755 | } |
| 668 | } | 756 | } |
| 669 | 757 | ||
| ... | @@ -702,15 +790,18 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live | ... | @@ -702,15 +790,18 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 702 | }, | 790 | }, |
| 703 | }; | 791 | }; |
| 704 | 792 | ||
| 705 | return self.finishUpdateDecl(module, func.owner_decl, code); | 793 | try self.updateDeclCode(decl_index, code, .FUNCTION); |
| 794 | |||
| 795 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 796 | const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; | ||
| 797 | return self.updateDeclExports(module, decl_index, decl_exports); | ||
| 706 | } | 798 | } |
| 707 | 799 | ||
| 708 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { | 800 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| 709 | _ = self; | 801 | _ = self; |
| 710 | _ = tv; | 802 | _ = tv; |
| 711 | _ = decl_index; | 803 | _ = decl_index; |
| 712 | log.debug("TODO lowerUnnamedConst for Coff", .{}); | 804 | @panic("TODO lowerUnnamedConst"); |
| 713 | return error.AnalysisFail; | ||
| 714 | } | 805 | } |
| 715 | 806 | ||
| 716 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { | 807 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | @@ -728,16 +819,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! | ... | @@ -728,16 +819,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 728 | if (decl.val.tag() == .extern_fn) { | 819 | if (decl.val.tag() == .extern_fn) { |
| 729 | return; // TODO Should we do more when front-end analyzed extern decl? | 820 | return; // TODO Should we do more when front-end analyzed extern decl? |
| 730 | } | 821 | } |
| 731 | 822 | if (decl.val.castTag(.variable)) |payload| { | |
| 732 | // TODO COFF/PE debug information | 823 | const variable = payload.data; |
| 733 | // TODO Implement exports | 824 | if (variable.is_extern) { |
| 825 | return; // TODO Should we do more when front-end analyzed extern decl? | ||
| 826 | } | ||
| 827 | } | ||
| 734 | 828 | ||
| 735 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 829 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 736 | defer code_buffer.deinit(); | 830 | defer code_buffer.deinit(); |
| 737 | 831 | ||
| 832 | const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; | ||
| 738 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ | 833 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 739 | .ty = decl.ty, | 834 | .ty = decl.ty, |
| 740 | .val = decl.val, | 835 | .val = decl_val, |
| 741 | }, &code_buffer, .none, .{ | 836 | }, &code_buffer, .none, .{ |
| 742 | .parent_atom_index = 0, | 837 | .parent_atom_index = 0, |
| 743 | }); | 838 | }); |
| ... | @@ -751,47 +846,98 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! | ... | @@ -751,47 +846,98 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 751 | }, | 846 | }, |
| 752 | }; | 847 | }; |
| 753 | 848 | ||
| 754 | return self.finishUpdateDecl(module, decl_index, code); | 849 | try self.updateDeclCode(decl_index, code, .NULL); |
| 850 | |||
| 851 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 852 | const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; | ||
| 853 | return self.updateDeclExports(module, decl_index, decl_exports); | ||
| 755 | } | 854 | } |
| 756 | 855 | ||
| 757 | fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void { | 856 | fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 { |
| 758 | const decl = module.declPtr(decl_index); | 857 | const ty = decl.ty; |
| 759 | const required_alignment = decl.ty.abiAlignment(self.base.options.target); | 858 | const zig_ty = ty.zigTypeTag(); |
| 760 | const curr_size = decl.link.coff.size; | 859 | const val = decl.val; |
| 761 | if (curr_size != 0) { | 860 | const index: u16 = blk: { |
| 762 | const capacity = decl.link.coff.capacity(); | 861 | if (val.isUndefDeep()) { |
| 763 | const need_realloc = code.len > capacity or | 862 | // TODO in release-fast and release-small, we should put undef in .bss |
| 764 | !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); | 863 | break :blk self.data_section_index.?; |
| 864 | } | ||
| 865 | |||
| 866 | switch (zig_ty) { | ||
| 867 | .Fn => break :blk self.text_section_index.?, | ||
| 868 | else => { | ||
| 869 | if (val.castTag(.variable)) |_| { | ||
| 870 | break :blk self.data_section_index.?; | ||
| 871 | } | ||
| 872 | break :blk self.rdata_section_index.?; | ||
| 873 | }, | ||
| 874 | } | ||
| 875 | }; | ||
| 876 | return index; | ||
| 877 | } | ||
| 878 | |||
| 879 | fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, complex_type: coff.ComplexType) !void { | ||
| 880 | const gpa = self.base.allocator; | ||
| 881 | const mod = self.base.options.module.?; | ||
| 882 | const decl = mod.declPtr(decl_index); | ||
| 883 | |||
| 884 | const decl_name = try decl.getFullyQualifiedName(mod); | ||
| 885 | defer gpa.free(decl_name); | ||
| 886 | |||
| 887 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); | ||
| 888 | const required_alignment = decl.getAlignment(self.base.options.target); | ||
| 889 | |||
| 890 | const decl_ptr = self.decls.getPtr(decl_index).?; | ||
| 891 | if (decl_ptr.* == null) { | ||
| 892 | decl_ptr.* = self.getDeclOutputSection(decl); | ||
| 893 | } | ||
| 894 | const sect_index = decl_ptr.*.?; | ||
| 895 | |||
| 896 | const code_len = @intCast(u32, code.len); | ||
| 897 | const atom = &decl.link.coff; | ||
| 898 | assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes() | ||
| 899 | if (atom.size != 0) { | ||
| 900 | const sym = atom.getSymbolPtr(self); | ||
| 901 | try self.setSymbolName(sym, decl_name); | ||
| 902 | sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1); | ||
| 903 | sym.@"type" = .{ .complex_type = complex_type, .base_type = .NULL }; | ||
| 904 | |||
| 905 | const capacity = atom.capacity(self); | ||
| 906 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); | ||
| 765 | if (need_realloc) { | 907 | if (need_realloc) { |
| 766 | const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset; | 908 | const vaddr = try self.growAtom(atom, code_len, required_alignment); |
| 767 | const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); | 909 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); |
| 768 | log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); | 910 | log.debug(" (required alignment 0x{x}", .{required_alignment}); |
| 769 | if (vaddr != curr_vaddr) { | 911 | |
| 770 | log.debug(" (writing new offset table entry)\n", .{}); | 912 | if (vaddr != sym.value) { |
| 771 | self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; | 913 | sym.value = vaddr; |
| 772 | try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); | 914 | log.debug(" (updating GOT entry)", .{}); |
| 915 | const got_atom = self.getGotAtomForSymbol(.{ .sym_index = atom.sym_index, .file = null }).?; | ||
| 916 | try self.writeGotAtom(got_atom); | ||
| 773 | } | 917 | } |
| 774 | } else if (code.len < curr_size) { | 918 | } else if (code_len < atom.size) { |
| 775 | self.shrinkTextBlock(&decl.link.coff, code.len); | 919 | self.shrinkAtom(atom, code_len); |
| 776 | } | 920 | } |
| 921 | atom.size = code_len; | ||
| 777 | } else { | 922 | } else { |
| 778 | const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); | 923 | const sym = atom.getSymbolPtr(self); |
| 779 | log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ | 924 | try self.setSymbolName(sym, decl_name); |
| 780 | mem.sliceTo(decl.name, 0), | 925 | sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1); |
| 781 | vaddr, | 926 | sym.@"type" = .{ .complex_type = complex_type, .base_type = .NULL }; |
| 782 | std.fmt.fmtIntSizeDec(code.len), | 927 | |
| 783 | }); | 928 | const vaddr = try self.allocateAtom(atom, code_len, required_alignment); |
| 784 | errdefer self.freeTextBlock(&decl.link.coff); | 929 | errdefer self.freeAtom(atom); |
| 785 | self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; | 930 | log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr }); |
| 786 | try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); | 931 | atom.size = code_len; |
| 932 | sym.value = vaddr; | ||
| 933 | |||
| 934 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | ||
| 935 | _ = try self.allocateGotEntry(got_target); | ||
| 936 | const got_atom = try self.createGotAtom(got_target); | ||
| 937 | try self.writeGotAtom(got_atom); | ||
| 787 | } | 938 | } |
| 788 | 939 | ||
| 789 | // Write the code into the file | 940 | try self.writeAtom(atom, code); |
| 790 | try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset); | ||
| 791 | |||
| 792 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 793 | const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{}; | ||
| 794 | return self.updateDeclExports(module, decl_index, decl_exports); | ||
| 795 | } | 941 | } |
| 796 | 942 | ||
| 797 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | 943 | pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| ... | @@ -802,9 +948,31 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | ... | @@ -802,9 +948,31 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 802 | const mod = self.base.options.module.?; | 948 | const mod = self.base.options.module.?; |
| 803 | const decl = mod.declPtr(decl_index); | 949 | const decl = mod.declPtr(decl_index); |
| 804 | 950 | ||
| 951 | log.debug("freeDecl {*}", .{decl}); | ||
| 952 | |||
| 953 | const kv = self.decls.fetchRemove(decl_index); | ||
| 954 | if (kv.?.value) |_| { | ||
| 955 | self.freeAtom(&decl.link.coff); | ||
| 956 | } | ||
| 957 | |||
| 805 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | 958 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 806 | self.freeTextBlock(&decl.link.coff); | 959 | const gpa = self.base.allocator; |
| 807 | self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; | 960 | const sym_index = decl.link.coff.sym_index; |
| 961 | if (sym_index != 0) { | ||
| 962 | self.locals_free_list.append(gpa, sym_index) catch {}; | ||
| 963 | |||
| 964 | // Try freeing GOT atom if this decl had one | ||
| 965 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | ||
| 966 | if (self.got_entries.getIndex(got_target)) |got_index| { | ||
| 967 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; | ||
| 968 | self.got_entries.values()[got_index] = 0; | ||
| 969 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); | ||
| 970 | } | ||
| 971 | |||
| 972 | self.locals.items[sym_index].section_number = @intToEnum(coff.SectionNumber, 0); | ||
| 973 | _ = self.atom_by_index_table.remove(sym_index); | ||
| 974 | decl.link.coff.sym_index = 0; | ||
| 975 | } | ||
| 808 | } | 976 | } |
| 809 | 977 | ||
| 810 | pub fn updateDeclExports( | 978 | pub fn updateDeclExports( |
| ... | @@ -817,64 +985,157 @@ pub fn updateDeclExports( | ... | @@ -817,64 +985,157 @@ pub fn updateDeclExports( |
| 817 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 985 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 818 | } | 986 | } |
| 819 | 987 | ||
| 820 | // Even in the case of LLVM, we need to notice certain exported symbols in order to | 988 | if (build_options.have_llvm) { |
| 821 | // detect the default subsystem. | 989 | // Even in the case of LLVM, we need to notice certain exported symbols in order to |
| 822 | for (exports) |exp| { | 990 | // detect the default subsystem. |
| 823 | const exported_decl = module.declPtr(exp.exported_decl); | 991 | for (exports) |exp| { |
| 824 | if (exported_decl.getFunction() == null) continue; | 992 | const exported_decl = module.declPtr(exp.exported_decl); |
| 825 | const winapi_cc = switch (self.base.options.target.cpu.arch) { | 993 | if (exported_decl.getFunction() == null) continue; |
| 826 | .i386 => std.builtin.CallingConvention.Stdcall, | 994 | const winapi_cc = switch (self.base.options.target.cpu.arch) { |
| 827 | else => std.builtin.CallingConvention.C, | 995 | .i386 => std.builtin.CallingConvention.Stdcall, |
| 828 | }; | 996 | else => std.builtin.CallingConvention.C, |
| 829 | const decl_cc = exported_decl.ty.fnCallingConvention(); | 997 | }; |
| 830 | if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and | 998 | const decl_cc = exported_decl.ty.fnCallingConvention(); |
| 831 | self.base.options.link_libc) | 999 | if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and |
| 832 | { | 1000 | self.base.options.link_libc) |
| 833 | module.stage1_flags.have_c_main = true; | 1001 | { |
| 834 | } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) { | 1002 | module.stage1_flags.have_c_main = true; |
| 835 | if (mem.eql(u8, exp.options.name, "WinMain")) { | 1003 | } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) { |
| 836 | module.stage1_flags.have_winmain = true; | 1004 | if (mem.eql(u8, exp.options.name, "WinMain")) { |
| 837 | } else if (mem.eql(u8, exp.options.name, "wWinMain")) { | 1005 | module.stage1_flags.have_winmain = true; |
| 838 | module.stage1_flags.have_wwinmain = true; | 1006 | } else if (mem.eql(u8, exp.options.name, "wWinMain")) { |
| 839 | } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) { | 1007 | module.stage1_flags.have_wwinmain = true; |
| 840 | module.stage1_flags.have_winmain_crt_startup = true; | 1008 | } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) { |
| 841 | } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) { | 1009 | module.stage1_flags.have_winmain_crt_startup = true; |
| 842 | module.stage1_flags.have_wwinmain_crt_startup = true; | 1010 | } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) { |
| 843 | } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) { | 1011 | module.stage1_flags.have_wwinmain_crt_startup = true; |
| 844 | module.stage1_flags.have_dllmain_crt_startup = true; | 1012 | } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) { |
| 1013 | module.stage1_flags.have_dllmain_crt_startup = true; | ||
| 1014 | } | ||
| 845 | } | 1015 | } |
| 846 | } | 1016 | } |
| 847 | } | ||
| 848 | 1017 | ||
| 849 | if (build_options.have_llvm) { | ||
| 850 | if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports); | 1018 | if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports); |
| 851 | } | 1019 | } |
| 852 | 1020 | ||
| 1021 | const tracy = trace(@src()); | ||
| 1022 | defer tracy.end(); | ||
| 1023 | |||
| 1024 | const gpa = self.base.allocator; | ||
| 1025 | |||
| 853 | const decl = module.declPtr(decl_index); | 1026 | const decl = module.declPtr(decl_index); |
| 1027 | const atom = &decl.link.coff; | ||
| 1028 | if (atom.sym_index == 0) return; | ||
| 1029 | const decl_sym = atom.getSymbol(self); | ||
| 1030 | |||
| 854 | for (exports) |exp| { | 1031 | for (exports) |exp| { |
| 1032 | log.debug("adding new export '{s}'", .{exp.options.name}); | ||
| 1033 | |||
| 855 | if (exp.options.section) |section_name| { | 1034 | if (exp.options.section) |section_name| { |
| 856 | if (!mem.eql(u8, section_name, ".text")) { | 1035 | if (!mem.eql(u8, section_name, ".text")) { |
| 857 | try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); | 1036 | try module.failed_exports.putNoClobber( |
| 858 | module.failed_exports.putAssumeCapacityNoClobber( | 1037 | module.gpa, |
| 859 | exp, | 1038 | exp, |
| 860 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}), | 1039 | try Module.ErrorMsg.create( |
| 1040 | gpa, | ||
| 1041 | decl.srcLoc(), | ||
| 1042 | "Unimplemented: ExportOptions.section", | ||
| 1043 | .{}, | ||
| 1044 | ), | ||
| 861 | ); | 1045 | ); |
| 862 | continue; | 1046 | continue; |
| 863 | } | 1047 | } |
| 864 | } | 1048 | } |
| 865 | if (mem.eql(u8, exp.options.name, "_start")) { | 1049 | |
| 866 | self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base; | 1050 | if (exp.options.linkage == .LinkOnce) { |
| 867 | } else { | 1051 | try module.failed_exports.putNoClobber( |
| 868 | try module.failed_exports.ensureUnusedCapacity(module.gpa, 1); | 1052 | module.gpa, |
| 869 | module.failed_exports.putAssumeCapacityNoClobber( | ||
| 870 | exp, | 1053 | exp, |
| 871 | try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}), | 1054 | try Module.ErrorMsg.create( |
| 1055 | gpa, | ||
| 1056 | decl.srcLoc(), | ||
| 1057 | "Unimplemented: GlobalLinkage.LinkOnce", | ||
| 1058 | .{}, | ||
| 1059 | ), | ||
| 872 | ); | 1060 | ); |
| 873 | continue; | 1061 | continue; |
| 874 | } | 1062 | } |
| 1063 | |||
| 1064 | const sym_index = exp.link.coff.sym_index orelse blk: { | ||
| 1065 | const sym_index = try self.allocateSymbol(); | ||
| 1066 | exp.link.coff.sym_index = sym_index; | ||
| 1067 | break :blk sym_index; | ||
| 1068 | }; | ||
| 1069 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | ||
| 1070 | const sym = self.getSymbolPtr(sym_loc); | ||
| 1071 | try self.setSymbolName(sym, exp.options.name); | ||
| 1072 | sym.value = decl_sym.value; | ||
| 1073 | sym.section_number = @intToEnum(coff.SectionNumber, self.text_section_index.? + 1); | ||
| 1074 | sym.@"type" = .{ .complex_type = .FUNCTION, .base_type = .NULL }; | ||
| 1075 | |||
| 1076 | switch (exp.options.linkage) { | ||
| 1077 | .Strong => { | ||
| 1078 | sym.storage_class = .EXTERNAL; | ||
| 1079 | }, | ||
| 1080 | .Internal => @panic("TODO Internal"), | ||
| 1081 | .Weak => @panic("TODO WeakExternal"), | ||
| 1082 | else => unreachable, | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | try self.resolveGlobalSymbol(sym_loc); | ||
| 1086 | } | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | pub fn deleteExport(self: *Coff, exp: Export) void { | ||
| 1090 | if (self.llvm_object) |_| return; | ||
| 1091 | const sym_index = exp.sym_index orelse return; | ||
| 1092 | |||
| 1093 | const gpa = self.base.allocator; | ||
| 1094 | |||
| 1095 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | ||
| 1096 | const sym = self.getSymbolPtr(sym_loc); | ||
| 1097 | const sym_name = self.getSymbolName(sym_loc); | ||
| 1098 | log.debug("deleting export '{s}'", .{sym_name}); | ||
| 1099 | assert(sym.storage_class == .EXTERNAL); | ||
| 1100 | sym.* = .{ | ||
| 1101 | .name = [_]u8{0} ** 8, | ||
| 1102 | .value = 0, | ||
| 1103 | .section_number = @intToEnum(coff.SectionNumber, 0), | ||
| 1104 | .@"type" = .{ .base_type = .NULL, .complex_type = .NULL }, | ||
| 1105 | .storage_class = .NULL, | ||
| 1106 | .number_of_aux_symbols = 0, | ||
| 1107 | }; | ||
| 1108 | self.locals_free_list.append(gpa, sym_index) catch {}; | ||
| 1109 | |||
| 1110 | if (self.globals.get(sym_name)) |global| blk: { | ||
| 1111 | if (global.sym_index != sym_index) break :blk; | ||
| 1112 | if (global.file != null) break :blk; | ||
| 1113 | const kv = self.globals.fetchSwapRemove(sym_name); | ||
| 1114 | gpa.free(kv.?.key); | ||
| 875 | } | 1115 | } |
| 876 | } | 1116 | } |
| 877 | 1117 | ||
| 1118 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { | ||
| 1119 | const gpa = self.base.allocator; | ||
| 1120 | const sym = self.getSymbol(current); | ||
| 1121 | _ = sym; | ||
| 1122 | const sym_name = self.getSymbolName(current); | ||
| 1123 | |||
| 1124 | const name = try gpa.dupe(u8, sym_name); | ||
| 1125 | const global_index = @intCast(u32, self.globals.values().len); | ||
| 1126 | _ = global_index; | ||
| 1127 | const gop = try self.globals.getOrPut(gpa, name); | ||
| 1128 | defer if (gop.found_existing) gpa.free(name); | ||
| 1129 | |||
| 1130 | if (!gop.found_existing) { | ||
| 1131 | gop.value_ptr.* = current; | ||
| 1132 | // TODO undef + tentative | ||
| 1133 | return; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | log.debug("TODO finish resolveGlobalSymbols implementation", .{}); | ||
| 1137 | } | ||
| 1138 | |||
| 878 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 1139 | pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 879 | if (self.base.options.emit == null) { | 1140 | if (self.base.options.emit == null) { |
| 880 | if (build_options.have_llvm) { | 1141 | if (build_options.have_llvm) { |
| ... | @@ -884,14 +1145,13 @@ pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !vo | ... | @@ -884,14 +1145,13 @@ pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !vo |
| 884 | } | 1145 | } |
| 885 | return; | 1146 | return; |
| 886 | } | 1147 | } |
| 887 | if (build_options.have_llvm and self.base.options.use_lld) { | 1148 | const use_lld = build_options.have_llvm and self.base.options.use_lld; |
| 888 | return self.linkWithLLD(comp, prog_node); | 1149 | if (use_lld) { |
| 889 | } else { | 1150 | return lld.linkWithLLD(self, comp, prog_node); |
| 890 | switch (self.base.options.effectiveOutputMode()) { | 1151 | } |
| 891 | .Exe, .Obj => {}, | 1152 | switch (self.base.options.output_mode) { |
| 892 | .Lib => return error.TODOImplementWritingLibFiles, | 1153 | .Exe, .Obj => return self.flushModule(comp, prog_node), |
| 893 | } | 1154 | .Lib => return error.TODOImplementWritingLibFiles, |
| 894 | return self.flushModule(comp, prog_node); | ||
| 895 | } | 1155 | } |
| 896 | } | 1156 | } |
| 897 | 1157 | ||
| ... | @@ -909,648 +1169,449 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -909,648 +1169,449 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 909 | sub_prog_node.activate(); | 1169 | sub_prog_node.activate(); |
| 910 | defer sub_prog_node.end(); | 1170 | defer sub_prog_node.end(); |
| 911 | 1171 | ||
| 912 | if (self.text_section_size_dirty) { | 1172 | if (build_options.enable_logging) { |
| 913 | // Write the new raw size in the .text header | 1173 | self.logSymtab(); |
| 914 | var buf: [4]u8 = undefined; | ||
| 915 | mem.writeIntLittle(u32, &buf, self.text_section_size); | ||
| 916 | try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16); | ||
| 917 | try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size); | ||
| 918 | self.text_section_size_dirty = false; | ||
| 919 | } | 1174 | } |
| 920 | 1175 | ||
| 921 | if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { | 1176 | { |
| 922 | const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment); | 1177 | var it = self.relocs.keyIterator(); |
| 923 | var buf: [4]u8 = undefined; | 1178 | while (it.next()) |atom| { |
| 924 | mem.writeIntLittle(u32, &buf, new_size_of_image); | 1179 | try self.resolveRelocs(atom.*); |
| 925 | try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); | 1180 | } |
| 926 | self.size_of_image_dirty = false; | ||
| 927 | } | 1181 | } |
| 928 | 1182 | ||
| 1183 | if (self.getEntryPoint()) |entry_sym_loc| { | ||
| 1184 | self.entry_addr = self.getSymbol(entry_sym_loc).value; | ||
| 1185 | } | ||
| 1186 | |||
| 1187 | try self.writeStrtab(); | ||
| 1188 | try self.writeDataDirectoriesHeaders(); | ||
| 1189 | try self.writeSectionHeaders(); | ||
| 1190 | |||
| 929 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 1191 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 930 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 1192 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 931 | self.error_flags.no_entry_point_found = true; | 1193 | self.error_flags.no_entry_point_found = true; |
| 932 | } else { | 1194 | } else { |
| 933 | log.debug("flushing. no_entry_point_found = false\n", .{}); | 1195 | log.debug("flushing. no_entry_point_found = false\n", .{}); |
| 934 | self.error_flags.no_entry_point_found = false; | 1196 | self.error_flags.no_entry_point_found = false; |
| 935 | 1197 | try self.writeHeader(); | |
| 936 | if (self.base.options.output_mode == .Exe) { | ||
| 937 | // Write AddressOfEntryPoint | ||
| 938 | var buf: [4]u8 = undefined; | ||
| 939 | mem.writeIntLittle(u32, &buf, self.entry_addr.?); | ||
| 940 | try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16); | ||
| 941 | } | ||
| 942 | } | 1198 | } |
| 943 | } | 1199 | } |
| 944 | 1200 | ||
| 945 | fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 1201 | pub fn getDeclVAddr( |
| 946 | const tracy = trace(@src()); | 1202 | self: *Coff, |
| 947 | defer tracy.end(); | 1203 | decl_index: Module.Decl.Index, |
| 948 | 1204 | reloc_info: link.File.RelocInfo, | |
| 949 | var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); | 1205 | ) !u64 { |
| 950 | defer arena_allocator.deinit(); | 1206 | _ = self; |
| 951 | const arena = arena_allocator.allocator(); | 1207 | _ = decl_index; |
| 952 | 1208 | _ = reloc_info; | |
| 953 | const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. | 1209 | @panic("TODO getDeclVAddr"); |
| 954 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); | 1210 | } |
| 955 | |||
| 956 | // If there is no Zig code to compile, then we should skip flushing the output file because it | ||
| 957 | // will not be part of the linker line anyway. | ||
| 958 | const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { | ||
| 959 | const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1; | ||
| 960 | if (use_stage1) { | ||
| 961 | const obj_basename = try std.zig.binNameAlloc(arena, .{ | ||
| 962 | .root_name = self.base.options.root_name, | ||
| 963 | .target = self.base.options.target, | ||
| 964 | .output_mode = .Obj, | ||
| 965 | }); | ||
| 966 | switch (self.base.options.cache_mode) { | ||
| 967 | .incremental => break :blk try module.zig_cache_artifact_directory.join( | ||
| 968 | arena, | ||
| 969 | &[_][]const u8{obj_basename}, | ||
| 970 | ), | ||
| 971 | .whole => break :blk try fs.path.join(arena, &.{ | ||
| 972 | fs.path.dirname(full_out_path).?, obj_basename, | ||
| 973 | }), | ||
| 974 | } | ||
| 975 | } | ||
| 976 | |||
| 977 | try self.flushModule(comp, prog_node); | ||
| 978 | 1211 | ||
| 979 | if (fs.path.dirname(full_out_path)) |dirname| { | 1212 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| 980 | break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); | 1213 | _ = self; |
| 981 | } else { | 1214 | _ = module; |
| 982 | break :blk self.base.intermediary_basename.?; | 1215 | _ = decl; |
| 983 | } | 1216 | log.debug("TODO implement updateDeclLineNumber", .{}); |
| 984 | } else null; | 1217 | } |
| 985 | 1218 | ||
| 986 | var sub_prog_node = prog_node.start("LLD Link", 0); | 1219 | fn writeStrtab(self: *Coff) !void { |
| 987 | sub_prog_node.activate(); | 1220 | const allocated_size = self.allocatedSize(self.strtab_offset.?); |
| 988 | sub_prog_node.context.refresh(); | 1221 | const needed_size = @intCast(u32, self.strtab.len()); |
| 989 | defer sub_prog_node.end(); | ||
| 990 | 1222 | ||
| 991 | const is_lib = self.base.options.output_mode == .Lib; | 1223 | if (needed_size > allocated_size) { |
| 992 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | 1224 | self.strtab_offset = null; |
| 993 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; | 1225 | self.strtab_offset = @intCast(u32, self.findFreeSpace(needed_size, 1)); |
| 994 | const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib; | 1226 | } |
| 995 | const target = self.base.options.target; | ||
| 996 | 1227 | ||
| 997 | // See link/Elf.zig for comments on how this mechanism works. | 1228 | log.debug("writing strtab from 0x{x} to 0x{x}", .{ self.strtab_offset.?, self.strtab_offset.? + needed_size }); |
| 998 | const id_symlink_basename = "lld.id"; | 1229 | try self.base.file.?.pwriteAll(self.strtab.buffer.items, self.strtab_offset.?); |
| 1230 | } | ||
| 999 | 1231 | ||
| 1000 | var man: Cache.Manifest = undefined; | 1232 | fn writeSectionHeaders(self: *Coff) !void { |
| 1001 | defer if (!self.base.options.disable_lld_caching) man.deinit(); | 1233 | const offset = self.getSectionHeadersOffset(); |
| 1234 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items(.header)), offset); | ||
| 1235 | } | ||
| 1002 | 1236 | ||
| 1003 | var digest: [Cache.hex_digest_len]u8 = undefined; | 1237 | fn writeDataDirectoriesHeaders(self: *Coff) !void { |
| 1238 | const offset = self.getDataDirectoryHeadersOffset(); | ||
| 1239 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(&self.data_directories), offset); | ||
| 1240 | } | ||
| 1004 | 1241 | ||
| 1005 | if (!self.base.options.disable_lld_caching) { | 1242 | fn writeHeader(self: *Coff) !void { |
| 1006 | man = comp.cache_parent.obtain(); | 1243 | const gpa = self.base.allocator; |
| 1007 | self.base.releaseLock(); | 1244 | var buffer = std.ArrayList(u8).init(gpa); |
| 1245 | defer buffer.deinit(); | ||
| 1246 | const writer = buffer.writer(); | ||
| 1008 | 1247 | ||
| 1009 | comptime assert(Compilation.link_hash_implementation_version == 7); | 1248 | try buffer.ensureTotalCapacity(self.getSizeOfHeaders()); |
| 1249 | writer.writeAll(msdos_stub) catch unreachable; | ||
| 1250 | mem.writeIntLittle(u32, buffer.items[0x3c..][0..4], msdos_stub.len); | ||
| 1010 | 1251 | ||
| 1011 | for (self.base.options.objects) |obj| { | 1252 | writer.writeAll("PE\x00\x00") catch unreachable; |
| 1012 | _ = try man.addFile(obj.path, null); | 1253 | var flags = coff.CoffHeaderFlags{ |
| 1013 | man.hash.add(obj.must_link); | 1254 | .EXECUTABLE_IMAGE = 1, |
| 1014 | } | 1255 | .DEBUG_STRIPPED = 1, // TODO |
| 1015 | for (comp.c_object_table.keys()) |key| { | 1256 | }; |
| 1016 | _ = try man.addFile(key.status.success.object_path, null); | 1257 | switch (self.ptr_width) { |
| 1017 | } | 1258 | .p32 => flags.@"32BIT_MACHINE" = 1, |
| 1018 | try man.addOptionalFile(module_obj_path); | 1259 | .p64 => flags.LARGE_ADDRESS_AWARE = 1, |
| 1019 | man.hash.addOptionalBytes(self.base.options.entry); | 1260 | } |
| 1020 | man.hash.addOptional(self.base.options.stack_size_override); | 1261 | if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic) { |
| 1021 | man.hash.addOptional(self.base.options.image_base_override); | 1262 | flags.DLL = 1; |
| 1022 | man.hash.addListOfBytes(self.base.options.lib_dirs); | ||
| 1023 | man.hash.add(self.base.options.skip_linker_dependencies); | ||
| 1024 | if (self.base.options.link_libc) { | ||
| 1025 | man.hash.add(self.base.options.libc_installation != null); | ||
| 1026 | if (self.base.options.libc_installation) |libc_installation| { | ||
| 1027 | man.hash.addBytes(libc_installation.crt_dir.?); | ||
| 1028 | if (target.abi == .msvc) { | ||
| 1029 | man.hash.addBytes(libc_installation.msvc_lib_dir.?); | ||
| 1030 | man.hash.addBytes(libc_installation.kernel32_lib_dir.?); | ||
| 1031 | } | ||
| 1032 | } | ||
| 1033 | } | ||
| 1034 | link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); | ||
| 1035 | man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys()); | ||
| 1036 | man.hash.addOptional(self.base.options.subsystem); | ||
| 1037 | man.hash.add(self.base.options.is_test); | ||
| 1038 | man.hash.add(self.base.options.tsaware); | ||
| 1039 | man.hash.add(self.base.options.nxcompat); | ||
| 1040 | man.hash.add(self.base.options.dynamicbase); | ||
| 1041 | // strip does not need to go into the linker hash because it is part of the hash namespace | ||
| 1042 | man.hash.addOptional(self.base.options.major_subsystem_version); | ||
| 1043 | man.hash.addOptional(self.base.options.minor_subsystem_version); | ||
| 1044 | |||
| 1045 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. | ||
| 1046 | _ = try man.hit(); | ||
| 1047 | digest = man.final(); | ||
| 1048 | var prev_digest_buf: [digest.len]u8 = undefined; | ||
| 1049 | const prev_digest: []u8 = Cache.readSmallFile( | ||
| 1050 | directory.handle, | ||
| 1051 | id_symlink_basename, | ||
| 1052 | &prev_digest_buf, | ||
| 1053 | ) catch |err| blk: { | ||
| 1054 | log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | ||
| 1055 | // Handle this as a cache miss. | ||
| 1056 | break :blk prev_digest_buf[0..0]; | ||
| 1057 | }; | ||
| 1058 | if (mem.eql(u8, prev_digest, &digest)) { | ||
| 1059 | log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | ||
| 1060 | // Hot diggity dog! The output binary is already there. | ||
| 1061 | self.base.lock = man.toOwnedLock(); | ||
| 1062 | return; | ||
| 1063 | } | ||
| 1064 | log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); | ||
| 1065 | |||
| 1066 | // We are about to change the output file to be different, so we invalidate the build hash now. | ||
| 1067 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | ||
| 1068 | error.FileNotFound => {}, | ||
| 1069 | else => |e| return e, | ||
| 1070 | }; | ||
| 1071 | } | 1263 | } |
| 1072 | 1264 | ||
| 1073 | if (self.base.options.output_mode == .Obj) { | 1265 | const timestamp = std.time.timestamp(); |
| 1074 | // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy | 1266 | const size_of_optional_header = @intCast(u16, self.getOptionalHeaderSize() + self.getDataDirectoryHeadersSize()); |
| 1075 | // here. TODO: think carefully about how we can avoid this redundant operation when doing | 1267 | var coff_header = coff.CoffHeader{ |
| 1076 | // build-obj. See also the corresponding TODO in linkAsArchive. | 1268 | .machine = coff.MachineType.fromTargetCpuArch(self.base.options.target.cpu.arch), |
| 1077 | const the_object_path = blk: { | 1269 | .number_of_sections = @intCast(u16, self.sections.slice().len), // TODO what if we prune a section |
| 1078 | if (self.base.options.objects.len != 0) | 1270 | .time_date_stamp = @truncate(u32, @bitCast(u64, timestamp)), |
| 1079 | break :blk self.base.options.objects[0].path; | 1271 | .pointer_to_symbol_table = self.strtab_offset orelse 0, |
| 1272 | .number_of_symbols = 0, | ||
| 1273 | .size_of_optional_header = size_of_optional_header, | ||
| 1274 | .flags = flags, | ||
| 1275 | }; | ||
| 1080 | 1276 | ||
| 1081 | if (comp.c_object_table.count() != 0) | 1277 | writer.writeAll(mem.asBytes(&coff_header)) catch unreachable; |
| 1082 | break :blk comp.c_object_table.keys()[0].status.success.object_path; | ||
| 1083 | 1278 | ||
| 1084 | if (module_obj_path) |p| | 1279 | const dll_flags: coff.DllFlags = .{ |
| 1085 | break :blk p; | 1280 | .HIGH_ENTROPY_VA = 0, //@boolToInt(self.base.options.pie), |
| 1281 | .DYNAMIC_BASE = 0, | ||
| 1282 | .TERMINAL_SERVER_AWARE = 1, // We are not a legacy app | ||
| 1283 | .NX_COMPAT = 1, // We are compatible with Data Execution Prevention | ||
| 1284 | }; | ||
| 1285 | const subsystem: coff.Subsystem = .WINDOWS_CUI; | ||
| 1286 | const size_of_image: u32 = self.getSizeOfImage(); | ||
| 1287 | const size_of_headers: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), default_file_alignment); | ||
| 1288 | const image_base = self.base.options.image_base_override orelse switch (self.base.options.output_mode) { | ||
| 1289 | .Exe => default_image_base_exe, | ||
| 1290 | .Lib => default_image_base_dll, | ||
| 1291 | else => unreachable, | ||
| 1292 | }; | ||
| 1086 | 1293 | ||
| 1087 | // TODO I think this is unreachable. Audit this situation when solving the above TODO | 1294 | const base_of_code = self.sections.get(self.text_section_index.?).header.virtual_address; |
| 1088 | // regarding eliding redundant object -> object transformations. | 1295 | const base_of_data = self.sections.get(self.data_section_index.?).header.virtual_address; |
| 1089 | return error.NoObjectsToLink; | 1296 | |
| 1090 | }; | 1297 | var size_of_code: u32 = 0; |
| 1091 | // This can happen when using --enable-cache and using the stage1 backend. In this case | 1298 | var size_of_initialized_data: u32 = 0; |
| 1092 | // we can skip the file copy. | 1299 | var size_of_uninitialized_data: u32 = 0; |
| 1093 | if (!mem.eql(u8, the_object_path, full_out_path)) { | 1300 | for (self.sections.items(.header)) |header| { |
| 1094 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); | 1301 | if (header.flags.CNT_CODE == 1) { |
| 1095 | } | 1302 | size_of_code += header.size_of_raw_data; |
| 1096 | } else { | ||
| 1097 | // Create an LLD command line and invoke it. | ||
| 1098 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | ||
| 1099 | defer argv.deinit(); | ||
| 1100 | // We will invoke ourselves as a child process to gain access to LLD. | ||
| 1101 | // This is necessary because LLD does not behave properly as a library - | ||
| 1102 | // it calls exit() and does not reset all global data between invocations. | ||
| 1103 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" }); | ||
| 1104 | |||
| 1105 | try argv.append("-ERRORLIMIT:0"); | ||
| 1106 | try argv.append("-NOLOGO"); | ||
| 1107 | if (!self.base.options.strip) { | ||
| 1108 | try argv.append("-DEBUG"); | ||
| 1109 | } | ||
| 1110 | if (self.base.options.lto) { | ||
| 1111 | switch (self.base.options.optimize_mode) { | ||
| 1112 | .Debug => {}, | ||
| 1113 | .ReleaseSmall => try argv.append("-OPT:lldlto=2"), | ||
| 1114 | .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"), | ||
| 1115 | } | ||
| 1116 | } | 1303 | } |
| 1117 | if (self.base.options.output_mode == .Exe) { | 1304 | if (header.flags.CNT_INITIALIZED_DATA == 1) { |
| 1118 | const stack_size = self.base.options.stack_size_override orelse 16777216; | 1305 | size_of_initialized_data += header.size_of_raw_data; |
| 1119 | try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); | ||
| 1120 | } | 1306 | } |
| 1121 | if (self.base.options.image_base_override) |image_base| { | 1307 | if (header.flags.CNT_UNINITIALIZED_DATA == 1) { |
| 1122 | try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base})); | 1308 | size_of_uninitialized_data += header.size_of_raw_data; |
| 1123 | } | 1309 | } |
| 1310 | } | ||
| 1124 | 1311 | ||
| 1125 | if (target.cpu.arch == .i386) { | 1312 | switch (self.ptr_width) { |
| 1126 | try argv.append("-MACHINE:X86"); | 1313 | .p32 => { |
| 1127 | } else if (target.cpu.arch == .x86_64) { | 1314 | var opt_header = coff.OptionalHeaderPE32{ |
| 1128 | try argv.append("-MACHINE:X64"); | 1315 | .magic = coff.IMAGE_NT_OPTIONAL_HDR32_MAGIC, |
| 1129 | } else if (target.cpu.arch.isARM()) { | 1316 | .major_linker_version = 0, |
| 1130 | if (target.cpu.arch.ptrBitWidth() == 32) { | 1317 | .minor_linker_version = 0, |
| 1131 | try argv.append("-MACHINE:ARM"); | 1318 | .size_of_code = size_of_code, |
| 1132 | } else { | 1319 | .size_of_initialized_data = size_of_initialized_data, |
| 1133 | try argv.append("-MACHINE:ARM64"); | 1320 | .size_of_uninitialized_data = size_of_uninitialized_data, |
| 1134 | } | 1321 | .address_of_entry_point = self.entry_addr orelse 0, |
| 1135 | } | 1322 | .base_of_code = base_of_code, |
| 1323 | .base_of_data = base_of_data, | ||
| 1324 | .image_base = @intCast(u32, image_base), | ||
| 1325 | .section_alignment = self.page_size, | ||
| 1326 | .file_alignment = default_file_alignment, | ||
| 1327 | .major_operating_system_version = 6, | ||
| 1328 | .minor_operating_system_version = 0, | ||
| 1329 | .major_image_version = 0, | ||
| 1330 | .minor_image_version = 0, | ||
| 1331 | .major_subsystem_version = 6, | ||
| 1332 | .minor_subsystem_version = 0, | ||
| 1333 | .win32_version_value = 0, | ||
| 1334 | .size_of_image = size_of_image, | ||
| 1335 | .size_of_headers = size_of_headers, | ||
| 1336 | .checksum = 0, | ||
| 1337 | .subsystem = subsystem, | ||
| 1338 | .dll_flags = dll_flags, | ||
| 1339 | .size_of_stack_reserve = default_size_of_stack_reserve, | ||
| 1340 | .size_of_stack_commit = default_size_of_stack_commit, | ||
| 1341 | .size_of_heap_reserve = default_size_of_heap_reserve, | ||
| 1342 | .size_of_heap_commit = default_size_of_heap_commit, | ||
| 1343 | .loader_flags = 0, | ||
| 1344 | .number_of_rva_and_sizes = @intCast(u32, self.data_directories.len), | ||
| 1345 | }; | ||
| 1346 | writer.writeAll(mem.asBytes(&opt_header)) catch unreachable; | ||
| 1347 | }, | ||
| 1348 | .p64 => { | ||
| 1349 | var opt_header = coff.OptionalHeaderPE64{ | ||
| 1350 | .magic = coff.IMAGE_NT_OPTIONAL_HDR64_MAGIC, | ||
| 1351 | .major_linker_version = 0, | ||
| 1352 | .minor_linker_version = 0, | ||
| 1353 | .size_of_code = size_of_code, | ||
| 1354 | .size_of_initialized_data = size_of_initialized_data, | ||
| 1355 | .size_of_uninitialized_data = size_of_uninitialized_data, | ||
| 1356 | .address_of_entry_point = self.entry_addr orelse 0, | ||
| 1357 | .base_of_code = base_of_code, | ||
| 1358 | .image_base = image_base, | ||
| 1359 | .section_alignment = self.page_size, | ||
| 1360 | .file_alignment = default_file_alignment, | ||
| 1361 | .major_operating_system_version = 6, | ||
| 1362 | .minor_operating_system_version = 0, | ||
| 1363 | .major_image_version = 0, | ||
| 1364 | .minor_image_version = 0, | ||
| 1365 | .major_subsystem_version = 6, | ||
| 1366 | .minor_subsystem_version = 0, | ||
| 1367 | .win32_version_value = 0, | ||
| 1368 | .size_of_image = size_of_image, | ||
| 1369 | .size_of_headers = size_of_headers, | ||
| 1370 | .checksum = 0, | ||
| 1371 | .subsystem = subsystem, | ||
| 1372 | .dll_flags = dll_flags, | ||
| 1373 | .size_of_stack_reserve = default_size_of_stack_reserve, | ||
| 1374 | .size_of_stack_commit = default_size_of_stack_commit, | ||
| 1375 | .size_of_heap_reserve = default_size_of_heap_reserve, | ||
| 1376 | .size_of_heap_commit = default_size_of_heap_commit, | ||
| 1377 | .loader_flags = 0, | ||
| 1378 | .number_of_rva_and_sizes = @intCast(u32, self.data_directories.len), | ||
| 1379 | }; | ||
| 1380 | writer.writeAll(mem.asBytes(&opt_header)) catch unreachable; | ||
| 1381 | }, | ||
| 1382 | } | ||
| 1136 | 1383 | ||
| 1137 | for (self.base.options.force_undefined_symbols.keys()) |symbol| { | 1384 | try self.base.file.?.pwriteAll(buffer.items, 0); |
| 1138 | try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); | 1385 | } |
| 1139 | } | ||
| 1140 | 1386 | ||
| 1141 | if (is_dyn_lib) { | 1387 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 1142 | try argv.append("-DLL"); | 1388 | // TODO https://github.com/ziglang/zig/issues/1284 |
| 1143 | } | 1389 | return math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch |
| 1390 | math.maxInt(@TypeOf(actual_size)); | ||
| 1391 | } | ||
| 1144 | 1392 | ||
| 1145 | if (self.base.options.entry) |entry| { | 1393 | fn detectAllocCollision(self: *Coff, start: u32, size: u32) ?u32 { |
| 1146 | try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry})); | 1394 | const headers_size = self.getSizeOfHeaders(); |
| 1147 | } | 1395 | if (start < headers_size) |
| 1396 | return headers_size; | ||
| 1148 | 1397 | ||
| 1149 | if (self.base.options.tsaware) { | 1398 | const end = start + size; |
| 1150 | try argv.append("-tsaware"); | ||
| 1151 | } | ||
| 1152 | if (self.base.options.nxcompat) { | ||
| 1153 | try argv.append("-nxcompat"); | ||
| 1154 | } | ||
| 1155 | if (self.base.options.dynamicbase) { | ||
| 1156 | try argv.append("-dynamicbase"); | ||
| 1157 | } | ||
| 1158 | 1399 | ||
| 1159 | try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); | 1400 | if (self.strtab_offset) |off| { |
| 1401 | const increased_size = @intCast(u32, self.strtab.len()); | ||
| 1402 | const test_end = off + increased_size; | ||
| 1403 | if (end > off and start < test_end) { | ||
| 1404 | return test_end; | ||
| 1405 | } | ||
| 1406 | } | ||
| 1160 | 1407 | ||
| 1161 | if (self.base.options.implib_emit) |emit| { | 1408 | for (self.sections.items(.header)) |header| { |
| 1162 | const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); | 1409 | const increased_size = header.size_of_raw_data; |
| 1163 | try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); | 1410 | const test_end = header.pointer_to_raw_data + increased_size; |
| 1411 | if (end > header.pointer_to_raw_data and start < test_end) { | ||
| 1412 | return test_end; | ||
| 1164 | } | 1413 | } |
| 1414 | } | ||
| 1165 | 1415 | ||
| 1166 | if (self.base.options.link_libc) { | 1416 | return null; |
| 1167 | if (self.base.options.libc_installation) |libc_installation| { | 1417 | } |
| 1168 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); | ||
| 1169 | 1418 | ||
| 1170 | if (target.abi == .msvc) { | 1419 | pub fn allocatedSize(self: *Coff, start: u32) u32 { |
| 1171 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); | 1420 | if (start == 0) |
| 1172 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); | 1421 | return 0; |
| 1173 | } | 1422 | var min_pos: u32 = std.math.maxInt(u32); |
| 1174 | } | 1423 | if (self.strtab_offset) |off| { |
| 1175 | } | 1424 | if (off > start and off < min_pos) min_pos = off; |
| 1425 | } | ||
| 1426 | for (self.sections.items(.header)) |header| { | ||
| 1427 | if (header.pointer_to_raw_data <= start) continue; | ||
| 1428 | if (header.pointer_to_raw_data < min_pos) min_pos = header.pointer_to_raw_data; | ||
| 1429 | } | ||
| 1430 | return min_pos - start; | ||
| 1431 | } | ||
| 1176 | 1432 | ||
| 1177 | for (self.base.options.lib_dirs) |lib_dir| { | 1433 | pub fn findFreeSpace(self: *Coff, object_size: u32, min_alignment: u32) u32 { |
| 1178 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); | 1434 | var start: u32 = 0; |
| 1179 | } | 1435 | while (self.detectAllocCollision(start, object_size)) |item_end| { |
| 1436 | start = mem.alignForwardGeneric(u32, item_end, min_alignment); | ||
| 1437 | } | ||
| 1438 | return start; | ||
| 1439 | } | ||
| 1180 | 1440 | ||
| 1181 | try argv.ensureUnusedCapacity(self.base.options.objects.len); | 1441 | inline fn getSizeOfHeaders(self: Coff) u32 { |
| 1182 | for (self.base.options.objects) |obj| { | 1442 | const msdos_hdr_size = msdos_stub.len + 4; |
| 1183 | if (obj.must_link) { | 1443 | return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize() + |
| 1184 | argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); | 1444 | self.getDataDirectoryHeadersSize() + self.getSectionHeadersSize()); |
| 1185 | } else { | 1445 | } |
| 1186 | argv.appendAssumeCapacity(obj.path); | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | 1446 | ||
| 1190 | for (comp.c_object_table.keys()) |key| { | 1447 | inline fn getOptionalHeaderSize(self: Coff) u32 { |
| 1191 | try argv.append(key.status.success.object_path); | 1448 | return switch (self.ptr_width) { |
| 1192 | } | 1449 | .p32 => @intCast(u32, @sizeOf(coff.OptionalHeaderPE32)), |
| 1450 | .p64 => @intCast(u32, @sizeOf(coff.OptionalHeaderPE64)), | ||
| 1451 | }; | ||
| 1452 | } | ||
| 1193 | 1453 | ||
| 1194 | if (module_obj_path) |p| { | 1454 | inline fn getDataDirectoryHeadersSize(self: Coff) u32 { |
| 1195 | try argv.append(p); | 1455 | return @intCast(u32, self.data_directories.len * @sizeOf(coff.ImageDataDirectory)); |
| 1196 | } | 1456 | } |
| 1197 | 1457 | ||
| 1198 | const resolved_subsystem: ?std.Target.SubSystem = blk: { | 1458 | inline fn getSectionHeadersSize(self: Coff) u32 { |
| 1199 | if (self.base.options.subsystem) |explicit| break :blk explicit; | 1459 | return @intCast(u32, self.sections.slice().len * @sizeOf(coff.SectionHeader)); |
| 1200 | switch (target.os.tag) { | 1460 | } |
| 1201 | .windows => { | ||
| 1202 | if (self.base.options.module) |module| { | ||
| 1203 | if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) | ||
| 1204 | break :blk null; | ||
| 1205 | if (module.stage1_flags.have_c_main or self.base.options.is_test or | ||
| 1206 | module.stage1_flags.have_winmain_crt_startup or | ||
| 1207 | module.stage1_flags.have_wwinmain_crt_startup) | ||
| 1208 | { | ||
| 1209 | break :blk .Console; | ||
| 1210 | } | ||
| 1211 | if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain) | ||
| 1212 | break :blk .Windows; | ||
| 1213 | } | ||
| 1214 | }, | ||
| 1215 | .uefi => break :blk .EfiApplication, | ||
| 1216 | else => {}, | ||
| 1217 | } | ||
| 1218 | break :blk null; | ||
| 1219 | }; | ||
| 1220 | 1461 | ||
| 1221 | const Mode = enum { uefi, win32 }; | 1462 | inline fn getDataDirectoryHeadersOffset(self: Coff) u32 { |
| 1222 | const mode: Mode = mode: { | 1463 | const msdos_hdr_size = msdos_stub.len + 4; |
| 1223 | if (resolved_subsystem) |subsystem| { | 1464 | return @intCast(u32, msdos_hdr_size + @sizeOf(coff.CoffHeader) + self.getOptionalHeaderSize()); |
| 1224 | const subsystem_suffix = ss: { | 1465 | } |
| 1225 | if (self.base.options.major_subsystem_version) |major| { | ||
| 1226 | if (self.base.options.minor_subsystem_version) |minor| { | ||
| 1227 | break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor }); | ||
| 1228 | } else { | ||
| 1229 | break :ss try allocPrint(arena, ",{d}", .{major}); | ||
| 1230 | } | ||
| 1231 | } | ||
| 1232 | break :ss ""; | ||
| 1233 | }; | ||
| 1234 | |||
| 1235 | switch (subsystem) { | ||
| 1236 | .Console => { | ||
| 1237 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{ | ||
| 1238 | subsystem_suffix, | ||
| 1239 | })); | ||
| 1240 | break :mode .win32; | ||
| 1241 | }, | ||
| 1242 | .EfiApplication => { | ||
| 1243 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{ | ||
| 1244 | subsystem_suffix, | ||
| 1245 | })); | ||
| 1246 | break :mode .uefi; | ||
| 1247 | }, | ||
| 1248 | .EfiBootServiceDriver => { | ||
| 1249 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{ | ||
| 1250 | subsystem_suffix, | ||
| 1251 | })); | ||
| 1252 | break :mode .uefi; | ||
| 1253 | }, | ||
| 1254 | .EfiRom => { | ||
| 1255 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{ | ||
| 1256 | subsystem_suffix, | ||
| 1257 | })); | ||
| 1258 | break :mode .uefi; | ||
| 1259 | }, | ||
| 1260 | .EfiRuntimeDriver => { | ||
| 1261 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{ | ||
| 1262 | subsystem_suffix, | ||
| 1263 | })); | ||
| 1264 | break :mode .uefi; | ||
| 1265 | }, | ||
| 1266 | .Native => { | ||
| 1267 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{ | ||
| 1268 | subsystem_suffix, | ||
| 1269 | })); | ||
| 1270 | break :mode .win32; | ||
| 1271 | }, | ||
| 1272 | .Posix => { | ||
| 1273 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{ | ||
| 1274 | subsystem_suffix, | ||
| 1275 | })); | ||
| 1276 | break :mode .win32; | ||
| 1277 | }, | ||
| 1278 | .Windows => { | ||
| 1279 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{ | ||
| 1280 | subsystem_suffix, | ||
| 1281 | })); | ||
| 1282 | break :mode .win32; | ||
| 1283 | }, | ||
| 1284 | } | ||
| 1285 | } else if (target.os.tag == .uefi) { | ||
| 1286 | break :mode .uefi; | ||
| 1287 | } else { | ||
| 1288 | break :mode .win32; | ||
| 1289 | } | ||
| 1290 | }; | ||
| 1291 | 1466 | ||
| 1292 | switch (mode) { | 1467 | inline fn getSectionHeadersOffset(self: Coff) u32 { |
| 1293 | .uefi => try argv.appendSlice(&[_][]const u8{ | 1468 | return self.getDataDirectoryHeadersOffset() + self.getDataDirectoryHeadersSize(); |
| 1294 | "-BASE:0", | 1469 | } |
| 1295 | "-ENTRY:EfiMain", | ||
| 1296 | "-OPT:REF", | ||
| 1297 | "-SAFESEH:NO", | ||
| 1298 | "-MERGE:.rdata=.data", | ||
| 1299 | "-ALIGN:32", | ||
| 1300 | "-NODEFAULTLIB", | ||
| 1301 | "-SECTION:.xdata,D", | ||
| 1302 | }), | ||
| 1303 | .win32 => { | ||
| 1304 | if (link_in_crt) { | ||
| 1305 | if (target.abi.isGnu()) { | ||
| 1306 | try argv.append("-lldmingw"); | ||
| 1307 | |||
| 1308 | if (target.cpu.arch == .i386) { | ||
| 1309 | try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); | ||
| 1310 | } else { | ||
| 1311 | try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | if (is_dyn_lib) { | ||
| 1315 | try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj")); | ||
| 1316 | if (target.cpu.arch == .i386) { | ||
| 1317 | try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); | ||
| 1318 | } else { | ||
| 1319 | try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); | ||
| 1320 | } | ||
| 1321 | } else { | ||
| 1322 | try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj")); | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); | ||
| 1326 | try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib")); | ||
| 1327 | try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib")); | ||
| 1328 | |||
| 1329 | for (mingw.always_link_libs) |name| { | ||
| 1330 | if (!self.base.options.system_libs.contains(name)) { | ||
| 1331 | const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); | ||
| 1332 | try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); | ||
| 1333 | } | ||
| 1334 | } | ||
| 1335 | } else { | ||
| 1336 | const lib_str = switch (self.base.options.link_mode) { | ||
| 1337 | .Dynamic => "", | ||
| 1338 | .Static => "lib", | ||
| 1339 | }; | ||
| 1340 | const d_str = switch (self.base.options.optimize_mode) { | ||
| 1341 | .Debug => "d", | ||
| 1342 | else => "", | ||
| 1343 | }; | ||
| 1344 | switch (self.base.options.link_mode) { | ||
| 1345 | .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), | ||
| 1346 | .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str })); | ||
| 1350 | try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str })); | ||
| 1351 | |||
| 1352 | //Visual C++ 2015 Conformance Changes | ||
| 1353 | //https://msdn.microsoft.com/en-us/library/bb531344.aspx | ||
| 1354 | try argv.append("legacy_stdio_definitions.lib"); | ||
| 1355 | |||
| 1356 | // msvcrt depends on kernel32 and ntdll | ||
| 1357 | try argv.append("kernel32.lib"); | ||
| 1358 | try argv.append("ntdll.lib"); | ||
| 1359 | } | ||
| 1360 | } else { | ||
| 1361 | try argv.append("-NODEFAULTLIB"); | ||
| 1362 | if (!is_lib) { | ||
| 1363 | if (self.base.options.module) |module| { | ||
| 1364 | if (module.stage1_flags.have_winmain_crt_startup) { | ||
| 1365 | try argv.append("-ENTRY:WinMainCRTStartup"); | ||
| 1366 | } else { | ||
| 1367 | try argv.append("-ENTRY:wWinMainCRTStartup"); | ||
| 1368 | } | ||
| 1369 | } else { | ||
| 1370 | try argv.append("-ENTRY:wWinMainCRTStartup"); | ||
| 1371 | } | ||
| 1372 | } | ||
| 1373 | } | ||
| 1374 | }, | ||
| 1375 | } | ||
| 1376 | 1470 | ||
| 1377 | // libc++ dep | 1471 | inline fn getSizeOfImage(self: Coff) u32 { |
| 1378 | if (self.base.options.link_libcpp) { | 1472 | var image_size: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), self.page_size); |
| 1379 | try argv.append(comp.libcxxabi_static_lib.?.full_object_path); | 1473 | for (self.sections.items(.header)) |header| { |
| 1380 | try argv.append(comp.libcxx_static_lib.?.full_object_path); | 1474 | image_size += mem.alignForwardGeneric(u32, header.virtual_size, self.page_size); |
| 1381 | } | 1475 | } |
| 1476 | return image_size; | ||
| 1477 | } | ||
| 1382 | 1478 | ||
| 1383 | // libunwind dep | 1479 | /// Returns symbol location corresponding to the set entrypoint (if any). |
| 1384 | if (self.base.options.link_libunwind) { | 1480 | pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { |
| 1385 | try argv.append(comp.libunwind_static_lib.?.full_object_path); | 1481 | const entry_name = self.base.options.entry orelse "_start"; // TODO this is incomplete |
| 1386 | } | 1482 | return self.globals.get(entry_name); |
| 1483 | } | ||
| 1387 | 1484 | ||
| 1388 | if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { | 1485 | /// Returns pointer-to-symbol described by `sym_with_loc` descriptor. |
| 1389 | if (!self.base.options.link_libc) { | 1486 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { |
| 1390 | if (comp.libc_static_lib) |lib| { | 1487 | assert(sym_loc.file == null); // TODO linking object files |
| 1391 | try argv.append(lib.full_object_path); | 1488 | return &self.locals.items[sym_loc.sym_index]; |
| 1392 | } | 1489 | } |
| 1393 | } | ||
| 1394 | // MinGW doesn't provide libssp symbols | ||
| 1395 | if (target.abi.isGnu()) { | ||
| 1396 | if (comp.libssp_static_lib) |lib| { | ||
| 1397 | try argv.append(lib.full_object_path); | ||
| 1398 | } | ||
| 1399 | } | ||
| 1400 | // MSVC compiler_rt is missing some stuff, so we build it unconditionally but | ||
| 1401 | // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. | ||
| 1402 | if (comp.compiler_rt_lib) |lib| { | ||
| 1403 | try argv.append(lib.full_object_path); | ||
| 1404 | } | ||
| 1405 | } | ||
| 1406 | 1490 | ||
| 1407 | try argv.ensureUnusedCapacity(self.base.options.system_libs.count()); | 1491 | /// Returns symbol described by `sym_with_loc` descriptor. |
| 1408 | for (self.base.options.system_libs.keys()) |key| { | 1492 | pub fn getSymbol(self: *const Coff, sym_loc: SymbolWithLoc) *const coff.Symbol { |
| 1409 | const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); | 1493 | assert(sym_loc.file == null); // TODO linking object files |
| 1410 | if (comp.crt_files.get(lib_basename)) |crt_file| { | 1494 | return &self.locals.items[sym_loc.sym_index]; |
| 1411 | argv.appendAssumeCapacity(crt_file.full_object_path); | 1495 | } |
| 1412 | continue; | ||
| 1413 | } | ||
| 1414 | if (try self.findLib(arena, lib_basename)) |full_path| { | ||
| 1415 | argv.appendAssumeCapacity(full_path); | ||
| 1416 | continue; | ||
| 1417 | } | ||
| 1418 | if (target.abi.isGnu()) { | ||
| 1419 | const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); | ||
| 1420 | if (try self.findLib(arena, fallback_name)) |full_path| { | ||
| 1421 | argv.appendAssumeCapacity(full_path); | ||
| 1422 | continue; | ||
| 1423 | } | ||
| 1424 | } | ||
| 1425 | log.err("DLL import library for -l{s} not found", .{key}); | ||
| 1426 | return error.DllImportLibraryNotFound; | ||
| 1427 | } | ||
| 1428 | 1496 | ||
| 1429 | if (self.base.options.verbose_link) { | 1497 | /// Returns name of the symbol described by `sym_with_loc` descriptor. |
| 1430 | // Skip over our own name so that the LLD linker name is the first argv item. | 1498 | pub fn getSymbolName(self: *const Coff, sym_loc: SymbolWithLoc) []const u8 { |
| 1431 | Compilation.dump_argv(argv.items[1..]); | 1499 | assert(sym_loc.file == null); // TODO linking object files |
| 1432 | } | 1500 | const sym = self.getSymbol(sym_loc); |
| 1501 | const offset = sym.getNameOffset() orelse return sym.getName().?; | ||
| 1502 | return self.strtab.get(offset).?; | ||
| 1503 | } | ||
| 1433 | 1504 | ||
| 1434 | if (std.process.can_spawn) { | 1505 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. |
| 1435 | // If possible, we run LLD as a child process because it does not always | 1506 | /// Returns null on failure. |
| 1436 | // behave properly as a library, unfortunately. | 1507 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1437 | // https://github.com/ziglang/zig/issues/3825 | 1508 | assert(sym_loc.file == null); // TODO linking with object files |
| 1438 | var child = std.ChildProcess.init(argv.items, arena); | 1509 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 1439 | if (comp.clang_passthrough_mode) { | 1510 | } |
| 1440 | child.stdin_behavior = .Inherit; | ||
| 1441 | child.stdout_behavior = .Inherit; | ||
| 1442 | child.stderr_behavior = .Inherit; | ||
| 1443 | |||
| 1444 | const term = child.spawnAndWait() catch |err| { | ||
| 1445 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 1446 | return error.UnableToSpawnSelf; | ||
| 1447 | }; | ||
| 1448 | switch (term) { | ||
| 1449 | .Exited => |code| { | ||
| 1450 | if (code != 0) { | ||
| 1451 | std.process.exit(code); | ||
| 1452 | } | ||
| 1453 | }, | ||
| 1454 | else => std.process.abort(), | ||
| 1455 | } | ||
| 1456 | } else { | ||
| 1457 | child.stdin_behavior = .Ignore; | ||
| 1458 | child.stdout_behavior = .Ignore; | ||
| 1459 | child.stderr_behavior = .Pipe; | ||
| 1460 | |||
| 1461 | try child.spawn(); | ||
| 1462 | |||
| 1463 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 1464 | |||
| 1465 | const term = child.wait() catch |err| { | ||
| 1466 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 1467 | return error.UnableToSpawnSelf; | ||
| 1468 | }; | ||
| 1469 | |||
| 1470 | switch (term) { | ||
| 1471 | .Exited => |code| { | ||
| 1472 | if (code != 0) { | ||
| 1473 | // TODO parse this output and surface with the Compilation API rather than | ||
| 1474 | // directly outputting to stderr here. | ||
| 1475 | std.debug.print("{s}", .{stderr}); | ||
| 1476 | return error.LLDReportedFailure; | ||
| 1477 | } | ||
| 1478 | }, | ||
| 1479 | else => { | ||
| 1480 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 1481 | return error.LLDCrashed; | ||
| 1482 | }, | ||
| 1483 | } | ||
| 1484 | 1511 | ||
| 1485 | if (stderr.len != 0) { | 1512 | /// Returns GOT atom that references `sym_with_loc` if one exists. |
| 1486 | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | 1513 | /// Returns null otherwise. |
| 1487 | } | 1514 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { |
| 1488 | } | 1515 | const got_index = self.got_entries.get(sym_loc) orelse return null; |
| 1489 | } else { | 1516 | return self.atom_by_index_table.get(got_index); |
| 1490 | const exit_code = try lldMain(arena, argv.items, false); | 1517 | } |
| 1491 | if (exit_code != 0) { | ||
| 1492 | if (comp.clang_passthrough_mode) { | ||
| 1493 | std.process.exit(exit_code); | ||
| 1494 | } else { | ||
| 1495 | return error.LLDReportedFailure; | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | } | ||
| 1499 | } | ||
| 1500 | 1518 | ||
| 1501 | if (!self.base.options.disable_lld_caching) { | 1519 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
| 1502 | // Update the file with the digest. If it fails we can continue; it only | 1520 | if (name.len <= 8) { |
| 1503 | // means that the next invocation will have an unnecessary cache miss. | 1521 | mem.copy(u8, &header.name, name); |
| 1504 | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { | 1522 | mem.set(u8, header.name[name.len..], 0); |
| 1505 | log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); | 1523 | return; |
| 1506 | }; | ||
| 1507 | // Again failure here only means an unnecessary cache miss. | ||
| 1508 | man.writeManifest() catch |err| { | ||
| 1509 | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | ||
| 1510 | }; | ||
| 1511 | // We hang on to this lock so that the output file path can be used without | ||
| 1512 | // other processes clobbering it. | ||
| 1513 | self.base.lock = man.toOwnedLock(); | ||
| 1514 | } | 1524 | } |
| 1525 | const offset = try self.strtab.insert(self.base.allocator, name); | ||
| 1526 | const name_offset = fmt.bufPrint(&header.name, "/{d}", .{offset}) catch unreachable; | ||
| 1527 | mem.set(u8, header.name[name_offset.len..], 0); | ||
| 1515 | } | 1528 | } |
| 1516 | 1529 | ||
| 1517 | fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 { | 1530 | fn setSymbolName(self: *Coff, symbol: *coff.Symbol, name: []const u8) !void { |
| 1518 | for (self.base.options.lib_dirs) |lib_dir| { | 1531 | if (name.len <= 8) { |
| 1519 | const full_path = try fs.path.join(arena, &.{ lib_dir, name }); | 1532 | mem.copy(u8, &symbol.name, name); |
| 1520 | fs.cwd().access(full_path, .{}) catch |err| switch (err) { | 1533 | mem.set(u8, symbol.name[name.len..], 0); |
| 1521 | error.FileNotFound => continue, | 1534 | return; |
| 1522 | else => |e| return e, | ||
| 1523 | }; | ||
| 1524 | return full_path; | ||
| 1525 | } | 1535 | } |
| 1526 | return null; | 1536 | const offset = try self.strtab.insert(self.base.allocator, name); |
| 1537 | mem.set(u8, symbol.name[0..4], 0); | ||
| 1538 | mem.writeIntLittle(u32, symbol.name[4..8], offset); | ||
| 1527 | } | 1539 | } |
| 1528 | 1540 | ||
| 1529 | pub fn getDeclVAddr( | 1541 | fn logSymAttributes(sym: *const coff.Symbol, buf: *[4]u8) []const u8 { |
| 1530 | self: *Coff, | 1542 | mem.set(u8, buf[0..4], '_'); |
| 1531 | decl_index: Module.Decl.Index, | 1543 | switch (sym.section_number) { |
| 1532 | reloc_info: link.File.RelocInfo, | 1544 | .UNDEFINED => { |
| 1533 | ) !u64 { | 1545 | buf[3] = 'u'; |
| 1534 | _ = reloc_info; | 1546 | switch (sym.storage_class) { |
| 1535 | const mod = self.base.options.module.?; | 1547 | .EXTERNAL => buf[1] = 'e', |
| 1536 | const decl = mod.declPtr(decl_index); | 1548 | .WEAK_EXTERNAL => buf[1] = 'w', |
| 1537 | assert(self.llvm_object == null); | 1549 | .NULL => {}, |
| 1538 | return self.text_section_virtual_address + decl.link.coff.text_offset; | 1550 | else => unreachable, |
| 1551 | } | ||
| 1552 | }, | ||
| 1553 | .ABSOLUTE => unreachable, // handle ABSOLUTE | ||
| 1554 | .DEBUG => unreachable, | ||
| 1555 | else => { | ||
| 1556 | buf[0] = 's'; | ||
| 1557 | switch (sym.storage_class) { | ||
| 1558 | .EXTERNAL => buf[1] = 'e', | ||
| 1559 | .WEAK_EXTERNAL => buf[1] = 'w', | ||
| 1560 | .NULL => {}, | ||
| 1561 | else => unreachable, | ||
| 1562 | } | ||
| 1563 | }, | ||
| 1564 | } | ||
| 1565 | return buf[0..]; | ||
| 1539 | } | 1566 | } |
| 1540 | 1567 | ||
| 1541 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { | 1568 | fn logSymtab(self: *Coff) void { |
| 1542 | _ = self; | 1569 | var buf: [4]u8 = undefined; |
| 1543 | _ = module; | 1570 | |
| 1544 | _ = decl; | 1571 | log.debug("symtab:", .{}); |
| 1545 | // TODO Implement this | 1572 | log.debug(" object(null)", .{}); |
| 1546 | } | 1573 | for (self.locals.items) |*sym, sym_id| { |
| 1574 | const where = if (sym.section_number == .UNDEFINED) "ord" else "sect"; | ||
| 1575 | const def_index: u16 = switch (sym.section_number) { | ||
| 1576 | .UNDEFINED => 0, // TODO | ||
| 1577 | .ABSOLUTE => unreachable, // TODO | ||
| 1578 | .DEBUG => unreachable, // TODO | ||
| 1579 | else => @enumToInt(sym.section_number), | ||
| 1580 | }; | ||
| 1581 | log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{ | ||
| 1582 | sym_id, | ||
| 1583 | self.getSymbolName(.{ .sym_index = @intCast(u32, sym_id), .file = null }), | ||
| 1584 | sym.value, | ||
| 1585 | where, | ||
| 1586 | def_index, | ||
| 1587 | logSymAttributes(sym, &buf), | ||
| 1588 | }); | ||
| 1589 | } | ||
| 1547 | 1590 | ||
| 1548 | pub fn deinit(self: *Coff) void { | 1591 | log.debug("globals table:", .{}); |
| 1549 | if (build_options.have_llvm) { | 1592 | for (self.globals.keys()) |name, id| { |
| 1550 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | 1593 | const value = self.globals.values()[id]; |
| 1594 | log.debug(" {s} => %{d} in object({?d})", .{ name, value.sym_index, value.file }); | ||
| 1551 | } | 1595 | } |
| 1552 | 1596 | ||
| 1553 | self.text_block_free_list.deinit(self.base.allocator); | 1597 | log.debug("GOT entries:", .{}); |
| 1554 | self.offset_table.deinit(self.base.allocator); | 1598 | for (self.got_entries.keys()) |target, i| { |
| 1555 | self.offset_table_free_list.deinit(self.base.allocator); | 1599 | const got_sym = self.getSymbol(.{ .sym_index = self.got_entries.values()[i], .file = null }); |
| 1600 | const target_sym = self.getSymbol(target); | ||
| 1601 | if (target_sym.section_number == .UNDEFINED) { | ||
| 1602 | log.debug(" {d}@{x} => import('{s}')", .{ | ||
| 1603 | i, | ||
| 1604 | got_sym.value, | ||
| 1605 | self.getSymbolName(target), | ||
| 1606 | }); | ||
| 1607 | } else { | ||
| 1608 | log.debug(" {d}@{x} => local(%{d}) in object({?d}) {s}", .{ | ||
| 1609 | i, | ||
| 1610 | got_sym.value, | ||
| 1611 | target.sym_index, | ||
| 1612 | target.file, | ||
| 1613 | logSymAttributes(target_sym, &buf), | ||
| 1614 | }); | ||
| 1615 | } | ||
| 1616 | } | ||
| 1556 | } | 1617 | } |
src/link/Coff/Atom.zig created+110| ... | @@ -0,0 +1,110 @@ | ||
| 1 | const Atom = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const coff = std.coff; | ||
| 5 | |||
| 6 | const Allocator = std.mem.Allocator; | ||
| 7 | |||
| 8 | const Coff = @import("../Coff.zig"); | ||
| 9 | const Reloc = Coff.Reloc; | ||
| 10 | const SymbolWithLoc = Coff.SymbolWithLoc; | ||
| 11 | |||
| 12 | /// Each decl always gets a local symbol with the fully qualified name. | ||
| 13 | /// The vaddr and size are found here directly. | ||
| 14 | /// The file offset is found by computing the vaddr offset from the section vaddr | ||
| 15 | /// the symbol references, and adding that to the file offset of the section. | ||
| 16 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | ||
| 17 | /// offset table entry. | ||
| 18 | sym_index: u32, | ||
| 19 | |||
| 20 | /// null means symbol defined by Zig source. | ||
| 21 | file: ?u32, | ||
| 22 | |||
| 23 | /// Used size of the atom | ||
| 24 | size: u32, | ||
| 25 | |||
| 26 | /// Alignment of the atom | ||
| 27 | alignment: u32, | ||
| 28 | |||
| 29 | /// Points to the previous and next neighbors, based on the `text_offset`. | ||
| 30 | /// This can be used to find, for example, the capacity of this `Atom`. | ||
| 31 | prev: ?*Atom, | ||
| 32 | next: ?*Atom, | ||
| 33 | |||
| 34 | pub const empty = Atom{ | ||
| 35 | .sym_index = 0, | ||
| 36 | .file = null, | ||
| 37 | .size = 0, | ||
| 38 | .alignment = 0, | ||
| 39 | .prev = null, | ||
| 40 | .next = null, | ||
| 41 | }; | ||
| 42 | |||
| 43 | pub fn deinit(self: *Atom, gpa: Allocator) void { | ||
| 44 | _ = self; | ||
| 45 | _ = gpa; | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Returns symbol referencing this atom. | ||
| 49 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { | ||
| 50 | return coff_file.getSymbol(.{ | ||
| 51 | .sym_index = self.sym_index, | ||
| 52 | .file = self.file, | ||
| 53 | }); | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Returns pointer-to-symbol referencing this atom. | ||
| 57 | pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol { | ||
| 58 | return coff_file.getSymbolPtr(.{ | ||
| 59 | .sym_index = self.sym_index, | ||
| 60 | .file = self.file, | ||
| 61 | }); | ||
| 62 | } | ||
| 63 | |||
| 64 | pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc { | ||
| 65 | return .{ .sym_index = self.sym_index, .file = self.file }; | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Returns the name of this atom. | ||
| 69 | pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { | ||
| 70 | return coff_file.getSymbolName(.{ | ||
| 71 | .sym_index = self.sym_index, | ||
| 72 | .file = self.file, | ||
| 73 | }); | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Returns how much room there is to grow in virtual address space. | ||
| 77 | pub fn capacity(self: Atom, coff_file: *const Coff) u32 { | ||
| 78 | const self_sym = self.getSymbol(coff_file); | ||
| 79 | if (self.next) |next| { | ||
| 80 | const next_sym = next.getSymbol(coff_file); | ||
| 81 | return next_sym.value - self_sym.value; | ||
| 82 | } else { | ||
| 83 | // We are the last atom. | ||
| 84 | // The capacity is limited only by virtual address space. | ||
| 85 | return std.math.maxInt(u32) - self_sym.value; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { | ||
| 90 | // No need to keep a free list node for the last atom. | ||
| 91 | const next = self.next orelse return false; | ||
| 92 | const self_sym = self.getSymbol(coff_file); | ||
| 93 | const next_sym = next.getSymbol(coff_file); | ||
| 94 | const cap = next_sym.value - self_sym.value; | ||
| 95 | const ideal_cap = Coff.padToIdeal(self.size); | ||
| 96 | if (cap <= ideal_cap) return false; | ||
| 97 | const surplus = cap - ideal_cap; | ||
| 98 | return surplus >= Coff.min_text_capacity; | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Reloc) !void { | ||
| 102 | const gpa = coff_file.base.allocator; | ||
| 103 | // TODO causes a segfault on Windows | ||
| 104 | // log.debug("adding reloc of type {s} to target %{d}", .{ @tagName(reloc.@"type"), reloc.target.sym_index }); | ||
| 105 | const gop = try coff_file.relocs.getOrPut(gpa, self); | ||
| 106 | if (!gop.found_existing) { | ||
| 107 | gop.value_ptr.* = .{}; | ||
| 108 | } | ||
| 109 | try gop.value_ptr.append(gpa, reloc); | ||
| 110 | } | ||
src/link/Coff/Object.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | const Object = @This(); | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const mem = std.mem; | ||
| 5 | |||
| 6 | const Allocator = mem.Allocator; | ||
| 7 | |||
| 8 | name: []const u8, | ||
| 9 | |||
| 10 | pub fn deinit(self: *Object, gpa: Allocator) void { | ||
| 11 | gpa.free(self.name); | ||
| 12 | } | ||
src/link/Coff/lld.zig created+602| ... | @@ -0,0 +1,602 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const build_options = @import("build_options"); | ||
| 3 | const allocPrint = std.fmt.allocPrint; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const fs = std.fs; | ||
| 6 | const log = std.log.scoped(.link); | ||
| 7 | const mem = std.mem; | ||
| 8 | |||
| 9 | const mingw = @import("../../mingw.zig"); | ||
| 10 | const link = @import("../../link.zig"); | ||
| 11 | const lldMain = @import("../../main.zig").lldMain; | ||
| 12 | const trace = @import("../../tracy.zig").trace; | ||
| 13 | |||
| 14 | const Allocator = mem.Allocator; | ||
| 15 | |||
| 16 | const Cache = @import("../../Cache.zig"); | ||
| 17 | const Coff = @import("../Coff.zig"); | ||
| 18 | const Compilation = @import("../../Compilation.zig"); | ||
| 19 | |||
| 20 | pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { | ||
| 21 | const tracy = trace(@src()); | ||
| 22 | defer tracy.end(); | ||
| 23 | |||
| 24 | var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); | ||
| 25 | defer arena_allocator.deinit(); | ||
| 26 | const arena = arena_allocator.allocator(); | ||
| 27 | |||
| 28 | const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. | ||
| 29 | const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); | ||
| 30 | |||
| 31 | // If there is no Zig code to compile, then we should skip flushing the output file because it | ||
| 32 | // will not be part of the linker line anyway. | ||
| 33 | const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { | ||
| 34 | const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1; | ||
| 35 | if (use_stage1) { | ||
| 36 | const obj_basename = try std.zig.binNameAlloc(arena, .{ | ||
| 37 | .root_name = self.base.options.root_name, | ||
| 38 | .target = self.base.options.target, | ||
| 39 | .output_mode = .Obj, | ||
| 40 | }); | ||
| 41 | switch (self.base.options.cache_mode) { | ||
| 42 | .incremental => break :blk try module.zig_cache_artifact_directory.join( | ||
| 43 | arena, | ||
| 44 | &[_][]const u8{obj_basename}, | ||
| 45 | ), | ||
| 46 | .whole => break :blk try fs.path.join(arena, &.{ | ||
| 47 | fs.path.dirname(full_out_path).?, obj_basename, | ||
| 48 | }), | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | try self.flushModule(comp, prog_node); | ||
| 53 | |||
| 54 | if (fs.path.dirname(full_out_path)) |dirname| { | ||
| 55 | break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); | ||
| 56 | } else { | ||
| 57 | break :blk self.base.intermediary_basename.?; | ||
| 58 | } | ||
| 59 | } else null; | ||
| 60 | |||
| 61 | var sub_prog_node = prog_node.start("LLD Link", 0); | ||
| 62 | sub_prog_node.activate(); | ||
| 63 | sub_prog_node.context.refresh(); | ||
| 64 | defer sub_prog_node.end(); | ||
| 65 | |||
| 66 | const is_lib = self.base.options.output_mode == .Lib; | ||
| 67 | const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; | ||
| 68 | const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; | ||
| 69 | const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib; | ||
| 70 | const target = self.base.options.target; | ||
| 71 | |||
| 72 | // See link/Elf.zig for comments on how this mechanism works. | ||
| 73 | const id_symlink_basename = "lld.id"; | ||
| 74 | |||
| 75 | var man: Cache.Manifest = undefined; | ||
| 76 | defer if (!self.base.options.disable_lld_caching) man.deinit(); | ||
| 77 | |||
| 78 | var digest: [Cache.hex_digest_len]u8 = undefined; | ||
| 79 | |||
| 80 | if (!self.base.options.disable_lld_caching) { | ||
| 81 | man = comp.cache_parent.obtain(); | ||
| 82 | self.base.releaseLock(); | ||
| 83 | |||
| 84 | comptime assert(Compilation.link_hash_implementation_version == 7); | ||
| 85 | |||
| 86 | for (self.base.options.objects) |obj| { | ||
| 87 | _ = try man.addFile(obj.path, null); | ||
| 88 | man.hash.add(obj.must_link); | ||
| 89 | } | ||
| 90 | for (comp.c_object_table.keys()) |key| { | ||
| 91 | _ = try man.addFile(key.status.success.object_path, null); | ||
| 92 | } | ||
| 93 | try man.addOptionalFile(module_obj_path); | ||
| 94 | man.hash.addOptionalBytes(self.base.options.entry); | ||
| 95 | man.hash.addOptional(self.base.options.stack_size_override); | ||
| 96 | man.hash.addOptional(self.base.options.image_base_override); | ||
| 97 | man.hash.addListOfBytes(self.base.options.lib_dirs); | ||
| 98 | man.hash.add(self.base.options.skip_linker_dependencies); | ||
| 99 | if (self.base.options.link_libc) { | ||
| 100 | man.hash.add(self.base.options.libc_installation != null); | ||
| 101 | if (self.base.options.libc_installation) |libc_installation| { | ||
| 102 | man.hash.addBytes(libc_installation.crt_dir.?); | ||
| 103 | if (target.abi == .msvc) { | ||
| 104 | man.hash.addBytes(libc_installation.msvc_lib_dir.?); | ||
| 105 | man.hash.addBytes(libc_installation.kernel32_lib_dir.?); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | ||
| 109 | link.hashAddSystemLibs(&man.hash, self.base.options.system_libs); | ||
| 110 | man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys()); | ||
| 111 | man.hash.addOptional(self.base.options.subsystem); | ||
| 112 | man.hash.add(self.base.options.is_test); | ||
| 113 | man.hash.add(self.base.options.tsaware); | ||
| 114 | man.hash.add(self.base.options.nxcompat); | ||
| 115 | man.hash.add(self.base.options.dynamicbase); | ||
| 116 | // strip does not need to go into the linker hash because it is part of the hash namespace | ||
| 117 | man.hash.addOptional(self.base.options.major_subsystem_version); | ||
| 118 | man.hash.addOptional(self.base.options.minor_subsystem_version); | ||
| 119 | |||
| 120 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. | ||
| 121 | _ = try man.hit(); | ||
| 122 | digest = man.final(); | ||
| 123 | var prev_digest_buf: [digest.len]u8 = undefined; | ||
| 124 | const prev_digest: []u8 = Cache.readSmallFile( | ||
| 125 | directory.handle, | ||
| 126 | id_symlink_basename, | ||
| 127 | &prev_digest_buf, | ||
| 128 | ) catch |err| blk: { | ||
| 129 | log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); | ||
| 130 | // Handle this as a cache miss. | ||
| 131 | break :blk prev_digest_buf[0..0]; | ||
| 132 | }; | ||
| 133 | if (mem.eql(u8, prev_digest, &digest)) { | ||
| 134 | log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); | ||
| 135 | // Hot diggity dog! The output binary is already there. | ||
| 136 | self.base.lock = man.toOwnedLock(); | ||
| 137 | return; | ||
| 138 | } | ||
| 139 | log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) }); | ||
| 140 | |||
| 141 | // We are about to change the output file to be different, so we invalidate the build hash now. | ||
| 142 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | ||
| 143 | error.FileNotFound => {}, | ||
| 144 | else => |e| return e, | ||
| 145 | }; | ||
| 146 | } | ||
| 147 | |||
| 148 | if (self.base.options.output_mode == .Obj) { | ||
| 149 | // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy | ||
| 150 | // here. TODO: think carefully about how we can avoid this redundant operation when doing | ||
| 151 | // build-obj. See also the corresponding TODO in linkAsArchive. | ||
| 152 | const the_object_path = blk: { | ||
| 153 | if (self.base.options.objects.len != 0) | ||
| 154 | break :blk self.base.options.objects[0].path; | ||
| 155 | |||
| 156 | if (comp.c_object_table.count() != 0) | ||
| 157 | break :blk comp.c_object_table.keys()[0].status.success.object_path; | ||
| 158 | |||
| 159 | if (module_obj_path) |p| | ||
| 160 | break :blk p; | ||
| 161 | |||
| 162 | // TODO I think this is unreachable. Audit this situation when solving the above TODO | ||
| 163 | // regarding eliding redundant object -> object transformations. | ||
| 164 | return error.NoObjectsToLink; | ||
| 165 | }; | ||
| 166 | // This can happen when using --enable-cache and using the stage1 backend. In this case | ||
| 167 | // we can skip the file copy. | ||
| 168 | if (!mem.eql(u8, the_object_path, full_out_path)) { | ||
| 169 | try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); | ||
| 170 | } | ||
| 171 | } else { | ||
| 172 | // Create an LLD command line and invoke it. | ||
| 173 | var argv = std.ArrayList([]const u8).init(self.base.allocator); | ||
| 174 | defer argv.deinit(); | ||
| 175 | // We will invoke ourselves as a child process to gain access to LLD. | ||
| 176 | // This is necessary because LLD does not behave properly as a library - | ||
| 177 | // it calls exit() and does not reset all global data between invocations. | ||
| 178 | try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" }); | ||
| 179 | |||
| 180 | try argv.append("-ERRORLIMIT:0"); | ||
| 181 | try argv.append("-NOLOGO"); | ||
| 182 | if (!self.base.options.strip) { | ||
| 183 | try argv.append("-DEBUG"); | ||
| 184 | } | ||
| 185 | if (self.base.options.lto) { | ||
| 186 | switch (self.base.options.optimize_mode) { | ||
| 187 | .Debug => {}, | ||
| 188 | .ReleaseSmall => try argv.append("-OPT:lldlto=2"), | ||
| 189 | .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"), | ||
| 190 | } | ||
| 191 | } | ||
| 192 | if (self.base.options.output_mode == .Exe) { | ||
| 193 | const stack_size = self.base.options.stack_size_override orelse 16777216; | ||
| 194 | try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); | ||
| 195 | } | ||
| 196 | if (self.base.options.image_base_override) |image_base| { | ||
| 197 | try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base})); | ||
| 198 | } | ||
| 199 | |||
| 200 | if (target.cpu.arch == .i386) { | ||
| 201 | try argv.append("-MACHINE:X86"); | ||
| 202 | } else if (target.cpu.arch == .x86_64) { | ||
| 203 | try argv.append("-MACHINE:X64"); | ||
| 204 | } else if (target.cpu.arch.isARM()) { | ||
| 205 | if (target.cpu.arch.ptrBitWidth() == 32) { | ||
| 206 | try argv.append("-MACHINE:ARM"); | ||
| 207 | } else { | ||
| 208 | try argv.append("-MACHINE:ARM64"); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | for (self.base.options.force_undefined_symbols.keys()) |symbol| { | ||
| 213 | try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); | ||
| 214 | } | ||
| 215 | |||
| 216 | if (is_dyn_lib) { | ||
| 217 | try argv.append("-DLL"); | ||
| 218 | } | ||
| 219 | |||
| 220 | if (self.base.options.entry) |entry| { | ||
| 221 | try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry})); | ||
| 222 | } | ||
| 223 | |||
| 224 | if (self.base.options.tsaware) { | ||
| 225 | try argv.append("-tsaware"); | ||
| 226 | } | ||
| 227 | if (self.base.options.nxcompat) { | ||
| 228 | try argv.append("-nxcompat"); | ||
| 229 | } | ||
| 230 | if (self.base.options.dynamicbase) { | ||
| 231 | try argv.append("-dynamicbase"); | ||
| 232 | } | ||
| 233 | |||
| 234 | try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); | ||
| 235 | |||
| 236 | if (self.base.options.implib_emit) |emit| { | ||
| 237 | const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); | ||
| 238 | try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); | ||
| 239 | } | ||
| 240 | |||
| 241 | if (self.base.options.link_libc) { | ||
| 242 | if (self.base.options.libc_installation) |libc_installation| { | ||
| 243 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); | ||
| 244 | |||
| 245 | if (target.abi == .msvc) { | ||
| 246 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); | ||
| 247 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | for (self.base.options.lib_dirs) |lib_dir| { | ||
| 253 | try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); | ||
| 254 | } | ||
| 255 | |||
| 256 | try argv.ensureUnusedCapacity(self.base.options.objects.len); | ||
| 257 | for (self.base.options.objects) |obj| { | ||
| 258 | if (obj.must_link) { | ||
| 259 | argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); | ||
| 260 | } else { | ||
| 261 | argv.appendAssumeCapacity(obj.path); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | for (comp.c_object_table.keys()) |key| { | ||
| 266 | try argv.append(key.status.success.object_path); | ||
| 267 | } | ||
| 268 | |||
| 269 | if (module_obj_path) |p| { | ||
| 270 | try argv.append(p); | ||
| 271 | } | ||
| 272 | |||
| 273 | const resolved_subsystem: ?std.Target.SubSystem = blk: { | ||
| 274 | if (self.base.options.subsystem) |explicit| break :blk explicit; | ||
| 275 | switch (target.os.tag) { | ||
| 276 | .windows => { | ||
| 277 | if (self.base.options.module) |module| { | ||
| 278 | if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) | ||
| 279 | break :blk null; | ||
| 280 | if (module.stage1_flags.have_c_main or self.base.options.is_test or | ||
| 281 | module.stage1_flags.have_winmain_crt_startup or | ||
| 282 | module.stage1_flags.have_wwinmain_crt_startup) | ||
| 283 | { | ||
| 284 | break :blk .Console; | ||
| 285 | } | ||
| 286 | if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain) | ||
| 287 | break :blk .Windows; | ||
| 288 | } | ||
| 289 | }, | ||
| 290 | .uefi => break :blk .EfiApplication, | ||
| 291 | else => {}, | ||
| 292 | } | ||
| 293 | break :blk null; | ||
| 294 | }; | ||
| 295 | |||
| 296 | const Mode = enum { uefi, win32 }; | ||
| 297 | const mode: Mode = mode: { | ||
| 298 | if (resolved_subsystem) |subsystem| { | ||
| 299 | const subsystem_suffix = ss: { | ||
| 300 | if (self.base.options.major_subsystem_version) |major| { | ||
| 301 | if (self.base.options.minor_subsystem_version) |minor| { | ||
| 302 | break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor }); | ||
| 303 | } else { | ||
| 304 | break :ss try allocPrint(arena, ",{d}", .{major}); | ||
| 305 | } | ||
| 306 | } | ||
| 307 | break :ss ""; | ||
| 308 | }; | ||
| 309 | |||
| 310 | switch (subsystem) { | ||
| 311 | .Console => { | ||
| 312 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{ | ||
| 313 | subsystem_suffix, | ||
| 314 | })); | ||
| 315 | break :mode .win32; | ||
| 316 | }, | ||
| 317 | .EfiApplication => { | ||
| 318 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{ | ||
| 319 | subsystem_suffix, | ||
| 320 | })); | ||
| 321 | break :mode .uefi; | ||
| 322 | }, | ||
| 323 | .EfiBootServiceDriver => { | ||
| 324 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{ | ||
| 325 | subsystem_suffix, | ||
| 326 | })); | ||
| 327 | break :mode .uefi; | ||
| 328 | }, | ||
| 329 | .EfiRom => { | ||
| 330 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{ | ||
| 331 | subsystem_suffix, | ||
| 332 | })); | ||
| 333 | break :mode .uefi; | ||
| 334 | }, | ||
| 335 | .EfiRuntimeDriver => { | ||
| 336 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{ | ||
| 337 | subsystem_suffix, | ||
| 338 | })); | ||
| 339 | break :mode .uefi; | ||
| 340 | }, | ||
| 341 | .Native => { | ||
| 342 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{ | ||
| 343 | subsystem_suffix, | ||
| 344 | })); | ||
| 345 | break :mode .win32; | ||
| 346 | }, | ||
| 347 | .Posix => { | ||
| 348 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{ | ||
| 349 | subsystem_suffix, | ||
| 350 | })); | ||
| 351 | break :mode .win32; | ||
| 352 | }, | ||
| 353 | .Windows => { | ||
| 354 | try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{ | ||
| 355 | subsystem_suffix, | ||
| 356 | })); | ||
| 357 | break :mode .win32; | ||
| 358 | }, | ||
| 359 | } | ||
| 360 | } else if (target.os.tag == .uefi) { | ||
| 361 | break :mode .uefi; | ||
| 362 | } else { | ||
| 363 | break :mode .win32; | ||
| 364 | } | ||
| 365 | }; | ||
| 366 | |||
| 367 | switch (mode) { | ||
| 368 | .uefi => try argv.appendSlice(&[_][]const u8{ | ||
| 369 | "-BASE:0", | ||
| 370 | "-ENTRY:EfiMain", | ||
| 371 | "-OPT:REF", | ||
| 372 | "-SAFESEH:NO", | ||
| 373 | "-MERGE:.rdata=.data", | ||
| 374 | "-ALIGN:32", | ||
| 375 | "-NODEFAULTLIB", | ||
| 376 | "-SECTION:.xdata,D", | ||
| 377 | }), | ||
| 378 | .win32 => { | ||
| 379 | if (link_in_crt) { | ||
| 380 | if (target.abi.isGnu()) { | ||
| 381 | try argv.append("-lldmingw"); | ||
| 382 | |||
| 383 | if (target.cpu.arch == .i386) { | ||
| 384 | try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); | ||
| 385 | } else { | ||
| 386 | try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); | ||
| 387 | } | ||
| 388 | |||
| 389 | if (is_dyn_lib) { | ||
| 390 | try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj")); | ||
| 391 | if (target.cpu.arch == .i386) { | ||
| 392 | try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12"); | ||
| 393 | } else { | ||
| 394 | try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup"); | ||
| 395 | } | ||
| 396 | } else { | ||
| 397 | try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj")); | ||
| 398 | } | ||
| 399 | |||
| 400 | try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); | ||
| 401 | try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib")); | ||
| 402 | try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib")); | ||
| 403 | |||
| 404 | for (mingw.always_link_libs) |name| { | ||
| 405 | if (!self.base.options.system_libs.contains(name)) { | ||
| 406 | const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); | ||
| 407 | try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | } else { | ||
| 411 | const lib_str = switch (self.base.options.link_mode) { | ||
| 412 | .Dynamic => "", | ||
| 413 | .Static => "lib", | ||
| 414 | }; | ||
| 415 | const d_str = switch (self.base.options.optimize_mode) { | ||
| 416 | .Debug => "d", | ||
| 417 | else => "", | ||
| 418 | }; | ||
| 419 | switch (self.base.options.link_mode) { | ||
| 420 | .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), | ||
| 421 | .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), | ||
| 422 | } | ||
| 423 | |||
| 424 | try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str })); | ||
| 425 | try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str })); | ||
| 426 | |||
| 427 | //Visual C++ 2015 Conformance Changes | ||
| 428 | //https://msdn.microsoft.com/en-us/library/bb531344.aspx | ||
| 429 | try argv.append("legacy_stdio_definitions.lib"); | ||
| 430 | |||
| 431 | // msvcrt depends on kernel32 and ntdll | ||
| 432 | try argv.append("kernel32.lib"); | ||
| 433 | try argv.append("ntdll.lib"); | ||
| 434 | } | ||
| 435 | } else { | ||
| 436 | try argv.append("-NODEFAULTLIB"); | ||
| 437 | if (!is_lib) { | ||
| 438 | if (self.base.options.module) |module| { | ||
| 439 | if (module.stage1_flags.have_winmain_crt_startup) { | ||
| 440 | try argv.append("-ENTRY:WinMainCRTStartup"); | ||
| 441 | } else { | ||
| 442 | try argv.append("-ENTRY:wWinMainCRTStartup"); | ||
| 443 | } | ||
| 444 | } else { | ||
| 445 | try argv.append("-ENTRY:wWinMainCRTStartup"); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | } | ||
| 449 | }, | ||
| 450 | } | ||
| 451 | |||
| 452 | // libc++ dep | ||
| 453 | if (self.base.options.link_libcpp) { | ||
| 454 | try argv.append(comp.libcxxabi_static_lib.?.full_object_path); | ||
| 455 | try argv.append(comp.libcxx_static_lib.?.full_object_path); | ||
| 456 | } | ||
| 457 | |||
| 458 | // libunwind dep | ||
| 459 | if (self.base.options.link_libunwind) { | ||
| 460 | try argv.append(comp.libunwind_static_lib.?.full_object_path); | ||
| 461 | } | ||
| 462 | |||
| 463 | if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { | ||
| 464 | if (!self.base.options.link_libc) { | ||
| 465 | if (comp.libc_static_lib) |lib| { | ||
| 466 | try argv.append(lib.full_object_path); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | // MinGW doesn't provide libssp symbols | ||
| 470 | if (target.abi.isGnu()) { | ||
| 471 | if (comp.libssp_static_lib) |lib| { | ||
| 472 | try argv.append(lib.full_object_path); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | // MSVC compiler_rt is missing some stuff, so we build it unconditionally but | ||
| 476 | // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. | ||
| 477 | if (comp.compiler_rt_lib) |lib| { | ||
| 478 | try argv.append(lib.full_object_path); | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | try argv.ensureUnusedCapacity(self.base.options.system_libs.count()); | ||
| 483 | for (self.base.options.system_libs.keys()) |key| { | ||
| 484 | const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); | ||
| 485 | if (comp.crt_files.get(lib_basename)) |crt_file| { | ||
| 486 | argv.appendAssumeCapacity(crt_file.full_object_path); | ||
| 487 | continue; | ||
| 488 | } | ||
| 489 | if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| { | ||
| 490 | argv.appendAssumeCapacity(full_path); | ||
| 491 | continue; | ||
| 492 | } | ||
| 493 | if (target.abi.isGnu()) { | ||
| 494 | const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); | ||
| 495 | if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| { | ||
| 496 | argv.appendAssumeCapacity(full_path); | ||
| 497 | continue; | ||
| 498 | } | ||
| 499 | } | ||
| 500 | log.err("DLL import library for -l{s} not found", .{key}); | ||
| 501 | return error.DllImportLibraryNotFound; | ||
| 502 | } | ||
| 503 | |||
| 504 | if (self.base.options.verbose_link) { | ||
| 505 | // Skip over our own name so that the LLD linker name is the first argv item. | ||
| 506 | Compilation.dump_argv(argv.items[1..]); | ||
| 507 | } | ||
| 508 | |||
| 509 | if (std.process.can_spawn) { | ||
| 510 | // If possible, we run LLD as a child process because it does not always | ||
| 511 | // behave properly as a library, unfortunately. | ||
| 512 | // https://github.com/ziglang/zig/issues/3825 | ||
| 513 | var child = std.ChildProcess.init(argv.items, arena); | ||
| 514 | if (comp.clang_passthrough_mode) { | ||
| 515 | child.stdin_behavior = .Inherit; | ||
| 516 | child.stdout_behavior = .Inherit; | ||
| 517 | child.stderr_behavior = .Inherit; | ||
| 518 | |||
| 519 | const term = child.spawnAndWait() catch |err| { | ||
| 520 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 521 | return error.UnableToSpawnSelf; | ||
| 522 | }; | ||
| 523 | switch (term) { | ||
| 524 | .Exited => |code| { | ||
| 525 | if (code != 0) { | ||
| 526 | std.process.exit(code); | ||
| 527 | } | ||
| 528 | }, | ||
| 529 | else => std.process.abort(), | ||
| 530 | } | ||
| 531 | } else { | ||
| 532 | child.stdin_behavior = .Ignore; | ||
| 533 | child.stdout_behavior = .Ignore; | ||
| 534 | child.stderr_behavior = .Pipe; | ||
| 535 | |||
| 536 | try child.spawn(); | ||
| 537 | |||
| 538 | const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 539 | |||
| 540 | const term = child.wait() catch |err| { | ||
| 541 | log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); | ||
| 542 | return error.UnableToSpawnSelf; | ||
| 543 | }; | ||
| 544 | |||
| 545 | switch (term) { | ||
| 546 | .Exited => |code| { | ||
| 547 | if (code != 0) { | ||
| 548 | // TODO parse this output and surface with the Compilation API rather than | ||
| 549 | // directly outputting to stderr here. | ||
| 550 | std.debug.print("{s}", .{stderr}); | ||
| 551 | return error.LLDReportedFailure; | ||
| 552 | } | ||
| 553 | }, | ||
| 554 | else => { | ||
| 555 | log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr }); | ||
| 556 | return error.LLDCrashed; | ||
| 557 | }, | ||
| 558 | } | ||
| 559 | |||
| 560 | if (stderr.len != 0) { | ||
| 561 | log.warn("unexpected LLD stderr:\n{s}", .{stderr}); | ||
| 562 | } | ||
| 563 | } | ||
| 564 | } else { | ||
| 565 | const exit_code = try lldMain(arena, argv.items, false); | ||
| 566 | if (exit_code != 0) { | ||
| 567 | if (comp.clang_passthrough_mode) { | ||
| 568 | std.process.exit(exit_code); | ||
| 569 | } else { | ||
| 570 | return error.LLDReportedFailure; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | if (!self.base.options.disable_lld_caching) { | ||
| 577 | // Update the file with the digest. If it fails we can continue; it only | ||
| 578 | // means that the next invocation will have an unnecessary cache miss. | ||
| 579 | Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { | ||
| 580 | log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); | ||
| 581 | }; | ||
| 582 | // Again failure here only means an unnecessary cache miss. | ||
| 583 | man.writeManifest() catch |err| { | ||
| 584 | log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | ||
| 585 | }; | ||
| 586 | // We hang on to this lock so that the output file path can be used without | ||
| 587 | // other processes clobbering it. | ||
| 588 | self.base.lock = man.toOwnedLock(); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | |||
| 592 | fn findLib(arena: Allocator, name: []const u8, lib_dirs: []const []const u8) !?[]const u8 { | ||
| 593 | for (lib_dirs) |lib_dir| { | ||
| 594 | const full_path = try fs.path.join(arena, &.{ lib_dir, name }); | ||
| 595 | fs.cwd().access(full_path, .{}) catch |err| switch (err) { | ||
| 596 | error.FileNotFound => continue, | ||
| 597 | else => |e| return e, | ||
| 598 | }; | ||
| 599 | return full_path; | ||
| 600 | } | ||
| 601 | return null; | ||
| 602 | } | ||
src/link/MachO.zig+1-2| ... | @@ -26,7 +26,7 @@ const trace = @import("../tracy.zig").trace; | ... | @@ -26,7 +26,7 @@ const trace = @import("../tracy.zig").trace; |
| 26 | const Air = @import("../Air.zig"); | 26 | const Air = @import("../Air.zig"); |
| 27 | const Allocator = mem.Allocator; | 27 | const Allocator = mem.Allocator; |
| 28 | const Archive = @import("MachO/Archive.zig"); | 28 | const Archive = @import("MachO/Archive.zig"); |
| 29 | const Atom = @import("MachO/Atom.zig"); | 29 | pub const Atom = @import("MachO/Atom.zig"); |
| 30 | const Cache = @import("../Cache.zig"); | 30 | const Cache = @import("../Cache.zig"); |
| 31 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 31 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 32 | const Compilation = @import("../Compilation.zig"); | 32 | const Compilation = @import("../Compilation.zig"); |
| ... | @@ -44,7 +44,6 @@ const Type = @import("../type.zig").Type; | ... | @@ -44,7 +44,6 @@ const Type = @import("../type.zig").Type; |
| 44 | const TypedValue = @import("../TypedValue.zig"); | 44 | const TypedValue = @import("../TypedValue.zig"); |
| 45 | const Value = @import("../value.zig").Value; | 45 | const Value = @import("../value.zig").Value; |
| 46 | 46 | ||
| 47 | pub const TextBlock = Atom; | ||
| 48 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); | 47 | pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); |
| 49 | 48 | ||
| 50 | pub const base_tag: File.Tag = File.Tag.macho; | 49 | pub const base_tag: File.Tag = File.Tag.macho; |
src/link/MachO/DebugSymbols.zig-1| ... | @@ -18,7 +18,6 @@ const Dwarf = @import("../Dwarf.zig"); | ... | @@ -18,7 +18,6 @@ const Dwarf = @import("../Dwarf.zig"); |
| 18 | const MachO = @import("../MachO.zig"); | 18 | const MachO = @import("../MachO.zig"); |
| 19 | const Module = @import("../../Module.zig"); | 19 | const Module = @import("../../Module.zig"); |
| 20 | const StringTable = @import("../strtab.zig").StringTable; | 20 | const StringTable = @import("../strtab.zig").StringTable; |
| 21 | const TextBlock = MachO.TextBlock; | ||
| 22 | const Type = @import("../../type.zig").Type; | 21 | const Type = @import("../../type.zig").Type; |
| 23 | 22 | ||
| 24 | base: *MachO, | 23 | base: *MachO, |
src/link/Wasm.zig+8-13| ... | @@ -2836,24 +2836,19 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! | ... | @@ -2836,24 +2836,19 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 2836 | try argv.append(entry); | 2836 | try argv.append(entry); |
| 2837 | } | 2837 | } |
| 2838 | 2838 | ||
| 2839 | if (self.base.options.output_mode == .Exe) { | 2839 | // Increase the default stack size to a more reasonable value of 1MB instead of |
| 2840 | // Increase the default stack size to a more reasonable value of 1MB instead of | 2840 | // the default of 1 Wasm page being 64KB, unless overridden by the user. |
| 2841 | // the default of 1 Wasm page being 64KB, unless overridden by the user. | 2841 | try argv.append("-z"); |
| 2842 | try argv.append("-z"); | 2842 | const stack_size = self.base.options.stack_size_override orelse wasm.page_size * 16; |
| 2843 | const stack_size = self.base.options.stack_size_override orelse 1048576; | 2843 | const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); |
| 2844 | const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); | 2844 | try argv.append(arg); |
| 2845 | try argv.append(arg); | ||
| 2846 | 2845 | ||
| 2846 | if (self.base.options.output_mode == .Exe) { | ||
| 2847 | if (self.base.options.wasi_exec_model == .reactor) { | 2847 | if (self.base.options.wasi_exec_model == .reactor) { |
| 2848 | // Reactor execution model does not have _start so lld doesn't look for it. | 2848 | // Reactor execution model does not have _start so lld doesn't look for it. |
| 2849 | try argv.append("--no-entry"); | 2849 | try argv.append("--no-entry"); |
| 2850 | } | 2850 | } |
| 2851 | } else { | 2851 | } else if (self.base.options.entry == null) { |
| 2852 | if (self.base.options.stack_size_override) |stack_size| { | ||
| 2853 | try argv.append("-z"); | ||
| 2854 | const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); | ||
| 2855 | try argv.append(arg); | ||
| 2856 | } | ||
| 2857 | try argv.append("--no-entry"); // So lld doesn't look for _start. | 2852 | try argv.append("--no-entry"); // So lld doesn't look for _start. |
| 2858 | } | 2853 | } |
| 2859 | try argv.appendSlice(&[_][]const u8{ | 2854 | try argv.appendSlice(&[_][]const u8{ |
src/link/strtab.zig+4| ... | @@ -109,5 +109,9 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { | ... | @@ -109,5 +109,9 @@ pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type { |
| 109 | pub fn getAssumeExists(self: Self, off: u32) []const u8 { | 109 | pub fn getAssumeExists(self: Self, off: u32) []const u8 { |
| 110 | return self.get(off) orelse unreachable; | 110 | return self.get(off) orelse unreachable; |
| 111 | } | 111 | } |
| 112 | |||
| 113 | pub fn len(self: Self) usize { | ||
| 114 | return self.buffer.items.len; | ||
| 115 | } | ||
| 112 | }; | 116 | }; |
| 113 | } | 117 | } |
src/main.zig+3-3| ... | @@ -3734,9 +3734,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -3734,9 +3734,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 3734 | const self_exe_path = try introspect.findZigExePath(arena); | 3734 | const self_exe_path = try introspect.findZigExePath(arena); |
| 3735 | 3735 | ||
| 3736 | var build_file: ?[]const u8 = null; | 3736 | var build_file: ?[]const u8 = null; |
| 3737 | var override_lib_dir: ?[]const u8 = null; | 3737 | var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); |
| 3738 | var override_global_cache_dir: ?[]const u8 = null; | 3738 | var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); |
| 3739 | var override_local_cache_dir: ?[]const u8 = null; | 3739 | var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); |
| 3740 | var child_argv = std.ArrayList([]const u8).init(arena); | 3740 | var child_argv = std.ArrayList([]const u8).init(arena); |
| 3741 | 3741 | ||
| 3742 | const argv_index_exe = child_argv.items.len; | 3742 | const argv_index_exe = child_argv.items.len; |
src/mingw.zig+4-4| ... | @@ -6,7 +6,6 @@ const assert = std.debug.assert; | ... | @@ -6,7 +6,6 @@ const assert = std.debug.assert; |
| 6 | const log = std.log.scoped(.mingw); | 6 | const log = std.log.scoped(.mingw); |
| 7 | 7 | ||
| 8 | const builtin = @import("builtin"); | 8 | const builtin = @import("builtin"); |
| 9 | const target_util = @import("target.zig"); | ||
| 10 | const Compilation = @import("Compilation.zig"); | 9 | const Compilation = @import("Compilation.zig"); |
| 11 | const build_options = @import("build_options"); | 10 | const build_options = @import("build_options"); |
| 12 | const Cache = @import("Cache.zig"); | 11 | const Cache = @import("Cache.zig"); |
| ... | @@ -404,11 +403,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { | ... | @@ -404,11 +403,12 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 404 | }); | 403 | }); |
| 405 | errdefer comp.gpa.free(lib_final_path); | 404 | errdefer comp.gpa.free(lib_final_path); |
| 406 | 405 | ||
| 407 | const llvm = @import("codegen/llvm/bindings.zig"); | 406 | const llvm_bindings = @import("codegen/llvm/bindings.zig"); |
| 408 | const arch_type = target_util.archToLLVM(target.cpu.arch); | 407 | const llvm = @import("codegen/llvm.zig"); |
| 408 | const arch_tag = llvm.targetArch(target.cpu.arch); | ||
| 409 | const def_final_path_z = try arena.dupeZ(u8, def_final_path); | 409 | const def_final_path_z = try arena.dupeZ(u8, def_final_path); |
| 410 | const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); | 410 | const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); |
| 411 | if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) { | 411 | if (llvm_bindings.WriteImportLibrary(def_final_path_z.ptr, arch_tag, lib_final_path_z.ptr, true)) { |
| 412 | // TODO surface a proper error here | 412 | // TODO surface a proper error here |
| 413 | log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); | 413 | log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); |
| 414 | return error.WritingImportLibFailed; | 414 | return error.WritingImportLibFailed; |
src/print_zir.zig+5-6| ... | @@ -283,7 +283,6 @@ const Writer = struct { | ... | @@ -283,7 +283,6 @@ const Writer = struct { |
| 283 | .mul_add => try self.writeMulAdd(stream, inst), | 283 | .mul_add => try self.writeMulAdd(stream, inst), |
| 284 | .field_parent_ptr => try self.writeFieldParentPtr(stream, inst), | 284 | .field_parent_ptr => try self.writeFieldParentPtr(stream, inst), |
| 285 | .builtin_call => try self.writeBuiltinCall(stream, inst), | 285 | .builtin_call => try self.writeBuiltinCall(stream, inst), |
| 286 | .builtin_async_call => try self.writeBuiltinAsyncCall(stream, inst), | ||
| 287 | 286 | ||
| 288 | .struct_init_anon, | 287 | .struct_init_anon, |
| 289 | .struct_init_anon_ref, | 288 | .struct_init_anon_ref, |
| ... | @@ -397,7 +396,7 @@ const Writer = struct { | ... | @@ -397,7 +396,7 @@ const Writer = struct { |
| 397 | .field_val_named, | 396 | .field_val_named, |
| 398 | => try self.writePlNodeFieldNamed(stream, inst), | 397 | => try self.writePlNodeFieldNamed(stream, inst), |
| 399 | 398 | ||
| 400 | .as_node => try self.writeAs(stream, inst), | 399 | .as_node, .as_shift_operand => try self.writeAs(stream, inst), |
| 401 | 400 | ||
| 402 | .repeat, | 401 | .repeat, |
| 403 | .repeat_inline, | 402 | .repeat_inline, |
| ... | @@ -531,6 +530,7 @@ const Writer = struct { | ... | @@ -531,6 +530,7 @@ const Writer = struct { |
| 531 | try stream.writeAll(") "); | 530 | try stream.writeAll(") "); |
| 532 | try self.writeSrc(stream, src); | 531 | try self.writeSrc(stream, src); |
| 533 | }, | 532 | }, |
| 533 | .builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended), | ||
| 534 | } | 534 | } |
| 535 | } | 535 | } |
| 536 | 536 | ||
| ... | @@ -814,9 +814,8 @@ const Writer = struct { | ... | @@ -814,9 +814,8 @@ const Writer = struct { |
| 814 | try self.writeSrc(stream, inst_data.src()); | 814 | try self.writeSrc(stream, inst_data.src()); |
| 815 | } | 815 | } |
| 816 | 816 | ||
| 817 | fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 817 | fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { |
| 818 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | 818 | const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data; |
| 819 | const extra = self.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index).data; | ||
| 820 | try self.writeInstRef(stream, extra.frame_buffer); | 819 | try self.writeInstRef(stream, extra.frame_buffer); |
| 821 | try stream.writeAll(", "); | 820 | try stream.writeAll(", "); |
| 822 | try self.writeInstRef(stream, extra.result_ptr); | 821 | try self.writeInstRef(stream, extra.result_ptr); |
| ... | @@ -825,7 +824,7 @@ const Writer = struct { | ... | @@ -825,7 +824,7 @@ const Writer = struct { |
| 825 | try stream.writeAll(", "); | 824 | try stream.writeAll(", "); |
| 826 | try self.writeInstRef(stream, extra.args); | 825 | try self.writeInstRef(stream, extra.args); |
| 827 | try stream.writeAll(") "); | 826 | try stream.writeAll(") "); |
| 828 | try self.writeSrc(stream, inst_data.src()); | 827 | try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.node)); |
| 829 | } | 828 | } |
| 830 | 829 | ||
| 831 | fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { | 830 | fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
src/target.zig+7-107| ... | @@ -1,5 +1,4 @@ | ... | @@ -1,5 +1,4 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const llvm = @import("codegen/llvm/bindings.zig"); | ||
| 3 | const Type = @import("type.zig").Type; | 2 | const Type = @import("type.zig").Type; |
| 4 | 3 | ||
| 5 | pub const ArchOsAbi = struct { | 4 | pub const ArchOsAbi = struct { |
| ... | @@ -338,112 +337,6 @@ pub fn supportsReturnAddress(target: std.Target) bool { | ... | @@ -338,112 +337,6 @@ pub fn supportsReturnAddress(target: std.Target) bool { |
| 338 | }; | 337 | }; |
| 339 | } | 338 | } |
| 340 | 339 | ||
| 341 | pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { | ||
| 342 | return switch (os_tag) { | ||
| 343 | .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS, | ||
| 344 | .windows, .uefi => .Win32, | ||
| 345 | .ananas => .Ananas, | ||
| 346 | .cloudabi => .CloudABI, | ||
| 347 | .dragonfly => .DragonFly, | ||
| 348 | .freebsd => .FreeBSD, | ||
| 349 | .fuchsia => .Fuchsia, | ||
| 350 | .ios => .IOS, | ||
| 351 | .kfreebsd => .KFreeBSD, | ||
| 352 | .linux => .Linux, | ||
| 353 | .lv2 => .Lv2, | ||
| 354 | .macos => .MacOSX, | ||
| 355 | .netbsd => .NetBSD, | ||
| 356 | .openbsd => .OpenBSD, | ||
| 357 | .solaris => .Solaris, | ||
| 358 | .zos => .ZOS, | ||
| 359 | .haiku => .Haiku, | ||
| 360 | .minix => .Minix, | ||
| 361 | .rtems => .RTEMS, | ||
| 362 | .nacl => .NaCl, | ||
| 363 | .aix => .AIX, | ||
| 364 | .cuda => .CUDA, | ||
| 365 | .nvcl => .NVCL, | ||
| 366 | .amdhsa => .AMDHSA, | ||
| 367 | .ps4 => .PS4, | ||
| 368 | .ps5 => .PS5, | ||
| 369 | .elfiamcu => .ELFIAMCU, | ||
| 370 | .tvos => .TvOS, | ||
| 371 | .watchos => .WatchOS, | ||
| 372 | .mesa3d => .Mesa3D, | ||
| 373 | .contiki => .Contiki, | ||
| 374 | .amdpal => .AMDPAL, | ||
| 375 | .hermit => .HermitCore, | ||
| 376 | .hurd => .Hurd, | ||
| 377 | .wasi => .WASI, | ||
| 378 | .emscripten => .Emscripten, | ||
| 379 | .driverkit => .DriverKit, | ||
| 380 | .shadermodel => .ShaderModel, | ||
| 381 | }; | ||
| 382 | } | ||
| 383 | |||
| 384 | pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { | ||
| 385 | return switch (arch_tag) { | ||
| 386 | .arm => .arm, | ||
| 387 | .armeb => .armeb, | ||
| 388 | .aarch64 => .aarch64, | ||
| 389 | .aarch64_be => .aarch64_be, | ||
| 390 | .aarch64_32 => .aarch64_32, | ||
| 391 | .arc => .arc, | ||
| 392 | .avr => .avr, | ||
| 393 | .bpfel => .bpfel, | ||
| 394 | .bpfeb => .bpfeb, | ||
| 395 | .csky => .csky, | ||
| 396 | .dxil => .dxil, | ||
| 397 | .hexagon => .hexagon, | ||
| 398 | .loongarch32 => .loongarch32, | ||
| 399 | .loongarch64 => .loongarch64, | ||
| 400 | .m68k => .m68k, | ||
| 401 | .mips => .mips, | ||
| 402 | .mipsel => .mipsel, | ||
| 403 | .mips64 => .mips64, | ||
| 404 | .mips64el => .mips64el, | ||
| 405 | .msp430 => .msp430, | ||
| 406 | .powerpc => .ppc, | ||
| 407 | .powerpcle => .ppcle, | ||
| 408 | .powerpc64 => .ppc64, | ||
| 409 | .powerpc64le => .ppc64le, | ||
| 410 | .r600 => .r600, | ||
| 411 | .amdgcn => .amdgcn, | ||
| 412 | .riscv32 => .riscv32, | ||
| 413 | .riscv64 => .riscv64, | ||
| 414 | .sparc => .sparc, | ||
| 415 | .sparc64 => .sparcv9, // In LLVM, sparc64 == sparcv9. | ||
| 416 | .sparcel => .sparcel, | ||
| 417 | .s390x => .systemz, | ||
| 418 | .tce => .tce, | ||
| 419 | .tcele => .tcele, | ||
| 420 | .thumb => .thumb, | ||
| 421 | .thumbeb => .thumbeb, | ||
| 422 | .i386 => .x86, | ||
| 423 | .x86_64 => .x86_64, | ||
| 424 | .xcore => .xcore, | ||
| 425 | .nvptx => .nvptx, | ||
| 426 | .nvptx64 => .nvptx64, | ||
| 427 | .le32 => .le32, | ||
| 428 | .le64 => .le64, | ||
| 429 | .amdil => .amdil, | ||
| 430 | .amdil64 => .amdil64, | ||
| 431 | .hsail => .hsail, | ||
| 432 | .hsail64 => .hsail64, | ||
| 433 | .spir => .spir, | ||
| 434 | .spir64 => .spir64, | ||
| 435 | .kalimba => .kalimba, | ||
| 436 | .shave => .shave, | ||
| 437 | .lanai => .lanai, | ||
| 438 | .wasm32 => .wasm32, | ||
| 439 | .wasm64 => .wasm64, | ||
| 440 | .renderscript32 => .renderscript32, | ||
| 441 | .renderscript64 => .renderscript64, | ||
| 442 | .ve => .ve, | ||
| 443 | .spu_2, .spirv32, .spirv64 => .UnknownArch, | ||
| 444 | }; | ||
| 445 | } | ||
| 446 | |||
| 447 | fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { | 340 | fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { |
| 448 | if (ignore_case) { | 341 | if (ignore_case) { |
| 449 | return std.ascii.eqlIgnoreCase(a, b); | 342 | return std.ascii.eqlIgnoreCase(a, b); |
| ... | @@ -800,3 +693,10 @@ pub fn supportsFunctionAlignment(target: std.Target) bool { | ... | @@ -800,3 +693,10 @@ pub fn supportsFunctionAlignment(target: std.Target) bool { |
| 800 | else => true, | 693 | else => true, |
| 801 | }; | 694 | }; |
| 802 | } | 695 | } |
| 696 | |||
| 697 | pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool { | ||
| 698 | switch (backend) { | ||
| 699 | .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), | ||
| 700 | else => return false, | ||
| 701 | } | ||
| 702 | } |
src/type.zig+4| ... | @@ -6598,6 +6598,8 @@ pub const CType = enum { | ... | @@ -6598,6 +6598,8 @@ pub const CType = enum { |
| 6598 | .powerpcle, | 6598 | .powerpcle, |
| 6599 | .powerpc64, | 6599 | .powerpc64, |
| 6600 | .powerpc64le, | 6600 | .powerpc64le, |
| 6601 | .wasm32, | ||
| 6602 | .wasm64, | ||
| 6601 | => return 128, | 6603 | => return 128, |
| 6602 | 6604 | ||
| 6603 | else => return 64, | 6605 | else => return 64, |
| ... | @@ -6646,6 +6648,8 @@ pub const CType = enum { | ... | @@ -6646,6 +6648,8 @@ pub const CType = enum { |
| 6646 | .powerpcle, | 6648 | .powerpcle, |
| 6647 | .powerpc64, | 6649 | .powerpc64, |
| 6648 | .powerpc64le, | 6650 | .powerpc64le, |
| 6651 | .wasm32, | ||
| 6652 | .wasm64, | ||
| 6649 | => return 128, | 6653 | => return 128, |
| 6650 | 6654 | ||
| 6651 | else => return 64, | 6655 | else => return 64, |
test/behavior/call.zig+68| ... | @@ -261,3 +261,71 @@ test "arguments to comptime parameters generated in comptime blocks" { | ... | @@ -261,3 +261,71 @@ test "arguments to comptime parameters generated in comptime blocks" { |
| 261 | }; | 261 | }; |
| 262 | S.foo(S.fortyTwo()); | 262 | S.foo(S.fortyTwo()); |
| 263 | } | 263 | } |
| 264 | |||
| 265 | test "forced tail call" { | ||
| 266 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 267 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 268 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 269 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 270 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 271 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 272 | |||
| 273 | if (builtin.zig_backend == .stage2_llvm) { | ||
| 274 | // Only attempt this test on targets we know have tail call support in LLVM. | ||
| 275 | if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) { | ||
| 276 | return error.SkipZigTest; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | const S = struct { | ||
| 281 | fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 { | ||
| 282 | if (n == 0) return a; | ||
| 283 | if (n == 1) return b; | ||
| 284 | return @call( | ||
| 285 | .{ .modifier = .always_tail }, | ||
| 286 | fibonacciTailInternal, | ||
| 287 | .{ n - 1, b, a + b }, | ||
| 288 | ); | ||
| 289 | } | ||
| 290 | |||
| 291 | fn fibonacciTail(n: u16) u16 { | ||
| 292 | return fibonacciTailInternal(n, 0, 1); | ||
| 293 | } | ||
| 294 | }; | ||
| 295 | try expect(S.fibonacciTail(10) == 55); | ||
| 296 | } | ||
| 297 | |||
| 298 | test "inline call preserves tail call" { | ||
| 299 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 300 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 301 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 302 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 303 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 304 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 305 | |||
| 306 | if (builtin.zig_backend == .stage2_llvm) { | ||
| 307 | // Only attempt this test on targets we know have tail call support in LLVM. | ||
| 308 | if (builtin.cpu.arch != .x86_64 and builtin.cpu.arch != .aarch64) { | ||
| 309 | return error.SkipZigTest; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | const max = std.math.maxInt(u16); | ||
| 314 | const S = struct { | ||
| 315 | var a: u16 = 0; | ||
| 316 | fn foo() void { | ||
| 317 | return bar(); | ||
| 318 | } | ||
| 319 | |||
| 320 | inline fn bar() void { | ||
| 321 | if (a == max) return; | ||
| 322 | // Stack overflow if not tail called | ||
| 323 | var buf: [max]u16 = undefined; | ||
| 324 | buf[a] = a; | ||
| 325 | a += 1; | ||
| 326 | return @call(.{ .modifier = .always_tail }, foo, .{}); | ||
| 327 | } | ||
| 328 | }; | ||
| 329 | S.foo(); | ||
| 330 | try expect(S.a == std.math.maxInt(u16)); | ||
| 331 | } |
test/behavior/cast.zig+1| ... | @@ -1430,6 +1430,7 @@ test "coerce between pointers of compatible differently-named floats" { | ... | @@ -1430,6 +1430,7 @@ test "coerce between pointers of compatible differently-named floats" { |
| 1430 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1430 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1431 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1431 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1432 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1432 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1433 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1433 | 1434 | ||
| 1434 | if (builtin.os.tag == .windows) { | 1435 | if (builtin.os.tag == .windows) { |
| 1435 | // https://github.com/ziglang/zig/issues/12396 | 1436 | // https://github.com/ziglang/zig/issues/12396 |
test/behavior/union.zig+28| ... | @@ -1324,3 +1324,31 @@ test "union and enum field order doesn't match" { | ... | @@ -1324,3 +1324,31 @@ test "union and enum field order doesn't match" { |
| 1324 | x = .b; | 1324 | x = .b; |
| 1325 | try expect(x == .b); | 1325 | try expect(x == .b); |
| 1326 | } | 1326 | } |
| 1327 | |||
| 1328 | test "@unionInit uses tag value instead of field index" { | ||
| 1329 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1330 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 1331 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 1332 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 1333 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1334 | |||
| 1335 | const E = enum(u8) { | ||
| 1336 | b = 255, | ||
| 1337 | a = 3, | ||
| 1338 | }; | ||
| 1339 | const U = union(E) { | ||
| 1340 | a: usize, | ||
| 1341 | b: isize, | ||
| 1342 | }; | ||
| 1343 | var i: isize = -1; | ||
| 1344 | var u = @unionInit(U, "b", i); | ||
| 1345 | { | ||
| 1346 | var a = u.b; | ||
| 1347 | try expect(a == i); | ||
| 1348 | } | ||
| 1349 | { | ||
| 1350 | var a = &u.b; | ||
| 1351 | try expect(a.* == i); | ||
| 1352 | } | ||
| 1353 | try expect(@enumToInt(u) == 255); | ||
| 1354 | } |
test/c_abi/cfuncs.c+6| ... | @@ -33,6 +33,7 @@ void zig_five_integers(int32_t, int32_t, int32_t, int32_t, int32_t); | ... | @@ -33,6 +33,7 @@ void zig_five_integers(int32_t, int32_t, int32_t, int32_t, int32_t); |
| 33 | 33 | ||
| 34 | void zig_f32(float); | 34 | void zig_f32(float); |
| 35 | void zig_f64(double); | 35 | void zig_f64(double); |
| 36 | void zig_longdouble(long double); | ||
| 36 | void zig_five_floats(float, float, float, float, float); | 37 | void zig_five_floats(float, float, float, float, float); |
| 37 | 38 | ||
| 38 | bool zig_ret_bool(); | 39 | bool zig_ret_bool(); |
| ... | @@ -157,6 +158,7 @@ void run_c_tests(void) { | ... | @@ -157,6 +158,7 @@ void run_c_tests(void) { |
| 157 | 158 | ||
| 158 | zig_f32(12.34f); | 159 | zig_f32(12.34f); |
| 159 | zig_f64(56.78); | 160 | zig_f64(56.78); |
| 161 | zig_longdouble(12.34l); | ||
| 160 | zig_five_floats(1.0f, 2.0f, 3.0f, 4.0f, 5.0f); | 162 | zig_five_floats(1.0f, 2.0f, 3.0f, 4.0f, 5.0f); |
| 161 | 163 | ||
| 162 | zig_ptr((void*)0xdeadbeefL); | 164 | zig_ptr((void*)0xdeadbeefL); |
| ... | @@ -271,6 +273,10 @@ void c_f64(double x) { | ... | @@ -271,6 +273,10 @@ void c_f64(double x) { |
| 271 | assert_or_panic(x == 56.78); | 273 | assert_or_panic(x == 56.78); |
| 272 | } | 274 | } |
| 273 | 275 | ||
| 276 | void c_long_double(long double x) { | ||
| 277 | assert_or_panic(x == 12.34l); | ||
| 278 | } | ||
| 279 | |||
| 274 | void c_ptr(void *x) { | 280 | void c_ptr(void *x) { |
| 275 | assert_or_panic(x == (void*)0xdeadbeefL); | 281 | assert_or_panic(x == (void*)0xdeadbeefL); |
| 276 | } | 282 | } |
test/c_abi/main.zig+11| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const print = std.debug.print; | 3 | const print = std.debug.print; |
| 3 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
| 4 | 5 | ||
| ... | @@ -89,6 +90,7 @@ export fn zig_struct_u128(a: U128) void { | ... | @@ -89,6 +90,7 @@ export fn zig_struct_u128(a: U128) void { |
| 89 | 90 | ||
| 90 | extern fn c_f32(f32) void; | 91 | extern fn c_f32(f32) void; |
| 91 | extern fn c_f64(f64) void; | 92 | extern fn c_f64(f64) void; |
| 93 | extern fn c_long_double(c_longdouble) void; | ||
| 92 | 94 | ||
| 93 | // On windows x64, the first 4 are passed via registers, others on the stack. | 95 | // On windows x64, the first 4 are passed via registers, others on the stack. |
| 94 | extern fn c_five_floats(f32, f32, f32, f32, f32) void; | 96 | extern fn c_five_floats(f32, f32, f32, f32, f32) void; |
| ... | @@ -107,12 +109,21 @@ test "C ABI floats" { | ... | @@ -107,12 +109,21 @@ test "C ABI floats" { |
| 107 | c_five_floats(1.0, 2.0, 3.0, 4.0, 5.0); | 109 | c_five_floats(1.0, 2.0, 3.0, 4.0, 5.0); |
| 108 | } | 110 | } |
| 109 | 111 | ||
| 112 | test "C ABI long double" { | ||
| 113 | if (!builtin.cpu.arch.isWasm()) return error.SkipZigTest; | ||
| 114 | c_long_double(12.34); | ||
| 115 | } | ||
| 116 | |||
| 110 | export fn zig_f32(x: f32) void { | 117 | export fn zig_f32(x: f32) void { |
| 111 | expect(x == 12.34) catch @panic("test failure: zig_f32"); | 118 | expect(x == 12.34) catch @panic("test failure: zig_f32"); |
| 112 | } | 119 | } |
| 113 | export fn zig_f64(x: f64) void { | 120 | export fn zig_f64(x: f64) void { |
| 114 | expect(x == 56.78) catch @panic("test failure: zig_f64"); | 121 | expect(x == 56.78) catch @panic("test failure: zig_f64"); |
| 115 | } | 122 | } |
| 123 | export fn zig_longdouble(x: c_longdouble) void { | ||
| 124 | if (!builtin.cpu.arch.isWasm()) return; // waiting for #1481 | ||
| 125 | expect(x == 12.34) catch @panic("test failure: zig_longdouble"); | ||
| 126 | } | ||
| 116 | 127 | ||
| 117 | extern fn c_ptr(*anyopaque) void; | 128 | extern fn c_ptr(*anyopaque) void; |
| 118 | 129 |
test/cases/aarch64-macos/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=aarch64-macos | 3 | // target=aarch64-macos |
| 4 | // | 4 | // |
| 5 | // :107:9: error: struct 'tmp.tmp' has no member named 'main' | 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' |
| 6 | // :7:1: note: struct declared here | 6 | // :7:1: note: struct declared here |
test/cases/compile_errors/invalid_tail_call.zig created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | fn myFn(_: usize) void { | ||
| 2 | return; | ||
| 3 | } | ||
| 4 | pub export fn entry() void { | ||
| 5 | @call(.{ .modifier = .always_tail }, myFn, .{0}); | ||
| 6 | } | ||
| 7 | |||
| 8 | // error | ||
| 9 | // backend=llvm | ||
| 10 | // target=native | ||
| 11 | // | ||
| 12 | // :5:5: error: unable to perform tail call: type of function being called 'fn(usize) void' does not match type of calling function 'fn() callconv(.C) void' | ||
test/cases/compile_errors/non-comptime-parameter-used-as-array-size.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | export fn entry() void { | ||
| 2 | const llamas1 = makeLlamas(5); | ||
| 3 | const llamas2 = makeLlamas(5); | ||
| 4 | _ = llamas1; | ||
| 5 | _ = llamas2; | ||
| 6 | } | ||
| 7 | |||
| 8 | fn makeLlamas(count: usize) [count]u8 { | ||
| 9 | _ = count; | ||
| 10 | } | ||
| 11 | |||
| 12 | // error | ||
| 13 | // target=native | ||
| 14 | // | ||
| 15 | // :8:30: error: unable to resolve comptime value | ||
| 16 | // :8:30: note: array length must be comptime known | ||
test/cases/compile_errors/runtime_to_comptime_num.zig created+31| ... | @@ -0,0 +1,31 @@ | ||
| 1 | pub export fn entry() void { | ||
| 2 | var a: u32 = 0; | ||
| 3 | _ = @as(comptime_int, a); | ||
| 4 | } | ||
| 5 | pub export fn entry2() void{ | ||
| 6 | var a: u32 = 0; | ||
| 7 | _ = @as(comptime_float, a); | ||
| 8 | } | ||
| 9 | pub export fn entry3() void{ | ||
| 10 | comptime var aa: comptime_float = 0.0; | ||
| 11 | var a: f32 = 4; | ||
| 12 | aa = a; | ||
| 13 | } | ||
| 14 | pub export fn entry4() void{ | ||
| 15 | comptime var aa: comptime_int = 0.0; | ||
| 16 | var a: f32 = 4; | ||
| 17 | aa = a; | ||
| 18 | } | ||
| 19 | |||
| 20 | // error | ||
| 21 | // backend=stage2 | ||
| 22 | // target=native | ||
| 23 | // | ||
| 24 | // :3:27: error: unable to resolve comptime value | ||
| 25 | // :3:27: note: value being casted to 'comptime_int' must be comptime known | ||
| 26 | // :7:29: error: unable to resolve comptime value | ||
| 27 | // :7:29: note: value being casted to 'comptime_float' must be comptime known | ||
| 28 | // :12:10: error: unable to resolve comptime value | ||
| 29 | // :12:10: note: value being casted to 'comptime_float' must be comptime known | ||
| 30 | // :17:10: error: unable to resolve comptime value | ||
| 31 | // :17:10: note: value being casted to 'comptime_int' must be comptime known | ||
test/cases/compile_errors/shifting_without_int_type_or_comptime_known.zig created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | export fn entry(x: u8) u8 { | ||
| 2 | return 0x11 << x; | ||
| 3 | } | ||
| 4 | export fn entry1(x: u8) u8 { | ||
| 5 | return 0x11 >> x; | ||
| 6 | } | ||
| 7 | export fn entry2() void { | ||
| 8 | var x: u5 = 1; | ||
| 9 | _ = @shlExact(12345, x); | ||
| 10 | } | ||
| 11 | export fn entry3() void { | ||
| 12 | var x: u5 = 1; | ||
| 13 | _ = @shrExact(12345, x); | ||
| 14 | } | ||
| 15 | |||
| 16 | // error | ||
| 17 | // backend=stage2 | ||
| 18 | // target=native | ||
| 19 | // | ||
| 20 | // :2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known | ||
| 21 | // :5:17: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known | ||
| 22 | // :9:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known | ||
| 23 | // :13:9: error: LHS of shift must be a fixed-width integer type, or RHS must be a comptime known | ||
test/cases/compile_errors/stage1/obj/shifting_without_int_type_or_comptime_known.zig deleted-9| ... | @@ -1,9 +0,0 @@ | ||
| 1 | export fn entry(x: u8) u8 { | ||
| 2 | return 0x11 << x; | ||
| 3 | } | ||
| 4 | |||
| 5 | // error | ||
| 6 | // backend=stage1 | ||
| 7 | // target=native | ||
| 8 | // | ||
| 9 | // tmp.zig:2:17: error: LHS of shift must be a fixed-width integer type, or RHS must be compile-time known | ||
test/cases/taill_call_noreturn.zig created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | pub fn foo(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn { | ||
| 4 | @call(.{ .modifier = .always_tail }, bar, .{ message, stack_trace }); | ||
| 5 | } | ||
| 6 | pub fn bar(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn { | ||
| 7 | _ = message; | ||
| 8 | _ = stack_trace; | ||
| 9 | std.process.exit(0); | ||
| 10 | } | ||
| 11 | |||
| 12 | pub fn main() void { | ||
| 13 | foo("foo", null); | ||
| 14 | } | ||
| 15 | |||
| 16 | // run | ||
| 17 | // backend=llvm | ||
| 18 | // target=x86_64-linux,x86_64-macos,aarch64-linux,aarch64-macos | ||
test/cases/x86_64-linux/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=x86_64-linux | 3 | // target=x86_64-linux |
| 4 | // | 4 | // |
| 5 | // :107:9: error: struct 'tmp.tmp' has no member named 'main' | 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' |
| 6 | // :7:1: note: struct declared here | 6 | // :7:1: note: struct declared here |
test/cases/x86_64-macos/hello_world_with_updates.0.zig+1-1| ... | @@ -2,5 +2,5 @@ | ... | @@ -2,5 +2,5 @@ |
| 2 | // output_mode=Exe | 2 | // output_mode=Exe |
| 3 | // target=x86_64-macos | 3 | // target=x86_64-macos |
| 4 | // | 4 | // |
| 5 | // :107:9: error: struct 'tmp.tmp' has no member named 'main' | 5 | // :105:9: error: struct 'tmp.tmp' has no member named 'main' |
| 6 | // :7:1: note: struct declared here | 6 | // :7:1: note: struct declared here |