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