use crate::{
abstr::{Abstr, BitvectorDomain, PanicBitvector, PanicResult},
bitvector::interval::SignedInterval,
concr::ConcreteBitvector,
forward::HwArith,
misc::{BitvectorBound, CBound, Join},
panic::message::{PANIC_NUM_DIV_BY_ZERO, PANIC_NUM_NO_PANIC, PANIC_NUM_REM_BY_ZERO},
};
use super::DualInterval;
impl<B: BitvectorBound> HwArith for DualInterval<B> {
type DivRemResult = PanicResult<Self>;
fn arith_neg(self) -> Self {
Self::from_value(ConcreteBitvector::zero(self.bound())).sub(self)
}
fn add(self, rhs: Self) -> Self {
Self::resolve_by_wrapping(self, rhs, |a, b| a.hw_add(b))
}
fn sub(self, rhs: Self) -> Self {
Self::resolve_by_wrapping(self, rhs, |a, b| a.hw_sub(b))
}
fn mul(self, rhs: Self) -> Self {
Self::resolve_by_wrapping(self, rhs, |a, b| a.hw_mul(b))
}
fn udiv(self, rhs: Self) -> PanicResult<Self> {
let result = Self::resolve_by_unsigned(self, rhs, |a, b| a.hw_udiv(b));
let zero = ConcreteBitvector::zero(self.bound());
let may_panic = rhs.contains_value(&zero);
let must_panic = rhs.concrete_value() == Some(zero);
construct_panic_result(result, may_panic, must_panic, PANIC_NUM_DIV_BY_ZERO)
}
fn sdiv(self, rhs: Self) -> PanicResult<Self> {
assert_eq!(self.bound(), rhs.bound());
let bound = self.bound();
let mut results = Vec::new();
let (dividend_near_half, dividend_far_half) = self.opt_halves();
let (divisor_near_half, divisor_far_half) = rhs.opt_halves();
if let Some(divisor_near_half) = divisor_near_half {
let mut divisor_min = divisor_near_half.min();
let divisor_max = divisor_near_half.max();
if divisor_max.is_zero() {
results.push(SignedInterval::from_value(
ConcreteBitvector::new_umax(bound).as_signed(),
));
} else {
if divisor_min.is_zero() {
results.push(SignedInterval::from_value(
ConcreteBitvector::new_umax(bound).as_signed(),
));
divisor_min = ConcreteBitvector::one(bound);
}
if let Some(dividend_near_half) = dividend_near_half {
let part_near_min =
(dividend_near_half.min().as_signed() / divisor_max.as_signed()).result;
let part_near_max =
(dividend_near_half.max().as_signed() / divisor_min.as_signed()).result;
results.push(SignedInterval::new(part_near_min, part_near_max));
}
if let Some(dividend_far_half) = dividend_far_half {
let part_far_min =
(dividend_far_half.min().as_signed() / divisor_min.as_signed()).result;
let part_far_max =
(dividend_far_half.max().as_signed() / divisor_max.as_signed()).result;
results.push(SignedInterval::new(part_far_min, part_far_max));
}
}
}
if let Some(divisor_far_half) = divisor_far_half {
fn causes_overflow<B: BitvectorBound>(
lhs: ConcreteBitvector<B>,
rhs: ConcreteBitvector<B>,
) -> bool {
lhs == ConcreteBitvector::new_overhalf(lhs.bound())
&& rhs == ConcreteBitvector::new_umax(rhs.bound())
}
let divisor_min = divisor_far_half.min().as_signed();
let divisor_max = divisor_far_half.max().as_signed();
if let Some(dividend_near_half) = dividend_near_half {
let part_near_min = (dividend_near_half.max().as_signed() / divisor_max).result;
let part_near_max = (dividend_near_half.min().as_signed() / divisor_min).result;
results.push(SignedInterval::new(part_near_min, part_near_max));
}
if let Some(dividend_far_half) = dividend_far_half {
let far_min_causes_overflow =
causes_overflow(dividend_far_half.max(), divisor_min.cast_bitvector());
let far_max_causes_overflow =
causes_overflow(dividend_far_half.min(), divisor_max.cast_bitvector());
if far_min_causes_overflow && far_max_causes_overflow {
results.push(SignedInterval::from_value(
ConcreteBitvector::new_overhalf(bound).as_signed(),
));
} else {
let part_far_min = if far_min_causes_overflow {
ConcreteBitvector::new_underhalf(bound).as_signed()
} else {
(dividend_far_half.max().as_signed() / divisor_min).result
};
let part_far_max = if far_max_causes_overflow {
results.push(SignedInterval::from_value(
ConcreteBitvector::new_overhalf(bound).as_signed(),
));
ConcreteBitvector::new_underhalf(bound).as_signed()
} else {
(dividend_far_half.min().as_signed() / divisor_max).result
};
results.push(SignedInterval::new(part_far_min, part_far_max));
}
}
}
let result = DualInterval::from_signed_intervals(&results);
let zero = ConcreteBitvector::zero(bound);
let may_panic_zero_division = rhs.contains_value(&zero);
let must_panic_zero_division = rhs.concrete_value() == Some(zero);
let may_panic = may_panic_zero_division;
let must_panic = must_panic_zero_division;
construct_panic_result(result, may_panic, must_panic, PANIC_NUM_DIV_BY_ZERO)
}
fn urem(self, rhs: Self) -> PanicResult<Self> {
let result = Self::resolve_by_unsigned(self, rhs, |a, b| a.hw_urem(b));
let zero = ConcreteBitvector::zero(self.bound());
let may_panic = rhs.contains_value(&zero);
let must_panic = rhs.concrete_value() == Some(zero);
construct_panic_result(result, may_panic, must_panic, PANIC_NUM_REM_BY_ZERO)
}
fn srem(self, rhs: Self) -> PanicResult<Self> {
assert_eq!(self.bound(), rhs.bound());
let bound = self.bound();
let zero = ConcreteBitvector::zero(bound);
let may_panic = rhs.contains_value(&zero);
let must_panic = rhs.concrete_value() == Some(zero);
if let Some(dividend) = self.concrete_value() {
if let Some(divisor) = rhs.concrete_value() {
let panic_result = dividend.as_signed() % divisor.as_signed();
let result = DualInterval::from_value(panic_result.result.cast_bitvector());
return PanicResult {
panic: PanicBitvector::from_concrete(panic_result.panic),
result,
};
}
}
let divisor_min = rhs.far_half.min().as_signed();
let divisor_max = rhs.near_half.max().as_signed();
let zero = ConcreteBitvector::zero(bound).as_signed();
let one = ConcreteBitvector::one(bound).as_signed();
let divisor_sign_remainder_min = if divisor_min < zero {
divisor_min + one
} else {
zero
};
let divisor_sign_remainder_max = if divisor_max > zero {
divisor_max - one
} else {
zero
};
let mut remainder_min = divisor_sign_remainder_min.min(-divisor_sign_remainder_max);
let mut remainder_max = divisor_sign_remainder_max.max(-divisor_sign_remainder_min);
if self.near_half.is_sign_bit_set() == self.far_half.is_sign_bit_set() {
if self.near_half.is_sign_bit_set() {
remainder_min = remainder_min.min(zero);
remainder_max = remainder_max.min(zero);
} else {
remainder_min = remainder_min.max(zero);
remainder_max = remainder_max.max(zero);
}
}
let dividend_min = self.far_half.min().as_signed();
let dividend_max = self.near_half.max().as_signed();
if must_panic {
remainder_min = dividend_min;
remainder_max = dividend_max;
} else if may_panic {
remainder_min = remainder_min.min(dividend_min);
remainder_max = remainder_max.max(dividend_max);
}
let result = DualInterval::from_signed_intervals(&[SignedInterval::new(
remainder_min,
remainder_max,
)]);
construct_panic_result(result, may_panic, must_panic, PANIC_NUM_REM_BY_ZERO)
}
}
fn construct_panic_result<T>(
result: T,
may_panic: bool,
must_panic: bool,
panic_msg_num: u64,
) -> PanicResult<T> {
let panic = if must_panic {
PanicBitvector::single_value(ConcreteBitvector::new(panic_msg_num, CBound))
} else if may_panic {
PanicBitvector::single_value(ConcreteBitvector::new(PANIC_NUM_NO_PANIC, CBound)).join(
&PanicBitvector::single_value(ConcreteBitvector::new(panic_msg_num, CBound)),
)
} else {
PanicBitvector::single_value(ConcreteBitvector::new(PANIC_NUM_NO_PANIC, CBound))
};
PanicResult { panic, result }
}