Skip to content
Snippets Groups Projects
Commit 44f49494 authored by Ryan Ziegler's avatar Ryan Ziegler
Browse files

finalize

parent d94294c4
No related branches found
No related tags found
1 merge request!69Dce dead calls
Pipeline #200397 passed
extern crate bitvec;
extern crate hercules_ir;
use self::bitvec::prelude::*;
use self::hercules_ir::callgraph::*;
use self::hercules_ir::ir::*;
use crate::*;
pub fn delete_uncalled(editors: &mut Vec<FunctionEditor>, callgraph: &CallGraph) -> Vec<usize> {
/**
* First renumber functions by deleting all functions unreachable from any entry function.
* Then, updates all functions to use the new function numbering.
* Returns a vec where the element at index i is the new idx of the ith function (if it was not
* deleted).
*/
pub fn delete_uncalled(
editors: &mut Vec<FunctionEditor>,
callgraph: &CallGraph,
) -> Vec<Option<usize>> {
// Step 1. Identify which functions are not reachable from entry nodes using BFS.
let mut reachable = vec![false; editors.len()];
let mut reachable = bitvec![u8, Lsb0; 0; editors.len()];
let mut worklist = vec![];
for (idx, editor) in editors.iter().enumerate() {
if editor.func().entry {
worklist.push(idx);
reachable[idx] = true;
reachable.set(idx, true);
}
}
while let Some(func_idx) = worklist.pop() {
for callee in callgraph.get_callees(FunctionID::new(func_idx)) {
if !reachable[callee.idx()] {
reachable[callee.idx()] = true;
reachable.set(callee.idx(), true);
worklist.push(callee.idx());
}
}
......@@ -27,19 +38,19 @@ pub fn delete_uncalled(editors: &mut Vec<FunctionEditor>, callgraph: &CallGraph)
// Step 2. Compute the new index of each function, which is obtained by
// deleteting all unreachable non-entry functions before it
let mut new_idx = vec![std::usize::MAX; editors.len()];
let mut new_idx = vec![None; editors.len()];
let mut deleted_count = 0;
for idx in 0..editors.len() {
if !reachable[idx] {
deleted_count += 1;
} else {
new_idx[idx] = idx - deleted_count;
new_idx[idx] = Some(idx - deleted_count);
}
}
// Step 3. Update all function callsites to use new indices. We should
// eventually handle the case where some edits fail, and just don't delete
// functions that would cause the failures.
// Step 3. Update all function callsites to use new indices. We assume
// that all nodes in all functions will be mutable, so panic if any
// edit fails.
for editor in editors.iter_mut() {
let callsites: Vec<_> = editor
.node_ids()
......@@ -47,18 +58,21 @@ pub fn delete_uncalled(editors: &mut Vec<FunctionEditor>, callgraph: &CallGraph)
.collect();
for callsite in callsites {
editor.edit(|mut edit| {
let success = editor.edit(|mut edit| {
let (control, function, dynamic_constants, args) =
edit.get_node(callsite).try_call().unwrap();
let new_node = edit.add_node(Node::Call {
control,
function: FunctionID::new(new_idx[function.idx()]),
function: FunctionID::new(new_idx[function.idx()].unwrap()),
dynamic_constants: dynamic_constants.clone(),
args: args.clone(),
});
let edit = edit.delete_node(callsite)?;
edit.replace_all_uses(callsite, new_node)
});
if !success {
panic!("Expected all delete_uncalled callsite edits to succeed!");
}
}
}
......
......@@ -637,6 +637,50 @@ impl PassManager {
}
self.clear_analyses();
}
Pass::DeleteUncalled => {
self.make_def_uses();
self.make_callgraph();
let def_uses = self.def_uses.as_ref().unwrap();
let callgraph = self.callgraph.as_ref().unwrap();
let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
let dynamic_constants_ref =
RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
// By default in an editor all nodes are mutable, which is desired in this case
// since we are only modifying the IDs of functions that we call.
let mut editors: Vec<_> =
zip(self.module.functions.iter_mut(), def_uses.iter())
.map(|(func, def_use)| {
FunctionEditor::new(
func,
&constants_ref,
&dynamic_constants_ref,
&types_ref,
def_use,
)
})
.collect();
let new_idx = delete_uncalled(&mut editors, callgraph);
self.module.constants = constants_ref.take();
self.module.dynamic_constants = dynamic_constants_ref.take();
self.module.types = types_ref.take();
let edits: Vec<_> = editors.into_iter().map(|editor| editor.edits()).collect();
for idx in 0..edits.len() {
if let Some(plans) = self.plans.as_mut() {
repair_plan(&mut plans[idx], &self.module.functions[idx], &edits[idx]);
}
let grave_mapping = self.module.functions[idx].delete_gravestones();
if let Some(plans) = self.plans.as_mut() {
plans[idx].fix_gravestones(&grave_mapping);
}
}
self.fix_deleted_functions(&new_idx);
self.clear_analyses();
}
Pass::Verify => {
let (
def_uses,
......@@ -787,46 +831,6 @@ impl PassManager {
file.write_all(&module_contents)
.expect("PANIC: Unable to write output module file contents.");
}
Pass::DeleteUncalled => {
self.make_def_uses();
self.make_callgraph();
let def_uses = self.def_uses.as_ref().unwrap();
let callgraph = self.callgraph.as_ref().unwrap();
let constants_ref = RefCell::new(std::mem::take(&mut self.module.constants));
let dynamic_constants_ref =
RefCell::new(std::mem::take(&mut self.module.dynamic_constants));
let types_ref = RefCell::new(std::mem::take(&mut self.module.types));
let mut editors: Vec<_> =
zip(self.module.functions.iter_mut(), def_uses.iter())
.map(|(func, def_use)| {
FunctionEditor::new(
func,
&constants_ref,
&dynamic_constants_ref,
&types_ref,
def_use,
)
})
.collect();
let id_mapping = delete_uncalled(&mut editors, callgraph);
self.module.constants = constants_ref.take();
self.module.dynamic_constants = dynamic_constants_ref.take();
self.module.types = types_ref.take();
let edits: Vec<_> = editors.into_iter().map(|editor| editor.edits()).collect();
for idx in 0..edits.len() {
if let Some(plans) = self.plans.as_mut() {
repair_plan(&mut plans[idx], &self.module.functions[idx], &edits[idx]);
}
let grave_mapping = self.module.functions[idx].delete_gravestones();
if let Some(plans) = self.plans.as_mut() {
plans[idx].fix_gravestones(&grave_mapping);
}
}
self.fix_deleted_functions(&id_mapping);
self.clear_analyses();
}
}
println!("Ran pass: {:?}", pass);
}
......@@ -872,12 +876,14 @@ impl PassManager {
self.manifests.unwrap()
}
fn fix_deleted_functions(&mut self, id_mapping: &[usize]) {
fn fix_deleted_functions(&mut self, id_mapping: &[Option<usize>]) {
let mut idx = 0;
// Rust does not like enumerate here, so use
// idx outside as a hack to make it happy.
self.module.functions.retain(|_| {
idx += 1;
id_mapping[idx - 1] < std::usize::MAX
id_mapping[idx - 1].is_some()
});
}
}
......@@ -157,6 +157,8 @@ pub fn compile_ir(
if x_dot {
pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
}
// Inlining may make some functions uncalled, so run this pass.
// In general, this should always be run after inlining.
add_pass!(pm, verify, DeleteUncalled);
if x_dot {
pm.add_pass(hercules_opt::pass::Pass::Xdot(true));
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment