1const builtin = @import("builtin");
2const std = @import("std");
3const assert = std.debug.assert;
4
5const Type = @import("Type.zig");
6const AddressSpace = std.lang.AddressSpace;
7const Alignment = @import("InternPool.zig").Alignment;
8const Compilation = @import("Compilation.zig");
9const Feature = @import("Zcu.zig").Feature;
10
11pub const default_stack_protector_buffer_size = 4;
12
13pub 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
33pub 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
42pub fn libCNeedsLibUnwind(target: *const std.Target, link_mode: std.lang.LinkMode) bool {
43 return target.isGnuLibC() and link_mode == .static;
44}
45
46pub 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
63pub 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.
74pub 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
80pub 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
89pub 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.
97pub 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
104pub 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
111pub fn alwaysSingleThreaded(target: *const std.Target) bool {
112 _ = target;
113 return false;
114}
115
116pub 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
124pub 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
136pub 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.
187pub 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.
280pub 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.
288pub 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.
299pub 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
325pub 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
334pub 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
349pub fn clangSupportsStackProtector(target: *const std.Target) bool {
350 return switch (target.cpu.arch) {
351 .spirv32, .spirv64 => return false,
352 else => true,
353 };
354}
355
356pub 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.
362pub 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
374pub const CompilerRtClassification = enum { none, only_compiler_rt, only_libunwind, both };
375
376pub 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
398pub 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
425pub 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
433pub 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
451pub 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.
468pub 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
472pub 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
488pub 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
506pub 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.
513pub 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.
522pub 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
534pub 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
559pub 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
578pub 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
590pub 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`.
610pub 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).
634pub 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
660pub 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
708pub 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).
750pub 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.
777pub 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
832pub 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
845pub 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
856pub 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
864pub 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
873pub 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
881pub 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
892pub 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
903pub 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
913pub 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
924pub 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
943pub 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}