use core::fmt;
use crate::fp::{FpKind, RoundingMode};
use crate::ops::{BinOp, CmpOpExt, UnOp};
#[derive(Clone, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub struct WExpr {
pub konst: i64,
pub terms: Vec<(u8, i64)>,
}
impl WExpr {
pub fn konst(k: i64) -> Self {
WExpr {
konst: k,
terms: Vec::new(),
}
}
pub fn var(v: u8) -> Self {
WExpr {
konst: 0,
terms: vec![(v, 1)],
}
}
pub fn as_konst(&self) -> Option<i64> {
self.terms.is_empty().then_some(self.konst)
}
pub fn eval(&self, vals: &[u16]) -> i64 {
self.terms.iter().fold(self.konst, |acc, &(v, c)| {
acc.saturating_add(c.saturating_mul(i64::from(vals[v as usize])))
})
}
pub(crate) const LIMIT: i64 = 1 << 24;
fn bounded(self) -> Option<WExpr> {
let ok = |v: i64| (-Self::LIMIT..=Self::LIMIT).contains(&v);
(ok(self.konst) && self.terms.iter().all(|&(_, c)| ok(c))).then_some(self)
}
pub(crate) fn add(&self, o: &WExpr, sign: i64) -> Option<WExpr> {
let mut terms = self.terms.clone();
for &(v, c) in &o.terms {
let c = sign.checked_mul(c)?;
match terms.iter_mut().find(|(x, _)| *x == v) {
Some((_, k)) => *k = k.checked_add(c)?,
None => terms.push((v, c)),
}
}
terms.retain(|&(_, c)| c != 0);
terms.sort_unstable();
WExpr {
konst: self.konst.checked_add(sign.checked_mul(o.konst)?)?,
terms,
}
.bounded()
}
pub(crate) fn scale(&self, k: i64) -> Option<WExpr> {
let mut terms = Vec::with_capacity(self.terms.len());
for &(v, c) in &self.terms {
let c = c.checked_mul(k)?;
if c != 0 {
terms.push((v, c));
}
}
WExpr {
konst: self.konst.checked_mul(k)?,
terms,
}
.bounded()
}
pub fn display<'a>(&'a self, names: &'a [String]) -> impl fmt::Display + 'a {
WDisplay { e: self, names }
}
}
struct WDisplay<'a> {
e: &'a WExpr,
names: &'a [String],
}
impl fmt::Display for WDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for &(v, c) in &self.e.terms {
let name = self.names.get(v as usize).map_or("?", String::as_str);
let sep = if first {
if c < 0 { "-" } else { "" }
} else if c < 0 {
" - "
} else {
" + "
};
first = false;
match c.abs() {
1 => write!(f, "{sep}{name}")?,
k => write!(f, "{sep}{k} * {name}")?,
}
}
match (first, self.e.konst) {
(true, k) => write!(f, "{k}"),
(false, 0) => Ok(()),
(false, k) if k < 0 => write!(f, " - {}", -k),
(false, k) => write!(f, " + {k}"),
}
}
}
impl fmt::Debug for WExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names: Vec<String> = (0..8).map(|i| format!("w{i}")).collect();
write!(f, "{}", self.display(&names))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum WCmp {
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum WCons {
Cmp(WExpr, WCmp, WExpr),
Mod(WExpr, u16, u16),
}
impl WCons {
pub fn holds(&self, vals: &[u16]) -> bool {
match self {
WCons::Cmp(a, op, b) => {
let (x, y) = (a.eval(vals), b.eval(vals));
match op {
WCmp::Eq => x == y,
WCmp::Ne => x != y,
WCmp::Lt => x < y,
WCmp::Le => x <= y,
WCmp::Gt => x > y,
WCmp::Ge => x >= y,
}
}
WCons::Mod(a, m, r) => a.eval(vals).rem_euclid(i64::from(*m)) == i64::from(*r),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum ParamKind {
Any,
Const,
Sym,
NonConst,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct Param {
pub name: String,
pub kind: ParamKind,
pub width: WExpr,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Literal {
Int {
limbs: Vec<u64>,
negative: bool,
},
Ones,
SMin,
SMax,
LowMask(WExpr),
Bit(WExpr),
Width(WExpr),
Float {
value: FloatLit,
eb: WExpr,
},
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum FloatLit {
Zero,
NegZero,
Inf,
NegInf,
Nan,
One,
NegOne,
Two,
Half,
MinNormal,
MinSubnormal,
MaxFinite,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Rounding {
Mode(RoundingMode),
Var(u8),
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct FpNode {
pub kind: FpKind,
pub rounding: Option<Rounding>,
pub eb: WExpr,
pub sb: WExpr,
pub to: Option<(WExpr, WExpr)>,
pub args: Vec<NodeId>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum FactPred {
ZeroBits,
OneBits,
NonZero,
Disjoint,
Proves,
FpNotNan,
FpFinite,
FpNonZero,
OneUse,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum ConstPred {
IsPow2,
IsLowMask,
IsShiftedMask,
}
pub type NodeId = u16;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum RNode {
Param(u16),
Let(u16),
Lit(Literal),
Un(UnOp, NodeId),
Bin(BinOp, NodeId, NodeId),
Cmp(CmpOpExt, NodeId, NodeId),
Zext(NodeId),
Sext(NodeId),
Extract(WExpr, NodeId),
Concat(NodeId, NodeId),
Select(NodeId, NodeId, NodeId),
And(NodeId, NodeId),
Or(NodeId, NodeId),
Not(NodeId),
Fact(FactPred, NodeId, Option<NodeId>),
ConstP(ConstPred, NodeId),
Fp(FpNode),
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum Sort {
Bv(WExpr),
Bool,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum RuleKind {
Rewrite,
Identity,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RuleId(pub [u64; 2]);
impl fmt::Debug for RuleId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl fmt::Display for RuleId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:016x}{:016x}", self.0[0], self.0[1])
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct LetDef {
pub name: String,
pub value: NodeId,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Rule {
pub name: String,
pub group: usize,
pub kind: RuleKind,
pub id: RuleId,
pub width_vars: Vec<String>,
pub modes: Vec<String>,
pub constraints: Vec<WCons>,
pub params: Vec<Param>,
pub lets: Vec<LetDef>,
pub nodes: Vec<RNode>,
pub sorts: Vec<Sort>,
pub lhs: NodeId,
pub rhs: NodeId,
pub guard: Option<NodeId>,
pub decreasing: bool,
pub examples: Vec<(String, String)>,
pub float_values: bool,
pub doc: String,
pub span: (usize, usize),
pub(crate) admitted_widths: Option<Box<[u64; 9]>>,
pub(crate) closed: Box<[bool]>,
}
impl Rule {
pub fn values_format(&self, widths: &[u16]) -> Option<crate::FpFormat> {
let RNode::Fp(f) = &self.nodes[self.lhs as usize] else {
return None;
};
let (eb, sb) = match (f.kind, &f.to) {
(FpKind::Convert, Some((e, s))) => (e.eval(widths), s.eval(widths)),
_ => (f.eb.eval(widths), f.sb.eval(widths)),
};
crate::FpFormat::new(u32::try_from(eb).ok()?, u32::try_from(sb).ok()?).ok()
}
pub fn is_directed(&self) -> bool {
self.decreasing
}
pub fn admits(&self, widths: &[u16]) -> bool {
super::eval::admitted(self, widths)
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Group {
pub name: String,
pub rules: Vec<usize>,
}