zig/lib/std / Target.zig

All the details about the machine that will be executing code. Unlike Query which might leave some things as "default" or "host", this data is fully resolved into a concrete set of OS versions, CPU features, etc.

//! All the details about the machine that will be executing code.
//! Unlike `Query` which might leave some things as "default" or "host", this
//! data is fully resolved into a concrete set of OS versions, CPU features,
//! etc.

Query

Target/Query.zig

Based on NTDDI version constants from https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt


has()

Latest Windows version that the Zig Standard Library is aware of

cpu: Cpu,

isAtLeast()

Compared against build numbers reported by the runtime to distinguish win10 versions, where 0x0A000000 + index corresponds to the WindowsVersion u32 value.

os: Os,
abi: Abi,
ofmt: ObjectFormat,
dynamic_linker: DynamicLinker = DynamicLinker.none,

isDarwin()

Returns whether the first version ver is newer (greater) than or equal to the second version ver.


pub const Query = @import("Target/Query.zig");

isBSD()

Checks if system is guaranteed to be at least version or older than version. Returns null if a runtime check is required.


pub const Os = struct {
    tag: Tag,
    version_range: VersionRange,

isSolarish()

This function is defined to serialize a Zig source code representation of this type, that, when parsed, will deserialize into the same data.


    pub const Tag = enum {
        freestanding,
        other,

exeFileExt()

Checks if system is guaranteed to be at least version or older than version. Returns null if a runtime check is required.


        contiki,
        fuchsia,
        hermit,

staticLibSuffix()

Android API level.


        aix,
        haiku,
        hurd,
        linux,
        plan9,
        rtems,
        serenity,
        zos,

dynamicLibSuffix()

Checks if system is guaranteed to be at least version or older than version. Returns null if a runtime check is required.


        dragonfly,
        freebsd,
        netbsd,
        openbsd,

libPrefix()

The version ranges here represent the minimum OS version to be supported and the maximum OS version to be supported. The default values represent the range that the Zig Standard Library bases its abstractions on. The minimum version of the range is the main setting to tweak for a target. Usually, the maximum target OS version will remain the default, which is the latest released version of the OS. To test at compile time if the target is guaranteed to support a given OS feature, one should check that the minimum version of the range is greater than or equal to the version the feature was introduced in. To test at compile time if the target certainly will not support a given OS feature, one should check that the maximum version of the range is less than the version the feature was introduced in. If neither of these cases apply, a runtime check should be used to determine if the target supports a given OS feature. Binaries built with a given maximum version will continue to function on newer operating system versions. However, such a binary may not take full advantage of the newer operating system APIs. See Os.isAtLeast.


        driverkit,
        ios,
        macos,
        tvos,
        visionos,
        watchos,

defaultVersionRange()

The default VersionRange represents the range that the Zig Standard Library bases its abstractions on.


        illumos,
        solaris,

versionRangeTag()

Provides a tagged union. Target does not store the tag because it is redundant with the OS tag; this function abstracts that part away.


        windows,
        uefi,

WindowsVersion

Checks if system is guaranteed to be at least version or older than version. Returns null if a runtime check is required.


        ps3,
        ps4,
        ps5,

latest

C source code.


        emscripten,
        wasi,

known_win10_build_numbers

The Common Object File Format used by Windows and UEFI.


        amdhsa,
        amdpal,
        cuda,
        mesa3d,
        nvcl,
        opencl,
        opengl,
        vulkan,

isAtLeast()

The Executable and Linkable Format used by many Unixes.


        // LLVM tags deliberately omitted:
        // - bridgeos
        // - darwin
        // - kfreebsd
        // - nacl
        // - shadermodel

Range

The Generalized Object File Format used by z/OS.


        pub inline fn isDarwin(tag: Tag) bool {
            return switch (tag) {
                .driverkit,
                .ios,
                .macos,
                .tvos,
                .visionos,
                .watchos,
                => true,
                else => false,
            };
        }

includesVersion()

The Intel HEX format for storing binary code in ASCII text.


        pub inline fn isBSD(tag: Tag) bool {
            return tag.isDarwin() or switch (tag) {
                .freebsd, .openbsd, .netbsd, .dragonfly => true,
                else => false,
            };
        }

isAtLeast()

The Mach object format used by macOS and other Apple platforms.


        pub inline fn isSolarish(tag: Tag) bool {
            return tag == .solaris or tag == .illumos;
        }

parse()

The a.out format used by Plan 9 from Bell Labs.


        pub fn exeFileExt(tag: Tag, arch: Cpu.Arch) [:0]const u8 {
            return switch (tag) {
                .windows => ".exe",
                .uefi => ".efi",
                .plan9 => arch.plan9Ext(),
                else => switch (arch) {
                    .wasm32, .wasm64 => ".wasm",
                    else => "",
                },
            };
        }

format()

Machine code with no metadata.


        pub fn staticLibSuffix(tag: Tag, abi: Abi) [:0]const u8 {
            return switch (abi) {
                .msvc, .itanium => ".lib",
                else => switch (tag) {
                    .windows, .uefi => ".lib",
                    else => ".a",
                },
            };
        }

HurdVersionRange

The Khronos Group's Standard Portable Intermediate Representation V.


        pub fn dynamicLibSuffix(tag: Tag) [:0]const u8 {
            return switch (tag) {
                .windows, .uefi => ".dll",
                .driverkit,
                .ios,
                .macos,
                .tvos,
                .visionos,
                .watchos,
                => ".dylib",
                else => ".so",
            };
        }

includesVersion()

The WebAssembly binary format.


        pub fn libPrefix(tag: Os.Tag, abi: Abi) [:0]const u8 {
            return switch (abi) {
                .msvc, .itanium => "",
                else => switch (tag) {
                    .windows, .uefi => "",
                    else => "lib",
                },
            };
        }

isAtLeast()

The eXtended Common Object File Format used by AIX.


        pub fn defaultVersionRange(tag: Tag, arch: Cpu.Arch, abi: Abi) Os {
            return .{
                .tag = tag,
                .version_range = .default(arch, tag, abi),
            };
        }

LinuxVersionRange

Architecture


        pub inline fn versionRangeTag(tag: Tag) @typeInfo(TaggedVersionRange).@"union".tag_type.? {
            return switch (tag) {
                .freestanding,
                .other,

includesVersion()

The CPU model to target. It has a set of features which are overridden with the features field.


                .haiku,
                .plan9,
                .serenity,

isAtLeast()

An explicit list of the entire CPU feature set. It may differ from the specific CPU model's features.


                .illumos,

VersionRange

The bit index into Set. Has a default value of undefined because the canonical structures are populated via comptime logic.


                .ps3,
                .ps4,
                .ps5,

default()

Has a default value of undefined because the canonical structures are populated via comptime logic.


                .emscripten,

TaggedVersionRange

If this corresponds to an LLVM-recognized feature, this will be populated; otherwise null.


                .mesa3d,
                => .none,

gnuLibCVersion()

Human-friendly UTF-8 text.


                .contiki,
                .fuchsia,
                .hermit,

versionRange()

Sparse Set of features this depends on.


                .aix,
                .rtems,
                .zos,

isAtLeast()

A bit set of all the features.


                .dragonfly,
                .freebsd,
                .netbsd,
                .openbsd,

aarch64

Target/aarch64.zig

Adds the specified feature but not its dependencies.


                .driverkit,
                .macos,
                .ios,
                .tvos,
                .visionos,
                .watchos,

amdgcn

Target/amdgcn.zig

Adds the specified feature set but not its dependencies.


                .solaris,

arc

Target/arc.zig

Removes the specified feature but not its dependents.


                .uefi,

arm

Target/arm.zig

Removes the specified feature but not its dependents.


                .wasi,

avr

Target/avr.zig

Populates only the feature bits specified.


                .amdhsa,
                .amdpal,
                .cuda,
                .nvcl,
                .opencl,
                .opengl,
                .vulkan,
                => .semver,

bpf

Target/bpf.zig

Returns true if the specified feature is enabled.


                .hurd => .hurd,
                .linux => .linux,

csky

Target/csky.zig

Returns true if any specified feature is enabled.


                .windows => .windows,
            };
        }
    };

hexagon

Target/hexagon.zig

Returns true if every specified feature is enabled.


    /// Based on NTDDI version constants from
    /// https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt
    pub const WindowsVersion = enum(u32) {
        nt4 = 0x04000000,
        win2k = 0x05000000,
        xp = 0x05010000,
        ws2003 = 0x05020000,
        vista = 0x06000000,
        win7 = 0x06010000,
        win8 = 0x06020000,
        win8_1 = 0x06030000,
        win10 = 0x0A000000, //aka win10_th1
        win10_th2 = 0x0A000001,
        win10_rs1 = 0x0A000002,
        win10_rs2 = 0x0A000003,
        win10_rs3 = 0x0A000004,
        win10_rs4 = 0x0A000005,
        win10_rs5 = 0x0A000006,
        win10_19h1 = 0x0A000007,
        win10_vb = 0x0A000008, //aka win10_19h2
        win10_mn = 0x0A000009, //aka win10_20h1
        win10_fe = 0x0A00000A, //aka win10_20h2
        win10_co = 0x0A00000B, //aka win10_21h1
        win10_ni = 0x0A00000C, //aka win10_21h2
        win10_cu = 0x0A00000D, //aka win10_22h2
        win11_zn = 0x0A00000E, //aka win11_21h2
        win11_ga = 0x0A00000F, //aka win11_22h2
        win11_ge = 0x0A000010, //aka win11_23h2
        win11_dt = 0x0A000011, //aka win11_24h2
        _,

kalimba

Target/generic.zig

An architecture family can encompass multiple architectures as represented by Arch. For a given family tag, it is guaranteed that an std.Target. namespace exists containing CPU model and feature data.


        /// Latest Windows version that the Zig Standard Library is aware of
        pub const latest = WindowsVersion.win11_dt;

lanai

Target/lanai.zig

Note that this includes Thumb.


        /// Compared against build numbers reported by the runtime to distinguish win10 versions,
        /// where 0x0A000000 + index corresponds to the WindowsVersion u32 value.
        pub const known_win10_build_numbers = [_]u32{
            10240, //win10 aka win10_th1
            10586, //win10_th2
            14393, //win10_rs1
            15063, //win10_rs2
            16299, //win10_rs3
            17134, //win10_rs4
            17763, //win10_rs5
            18362, //win10_19h1
            18363, //win10_vb aka win10_19h2
            19041, //win10_mn aka win10_20h1
            19042, //win10_fe aka win10_20h2
            19043, //win10_co aka win10_21h1
            19044, //win10_ni aka win10_21h2
            19045, //win10_cu aka win10_22h2
            22000, //win11_zn aka win11_21h2
            22621, //win11_ga aka win11_22h2
            22631, //win11_ge aka win11_23h2
            26100, //win11_dt aka win11_24h2
        };

loongarch

Target/loongarch.zig

All CPU features Zig is aware of, sorted lexicographically by name.


        /// Returns whether the first version `ver` is newer (greater) than or equal to the second version `ver`.
        pub inline fn isAtLeast(ver: WindowsVersion, min_ver: WindowsVersion) bool {
            return @intFromEnum(ver) >= @intFromEnum(min_ver);
        }

m68k

Target/m68k.zig

All processors Zig is aware of, sorted lexicographically by name.


        pub const Range = struct {
            min: WindowsVersion,
            max: WindowsVersion,

mips

Target/mips.zig

0c spim little-endian MIPS 3000 family 1c 68000 Motorola MC68000 2c 68020 Motorola MC68020 5c arm little-endian ARM 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) 7c arm64 ARM64 (ARMv8) 8c 386 Intel x86, i486, Pentium, etc. kc sparc Sun SPARC qc power Power PC vc mips big-endian MIPS 3000 family


            pub inline fn includesVersion(range: Range, ver: WindowsVersion) bool {
                return @intFromEnum(ver) >= @intFromEnum(range.min) and
                    @intFromEnum(ver) <= @intFromEnum(range.max);
            }

msp430

Target/msp430.zig

Returns the array of Arch to which a specific std.builtin.CallingConvention applies. Asserts that cc is not .auto, .@"async", .naked, or .@"inline".


            /// Checks if system is guaranteed to be at least `version` or older than `version`.
            /// Returns `null` if a runtime check is required.
            pub inline fn isAtLeast(range: Range, min_ver: WindowsVersion) ?bool {
                if (@intFromEnum(range.min) >= @intFromEnum(min_ver)) return true;
                if (@intFromEnum(range.max) < @intFromEnum(min_ver)) return false;
                return null;
            }
        };

nvptx

Target/nvptx.zig

Returns the most bare-bones CPU model that is valid for arch. Note that this function can return CPU models that are understood by LLVM, but *not* understood by Clang. If Clang compatibility is important, consider using baseline instead.


        pub fn parse(str: []const u8) !WindowsVersion {
            return std.meta.stringToEnum(WindowsVersion, str) orelse
                @enumFromInt(std.fmt.parseInt(u32, str, 0) catch
                    return error.InvalidOperatingSystemVersion);
        }

or1k

Target/generic.zig

Returns a conservative CPU model for arch that is expected to be compatible with the vast majority of hardware available. This function is guaranteed to return CPU models that are understood by both LLVM and Clang, unlike generic. For certain os values, this function will additionally bump the baseline higher than the baseline would be for arch in isolation; for example, for aarch64-macos, the baseline is considered to be apple_m1. To avoid this behavior entirely, pass Os.Tag.freestanding.


        /// This function is defined to serialize a Zig source code representation of this
        /// type, that, when parsed, will deserialize into the same data.
        pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void {
            if (std.enums.tagName(WindowsVersion, wv)) |name| {
                var vecs: [2][]const u8 = .{ ".", name };
                return w.writeVecAll(&vecs);
            } else {
                return w.print("@enumFromInt(0x{X:0>8})", .{wv});
            }
        }
    };

