mod table;
#[derive(Clone, Copy, Debug)]
pub struct Intrinsic {
pub name: &'static str,
pub feature: &'static str,
pub arity: u8,
pub imm: &'static [Imm],
pub x86_64_only: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct Imm {
pub index: u8,
pub rust_ty: &'static str,
}
impl Intrinsic {
pub fn immediate_at(&self, index: usize) -> Option<&'static Imm> {
self.imm.iter().find(|imm| usize::from(imm.index) == index)
}
}
pub fn lookup(name: &str) -> Option<&'static Intrinsic> {
table::INTRINSICS
.binary_search_by(|probe| probe.name.cmp(name))
.ok()
.map(|index| &table::INTRINSICS[index])
}
pub fn count() -> usize {
table::INTRINSICS.len()
}
pub fn all() -> &'static [Intrinsic] {
table::INTRINSICS
}
pub const TARGET_FEATURES: &[(&str, &[&str])] = &[
("abm", &["lzcnt", "popcnt"]),
("adx", &["adx"]),
("aes", &["aes"]),
("avx", &["avx"]),
("avx2", &["avx2"]),
("avx512bf16", &["avx512bf16"]),
("avx512bitalg", &["avx512bitalg"]),
("avx512bw", &["avx512bw"]),
("avx512cd", &["avx512cd"]),
("avx512dq", &["avx512dq"]),
("avx512f", &["avx512f"]),
("avx512fp16", &["avx512fp16"]),
("avx512ifma", &["avx512ifma"]),
("avx512vbmi", &["avx512vbmi"]),
("avx512vbmi2", &["avx512vbmi2"]),
("avx512vl", &["avx512vl"]),
("avx512vnni", &["avx512vnni"]),
("avx512vp2intersect", &["avx512vp2intersect"]),
("avx512vpopcntdq", &["avx512vpopcntdq"]),
("avxifma", &["avxifma"]),
("avxneconvert", &["avxneconvert"]),
("avxvnni", &["avxvnni"]),
("avxvnniint16", &["avxvnniint16"]),
("avxvnniint8", &["avxvnniint8"]),
("bmi", &["bmi1"]),
("bmi2", &["bmi2"]),
("cx16", &["cmpxchg16b"]),
("f16c", &["f16c"]),
("fma", &["fma"]),
("fxsr", &["fxsr"]),
("gfni", &["gfni"]),
("kl", &["kl"]),
("lzcnt", &["lzcnt"]),
("movbe", &["movbe"]),
("pclmul", &["pclmulqdq"]),
("popcnt", &["popcnt"]),
("rdrnd", &["rdrand"]),
("rdseed", &["rdseed"]),
("sha", &["sha"]),
("sha512", &["sha512"]),
("sm3", &["sm3"]),
("sm4", &["sm4"]),
("sse", &["sse"]),
("sse2", &["sse2"]),
("sse3", &["sse3"]),
("sse4", &["sse4.1", "sse4.2"]),
("sse4.1", &["sse4.1"]),
("sse4.2", &["sse4.2"]),
("ssse3", &["ssse3"]),
("vaes", &["vaes"]),
("vpclmulqdq", &["vpclmulqdq"]),
("widekl", &["widekl"]),
("xsave", &["xsave"]),
];
pub const UNSUPPORTED_FEATURES: &[(&str, &str)] = &[
(
"3dnow",
"3DNow! was removed from every compiler and from Rust's core::arch",
),
(
"mmx",
"Rust's core::arch has no MMX and no '__m64'; the SSE2 forms in \
<emmintrin.h> do the same work",
),
(
"sse4a",
"the SSE4a target feature is still unstable in rustc, so asking for it \
would make this a nightly-only build",
),
(
"fma4",
"Rust's core::arch has no FMA4; Intel's 'fma' is the portable one",
),
("xop", "Rust's core::arch has no XOP"),
("tbm", "the TBM target feature is still unstable in rustc"),
("rtm", "the RTM target feature is still unstable in rustc"),
];
pub fn target_features(gcc: &str) -> Option<&'static [&'static str]> {
feature_row(gcc).map(|index| TARGET_FEATURES[index].1)
}
pub fn feature_row(gcc: &str) -> Option<usize> {
let name = gcc.trim();
TARGET_FEATURES
.iter()
.position(|(n, _)| *n == name || n.replace('.', "_") == name)
}
const _: () = assert!(
TARGET_FEATURES.len() <= u8::MAX as usize,
"TARGET_FEATURES has outgrown the byte BuiltinOp::CpuSupports carries"
);
pub fn detect_features(index: u8) -> &'static [&'static str] {
TARGET_FEATURES
.get(usize::from(index))
.map_or(&[][..], |(_, rust)| *rust)
}
pub fn unsupported_feature(gcc: &str) -> Option<&'static str> {
let name = gcc.trim();
UNSUPPORTED_FEATURES
.iter()
.find(|(n, _)| *n == name)
.map(|(_, why)| *why)
}
pub fn describe_features(feature: &str) -> String {
let names: Vec<String> = feature
.split(',')
.map(str::trim)
.filter(|name| !name.is_empty())
.map(|name| format!("'{name}'"))
.collect();
match names.as_slice() {
[] => "an instruction set".to_owned(),
[one] => format!("the {one} instruction set"),
[init @ .., last] => format!("the {} and {last} instruction sets", init.join(", ")),
}
}
pub const TARGET_MACROS: &[(&str, &[&str], &[&str])] = &[
("abm", &["__ABM__"], &["lzcnt", "popcnt"]),
("adx", &["__ADX__"], &[]),
("aes", &["__AES__"], &["sse2"]),
("avx", &["__AVX__"], &["sse4.2", "xsave"]),
("avx2", &["__AVX2__"], &["avx"]),
("avx512bf16", &["__AVX512BF16__"], &["avx512bw"]),
("avx512bitalg", &["__AVX512BITALG__"], &["avx512bw"]),
("avx512bw", &["__AVX512BW__"], &["avx512f"]),
("avx512cd", &["__AVX512CD__"], &["avx512f"]),
("avx512dq", &["__AVX512DQ__"], &["avx512f"]),
("avx512f", &["__AVX512F__", "__EVEX512__"], &["avx2"]),
("avx512fp16", &["__AVX512FP16__"], &["avx512bw"]),
("avx512ifma", &["__AVX512IFMA__"], &["avx512f"]),
("avx512vbmi", &["__AVX512VBMI__"], &["avx512bw"]),
("avx512vbmi2", &["__AVX512VBMI2__"], &["avx512bw"]),
("avx512vl", &["__AVX512VL__", "__EVEX256__"], &["avx512f"]),
("avx512vnni", &["__AVX512VNNI__"], &["avx512f"]),
(
"avx512vp2intersect",
&["__AVX512VP2INTERSECT__"],
&["avx512dq"],
),
("avx512vpopcntdq", &["__AVX512VPOPCNTDQ__"], &["avx512f"]),
("avxifma", &["__AVXIFMA__"], &["avx2"]),
("avxneconvert", &["__AVXNECONVERT__"], &["avx2"]),
("avxvnni", &["__AVXVNNI__"], &["avx2"]),
("avxvnniint16", &["__AVXVNNIINT16__"], &["avx2"]),
("avxvnniint8", &["__AVXVNNIINT8__"], &["avx2"]),
("bmi", &["__BMI__"], &[]),
("bmi2", &["__BMI2__"], &[]),
("crc32", &["__CRC32__"], &[]),
("cx16", &[], &[]),
("f16c", &["__F16C__"], &["avx"]),
("fma", &["__FMA__"], &["avx"]),
("fxsr", &["__FXSR__"], &[]),
("gfni", &["__GFNI__"], &["sse2"]),
("kl", &["__KL__"], &[]),
("lzcnt", &["__LZCNT__"], &[]),
("movbe", &["__MOVBE__"], &[]),
("pclmul", &["__PCLMUL__"], &["sse2"]),
("popcnt", &["__POPCNT__"], &[]),
("rdrnd", &["__RDRND__"], &[]),
("rdseed", &["__RDSEED__"], &[]),
("sha", &["__SHA__"], &["sse2"]),
("sha512", &["__SHA512__"], &["avx"]),
("sm3", &["__SM3__"], &["avx"]),
("sm4", &["__SM4__"], &["avx"]),
("sse", &["__SSE__"], &[]),
("sse2", &["__SSE2__"], &["sse"]),
("sse3", &["__SSE3__"], &["sse2"]),
("sse4", &[], &["sse4.2"]),
("sse4.1", &["__SSE4_1__"], &["ssse3"]),
("sse4.2", &["__SSE4_2__"], &["sse4.1", "popcnt", "crc32"]),
("ssse3", &["__SSSE3__"], &["sse3"]),
("vaes", &["__VAES__"], &["avx"]),
("vpclmulqdq", &["__VPCLMULQDQ__"], &["avx", "pclmul"]),
("widekl", &["__WIDEKL__"], &["kl"]),
("xsave", &["__XSAVE__"], &[]),
];
fn macro_row(
gcc: &str,
) -> Option<&'static (
&'static str,
&'static [&'static str],
&'static [&'static str],
)> {
let name = gcc.trim();
TARGET_MACROS
.iter()
.find(|(n, _, _)| *n == name || n.replace('.', "_") == name)
}
fn implied_macros(gcc: &str, out: &mut std::collections::BTreeSet<&'static str>) {
if let Some((_, own, implies)) = macro_row(gcc) {
out.extend(own.iter().copied());
for name in *implies {
implied_macros(name, out);
}
}
}
pub fn target_macros(
baseline: &[&'static str],
names: &[&str],
) -> std::collections::BTreeSet<&'static str> {
let mut set: std::collections::BTreeSet<&'static str> = baseline.iter().copied().collect();
for name in names {
let name = name.trim();
if let Some(off) = name.strip_prefix("no-") {
let Some((off_name, _, _)) = macro_row(off) else {
continue;
};
for (row, own, _) in TARGET_MACROS {
let mut closure = std::collections::BTreeSet::new();
implies_name(row, off_name, &mut closure);
if !closure.is_empty() {
for m in *own {
set.remove(m);
}
}
}
} else {
implied_macros(name, &mut set);
}
}
set
}
fn implies_name(row: &str, target: &str, found: &mut std::collections::BTreeSet<&'static str>) {
let Some((name, _, implies)) = macro_row(row) else {
return;
};
if *name == target {
found.insert(name);
return;
}
for next in *implies {
implies_name(next, target, found);
}
}
pub fn feature_names() -> Vec<&'static str> {
TARGET_FEATURES.iter().map(|(name, _)| *name).collect()
}