Unverified Commit 2487aac1 by van den Bosch Committed by GitHub

remove dead code originating from control flow propagation (#384)

parent 6e16f015
use crate::analysis::graph::{Edge, Graph, Node}; use crate::analysis::graph::{Edge, Graph, Node};
use crate::intermediate_representation::*; use crate::intermediate_representation::*;
use petgraph::graph::NodeIndex; use petgraph::graph::NodeIndex;
use std::collections::{BTreeSet, HashMap}; use petgraph::Direction::Incoming;
use std::collections::{BTreeSet, HashMap, HashSet};
/// The `propagate_control_flow` normalization pass tries to simplify the representation of /// The `propagate_control_flow` normalization pass tries to simplify the representation of
/// sequences of if-else blocks that all have the same condition /// sequences of if-else blocks that all have the same condition
...@@ -13,15 +14,17 @@ use std::collections::{BTreeSet, HashMap}; ...@@ -13,15 +14,17 @@ use std::collections::{BTreeSet, HashMap};
/// we look for sequences of (conditional) jumps where the final jump target is determined by the source of the first jump /// we look for sequences of (conditional) jumps where the final jump target is determined by the source of the first jump
/// (because we know that the conditionals for all jumps evaluate to the same value along the sequence). /// (because we know that the conditionals for all jumps evaluate to the same value along the sequence).
/// For such a sequence we then retarget the destination of the first jump to the final jump destination of the sequence. /// For such a sequence we then retarget the destination of the first jump to the final jump destination of the sequence.
/// Lastly, the newly bypassed blocks are considered dead code and are removed.
pub fn propagate_control_flow(project: &mut Project) { pub fn propagate_control_flow(project: &mut Project) {
let extern_subs = project let extern_subs: HashSet<Tid> = project
.program .program
.term .term
.extern_symbols .extern_symbols
.keys() .keys()
.cloned() .cloned()
.collect(); .collect();
let cfg = crate::analysis::graph::get_program_cfg(&project.program, extern_subs); let cfg = crate::analysis::graph::get_program_cfg(&project.program, extern_subs.clone());
let nodes_without_incoming_edges_at_beginning = get_nodes_without_incoming_edge(&cfg);
let mut jmps_to_retarget = HashMap::new(); let mut jmps_to_retarget = HashMap::new();
for node in cfg.node_indices() { for node in cfg.node_indices() {
...@@ -71,6 +74,15 @@ pub fn propagate_control_flow(project: &mut Project) { ...@@ -71,6 +74,15 @@ pub fn propagate_control_flow(project: &mut Project) {
} }
} }
retarget_jumps(project, jmps_to_retarget); retarget_jumps(project, jmps_to_retarget);
let cfg = crate::analysis::graph::get_program_cfg(&project.program, extern_subs);
let nodes_without_incoming_edges_at_end = get_nodes_without_incoming_edge(&cfg);
remove_new_orphaned_blocks(
project,
nodes_without_incoming_edges_at_beginning,
nodes_without_incoming_edges_at_end,
);
} }
/// Insert the new target TIDs into jump instructions for which a new target was computed. /// Insert the new target TIDs into jump instructions for which a new target was computed.
...@@ -242,6 +254,33 @@ fn negate_condition(expr: Expression) -> Expression { ...@@ -242,6 +254,33 @@ fn negate_condition(expr: Expression) -> Expression {
} }
} }
/// Iterates the CFG and returns all node's blocks, that do not have an incoming edge.
fn get_nodes_without_incoming_edge(cfg: &Graph) -> HashSet<Tid> {
let mut nodes_without_incoming_edges = HashSet::new();
for node in cfg.node_indices() {
if cfg.neighbors_directed(node, Incoming).next().is_none() {
nodes_without_incoming_edges.insert(cfg[node].get_block().tid.clone());
}
}
nodes_without_incoming_edges
}
/// Calculates the difference of the orphaned blocks and removes them from the project.
fn remove_new_orphaned_blocks(
project: &mut Project,
orphaned_blocks_before: HashSet<Tid>,
orphaned_blocks_after: HashSet<Tid>,
) {
let new_orphan_blocks: HashSet<&Tid> = orphaned_blocks_after
.difference(&orphaned_blocks_before)
.collect();
for sub in project.program.term.subs.values_mut() {
sub.term
.blocks
.retain(|blk| !new_orphan_blocks.contains(&&blk.tid));
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
...@@ -251,7 +290,7 @@ pub mod tests { ...@@ -251,7 +290,7 @@ pub mod tests {
fn mock_condition_block(name: &str, if_target: &str, else_target: &str) -> Term<Blk> { fn mock_condition_block(name: &str, if_target: &str, else_target: &str) -> Term<Blk> {
let if_jmp = Jmp::CBranch { let if_jmp = Jmp::CBranch {
target: Tid::new(if_target), target: Tid::new(if_target),
condition: expr!("zero_flag:1"), condition: expr!("ZF:1"),
}; };
let if_jmp = Term { let if_jmp = Term {
tid: Tid::new(name.to_string() + "_jmp_if"), tid: Tid::new(name.to_string() + "_jmp_if"),
...@@ -275,7 +314,6 @@ pub mod tests { ...@@ -275,7 +314,6 @@ pub mod tests {
fn mock_block_with_defs(name: &str, return_target: &str) -> Term<Blk> { fn mock_block_with_defs(name: &str, return_target: &str) -> Term<Blk> {
let def = def![format!("{name}_def: r0:4 = r1:4")]; let def = def![format!("{name}_def: r0:4 = r1:4")];
let jmp = Jmp::Branch(Tid::new(return_target)); let jmp = Jmp::Branch(Tid::new(return_target));
let jmp = Term { let jmp = Term {
tid: Tid::new(name.to_string() + "_jmp"), tid: Tid::new(name.to_string() + "_jmp"),
...@@ -318,9 +356,9 @@ pub mod tests { ...@@ -318,9 +356,9 @@ pub mod tests {
let expected_blocks = vec![ let expected_blocks = vec![
mock_condition_block("cond_blk_1", "def_blk_1", "end_blk"), mock_condition_block("cond_blk_1", "def_blk_1", "end_blk"),
mock_block_with_defs("def_blk_1", "def_blk_2"), mock_block_with_defs("def_blk_1", "def_blk_2"),
mock_condition_block("cond_blk_2", "def_blk_2", "end_blk"), // cond_blk_2 removed, since no incoming edge anymore
mock_block_with_defs("def_blk_2", "def_blk_3"), mock_block_with_defs("def_blk_2", "def_blk_3"),
mock_condition_block("cond_blk_3", "def_blk_3", "end_blk"), // cond_blk_3 removed, since no incoming edge anymore
mock_block_with_defs("def_blk_3", "end_blk"), mock_block_with_defs("def_blk_3", "end_blk"),
mock_block_with_defs("end_blk", "end_blk"), mock_block_with_defs("end_blk", "end_blk"),
]; ];
......
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