powerpc

Target/powerpc.zig

The "default" set of CPU features for cross-compiling. A conservative set of features that is expected to be supported on most available hardware.


    pub const HurdVersionRange = struct {
        range: std.SemanticVersion.Range,
        glibc: std.SemanticVersion,

propeller

Target/propeller.zig

Returns whether this architecture supports address_space. If context is null, this function simply answers the general question of whether the architecture has any concept of address_space; if non-null, the function additionally checks whether address_space is valid in that context.


        pub inline fn includesVersion(range: HurdVersionRange, ver: std.SemanticVersion) bool {
            return range.range.includesVersion(ver);
        }

riscv

Target/riscv.zig

Returns true if feature is enabled.


        /// Checks if system is guaranteed to be at least `version` or older than `version`.
        /// Returns `null` if a runtime check is required.
        pub inline fn isAtLeast(range: HurdVersionRange, ver: std.SemanticVersion) ?bool {
            return range.range.isAtLeast(ver);
        }
    };

s390x

Target/s390x.zig

Returns true if any feature in features is enabled.


    pub const LinuxVersionRange = struct {
        range: std.SemanticVersion.Range,
        glibc: std.SemanticVersion,
        /// Android API level.
        android: u32,

sparc

Target/sparc.zig

Returns true if all features in features are enabled.


        pub inline fn includesVersion(range: LinuxVersionRange, ver: std.SemanticVersion) bool {
            return range.range.includesVersion(ver);
        }

spirv

Target/spirv.zig

Does this target require linking libc? This may be the case if the target has an unstable syscall interface, for example.


        /// Checks if system is guaranteed to be at least `version` or older than `version`.
        /// Returns `null` if a runtime check is required.
        pub inline fn isAtLeast(range: LinuxVersionRange, ver: std.SemanticVersion) ?bool {
            return range.range.isAtLeast(ver);
        }
    };

ve

Target/ve.zig

Contains the memory used to store the dynamic linker path. This field should not be used directly. See get and set. This field exists so that this API requires no allocator.


    /// The version ranges here represent the minimum OS version to be supported
    /// and the maximum OS version to be supported. The default values represent
    /// the range that the Zig Standard Library bases its abstractions on.
    ///
    /// The minimum version of the range is the main setting to tweak for a target.
    /// Usually, the maximum target OS version will remain the default, which is
    /// the latest released version of the OS.
    ///
    /// To test at compile time if the target is guaranteed to support a given OS feature,
    /// one should check that the minimum version of the range is greater than or equal to
    /// the version the feature was introduced in.
    ///
    /// To test at compile time if the target certainly will not support a given OS feature,
    /// one should check that the maximum version of the range is less than the version the
    /// feature was introduced in.
    ///
    /// If neither of these cases apply, a runtime check should be used to determine if the
    /// target supports a given OS feature.
    ///
    /// Binaries built with a given maximum version will continue to function on newer
    /// operating system versions. However, such a binary may not take full advantage of the
    /// newer operating system APIs.
    ///
    /// See `Os.isAtLeast`.
    pub const VersionRange = union {
        none: void,
        semver: std.SemanticVersion.Range,
        hurd: HurdVersionRange,
        linux: LinuxVersionRange,
        windows: WindowsVersion.Range,

wasm

Target/wasm.zig

Used to construct the dynamic linker path. This field should not be used directly. See get and set.


        /// The default `VersionRange` represents the range that the Zig Standard Library
        /// bases its abstractions on.
        pub fn default(arch: Cpu.Arch, tag: Tag, abi: Abi) VersionRange {
            return switch (tag) {
                .freestanding,
                .other,

x86

Target/x86.zig

Asserts that the length is less than or equal to 255 bytes.


                .haiku,
                .plan9,
                .serenity,

xcore

Target/xcore.zig

The returned memory has the same lifetime as the DynamicLinker.


                .illumos,

xtensa

Target/xtensa.zig

Asserts that the length is less than or equal to 255 bytes.


                .ps3,
                .ps4,
                .ps5,

Abi

Asserts that the length is less than or equal to 255 bytes.


                .emscripten,

default()

No dynamic linker.


                .mesa3d,
                => .{ .none = {} },

isGnu()

Dynamic linker path is determined by the arch/OS components.


                .contiki => .{
                    .semver = .{
                        .min = .{ .major = 4, .minor = 0, .patch = 0 },
                        .max = .{ .major = 5, .minor = 0, .patch = 0 },
                    },
                },
                .fuchsia => .{
                    .semver = .{
                        .min = .{ .major = 1, .minor = 0, .patch = 0 },
                        .max = .{ .major = 27, .minor = 0, .patch = 0 },
                    },
                },
                .hermit => .{
                    .semver = .{
                        .min = .{ .major = 0, .minor = 5, .patch = 0 },
                        .max = .{ .major = 0, .minor = 11, .patch = 0 },
                    },
                },

isMusl()

Dynamic linker path is determined by the arch/OS/ABI components.


                .aix => .{
                    .semver = .{
                        .min = .{ .major = 7, .minor = 2, .patch = 5 },
                        .max = .{ .major = 7, .minor = 3, .patch = 3 },
                    },
                },
                .hurd => .{
                    .hurd = .{
                        .range = .{
                            .min = .{ .major = 0, .minor = 9, .patch = 0 },
                            .max = .{ .major = 0, .minor = 9, .patch = 0 },
                        },
                        .glibc = .{ .major = 2, .minor = 28, .patch = 0 },
                    },
                },
                .linux => .{
                    .linux = .{
                        .range = .{
                            .min = blk: {
                                const default_min: std.SemanticVersion = .{ .major = 5, .minor = 10, .patch = 0 };

isOpenHarmony()

The strictness of this function depends on the value of kind(os.tag): * .none: Ignores all arguments and just returns none. * .arch_os: Ignores abi and returns the dynamic linker matching cpu and os. * .arch_os_abi: Returns the dynamic linker matching cpu, os, and abi. In the case of .arch_os in particular, callers should be aware that a valid dynamic linker being returned only means that the cpu + os combination represents a platform that actually exists and which has an established dynamic linker path that does not change with the ABI; it does not necessarily mean that abi makes any sense at all for that platform. The responsibility for determining whether abi is valid in this case rests with the caller. Abi.default() can be used to pick a best-effort default ABI for such platforms.


                                for (std.zig.target.available_libcs) |libc| {
                                    if (libc.arch != arch or libc.os != tag or libc.abi != abi) continue;

isAndroid()

Default signedness of char for the native C compiler for this target Note that char signedness is implementation-defined and many compilers provide an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char


                                    if (libc.os_ver) |min| {
                                        if (min.order(default_min) == .gt) break :blk min;
                                    }
                                }

Float


                                break :blk default_min;
                            },
                            .max = .{ .major = 6, .minor = 16, .patch = 0 },
                        },
                        .glibc = blk: {
                            // For 32-bit targets that traditionally used 32-bit time, we require
                            // glibc 2.34 for full 64-bit time support. For everything else, we only
                            // require glibc 2.31.
                            const default_min: std.SemanticVersion = switch (arch) {
                                .arm,
                                .armeb,
                                .csky,
                                .m68k,
                                .mips,
                                .mipsel,
                                .powerpc,
                                .sparc,
                                .x86,
                                => .{ .major = 2, .minor = 34, .patch = 0 },
                                .mips64,
                                .mips64el,
                                => if (abi == .gnuabin32)
                                    .{ .major = 2, .minor = 34, .patch = 0 }
                                else
                                    .{ .major = 2, .minor = 31, .patch = 0 },
                                else => .{ .major = 2, .minor = 31, .patch = 0 },
                            };

float()


                            for (std.zig.target.available_libcs) |libc| {
                                if (libc.os != tag or libc.arch != arch or libc.abi != abi) continue;

ObjectFormat


                                if (libc.glibc_min) |min| {
                                    if (min.order(default_min) == .gt) break :blk min;
                                }
                            }

fileExt()


                            break :blk default_min;
                        },
                        .android = 24,
                    },
                },
                .rtems => .{
                    .semver = .{
                        .min = .{ .major = 5, .minor = 1, .patch = 0 },
                        .max = .{ .major = 6, .minor = 1, .patch = 0 },
                    },
                },
                .zos => .{
                    .semver = .{
                        .min = .{ .major = 2, .minor = 5, .patch = 0 },
                        .max = .{ .major = 3, .minor = 1, .patch = 0 },
                    },
                },

default()


                .dragonfly => .{
                    .semver = .{
                        .min = .{ .major = 6, .minor = 0, .patch = 0 },
                        .max = .{ .major = 6, .minor = 4, .patch = 1 },
                    },
                },
                .freebsd => .{
                    .semver = .{
                        .min = blk: {
                            const default_min: std.SemanticVersion = .{ .major = 14, .minor = 0, .patch = 0 };

toElfMachine()


                            for (std.zig.target.available_libcs) |libc| {
                                if (libc.arch != arch or libc.os != tag or libc.abi != abi) continue;

toCoffMachine()


                                if (libc.os_ver) |min| {
                                    if (min.order(default_min) == .gt) break :blk min;
                                }
                            }

SubSystem


                            break :blk default_min;
                        },
                        .max = .{ .major = 14, .minor = 3, .patch = 0 },
                    },
                },
                .netbsd => .{
                    .semver = .{
                        .min = blk: {
                            const default_min: std.SemanticVersion = .{ .major = 10, .minor = 1, .patch = 0 };

Cpu


                            for (std.zig.target.available_libcs) |libc| {
                                if (libc.arch != arch or libc.os != tag or libc.abi != abi) continue;

Feature


                                if (libc.os_ver) |min| {
                                    if (min.order(default_min) == .gt) break :blk min;
                                }
                            }

Set


                            break :blk default_min;
                        },
                        .max = .{ .major = 10, .minor = 1, .patch = 0 },
                    },
                },
                .openbsd => .{
                    .semver = .{
                        .min = .{ .major = 7, .minor = 6, .patch = 0 },
                        .max = .{ .major = 7, .minor = 7, .patch = 0 },
                    },
                },

needed_bit_count


                .driverkit => .{
                    .semver = .{
                        .min = .{ .major = 20, .minor = 0, .patch = 0 },
                        .max = .{ .major = 25, .minor = 0, .patch = 0 },
                    },
                },
                .macos => .{
                    .semver = .{
                        .min = .{ .major = 13, .minor = 0, .patch = 0 },
                        .max = .{ .major = 15, .minor = 6, .patch = 0 },
                    },
                },
                .ios => .{
                    .semver = .{
                        .min = .{ .major = 15, .minor = 0, .patch = 0 },
                        .max = .{ .major = 18, .minor = 6, .patch = 0 },
                    },
                },
                .tvos => .{
                    .semver = .{
                        .min = .{ .major = 15, .minor = 0, .patch = 0 },
                        .max = .{ .major = 18, .minor = 5, .patch = 0 },
                    },
                },
                .visionos => .{
                    .semver = .{
                        .min = .{ .major = 1, .minor = 0, .patch = 0 },
                        .max = .{ .major = 2, .minor = 5, .patch = 0 },
                    },
                },
                .watchos => .{
                    .semver = .{
                        .min = .{ .major = 8, .minor = 0, .patch = 0 },
                        .max = .{ .major = 11, .minor = 6, .patch = 0 },
                    },
                },

byte_count


                .solaris => .{
                    .semver = .{
                        .min = .{ .major = 11, .minor = 0, .patch = 0 },
                        .max = .{ .major = 11, .minor = 4, .patch = 0 },
                    },
                },

usize_count


                .windows => .{
                    .windows = .{
                        .min = .win10,
                        .max = WindowsVersion.latest,
                    },
                },
                .uefi => .{
                    .semver = .{
                        .min = .{ .major = 2, .minor = 0, .patch = 0 },
                        .max = .{ .major = 2, .minor = 11, .patch = 0 },
                    },
                },

Index


                .wasi => .{
                    .semver = .{
                        .min = .{ .major = 0, .minor = 1, .patch = 0 },
                        .max = .{ .major = 0, .minor = 2, .patch = 2 },
                    },
                },

ShiftInt


                .amdhsa => .{
                    .semver = .{
                        .min = .{ .major = 5, .minor = 0, .patch = 0 },
                        .max = .{ .major = 6, .minor = 4, .patch = 2 },
                    },
                },
                .amdpal => .{
                    .semver = .{
                        .min = .{ .major = 1, .minor = 1, .patch = 0 },
                        .max = .{ .major = 3, .minor = 5, .patch = 0 },
                    },
                },
                .cuda => .{
                    .semver = .{
                        .min = .{ .major = 11, .minor = 0, .patch = 1 },
                        .max = .{ .major = 12, .minor = 9, .patch = 1 },
                    },
                },
                .nvcl,
                .opencl,
                => .{
                    .semver = .{
                        .min = .{ .major = 2, .minor = 2, .patch = 0 },
                        .max = .{ .major = 3, .minor = 0, .patch = 17 },
                    },
                },
                .opengl => .{
                    .semver = .{
                        .min = .{ .major = 4, .minor = 5, .patch = 0 },
                        .max = .{ .major = 4, .minor = 6, .patch = 0 },
                    },
                },
                .vulkan => .{
                    .semver = .{
                        .min = .{ .major = 1, .minor = 2, .patch = 0 },
                        .max = .{ .major = 1, .minor = 4, .patch = 321 },
                    },
                },
            };
        }
    };

empty


    pub const TaggedVersionRange = union(enum) {
        none: void,
        semver: std.SemanticVersion.Range,
        hurd: HurdVersionRange,
        linux: LinuxVersionRange,
        windows: WindowsVersion.Range,

isEmpty()


        pub fn gnuLibCVersion(range: TaggedVersionRange) ?std.SemanticVersion {
            return switch (range) {
                .none, .semver, .windows => null,
                .hurd => |h| h.glibc,
                .linux => |l| l.glibc,
            };
        }
    };

count()


    /// Provides a tagged union. `Target` does not store the tag because it is
    /// redundant with the OS tag; this function abstracts that part away.
    pub inline fn versionRange(os: Os) TaggedVersionRange {
        return switch (os.tag.versionRangeTag()) {
            .none => .{ .none = {} },
            .semver => .{ .semver = os.version_range.semver },
            .hurd => .{ .hurd = os.version_range.hurd },
            .linux => .{ .linux = os.version_range.linux },
            .windows => .{ .windows = os.version_range.windows },
        };
    }

isEnabled()


    /// Checks if system is guaranteed to be at least `version` or older than `version`.
    /// Returns `null` if a runtime check is required.
    pub inline fn isAtLeast(os: Os, comptime tag: Tag, ver: switch (tag.versionRangeTag()) {
        .none => void,
        .semver, .hurd, .linux => std.SemanticVersion,
        .windows => WindowsVersion,
    }) ?bool {
        return if (os.tag != tag) false else switch (tag.versionRangeTag()) {
            .none => true,
            inline .semver,
            .hurd,
            .linux,
            .windows,
            => |field| @field(os.version_range, @tagName(field)).isAtLeast(ver),
        };
    }

none:

};

addFeatureSet()


pub const aarch64 = @import("Target/aarch64.zig");
pub const amdgcn = @import("Target/amdgcn.zig");
pub const arc = @import("Target/arc.zig");
pub const arm = @import("Target/arm.zig");
pub const avr = @import("Target/avr.zig");
pub const bpf = @import("Target/bpf.zig");
pub const csky = @import("Target/csky.zig");
pub const hexagon = @import("Target/hexagon.zig");
pub const kalimba = @import("Target/generic.zig");
pub const lanai = @import("Target/lanai.zig");
pub const loongarch = @import("Target/loongarch.zig");
pub const m68k = @import("Target/m68k.zig");
pub const mips = @import("Target/mips.zig");
pub const msp430 = @import("Target/msp430.zig");
pub const nvptx = @import("Target/nvptx.zig");
pub const or1k = @import("Target/generic.zig");
pub const powerpc = @import("Target/powerpc.zig");
pub const propeller = @import("Target/propeller.zig");
pub const riscv = @import("Target/riscv.zig");
pub const s390x = @import("Target/s390x.zig");
pub const sparc = @import("Target/sparc.zig");
pub const spirv = @import("Target/spirv.zig");
pub const ve = @import("Target/ve.zig");
pub const wasm = @import("Target/wasm.zig");
pub const x86 = @import("Target/x86.zig");
pub const xcore = @import("Target/xcore.zig");
pub const xtensa = @import("Target/xtensa.zig");

removeFeature()


pub const Abi = enum {
    none,
    gnu,
    gnuabin32,
    gnuabi64,
    gnueabi,
    gnueabihf,
    gnuf32,
    gnusf,
    gnux32,
    code16,
    eabi,
    eabihf,
    ilp32,
    android,
    androideabi,
    musl,
    muslabin32,
    muslabi64,
    musleabi,
    musleabihf,
    muslf32,
    muslsf,
    muslx32,
    msvc,
    itanium,
    cygnus,
    simulator,
    macabi,
    ohos,
    ohoseabi,

removeFeatureSet()


    // LLVM tags deliberately omitted:
    // - amplification
    // - anyhit
    // - callable
    // - closesthit
    // - compute
    // - coreclr
    // - domain
    // - geometry
    // - gnuf64
    // - hull
    // - intersection
    // - library
    // - mesh
    // - miss
    // - pixel
    // - raygeneration
    // - vertex

populateDependencies()


    pub fn default(arch: Cpu.Arch, os_tag: Os.Tag) Abi {
        return switch (os_tag) {
            .freestanding, .other => switch (arch) {
                // Soft float is usually a sane default for freestanding.
                .arm,
                .armeb,
                .thumb,
                .thumbeb,
                .csky,
                .mips,
                .mipsel,
                .powerpc,
                .powerpcle,
                => .eabi,
                else => .none,
            },
            .aix => if (arch == .powerpc) .eabihf else .none,
            .haiku => switch (arch) {
                .arm,
                .powerpc,
                => .eabihf,
                else => .none,
            },
            .hurd => .gnu,
            .linux => switch (arch) {
                .arm,
                .armeb,
                .thumb,
                .thumbeb,
                .powerpc,
                .powerpcle,
                => .musleabihf,
                // Soft float tends to be more common for CSKY and MIPS.
                .csky,
                => .gnueabi, // No musl support.
                .mips,
                .mipsel,
                => .musleabi,
                .mips64,
                .mips64el,
                => .muslabi64,
                else => .musl,
            },
            .rtems => switch (arch) {
                .arm,
                .armeb,
                .thumb,
                .thumbeb,
                .mips,
                .mipsel,
                => .eabi,
                .powerpc,
                => .eabihf,
                else => .none,
            },
            .freebsd => switch (arch) {
                .arm,
                .powerpc,
                => .eabihf,
                else => .none,
            },
            .netbsd => switch (arch) {
                .arm,
                .armeb,
                .powerpc,
                => .eabihf,
                // Soft float tends to be more common for MIPS.
                .mips,
                .mipsel,
                => .eabi,
                else => .none,
            },
            .openbsd => switch (arch) {
                .arm,
                => .eabi,
                .powerpc,
                => .eabihf,
                else => .none,
            },
            .ios => if (arch == .x86_64) .macabi else .none,
            .tvos, .visionos, .watchos => if (arch == .x86_64) .simulator else .none,
            .windows => .gnu,
            .uefi => .msvc,
            .wasi, .emscripten => .musl,

asBytes()


            .contiki,
            .fuchsia,
            .hermit,
            .plan9,
            .serenity,
            .zos,
            .dragonfly,
            .driverkit,
            .macos,
            .illumos,
            .solaris,
            .ps3,
            .ps4,
            .ps5,
            .amdhsa,
            .amdpal,
            .cuda,
            .mesa3d,
            .nvcl,
            .opencl,
            .opengl,
            .vulkan,
            => .none,
        };
    }

eql()


    pub inline fn isGnu(abi: Abi) bool {
        return switch (abi) {
            .gnu,
            .gnuabin32,
            .gnuabi64,
            .gnueabi,
            .gnueabihf,
            .gnuf32,
            .gnusf,
            .gnux32,
            => true,
            else => false,
        };
    }

isSuperSetOf()


    pub inline fn isMusl(abi: Abi) bool {
        return switch (abi) {
            .musl,
            .muslabin32,
            .muslabi64,
            .musleabi,
            .musleabihf,
            .muslf32,
            .muslsf,
            .muslx32,
            => true,
            else => abi.isOpenHarmony(),
        };
    }

FeatureSetFns()


    pub inline fn isOpenHarmony(abi: Abi) bool {
        return switch (abi) {
            .ohos, .ohoseabi => true,
            else => false,
        };
    }

featureSet()


    pub inline fn isAndroid(abi: Abi) bool {
        return switch (abi) {
            .android, .androideabi => true,
            else => false,
        };
    }

featureSetHas()


    pub const Float = enum {
        hard,
        soft,
    };

featureSetHasAny()


    pub inline fn float(abi: Abi) Float {
        return switch (abi) {
            .androideabi,
            .eabi,
            .gnueabi,
            .musleabi,
            .gnusf,
            .ohoseabi,
            => .soft,
            else => .hard,
        };
    }

none:

};

Arch


pub const ObjectFormat = enum {
    /// C source code.
    c,
    /// The Common Object File Format used by Windows and UEFI.
    coff,
    /// The Executable and Linkable Format used by many Unixes.
    elf,
    /// The Generalized Object File Format used by z/OS.
    goff,
    /// The Intel HEX format for storing binary code in ASCII text.
    hex,
    /// The Mach object format used by macOS and other Apple platforms.
    macho,
    /// The a.out format used by Plan 9 from Bell Labs.
    plan9,
    /// Machine code with no metadata.
    raw,
    /// The Khronos Group's Standard Portable Intermediate Representation V.
    spirv,
    /// The WebAssembly binary format.
    wasm,
    /// The eXtended Common Object File Format used by AIX.
    xcoff,

Family


    // LLVM tags deliberately omitted:
    // - dxcontainer

family()


    pub fn fileExt(of: ObjectFormat, arch: Cpu.Arch) [:0]const u8 {
        return switch (of) {
            .c => ".c",
            .coff => ".obj",
            .elf, .goff, .macho, .wasm, .xcoff => ".o",
            .hex => ".ihex",
            .plan9 => arch.plan9Ext(),
            .raw => ".bin",
            .spirv => ".spv",
        };
    }

isX86()


    pub fn default(os_tag: Os.Tag, arch: Cpu.Arch) ObjectFormat {
        return switch (os_tag) {
            .aix => .xcoff,
            .driverkit, .ios, .macos, .tvos, .visionos, .watchos => .macho,
            .plan9 => .plan9,
            .uefi, .windows => .coff,
            .zos => .goff,
            else => switch (arch) {
                .spirv32, .spirv64 => .spirv,
                .wasm32, .wasm64 => .wasm,
                else => .elf,
            },
        };
    }

none:

};

isThumb()


pub fn toElfMachine(target: *const Target) std.elf.EM {
    return switch (target.cpu.arch) {
        .amdgcn => .AMDGPU,
        .arc => .ARC_COMPACT,
        .arm, .armeb, .thumb, .thumbeb => .ARM,
        .aarch64, .aarch64_be => .AARCH64,
        .avr => .AVR,
        .bpfel, .bpfeb => .BPF,
        .csky => .CSKY,
        .hexagon => .QDSP6,
        .kalimba => .CSR_KALIMBA,
        .lanai => .LANAI,
        .loongarch32, .loongarch64 => .LOONGARCH,
        .m68k => .@"68K",
        .mips, .mips64, .mipsel, .mips64el => .MIPS,
        .msp430 => .MSP430,
        .or1k => .OR1K,
        .powerpc, .powerpcle => .PPC,
        .powerpc64, .powerpc64le => .PPC64,
        .propeller => .PROPELLER,
        .riscv32, .riscv64 => .RISCV,
        .s390x => .S390,
        .sparc => if (target.cpu.has(.sparc, .v9)) .SPARC32PLUS else .SPARC,
        .sparc64 => .SPARCV9,
        .ve => .VE,
        .x86 => .@"386",
        .x86_64 => .X86_64,
        .xcore => .XCORE,
        .xtensa => .XTENSA,

isAARCH64()


        .nvptx,
        .nvptx64,
        .spirv32,
        .spirv64,
        .wasm32,
        .wasm64,
        => .NONE,
    };

none:

}

isLoongArch()


pub fn toCoffMachine(target: *const Target) std.coff.MachineType {
    return switch (target.cpu.arch) {
        .arm => .ARM,
        .thumb => .ARMNT,
        .aarch64 => .ARM64,
        .loongarch32 => .LOONGARCH32,
        .loongarch64 => .LOONGARCH64,
        .riscv32 => .RISCV32,
        .riscv64 => .RISCV64,
        .x86 => .I386,
        .x86_64 => .X64,

isRISCV()


        .amdgcn,
        .arc,
        .armeb,
        .thumbeb,
        .aarch64_be,
        .avr,
        .bpfel,
        .bpfeb,
        .csky,
        .hexagon,
        .kalimba,
        .lanai,
        .m68k,
        .mips,
        .mipsel,
        .mips64,
        .mips64el,
        .msp430,
        .or1k,
        .nvptx,
        .nvptx64,
        .powerpc,
        .powerpcle,
        .powerpc64,
        .powerpc64le,
        .s390x,
        .sparc,
        .sparc64,
        .spirv32,
        .spirv64,
        .ve,
        .wasm32,
        .wasm64,
        .xcore,
        .xtensa,
        .propeller,
        => .UNKNOWN,
    };

none:

}

isMIPS32()


pub const SubSystem = enum {
    Console,
    Windows,
    Posix,
    Native,
    EfiApplication,
    EfiBootServiceDriver,
    EfiRom,
    EfiRuntimeDriver,

none:

};

isPowerPC()


pub const Cpu = struct {
    /// Architecture
    arch: Arch,

isPowerPC32()


    /// The CPU model to target. It has a set of features
    /// which are overridden with the `features` field.
    model: *const Model,

isPowerPC64()


    /// An explicit list of the entire CPU feature set. It may differ from the specific CPU model's features.
    features: Feature.Set,

isSPARC()


    pub const Feature = struct {
        /// The bit index into `Set`. Has a default value of `undefined` because the canonical
        /// structures are populated via comptime logic.
        index: Set.Index = undefined,

isSpirV()


        /// Has a default value of `undefined` because the canonical
        /// structures are populated via comptime logic.
        name: []const u8 = undefined,

isBpf()


        /// If this corresponds to an LLVM-recognized feature, this will be populated;
        /// otherwise null.
        llvm_name: ?[:0]const u8,

isNvptx()


        /// Human-friendly UTF-8 text.
        description: []const u8,

parseCpuModel()


        /// Sparse `Set` of features this depends on.
        dependencies: Set,

endian()


        /// A bit set of all the features.
        pub const Set = struct {
            ints: [usize_count]usize,

allFeaturesList()


            pub const needed_bit_count = 288;
            pub const byte_count = (needed_bit_count + 7) / 8;
            pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize);
            pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize)));
            pub const ShiftInt = std.math.Log2Int(usize);

allCpuModels()


            pub const empty = Set{ .ints = [1]usize{0} ** usize_count };

plan9Ext()


            pub fn isEmpty(set: Set) bool {
                return for (set.ints) |x| {
                    if (x != 0) break false;
                } else true;
            }

fromCallingConvention()


            pub fn count(set: Set) std.math.IntFittingRange(0, needed_bit_count) {
                var sum: usize = 0;
                for (set.ints) |x| sum += @popCount(x);
                return @intCast(sum);
            }

Model


            pub fn isEnabled(set: Set, arch_feature_index: Index) bool {
                const usize_index = arch_feature_index / @bitSizeOf(usize);
                const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));
                return (set.ints[usize_index] & (@as(usize, 1) << bit_index)) != 0;
            }

toCpu()


            /// Adds the specified feature but not its dependencies.
            pub fn addFeature(set: *Set, arch_feature_index: Index) void {
                const usize_index = arch_feature_index / @bitSizeOf(usize);
                const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));
                set.ints[usize_index] |= @as(usize, 1) << bit_index;
            }

generic()


            /// Adds the specified feature set but not its dependencies.
            pub fn addFeatureSet(set: *Set, other_set: Set) void {
                set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints);
            }

baseline()


            /// Removes the specified feature but not its dependents.
            pub fn removeFeature(set: *Set, arch_feature_index: Index) void {
                const usize_index = arch_feature_index / @bitSizeOf(usize);
                const bit_index: ShiftInt = @intCast(arch_feature_index % @bitSizeOf(usize));
                set.ints[usize_index] &= ~(@as(usize, 1) << bit_index);
            }

baseline()


            /// Removes the specified feature but not its dependents.
            pub fn removeFeatureSet(set: *Set, other_set: Set) void {
                set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints);
            }

supportsAddressSpace()


            pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void {
                @setEvalBranchQuota(1000000);

has()


                var old = set.ints;
                while (true) {
                    for (all_features_list, 0..) |feature, index_usize| {
                        const index: Index = @intCast(index_usize);
                        if (set.isEnabled(index)) {
                            set.addFeatureSet(feature.dependencies);
                        }
                    }
                    const nothing_changed = std.mem.eql(usize, &old, &set.ints);
                    if (nothing_changed) return;
                    old = set.ints;
                }
            }

hasAny()


            pub fn asBytes(set: *const Set) *const [byte_count]u8 {
                return std.mem.sliceAsBytes(&set.ints)[0..byte_count];
            }

hasAll()


            pub fn eql(set: Set, other_set: Set) bool {
                return std.mem.eql(usize, &set.ints, &other_set.ints);
            }

zigTriple()


            pub fn isSuperSetOf(set: Set, other_set: Set) bool {
                const V = @Vector(usize_count, usize);
                const set_v: V = set.ints;
                const other_v: V = other_set.ints;
                return @reduce(.And, (set_v & other_v) == other_v);
            }
        };

hurdTupleSimple()


        pub fn FeatureSetFns(comptime F: type) type {
            return struct {
                /// Populates only the feature bits specified.
                pub fn featureSet(features: []const F) Set {
                    var x = Set.empty;
                    for (features) |feature| {
                        x.addFeature(@intFromEnum(feature));
                    }
                    return x;
                }

hurdTuple()


                /// Returns true if the specified feature is enabled.
                pub fn featureSetHas(set: Set, feature: F) bool {
                    return set.isEnabled(@intFromEnum(feature));
                }

linuxTripleSimple()


                /// Returns true if any specified feature is enabled.
                pub fn featureSetHasAny(set: Set, features: anytype) bool {
                    inline for (features) |feature| {
                        if (set.isEnabled(@intFromEnum(@as(F, feature)))) return true;
                    }
                    return false;
                }

linuxTriple()


                /// Returns true if every specified feature is enabled.
                pub fn featureSetHasAll(set: Set, features: anytype) bool {
                    inline for (features) |feature| {
                        if (!set.isEnabled(@intFromEnum(@as(F, feature)))) return false;
                    }
                    return true;
                }
            };
        }
    };

