struct Cpu [src]

Fields

arch: ArchArchitecture
model: *const ModelThe CPU model to target. It has a set of features which are overridden with the features field.
features: Feature.SetAn explicit list of the entire CPU feature set. It may differ from the specific CPU model's features.

Members

Source

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 = 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); pub const empty = Set{ .ints = [1]usize{0} ** usize_count }; 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 { if (builtin.zig_backend == .stage2_x86_64 and builtin.object_format == .coff) { for (&set.ints, other_set.ints) |*set_int, other_set_int| set_int.* |= other_set_int; } else { 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 { if (builtin.zig_backend == .stage2_x86_64 and builtin.object_format == .coff) { for (&set.ints, other_set.ints) |*set_int, other_set_int| set_int.* &= ~other_set_int; } else { 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 { if (builtin.zig_backend == .stage2_x86_64 and builtin.object_format == .coff) { var result = true; for (&set.ints, other_set.ints) |*set_int, other_set_int| result = result and (set_int.* & other_set_int) == other_set_int; return result; } else { 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(@intFromEnum(feature)); } return x; } /// Returns true if the specified feature is enabled. pub fn featureSetHas(set: Set, feature: F) bool { return set.isEnabled(@intFromEnum(feature)); } /// 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; } /// 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; } }; } }; 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, nvptx, nvptx64, powerpc, powerpcle, powerpc64, powerpc64le, propeller, riscv32, riscv64, s390x, sparc, sparc64, spirv, spirv32, spirv64, ve, wasm32, wasm64, x86, x86_64, xcore, xtensa, // LLVM tags deliberately omitted: // - aarch64_32 // - amdil // - amdil64 // - dxil // - le32 // - le64 // - r600 // - hsail // - hsail64 // - renderscript32 // - renderscript64 // - shave // - sparcel // - spir // - spir64 // - tce // - tcele pub inline fn isX86(arch: Arch) bool { return switch (arch) { .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 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 switch (arch) { .riscv32, .riscv64 => 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) { .spirv, .spirv32, .spirv64 => 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 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; } 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. .spirv, .spirv32, .spirv64, .loongarch32, .loongarch64, .arc, .propeller, => .little, .armeb, .aarch64_be, .bpfeb, .m68k, .mips, .mips64, .powerpc, .powerpc64, .thumbeb, .sparc, .sparc64, .lanai, .s390x, => .big, }; } /// Returns a name that matches the lib/std/target/* source file name. pub fn genericName(arch: Arch) [:0]const u8 { return switch (arch) { .arm, .armeb, .thumb, .thumbeb => "arm", .aarch64, .aarch64_be => "aarch64", .bpfel, .bpfeb => "bpf", .loongarch32, .loongarch64 => "loongarch", .mips, .mipsel, .mips64, .mips64el => "mips", .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc", .propeller => "propeller", .riscv32, .riscv64 => "riscv", .sparc, .sparc64 => "sparc", .s390x => "s390x", .x86, .x86_64 => "x86", .nvptx, .nvptx64 => "nvptx", .wasm32, .wasm64 => "wasm", .spirv, .spirv32, .spirv64 => "spirv", else => @tagName(arch), }; } /// All CPU features Zig is aware of, sorted lexicographically by name. pub fn allFeaturesList(arch: Arch) []const Cpu.Feature { return switch (arch) { .arm, .armeb, .thumb, .thumbeb => &arm.all_features, .aarch64, .aarch64_be => &aarch64.all_features, .arc => &arc.all_features, .avr => &avr.all_features, .bpfel, .bpfeb => &bpf.all_features, .csky => &csky.all_features, .hexagon => &hexagon.all_features, .lanai => &lanai.all_features, .loongarch32, .loongarch64 => &loongarch.all_features, .m68k => &m68k.all_features, .mips, .mipsel, .mips64, .mips64el => &mips.all_features, .msp430 => &msp430.all_features, .powerpc, .powerpcle, .powerpc64, .powerpc64le => &powerpc.all_features, .amdgcn => &amdgcn.all_features, .riscv32, .riscv64 => &riscv.all_features, .sparc, .sparc64 => &sparc.all_features, .spirv, .spirv32, .spirv64 => &spirv.all_features, .s390x => &s390x.all_features, .x86, .x86_64 => &x86.all_features, .xcore => &xcore.all_features, .xtensa => &xtensa.all_features, .nvptx, .nvptx64 => &nvptx.all_features, .ve => &ve.all_features, .wasm32, .wasm64 => &wasm.all_features, else => &[0]Cpu.Feature{}, }; } /// All processors Zig is aware of, sorted lexicographically by name. pub fn allCpuModels(arch: Arch) []const *const Cpu.Model { return switch (arch) { .arc => comptime allCpusFromDecls(arc.cpu), .arm, .armeb, .thumb, .thumbeb => comptime allCpusFromDecls(arm.cpu), .aarch64, .aarch64_be => comptime allCpusFromDecls(aarch64.cpu), .avr => comptime allCpusFromDecls(avr.cpu), .bpfel, .bpfeb => comptime allCpusFromDecls(bpf.cpu), .csky => comptime allCpusFromDecls(csky.cpu), .hexagon => comptime allCpusFromDecls(hexagon.cpu), .lanai => comptime allCpusFromDecls(lanai.cpu), .loongarch32, .loongarch64 => comptime allCpusFromDecls(loongarch.cpu), .m68k => comptime allCpusFromDecls(m68k.cpu), .mips, .mipsel, .mips64, .mips64el => comptime allCpusFromDecls(mips.cpu), .msp430 => comptime allCpusFromDecls(msp430.cpu), .powerpc, .powerpcle, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu), .amdgcn => comptime allCpusFromDecls(amdgcn.cpu), .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu), .sparc, .sparc64 => comptime allCpusFromDecls(sparc.cpu), .spirv, .spirv32, .spirv64 => comptime allCpusFromDecls(spirv.cpu), .s390x => comptime allCpusFromDecls(s390x.cpu), .x86, .x86_64 => comptime allCpusFromDecls(x86.cpu), .xcore => comptime allCpusFromDecls(xcore.cpu), .xtensa => comptime allCpusFromDecls(xtensa.cpu), .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu), .ve => comptime allCpusFromDecls(ve.cpu), .wasm32, .wasm64 => comptime allCpusFromDecls(wasm.cpu), else => &[0]*const Model{}, }; } 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; } /// 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_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}, .aarch64_aapcs, .aarch64_aapcs_darwin, .aarch64_aapcs_win, .aarch64_vfabi, .aarch64_vfabi_sve, => &.{ .aarch64, .aarch64_be }, .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}, .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}, .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, => &.{ .spirv, .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 { const S = struct { const generic_model = Model{ .name = "generic", .llvm_name = null, .features = Cpu.Feature.Set.empty, }; }; return switch (arch) { .amdgcn => &amdgcn.cpu.gfx600, .arc => &arc.cpu.generic, .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic, .aarch64, .aarch64_be => &aarch64.cpu.generic, .avr => &avr.cpu.avr1, .bpfel, .bpfeb => &bpf.cpu.generic, .csky => &csky.cpu.generic, .hexagon => &hexagon.cpu.generic, .lanai => &lanai.cpu.generic, .loongarch32 => &loongarch.cpu.generic_la32, .loongarch64 => &loongarch.cpu.generic_la64, .m68k => &m68k.cpu.generic, .mips, .mipsel => &mips.cpu.mips32, .mips64, .mips64el => &mips.cpu.mips64, .msp430 => &msp430.cpu.generic, .powerpc, .powerpcle => &powerpc.cpu.ppc, .powerpc64, .powerpc64le => &powerpc.cpu.ppc64, .propeller => &propeller.cpu.p1, .riscv32 => &riscv.cpu.generic_rv32, .riscv64 => &riscv.cpu.generic_rv64, .spirv, .spirv32, .spirv64 => &spirv.cpu.generic, .sparc => &sparc.cpu.generic, .sparc64 => &sparc.cpu.v9, // 64-bit SPARC needs v9 as the baseline .s390x => &s390x.cpu.generic, .x86 => &x86.cpu.i386, .x86_64 => &x86.cpu.x86_64, .nvptx, .nvptx64 => &nvptx.cpu.sm_20, .ve => &ve.cpu.generic, .wasm32, .wasm64 => &wasm.cpu.mvp, .xcore => &xcore.cpu.generic, .xtensa => &xtensa.cpu.generic, .kalimba, => &S.generic_model, }; } /// 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.v1, .csky => &csky.cpu.ck810, // gcc/clang do not have a generic csky model. .hexagon => &hexagon.cpu.hexagonv60, // 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 std.Target.propeller.featureSetHas(cpu.features, .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 => is_spirv, }; } }