Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

Cpu

Target.Cpu
pub const Cpu = struct

File

lib/std/Target.zig:1197

Code

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

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

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

    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,

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

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

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

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

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

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

            pub const empty: Set = .{ .ints = @splat(0) };

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

            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);
            }

            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;
            }

            /// 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;
            }

            /// 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);
            }

            /// 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);
            }

            /// 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);
            }

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

                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;
                }
            }

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

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

            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);
            }
        };

        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(@backingInt(feature));
                    }
                    return x;
                }

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

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

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

    pub const Arch = enum {
        aarch64,
        aarch64_be,
        alpha,
        amdgcn,
        arc,
        arceb,
        arm,
        armeb,
        avr,
        bpfeb,
        bpfel,
        csky,
        ez80,
        hexagon,
        hppa,
        hppa64,
        kalimba,
        kvx,
        lanai,
        loongarch32,
        loongarch64,
        m68k,
        m88k,
        microblaze,
        microblazeel,
        mips,
        mipsel,
        mips64,
        mips64el,
        msp430,
        nvptx,
        nvptx64,
        or1k,
        powerpc,
        powerpcle,
        powerpc64,
        powerpc64le,
        propeller,
        riscv32,
        riscv32be,
        riscv64,
        riscv64be,
        s390x,
        sh,
        sheb,
        sparc,
        sparc64,
        spirv32,
        spirv64,
        thumb,
        thumbeb,
        ve,
        wasm32,
        wasm64,
        x86_16,
        x86,
        x86_64,
        xcore,
        xtensa,
        xtensaeb,

        /// 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 {
            aarch64,
            alpha,
            amdgcn,
            arc,
            arm,
            avr,
            bpf,
            csky,
            hexagon,
            hppa,
            kalimba,
            kvx,
            lanai,
            loongarch,
            m68k,
            m88k,
            microblaze,
            mips,
            msp430,
            nvptx,
            or1k,
            powerpc,
            propeller,
            riscv,
            s390x,
            sh,
            sparc,
            spirv,
            ve,
            wasm,
            x86,
            xcore,
            xtensa,
            z80,
        };

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

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

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

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

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

        pub inline fn isArc(arch: Arch) bool {
            return switch (arch) {
                .arc, .arceb => true,
                else => false,
            };
        }

        pub inline fn isHppa(arch: Arch) bool {
            return switch (arch) {
                .hppa, .hppa64 => true,
                else => false,
            };
        }

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

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

        pub inline fn isRISCV(arch: Arch) bool {
            return arch.isRiscv32() or arch.isRiscv64();
        }

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

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

        pub inline fn isMicroblaze(arch: Arch) bool {
            return switch (arch) {
                .microblaze, .microblazeel => true,
                else => false,
            };
        }

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

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

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

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

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

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

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

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

        pub inline fn isSh(arch: Arch) bool {
            return switch (arch) {
                .sh, .sheb => true,
                else => false,
            };
        }

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

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

        pub inline fn isXtensa(arch: Arch) bool {
            return switch (arch) {
                .xtensa, .xtensaeb => true,
                else => false,
            };
        }

        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 null;
        }

        pub fn endian(arch: Arch) std.builtin.Endian {
            return switch (arch) {
                .aarch64,
                .alpha,
                .arm,
                .arc,
                .avr,
                .bpfel,
                .csky,
                .hexagon,
                .kalimba,
                .kvx,
                .loongarch32,
                .loongarch64,
                .microblazeel,
                .mipsel,
                .mips64el,
                .msp430,
                .powerpcle,
                .powerpc64le,
                .propeller,
                .riscv32,
                .riscv64,
                .sh,
                .thumb,
                .ve,
                .wasm32,
                .wasm64,
                .x86_16,
                .x86,
                .x86_64,
                .xcore,
                .xtensa,
                .ez80,
                => .little,

                .aarch64_be,
                .arceb,
                .armeb,
                .bpfeb,
                .hppa,
                .hppa64,
                .lanai,
                .m68k,
                .m88k,
                .microblaze,
                .mips,
                .mips64,
                .or1k,
                .powerpc,
                .powerpc64,
                .riscv32be,
                .riscv64be,
                .s390x,
                .sheb,
                .thumbeb,
                .sparc,
                .sparc64,
                .xtensaeb,
                => .big,

                // GPU endianness is opaque. For now, assume little endian.
                .amdgcn,
                .nvptx,
                .nvptx64,
                .spirv32,
                .spirv64,
                => .little,
            };
        }

        /// 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,
            };
        }

        /// 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),
            };
        }

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

        /// 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",
            };
        }

        /// 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,

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

                .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},

                .x86_16_cdecl,
                .x86_16_stdcall,
                .x86_16_regparmcall,
                .x86_16_interrupt,
                => &.{.x86_16},

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

                .alpha_osf,
                => &.{.alpha},

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

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

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

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

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

                .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_interrupt,
                => &.{ .arc, .arceb },

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

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

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

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

                .hppa_elf,
                => &.{.hppa},

                .hppa64_elf,
                => &.{.hppa64},

                .kvx_lp64,
                .kvx_ilp32,
                => &.{.kvx},

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

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

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

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

                .m88k_sysv,
                => &.{.m88k},

                .microblaze_std,
                .microblaze_interrupt,
                => &.{ .microblaze, .microblazeel },

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

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

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

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

                .sh_gnu,
                .sh_renesas,
                .sh_interrupt,
                => &.{ .sh, .sheb },

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

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

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

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

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

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

                .ez80_cet,
                .ez80_tiflags,
                => &.{.ez80},
            };
        }
    };

    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) {
                .alpha => &alpha.cpu.ev4,
                .amdgcn => &amdgcn.cpu.gfx600,
                .avr => &avr.cpu.avr1,
                .hppa => &hppa.cpu.ts_1,
                .hppa64 => &hppa.cpu.pa_8000,
                .kvx => &kvx.cpu.coolidge_v1,
                .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, .riscv32be => &riscv.cpu.generic_rv32,
                .riscv64, .riscv64be => &riscv.cpu.generic_rv64,
                .sparc64 => &sparc.cpu.v9, // SPARC can only be 64-bit from v9 and up.
                .wasm32, .wasm64 => &wasm.cpu.mvp,
                .x86_16 => &x86.cpu.i86,
                .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) {
                .alpha => &alpha.cpu.ev6,
                .amdgcn => &amdgcn.cpu.gfx906,
                .arm => switch (os.tag) {
                    .@"3ds" => &arm.cpu.mpcore,
                    .vita => &arm.cpu.cortex_a9,
                    else => &arm.cpu.baseline,
                },
                .thumb => switch (os.tag) {
                    .vita => &arm.cpu.cortex_a9,
                    else => &arm.cpu.baseline,
                },
                .armeb, .thumbeb => &arm.cpu.baseline,
                .aarch64 => switch (os.tag) {
                    .haiku => &aarch64.cpu.cortex_a55,
                    .driverkit, .maccatalyst, .macos => &aarch64.cpu.apple_m1,
                    .ios, .tvos => &aarch64.cpu.apple_a7,
                    .visionos => &aarch64.cpu.apple_m2,
                    .watchos => &aarch64.cpu.apple_s4,
                    .@"switch" => &aarch64.cpu.cortex_a57,
                    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.
                .hppa => &hppa.cpu.pa_7300lc,
                .kvx => &kvx.cpu.coolidge_v2,
                .lanai => &lanai.cpu.v11, // clang does not have a generic lanai model.
                .loongarch32 => &loongarch.cpu.la32v1_0,
                .loongarch64 => &loongarch.cpu.la64v1_0,
                .m68k => &m68k.cpu.M68030,
                .mips => &mips.cpu.mips32r2,
                .mipsel => switch (os.tag) {
                    .psx => &mips.cpu.r3000a,
                    .psp => &mips.cpu.allegrex,
                    else => &mips.cpu.mips32r2,
                },
                .mips64 => switch (os.tag) {
                    .openbsd => &mips.cpu.octeon,
                    else => &mips.cpu.mips64r2,
                },
                .mips64el => &mips.cpu.mips64r2,
                .msp430 => &msp430.cpu.msp430,
                .nvptx, .nvptx64 => &nvptx.cpu.sm_52,
                .powerpc => switch (os.tag) {
                    .openbsd, .wiiu => &powerpc.cpu.@"750",
                    else => generic(arch),
                },
                .powerpc64 => switch (os.tag) {
                    .openbsd => &powerpc.cpu.pwr9,
                    else => generic(arch),
                },
                .powerpc64le => &powerpc.cpu.ppc64le,
                .riscv32, .riscv32be => &riscv.cpu.baseline_rv32,
                .riscv64, .riscv64be => &riscv.cpu.baseline_rv64,
                .s390x => &s390x.cpu.arch11,
                .sparc => switch (os.tag) {
                    .linux => &sparc.cpu.v9, // glibc does not work with 'plain' v8.
                    else => generic(arch),
                },
                .sparc64 => &sparc.cpu.ultrasparc,
                .x86 => &x86.cpu.pentium4,
                .x86_64 => switch (os.tag) {
                    .driverkit, .maccatalyst => &x86.cpu.nehalem,
                    .macos => &x86.cpu.core2,
                    .ps4 => &x86.cpu.btver2,
                    .ps5 => &x86.cpu.znver2,
                    else => generic(arch),
                },
                .xcore => &xcore.cpu.xs1b_generic,
                .xtensa => &xtensa.cpu.esp32,
                .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 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(@backingInt(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(@backingInt(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(@backingInt(feature))) return false;
        }
        return true;
    }
}