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,201 +24,318 @@ impl Expression { ...@@ -28,201 +24,318 @@ impl Expression {
} }
_ => (), _ => (),
} }
} else { }
match (&**lhs, op, &**rhs) { }
(Const(bitvec), op, other) | (other, op, Const(bitvec)) }
if bitvec.is_zero() && matches!(op, IntOr | IntXOr | BoolOr | BoolXOr) =>
{ /// Substitute `AND`, `OR` and `XOR` operations where one operand is a constant.
// `a or 0 = a` and `a xor 0 = a` fn substitute_and_xor_or_with_constant(&mut self) {
*self = other.clone(); use BinOpType::*;
} use Expression::*;
(Const(bitvec), op, other) | (other, op, Const(bitvec)) if let BinOp { op, lhs, rhs } = self {
if bitvec.clone().into_bitnot().is_zero() match (&**lhs, op, &**rhs) {
&& matches!(op, IntAnd | BoolAnd) => (Const(bitvec), op, other) | (other, op, Const(bitvec))
{ if bitvec.is_zero() && matches!(op, IntOr | IntXOr | BoolOr | BoolXOr) =>
// `a and -1 = a` since all bits of -1 are 1. {
*self = other.clone() // `a or 0 = a` and `a xor 0 = a`
*self = other.clone();
}
(Const(bitvec), op, other) | (other, op, Const(bitvec))
if bitvec.clone().into_bitnot().is_zero() && matches!(op, IntAnd | BoolAnd) =>
{
// `a and -1 = a` since all bits of -1 are 1.
*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),
op,
Expression::BinOp {
lhs: inner_lhs,
op: IntSub,
rhs: inner_rhs,
},
)
| (
Expression::BinOp {
lhs: inner_lhs,
op: IntSub,
rhs: inner_rhs,
},
op,
Const(bitvec),
) if (bitvec.is_zero() || bitvec.is_one())
&& matches!(op, IntEqual | IntNotEqual) =>
{
// `0 == x - y` is equivalent to `x == y`
let new_op = match (op, bitvec.is_zero()) {
(IntEqual, true) | (IntNotEqual, false) => IntEqual,
(IntEqual, false) | (IntNotEqual, true) => IntNotEqual,
_ => unreachable!(),
};
*self = Expression::BinOp {
lhs: inner_lhs.clone(),
op: new_op,
rhs: inner_rhs.clone(),
} }
( }
Const(bitvec), (
op, Expression::BinOp {
Expression::BinOp { lhs: less_left,
lhs: inner_lhs, op: IntSLess,
op: IntSub, rhs: less_right,
rhs: inner_rhs, },
}, BoolOr,
) Expression::BinOp {
| ( lhs: equal_left,
Expression::BinOp { op: IntEqual,
lhs: inner_lhs, rhs: equal_right,
},
)
| (
Expression::BinOp {
lhs: equal_left,
op: IntEqual,
rhs: equal_right,
},
BoolOr,
Expression::BinOp {
lhs: less_left,
op: IntSLess,
rhs: less_right,
},
) if (less_left == equal_left && less_right == equal_right)
|| (less_left == equal_right && less_right == equal_left) =>
{
// `x < y or x == y` is equivalent to `x <= y `
*self = Expression::BinOp {
lhs: less_left.clone(),
op: IntSLessEqual,
rhs: less_right.clone(),
};
}
(
Expression::BinOp {
lhs: less_left,
op: IntLess,
rhs: less_right,
},
BoolOr,
Expression::BinOp {
lhs: equal_left,
op: IntEqual,
rhs: equal_right,
},
)
| (
Expression::BinOp {
lhs: equal_left,
op: IntEqual,
rhs: equal_right,
},
BoolOr,
Expression::BinOp {
lhs: less_left,
op: IntLess,
rhs: less_right,
},
) if (less_left == equal_left && less_right == equal_right)
|| (less_left == equal_right && less_right == equal_left) =>
{
// `x < y or x == y` is equivalent to `x <= y `
*self = Expression::BinOp {
lhs: less_left.clone(),
op: IntLessEqual,
rhs: less_right.clone(),
};
}
(
Expression::BinOp {
lhs: lessequal_left,
op: IntLessEqual,
rhs: lessequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
)
| (
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: lessequal_left,
op: IntLessEqual,
rhs: lessequal_right,
},
) if (lessequal_left == notequal_left && lessequal_right == notequal_right)
|| (lessequal_left == notequal_right && lessequal_right == notequal_left) =>
{
// `x <= y and x != y` is equivalent to `x < y `
*self = Expression::BinOp {
lhs: lessequal_left.clone(),
op: IntLess,
rhs: lessequal_right.clone(),
};
}
(
Expression::BinOp {
lhs: lessequal_left,
op: IntSLessEqual,
rhs: lessequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
)
| (
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: lessequal_left,
op: IntSLessEqual,
rhs: lessequal_right,
},
) if (lessequal_left == notequal_left && lessequal_right == notequal_right)
|| (lessequal_left == notequal_right && lessequal_right == notequal_left) =>
{
// `x <= y and x != y` is equivalent to `x < y `
*self = Expression::BinOp {
lhs: lessequal_left.clone(),
op: IntSLess,
rhs: lessequal_right.clone(),
};
}
_ => (),
}
}
}
/// 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, op: IntSub,
rhs: inner_rhs, rhs: Box::new(Const(const_middle.bin_op(IntAdd, const_right).unwrap())),
},
op,
Const(bitvec),
) if (bitvec.is_zero() || bitvec.is_one())
&& matches!(op, IntEqual | IntNotEqual) =>
{
// `0 == x - y` is equivalent to `x == y`
let new_op = match (op, bitvec.is_zero()) {
(IntEqual, true) | (IntNotEqual, false) => IntEqual,
(IntEqual, false) | (IntNotEqual, true) => IntNotEqual,
_ => unreachable!(),
};
*self = Expression::BinOp {
lhs: inner_lhs.clone(),
op: new_op,
rhs: inner_rhs.clone(),
} }
} }
( }
Expression::BinOp { (
lhs: less_left, BinOp {
op: IntSLess, lhs: left,
rhs: less_right, op: IntAdd,
}, rhs: middle,
BoolOr, },
Expression::BinOp { IntAdd,
lhs: equal_left, Const(const_right),
op: IntEqual, ) => {
rhs: equal_right, if let Const(const_middle) = &**middle {
}, // `(x + const_1) + const_2 = x + (const_1 + const_2)`
) *self = BinOp {
| ( lhs: left.clone(),
Expression::BinOp { op: IntAdd,
lhs: equal_left, rhs: Box::new(Const(const_middle.bin_op(IntAdd, const_right).unwrap())),
op: IntEqual, }
rhs: equal_right, } else if let Const(const_left) = &**left {
}, // `(const_1 + x) + const_2 = x + (const_1 + const_2)`
BoolOr, *self = BinOp {
Expression::BinOp { lhs: middle.clone(),
lhs: less_left, op: IntAdd,
op: IntSLess, rhs: Box::new(Const(const_left.bin_op(IntAdd, const_right).unwrap())),
rhs: less_right, }
},
) if (less_left == equal_left && less_right == equal_right)
|| (less_left == equal_right && less_right == equal_left) =>
{
// `x < y or x == y` is equivalent to `x <= y `
*self = Expression::BinOp {
lhs: less_left.clone(),
op: IntSLessEqual,
rhs: less_right.clone(),
};
}
(
Expression::BinOp {
lhs: less_left,
op: IntLess,
rhs: less_right,
},
BoolOr,
Expression::BinOp {
lhs: equal_left,
op: IntEqual,
rhs: equal_right,
},
)
| (
Expression::BinOp {
lhs: equal_left,
op: IntEqual,
rhs: equal_right,
},
BoolOr,
Expression::BinOp {
lhs: less_left,
op: IntLess,
rhs: less_right,
},
) if (less_left == equal_left && less_right == equal_right)
|| (less_left == equal_right && less_right == equal_left) =>
{
// `x < y or x == y` is equivalent to `x <= y `
*self = Expression::BinOp {
lhs: less_left.clone(),
op: IntLessEqual,
rhs: less_right.clone(),
};
}
(
Expression::BinOp {
lhs: lessequal_left,
op: IntLessEqual,
rhs: lessequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
)
| (
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: lessequal_left,
op: IntLessEqual,
rhs: lessequal_right,
},
) if (lessequal_left == notequal_left && lessequal_right == notequal_right)
|| (lessequal_left == notequal_right
&& lessequal_right == notequal_left) =>
{
// `x <= y and x != y` is equivalent to `x < y `
*self = Expression::BinOp {
lhs: lessequal_left.clone(),
op: IntLess,
rhs: lessequal_right.clone(),
};
}
(
Expression::BinOp {
lhs: lessequal_left,
op: IntSLessEqual,
rhs: lessequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
)
| (
Expression::BinOp {
lhs: notequal_left,
op: IntNotEqual,
rhs: notequal_right,
},
BoolAnd,
Expression::BinOp {
lhs: lessequal_left,
op: IntSLessEqual,
rhs: lessequal_right,
},
) if (lessequal_left == notequal_left && lessequal_right == notequal_right)
|| (lessequal_left == notequal_right
&& lessequal_right == notequal_left) =>
{
// `x <= y and x != y` is equivalent to `x < y `
*self = Expression::BinOp {
lhs: lessequal_left.clone(),
op: IntSLess,
rhs: lessequal_right.clone(),
};
} }
_ => (),
} }
_ => (),
} }
} }
} }
/// 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.
/// E.g. substitute `a XOR a` with zero or substitute `a OR a` with `a`. /// E.g. substitute `a XOR a` with zero or substitute `a OR a` with `a`.
pub fn substitute_trivial_operations(&mut self) { pub fn substitute_trivial_operations(&mut self) {
......
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