exeFileExt()


    pub const Arch = enum {
        amdgcn,
        arc,
        arm,
        armeb,
        thumb,
        thumbeb,
        aarch64,
        aarch64_be,
        avr,
        bpfel,
        bpfeb,
        csky,
        hexagon,
        kalimba,
        lanai,
        loongarch32,
        loongarch64,
        m68k,
        mips,
        mipsel,
        mips64,
        mips64el,
        msp430,
        or1k,
        nvptx,
        nvptx64,
        powerpc,
        powerpcle,
        powerpc64,
        powerpc64le,
        propeller,
        riscv32,
        riscv64,
        s390x,
        sparc,
        sparc64,
        spirv32,
        spirv64,
        ve,
        wasm32,
        wasm64,
        x86,
        x86_64,
        xcore,
        xtensa,

staticLibSuffix()


        // LLVM tags deliberately omitted:
        // - aarch64_32
        // - amdil
        // - amdil64
        // - dxil
        // - le32
        // - le64
        // - r600
        // - hsail
        // - hsail64
        // - renderscript32
        // - renderscript64
        // - shave
        // - sparcel
        // - spir
        // - spir64
        // - tce
        // - tcele

dynamicLibSuffix()


        /// An architecture family can encompass multiple architectures as represented by `Arch`.
        /// For a given family tag, it is guaranteed that an `std.Target.<tag>` namespace exists
        /// containing CPU model and feature data.
        pub const Family = enum {
            amdgcn,
            arc,
            arm,
            aarch64,
            avr,
            bpf,
            csky,
            hexagon,
            kalimba,
            lanai,
            loongarch,
            m68k,
            mips,
            msp430,
            nvptx,
            or1k,
            powerpc,
            propeller,
            riscv,
            s390x,
            sparc,
            spirv,
            ve,
            wasm,
            x86,
            xcore,
            xtensa,
        };

libPrefix()


        pub inline fn family(arch: Arch) Family {
            return switch (arch) {
                .amdgcn => .amdgcn,
                .arc => .arc,
                .arm, .armeb, .thumb, .thumbeb => .arm,
                .aarch64, .aarch64_be => .aarch64,
                .avr => .avr,
                .bpfel, .bpfeb => .bpf,
                .csky => .csky,
                .hexagon => .hexagon,
                .kalimba => .kalimba,
                .lanai => .lanai,
                .loongarch32, .loongarch64 => .loongarch,
                .m68k => .m68k,
                .mips, .mipsel, .mips64, .mips64el => .mips,
                .msp430 => .msp430,
                .or1k => .or1k,
                .nvptx, .nvptx64 => .nvptx,
                .powerpc, .powerpcle, .powerpc64, .powerpc64le => .powerpc,
                .propeller => .propeller,
                .riscv32, .riscv64 => .riscv,
                .s390x => .s390x,
                .sparc, .sparc64 => .sparc,
                .spirv32, .spirv64 => .spirv,
                .ve => .ve,
                .wasm32, .wasm64 => .wasm,
                .x86, .x86_64 => .x86,
                .xcore => .xcore,
                .xtensa => .xtensa,
            };
        }

isMinGW()


        pub inline fn isX86(arch: Arch) bool {
            return switch (arch) {
                .x86, .x86_64 => true,
                else => false,
            };
        }

isGnuLibC()


        /// Note that this includes Thumb.
        pub inline fn isArm(arch: Arch) bool {
            return switch (arch) {
                .arm, .armeb => true,
                else => arch.isThumb(),
            };
        }

isMuslLibC()


        pub inline fn isThumb(arch: Arch) bool {
            return switch (arch) {
                .thumb, .thumbeb => true,
                else => false,
            };
        }

isDarwinLibC()


        pub inline fn isAARCH64(arch: Arch) bool {
            return switch (arch) {
                .aarch64, .aarch64_be => true,
                else => false,
            };
        }

isFreeBSDLibC()


        pub inline fn isWasm(arch: Arch) bool {
            return switch (arch) {
                .wasm32, .wasm64 => true,
                else => false,
            };
        }

isNetBSDLibC()


        pub inline fn isLoongArch(arch: Arch) bool {
            return switch (arch) {
                .loongarch32, .loongarch64 => true,
                else => false,
            };
        }

isWasiLibC()


        pub inline fn isRISCV(arch: Arch) bool {
            return switch (arch) {
                .riscv32, .riscv64 => true,
                else => false,
            };
        }

requiresLibC()


        pub inline fn isMIPS(arch: Arch) bool {
            return arch.isMIPS32() or arch.isMIPS64();
        }

DynamicLinker


        pub inline fn isMIPS32(arch: Arch) bool {
            return switch (arch) {
                .mips, .mipsel => true,
                else => false,
            };
        }

none:


        pub inline fn isMIPS64(arch: Arch) bool {
            return switch (arch) {
                .mips64, .mips64el => true,
                else => false,
            };
        }

init()


        pub inline fn isPowerPC(arch: Arch) bool {
            return arch.isPowerPC32() or arch.isPowerPC64();
        }

initFmt()


        pub inline fn isPowerPC32(arch: Arch) bool {
            return switch (arch) {
                .powerpc, .powerpcle => true,
                else => false,
            };
        }

get()


        pub inline fn isPowerPC64(arch: Arch) bool {
            return switch (arch) {
                .powerpc64, .powerpc64le => true,
                else => false,
            };
        }

set()


        pub inline fn isSPARC(arch: Arch) bool {
            return switch (arch) {
                .sparc, .sparc64 => true,
                else => false,
            };
        }

setFmt()


        pub inline fn isSpirV(arch: Arch) bool {
            return switch (arch) {
                .spirv32, .spirv64 => true,
                else => false,
            };
        }

eql()


        pub inline fn isBpf(arch: Arch) bool {
            return switch (arch) {
                .bpfel, .bpfeb => true,
                else => false,
            };
        }

Kind


        pub inline fn isNvptx(arch: Arch) bool {
            return switch (arch) {
                .nvptx, .nvptx64 => true,
                else => false,
            };
        }

kind()


        pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model {
            for (arch.allCpuModels()) |cpu| {
                if (std.mem.eql(u8, cpu_name, cpu.name)) {
                    return cpu;
                }
            }
            return error.UnknownCpuModel;
        }

standard()


        pub fn endian(arch: Arch) std.builtin.Endian {
            return switch (arch) {
                .avr,
                .arm,
                .aarch64,
                .amdgcn,
                .bpfel,
                .csky,
                .xtensa,
                .hexagon,
                .kalimba,
                .mipsel,
                .mips64el,
                .msp430,
                .nvptx,
                .nvptx64,
                .powerpcle,
                .powerpc64le,
                .riscv32,
                .riscv64,
                .x86,
                .x86_64,
                .wasm32,
                .wasm64,
                .xcore,
                .thumb,
                .ve,
                // GPU bitness is opaque. For now, assume little endian.
                .spirv32,
                .spirv64,
                .loongarch32,
                .loongarch64,
                .arc,
                .propeller,
                => .little,

standardDynamicLinkerPath()


                .armeb,
                .aarch64_be,
                .bpfeb,
                .m68k,
                .mips,
                .mips64,
                .or1k,
                .powerpc,
                .powerpc64,
                .thumbeb,
                .sparc,
                .sparc64,
                .lanai,
                .s390x,
                => .big,
            };
        }

ptrBitWidth_cpu_abi()


        /// All CPU features Zig is aware of, sorted lexicographically by name.
        pub fn allFeaturesList(arch: Arch) []const Cpu.Feature {
            return switch (arch.family()) {
                inline else => |f| &@field(Target, @tagName(f)).all_features,
            };
        }

ptrBitWidth_arch_abi()


        /// All processors Zig is aware of, sorted lexicographically by name.
        pub fn allCpuModels(arch: Arch) []const *const Cpu.Model {
            return switch (arch.family()) {
                inline else => |f| comptime allCpusFromDecls(@field(Target, @tagName(f)).cpu),
            };
        }

ptrBitWidth()


        fn allCpusFromDecls(comptime cpus: type) []const *const Cpu.Model {
            @setEvalBranchQuota(2000);
            const decls = @typeInfo(cpus).@"struct".decls;
            var array: [decls.len]*const Cpu.Model = undefined;
            for (decls, 0..) |decl, i| {
                array[i] = &@field(cpus, decl.name);
            }
            const finalized = array;
            return &finalized;
        }

stackAlignment()


        /// 0c spim    little-endian MIPS 3000 family
        /// 1c 68000   Motorola MC68000
        /// 2c 68020   Motorola MC68020
        /// 5c arm     little-endian ARM
        /// 6c amd64   AMD64 and compatibles (e.g., Intel EM64T)
        /// 7c arm64   ARM64 (ARMv8)
        /// 8c 386     Intel x86, i486, Pentium, etc.
        /// kc sparc   Sun SPARC
        /// qc power   Power PC
        /// vc mips    big-endian MIPS 3000 family
        pub fn plan9Ext(arch: Cpu.Arch) [:0]const u8 {
            return switch (arch) {
                .arm => ".5",
                .x86_64 => ".6",
                .aarch64 => ".7",
                .x86 => ".8",
                .sparc => ".k",
                .powerpc, .powerpcle => ".q",
                .mips, .mipsel => ".v",
                // ISAs without designated characters get 'X' for lack of a better option.
                else => ".X",
            };
        }

cCharSignedness()


        /// Returns the array of `Arch` to which a specific `std.builtin.CallingConvention` applies.
        /// Asserts that `cc` is not `.auto`, `.@"async"`, `.naked`, or `.@"inline"`.
        pub fn fromCallingConvention(cc: std.builtin.CallingConvention.Tag) []const Arch {
            return switch (cc) {
                .auto,
                .async,
                .naked,
                .@"inline",
                => unreachable,

CType


                .x86_64_sysv,
                .x86_64_win,
                .x86_64_regcall_v3_sysv,
                .x86_64_regcall_v4_win,
                .x86_64_vectorcall,
                .x86_64_interrupt,
                => &.{.x86_64},

cTypeByteSize()


                .x86_sysv,
                .x86_win,
                .x86_stdcall,
                .x86_fastcall,
                .x86_thiscall,
                .x86_thiscall_mingw,
                .x86_regcall_v3,
                .x86_regcall_v4_win,
                .x86_vectorcall,
                .x86_interrupt,
                => &.{.x86},

cTypeBitSize()


                .aarch64_aapcs,
                .aarch64_aapcs_darwin,
                .aarch64_aapcs_win,
                .aarch64_vfabi,
                .aarch64_vfabi_sve,
                => &.{ .aarch64, .aarch64_be },

cTypeAlignment()


                .arm_aapcs,
                .arm_aapcs_vfp,
                .arm_interrupt,
                => &.{ .arm, .armeb, .thumb, .thumbeb },

cTypePreferredAlignment()


                .mips64_n64,
                .mips64_n32,
                .mips64_interrupt,
                => &.{ .mips64, .mips64el },

cMaxIntAlignment()


                .mips_o32,
                .mips_interrupt,
                => &.{ .mips, .mipsel },

cCallingConvention()


                .riscv64_lp64,
                .riscv64_lp64_v,
                .riscv64_interrupt,
                => &.{.riscv64},

                .riscv32_ilp32,
                .riscv32_ilp32_v,
                .riscv32_interrupt,
                => &.{.riscv32},

                .sparc64_sysv,
                => &.{.sparc64},

                .sparc_sysv,
                => &.{.sparc},

                .powerpc64_elf,
                .powerpc64_elf_altivec,
                .powerpc64_elf_v2,
                => &.{ .powerpc64, .powerpc64le },

                .powerpc_sysv,
                .powerpc_sysv_altivec,
                .powerpc_aix,
                .powerpc_aix_altivec,
                => &.{ .powerpc, .powerpcle },

                .wasm_mvp,
                => &.{ .wasm64, .wasm32 },

                .arc_sysv,
                => &.{.arc},

                .avr_gnu,
                .avr_builtin,
                .avr_signal,
                .avr_interrupt,
                => &.{.avr},

                .bpf_std,
                => &.{ .bpfel, .bpfeb },

                .csky_sysv,
                .csky_interrupt,
                => &.{.csky},

                .hexagon_sysv,
                .hexagon_sysv_hvx,
                => &.{.hexagon},

                .lanai_sysv,
                => &.{.lanai},

                .loongarch64_lp64,
                => &.{.loongarch64},

                .loongarch32_ilp32,
                => &.{.loongarch32},

                .m68k_sysv,
                .m68k_gnu,
                .m68k_rtd,
                .m68k_interrupt,
                => &.{.m68k},

                .msp430_eabi,
                => &.{.msp430},

                .or1k_sysv,
                => &.{.or1k},

                .propeller_sysv,
                => &.{.propeller},

                .s390x_sysv,
                .s390x_sysv_vx,
                => &.{.s390x},

                .ve_sysv,
                => &.{.ve},

                .xcore_xs1,
                .xcore_xs2,
                => &.{.xcore},

                .xtensa_call0,
                .xtensa_windowed,
                => &.{.xtensa},

                .amdgcn_device,
                .amdgcn_kernel,
                .amdgcn_cs,
                => &.{.amdgcn},

                .nvptx_device,
                .nvptx_kernel,
                => &.{ .nvptx, .nvptx64 },

                .spirv_device,
                .spirv_kernel,
                .spirv_fragment,
                .spirv_vertex,
                => &.{ .spirv32, .spirv64 },
            };
        }
    };

    pub const Model = struct {
        name: []const u8,
        llvm_name: ?[:0]const u8,
        features: Feature.Set,

        pub fn toCpu(model: *const Model, arch: Arch) Cpu {
            var features = model.features;
            features.populateDependencies(arch.allFeaturesList());
            return .{
                .arch = arch,
                .model = model,
                .features = features,
            };
        }

        /// Returns the most bare-bones CPU model that is valid for `arch`. Note that this function
        /// can return CPU models that are understood by LLVM, but *not* understood by Clang. If
        /// Clang compatibility is important, consider using `baseline` instead.
        pub fn generic(arch: Arch) *const Model {
            return switch (arch) {
                .amdgcn => &amdgcn.cpu.gfx600,
                .avr => &avr.cpu.avr1,
                .loongarch32 => &loongarch.cpu.generic_la32,
                .loongarch64 => &loongarch.cpu.generic_la64,
                .mips, .mipsel => &mips.cpu.mips32,
                .mips64, .mips64el => &mips.cpu.mips64,
                .nvptx, .nvptx64 => &nvptx.cpu.sm_20,
                .powerpc, .powerpcle => &powerpc.cpu.ppc,
                .powerpc64, .powerpc64le => &powerpc.cpu.ppc64,
                .propeller => &propeller.cpu.p1,
                .riscv32 => &riscv.cpu.generic_rv32,
                .riscv64 => &riscv.cpu.generic_rv64,
                .sparc64 => &sparc.cpu.v9, // SPARC can only be 64-bit from v9 and up.
                .wasm32, .wasm64 => &wasm.cpu.mvp,
                .x86 => &x86.cpu.i386,
                .x86_64 => &x86.cpu.x86_64,
                inline else => |a| &@field(Target, @tagName(a.family())).cpu.generic,
            };
        }

        /// Returns a conservative CPU model for `arch` that is expected to be compatible with the
        /// vast majority of hardware available. This function is guaranteed to return CPU models
        /// that are understood by both LLVM and Clang, unlike `generic`.
        ///
        /// For certain `os` values, this function will additionally bump the baseline higher than
        /// the baseline would be for `arch` in isolation; for example, for `aarch64-macos`, the
        /// baseline is considered to be `apple_m1`. To avoid this behavior entirely, pass
        /// `Os.Tag.freestanding`.
        pub fn baseline(arch: Arch, os: Os) *const Model {
            return switch (arch) {
                .amdgcn => &amdgcn.cpu.gfx906,
                .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline,
                .aarch64 => switch (os.tag) {
                    .driverkit, .macos => &aarch64.cpu.apple_m1,
                    .ios, .tvos => &aarch64.cpu.apple_a7,
                    .visionos => &aarch64.cpu.apple_m2,
                    .watchos => &aarch64.cpu.apple_s4,
                    else => generic(arch),
                },
                .avr => &avr.cpu.avr2,
                .bpfel, .bpfeb => &bpf.cpu.v3,
                .csky => &csky.cpu.ck810, // gcc/clang do not have a generic csky model.
                .hexagon => &hexagon.cpu.hexagonv68, // gcc/clang do not have a generic hexagon model.
                .lanai => &lanai.cpu.v11, // clang does not have a generic lanai model.
                .loongarch64 => &loongarch.cpu.loongarch64,
                .m68k => &m68k.cpu.M68000,
                .mips, .mipsel => &mips.cpu.mips32r2,
                .mips64, .mips64el => &mips.cpu.mips64r2,
                .msp430 => &msp430.cpu.msp430,
                .nvptx, .nvptx64 => &nvptx.cpu.sm_52,
                .powerpc64le => &powerpc.cpu.ppc64le,
                .riscv32 => &riscv.cpu.baseline_rv32,
                .riscv64 => &riscv.cpu.baseline_rv64,
                .s390x => &s390x.cpu.arch8, // gcc/clang do not have a generic s390x model.
                .sparc => &sparc.cpu.v9, // glibc does not work with 'plain' v8.
                .x86 => &x86.cpu.pentium4,
                .x86_64 => switch (os.tag) {
                    .driverkit => &x86.cpu.nehalem,
                    .ios, .macos, .tvos, .visionos, .watchos => &x86.cpu.core2,
                    .ps4 => &x86.cpu.btver2,
                    .ps5 => &x86.cpu.znver2,
                    else => generic(arch),
                },
                .xcore => &xcore.cpu.xs1b_generic,
                .wasm32, .wasm64 => &wasm.cpu.lime1,

                else => generic(arch),
            };
        }
    };

    /// The "default" set of CPU features for cross-compiling. A conservative set
    /// of features that is expected to be supported on most available hardware.
    pub fn baseline(arch: Arch, os: Os) Cpu {
        return Model.baseline(arch, os).toCpu(arch);
    }

    /// Returns whether this architecture supports `address_space`. If `context` is `null`, this
    /// function simply answers the general question of whether the architecture has any concept
    /// of `address_space`; if non-`null`, the function additionally checks whether
    /// `address_space` is valid in that context.
    pub fn supportsAddressSpace(
        cpu: Cpu,
        address_space: std.builtin.AddressSpace,
        context: ?std.builtin.AddressSpace.Context,
    ) bool {
        const arch = cpu.arch;

        const is_nvptx = arch.isNvptx();
        const is_spirv = arch.isSpirV();
        const is_gpu = is_nvptx or is_spirv or arch == .amdgcn;

        return switch (address_space) {
            .generic => true,
            .fs, .gs, .ss => (arch == .x86_64 or arch == .x86) and (context == null or context == .pointer),
            .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, // TODO this should also check how many flash banks the cpu has
            .cog, .hub => arch == .propeller,
            .lut => arch == .propeller and cpu.has(.propeller, .p2),

            .global, .local, .shared => is_gpu,
            .constant => is_gpu and (context == null or context == .constant),
            .param => is_nvptx,
            .input, .output, .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => is_spirv,
        };
    }

    /// Returns true if `feature` is enabled.
    pub fn has(cpu: Cpu, comptime family: Arch.Family, feature: @field(Target, @tagName(family)).Feature) bool {
        if (family != cpu.arch.family()) return false;
        return cpu.features.isEnabled(@intFromEnum(feature));
    }

    /// Returns true if any feature in `features` is enabled.
    pub fn hasAny(cpu: Cpu, comptime family: Arch.Family, features: []const @field(Target, @tagName(family)).Feature) bool {
        if (family != cpu.arch.family()) return false;
        for (features) |feature| {
            if (cpu.features.isEnabled(@intFromEnum(feature))) return true;
        }
        return false;
    }

    /// Returns true if all features in `features` are enabled.
    pub fn hasAll(cpu: Cpu, comptime family: Arch.Family, features: []const @field(Target, @tagName(family)).Feature) bool {
        if (family != cpu.arch.family()) return false;
        for (features) |feature| {
            if (!cpu.features.isEnabled(@intFromEnum(feature))) return false;
        }
        return true;
    }
};

