| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); |
| 3 | const assert = std.debug.assert; |
| 4 | |
| 5 | const Type = @import("Type.zig"); |
| 6 | const AddressSpace = std.lang.AddressSpace; |
| 7 | const Alignment = @import("InternPool.zig").Alignment; |
| 8 | const Compilation = @import("Compilation.zig"); |
| 9 | const Feature = @import("Zcu.zig").Feature; |
| 10 | |
| 11 | pub const default_stack_protector_buffer_size = 4; |
| 12 | |
| 13 | pub fn canDynamicLink(target: *const std.Target) bool { |
| 14 | return switch (target.cpu.arch) { |
| 15 | .bpfeb, |
| 16 | .bpfel, |
| 17 | .nvptx, |
| 18 | .nvptx64, |
| 19 | .spirv32, |
| 20 | .spirv64, |
| 21 | => false, |
| 22 | .wasm32, |
| 23 | .wasm64, |
| 24 | => true, |
| 25 | else => switch (target.os.tag) { |
| 26 | // This list is likely incomplete. |
| 27 | .freestanding, .uefi => false, |
| 28 | else => true, |
| 29 | }, |
| 30 | }; |
| 31 | } |
| 32 | |
| 33 | pub fn canStaticLinkExe(target: *const std.Target) bool { |
| 34 | return switch (target.os.tag) { |
| 35 | .fuchsia, |
| 36 | .haiku, |
| 37 | => false, |
| 38 | else => true, |
| 39 | }; |
| 40 | } |
| 41 | |
| 42 | pub fn libCNeedsLibUnwind(target: *const std.Target, link_mode: std.lang.LinkMode) bool { |
| 43 | return target.isGnuLibC() and link_mode == .static; |
| 44 | } |
| 45 | |
| 46 | pub fn libCxxNeedsLibUnwind(target: *const std.Target) bool { |
| 47 | return switch (target.os.tag) { |
| 48 | .maccatalyst, |
| 49 | .macos, |
| 50 | .ios, |
| 51 | .watchos, |
| 52 | .tvos, |
| 53 | .visionos, |
| 54 | .freestanding, |
| 55 | .wasi, // Wasm/WASI currently doesn't offer support for libunwind, so don't link it. |
| 56 | => false, |
| 57 | |
| 58 | .windows => target.abi.isGnu(), |
| 59 | else => true, |
| 60 | }; |
| 61 | } |
| 62 | |
| 63 | pub fn requiresPie(target: *const std.Target, link_mode: std.lang.LinkMode) bool { |
| 64 | return switch (target.os.tag) { |
| 65 | .ashetos, |
| 66 | .fuchsia, |
| 67 | .@"switch", |
| 68 | => true, |
| 69 | else => target.abi.isAndroid() and link_mode == .dynamic, |
| 70 | }; |
| 71 | } |
| 72 | |
| 73 | /// This function returns whether non-pic code is completely invalid on the given target. |
| 74 | pub fn requiresPic(target: *const std.Target, linking_libc: bool) bool { |
| 75 | return ((target.os.tag == .windows or target.os.tag == .uefi) and (target.cpu.arch == .aarch64 or target.cpu.arch == .x86_64)) or |
| 76 | target.requiresLibC() or |
| 77 | (linking_libc and target.isGnuLibC()); |
| 78 | } |
| 79 | |
| 80 | pub fn requiresPicForDynamicLink(target: *const std.Target) bool { |
| 81 | assert(canDynamicLink(target)); |
| 82 | |
| 83 | return switch (target.os.tag) { |
| 84 | .windows => target.cpu.arch == .aarch64 or target.cpu.arch == .x86_64, |
| 85 | else => !target.cpu.arch.isWasm(), |
| 86 | }; |
| 87 | } |
| 88 | |
| 89 | pub fn picLevel(target: *const std.Target) u32 { |
| 90 | // MIPS always uses PIC level 1; other platforms vary in their default PIC levels, but they |
| 91 | // support both level 1 and 2, in which case we prefer 2. |
| 92 | return if (target.cpu.arch.isMIPS()) 1 else 2; |
| 93 | } |
| 94 | |
| 95 | /// This is not whether the target supports Position Independent Code, but whether the -fPIC |
| 96 | /// C compiler argument is valid to Clang. |
| 97 | pub fn supports_fpic(target: *const std.Target) bool { |
| 98 | return switch (target.os.tag) { |
| 99 | .windows, .uefi => false, // Technically allowed for `Abi.gnu`, but completely ignored by Clang (by design) anyway. |
| 100 | else => true, |
| 101 | }; |
| 102 | } |
| 103 | |
| 104 | pub fn defaultPie(target: *const std.Target) bool { |
| 105 | return switch (target.os.tag) { |
| 106 | .openbsd, .serenity => true, |
| 107 | else => target.os.tag.isDarwin(), |
| 108 | }; |
| 109 | } |
| 110 | |
| 111 | pub fn alwaysSingleThreaded(target: *const std.Target) bool { |
| 112 | _ = target; |
| 113 | return false; |
| 114 | } |
| 115 | |
| 116 | pub fn defaultSingleThreaded(target: *const std.Target) bool { |
| 117 | switch (target.cpu.arch) { |
| 118 | .wasm32, .wasm64 => return true, |
| 119 | else => {}, |
| 120 | } |
| 121 | return false; |
| 122 | } |
| 123 | |
| 124 | pub fn useEmulatedTls(target: *const std.Target) bool { |
| 125 | if (target.abi.isAndroid()) { |
| 126 | if (target.os.version_range.linux.android < 29) return true; |
| 127 | return false; |
| 128 | } |
| 129 | if (target.abi.isOpenHarmony()) return true; |
| 130 | return switch (target.os.tag) { |
| 131 | .openbsd => true, |
| 132 | else => false, |
| 133 | }; |
| 134 | } |
| 135 | |
| 136 | pub fn hasValgrindSupport(target: *const std.Target, backend: std.lang.CompilerBackend) bool { |
| 137 | // We can't currently output the necessary Valgrind client request assembly when using the C |
| 138 | // backend and compiling with an MSVC-like compiler. |
| 139 | const ofmt_c_msvc = (target.abi == .msvc or target.abi == .itanium) and target.ofmt == .c; |
| 140 | |
| 141 | return switch (target.cpu.arch) { |
| 142 | .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { |
| 143 | .linux => true, |
| 144 | else => false, |
| 145 | }, |
| 146 | .aarch64, .aarch64_be => switch (target.os.tag) { |
| 147 | .linux, .freebsd => true, |
| 148 | else => false, |
| 149 | }, |
| 150 | .mips, .mipsel, .mips64, .mips64el => switch (target.os.tag) { |
| 151 | .linux => true, |
| 152 | else => false, |
| 153 | }, |
| 154 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => switch (target.os.tag) { |
| 155 | .linux => backend != .stage2_powerpc, // Insufficient inline assembly support in self-hosted. |
| 156 | else => false, |
| 157 | }, |
| 158 | .riscv64 => switch (target.os.tag) { |
| 159 | .linux => backend != .stage2_riscv64, // Insufficient inline assembly support in self-hosted. |
| 160 | else => false, |
| 161 | }, |
| 162 | .s390x => switch (target.os.tag) { |
| 163 | .linux => true, |
| 164 | else => false, |
| 165 | }, |
| 166 | .x86 => switch (target.os.tag) { |
| 167 | .linux, .freebsd, .illumos => true, |
| 168 | .windows => !ofmt_c_msvc, |
| 169 | else => false, |
| 170 | }, |
| 171 | .x86_64 => switch (target.os.tag) { |
| 172 | .linux => switch (target.abi) { |
| 173 | .gnux32, .muslx32, .x32 => false, |
| 174 | else => true, |
| 175 | }, |
| 176 | .freebsd, .illumos => true, |
| 177 | .windows => !ofmt_c_msvc, |
| 178 | else => false, |
| 179 | }, |
| 180 | else => false, |
| 181 | }; |
| 182 | } |
| 183 | |
| 184 | /// The set of targets that LLVM has non-experimental support for. |
| 185 | /// Used to select between LLVM backend and self-hosted backend when compiling in |
| 186 | /// release modes. |
| 187 | pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat) bool { |
| 188 | switch (ofmt) { |
| 189 | // LLVM does not support these object formats: |
| 190 | .c, |
| 191 | .plan9, |
| 192 | => return false, |
| 193 | |
| 194 | .coff, |
| 195 | .elf, |
| 196 | .hex, |
| 197 | .macho, |
| 198 | .spirv, |
| 199 | .raw, |
| 200 | .wasm, |
| 201 | => {}, |
| 202 | } |
| 203 | |
| 204 | return switch (target.cpu.arch) { |
| 205 | .arm, |
| 206 | .armeb, |
| 207 | .aarch64, |
| 208 | .aarch64_be, |
| 209 | .arc, |
| 210 | .avr, |
| 211 | .bpfel, |
| 212 | .bpfeb, |
| 213 | .hexagon, |
| 214 | .loongarch32, |
| 215 | .loongarch64, |
| 216 | .m68k, |
| 217 | .mips, |
| 218 | .mipsel, |
| 219 | .mips64, |
| 220 | .mips64el, |
| 221 | .msp430, |
| 222 | .powerpc, |
| 223 | .powerpcle, |
| 224 | .powerpc64, |
| 225 | .powerpc64le, |
| 226 | .amdgcn, |
| 227 | .riscv32, |
| 228 | .riscv32be, |
| 229 | .riscv64, |
| 230 | .riscv64be, |
| 231 | .sparc, |
| 232 | .sparc64, |
| 233 | .spirv32, |
| 234 | .spirv64, |
| 235 | .s390x, |
| 236 | .thumb, |
| 237 | .thumbeb, |
| 238 | .x86, |
| 239 | .x86_64, |
| 240 | .xcore, |
| 241 | .nvptx, |
| 242 | .nvptx64, |
| 243 | .lanai, |
| 244 | .wasm32, |
| 245 | .wasm64, |
| 246 | .ve, |
| 247 | .xtensa, |
| 248 | => true, |
| 249 | |
| 250 | // LLVM backend exists but can produce neither assembly nor object files. |
| 251 | .csky, |
| 252 | => false, |
| 253 | |
| 254 | // Third-party LLVM backend exists. |
| 255 | .ez80, |
| 256 | => false, |
| 257 | |
| 258 | // No LLVM backend exists. |
| 259 | .alpha, |
| 260 | .arceb, |
| 261 | .hppa, |
| 262 | .hppa64, |
| 263 | .kalimba, |
| 264 | .kvx, |
| 265 | .m88k, |
| 266 | .microblaze, |
| 267 | .microblazeel, |
| 268 | .or1k, |
| 269 | .propeller, |
| 270 | .sh, |
| 271 | .sheb, |
| 272 | .x86_16, |
| 273 | .xtensaeb, |
| 274 | .spork8, |
| 275 | => false, |
| 276 | }; |
| 277 | } |
| 278 | |
| 279 | /// The set of targets that Zig supports using LLD to link for. |
| 280 | pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { |
| 281 | return switch (ofmt) { |
| 282 | .elf, .coff, .wasm => true, |
| 283 | else => false, |
| 284 | }; |
| 285 | } |
| 286 | |
| 287 | /// Returns `true` if `ofmt` has two linker implementations, so `-fnew-linker` is meaningful. |
| 288 | pub fn hasNewLinker(ofmt: std.Target.ObjectFormat) bool { |
| 289 | return switch (ofmt) { |
| 290 | .elf => true, |
| 291 | else => false, |
| 292 | }; |
| 293 | } |
| 294 | |
| 295 | /// The set of targets that our own self-hosted backends have robust support for. |
| 296 | /// Used to select between LLVM backend and self-hosted backend when compiling in |
| 297 | /// debug mode. A given target should only return true here if it is passing greater |
| 298 | /// than or equal to the number of behavior tests as the respective LLVM backend. |
| 299 | pub fn selfHostedBackendIsAsRobustAsLlvm(target: *const std.Target) bool { |
| 300 | if (comptime builtin.cpu.arch.endian() == .big) return false; // https://github.com/ziglang/zig/issues/25961 |
| 301 | if (target.cpu.arch.isSpirV()) return true; |
| 302 | if (target.cpu.arch == .x86_64 and target.ptrBitWidth() == 64) { |
| 303 | if (target.os.tag == .illumos) { |
| 304 | // https://github.com/ziglang/zig/issues/25699 |
| 305 | return false; |
| 306 | } |
| 307 | // Self-hosted linker needs work: https://github.com/ziglang/zig/issues/24341 |
| 308 | switch (target.os.tag) { |
| 309 | .dragonfly, |
| 310 | .freebsd, |
| 311 | .netbsd, |
| 312 | .openbsd, |
| 313 | => return false, |
| 314 | else => {}, |
| 315 | } |
| 316 | return switch (target.ofmt) { |
| 317 | .elf => true, |
| 318 | .macho => false, // https://codeberg.org/ziglang/zig/issues/35267 |
| 319 | else => false, |
| 320 | }; |
| 321 | } |
| 322 | return false; |
| 323 | } |
| 324 | |
| 325 | pub fn supportsStackProbing(target: *const std.Target, backend: std.lang.CompilerBackend) bool { |
| 326 | return switch (backend) { |
| 327 | .stage2_aarch64, .stage2_x86_64 => true, |
| 328 | .stage2_llvm => target.os.tag != .windows and target.os.tag != .uefi and |
| 329 | (target.cpu.arch == .x86 or target.cpu.arch == .x86_64), |
| 330 | else => false, |
| 331 | }; |
| 332 | } |
| 333 | |
| 334 | pub fn supportsStackProtector(target: *const std.Target, backend: std.lang.CompilerBackend) bool { |
| 335 | switch (target.os.tag) { |
| 336 | .plan9 => return false, |
| 337 | else => {}, |
| 338 | } |
| 339 | switch (target.cpu.arch) { |
| 340 | .spirv32, .spirv64 => return false, |
| 341 | else => {}, |
| 342 | } |
| 343 | return switch (backend) { |
| 344 | .stage2_llvm => true, |
| 345 | else => false, |
| 346 | }; |
| 347 | } |
| 348 | |
| 349 | pub fn clangSupportsStackProtector(target: *const std.Target) bool { |
| 350 | return switch (target.cpu.arch) { |
| 351 | .spirv32, .spirv64 => return false, |
| 352 | else => true, |
| 353 | }; |
| 354 | } |
| 355 | |
| 356 | pub fn libcProvidesStackProtector(target: *const std.Target) bool { |
| 357 | return !target.isMinGW() and target.os.tag != .wasi and !target.cpu.arch.isSpirV(); |
| 358 | } |
| 359 | |
| 360 | /// Returns true if `@returnAddress()` is supported by the target and has a |
| 361 | /// reasonably performant implementation for the requested optimization mode. |
| 362 | pub fn supportsReturnAddress(target: *const std.Target, optimize: std.lang.Optimize) bool { |
| 363 | return switch (target.cpu.arch) { |
| 364 | // Emscripten currently implements `emscripten_return_address()` by calling |
| 365 | // out into JavaScript and parsing a stack trace, which introduces significant |
| 366 | // overhead that we would prefer to avoid in release builds. |
| 367 | .wasm32, .wasm64 => target.os.tag == .emscripten and optimize == .debug, |
| 368 | .bpfel, .bpfeb => false, |
| 369 | .spirv32, .spirv64 => false, |
| 370 | else => true, |
| 371 | }; |
| 372 | } |
| 373 | |
| 374 | pub const CompilerRtClassification = enum { none, only_compiler_rt, only_libunwind, both }; |
| 375 | |
| 376 | pub fn classifyCompilerRtLibName(name: []const u8) CompilerRtClassification { |
| 377 | if (std.mem.eql(u8, name, "gcc_s")) { |
| 378 | // libgcc_s includes exception handling functions, so if linking this library |
| 379 | // is requested, zig needs to instead link libunwind. Otherwise we end up with |
| 380 | // the linker unable to find `_Unwind_RaiseException` and other related symbols. |
| 381 | return .both; |
| 382 | } |
| 383 | if (std.mem.eql(u8, name, "compiler_rt") or |
| 384 | std.mem.eql(u8, name, "gcc") or |
| 385 | std.mem.eql(u8, name, "atomic") or |
| 386 | std.mem.eql(u8, name, "ssp")) |
| 387 | { |
| 388 | return .only_compiler_rt; |
| 389 | } |
| 390 | if (std.mem.eql(u8, name, "unwind") or |
| 391 | std.mem.eql(u8, name, "gcc_eh")) |
| 392 | { |
| 393 | return .only_libunwind; |
| 394 | } |
| 395 | return .none; |
| 396 | } |
| 397 | |
| 398 | pub fn hasDebugInfo(target: *const std.Target) bool { |
| 399 | return switch (target.ofmt) { |
| 400 | .raw, .hex => false, |
| 401 | else => switch (target.cpu.arch) { |
| 402 | // TODO: We should make newer PTX versions depend on older ones so we'd just check `ptx75`. |
| 403 | .nvptx, .nvptx64 => target.cpu.hasAny(.nvptx, &.{ |
| 404 | .ptx75, |
| 405 | .ptx76, |
| 406 | .ptx77, |
| 407 | .ptx78, |
| 408 | .ptx80, |
| 409 | .ptx81, |
| 410 | .ptx82, |
| 411 | .ptx83, |
| 412 | .ptx84, |
| 413 | .ptx85, |
| 414 | .ptx86, |
| 415 | .ptx87, |
| 416 | .ptx88, |
| 417 | .ptx90, |
| 418 | }), |
| 419 | .bpfel, .bpfeb => false, |
| 420 | else => true, |
| 421 | }, |
| 422 | }; |
| 423 | } |
| 424 | |
| 425 | pub fn defaultCompilerRtOptimizeMode(target: *const std.Target) std.lang.Optimize { |
| 426 | if (target.cpu.arch.isWasm() and target.os.tag == .freestanding) { |
| 427 | return .small; |
| 428 | } else { |
| 429 | return .fast; |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | pub fn canBuildLibCompilerRt(target: *const std.Target) enum { no, yes, llvm_only } { |
| 434 | switch (target.os.tag) { |
| 435 | .plan9 => return .no, |
| 436 | else => {}, |
| 437 | } |
| 438 | switch (target.cpu.arch) { |
| 439 | .spirv32, .spirv64 => return .no, |
| 440 | .spork8 => return .no, |
| 441 | // Remove this once https://github.com/ziglang/zig/issues/23714 is fixed |
| 442 | .amdgcn => return .no, |
| 443 | else => {}, |
| 444 | } |
| 445 | return switch (zigBackend(target, false)) { |
| 446 | .stage2_aarch64, .stage2_wasm, .stage2_x86_64 => .yes, |
| 447 | else => .llvm_only, |
| 448 | }; |
| 449 | } |
| 450 | |
| 451 | pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only, llvm_lld_only } { |
| 452 | switch (target.cpu.arch) { |
| 453 | .spork8 => return .no, |
| 454 | .spirv32, .spirv64 => return .no, |
| 455 | // Remove this once https://github.com/ziglang/zig/issues/23715 is fixed |
| 456 | .nvptx, .nvptx64 => return .no, |
| 457 | else => {}, |
| 458 | } |
| 459 | return switch (zigBackend(target, false)) { |
| 460 | .stage2_wasm => .yes, |
| 461 | .stage2_x86_64 => .yes, |
| 462 | else => .llvm_only, |
| 463 | }; |
| 464 | } |
| 465 | |
| 466 | /// Whether libzigc can fill-in the gaps of an existing libc |
| 467 | /// or *is* the libc of the target. |
| 468 | pub fn wantsZigC(target: *const std.Target, link_mode: std.lang.LinkMode) bool { |
| 469 | return (target.isMuslLibC() and link_mode == .static) or target.isWasiLibC() or target.isMinGW(); |
| 470 | } |
| 471 | |
| 472 | pub fn hasRedZone(target: *const std.Target) bool { |
| 473 | return switch (target.cpu.arch) { |
| 474 | .aarch64, |
| 475 | .aarch64_be, |
| 476 | .powerpc, |
| 477 | .powerpcle, |
| 478 | .powerpc64, |
| 479 | .powerpc64le, |
| 480 | .x86_64, |
| 481 | .x86, |
| 482 | => true, |
| 483 | |
| 484 | else => false, |
| 485 | }; |
| 486 | } |
| 487 | |
| 488 | pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 { |
| 489 | // The linking order of these is significant and should match the order other |
| 490 | // c compilers such as gcc or clang use. |
| 491 | const result: []const []const u8 = switch (target.os.tag) { |
| 492 | .dragonfly, .freebsd, .netbsd, .openbsd => &.{ "-lm", "-lpthread", "-lc", "-lutil" }, |
| 493 | .illumos => &.{ "-lm", "-lsocket", "-lnsl", "-lc" }, |
| 494 | .haiku => &.{ "-lm", "-lroot", "-lpthread", "-lc", "-lnetwork" }, |
| 495 | .linux => switch (target.abi) { |
| 496 | .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" }, |
| 497 | else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" }, |
| 498 | }, |
| 499 | // On SerenityOS libc includes libm, libpthread, libdl, and libssp. |
| 500 | .serenity => &.{"-lc"}, |
| 501 | else => &.{}, |
| 502 | }; |
| 503 | return result; |
| 504 | } |
| 505 | |
| 506 | pub fn clangMightShellOutForAssembly(target: *const std.Target) bool { |
| 507 | // Clang defaults to using the system assembler in some cases. |
| 508 | return target.cpu.arch.isNvptx() or target.cpu.arch == .xcore; |
| 509 | } |
| 510 | |
| 511 | /// Each backend architecture in Clang has a different codepath which may or may not |
| 512 | /// support an -mcpu flag. |
| 513 | pub fn clangAssemblerSupportsMcpuArg(target: *const std.Target) bool { |
| 514 | return switch (target.cpu.arch) { |
| 515 | .arm, .armeb, .thumb, .thumbeb => true, |
| 516 | else => false, |
| 517 | }; |
| 518 | } |
| 519 | |
| 520 | /// Some experimental or poorly-maintained LLVM targets do not properly process CPU models in their |
| 521 | /// Clang driver code. For these, we should omit the `-Xclang -target-cpu -Xclang <model>` flags. |
| 522 | pub fn clangSupportsTargetCpuArg(target: *const std.Target) bool { |
| 523 | return switch (target.cpu.arch) { |
| 524 | .arc, |
| 525 | .msp430, |
| 526 | .ve, |
| 527 | .xcore, |
| 528 | .xtensa, |
| 529 | => false, |
| 530 | else => true, |
| 531 | }; |
| 532 | } |
| 533 | |
| 534 | pub fn clangSupportsFloatAbiArg(target: *const std.Target) bool { |
| 535 | return switch (target.cpu.arch) { |
| 536 | .arm, |
| 537 | .armeb, |
| 538 | .thumb, |
| 539 | .thumbeb, |
| 540 | .csky, |
| 541 | .mips, |
| 542 | .mipsel, |
| 543 | .mips64, |
| 544 | .mips64el, |
| 545 | .powerpc, |
| 546 | .powerpcle, |
| 547 | .powerpc64, |
| 548 | .powerpc64le, |
| 549 | .s390x, |
| 550 | .sparc, |
| 551 | .sparc64, |
| 552 | => true, |
| 553 | // We use the target triple for LoongArch. |
| 554 | .loongarch32, .loongarch64 => false, |
| 555 | else => false, |
| 556 | }; |
| 557 | } |
| 558 | |
| 559 | pub fn clangSupportsNoImplicitFloatArg(target: *const std.Target) bool { |
| 560 | return switch (target.cpu.arch) { |
| 561 | .aarch64, |
| 562 | .aarch64_be, |
| 563 | .arm, |
| 564 | .armeb, |
| 565 | .thumb, |
| 566 | .thumbeb, |
| 567 | .riscv32, |
| 568 | .riscv32be, |
| 569 | .riscv64, |
| 570 | .riscv64be, |
| 571 | .x86, |
| 572 | .x86_64, |
| 573 | => true, |
| 574 | else => false, |
| 575 | }; |
| 576 | } |
| 577 | |
| 578 | pub fn defaultUnwindTables(target: *const std.Target, libunwind: bool, libtsan: bool) std.lang.UnwindTables { |
| 579 | if (target.os.tag == .windows) { |
| 580 | // The old 32-bit x86 variant of SEH doesn't use tables. |
| 581 | return if (target.cpu.arch != .x86) .async else .none; |
| 582 | } |
| 583 | if (target.os.tag.isDarwin()) return .async; |
| 584 | if (libunwind) return .async; |
| 585 | if (libtsan) return .async; |
| 586 | if (std.debug.Dwarf.supportsUnwinding(target)) return .async; |
| 587 | return .none; |
| 588 | } |
| 589 | |
| 590 | pub fn defaultAddressSpace( |
| 591 | target: *const std.Target, |
| 592 | context: enum { |
| 593 | /// Query the default address space for global constant values. |
| 594 | global_constant, |
| 595 | /// Query the default address space for global mutable values. |
| 596 | global_mutable, |
| 597 | /// Query the default address space for function-local values. |
| 598 | local, |
| 599 | /// Query the default address space for functions themselves. |
| 600 | function, |
| 601 | }, |
| 602 | ) AddressSpace { |
| 603 | // The default address space for functions on AVR is .flash to produce |
| 604 | // correct fixups into progmem. |
| 605 | if (context == .function and target.cpu.arch == .avr) return .flash; |
| 606 | return .generic; |
| 607 | } |
| 608 | |
| 609 | /// Returns true if pointers in `from` can be converted to a pointer in `to`. |
| 610 | pub fn addrSpaceCastIsValid( |
| 611 | target: *const std.Target, |
| 612 | from: AddressSpace, |
| 613 | to: AddressSpace, |
| 614 | ) bool { |
| 615 | switch (target.cpu.arch) { |
| 616 | .x86_64, .x86 => return target.supportsAddressSpace(from, null) and target.supportsAddressSpace(to, null), |
| 617 | .nvptx64, .nvptx, .amdgcn => { |
| 618 | const to_generic = target.supportsAddressSpace(from, null) and to == .generic; |
| 619 | const from_generic = target.supportsAddressSpace(to, null) and from == .generic; |
| 620 | return to_generic or from_generic; |
| 621 | }, |
| 622 | else => return from == .generic and to == .generic, |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | /// Returns whether pointer operations (arithmetic, indexing, etc.) should be blocked |
| 627 | /// for the given address space on the target architecture. |
| 628 | /// |
| 629 | /// Under SPIR-V with Vulkan |
| 630 | /// (a) all physical pointers (.physical_storage_buffer, .global) always support pointer operations, |
| 631 | /// (b) by default logical pointers (.constant, .input, .output, etc.) never support operations |
| 632 | /// (c) some logical pointers (.storage_buffer, .shared) do support operations when |
| 633 | /// the VariablePointers capability is enabled (which enables OpPtrAccessChain). |
| 634 | pub fn shouldBlockPointerOps(target: *const std.Target, as: AddressSpace) bool { |
| 635 | if (target.os.tag != .vulkan and target.os.tag != .opengl) return false; |
| 636 | |
| 637 | return switch (as) { |
| 638 | // TODO: Vulkan doesn't support pointers in the generic address space, we |
| 639 | // should remove this case but this requires a change in defaultAddressSpace(). |
| 640 | .generic => true, |
| 641 | // For now, all global pointers are represented using StorageBuffer or CrossWorkgroup, |
| 642 | // so these are real pointers. |
| 643 | // Physical pointers always support operations |
| 644 | .global, .physical_storage_buffer => false, |
| 645 | // Logical pointers that support operations with VariablePointers capability |
| 646 | .shared => !target.cpu.features.isEnabled(@backingInt(std.Target.spirv.Feature.variable_pointers)), |
| 647 | .storage_buffer => !target.cpu.features.isEnabled(@backingInt(std.Target.spirv.Feature.variable_pointers)), |
| 648 | // Logical pointers that never support operations |
| 649 | .constant, |
| 650 | .local, |
| 651 | .input, |
| 652 | .output, |
| 653 | .uniform, |
| 654 | .push_constant, |
| 655 | => true, |
| 656 | else => unreachable, |
| 657 | }; |
| 658 | } |
| 659 | |
| 660 | pub fn isDynamicAMDGCNFeature(target: *const std.Target, feature: std.Target.Cpu.Feature) bool { |
| 661 | if (target.cpu.arch != .amdgcn) return false; |
| 662 | |
| 663 | const sramecc_only = &[_]*const std.Target.Cpu.Model{ |
| 664 | &std.Target.amdgcn.cpu.gfx1010, |
| 665 | &std.Target.amdgcn.cpu.gfx1011, |
| 666 | &std.Target.amdgcn.cpu.gfx1012, |
| 667 | &std.Target.amdgcn.cpu.gfx1013, |
| 668 | }; |
| 669 | const xnack_or_sramecc = &[_]*const std.Target.Cpu.Model{ |
| 670 | &std.Target.amdgcn.cpu.gfx1030, |
| 671 | &std.Target.amdgcn.cpu.gfx1031, |
| 672 | &std.Target.amdgcn.cpu.gfx1032, |
| 673 | &std.Target.amdgcn.cpu.gfx1033, |
| 674 | &std.Target.amdgcn.cpu.gfx1034, |
| 675 | &std.Target.amdgcn.cpu.gfx1035, |
| 676 | &std.Target.amdgcn.cpu.gfx1036, |
| 677 | &std.Target.amdgcn.cpu.gfx1100, |
| 678 | &std.Target.amdgcn.cpu.gfx1101, |
| 679 | &std.Target.amdgcn.cpu.gfx1102, |
| 680 | &std.Target.amdgcn.cpu.gfx1103, |
| 681 | &std.Target.amdgcn.cpu.gfx1150, |
| 682 | &std.Target.amdgcn.cpu.gfx1151, |
| 683 | &std.Target.amdgcn.cpu.gfx1152, |
| 684 | &std.Target.amdgcn.cpu.gfx1153, |
| 685 | &std.Target.amdgcn.cpu.gfx1200, |
| 686 | &std.Target.amdgcn.cpu.gfx1201, |
| 687 | }; |
| 688 | const feature_tag: std.Target.amdgcn.Feature = @fromBackingInt(@intCast(feature.index)); |
| 689 | |
| 690 | if (feature_tag == .sramecc) { |
| 691 | if (std.mem.findScalar( |
| 692 | *const std.Target.Cpu.Model, |
| 693 | sramecc_only ++ xnack_or_sramecc, |
| 694 | target.cpu.model, |
| 695 | )) |_| return true; |
| 696 | } |
| 697 | if (feature_tag == .xnack) { |
| 698 | if (std.mem.findScalar( |
| 699 | *const std.Target.Cpu.Model, |
| 700 | xnack_or_sramecc, |
| 701 | target.cpu.model, |
| 702 | )) |_| return true; |
| 703 | } |
| 704 | |
| 705 | return false; |
| 706 | } |
| 707 | |
| 708 | pub fn llvmMachineAbi(target: *const std.Target) ?[:0]const u8 { |
| 709 | return switch (target.cpu.arch) { |
| 710 | .arm, .armeb, .thumb, .thumbeb => "aapcs", |
| 711 | .loongarch64 => switch (target.abi) { |
| 712 | .gnusf, .muslsf => "lp64s", |
| 713 | .gnuf32, .muslf32 => "lp64f", |
| 714 | else => "lp64d", |
| 715 | }, |
| 716 | .loongarch32 => switch (target.abi) { |
| 717 | .gnusf, .muslsf => "ilp32s", |
| 718 | .gnuf32, .muslf32 => "ilp32f", |
| 719 | else => "ilp32d", |
| 720 | }, |
| 721 | .mips, .mipsel => "o32", |
| 722 | .mips64, .mips64el => switch (target.abi) { |
| 723 | .gnuabin32, .muslabin32, .abin32 => "n32", |
| 724 | else => "n64", |
| 725 | }, |
| 726 | .powerpc64 => if (target.os.tag == .ps3) "elfv1" else "elfv2", |
| 727 | .powerpc64le => "elfv2", |
| 728 | .riscv64, .riscv64be => if (target.cpu.has(.riscv, .e)) |
| 729 | "lp64e" |
| 730 | else if (target.cpu.has(.riscv, .d)) |
| 731 | "lp64d" |
| 732 | else if (target.cpu.has(.riscv, .f)) |
| 733 | "lp64f" |
| 734 | else |
| 735 | "lp64", |
| 736 | .riscv32, .riscv32be => if (target.cpu.has(.riscv, .e)) |
| 737 | "ilp32e" |
| 738 | else if (target.cpu.has(.riscv, .d)) |
| 739 | "ilp32d" |
| 740 | else if (target.cpu.has(.riscv, .f)) |
| 741 | "ilp32f" |
| 742 | else |
| 743 | "ilp32", |
| 744 | else => null, |
| 745 | }; |
| 746 | } |
| 747 | |
| 748 | /// This function returns 1 if function alignment is not observable or settable. Note that this |
| 749 | /// value will not necessarily match the backend's default function alignment (e.g. for LLVM). |
| 750 | pub fn defaultFunctionAlignment(target: *const std.Target) Alignment { |
| 751 | // Overrides of the minimum for performance. |
| 752 | return switch (target.cpu.arch) { |
| 753 | .csky, |
| 754 | .thumb, |
| 755 | .thumbeb, |
| 756 | .xcore, |
| 757 | => .@"4", |
| 758 | .aarch64, |
| 759 | .aarch64_be, |
| 760 | .hexagon, |
| 761 | .powerpc, |
| 762 | .powerpcle, |
| 763 | .powerpc64, |
| 764 | .powerpc64le, |
| 765 | .s390x, |
| 766 | .x86, |
| 767 | .x86_64, |
| 768 | => .@"16", |
| 769 | .loongarch32, |
| 770 | .loongarch64, |
| 771 | => .@"32", |
| 772 | else => minFunctionAlignment(target), |
| 773 | }; |
| 774 | } |
| 775 | |
| 776 | /// This function returns 1 if function alignment is not observable or settable. |
| 777 | pub fn minFunctionAlignment(target: *const std.Target) Alignment { |
| 778 | return switch (target.cpu.arch) { |
| 779 | .riscv32, |
| 780 | .riscv32be, |
| 781 | .riscv64, |
| 782 | .riscv64be, |
| 783 | => if (target.cpu.hasAny(.riscv, &.{ .c, .zca })) .@"2" else .@"4", |
| 784 | .thumb, |
| 785 | .thumbeb, |
| 786 | .csky, |
| 787 | .m68k, |
| 788 | .msp430, |
| 789 | .sh, |
| 790 | .sheb, |
| 791 | .s390x, |
| 792 | .xcore, |
| 793 | => .@"2", |
| 794 | .aarch64, |
| 795 | .aarch64_be, |
| 796 | .alpha, |
| 797 | .arc, |
| 798 | .arceb, |
| 799 | .arm, |
| 800 | .armeb, |
| 801 | .hexagon, |
| 802 | .hppa, |
| 803 | .hppa64, |
| 804 | .lanai, |
| 805 | .loongarch32, |
| 806 | .loongarch64, |
| 807 | .microblaze, |
| 808 | .microblazeel, |
| 809 | .mips, |
| 810 | .mipsel, |
| 811 | .powerpc, |
| 812 | .powerpcle, |
| 813 | .powerpc64, |
| 814 | .powerpc64le, |
| 815 | .sparc, |
| 816 | .sparc64, |
| 817 | .xtensa, |
| 818 | .xtensaeb, |
| 819 | => .@"4", |
| 820 | .bpfeb, |
| 821 | .bpfel, |
| 822 | .kvx, |
| 823 | .mips64, |
| 824 | .mips64el, |
| 825 | => .@"8", |
| 826 | .ve, |
| 827 | => .@"16", |
| 828 | else => .@"1", |
| 829 | }; |
| 830 | } |
| 831 | |
| 832 | pub fn supportsFunctionAlignment(target: *const std.Target) bool { |
| 833 | return switch (target.cpu.arch) { |
| 834 | .nvptx, |
| 835 | .nvptx64, |
| 836 | .spirv32, |
| 837 | .spirv64, |
| 838 | .wasm32, |
| 839 | .wasm64, |
| 840 | => false, |
| 841 | else => true, |
| 842 | }; |
| 843 | } |
| 844 | |
| 845 | pub fn functionPointerMask(target: *const std.Target) ?u64 { |
| 846 | // 32-bit Arm uses the LSB to mean that the target function contains Thumb code. |
| 847 | // MIPS uses the LSB to mean that the target function contains MIPS16/microMIPS code. |
| 848 | return if (target.cpu.arch.isArm() or target.cpu.arch.isMIPS32()) |
| 849 | ~@as(u32, 1) |
| 850 | else if (target.cpu.arch.isMIPS64()) |
| 851 | ~@as(u64, 1) |
| 852 | else |
| 853 | null; |
| 854 | } |
| 855 | |
| 856 | pub fn supportsTailCall(target: *const std.Target, backend: std.lang.CompilerBackend) bool { |
| 857 | switch (backend) { |
| 858 | .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), |
| 859 | .stage2_c => return true, |
| 860 | else => return false, |
| 861 | } |
| 862 | } |
| 863 | |
| 864 | pub fn supportsThreads(target: *const std.Target, backend: std.lang.CompilerBackend) bool { |
| 865 | _ = target; |
| 866 | return switch (backend) { |
| 867 | .stage2_aarch64 => false, |
| 868 | .stage2_loongarch => false, |
| 869 | else => true, |
| 870 | }; |
| 871 | } |
| 872 | |
| 873 | pub fn libcFloatPrefix(float_bits: u16) []const u8 { |
| 874 | return switch (float_bits) { |
| 875 | 16, 80 => "__", |
| 876 | 32, 64, 128 => "", |
| 877 | else => unreachable, |
| 878 | }; |
| 879 | } |
| 880 | |
| 881 | pub fn libcFloatSuffix(float_bits: u16) []const u8 { |
| 882 | return switch (float_bits) { |
| 883 | 16 => "h", // Non-standard |
| 884 | 32 => "f", |
| 885 | 64 => "", |
| 886 | 80 => "x", // Non-standard |
| 887 | 128 => "f128", |
| 888 | else => unreachable, |
| 889 | }; |
| 890 | } |
| 891 | |
| 892 | pub fn compilerRtFloatAbbrev(target: *const std.Target, float_bits: u16) []const u8 { |
| 893 | return switch (float_bits) { |
| 894 | 16 => "h", |
| 895 | 32 => "s", |
| 896 | 64 => "d", |
| 897 | 80 => "x", |
| 898 | 128 => if (target.cpu.arch.isPowerPC()) "k" else "t", |
| 899 | else => unreachable, |
| 900 | }; |
| 901 | } |
| 902 | |
| 903 | pub fn compilerRtIntAbbrev(bits: u16) []const u8 { |
| 904 | return switch (bits) { |
| 905 | 16 => "h", |
| 906 | 32 => "s", |
| 907 | 64 => "d", |
| 908 | 128 => "t", |
| 909 | else => unreachable, |
| 910 | }; |
| 911 | } |
| 912 | |
| 913 | pub fn fnCallConvAllowsZigTypes(cc: std.lang.CallingConvention) bool { |
| 914 | return switch (cc) { |
| 915 | .auto, .async, .@"inline" => true, |
| 916 | // For now we want to authorize PTX kernel to use zig objects, even if |
| 917 | // we end up exposing the ABI. The goal is to experiment with more |
| 918 | // integrated CPU/GPU code. |
| 919 | .nvptx_kernel => true, |
| 920 | else => false, |
| 921 | }; |
| 922 | } |
| 923 | |
| 924 | pub fn zigBackend(target: *const std.Target, use_llvm: bool) std.lang.CompilerBackend { |
| 925 | if (use_llvm) return .stage2_llvm; |
| 926 | if (target.ofmt == .c) return .stage2_c; |
| 927 | return switch (target.cpu.arch) { |
| 928 | .aarch64, .aarch64_be => .stage2_aarch64, |
| 929 | .arm, .armeb, .thumb, .thumbeb => .stage2_arm, |
| 930 | .loongarch32, .loongarch64 => .stage2_loongarch, |
| 931 | .powerpc, .powerpcle, .powerpc64, .powerpc64le => .stage2_powerpc, |
| 932 | .riscv64 => .stage2_riscv64, |
| 933 | .sparc64 => .stage2_sparc64, |
| 934 | .spirv32, .spirv64 => .stage2_spirv, |
| 935 | .wasm32, .wasm64 => .stage2_wasm, |
| 936 | .x86 => .stage2_x86, |
| 937 | .x86_64 => .stage2_x86_64, |
| 938 | .spork8 => .zsf_spork8, |
| 939 | else => .other, |
| 940 | }; |
| 941 | } |
| 942 | |
| 943 | pub inline fn backendSupportsFeature(backend: std.lang.CompilerBackend, comptime feature: Feature) bool { |
| 944 | return switch (feature) { |
| 945 | .panic_fn => switch (backend) { |
| 946 | .stage2_aarch64, |
| 947 | .stage2_c, |
| 948 | .stage2_llvm, |
| 949 | .stage2_x86_64, |
| 950 | .stage2_riscv64, |
| 951 | .stage2_wasm, |
| 952 | => true, |
| 953 | else => false, |
| 954 | }, |
| 955 | .error_return_trace => switch (backend) { |
| 956 | .stage2_llvm, .stage2_x86_64 => true, |
| 957 | else => false, |
| 958 | }, |
| 959 | .is_named_enum_value => switch (backend) { |
| 960 | .stage2_llvm, .stage2_x86_64, .stage2_wasm => true, |
| 961 | else => false, |
| 962 | }, |
| 963 | .error_set_has_value => switch (backend) { |
| 964 | .stage2_llvm, .stage2_wasm, .stage2_x86_64 => true, |
| 965 | else => false, |
| 966 | }, |
| 967 | .field_reordering => switch (backend) { |
| 968 | .stage2_aarch64, .stage2_c, .stage2_llvm, .stage2_loongarch, .stage2_x86_64, .stage2_wasm => true, |
| 969 | else => false, |
| 970 | }, |
| 971 | .separate_thread => switch (backend) { |
| 972 | // Supports a separate thread but does not support N separate |
| 973 | // threads because they would all just be locking the same mutex to |
| 974 | // protect Builder. |
| 975 | .stage2_llvm => false, |
| 976 | // Please do not make any more exceptions. Backends must support |
| 977 | // being run in a separate thread from now on. |
| 978 | else => true, |
| 979 | }, |
| 980 | }; |
| 981 | } |