use crate::{
ir::{value::ConstValue, variable::SsaVarId},
target::Target,
PointerSize,
};
#[derive(Debug, Clone, PartialEq)]
pub enum Constraint<T: Target> {
Equal(ConstValue<T>),
NotEqual(ConstValue<T>),
GreaterThan(ConstValue<T>),
LessThan(ConstValue<T>),
GreaterOrEqual(ConstValue<T>),
LessOrEqual(ConstValue<T>),
GreaterThanUnsigned(ConstValue<T>),
LessThanUnsigned(ConstValue<T>),
}
impl<T: Target> Constraint<T> {
#[must_use]
pub fn is_satisfied_by(&self, value: &ConstValue<T>) -> bool {
match self {
Self::Equal(v) => value.ceq(v).is_some_and(|r| !r.is_zero()),
Self::NotEqual(v) => value.ceq(v).is_some_and(|r| r.is_zero()),
Self::GreaterThan(v) => value.cgt(v).is_some_and(|r| !r.is_zero()),
Self::LessThan(v) => value.clt(v).is_some_and(|r| !r.is_zero()),
Self::GreaterOrEqual(v) => value.clt(v).is_some_and(|r| r.is_zero()),
Self::LessOrEqual(v) => value.cgt(v).is_some_and(|r| r.is_zero()),
Self::GreaterThanUnsigned(v) => value.cgt_un(v).is_some_and(|r| !r.is_zero()),
Self::LessThanUnsigned(v) => value.clt_un(v).is_some_and(|r| !r.is_zero()),
}
}
#[must_use]
pub fn as_equal(&self) -> Option<&ConstValue<T>> {
match self {
Self::Equal(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn conflicts_with(&self, other: &Constraint<T>, ptr_size: PointerSize) -> bool {
match (self, other) {
(Self::Equal(a), Self::Equal(b)) => a != b,
(Self::Equal(a), Self::NotEqual(b)) | (Self::NotEqual(b), Self::Equal(a)) => a == b,
(Self::Equal(a), Self::GreaterThan(b)) | (Self::GreaterThan(b), Self::Equal(a)) => {
a.cgt(b).is_none_or(|r| r.is_zero())
}
(Self::Equal(a), Self::LessThan(b)) | (Self::LessThan(b), Self::Equal(a)) => {
a.clt(b).is_none_or(|r| r.is_zero())
}
(Self::GreaterThan(a), Self::LessThan(b))
| (Self::LessThan(b), Self::GreaterThan(a)) => {
let one: ConstValue<T> = ConstValue::I32(1);
a.add(&one, ptr_size)
.and_then(|a_plus_1| b.cgt(&a_plus_1))
.is_none_or(|r| r.is_zero())
}
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PathConstraint<T: Target> {
pub variable: SsaVarId,
pub constraint: Constraint<T>,
}
impl<T: Target> PathConstraint<T> {
#[must_use]
pub fn equal(variable: SsaVarId, value: ConstValue<T>) -> Self {
Self {
variable,
constraint: Constraint::Equal(value),
}
}
#[must_use]
pub fn not_equal(variable: SsaVarId, value: ConstValue<T>) -> Self {
Self {
variable,
constraint: Constraint::NotEqual(value),
}
}
#[must_use]
pub fn less_than(variable: SsaVarId, value: ConstValue<T>) -> Self {
Self {
variable,
constraint: Constraint::LessThan(value),
}
}
#[must_use]
pub fn greater_than(variable: SsaVarId, value: ConstValue<T>) -> Self {
Self {
variable,
constraint: Constraint::GreaterThan(value),
}
}
#[must_use]
pub fn is_satisfied_by(&self, value: &ConstValue<T>) -> bool {
self.constraint.is_satisfied_by(value)
}
}