use crate::error::*;
use crate::ir;
use std::cmp::Ordering;
use std::fmt;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Value {
Top(usize),
Value(ir::Constant),
Bottom(usize),
}
impl Value {
pub fn is_top(&self) -> bool {
matches!(self, Value::Top(_))
}
pub fn value(&self) -> Option<&ir::Constant> {
match self {
Value::Value(c) => Some(c),
_ => None,
}
}
pub fn bits(&self) -> usize {
match self {
Value::Top(bits) => *bits,
Value::Value(constant) => constant.bits(),
Value::Bottom(bits) => *bits,
}
}
/// A partial comparison that is greater when the lhs value is less than the
/// rhs value. Used as the lo value in intervals
pub fn partial_cmp_lo(&self, other: &Value) -> Result> {
Ok(match self {
Value::Top(_) => match other {
Value::Top(_) => Some(Ordering::Equal),
Value::Value(_) | Value::Bottom(_) => Some(Ordering::Greater),
},
Value::Value(lhs) => match other {
Value::Top(_) => Some(Ordering::Less),
Value::Value(rhs) => {
if lhs == rhs {
Some(Ordering::Equal)
} else if lhs.cmpltu(rhs)?.is_one() {
Some(Ordering::Greater)
} else if rhs.cmpltu(lhs)?.is_one() {
Some(Ordering::Less)
} else {
None
}
}
Value::Bottom(_) => Some(Ordering::Greater),
},
Value::Bottom(_) => match other {
Value::Top(_) | Value::Value(_) => Some(Ordering::Less),
Value::Bottom(_) => Some(Ordering::Equal),
},
})
}
/// A partial comparison that is greater when the lhs value is greater than the
/// rhs value. Used as the hi value in intervals
pub fn partial_cmp_hi(&self, other: &Value) -> Result > {
Ok(match self {
Value::Top(_) => match other {
Value::Top(_) => Some(Ordering::Equal),
Value::Value(_) | Value::Bottom(_) => Some(Ordering::Greater),
},
Value::Value(lhs) => match other {
Value::Top(_) => Some(Ordering::Less),
Value::Value(rhs) => {
if lhs == rhs {
Some(Ordering::Equal)
} else if lhs.cmpltu(rhs)?.is_one() {
Some(Ordering::Less)
} else if rhs.cmpltu(lhs)?.is_one() {
Some(Ordering::Greater)
} else {
None
}
}
Value::Bottom(_) => Some(Ordering::Greater),
},
Value::Bottom(_) => match other {
Value::Top(_) | Value::Value(_) => Some(Ordering::Less),
Value::Bottom(_) => Some(Ordering::Equal),
},
})
}
pub(crate) fn max_(&self, other: &Value) -> Result {
Ok(match self {
Value::Top(bits) => Value::Top(*bits),
Value::Value(lhs) => match other {
Value::Top(bits) => Value::Top(*bits),
Value::Value(rhs) => {
if lhs.cmpltu(rhs)?.is_one() {
other.clone()
} else {
self.clone()
}
}
Value::Bottom(_) => self.clone(),
},
Value::Bottom(_) => other.clone(),
})
}
pub fn min_(&self, other: &Value) -> Result {
Ok(match self {
Value::Top(bits) => Value::Top(*bits),
Value::Value(lhs) => match other {
Value::Top(bits) => Value::Top(*bits),
Value::Value(rhs) => {
if lhs.cmpltu(rhs)?.is_one() {
self.clone()
} else {
other.clone()
}
}
Value::Bottom(_) => self.clone(),
},
Value::Bottom(_) => other.clone(),
})
}
pub fn is_signed(&self) -> Result {
Ok(match self {
Value::Top(_) => false,
Value::Value(constant) => constant
.shr(&ir::const_((constant.bits() - 1) as u64, constant.bits()))?
.is_one(),
Value::Bottom(_) => false,
})
}
/// Returns true if both contain values, but sign bits differ
pub fn sign_overflow(&self, other: &Value) -> Result {
Ok(match self {
Value::Top(_) => false,
Value::Value(lhs) => match other {
Value::Top(_) | Value::Bottom(_) => false,
Value::Value(rhs) => {
if lhs.bits() != rhs.bits() {
return Ok(false);
}
if lhs.bits() == 1 {
return Ok(false);
}
let shift = ir::const_(lhs.bits() as u64 - 1, lhs.bits());
let lhs_bit = lhs.shr(&shift)?.trun(1)?.value_u64().unwrap();
let rhs_bit = lhs.shr(&shift)?.trun(1)?.value_u64().unwrap();
lhs_bit != rhs_bit
}
},
Value::Bottom(_) => false,
})
}
/// Returns true if both are values, and self is ltu to other
pub fn value_ltu(&self, other: &Value) -> Result {
if let Some(lhs) = self.value() {
if let Some(rhs) = other.value() {
return Ok(lhs.cmpltu(rhs)?.is_one());
}
}
Ok(false)
}
pub fn join(&self, other: &Value) -> Value {
match self {
Value::Top(bits) => Value::Top(*bits),
Value::Value(lhs) => match other {
Value::Top(bits) => Value::Top(*bits),
Value::Value(rhs) => {
if lhs == rhs {
self.clone()
} else {
Value::Top(lhs.bits())
}
}
Value::Bottom(_) => self.clone(),
},
Value::Bottom(_) => other.clone(),
}
}
pub fn binop(&self, rhs: &Value, f: F) -> Result
where
F: Fn(&ir::Constant, &ir::Constant) -> Result,
{
Ok(match self {
Value::Top(bits) => Value::Top(*bits),
Value::Value(lhs) => match rhs {
Value::Top(bits) => Value::Top(*bits),
Value::Value(rhs) => Value::Value(f(lhs, rhs)?),
Value::Bottom(_) => Value::Value(lhs.clone()),
},
Value::Bottom(_) => rhs.clone(),
})
}
pub fn extop(&self, bits: usize, f: F) -> Result
where
F: Fn(&ir::Constant) -> Result,
{
Ok(match self {
Value::Top(_) => Value::Top(bits),
Value::Value(constant) => Value::Value(f(constant)?),
Value::Bottom(_) => Value::Bottom(bits),
})
}
}
/*
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Value) -> Option {
match self {
Value::Top(_) => match other {
Value::Top(_) => Some(Ordering::Equal),
Value::Value(_) | Value::Bottom(_) => Some(Ordering::Greater),
},
Value::Value(lhs) => match other {
Value::Top(_) => Some(Ordering::Less),
Value::Value(rhs) => {
if lhs == rhs {
Some(Ordering::Equal)
} else {
None
}
}
Value::Bottom(_) => Some(Ordering::Greater),
},
Value::Bottom(_) => match other {
Value::Top(_) | Value::Value(_) => Some(Ordering::Less),
Value::Bottom(_) => Some(Ordering::Equal),
},
}
}
}
*/
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::Top(bits) => write!(f, "⊤:{}", bits),
Value::Value(value) => value.fmt(f),
Value::Bottom(bits) => write!(f, "⊥:{}", bits),
}
}
}