Unverified Commit 1e46589f by Enkelmann Committed by GitHub

Simplify more cases of trivial expressions (#346)

parent 44ec840b
...@@ -268,6 +268,25 @@ fn trivial_expression_substitution() { ...@@ -268,6 +268,25 @@ fn trivial_expression_substitution() {
rhs: Box::new(setup.rax_variable.clone()), rhs: Box::new(setup.rax_variable.clone()),
} }
); );
// Test (x - const_1) - const_2 = x - (const_1 + const_2)
let mut expr = Expression::BinOp {
lhs: Box::new(Expression::BinOp {
lhs: Box::new(setup.rax_variable.clone()),
op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(3))),
}),
op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(4))),
};
expr.substitute_trivial_operations();
assert_eq!(
expr,
Expression::BinOp {
lhs: Box::new(setup.rax_variable.clone()),
op: BinOpType::IntSub,
rhs: Box::new(Expression::Const(Bitvector::from_i64(7)))
}
);
} }
#[test] #[test]
......
use super::*; use super::*;
impl Expression { impl Expression {
/// Substitute trivial BinOp-expressions with their results, /// Substitute cases, where a binary operation can be simplified because the left and right operand are identical.
/// e.g. substitute `a or a` with `a`. fn substitute_binop_for_lhs_equal_rhs(&mut self) {
///
/// This function assumes that `self` is a `BinOp`
/// and it does not substitute trivial expressions in the two input expressions of the `BinOp`.
fn substitute_trivial_binops(&mut self) {
use BinOpType::*; use BinOpType::*;
use Expression::*; use Expression::*;
if let BinOp { op, lhs, rhs } = self { if let BinOp { op, lhs, rhs } = self {
...@@ -28,7 +24,15 @@ impl Expression { ...@@ -28,7 +24,15 @@ impl Expression {
} }
_ => (), _ => (),
} }
} else { }
}
}
/// Substitute `AND`, `OR` and `XOR` operations where one operand is a constant.
fn substitute_and_xor_or_with_constant(&mut self) {
use BinOpType::*;
use Expression::*;
if let BinOp { op, lhs, rhs } = self {
match (&**lhs, op, &**rhs) { match (&**lhs, op, &**rhs) {
(Const(bitvec), op, other) | (other, op, Const(bitvec)) (Const(bitvec), op, other) | (other, op, Const(bitvec))
if bitvec.is_zero() && matches!(op, IntOr | IntXOr | BoolOr | BoolXOr) => if bitvec.is_zero() && matches!(op, IntOr | IntXOr | BoolOr | BoolXOr) =>
...@@ -37,12 +41,51 @@ impl Expression { ...@@ -37,12 +41,51 @@ impl Expression {
*self = other.clone(); *self = other.clone();
} }
(Const(bitvec), op, other) | (other, op, Const(bitvec)) (Const(bitvec), op, other) | (other, op, Const(bitvec))
if bitvec.clone().into_bitnot().is_zero() if bitvec.clone().into_bitnot().is_zero() && matches!(op, IntAnd | BoolAnd) =>
&& matches!(op, IntAnd | BoolAnd) =>
{ {
// `a and -1 = a` since all bits of -1 are 1. // `a and -1 = a` since all bits of -1 are 1.
*self = other.clone() *self = other.clone()
} }
(Const(bitvec), BoolAnd, _other) | (_other, BoolAnd, Const(bitvec))
if bitvec.is_zero() =>
{
// `a and 0 = 0` for booleans
*self = Const(bitvec.clone());
}
(Const(bitvec), BoolAnd, other) | (other, BoolAnd, Const(bitvec))
if bitvec.is_one() =>
{
// `a and 1 = a` for booleans
*self = other.clone();
}
(Const(bitvec), BoolOr, _other) | (_other, BoolOr, Const(bitvec))
if bitvec.is_one() =>
{
// `a or 1 = 1` for booleans
*self = Const(bitvec.clone());
}
(Const(bitvec), BoolXOr, other) | (other, BoolXOr, Const(bitvec))
if bitvec.is_one() =>
{
// `a xor 1 = ¬a` for booleans
*self = UnOp {
op: UnOpType::BoolNegate,
arg: Box::new(other.clone()),
};
}
_ => (),
}
}
}
/// Simplify some comparison operations.
///
/// For example, `a == b || a < b` can be simplified to `a <= b`.
fn substitute_equivalent_comparison_ops(&mut self) {
use BinOpType::*;
use Expression::*;
if let BinOp { op, lhs, rhs } = self {
match (&**lhs, op, &**rhs) {
( (
Const(bitvec), Const(bitvec),
op, op,
...@@ -171,8 +214,7 @@ impl Expression { ...@@ -171,8 +214,7 @@ impl Expression {
rhs: lessequal_right, rhs: lessequal_right,
}, },
) if (lessequal_left == notequal_left && lessequal_right == notequal_right) ) if (lessequal_left == notequal_left && lessequal_right == notequal_right)
|| (lessequal_left == notequal_right || (lessequal_left == notequal_right && lessequal_right == notequal_left) =>
&& lessequal_right == notequal_left) =>
{ {
// `x <= y and x != y` is equivalent to `x < y ` // `x <= y and x != y` is equivalent to `x < y `
*self = Expression::BinOp { *self = Expression::BinOp {
...@@ -207,8 +249,7 @@ impl Expression { ...@@ -207,8 +249,7 @@ impl Expression {
rhs: lessequal_right, rhs: lessequal_right,
}, },
) if (lessequal_left == notequal_left && lessequal_right == notequal_right) ) if (lessequal_left == notequal_left && lessequal_right == notequal_right)
|| (lessequal_left == notequal_right || (lessequal_left == notequal_right && lessequal_right == notequal_left) =>
&& lessequal_right == notequal_left) =>
{ {
// `x <= y and x != y` is equivalent to `x < y ` // `x <= y and x != y` is equivalent to `x < y `
*self = Expression::BinOp { *self = Expression::BinOp {
...@@ -221,6 +262,78 @@ impl Expression { ...@@ -221,6 +262,78 @@ impl Expression {
} }
} }
} }
/// Simplify arithmetic operations where intermediate results can be computed because some operands are constants.
fn substitute_arithmetics_with_constants(&mut self) {
use BinOpType::*;
use Expression::*;
if let BinOp { op, lhs, rhs } = self {
match (&**lhs, op, &**rhs) {
(Const(left), op, Const(right)) if matches!(op, IntSub | IntAdd) => {
// Compute the result of arithmetics with constants
*self = Const(
left.bin_op(*op, right)
.expect("Arithmetic operation with non-matching byte sizes."),
);
}
(
BinOp {
lhs: left,
op: IntSub,
rhs: middle,
},
IntSub,
Const(const_right),
) => {
if let Const(const_middle) = &**middle {
// `(x - const_1) - const_2 = x - (const_1 + const_2)`
*self = BinOp {
lhs: left.clone(),
op: IntSub,
rhs: Box::new(Const(const_middle.bin_op(IntAdd, const_right).unwrap())),
}
}
}
(
BinOp {
lhs: left,
op: IntAdd,
rhs: middle,
},
IntAdd,
Const(const_right),
) => {
if let Const(const_middle) = &**middle {
// `(x + const_1) + const_2 = x + (const_1 + const_2)`
*self = BinOp {
lhs: left.clone(),
op: IntAdd,
rhs: Box::new(Const(const_middle.bin_op(IntAdd, const_right).unwrap())),
}
} else if let Const(const_left) = &**left {
// `(const_1 + x) + const_2 = x + (const_1 + const_2)`
*self = BinOp {
lhs: middle.clone(),
op: IntAdd,
rhs: Box::new(Const(const_left.bin_op(IntAdd, const_right).unwrap())),
}
}
}
_ => (),
}
}
}
/// Substitute trivial BinOp-expressions with their results,
/// e.g. substitute `a or a` with `a`.
///
/// This function assumes that `self` is a `BinOp`
/// and it does not substitute trivial expressions in the two input expressions of the `BinOp`.
fn substitute_trivial_binops(&mut self) {
self.substitute_binop_for_lhs_equal_rhs();
self.substitute_and_xor_or_with_constant();
self.substitute_equivalent_comparison_ops();
self.substitute_arithmetics_with_constants();
} }
/// Substitute some trivial expressions with their result. /// Substitute some trivial expressions with their result.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment