1const builtin = @import("builtin");
2const std = @import("std");
3const assert = std.debug.assert;
4
5const dev = @import("dev.zig");
6const Type = @import("Type.zig");
7const AddressSpace = std.lang.AddressSpace;
8const Alignment = @import("InternPool.zig").Alignment;
9const Compilation = @import("Compilation.zig");
10const Feature = @import("Zcu.zig").Feature;
11
12pub const default_stack_protector_buffer_size = 4;
13
14pub 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
34pub 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
43pub fn libCNeedsLibUnwind(target: *const std.Target, link_mode: std.lang.LinkMode) bool {
44 return target.isGnuLibC() and link_mode == .static;
45}
46
47pub 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
64pub 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.
75pub 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
81pub 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
90pub 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.
98pub 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
105pub 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
112pub fn alwaysSingleThreaded(target: *const std.Target) bool {
113 _ = target;
114 return false;
115}
116
117pub 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
125pub 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
137pub 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.
188pub 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.
281pub 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.
289pub 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.
300pub 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
326pub 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
335pub 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
350pub fn clangSupportsStackProtector(target: *const std.Target) bool {
351 return switch (target.cpu.arch) {
352 .spirv32, .spirv64 => return false,
353 else => true,
354 };
355}
356
357pub 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.
363pub 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
375pub const CompilerRtClassification = enum { none, only_compiler_rt, only_libunwind, both };
376
377pub 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
399pub 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
426pub 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
434pub 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
452pub 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.
469pub 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
473pub 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
489pub 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
507pub 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.
514pub 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.
523pub 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
535pub 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
560pub 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
579pub 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
591pub 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`.
611pub 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).
635pub 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
661pub 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
709pub 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).
751pub 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.
778pub 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
833pub 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
846pub 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
857pub 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
868pub 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
877pub 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
885pub 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
896pub 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
907pub 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
917pub 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
928pub 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
947pub 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}