pub fn zigTriple(target: *const Target, allocator: Allocator) Allocator.Error![]u8 {
    return Query.fromTarget(target).zigTriple(allocator);
}

pub fn hurdTupleSimple(allocator: Allocator, arch: Cpu.Arch, abi: Abi) ![]u8 {
    return std.fmt.allocPrint(allocator, "{s}-{s}", .{ @tagName(arch), @tagName(abi) });
}

pub fn hurdTuple(target: *const Target, allocator: Allocator) ![]u8 {
    return hurdTupleSimple(allocator, target.cpu.arch, target.abi);
}

pub fn linuxTripleSimple(allocator: Allocator, arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![]u8 {
    return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(arch), @tagName(os_tag), @tagName(abi) });
}

pub fn linuxTriple(target: *const Target, allocator: Allocator) ![]u8 {
    return linuxTripleSimple(allocator, target.cpu.arch, target.os.tag, target.abi);
}

pub fn exeFileExt(target: *const Target) [:0]const u8 {
    return target.os.tag.exeFileExt(target.cpu.arch);
}

pub fn staticLibSuffix(target: *const Target) [:0]const u8 {
    return target.os.tag.staticLibSuffix(target.abi);
}

pub fn dynamicLibSuffix(target: *const Target) [:0]const u8 {
    return target.os.tag.dynamicLibSuffix();
}

pub fn libPrefix(target: *const Target) [:0]const u8 {
    return target.os.tag.libPrefix(target.abi);
}

pub inline fn isMinGW(target: *const Target) bool {
    return target.os.tag == .windows and target.abi.isGnu();
}

pub inline fn isGnuLibC(target: *const Target) bool {
    return switch (target.os.tag) {
        .hurd, .linux => target.abi.isGnu(),
        else => false,
    };
}

pub inline fn isMuslLibC(target: *const Target) bool {
    return target.os.tag == .linux and target.abi.isMusl();
}

pub inline fn isDarwinLibC(target: *const Target) bool {
    return switch (target.abi) {
        .none, .macabi, .simulator => target.os.tag.isDarwin(),
        else => false,
    };
}

pub inline fn isFreeBSDLibC(target: *const Target) bool {
    return switch (target.abi) {
        .none, .eabihf => target.os.tag == .freebsd,
        else => false,
    };
}

pub inline fn isNetBSDLibC(target: *const Target) bool {
    return switch (target.abi) {
        .none, .eabi, .eabihf => target.os.tag == .netbsd,
        else => false,
    };
}

pub inline fn isWasiLibC(target: *const Target) bool {
    return target.os.tag == .wasi and target.abi.isMusl();
}

/// Does this target require linking libc? This may be the case if the target has an unstable
/// syscall interface, for example.
pub fn requiresLibC(target: *const Target) bool {
    return switch (target.os.tag) {
        .aix,
        .driverkit,
        .macos,
        .ios,
        .tvos,
        .watchos,
        .visionos,
        .dragonfly,
        .openbsd,
        .haiku,
        .solaris,
        .illumos,
        .serenity,
        => true,

        // Android API levels prior to 29 did not have native TLS support. For these API levels, TLS
        // is implemented through calls to `__emutls_get_address`. We provide this function in
        // compiler-rt, but it's implemented by way of `pthread_key_create` et al, so linking libc
        // is required.
        .linux => target.abi.isAndroid() and target.os.version_range.linux.android < 29,

        .windows,
        .freebsd,
        .netbsd,
        .freestanding,
        .fuchsia,
        .ps3,
        .zos,
        .rtems,
        .cuda,
        .nvcl,
        .amdhsa,
        .ps4,
        .ps5,
        .mesa3d,
        .contiki,
        .amdpal,
        .hermit,
        .hurd,
        .wasi,
        .emscripten,
        .uefi,
        .opencl,
        .opengl,
        .vulkan,
        .plan9,
        .other,
        => false,
    };
}

pub const DynamicLinker = struct {
    /// Contains the memory used to store the dynamic linker path. This field
    /// should not be used directly. See `get` and `set`. This field exists so
    /// that this API requires no allocator.
    buffer: [255]u8,

    /// Used to construct the dynamic linker path. This field should not be used
    /// directly. See `get` and `set`.
    len: u8,

    pub const none: DynamicLinker = .{ .buffer = undefined, .len = 0 };

    /// Asserts that the length is less than or equal to 255 bytes.
    pub fn init(maybe_path: ?[]const u8) DynamicLinker {
        var dl: DynamicLinker = undefined;
        dl.set(maybe_path);
        return dl;
    }

    pub fn initFmt(comptime fmt_str: []const u8, args: anytype) !DynamicLinker {
        var dl: DynamicLinker = undefined;
        try dl.setFmt(fmt_str, args);
        return dl;
    }

    /// The returned memory has the same lifetime as the `DynamicLinker`.
    pub fn get(dl: *const DynamicLinker) ?[]const u8 {
        return if (dl.len > 0) dl.buffer[0..dl.len] else null;
    }

    /// Asserts that the length is less than or equal to 255 bytes.
    pub fn set(dl: *DynamicLinker, maybe_path: ?[]const u8) void {
        const path = maybe_path orelse "";
        @memcpy(dl.buffer[0..path.len], path);
        dl.len = @intCast(path.len);
    }

    /// Asserts that the length is less than or equal to 255 bytes.
    pub fn setFmt(dl: *DynamicLinker, comptime fmt_str: []const u8, args: anytype) !void {
        dl.len = @intCast((try std.fmt.bufPrint(&dl.buffer, fmt_str, args)).len);
    }

    pub fn eql(lhs: DynamicLinker, rhs: DynamicLinker) bool {
        return std.mem.eql(u8, lhs.buffer[0..lhs.len], rhs.buffer[0..rhs.len]);
    }

    pub const Kind = enum {
        /// No dynamic linker.
        none,
        /// Dynamic linker path is determined by the arch/OS components.
        arch_os,
        /// Dynamic linker path is determined by the arch/OS/ABI components.
        arch_os_abi,
    };

    pub fn kind(os: Os.Tag) Kind {
        return switch (os) {
            .fuchsia,

            .haiku,
            .serenity,

            .dragonfly,
            .freebsd,
            .netbsd,
            .openbsd,

            .driverkit,
            .ios,
            .macos,
            .tvos,
            .visionos,
            .watchos,

            .illumos,
            .solaris,
            => .arch_os,
            .hurd,
            .linux,
            => .arch_os_abi,
            .freestanding,
            .other,

            .contiki,
            .hermit,

            .aix,
            .plan9,
            .rtems,
            .zos,

            .uefi,
            .windows,

            .emscripten,
            .wasi,

            .amdhsa,
            .amdpal,
            .cuda,
            .mesa3d,
            .nvcl,
            .opencl,
            .opengl,
            .vulkan,

            .ps3,
            .ps4,
            .ps5,
            => .none,
        };
    }

    /// The strictness of this function depends on the value of `kind(os.tag)`:
    ///
    /// * `.none`: Ignores all arguments and just returns `none`.
    /// * `.arch_os`: Ignores `abi` and returns the dynamic linker matching `cpu` and `os`.
    /// * `.arch_os_abi`: Returns the dynamic linker matching `cpu`, `os`, and `abi`.
    ///
    /// In the case of `.arch_os` in particular, callers should be aware that a valid dynamic linker
    /// being returned only means that the `cpu` + `os` combination represents a platform that
    /// actually exists and which has an established dynamic linker path that does not change with
    /// the ABI; it does not necessarily mean that `abi` makes any sense at all for that platform.
    /// The responsibility for determining whether `abi` is valid in this case rests with the
    /// caller. `Abi.default()` can be used to pick a best-effort default ABI for such platforms.
    pub fn standard(cpu: Cpu, os: Os, abi: Abi) DynamicLinker {
        return switch (os.tag) {
            .fuchsia => switch (cpu.arch) {
                .aarch64,
                .riscv64,
                .x86_64,
                => init("ld.so.1"), // Fuchsia is unusual in that `DT_INTERP` is just a basename.
                else => none,
            },

            .haiku => switch (cpu.arch) {
                .arm,
                .aarch64,
                .m68k,
                .powerpc,
                .riscv64,
                .sparc64,
                .x86,
                .x86_64,
                => init("/system/runtime_loader"),
                else => none,
            },

            .hurd => switch (cpu.arch) {
                .aarch64,
                .aarch64_be,
                => |arch| if (abi == .gnu) initFmt("/lib/ld-{s}.so.1", .{@tagName(arch)}) else none,

                .x86 => if (abi == .gnu) init("/lib/ld.so.1") else none,
                .x86_64 => initFmt("/lib/ld-{s}.so.1", .{switch (abi) {
                    .gnu => "x86-64",
                    .gnux32 => "x32",
                    else => return none,
                }}),

                else => none,
            },

            .linux => if (abi.isAndroid())
                switch (cpu.arch) {
                    .arm => if (abi == .androideabi) init("/system/bin/linker") else none,

                    .aarch64,
                    .riscv64,
                    .x86,
                    .x86_64,
                    => if (abi == .android) initFmt("/system/bin/linker{s}", .{
                        if (ptrBitWidth_cpu_abi(cpu, abi) == 64) "64" else "",
                    }) else none,

                    else => none,
                }
            else if (abi.isMusl())
                switch (cpu.arch) {
                    .arm,
                    .armeb,
                    .thumb,
                    .thumbeb,
                    => |arch| initFmt("/lib/ld-musl-arm{s}{s}.so.1", .{
                        if (arch == .armeb or arch == .thumbeb) "eb" else "",
                        switch (abi) {
                            .musleabi => "",
                            .musleabihf => "hf",
                            else => return none,
                        },
                    }),

                    .loongarch32,
                    .loongarch64,
                    => |arch| initFmt("/lib/ld-musl-{s}{s}.so.1", .{
                        @tagName(arch),
                        switch (abi) {
                            .musl => "",
                            .muslf32 => "-sp",
                            .muslsf => "-sf",
                            else => return none,
                        },
                    }),

                    .aarch64,
                    .aarch64_be,
                    .m68k,
                    .powerpc64,
                    .powerpc64le,
                    .s390x,
                    => |arch| if (abi == .musl) initFmt("/lib/ld-musl-{s}.so.1", .{@tagName(arch)}) else none,

                    .mips,
                    .mipsel,
                    => |arch| initFmt("/lib/ld-musl-mips{s}{s}{s}.so.1", .{
                        if (cpu.has(.mips, .mips32r6)) "r6" else "",
                        if (arch == .mipsel) "el" else "",
                        switch (abi) {
                            .musleabi => "-sf",
                            .musleabihf => "",
                            else => return none,
                        },
                    }),

                    .mips64,
                    .mips64el,
                    => |arch| initFmt("/lib/ld-musl-mips{s}{s}{s}.so.1", .{
                        switch (abi) {
                            .muslabi64 => "64",
                            .muslabin32 => "n32",
                            else => return none,
                        },
                        if (cpu.has(.mips, .mips64r6)) "r6" else "",
                        if (arch == .mips64el) "el" else "",
                    }),

                    .powerpc => initFmt("/lib/ld-musl-powerpc{s}.so.1", .{switch (abi) {
                        .musleabi => "-sf",
                        .musleabihf => "",
                        else => return none,
                    }}),

                    .riscv32,
                    .riscv64,
                    => |arch| if (abi == .musl) initFmt("/lib/ld-musl-{s}{s}.so.1", .{
                        @tagName(arch),
                        if (cpu.has(.riscv, .d))
                            ""
                        else if (cpu.has(.riscv, .f))
                            "-sp"
                        else
                            "-sf",
                    }) else none,

                    .x86 => if (abi == .musl) init("/lib/ld-musl-i386.so.1") else none,
                    .x86_64 => initFmt("/lib/ld-musl-{s}.so.1", .{switch (abi) {
                        .musl => "x86_64",
                        .muslx32 => "x32",
                        else => return none,
                    }}),

                    else => none,
                }
            else if (abi.isGnu())
                switch (cpu.arch) {
                    // TODO: `eb` architecture support.
                    // TODO: `700` ABI support.
                    .arc => if (abi == .gnu) init("/lib/ld-linux-arc.so.2") else none,

                    .arm,
                    .armeb,
                    .thumb,
                    .thumbeb,
                    => initFmt("/lib/ld-linux{s}.so.3", .{switch (abi) {
                        .gnueabi => "",
                        .gnueabihf => "-armhf",
                        else => return none,
                    }}),

                    .aarch64,
                    .aarch64_be,
                    => |arch| if (abi == .gnu) initFmt("/lib/ld-linux-{s}.so.1", .{@tagName(arch)}) else none,

                    // TODO: `-be` architecture support.
                    .csky => initFmt("/lib/ld-linux-cskyv2{s}.so.1", .{switch (abi) {
                        .gnueabi => "",
                        .gnueabihf => "-hf",
                        else => return none,
                    }}),

                    .loongarch64 => initFmt("/lib64/ld-linux-loongarch-{s}.so.1", .{switch (abi) {
                        .gnu => "lp64d",
                        .gnuf32 => "lp64f",
                        .gnusf => "lp64s",
                        else => return none,
                    }}),

                    .m68k => if (abi == .gnu) init("/lib/ld.so.1") else none,

                    .mips,
                    .mipsel,
                    => switch (abi) {
                        .gnueabi,
                        .gnueabihf,
                        => initFmt("/lib/ld{s}.so.1", .{
                            if (cpu.has(.mips, .nan2008)) "-linux-mipsn8" else "",
                        }),
                        else => none,
                    },

                    .mips64,
                    .mips64el,
                    => initFmt("/lib{s}/ld{s}.so.1", .{
                        switch (abi) {
                            .gnuabi64 => "64",
                            .gnuabin32 => "32",
                            else => return none,
                        },
                        if (cpu.has(.mips, .nan2008)) "-linux-mipsn8" else "",
                    }),

                    .powerpc => switch (abi) {
                        .gnueabi,
                        .gnueabihf,
                        => init("/lib/ld.so.1"),
                        else => none,
                    },
                    // TODO: ELFv2 ABI (`/lib64/ld64.so.2`) opt-in support.
                    .powerpc64 => if (abi == .gnu) init("/lib64/ld64.so.1") else none,
                    .powerpc64le => if (abi == .gnu) init("/lib64/ld64.so.2") else none,

                    .riscv32,
                    .riscv64,
                    => |arch| if (abi == .gnu) initFmt("/lib/ld-linux-{s}{s}.so.1", .{
                        switch (arch) {
                            .riscv32 => "riscv32-ilp32",
                            .riscv64 => "riscv64-lp64",
                            else => unreachable,
                        },
                        if (cpu.has(.riscv, .d))
                            "d"
                        else if (cpu.has(.riscv, .f))
                            "f"
                        else
                            "",
                    }) else none,

                    .s390x => if (abi == .gnu) init("/lib/ld64.so.1") else none,

                    .sparc => if (abi == .gnu) init("/lib/ld-linux.so.2") else none,
                    .sparc64 => if (abi == .gnu) init("/lib64/ld-linux.so.2") else none,

                    .x86 => if (abi == .gnu) init("/lib/ld-linux.so.2") else none,
                    .x86_64 => switch (abi) {
                        .gnu => init("/lib64/ld-linux-x86-64.so.2"),
                        .gnux32 => init("/libx32/ld-linux-x32.so.2"),
                        else => none,
                    },

                    .xtensa => if (abi == .gnu) init("/lib/ld.so.1") else none,

                    else => none,
                }
            else
                none, // Not a known Linux libc.

            .serenity => switch (cpu.arch) {
                .aarch64,
                .riscv64,
                .x86_64,
                => init("/usr/lib/Loader.so"),
                else => none,
            },

            .dragonfly => if (cpu.arch == .x86_64) initFmt("{s}/libexec/ld-elf.so.2", .{
                if (os.version_range.semver.isAtLeast(.{ .major = 3, .minor = 8, .patch = 0 }) orelse false)
                    ""
                else
                    "/usr",
            }) else none,

            .freebsd => switch (cpu.arch) {
                .arm,
                .aarch64,
                .powerpc,
                .powerpc64,
                .powerpc64le,
                .riscv64,
                .x86,
                .x86_64,
                => initFmt("{s}/libexec/ld-elf.so.1", .{
                    if (os.version_range.semver.isAtLeast(.{ .major = 6, .minor = 0, .patch = 0 }) orelse false)
                        ""
                    else
                        "/usr",
                }),
                else => none,
            },

            .netbsd => switch (cpu.arch) {
                .arm,
                .armeb,
                .aarch64,
                .aarch64_be,
                .m68k,
                .mips,
                .mipsel,
                .mips64,
                .mips64el,
                .powerpc,
                .sparc,
                .sparc64,
                .x86,
                .x86_64,
                => init("/libexec/ld.elf_so"),
                else => none,
            },

            .openbsd => switch (cpu.arch) {
                .arm,
                .aarch64,
                .mips64,
                .mips64el,
                .powerpc,
                .powerpc64,
                .riscv64,
                .sparc64,
                .x86,
                .x86_64,
                => init("/usr/libexec/ld.so"),
                else => none,
            },

            .driverkit,
            .ios,
            .macos,
            .tvos,
            .visionos,
            .watchos,
            => switch (cpu.arch) {
                .aarch64,
                .x86_64,
                => init("/usr/lib/dyld"),
                else => none,
            },

            .illumos,
            => switch (cpu.arch) {
                .x86,
                .x86_64,
                => initFmt("/lib/{s}ld.so.1", .{if (ptrBitWidth_cpu_abi(cpu, .none) == 64) "64/" else ""}),
                else => none,
            },

            .solaris,
            => switch (cpu.arch) {
                .sparc64,
                .x86_64,
                => initFmt("/lib/{s}ld.so.1", .{if (ptrBitWidth_cpu_abi(cpu, .none) == 64) "64/" else ""}),
                else => none,
            },

            // Operating systems in this list have been verified as not having a standard
            // dynamic linker path.
            .freestanding,
            .other,

            .contiki,
            .hermit,

            .aix,
            .plan9,
            .rtems,
            .zos,

            .uefi,
            .windows,

            .emscripten,
            .wasi,

            .amdhsa,
            .amdpal,
            .cuda,
            .mesa3d,
            .nvcl,
            .opencl,
            .opengl,
            .vulkan,
            => none,

            // TODO go over each item in this list and either move it to the above list, or
            // implement the standard dynamic linker path code for it.
            .ps3,
            .ps4,
            .ps5,
            => none,
        } catch unreachable;
    }
};

