use crate::fp::FpFormat;
pub type NodeId = u32;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Ty {
Int(u16),
Float(FpFormat),
}
impl Ty {
pub fn bits(self) -> u16 {
match self {
Ty::Int(w) => w,
Ty::Float(f) => f.width().bits(),
}
}
}
impl core::fmt::Display for Ty {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match *self {
Ty::Int(w) => write!(f, "i{w}"),
Ty::Float(x) => f.write_str(float_name(x).unwrap_or("float?")),
}
}
}
pub(crate) fn float_name(f: FpFormat) -> Option<&'static str> {
Some(match (f.eb(), f.sb()) {
(5, 11) => "half",
(8, 8) => "bfloat",
(8, 24) => "float",
(11, 53) => "double",
(15, 113) => "fp128",
_ => return None,
})
}
pub(crate) fn float_type(name: &str) -> Option<FpFormat> {
Some(match name {
"half" => FpFormat::F16,
"bfloat" => FpFormat::BF16,
"float" => FpFormat::F32,
"double" => FpFormat::F64,
"fp128" => FpFormat::F128,
_ => return None,
})
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Lit {
Num(String),
Bool(bool),
Poison,
Undef,
Inf(bool),
Nan,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum IPred {
Eq,
Ne,
Ugt,
Uge,
Ult,
Ule,
Sgt,
Sge,
Slt,
Sle,
}
impl IPred {
pub(crate) fn parse(s: &str) -> Option<IPred> {
Some(match s {
"eq" => IPred::Eq,
"ne" => IPred::Ne,
"ugt" => IPred::Ugt,
"uge" => IPred::Uge,
"ult" => IPred::Ult,
"ule" => IPred::Ule,
"sgt" => IPred::Sgt,
"sge" => IPred::Sge,
"slt" => IPred::Slt,
"sle" => IPred::Sle,
_ => return None,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum FPred {
False,
Oeq,
Ogt,
Oge,
Olt,
Ole,
One,
Ord,
Ueq,
Ugt,
Uge,
Ult,
Ule,
Une,
Uno,
True,
}
impl FPred {
pub(crate) fn parse(s: &str) -> Option<FPred> {
Some(match s {
"false" => FPred::False,
"oeq" => FPred::Oeq,
"ogt" => FPred::Ogt,
"oge" => FPred::Oge,
"olt" => FPred::Olt,
"ole" => FPred::Ole,
"one" => FPred::One,
"ord" => FPred::Ord,
"ueq" => FPred::Ueq,
"ugt" => FPred::Ugt,
"uge" => FPred::Uge,
"ult" => FPred::Ult,
"ule" => FPred::Ule,
"une" => FPred::Une,
"uno" => FPred::Uno,
"true" => FPred::True,
_ => return None,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Intrinsic {
UMin,
UMax,
SMin,
SMax,
Abs,
Ctpop,
Ctlz,
Cttz,
Bswap,
BitReverse,
Fshl,
Fshr,
UAddSat,
USubSat,
SAddSat,
SSubSat,
Assume,
FAbs,
CopySign,
Sqrt,
Fma,
FMulAdd,
MinNum,
MaxNum,
Minimum,
Maximum,
MinimumNum,
MaximumNum,
Floor,
Ceil,
Trunc,
Round,
RoundEven,
Rint,
NearbyInt,
}
impl Intrinsic {
pub(crate) fn parse(name: &str) -> Option<Intrinsic> {
let base = name.strip_prefix("llvm.")?;
let known: &[(&str, Intrinsic)] = &[
("umin", Intrinsic::UMin),
("umax", Intrinsic::UMax),
("smin", Intrinsic::SMin),
("smax", Intrinsic::SMax),
("abs", Intrinsic::Abs),
("ctpop", Intrinsic::Ctpop),
("ctlz", Intrinsic::Ctlz),
("cttz", Intrinsic::Cttz),
("bswap", Intrinsic::Bswap),
("bitreverse", Intrinsic::BitReverse),
("fshl", Intrinsic::Fshl),
("fshr", Intrinsic::Fshr),
("uadd.sat", Intrinsic::UAddSat),
("usub.sat", Intrinsic::USubSat),
("sadd.sat", Intrinsic::SAddSat),
("ssub.sat", Intrinsic::SSubSat),
("assume", Intrinsic::Assume),
("fabs", Intrinsic::FAbs),
("copysign", Intrinsic::CopySign),
("sqrt", Intrinsic::Sqrt),
("fma", Intrinsic::Fma),
("fmuladd", Intrinsic::FMulAdd),
("minnum", Intrinsic::MinNum),
("maxnum", Intrinsic::MaxNum),
("minimum", Intrinsic::Minimum),
("maximum", Intrinsic::Maximum),
("minimumnum", Intrinsic::MinimumNum),
("maximumnum", Intrinsic::MaximumNum),
("floor", Intrinsic::Floor),
("ceil", Intrinsic::Ceil),
("trunc", Intrinsic::Trunc),
("round", Intrinsic::Round),
("roundeven", Intrinsic::RoundEven),
("rint", Intrinsic::Rint),
("nearbyint", Intrinsic::NearbyInt),
];
known
.iter()
.filter(|(k, _)| {
base == *k
|| base
.strip_prefix(k)
.is_some_and(|rest| rest.starts_with('.'))
})
.max_by_key(|(k, _)| k.len())
.map(|&(_, i)| i)
}
pub(crate) fn is_float(self) -> bool {
matches!(
self,
Intrinsic::FAbs
| Intrinsic::CopySign
| Intrinsic::Sqrt
| Intrinsic::Fma
| Intrinsic::FMulAdd
| Intrinsic::MinNum
| Intrinsic::MaxNum
| Intrinsic::Minimum
| Intrinsic::Maximum
| Intrinsic::MinimumNum
| Intrinsic::MaximumNum
| Intrinsic::Floor
| Intrinsic::Ceil
| Intrinsic::Trunc
| Intrinsic::Round
| Intrinsic::RoundEven
| Intrinsic::Rint
| Intrinsic::NearbyInt
)
}
pub(crate) fn arity(self) -> usize {
match self {
Intrinsic::Ctpop
| Intrinsic::Bswap
| Intrinsic::BitReverse
| Intrinsic::Assume
| Intrinsic::FAbs
| Intrinsic::Sqrt
| Intrinsic::Floor
| Intrinsic::Ceil
| Intrinsic::Trunc
| Intrinsic::Round
| Intrinsic::RoundEven
| Intrinsic::Rint
| Intrinsic::NearbyInt => 1,
Intrinsic::Fshl | Intrinsic::Fshr | Intrinsic::Fma | Intrinsic::FMulAdd => 3,
_ => 2,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Op {
Add,
Sub,
Mul,
UDiv,
SDiv,
URem,
SRem,
Shl,
LShr,
AShr,
And,
Or,
Xor,
FAdd,
FSub,
FMul,
FDiv,
FRem,
FNeg,
ICmp(IPred),
FCmp(FPred),
Select,
Freeze,
Trunc,
ZExt,
SExt,
FPTrunc,
FPExt,
FPToUI,
FPToSI,
UIToFP,
SIToFP,
BitCast,
Phi,
Call(Intrinsic),
Copy,
Load,
Store,
Gep(u16),
Alloca,
Resize,
}
impl Op {
pub(crate) fn binary(s: &str) -> Option<Op> {
Some(match s {
"add" => Op::Add,
"sub" => Op::Sub,
"mul" => Op::Mul,
"udiv" => Op::UDiv,
"sdiv" => Op::SDiv,
"urem" => Op::URem,
"srem" => Op::SRem,
"shl" => Op::Shl,
"lshr" => Op::LShr,
"ashr" => Op::AShr,
"and" => Op::And,
"or" => Op::Or,
"xor" => Op::Xor,
"fadd" => Op::FAdd,
"fsub" => Op::FSub,
"fmul" => Op::FMul,
"fdiv" => Op::FDiv,
"frem" => Op::FRem,
_ => return None,
})
}
pub(crate) fn cast(s: &str) -> Option<Op> {
Some(match s {
"trunc" => Op::Trunc,
"zext" => Op::ZExt,
"sext" => Op::SExt,
"fptrunc" => Op::FPTrunc,
"fpext" => Op::FPExt,
"fptoui" => Op::FPToUI,
"fptosi" => Op::FPToSI,
"uitofp" => Op::UIToFP,
"sitofp" => Op::SIToFP,
"bitcast" => Op::BitCast,
_ => return None,
})
}
}
pub mod flags {
pub const NSW: u16 = 1;
pub const NUW: u16 = 1 << 1;
pub const EXACT: u16 = 1 << 2;
pub const DISJOINT: u16 = 1 << 3;
pub const NNEG: u16 = 1 << 4;
pub const SAMESIGN: u16 = 1 << 5;
pub const NNAN: u16 = 1 << 6;
pub const NINF: u16 = 1 << 7;
pub const NSZ: u16 = 1 << 8;
pub const ARCP: u16 = 1 << 9;
pub const CONTRACT: u16 = 1 << 10;
pub const AFN: u16 = 1 << 11;
pub const REASSOC: u16 = 1 << 12;
pub const REWRITE: u16 = ARCP | CONTRACT | AFN | REASSOC;
pub fn parse(s: &str) -> Option<u16> {
Some(match s {
"nsw" => NSW,
"nuw" => NUW,
"exact" => EXACT,
"disjoint" => DISJOINT,
"nneg" => NNEG,
"samesign" => SAMESIGN,
"nnan" => NNAN,
"ninf" => NINF,
"nsz" => NSZ,
"arcp" => ARCP,
"contract" => CONTRACT,
"afn" => AFN,
"reassoc" => REASSOC,
"fast" => NNAN | NINF | NSZ | ARCP | CONTRACT | AFN | REASSOC,
_ => return None,
})
}
}
#[derive(Clone, Debug)]
pub struct Inst {
pub name: String,
pub op: Op,
pub flags: u16,
pub args: Vec<NodeId>,
pub incoming: Vec<usize>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CFun {
Neg,
Not,
Add,
Sub,
Mul,
SDiv,
UDiv,
SRem,
URem,
Shl,
AShr,
LShr,
And,
Or,
Xor,
Abs,
Log2,
Width,
Trunc,
ZExt,
SExt,
UMax,
UMin,
SMax,
SMin,
Clz,
Ctz,
Popcount,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum PFun {
And,
Or,
Not,
Cmp(IPred),
True,
IsPowerOf2,
IsPowerOf2OrZero,
IsSignBit,
IsShiftedMask,
IsMask,
MaskedValueIsZero,
WillNotOverflowSignedAdd,
WillNotOverflowUnsignedAdd,
WillNotOverflowSignedSub,
WillNotOverflowUnsignedSub,
WillNotOverflowSignedMul,
WillNotOverflowUnsignedMul,
WillNotOverflowUnsignedShl,
WillNotOverflowSignedShl,
}
impl PFun {
pub(crate) fn function(s: &str) -> Option<(PFun, usize)> {
Some(match s {
"isPowerOf2" => (PFun::IsPowerOf2, 1),
"isPowerOf2OrZero" => (PFun::IsPowerOf2OrZero, 1),
"isSignBit" => (PFun::IsSignBit, 1),
"isShiftedMask" => (PFun::IsShiftedMask, 1),
"isMask" => (PFun::IsMask, 1),
"MaskedValueIsZero" => (PFun::MaskedValueIsZero, 2),
"WillNotOverflowSignedAdd" => (PFun::WillNotOverflowSignedAdd, 2),
"WillNotOverflowUnsignedAdd" => (PFun::WillNotOverflowUnsignedAdd, 2),
"WillNotOverflowSignedSub" => (PFun::WillNotOverflowSignedSub, 2),
"WillNotOverflowUnsignedSub" => (PFun::WillNotOverflowUnsignedSub, 2),
"WillNotOverflowSignedMul" => (PFun::WillNotOverflowSignedMul, 2),
"WillNotOverflowUnsignedMul" => (PFun::WillNotOverflowUnsignedMul, 2),
"WillNotOverflowUnsignedShl" => (PFun::WillNotOverflowUnsignedShl, 2),
"WillNotOverflowSignedShl" => (PFun::WillNotOverflowSignedShl, 2),
"hasOneUse" | "isConstant" => (PFun::True, 1),
_ => return None,
})
}
}
#[derive(Clone, Debug)]
pub enum Node {
Input {
name: String,
noundef: bool,
range: Option<(String, String)>,
},
Sym(String),
Lit(Lit),
CExpr(CFun, Vec<NodeId>),
Pred(PFun, Vec<NodeId>),
Inst(Inst),
}
#[derive(Clone, Debug)]
pub enum Term {
None,
Ret(Option<NodeId>),
Br(NodeId, usize, usize),
Jmp(usize),
Switch(NodeId, usize, Vec<(NodeId, usize)>),
Unreachable,
}
#[derive(Clone, Debug)]
pub struct Block {
pub name: String,
pub insts: Vec<NodeId>,
pub term: Term,
}
#[derive(Clone, Debug, Default)]
pub struct Body {
pub blocks: Vec<Block>,
pub root: Option<NodeId>,
pub ret_noundef: bool,
pub ret_range: Option<(String, String)>,
pub ret_nofpclass: u16,
pub returns_value: bool,
}
#[derive(Clone, Debug)]
pub struct Transform {
pub name: String,
pub nodes: Vec<Node>,
pub types: Vec<Option<Ty>>,
pub inputs: Vec<NodeId>,
pub consts: Vec<NodeId>,
pub pre: Option<NodeId>,
pub src: Body,
pub tgt: Body,
pub nofpclass: Vec<(NodeId, u16)>,
}
impl Transform {
pub(crate) fn new(name: String) -> Self {
Transform {
name,
nodes: Vec::new(),
types: Vec::new(),
inputs: Vec::new(),
consts: Vec::new(),
pre: None,
src: Body::default(),
tgt: Body::default(),
nofpclass: Vec::new(),
}
}
pub(crate) fn push(&mut self, n: Node, ty: Option<Ty>) -> NodeId {
self.nodes.push(n);
self.types.push(ty);
(self.nodes.len() - 1) as NodeId
}
pub fn node(&self, n: NodeId) -> &Node {
&self.nodes[n as usize]
}
pub fn label(&self, n: NodeId) -> String {
match self.node(n) {
Node::Input { name, .. } => format!("%{name}"),
Node::Sym(s) => s.clone(),
Node::Inst(i) if !i.name.is_empty() => format!("%{}", i.name),
_ => format!("#{n}"),
}
}
}