Unverified Commit 52804195 by Enkelmann Committed by GitHub

replace let-bindings in expressions (#82)

parent a849f2ae
......@@ -63,6 +63,134 @@ pub enum Expression {
},
}
impl Expression {
/// Resolve all let-bindings inside an expression to create an equivalent expression without usage of let-bindings.
pub fn replace_let_bindings(&mut self) {
use Expression::*;
match self {
Var(_) | Const(_) | Unknown { .. } => (),
Load {
memory, address, ..
} => {
memory.replace_let_bindings();
address.replace_let_bindings();
}
Store {
memory,
address,
value,
..
} => {
memory.replace_let_bindings();
address.replace_let_bindings();
value.replace_let_bindings();
}
BinOp { op: _, lhs, rhs } => {
lhs.replace_let_bindings();
rhs.replace_let_bindings();
}
UnOp { op: _, arg } => arg.replace_let_bindings(),
Cast {
kind: _,
width: _,
arg,
} => arg.replace_let_bindings(),
Let {
var,
bound_exp,
body_exp,
} => {
let to_replace = Expression::Var(var.clone());
body_exp.replace_let_bindings();
body_exp.substitute(&to_replace, bound_exp);
*self = *body_exp.clone();
}
IfThenElse {
condition,
true_exp,
false_exp,
} => {
condition.replace_let_bindings();
true_exp.replace_let_bindings();
false_exp.replace_let_bindings();
}
Extract {
low_bit: _,
high_bit: _,
arg,
} => arg.replace_let_bindings(),
Concat { left, right } => {
left.replace_let_bindings();
right.replace_let_bindings();
}
}
}
/// Substitutes all subexpressions equal to `to_replace` with the expression `replace_with`.
fn substitute(&mut self, to_replace: &Expression, replace_with: &Expression) {
use Expression::*;
if self == to_replace {
*self = replace_with.clone();
} else {
match self {
Var(_) | Const(_) | Unknown { .. } => (),
Load {
memory, address, ..
} => {
memory.substitute(to_replace, replace_with);
address.substitute(to_replace, replace_with);
}
Store {
memory,
address,
value,
..
} => {
memory.substitute(to_replace, replace_with);
address.substitute(to_replace, replace_with);
value.substitute(to_replace, replace_with);
}
BinOp { op: _, lhs, rhs } => {
lhs.substitute(to_replace, replace_with);
rhs.substitute(to_replace, replace_with);
}
UnOp { op: _, arg } => arg.substitute(to_replace, replace_with),
Cast {
kind: _,
width: _,
arg,
} => arg.substitute(to_replace, replace_with),
Let {
var: _,
bound_exp,
body_exp,
} => {
bound_exp.substitute(to_replace, replace_with);
body_exp.substitute(to_replace, replace_with);
}
IfThenElse {
condition,
true_exp,
false_exp,
} => {
condition.substitute(to_replace, replace_with);
true_exp.substitute(to_replace, replace_with);
false_exp.substitute(to_replace, replace_with);
}
Extract {
low_bit: _,
high_bit: _,
arg,
} => arg.substitute(to_replace, replace_with),
Concat { left, right } => {
left.substitute(to_replace, replace_with);
right.substitute(to_replace, replace_with);
}
}
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum CastType {
UNSIGNED,
......@@ -110,6 +238,14 @@ pub enum Endianness {
mod tests {
use super::*;
fn register(name: &str) -> Variable {
Variable {
name: name.into(),
type_: Type::Immediate(64),
is_temp: false,
}
}
#[test]
fn variant_deserialization() {
let string = "\"UNSIGNED\"";
......@@ -140,4 +276,25 @@ mod tests {
println!("{}", serde_json::to_string(&exp).unwrap());
assert_eq!(exp, serde_json::from_str(string).unwrap())
}
#[test]
fn replace_let_bindings() {
let mut source_exp = Expression::Let {
var: register("x"),
bound_exp: Box::new(Expression::Const(Bitvector::from_u64(12))),
body_exp: Box::new(Expression::BinOp {
op: BinOpType::PLUS,
lhs: Box::new(Expression::Var(register("x"))),
rhs: Box::new(Expression::Const(Bitvector::from_u64(42))),
}),
};
let target_exp = Expression::BinOp {
op: BinOpType::PLUS,
lhs: Box::new(Expression::Const(Bitvector::from_u64(12))),
rhs: Box::new(Expression::Const(Bitvector::from_u64(42))),
};
source_exp.replace_let_bindings();
assert_eq!(source_exp, target_exp);
}
}
......@@ -8,9 +8,10 @@ use super::failwith_on_panic;
fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarning>, Vec<String>) {
let json_builder = unsafe { JsonBuilder::from_ocaml(&program_jsonbuilder_val) };
let program_json = serde_json::Value::from(json_builder);
let project: Project =
let mut project: Project =
serde_json::from_value(program_json).expect("Project deserialization failed");
project.replace_let_bindings();
crate::analysis::pointer_inference::run(&project, false)
}
......@@ -26,9 +27,10 @@ caml!(rs_run_pointer_inference(program_jsonbuilder_val) {
fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value) {
let json_builder = unsafe { JsonBuilder::from_ocaml(&program_jsonbuilder_val) };
let program_json = serde_json::Value::from(json_builder);
let project: Project =
let mut project: Project =
serde_json::from_value(program_json).expect("Project deserialization failed");
project.replace_let_bindings();
crate::analysis::pointer_inference::run(&project, true); // Note: This discard all CweWarnings and log messages.
}
......
......@@ -92,9 +92,49 @@ pub struct Project {
}
impl Project {
/// Get the bitsize of pointer values for the architecture of the project.
pub fn get_pointer_bitsize(&self) -> BitSize {
self.stack_pointer_register.bitsize().unwrap()
}
/// Substitute all let-binding-expressions in the project with equivalent expressions,
/// that do not contain the let-bindings.
/// This way subsequent analyses do not have to handle expressions containing let-bindings.
pub fn replace_let_bindings(&mut self) {
for sub in self.program.term.subs.iter_mut() {
for blk in sub.term.blocks.iter_mut() {
for def in blk.term.defs.iter_mut() {
def.term.rhs.replace_let_bindings();
}
for jmp in blk.term.jmps.iter_mut() {
if let Some(ref mut condition) = jmp.term.condition {
condition.replace_let_bindings();
}
match &mut jmp.term.kind {
JmpKind::Call(call) => {
call.target.replace_let_bindings();
if let Some(ref mut return_target) = call.return_ {
return_target.replace_let_bindings();
}
}
JmpKind::Goto(label) | JmpKind::Return(label) => {
label.replace_let_bindings()
}
JmpKind::Interrupt { .. } => (),
}
}
}
}
}
}
impl Label {
/// Replace let-bindings inside the expression for `Indirect` labels.
fn replace_let_bindings(&mut self) {
if let Label::Indirect(expression) = self {
expression.replace_let_bindings();
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
......
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