use crate::{
analysis::ssa::{ConstValue, SsaVarId},
metadata::typesystem::PointerSize,
};
#[derive(Debug, Clone, PartialEq)]
pub enum Constraint {
Equal(ConstValue),
NotEqual(ConstValue),
GreaterThan(ConstValue),
LessThan(ConstValue),
GreaterOrEqual(ConstValue),
LessOrEqual(ConstValue),
GreaterThanUnsigned(ConstValue),
LessThanUnsigned(ConstValue),
}
impl Constraint {
#[must_use]
pub fn is_satisfied_by(&self, value: &ConstValue) -> 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> {
match self {
Self::Equal(v) => Some(v),
_ => None,
}
}
#[must_use]
pub fn conflicts_with(&self, other: &Constraint, 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::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 {
pub variable: SsaVarId,
pub constraint: Constraint,
}
impl PathConstraint {
#[must_use]
pub fn equal(variable: SsaVarId, value: ConstValue) -> Self {
Self {
variable,
constraint: Constraint::Equal(value),
}
}
#[must_use]
pub fn not_equal(variable: SsaVarId, value: ConstValue) -> Self {
Self {
variable,
constraint: Constraint::NotEqual(value),
}
}
#[must_use]
pub fn less_than(variable: SsaVarId, value: ConstValue) -> Self {
Self {
variable,
constraint: Constraint::LessThan(value),
}
}
#[must_use]
pub fn greater_than(variable: SsaVarId, value: ConstValue) -> Self {
Self {
variable,
constraint: Constraint::GreaterThan(value),
}
}
#[must_use]
pub fn is_satisfied_by(&self, value: &ConstValue) -> bool {
self.constraint.is_satisfied_by(value)
}
}
#[cfg(test)]
mod tests {
use crate::{
analysis::ssa::{
constraints::{Constraint, PathConstraint},
ConstValue, SsaVarId,
},
metadata::typesystem::PointerSize,
};
#[test]
fn test_constraint_satisfied() {
let c = Constraint::Equal(ConstValue::I32(5));
assert!(c.is_satisfied_by(&ConstValue::I32(5)));
assert!(!c.is_satisfied_by(&ConstValue::I32(6)));
let c = Constraint::NotEqual(ConstValue::I32(5));
assert!(!c.is_satisfied_by(&ConstValue::I32(5)));
assert!(c.is_satisfied_by(&ConstValue::I32(6)));
let c = Constraint::GreaterThan(ConstValue::I32(5));
assert!(c.is_satisfied_by(&ConstValue::I32(10)));
assert!(!c.is_satisfied_by(&ConstValue::I32(5)));
let c = Constraint::LessThan(ConstValue::I32(10));
assert!(c.is_satisfied_by(&ConstValue::I32(5)));
assert!(!c.is_satisfied_by(&ConstValue::I32(10)));
}
#[test]
fn test_constraint_conflicts() {
let c1 = Constraint::Equal(ConstValue::I32(5));
let c2 = Constraint::Equal(ConstValue::I32(10));
assert!(c1.conflicts_with(&c2, PointerSize::Bit64));
let c3 = Constraint::NotEqual(ConstValue::I32(5));
assert!(c1.conflicts_with(&c3, PointerSize::Bit64));
let c4 = Constraint::GreaterThan(ConstValue::I32(5));
assert!(c1.conflicts_with(&c4, PointerSize::Bit64));
let c5 = Constraint::GreaterThan(ConstValue::I32(4));
assert!(!c1.conflicts_with(&c5, PointerSize::Bit64)); }
#[test]
fn test_path_constraint_satisfied() {
let var = SsaVarId::new();
let eq = PathConstraint::equal(var, ConstValue::I32(5));
assert!(eq.is_satisfied_by(&ConstValue::I32(5)));
assert!(!eq.is_satisfied_by(&ConstValue::I32(6)));
let ne = PathConstraint::not_equal(var, ConstValue::I32(5));
assert!(!ne.is_satisfied_by(&ConstValue::I32(5)));
assert!(ne.is_satisfied_by(&ConstValue::I32(6)));
}
#[test]
fn test_path_constraint_kinds() {
let var = SsaVarId::new();
assert!(PathConstraint::less_than(var, ConstValue::I32(10))
.is_satisfied_by(&ConstValue::I32(5)));
assert!(!PathConstraint::less_than(var, ConstValue::I32(10))
.is_satisfied_by(&ConstValue::I32(10)));
assert!(PathConstraint::greater_than(var, ConstValue::I32(5))
.is_satisfied_by(&ConstValue::I32(10)));
assert!(!PathConstraint::greater_than(var, ConstValue::I32(5))
.is_satisfied_by(&ConstValue::I32(5)));
}
}