use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone)]
pub struct IntrinsicDesc {
pub name: String,
pub return_type: String,
pub param_types: Vec<String>,
pub attributes: Vec<IntrinsicAttribute>,
pub targets: Vec<String>,
pub category: IntrinsicCategory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntrinsicAttribute {
NoUnwind,
ReadNone,
ReadOnly,
NoMem,
ArgMemOnly,
WillReturn,
NoSync,
NoFree,
InaccessibleMemOnly,
Speculatable,
Convergent,
NoDuplicate,
}
impl IntrinsicAttribute {
pub fn as_str(&self) -> &'static str {
match self {
IntrinsicAttribute::NoUnwind => "nounwind",
IntrinsicAttribute::ReadNone => "readnone",
IntrinsicAttribute::ReadOnly => "readonly",
IntrinsicAttribute::NoMem => "inaccessiblememonly",
IntrinsicAttribute::ArgMemOnly => "argmemonly",
IntrinsicAttribute::WillReturn => "willreturn",
IntrinsicAttribute::NoSync => "nosync",
IntrinsicAttribute::NoFree => "nofree",
IntrinsicAttribute::InaccessibleMemOnly => "inaccessiblememonly",
IntrinsicAttribute::Speculatable => "speculatable",
IntrinsicAttribute::Convergent => "convergent",
IntrinsicAttribute::NoDuplicate => "noduplicate",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntrinsicCategory {
General,
VarArgs,
GC,
CodeGen,
BitManip,
OverflowArith,
SaturatingArith,
FixedPoint,
FloatingPoint,
Vector,
Matrix,
Stack,
Atomic,
Expect,
Lifetime,
Coroutine,
DebugInfo,
ExceptionHandling,
X86,
X86SSE,
X86AVX,
X86AVX2,
X86AVX512,
X86FMA,
ARM,
ARMNeon,
AArch64,
AArch64Neon,
AArch64SVE,
RISCV,
RISCVVector,
BPF,
WebAssembly,
NVPTX,
AMDGPU,
}
impl IntrinsicCategory {
pub fn as_str(&self) -> &'static str {
match self {
IntrinsicCategory::General => "general",
IntrinsicCategory::VarArgs => "varargs",
IntrinsicCategory::GC => "gc",
IntrinsicCategory::CodeGen => "codegen",
IntrinsicCategory::BitManip => "bitmanip",
IntrinsicCategory::OverflowArith => "overflow",
IntrinsicCategory::SaturatingArith => "saturating",
IntrinsicCategory::FixedPoint => "fixedpoint",
IntrinsicCategory::FloatingPoint => "float",
IntrinsicCategory::Vector => "vector",
IntrinsicCategory::Matrix => "matrix",
IntrinsicCategory::Stack => "stack",
IntrinsicCategory::Atomic => "atomic",
IntrinsicCategory::Expect => "expect",
IntrinsicCategory::Lifetime => "lifetime",
IntrinsicCategory::Coroutine => "coroutine",
IntrinsicCategory::DebugInfo => "debug",
IntrinsicCategory::ExceptionHandling => "eh",
IntrinsicCategory::X86 => "x86",
IntrinsicCategory::X86SSE => "x86.sse",
IntrinsicCategory::X86AVX => "x86.avx",
IntrinsicCategory::X86AVX2 => "x86.avx2",
IntrinsicCategory::X86AVX512 => "x86.avx512",
IntrinsicCategory::X86FMA => "x86.fma",
IntrinsicCategory::ARM => "arm",
IntrinsicCategory::ARMNeon => "arm.neon",
IntrinsicCategory::AArch64 => "aarch64",
IntrinsicCategory::AArch64Neon => "aarch64.neon",
IntrinsicCategory::AArch64SVE => "aarch64.sve",
IntrinsicCategory::RISCV => "riscv",
IntrinsicCategory::RISCVVector => "riscv.vector",
IntrinsicCategory::BPF => "bpf",
IntrinsicCategory::WebAssembly => "wasm",
IntrinsicCategory::NVPTX => "nvptx",
IntrinsicCategory::AMDGPU => "amdgpu",
}
}
}
pub struct LLVMIntrinsics {
intrinsics: Vec<IntrinsicDesc>,
by_name: HashMap<String, usize>,
}
impl LLVMIntrinsics {
pub fn new() -> Self {
let mut db = Self {
intrinsics: Vec::new(),
by_name: HashMap::new(),
};
db.populate();
db
}
fn add(&mut self, desc: IntrinsicDesc) {
let idx = self.intrinsics.len();
self.by_name.insert(desc.name.clone(), idx);
self.intrinsics.push(desc);
}
fn populate(&mut self) {
self.populate_general();
self.populate_bit_manipulation();
self.populate_overflow_arithmetic();
self.populate_saturating_arithmetic();
self.populate_floating_point();
self.populate_vector();
self.populate_x86_sse();
self.populate_x86_avx();
self.populate_x86_avx2();
self.populate_x86_avx512();
self.populate_arm_neon();
self.populate_aarch64_neon();
self.populate_riscv();
self.populate_coroutines();
self.populate_debug_info();
}
fn populate_general(&mut self) {
let nounwind = vec![IntrinsicAttribute::NoUnwind];
let nounwind_readnone = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
let nounwind_readonly = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadOnly];
self.add(IntrinsicDesc {
name: "llvm.memcpy.p0.p0.i64".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into(), "i64".into(), "i1".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.memmove.p0.p0.i64".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into(), "i64".into(), "i1".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.memset.p0.i64".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "i8".into(), "i64".into(), "i1".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.trap".into(),
return_type: "void".into(),
param_types: vec![],
attributes: vec![IntrinsicAttribute::NoUnwind],
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.debugtrap".into(),
return_type: "void".into(),
param_types: vec![],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::DebugInfo,
});
self.add(IntrinsicDesc {
name: "llvm.stackprotector".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.stackguard".into(),
return_type: "ptr".into(),
param_types: vec![],
attributes: nounwind_readnone.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.va_start".into(),
return_type: "void".into(),
param_types: vec!["ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::VarArgs,
});
self.add(IntrinsicDesc {
name: "llvm.va_end".into(),
return_type: "void".into(),
param_types: vec!["ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::VarArgs,
});
self.add(IntrinsicDesc {
name: "llvm.va_copy".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::VarArgs,
});
self.add(IntrinsicDesc {
name: "llvm.gcroot".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::GC,
});
self.add(IntrinsicDesc {
name: "llvm.returnaddress".into(),
return_type: "ptr".into(),
param_types: vec!["i32".into()],
attributes: nounwind_readnone.clone(),
targets: vec![],
category: IntrinsicCategory::CodeGen,
});
self.add(IntrinsicDesc {
name: "llvm.frameaddress".into(),
return_type: "ptr".into(),
param_types: vec!["i32".into()],
attributes: nounwind_readnone.clone(),
targets: vec![],
category: IntrinsicCategory::CodeGen,
});
self.add(IntrinsicDesc {
name: "llvm.stacksave".into(),
return_type: "ptr".into(),
param_types: vec![],
attributes: nounwind_readnone.clone(),
targets: vec![],
category: IntrinsicCategory::Stack,
});
self.add(IntrinsicDesc {
name: "llvm.stackrestore".into(),
return_type: "void".into(),
param_types: vec!["ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::Stack,
});
self.add(IntrinsicDesc {
name: "llvm.expect.i64".into(),
return_type: "i64".into(),
param_types: vec!["i64".into(), "i64".into()],
attributes: nounwind_readnone.clone(),
targets: vec![],
category: IntrinsicCategory::Expect,
});
self.add(IntrinsicDesc {
name: "llvm.assume".into(),
return_type: "void".into(),
param_types: vec!["i1".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::Expect,
});
self.add(IntrinsicDesc {
name: "llvm.lifetime.start.p0".into(),
return_type: "void".into(),
param_types: vec!["i64".into(), "ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::Lifetime,
});
self.add(IntrinsicDesc {
name: "llvm.lifetime.end.p0".into(),
return_type: "void".into(),
param_types: vec!["i64".into(), "ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::Lifetime,
});
self.add(IntrinsicDesc {
name: "llvm.invariant.start.p0".into(),
return_type: "ptr".into(),
param_types: vec!["i64".into(), "ptr".into()],
attributes: nounwind_readonly.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.invariant.end.p0".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "i64".into(), "ptr".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.threadlocal.address".into(),
return_type: "ptr".into(),
param_types: vec!["ptr".into()],
attributes: nounwind_readonly.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.prefetch".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "i32".into(), "i32".into(), "i32".into()],
attributes: nounwind.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
self.add(IntrinsicDesc {
name: "llvm.objectsize.i64.p0".into(),
return_type: "i64".into(),
param_types: vec!["ptr".into(), "i1".into(), "i1".into(), "i1".into()],
attributes: nounwind_readnone.clone(),
targets: vec![],
category: IntrinsicCategory::General,
});
}
fn populate_bit_manipulation(&mut self) {
let nr = vec![
IntrinsicAttribute::NoUnwind,
IntrinsicAttribute::ReadNone,
IntrinsicAttribute::Speculatable,
];
self.add(intrin(
"llvm.ctlz.i64",
"i64",
&["i64", "i1"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.cttz.i64",
"i64",
&["i64", "i1"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.ctpop.i64",
"i64",
&["i64"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.bswap.i64",
"i64",
&["i64"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.bitreverse.i64",
"i64",
&["i64"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.fshl.i64",
"i64",
&["i64", "i64", "i64"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.fshr.i64",
"i64",
&["i64", "i64", "i64"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.ctlz.i32",
"i32",
&["i32", "i1"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.cttz.i32",
"i32",
&["i32", "i1"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.ctpop.i32",
"i32",
&["i32"],
&nr,
IntrinsicCategory::BitManip,
));
self.add(intrin(
"llvm.bswap.i32",
"i32",
&["i32"],
&nr,
IntrinsicCategory::BitManip,
));
}
fn populate_overflow_arithmetic(&mut self) {
let nr = vec![
IntrinsicAttribute::NoUnwind,
IntrinsicAttribute::ReadNone,
IntrinsicAttribute::Speculatable,
];
for ty in &["i32", "i64"] {
for (op, cat) in &[
("sadd", "sadd"),
("uadd", "uadd"),
("ssub", "ssub"),
("usub", "usub"),
("smul", "smul"),
("umul", "umul"),
] {
let name = format!("llvm.{}.with.overflow.{}", op, ty);
self.add(IntrinsicDesc {
name,
return_type: format!("{{{}, i1}}", ty),
param_types: vec![ty.to_string(), ty.to_string()],
attributes: nr.clone(),
targets: vec![],
category: IntrinsicCategory::OverflowArith,
});
}
}
}
fn populate_saturating_arithmetic(&mut self) {
let nr = vec![
IntrinsicAttribute::NoUnwind,
IntrinsicAttribute::ReadNone,
IntrinsicAttribute::Speculatable,
];
for ty in &["i8", "i16", "i32", "i64"] {
for op in &["sadd.sat", "uadd.sat", "ssub.sat", "usub.sat"] {
let name = format!("llvm.{}.{}", op, ty);
self.add(IntrinsicDesc {
name,
return_type: ty.to_string(),
param_types: vec![ty.to_string(), ty.to_string()],
attributes: nr.clone(),
targets: vec![],
category: IntrinsicCategory::SaturatingArith,
});
}
}
}
fn populate_floating_point(&mut self) {
let nr = vec![
IntrinsicAttribute::NoUnwind,
IntrinsicAttribute::ReadNone,
IntrinsicAttribute::Speculatable,
];
for ty in &["f32", "f64"] {
for op in &[
"sqrt",
"sin",
"cos",
"pow",
"exp",
"exp2",
"log",
"log2",
"log10",
"fabs",
"floor",
"ceil",
"trunc",
"round",
"nearbyint",
"rint",
] {
let name = format!("llvm.{}.{}", op, ty);
let params: Vec<String> = if *op == "pow" {
vec![ty.to_string(), ty.to_string()]
} else {
vec![ty.to_string()]
};
self.add(IntrinsicDesc {
name,
return_type: ty.to_string(),
param_types: params,
attributes: nr.clone(),
targets: vec![],
category: IntrinsicCategory::FloatingPoint,
});
}
}
for ty in &["f32", "f64"] {
self.add(intrin(
&format!("llvm.fmuladd.{}", ty),
ty,
&[ty, ty, ty],
&nr,
IntrinsicCategory::FloatingPoint,
));
}
for ty in &["f32", "f64"] {
self.add(intrin(
&format!("llvm.copysign.{}", ty),
ty,
&[ty, ty],
&nr,
IntrinsicCategory::FloatingPoint,
));
}
for ty in &["f32", "f64"] {
self.add(intrin(
&format!("llvm.minnum.{}", ty),
ty,
&[ty, ty],
&nr,
IntrinsicCategory::FloatingPoint,
));
self.add(intrin(
&format!("llvm.maxnum.{}", ty),
ty,
&[ty, ty],
&nr,
IntrinsicCategory::FloatingPoint,
));
}
}
fn populate_vector(&mut self) {
let nr = vec![
IntrinsicAttribute::NoUnwind,
IntrinsicAttribute::ReadNone,
IntrinsicAttribute::Speculatable,
];
self.add(intrin(
"llvm.experimental.vector.reduce.add.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.mul.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.and.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.or.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.xor.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.smax.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.smin.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.umax.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.umin.v4i32",
"i32",
&["<4 x i32>"],
&nr,
IntrinsicCategory::Vector,
));
self.add(intrin(
"llvm.experimental.vector.reduce.fadd.v4f32",
"f32",
&["<4 x f32>"],
&nr,
IntrinsicCategory::Vector,
));
}
fn populate_x86_sse(&mut self) {
let x86 = vec!["x86".into(), "x86_64".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
for op in &["add", "sub", "mul", "div", "min", "max", "sqrt"] {
self.add(intrin_t(
&format!("llvm.x86.sse.{}.ps", op),
"<4 x f32>",
&["<4 x f32>", "<4 x f32>"],
&nr,
IntrinsicCategory::X86SSE,
&x86,
));
self.add(intrin_t(
&format!("llvm.x86.sse.{}.ss", op),
"<4 x f32>",
&["<4 x f32>", "<4 x f32>"],
&nr,
IntrinsicCategory::X86SSE,
&x86,
));
}
for op in &["add", "sub", "mul", "div"] {
self.add(intrin_t(
&format!("llvm.x86.sse.{}.pd", op),
"<2 x f64>",
&["<2 x f64>", "<2 x f64>"],
&nr,
IntrinsicCategory::X86SSE,
&x86,
));
}
self.add(intrin_t(
"llvm.x86.sse.cmp.ps",
"<4 x f32>",
&["<4 x f32>", "<4 x f32>", "i8"],
&nr,
IntrinsicCategory::X86SSE,
&x86,
));
self.add(intrin_t(
"llvm.x86.sse.cmp.ss",
"<4 x f32>",
&["<4 x f32>", "<4 x f32>", "i8"],
&nr,
IntrinsicCategory::X86SSE,
&x86,
));
}
fn populate_x86_avx(&mut self) {
let x86 = vec!["x86".into(), "x86_64".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
for op in &["add", "sub", "mul", "div"] {
self.add(intrin_t(
&format!("llvm.x86.avx.{}.ps.256", op),
"<8 x f32>",
&["<8 x f32>", "<8 x f32>"],
&nr,
IntrinsicCategory::X86AVX,
&x86,
));
self.add(intrin_t(
&format!("llvm.x86.avx.{}.pd.256", op),
"<4 x f64>",
&["<4 x f64>", "<4 x f64>"],
&nr,
IntrinsicCategory::X86AVX,
&x86,
));
}
}
fn populate_x86_avx2(&mut self) {
let x86 = vec!["x86".into(), "x86_64".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
self.add(intrin_t(
"llvm.x86.avx2.gather.d.ps.256",
"<8 x f32>",
&["<8 x f32>", "ptr", "<8 x i32>", "<8 x f32>", "i8"],
&nr,
IntrinsicCategory::X86AVX2,
&x86,
));
}
fn populate_x86_avx512(&mut self) {
let x86 = vec!["x86".into(), "x86_64".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
for op in &["add", "sub", "mul", "div"] {
self.add(intrin_t(
&format!("llvm.x86.avx512.{}.ps.512", op),
"<16 x f32>",
&["<16 x f32>", "<16 x f32>"],
&nr,
IntrinsicCategory::X86AVX512,
&x86,
));
self.add(intrin_t(
&format!("llvm.x86.avx512.{}.pd.512", op),
"<8 x f64>",
&["<8 x f64>", "<8 x f64>"],
&nr,
IntrinsicCategory::X86AVX512,
&x86,
));
}
}
fn populate_arm_neon(&mut self) {
let arm = vec!["arm".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
for op in &["add", "sub", "mul"] {
self.add(intrin_t(
&format!("llvm.arm.neon.{}.v4i32", op),
"<4 x i32>",
&["<4 x i32>", "<4 x i32>"],
&nr,
IntrinsicCategory::ARMNeon,
&arm,
));
}
self.add(intrin_t(
"llvm.arm.neon.vaba.v4i32",
"<4 x i32>",
&["<4 x i32>", "<4 x i32>", "<4 x i32>"],
&nr,
IntrinsicCategory::ARMNeon,
&arm,
));
self.add(intrin_t(
"llvm.arm.neon.vmla.v4i32",
"<4 x i32>",
&["<4 x i32>", "<4 x i32>", "<4 x i32>"],
&nr,
IntrinsicCategory::ARMNeon,
&arm,
));
}
fn populate_aarch64_neon(&mut self) {
let aarch64 = vec!["aarch64".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
for op in &["add", "sub", "mul", "fadd", "fsub", "fmul"] {
self.add(intrin_t(
&format!("llvm.aarch64.neon.{}.v4i32", op),
"<4 x i32>",
&["<4 x i32>", "<4 x i32>"],
&nr,
IntrinsicCategory::AArch64Neon,
&aarch64,
));
}
for op in &["fadd", "fsub", "fmul", "fdiv"] {
self.add(intrin_t(
&format!("llvm.aarch64.neon.{}.v2f64", op),
"<2 x f64>",
&["<2 x f64>", "<2 x f64>"],
&nr,
IntrinsicCategory::AArch64Neon,
&aarch64,
));
}
}
fn populate_riscv(&mut self) {
let riscv = vec!["riscv32".into(), "riscv64".into()];
let nr = vec![IntrinsicAttribute::NoUnwind, IntrinsicAttribute::ReadNone];
self.add(intrin_t(
"llvm.riscv.mul",
"i64",
&["i64", "i64"],
&nr,
IntrinsicCategory::RISCV,
&riscv,
));
self.add(intrin_t(
"llvm.riscv.mulh",
"i64",
&["i64", "i64"],
&nr,
IntrinsicCategory::RISCV,
&riscv,
));
self.add(intrin_t(
"llvm.riscv.div",
"i64",
&["i64", "i64"],
&nr,
IntrinsicCategory::RISCV,
&riscv,
));
for op in &["vadd", "vsub", "vmul"] {
self.add(intrin_t(
&format!("llvm.riscv.{}.vv", op),
"<vscale x 4 x i32>",
&["<vscale x 4 x i32>", "<vscale x 4 x i32>"],
&nr,
IntrinsicCategory::RISCVVector,
&riscv,
));
}
}
fn populate_coroutines(&mut self) {
let nw = vec![IntrinsicAttribute::NoUnwind];
for name in &[
"llvm.coro.begin",
"llvm.coro.end",
"llvm.coro.free",
"llvm.coro.destroy",
"llvm.coro.suspend",
"llvm.coro.save",
"llvm.coro.promise",
"llvm.coro.done",
"llvm.coro.frame",
"llvm.coro.subfn.addr",
] {
self.add(IntrinsicDesc {
name: name.to_string(),
return_type: "ptr".into(),
param_types: vec!["ptr".into()],
attributes: nw.clone(),
targets: vec![],
category: IntrinsicCategory::Coroutine,
});
}
}
fn populate_debug_info(&mut self) {
let nw = vec![IntrinsicAttribute::NoUnwind];
self.add(IntrinsicDesc {
name: "llvm.dbg.declare".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into(), "ptr".into()],
attributes: nw.clone(),
targets: vec![],
category: IntrinsicCategory::DebugInfo,
});
self.add(IntrinsicDesc {
name: "llvm.dbg.value".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "i64".into(), "ptr".into(), "ptr".into()],
attributes: nw.clone(),
targets: vec![],
category: IntrinsicCategory::DebugInfo,
});
self.add(IntrinsicDesc {
name: "llvm.dbg.addr".into(),
return_type: "void".into(),
param_types: vec!["ptr".into(), "ptr".into(), "ptr".into()],
attributes: nw.clone(),
targets: vec![],
category: IntrinsicCategory::DebugInfo,
});
self.add(IntrinsicDesc {
name: "llvm.dbg.label".into(),
return_type: "void".into(),
param_types: vec!["ptr".into()],
attributes: nw,
targets: vec![],
category: IntrinsicCategory::DebugInfo,
});
}
pub fn lookup(&self, name: &str) -> Option<&IntrinsicDesc> {
self.by_name.get(name).map(|&i| &self.intrinsics[i])
}
pub fn by_category(&self, category: IntrinsicCategory) -> Vec<&IntrinsicDesc> {
self.intrinsics
.iter()
.filter(|i| i.category == category)
.collect()
}
pub fn count(&self) -> usize {
self.intrinsics.len()
}
pub fn count_by_category(&self, cat: IntrinsicCategory) -> usize {
self.intrinsics.iter().filter(|i| i.category == cat).count()
}
}
fn intrin(
name: &str,
ret: &str,
params: &[&str],
attrs: &[IntrinsicAttribute],
cat: IntrinsicCategory,
) -> IntrinsicDesc {
IntrinsicDesc {
name: name.to_string(),
return_type: ret.to_string(),
param_types: params.iter().map(|s| s.to_string()).collect(),
attributes: attrs.to_vec(),
targets: vec![],
category: cat,
}
}
fn intrin_t(
name: &str,
ret: &str,
params: &[&str],
attrs: &[IntrinsicAttribute],
cat: IntrinsicCategory,
targets: &[&str],
) -> IntrinsicDesc {
IntrinsicDesc {
name: name.to_string(),
return_type: ret.to_string(),
param_types: params.iter().map(|s| s.to_string()).collect(),
attributes: attrs.to_vec(),
targets: targets.iter().map(|s| s.to_string()).collect(),
category: cat,
}
}
impl Default for LLVMIntrinsics {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intrinsic_count() {
let db = LLVMIntrinsics::new();
assert!(db.count() > 100);
}
#[test]
fn test_lookup_memcpy() {
let db = LLVMIntrinsics::new();
let desc = db.lookup("llvm.memcpy.p0.p0.i64").unwrap();
assert_eq!(desc.return_type, "void");
assert_eq!(desc.param_types.len(), 4);
}
#[test]
fn test_lookup_ctlz() {
let db = LLVMIntrinsics::new();
let desc = db.lookup("llvm.ctlz.i64").unwrap();
assert_eq!(desc.return_type, "i64");
}
#[test]
fn test_lookup_sqrt() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.sqrt.f64").is_some());
assert!(db.lookup("llvm.sqrt.f32").is_some());
}
#[test]
fn test_lookup_trap() {
let db = LLVMIntrinsics::new();
let desc = db.lookup("llvm.trap").unwrap();
assert!(desc.param_types.is_empty());
}
#[test]
fn test_category_counts() {
let db = LLVMIntrinsics::new();
assert!(db.count_by_category(IntrinsicCategory::General) > 5);
assert!(db.count_by_category(IntrinsicCategory::BitManip) > 5);
assert!(db.count_by_category(IntrinsicCategory::FloatingPoint) > 10);
}
#[test]
fn test_x86_sse() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.x86.sse.add.ps").is_some());
assert!(db.lookup("llvm.x86.sse.mul.ss").is_some());
}
#[test]
fn test_arm_neon() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.arm.neon.add.v4i32").is_some());
}
#[test]
fn test_aarch64_neon() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.aarch64.neon.fadd.v2f64").is_some());
}
#[test]
fn test_riscv() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.riscv.mul").is_some());
assert!(db.lookup("llvm.riscv.vadd.vv").is_some());
}
#[test]
fn test_coroutines() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.coro.begin").is_some());
assert!(db.lookup("llvm.coro.end").is_some());
assert!(db.lookup("llvm.coro.suspend").is_some());
}
#[test]
fn test_debug_info() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.dbg.declare").is_some());
assert!(db.lookup("llvm.dbg.value").is_some());
}
#[test]
fn test_overflow_arithmetic() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.sadd.with.overflow.i32").is_some());
assert!(db.lookup("llvm.umul.with.overflow.i64").is_some());
}
#[test]
fn test_saturating_arithmetic() {
let db = LLVMIntrinsics::new();
assert!(db.lookup("llvm.sadd.sat.i32").is_some());
assert!(db.lookup("llvm.usub.sat.i64").is_some());
}
#[test]
fn test_vector_reduce() {
let db = LLVMIntrinsics::new();
assert!(db
.lookup("llvm.experimental.vector.reduce.add.v4i32")
.is_some());
}
}