feature. See also
. The project being documented here (as the example) is the Zig library itself.
Target.Cpu
pub const Cpu = struct
File
Code
pub const Cpu = struct {
arch: Arch,
model: *const Model,
features: Feature.Set,
pub const Feature = struct {
index: Set.Index = undefined,
name: []const u8 = undefined,
llvm_name: ?[:0]const u8,
description: []const u8,
dependencies: Set,
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;
}
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;
}
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);
}
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);
}
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 {
pub fn featureSet(features: []const F) Set {
var x = Set.empty;
for (features) |feature| {
x.addFeature(@backingInt(feature));
}
return x;
}
pub fn featureSetHas(set: Set, feature: F) bool {
return set.isEnabled(@backingInt(feature));
}
pub fn featureSetHasAny(set: Set, features: anytype) bool {
inline for (features) |feature| {
if (set.isEnabled(@backingInt(@as(F, feature)))) return true;
}
return false;
}
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,
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,
};
}
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,
.amdgcn,
.nvptx,
.nvptx64,
.spirv32,
.spirv64,
=> .little,
};
}
pub fn allFeaturesList(arch: Arch) []const Cpu.Feature {
return switch (arch.family()) {
inline else => |f| &@field(Target, @tagName(f)).all_features,
};
}
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;
}
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",
else => ".X",
};
}
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,
};
}
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,
.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,
};
}
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,
.hexagon => &hexagon.cpu.hexagonv68,
.hppa => &hppa.cpu.pa_7300lc,
.kvx => &kvx.cpu.coolidge_v2,
.lanai => &lanai.cpu.v11,
.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,
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),
};
}
};
pub fn baseline(arch: Arch, os: Os) Cpu {
return Model.baseline(arch, os).toCpu(arch);
}
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));
}
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;
}
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;
}
}