1const std = @import("std");
2const llvm = @import("codegen/llvm/bindings.zig");
3
4pub const ArchOsAbi = struct {
5    arch: std.Target.Cpu.Arch,
6    os: std.Target.Os.Tag,
7    abi: std.Target.Abi,
8    os_ver: ?std.builtin.Version = null,
9};
10
11pub const available_libcs = [_]ArchOsAbi{
12    .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu },
13    .{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
14    .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
15    .{ .arch = .aarch64, .os = .linux, .abi = .gnu },
16    .{ .arch = .aarch64, .os = .linux, .abi = .musl },
17    .{ .arch = .aarch64, .os = .windows, .abi = .gnu },
18    .{ .arch = .aarch64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 11, .minor = 0 } },
19    .{ .arch = .aarch64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 12, .minor = 0 } },
20    .{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
21    .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
22    .{ .arch = .armeb, .os = .linux, .abi = .musleabi },
23    .{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
24    .{ .arch = .armeb, .os = .windows, .abi = .gnu },
25    .{ .arch = .arm, .os = .linux, .abi = .gnueabi },
26    .{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
27    .{ .arch = .arm, .os = .linux, .abi = .musleabi },
28    .{ .arch = .arm, .os = .linux, .abi = .musleabihf },
29    .{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
30    .{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
31    .{ .arch = .thumb, .os = .linux, .abi = .musleabi },
32    .{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
33    .{ .arch = .arm, .os = .windows, .abi = .gnu },
34    .{ .arch = .csky, .os = .linux, .abi = .gnueabi },
35    .{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
36    .{ .arch = .i386, .os = .linux, .abi = .gnu },
37    .{ .arch = .i386, .os = .linux, .abi = .musl },
38    .{ .arch = .i386, .os = .windows, .abi = .gnu },
39    .{ .arch = .m68k, .os = .linux, .abi = .gnu },
40    .{ .arch = .m68k, .os = .linux, .abi = .musl },
41    .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
42    .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
43    .{ .arch = .mips64el, .os = .linux, .abi = .musl },
44    .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
45    .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
46    .{ .arch = .mips64, .os = .linux, .abi = .musl },
47    .{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
48    .{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
49    .{ .arch = .mipsel, .os = .linux, .abi = .musl },
50    .{ .arch = .mips, .os = .linux, .abi = .gnueabi },
51    .{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
52    .{ .arch = .mips, .os = .linux, .abi = .musl },
53    .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu },
54    .{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
55    .{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
56    .{ .arch = .powerpc64, .os = .linux, .abi = .musl },
57    .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
58    .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
59    .{ .arch = .powerpc, .os = .linux, .abi = .musl },
60    .{ .arch = .riscv64, .os = .linux, .abi = .gnu },
61    .{ .arch = .riscv64, .os = .linux, .abi = .musl },
62    .{ .arch = .s390x, .os = .linux, .abi = .gnu },
63    .{ .arch = .s390x, .os = .linux, .abi = .musl },
64    .{ .arch = .sparc, .os = .linux, .abi = .gnu },
65    .{ .arch = .sparcv9, .os = .linux, .abi = .gnu },
66    .{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
67    .{ .arch = .wasm32, .os = .wasi, .abi = .musl },
68    .{ .arch = .x86_64, .os = .linux, .abi = .gnu },
69    .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
70    .{ .arch = .x86_64, .os = .linux, .abi = .musl },
71    .{ .arch = .x86_64, .os = .windows, .abi = .gnu },
72    .{ .arch = .x86_64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 10, .minor = 0 } },
73    .{ .arch = .x86_64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 11, .minor = 0 } },
74    .{ .arch = .x86_64, .os = .macos, .abi = .gnu, .os_ver = .{ .major = 12, .minor = 0 } },
75};
76
77pub fn libCGenericName(target: std.Target) [:0]const u8 {
78    switch (target.os.tag) {
79        .windows => return "mingw",
80        .macos, .ios, .tvos, .watchos => return "darwin",
81        else => {},
82    }
83    switch (target.abi) {
84        .gnu,
85        .gnuabin32,
86        .gnuabi64,
87        .gnueabi,
88        .gnueabihf,
89        .gnux32,
90        .gnuilp32,
91        => return "glibc",
92        .musl,
93        .musleabi,
94        .musleabihf,
95        .muslx32,
96        .none,
97        => return "musl",
98        .code16,
99        .eabi,
100        .eabihf,
101        .android,
102        .msvc,
103        .itanium,
104        .cygnus,
105        .coreclr,
106        .simulator,
107        .macabi,
108        => unreachable,
109    }
110}
111
112pub fn osArchName(target: std.Target) [:0]const u8 {
113    return switch (target.os.tag) {
114        .linux => switch (target.cpu.arch) {
115            .arm, .armeb, .thumb, .thumbeb => "arm",
116            .aarch64, .aarch64_be, .aarch64_32 => "arm64",
117            .mips, .mipsel, .mips64, .mips64el => "mips",
118            .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
119            .riscv32, .riscv64 => "riscv",
120            .sparc, .sparcel, .sparcv9 => "sparc",
121            .i386, .x86_64 => "x86",
122            else => @tagName(target.cpu.arch),
123        },
124        else => @tagName(target.cpu.arch),
125    };
126}
127
128pub fn canBuildLibC(target: std.Target) bool {
129    for (available_libcs) |libc| {
130        if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
131            if (target.os.tag == .macos) {
132                const ver = target.os.version_range.semver;
133                if (ver.min.major != libc.os_ver.?.major) continue; // no match, keep going
134            }
135            return true;
136        }
137    }
138    return false;
139}
140
141pub fn cannotDynamicLink(target: std.Target) bool {
142    return switch (target.os.tag) {
143        .freestanding, .other => true,
144        else => false,
145    };
146}
147
148/// On Darwin, we always link libSystem which contains libc.
149/// Similarly on FreeBSD and NetBSD we always link system libc
150/// since this is the stable syscall interface.
151pub fn osRequiresLibC(target: std.Target) bool {
152    return target.os.requiresLibC();
153}
154
155pub fn libcNeedsLibUnwind(target: std.Target) bool {
156    return switch (target.os.tag) {
157        .macos,
158        .ios,
159        .watchos,
160        .tvos,
161        .freestanding,
162        .wasi, // Wasm/WASI currently doesn't offer support for libunwind, so don't link it.
163        => false,
164
165        .windows => target.abi != .msvc,
166        else => true,
167    };
168}
169
170pub fn requiresPIE(target: std.Target) bool {
171    return target.isAndroid() or target.isDarwin() or target.os.tag == .openbsd;
172}
173
174/// This function returns whether non-pic code is completely invalid on the given target.
175pub fn requiresPIC(target: std.Target, linking_libc: bool) bool {
176    return target.isAndroid() or
177        target.os.tag == .windows or target.os.tag == .uefi or
178        osRequiresLibC(target) or
179        (linking_libc and target.isGnuLibC());
180}
181
182/// This is not whether the target supports Position Independent Code, but whether the -fPIC
183/// C compiler argument is valid to Clang.
184pub fn supports_fpic(target: std.Target) bool {
185    return target.os.tag != .windows;
186}
187
188pub fn isSingleThreaded(target: std.Target) bool {
189    return target.isWasm();
190}
191
192/// Valgrind supports more, but Zig does not support them yet.
193pub fn hasValgrindSupport(target: std.Target) bool {
194    switch (target.cpu.arch) {
195        .x86_64 => {
196            return target.os.tag == .linux or target.os.tag == .solaris or
197                (target.os.tag == .windows and target.abi != .msvc);
198        },
199        else => return false,
200    }
201}
202
203/// The set of targets that LLVM has non-experimental support for.
204/// Used to select between LLVM backend and self-hosted backend when compiling in
205/// release modes.
206pub fn hasLlvmSupport(target: std.Target) bool {
207    return switch (target.cpu.arch) {
208        .arm,
209        .armeb,
210        .aarch64,
211        .aarch64_be,
212        .aarch64_32,
213        .arc,
214        .avr,
215        .bpfel,
216        .bpfeb,
217        .csky,
218        .hexagon,
219        .m68k,
220        .mips,
221        .mipsel,
222        .mips64,
223        .mips64el,
224        .msp430,
225        .powerpc,
226        .powerpcle,
227        .powerpc64,
228        .powerpc64le,
229        .r600,
230        .amdgcn,
231        .riscv32,
232        .riscv64,
233        .sparc,
234        .sparcv9,
235        .sparcel,
236        .s390x,
237        .tce,
238        .tcele,
239        .thumb,
240        .thumbeb,
241        .i386,
242        .x86_64,
243        .xcore,
244        .nvptx,
245        .nvptx64,
246        .le32,
247        .le64,
248        .amdil,
249        .amdil64,
250        .hsail,
251        .hsail64,
252        .spir,
253        .spir64,
254        .kalimba,
255        .shave,
256        .lanai,
257        .wasm32,
258        .wasm64,
259        .renderscript32,
260        .renderscript64,
261        .ve,
262        => true,
263
264        .spu_2,
265        .spirv32,
266        .spirv64,
267        => false,
268    };
269}
270
271pub fn supportsStackProbing(target: std.Target) bool {
272    return target.os.tag != .windows and target.os.tag != .uefi and
273        (target.cpu.arch == .i386 or target.cpu.arch == .x86_64);
274}
275
276pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType {
277    return switch (os_tag) {
278        .freestanding, .other, .opencl, .glsl450, .vulkan, .plan9 => .UnknownOS,
279        .windows, .uefi => .Win32,
280        .ananas => .Ananas,
281        .cloudabi => .CloudABI,
282        .dragonfly => .DragonFly,
283        .freebsd => .FreeBSD,
284        .fuchsia => .Fuchsia,
285        .ios => .IOS,
286        .kfreebsd => .KFreeBSD,
287        .linux => .Linux,
288        .lv2 => .Lv2,
289        .macos => .MacOSX,
290        .netbsd => .NetBSD,
291        .openbsd => .OpenBSD,
292        .solaris => .Solaris,
293        .zos => .ZOS,
294        .haiku => .Haiku,
295        .minix => .Minix,
296        .rtems => .RTEMS,
297        .nacl => .NaCl,
298        .aix => .AIX,
299        .cuda => .CUDA,
300        .nvcl => .NVCL,
301        .amdhsa => .AMDHSA,
302        .ps4 => .PS4,
303        .elfiamcu => .ELFIAMCU,
304        .tvos => .TvOS,
305        .watchos => .WatchOS,
306        .mesa3d => .Mesa3D,
307        .contiki => .Contiki,
308        .amdpal => .AMDPAL,
309        .hermit => .HermitCore,
310        .hurd => .Hurd,
311        .wasi => .WASI,
312        .emscripten => .Emscripten,
313    };
314}
315
316pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType {
317    return switch (arch_tag) {
318        .arm => .arm,
319        .armeb => .armeb,
320        .aarch64 => .aarch64,
321        .aarch64_be => .aarch64_be,
322        .aarch64_32 => .aarch64_32,
323        .arc => .arc,
324        .avr => .avr,
325        .bpfel => .bpfel,
326        .bpfeb => .bpfeb,
327        .csky => .csky,
328        .hexagon => .hexagon,
329        .m68k => .m68k,
330        .mips => .mips,
331        .mipsel => .mipsel,
332        .mips64 => .mips64,
333        .mips64el => .mips64el,
334        .msp430 => .msp430,
335        .powerpc => .ppc,
336        .powerpcle => .ppcle,
337        .powerpc64 => .ppc64,
338        .powerpc64le => .ppc64le,
339        .r600 => .r600,
340        .amdgcn => .amdgcn,
341        .riscv32 => .riscv32,
342        .riscv64 => .riscv64,
343        .sparc => .sparc,
344        .sparcv9 => .sparcv9,
345        .sparcel => .sparcel,
346        .s390x => .systemz,
347        .tce => .tce,
348        .tcele => .tcele,
349        .thumb => .thumb,
350        .thumbeb => .thumbeb,
351        .i386 => .x86,
352        .x86_64 => .x86_64,
353        .xcore => .xcore,
354        .nvptx => .nvptx,
355        .nvptx64 => .nvptx64,
356        .le32 => .le32,
357        .le64 => .le64,
358        .amdil => .amdil,
359        .amdil64 => .amdil64,
360        .hsail => .hsail,
361        .hsail64 => .hsail64,
362        .spir => .spir,
363        .spir64 => .spir64,
364        .kalimba => .kalimba,
365        .shave => .shave,
366        .lanai => .lanai,
367        .wasm32 => .wasm32,
368        .wasm64 => .wasm64,
369        .renderscript32 => .renderscript32,
370        .renderscript64 => .renderscript64,
371        .ve => .ve,
372        .spu_2, .spirv32, .spirv64 => .UnknownArch,
373    };
374}
375
376fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
377    if (ignore_case) {
378        return std.ascii.eqlIgnoreCase(a, b);
379    } else {
380        return std.mem.eql(u8, a, b);
381    }
382}
383
384pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool {
385    const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
386
387    if (eqlIgnoreCase(ignore_case, name, "c"))
388        return true;
389
390    if (target.isMinGW()) {
391        if (eqlIgnoreCase(ignore_case, name, "m"))
392            return true;
393
394        return false;
395    }
396
397    if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) {
398        if (eqlIgnoreCase(ignore_case, name, "m"))
399            return true;
400        if (eqlIgnoreCase(ignore_case, name, "rt"))
401            return true;
402        if (eqlIgnoreCase(ignore_case, name, "pthread"))
403            return true;
404        if (eqlIgnoreCase(ignore_case, name, "crypt"))
405            return true;
406        if (eqlIgnoreCase(ignore_case, name, "util"))
407            return true;
408        if (eqlIgnoreCase(ignore_case, name, "xnet"))
409            return true;
410        if (eqlIgnoreCase(ignore_case, name, "resolv"))
411            return true;
412        if (eqlIgnoreCase(ignore_case, name, "dl"))
413            return true;
414    }
415
416    if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System"))
417        return true;
418
419    return false;
420}
421
422pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool {
423    const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows;
424
425    return eqlIgnoreCase(ignore_case, name, "c++") or
426        eqlIgnoreCase(ignore_case, name, "stdc++") or
427        eqlIgnoreCase(ignore_case, name, "c++abi");
428}
429
430pub fn hasDebugInfo(target: std.Target) bool {
431    return !target.cpu.arch.isWasm();
432}
433
434pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.Mode {
435    if (target.cpu.arch.isWasm() and target.os.tag == .freestanding) {
436        return .ReleaseSmall;
437    } else {
438        return .ReleaseFast;
439    }
440}
441
442pub fn hasRedZone(target: std.Target) bool {
443    return switch (target.cpu.arch) {
444        .x86_64,
445        .i386,
446        .powerpc,
447        .powerpc64,
448        .powerpc64le,
449        .aarch64,
450        .aarch64_be,
451        .aarch64_32,
452        => true,
453
454        else => false,
455    };
456}
457
458pub fn libcFullLinkFlags(target: std.Target) []const []const u8 {
459    // The linking order of these is significant and should match the order other
460    // c compilers such as gcc or clang use.
461    return switch (target.os.tag) {
462        .netbsd, .openbsd => &[_][]const u8{
463            "-lm",
464            "-lpthread",
465            "-lc",
466            "-lutil",
467        },
468        .solaris => &[_][]const u8{
469            "-lm",
470            "-lsocket",
471            "-lnsl",
472            // Solaris releases after 10 merged the threading libraries into libc.
473            "-lc",
474        },
475        .haiku => &[_][]const u8{
476            "-lm",
477            "-lroot",
478            "-lpthread",
479            "-lc",
480        },
481        else => switch (target.abi) {
482            .android => &[_][]const u8{
483                "-lm",
484                "-lc",
485                "-ldl",
486            },
487            else => &[_][]const u8{
488                "-lm",
489                "-lpthread",
490                "-lc",
491                "-ldl",
492                "-lrt",
493                "-lutil",
494            },
495        },
496    };
497}
498
499pub fn clangMightShellOutForAssembly(target: std.Target) bool {
500    // Clang defaults to using the system assembler over the internal one
501    // when targeting a non-BSD OS.
502    return target.cpu.arch.isSPARC();
503}
504
505/// Each backend architecture in Clang has a different codepath which may or may not
506/// support an -mcpu flag.
507pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {
508    return switch (target.cpu.arch) {
509        .arm, .armeb, .thumb, .thumbeb => true,
510        else => false,
511    };
512}
513
514pub fn needUnwindTables(target: std.Target) bool {
515    return target.os.tag == .windows;
516}
517
518/// TODO this was ported from stage1 but it does not take into account CPU features,
519/// which can affect this value. Audit this!
520pub fn largestAtomicBits(target: std.Target) u32 {
521    return switch (target.cpu.arch) {
522        .avr,
523        .msp430,
524        .spu_2,
525        => 16,
526
527        .arc,
528        .arm,
529        .armeb,
530        .hexagon,
531        .m68k,
532        .le32,
533        .mips,
534        .mipsel,
535        .nvptx,
536        .powerpc,
537        .powerpcle,
538        .r600,
539        .riscv32,
540        .sparc,
541        .sparcel,
542        .tce,
543        .tcele,
544        .thumb,
545        .thumbeb,
546        .i386,
547        .xcore,
548        .amdil,
549        .hsail,
550        .spir,
551        .kalimba,
552        .lanai,
553        .shave,
554        .wasm32,
555        .renderscript32,
556        .csky,
557        .spirv32,
558        => 32,
559
560        .aarch64,
561        .aarch64_be,
562        .aarch64_32,
563        .amdgcn,
564        .bpfel,
565        .bpfeb,
566        .le64,
567        .mips64,
568        .mips64el,
569        .nvptx64,
570        .powerpc64,
571        .powerpc64le,
572        .riscv64,
573        .sparcv9,
574        .s390x,
575        .amdil64,
576        .hsail64,
577        .spir64,
578        .wasm64,
579        .renderscript64,
580        .ve,
581        .spirv64,
582        => 64,
583
584        .x86_64 => 128,
585    };
586}
587
588pub fn defaultAddressSpace(
589    target: std.Target,
590    context: enum {
591        /// Query the default address space for global constant values.
592        global_constant,
593        /// Query the default address space for global mutable values.
594        global_mutable,
595        /// Query the default address space for function-local values.
596        local,
597        /// Query the default address space for functions themselves.
598        function,
599    },
600) std.builtin.AddressSpace {
601    _ = target;
602    _ = context;
603    return .generic;
604}
605
606pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 {
607    const have_float = switch (target.abi) {
608        .gnuilp32 => return "ilp32",
609        .gnueabihf, .musleabihf, .eabihf => true,
610        else => false,
611    };
612
613    switch (target.cpu.arch) {
614        .riscv64 => {
615            const featureSetHas = std.Target.riscv.featureSetHas;
616            if (featureSetHas(target.cpu.features, .d)) {
617                return "lp64d";
618            } else if (have_float) {
619                return "lp64f";
620            } else {
621                return "lp64";
622            }
623        },
624        .riscv32 => {
625            const featureSetHas = std.Target.riscv.featureSetHas;
626            if (featureSetHas(target.cpu.features, .d)) {
627                return "ilp32d";
628            } else if (have_float) {
629                return "ilp32f";
630            } else if (featureSetHas(target.cpu.features, .e)) {
631                return "ilp32e";
632            } else {
633                return "ilp32";
634            }
635        },
636        //TODO add ARM, Mips, and PowerPC
637        else => return null,
638    }
639}
640