pub fn standardDynamicLinkerPath(target: *const Target) DynamicLinker {
    return DynamicLinker.standard(target.cpu, target.os, target.abi);
}

pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 {
    return ptrBitWidth_arch_abi(cpu.arch, abi);
}

pub fn ptrBitWidth_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) u16 {
    switch (abi) {
        .gnux32, .muslx32, .gnuabin32, .muslabin32, .ilp32 => return 32,
        .gnuabi64, .muslabi64 => return 64,
        else => {},
    }
    return switch (cpu_arch) {
        .avr,
        .msp430,
        => 16,

        .arc,
        .arm,
        .armeb,
        .csky,
        .hexagon,
        .m68k,
        .mips,
        .mipsel,
        .or1k,
        .powerpc,
        .powerpcle,
        .riscv32,
        .thumb,
        .thumbeb,
        .x86,
        .xcore,
        .nvptx,
        .kalimba,
        .lanai,
        .wasm32,
        .sparc,
        .spirv32,
        .loongarch32,
        .xtensa,
        .propeller,
        => 32,

        .aarch64,
        .aarch64_be,
        .mips64,
        .mips64el,
        .powerpc64,
        .powerpc64le,
        .riscv64,
        .x86_64,
        .nvptx64,
        .wasm64,
        .amdgcn,
        .bpfel,
        .bpfeb,
        .sparc64,
        .s390x,
        .ve,
        .spirv64,
        .loongarch64,
        => 64,
    };
}

pub fn ptrBitWidth(target: *const Target) u16 {
    return ptrBitWidth_cpu_abi(target.cpu, target.abi);
}

pub fn stackAlignment(target: *const Target) u16 {
    // Overrides for when the stack alignment is not equal to the pointer width.
    switch (target.cpu.arch) {
        .m68k,
        => return 2,
        .amdgcn,
        => return 4,
        .arm,
        .armeb,
        .thumb,
        .thumbeb,
        .lanai,
        .mips,
        .mipsel,
        .sparc,
        => return 8,
        .aarch64,
        .aarch64_be,
        .bpfeb,
        .bpfel,
        .loongarch32,
        .loongarch64,
        .mips64,
        .mips64el,
        .sparc64,
        .ve,
        .wasm32,
        .wasm64,
        => return 16,
        // Some of the following prongs should really be testing the ABI, but our current `Abi` enum
        // can't handle that level of nuance yet.
        .powerpc64,
        .powerpc64le,
        => if (target.os.tag == .linux or target.os.tag == .aix) return 16,
        .riscv32,
        .riscv64,
        => if (!target.cpu.has(.riscv, .e)) return 16,
        .x86 => if (target.os.tag != .windows and target.os.tag != .uefi) return 16,
        .x86_64 => return 16,
        else => {},
    }

    return @divExact(target.ptrBitWidth(), 8);
}

/// Default signedness of `char` for the native C compiler for this target
/// Note that char signedness is implementation-defined and many compilers provide
/// an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char
pub fn cCharSignedness(target: *const Target) std.builtin.Signedness {
    if (target.os.tag.isDarwin() or target.os.tag == .windows or target.os.tag == .uefi) return .signed;

    return switch (target.cpu.arch) {
        .arm,
        .armeb,
        .thumb,
        .thumbeb,
        .aarch64,
        .aarch64_be,
        .arc,
        .csky,
        .hexagon,
        .msp430,
        .powerpc,
        .powerpcle,
        .powerpc64,
        .powerpc64le,
        .s390x,
        .riscv32,
        .riscv64,
        .xcore,
        .xtensa,
        => .unsigned,
        else => .signed,
    };
}

pub const CType = enum {
    char,
    short,
    ushort,
    int,
    uint,
    long,
    ulong,
    longlong,
    ulonglong,
    float,
    double,
    longdouble,
};

pub fn cTypeByteSize(t: *const Target, c_type: CType) u16 {
    return switch (c_type) {
        .char,
        .short,
        .ushort,
        .int,
        .uint,
        .long,
        .ulong,
        .longlong,
        .ulonglong,
        .float,
        .double,
        => @divExact(cTypeBitSize(t, c_type), 8),

        .longdouble => switch (cTypeBitSize(t, c_type)) {
            16 => 2,
            32 => 4,
            64 => 8,
            80 => @intCast(std.mem.alignForward(usize, 10, cTypeAlignment(t, .longdouble))),
            128 => 16,
            else => unreachable,
        },
    };
}

pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 {
    switch (target.os.tag) {
        .freestanding, .other => switch (target.cpu.arch) {
            .msp430 => switch (c_type) {
                .char => return 8,
                .short, .ushort, .int, .uint => return 16,
                .float, .long, .ulong => return 32,
                .longlong, .ulonglong, .double, .longdouble => return 64,
            },
            .avr => switch (c_type) {
                .char => return 8,
                .short, .ushort, .int, .uint => return 16,
                .long, .ulong, .float, .double, .longdouble => return 32,
                .longlong, .ulonglong => return 64,
            },
            .mips64, .mips64el => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => switch (target.abi) {
                    .gnuabin32, .muslabin32 => return 32,
                    else => return 64,
                },
                .longlong, .ulonglong, .double => return 64,
                .longdouble => return 128,
            },
            .x86_64 => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => switch (target.abi) {
                    .gnux32, .muslx32 => return 32,
                    else => return 64,
                },
                .longlong, .ulonglong, .double => return 64,
                .longdouble => return 80,
            },
            else => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => return target.ptrBitWidth(),
                .longlong, .ulonglong, .double => return 64,
                .longdouble => switch (target.cpu.arch) {
                    .x86 => switch (target.abi) {
                        .android => return 64,
                        else => return 80,
                    },

                    .powerpc,
                    .powerpcle,
                    .powerpc64,
                    .powerpc64le,
                    => switch (target.abi) {
                        .musl,
                        .muslabin32,
                        .muslabi64,
                        .musleabi,
                        .musleabihf,
                        .muslx32,
                        => return 64,
                        else => return 128,
                    },

                    .riscv32,
                    .riscv64,
                    .aarch64,
                    .aarch64_be,
                    .s390x,
                    .sparc64,
                    .wasm32,
                    .wasm64,
                    .loongarch32,
                    .loongarch64,
                    .ve,
                    => return 128,

                    else => return 64,
                },
            },
        },

        .fuchsia,
        .hermit,

        .aix,
        .haiku,
        .hurd,
        .linux,
        .plan9,
        .rtems,
        .serenity,
        .zos,

        .freebsd,
        .dragonfly,
        .netbsd,
        .openbsd,

        .illumos,
        .solaris,

        .wasi,
        .emscripten,
        => switch (target.cpu.arch) {
            .msp430 => switch (c_type) {
                .char => return 8,
                .short, .ushort, .int, .uint => return 16,
                .long, .ulong, .float => return 32,
                .longlong, .ulonglong, .double, .longdouble => return 64,
            },
            .avr => switch (c_type) {
                .char => return 8,
                .short, .ushort, .int, .uint => return 16,
                .long, .ulong, .float, .double, .longdouble => return 32,
                .longlong, .ulonglong => return 64,
            },
            .mips64, .mips64el => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => switch (target.abi) {
                    .gnuabin32, .muslabin32 => return 32,
                    else => return 64,
                },
                .longlong, .ulonglong, .double => return 64,
                .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
            },
            .x86_64 => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => switch (target.abi) {
                    .gnux32, .muslx32 => return 32,
                    else => return 64,
                },
                .longlong, .ulonglong, .double => return 64,
                .longdouble => return 80,
            },
            else => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => return target.ptrBitWidth(),
                .longlong, .ulonglong, .double => return 64,
                .longdouble => switch (target.cpu.arch) {
                    .x86 => switch (target.abi) {
                        .android => return 64,
                        else => return 80,
                    },

                    .powerpc,
                    .powerpcle,
                    => switch (target.abi) {
                        .musl,
                        .muslabin32,
                        .muslabi64,
                        .musleabi,
                        .musleabihf,
                        .muslx32,
                        => return 64,
                        else => switch (target.os.tag) {
                            .aix, .freebsd, .netbsd, .openbsd => return 64,
                            else => return 128,
                        },
                    },

                    .powerpc64,
                    .powerpc64le,
                    => switch (target.abi) {
                        .musl,
                        .muslabin32,
                        .muslabi64,
                        .musleabi,
                        .musleabihf,
                        .muslx32,
                        => return 64,
                        else => switch (target.os.tag) {
                            .aix, .freebsd, .openbsd => return 64,
                            else => return 128,
                        },
                    },

                    .riscv32,
                    .riscv64,
                    .aarch64,
                    .aarch64_be,
                    .s390x,
                    .mips64,
                    .mips64el,
                    .sparc64,
                    .wasm32,
                    .wasm64,
                    .loongarch32,
                    .loongarch64,
                    .ve,
                    => return 128,

                    else => return 64,
                },
            },
        },

        .windows, .uefi => switch (target.cpu.arch) {
            .x86 => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => return 32,
                .longlong, .ulonglong, .double => return 64,
                .longdouble => switch (target.abi) {
                    .gnu, .ilp32, .cygnus => return 80,
                    else => return 64,
                },
            },
            .x86_64 => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => switch (target.abi) {
                    .cygnus => return 64,
                    else => return 32,
                },
                .longlong, .ulonglong, .double => return 64,
                .longdouble => switch (target.abi) {
                    .gnu, .ilp32, .cygnus => return 80,
                    else => return 64,
                },
            },
            else => switch (c_type) {
                .char => return 8,
                .short, .ushort => return 16,
                .int, .uint, .float => return 32,
                .long, .ulong => return 32,
                .longlong, .ulonglong, .double => return 64,
                .longdouble => return 64,
            },
        },

        .driverkit,
        .ios,
        .macos,
        .tvos,
        .visionos,
        .watchos,
        => switch (c_type) {
            .char => return 8,
            .short, .ushort => return 16,
            .int, .uint, .float => return 32,
            .long, .ulong => switch (target.cpu.arch) {
                .x86_64 => return 64,
                else => switch (target.abi) {
                    .ilp32 => return 32,
                    else => return 64,
                },
            },
            .longlong, .ulonglong, .double => return 64,
            .longdouble => switch (target.cpu.arch) {
                .x86_64 => return 80,
                else => return 64,
            },
        },

        .nvcl, .cuda => switch (c_type) {
            .char => return 8,
            .short, .ushort => return 16,
            .int, .uint, .float => return 32,
            .long, .ulong => switch (target.cpu.arch) {
                .nvptx => return 32,
                .nvptx64 => return 64,
                else => return 64,
            },
            .longlong, .ulonglong, .double => return 64,
            .longdouble => return 64,
        },

        .amdhsa, .amdpal, .mesa3d => switch (c_type) {
            .char => return 8,
            .short, .ushort => return 16,
            .int, .uint, .float => return 32,
            .long, .ulong, .longlong, .ulonglong, .double => return 64,
            .longdouble => return 128,
        },

        .opencl, .vulkan => switch (c_type) {
            .char => return 8,
            .short, .ushort => return 16,
            .int, .uint, .float => return 32,
            .long, .ulong, .double => return 64,
            .longlong, .ulonglong => return 128,
            // Note: The OpenCL specification does not guarantee a particular size for long double,
            // but clang uses 128 bits.
            .longdouble => return 128,
        },

        .ps4, .ps5 => switch (c_type) {
            .char => return 8,
            .short, .ushort => return 16,
            .int, .uint, .float => return 32,
            .long, .ulong => return 64,
            .longlong, .ulonglong, .double => return 64,
            .longdouble => return 80,
        },

        .ps3,
        .contiki,
        .opengl,
        => @panic("specify the C integer and float type sizes for this OS"),
    }
}

pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 {
    // Overrides for unusual alignments
    switch (target.cpu.arch) {
        .avr => return 1,
        .x86 => switch (target.os.tag) {
            .windows, .uefi => switch (c_type) {
                .longlong, .ulonglong, .double => return 8,
                .longdouble => switch (target.abi) {
                    .gnu, .ilp32, .cygnus => return 4,
                    else => return 8,
                },
                else => {},
            },
            else => {},
        },
        .powerpc, .powerpcle, .powerpc64, .powerpc64le => switch (target.os.tag) {
            .aix => switch (c_type) {
                .double, .longdouble => return 4,
                else => {},
            },
            else => {},
        },
        .wasm32, .wasm64 => switch (target.os.tag) {
            .emscripten => switch (c_type) {
                .longdouble => return 8,
                else => {},
            },
            else => {},
        },
        else => {},
    }

    // Next-power-of-two-aligned, up to a maximum.
    return @min(
        std.math.ceilPowerOfTwoAssert(u16, (cTypeBitSize(target, c_type) + 7) / 8),
        @as(u16, switch (target.cpu.arch) {
            .msp430,
            => 2,

            .arc,
            .csky,
            .x86,
            .xcore,
            .or1k,
            .kalimba,
            .xtensa,
            .propeller,
            => 4,

            .arm,
            .armeb,
            .thumb,
            .thumbeb,
            .amdgcn,
            .bpfel,
            .bpfeb,
            .hexagon,
            .m68k,
            .mips,
            .mipsel,
            .sparc,
            .lanai,
            .nvptx,
            .nvptx64,
            .s390x,
            => 8,

            .aarch64,
            .aarch64_be,
            .loongarch32,
            .loongarch64,
            .mips64,
            .mips64el,
            .powerpc,
            .powerpcle,
            .powerpc64,
            .powerpc64le,
            .riscv32,
            .riscv64,
            .sparc64,
            .spirv32,
            .spirv64,
            .x86_64,
            .ve,
            .wasm32,
            .wasm64,
            => 16,

            .avr,
            => unreachable, // Handled above.
        }),
    );
}

pub fn cTypePreferredAlignment(target: *const Target, c_type: CType) u16 {
    // Overrides for unusual alignments
    switch (target.cpu.arch) {
        .arc => switch (c_type) {
            .longdouble => return 4,
            else => {},
        },
        .avr => return 1,
        .x86 => switch (target.os.tag) {
            .windows, .uefi => switch (c_type) {
                .longdouble => switch (target.abi) {
                    .gnu, .ilp32, .cygnus => return 4,
                    else => return 8,
                },
                else => {},
            },
            else => switch (c_type) {
                .longdouble => return 4,
                else => {},
            },
        },
        .wasm32, .wasm64 => switch (target.os.tag) {
            .emscripten => switch (c_type) {
                .longdouble => return 8,
                else => {},
            },
            else => {},
        },
        else => {},
    }

    // Next-power-of-two-aligned, up to a maximum.
    return @min(
        std.math.ceilPowerOfTwoAssert(u16, (cTypeBitSize(target, c_type) + 7) / 8),
        @as(u16, switch (target.cpu.arch) {
            .msp430 => 2,

            .csky,
            .xcore,
            .or1k,
            .kalimba,
            .xtensa,
            .propeller,
            => 4,

            .arc,
            .arm,
            .armeb,
            .thumb,
            .thumbeb,
            .amdgcn,
            .bpfel,
            .bpfeb,
            .hexagon,
            .x86,
            .m68k,
            .mips,
            .mipsel,
            .sparc,
            .lanai,
            .nvptx,
            .nvptx64,
            .s390x,
            => 8,

            .aarch64,
            .aarch64_be,
            .loongarch32,
            .loongarch64,
            .mips64,
            .mips64el,
            .powerpc,
            .powerpcle,
            .powerpc64,
            .powerpc64le,
            .riscv32,
            .riscv64,
            .sparc64,
            .spirv32,
            .spirv64,
            .x86_64,
            .ve,
            .wasm32,
            .wasm64,
            => 16,

            .avr,
            => unreachable, // Handled above.
        }),
    );
}

pub fn cMaxIntAlignment(target: *const Target) u16 {
    return switch (target.cpu.arch) {
        .avr => 1,

        .msp430 => 2,

        .xcore,
        .propeller,
        => 4,

        .amdgcn,
        .arm,
        .armeb,
        .thumb,
        .thumbeb,
        .lanai,
        .hexagon,
        .mips,
        .mipsel,
        .or1k,
        .powerpc,
        .powerpcle,
        .riscv32,
        .s390x,
        => 8,

        // Even LLVMABIAlignmentOfType(i128) agrees on these targets.
        .aarch64,
        .aarch64_be,
        .bpfel,
        .bpfeb,
        .mips64,
        .mips64el,
        .nvptx,
        .nvptx64,
        .powerpc64,
        .powerpc64le,
        .riscv64,
        .sparc,
        .sparc64,
        .wasm32,
        .wasm64,
        .x86,
        .x86_64,
        => 16,

        // Below this comment are unverified but based on the fact that C requires
        // int128_t to be 16 bytes aligned, it's a safe default.
        .arc,
        .csky,
        .kalimba,
        .loongarch32,
        .loongarch64,
        .m68k,
        .spirv32,
        .spirv64,
        .ve,
        .xtensa,
        => 16,
    };
}

pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention {
    return switch (target.cpu.arch) {
        .x86_64 => switch (target.os.tag) {
            .windows, .uefi => .{ .x86_64_win = .{} },
            else => .{ .x86_64_sysv = .{} },
        },
        .x86 => switch (target.os.tag) {
            .windows, .uefi => .{ .x86_win = .{} },
            else => .{ .x86_sysv = .{} },
        },
        .aarch64, .aarch64_be => if (target.os.tag.isDarwin()) cc: {
            break :cc .{ .aarch64_aapcs_darwin = .{} };
        } else switch (target.os.tag) {
            .windows => .{ .aarch64_aapcs_win = .{} },
            else => .{ .aarch64_aapcs = .{} },
        },
        .arm, .armeb, .thumb, .thumbeb => switch (target.abi.float()) {
            .soft => .{ .arm_aapcs = .{} },
            .hard => .{ .arm_aapcs_vfp = .{} },
        },
        .mips64, .mips64el => switch (target.abi) {
            .gnuabin32 => .{ .mips64_n32 = .{} },
            else => .{ .mips64_n64 = .{} },
        },
        .mips, .mipsel => .{ .mips_o32 = .{} },
        .riscv64 => .{ .riscv64_lp64 = .{} },
        .riscv32 => .{ .riscv32_ilp32 = .{} },
        .sparc64 => .{ .sparc64_sysv = .{} },
        .sparc => .{ .sparc_sysv = .{} },
        .powerpc64 => if (target.abi.isMusl())
            .{ .powerpc64_elf_v2 = .{} }
        else
            .{ .powerpc64_elf = .{} },
        .powerpc64le => .{ .powerpc64_elf_v2 = .{} },
        .powerpc, .powerpcle => switch (target.os.tag) {
            .aix => .{ .powerpc_aix = .{} },
            else => .{ .powerpc_sysv = .{} },
        },
        .wasm32, .wasm64 => .{ .wasm_mvp = .{} },
        .arc => .{ .arc_sysv = .{} },
        .avr => .avr_gnu,
        .bpfel, .bpfeb => .{ .bpf_std = .{} },
        .csky => .{ .csky_sysv = .{} },
        .hexagon => .{ .hexagon_sysv = .{} },
        .kalimba => null,
        .lanai => .{ .lanai_sysv = .{} },
        .loongarch64 => .{ .loongarch64_lp64 = .{} },
        .loongarch32 => .{ .loongarch32_ilp32 = .{} },
        .m68k => if (target.abi.isGnu() or target.abi.isMusl())
            .{ .m68k_gnu = .{} }
        else
            .{ .m68k_sysv = .{} },
        .msp430 => .{ .msp430_eabi = .{} },
        .or1k => .{ .or1k_sysv = .{} },
        .propeller => .{ .propeller_sysv = .{} },
        .s390x => .{ .s390x_sysv = .{} },
        .ve => .{ .ve_sysv = .{} },
        .xcore => .{ .xcore_xs1 = .{} },
        .xtensa => .{ .xtensa_call0 = .{} },
        .amdgcn => .{ .amdgcn_device = .{} },
        .nvptx, .nvptx64 => .nvptx_device,
        .spirv32, .spirv64 => .spirv_device,
    };
}

const Target = @This();
const std = @import("std.zig");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;

test {
    std.testing.refAllDecls(Cpu.Arch);
}