Skip to content
Snippets Groups Projects

Round 1 of rodinia schedule optimization

Merged rarbore2 requested to merge rodinia_opt1 into main
5 files
+ 153
4
Compare changes
  • Side-by-side
  • Inline
Files
5
+ 204
3
use std::cell::Ref;
use std::collections::HashMap;
use hercules_ir::callgraph::*;
use hercules_ir::def_use::*;
use hercules_ir::ir::*;
use hercules_ir::*;
use crate::*;
@@ -235,3 +234,205 @@ fn inline_func(
});
}
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
enum ParameterLattice {
Top,
Constant(ConstantID),
// Dynamic constant
DynamicConstant(DynamicConstantID, FunctionID),
Bottom,
}
impl ParameterLattice {
fn from_node(node: &Node, func_id: FunctionID) -> Self {
use ParameterLattice::*;
match node {
Node::Undef { ty: _ } => Top,
Node::Constant { id } => Constant(*id),
Node::DynamicConstant { id } => DynamicConstant(*id, func_id),
_ => Bottom,
}
}
fn meet(&mut self, b: Self, cons: Ref<'_, Vec<Constant>>, dcs: Ref<'_, Vec<DynamicConstant>>) {
use ParameterLattice::*;
*self = match (*self, b) {
(Top, b) => b,
(a, Top) => a,
(Bottom, _) | (_, Bottom) => Bottom,
(Constant(id_a), Constant(id_b)) => {
if id_a == id_b {
Constant(id_a)
} else {
Bottom
}
}
(DynamicConstant(dc_a, f_a), DynamicConstant(dc_b, f_b)) => {
if dc_a == dc_b && f_a == f_b {
DynamicConstant(dc_a, f_a)
} else if let (
ir::DynamicConstant::Constant(dcv_a),
ir::DynamicConstant::Constant(dcv_b),
) = (&dcs[dc_a.idx()], &dcs[dc_b.idx()])
&& *dcv_a == *dcv_b
{
DynamicConstant(dc_a, f_a)
} else {
Bottom
}
}
(DynamicConstant(dc, _), Constant(con)) | (Constant(con), DynamicConstant(dc, _)) => {
match (&cons[con.idx()], &dcs[dc.idx()]) {
(ir::Constant::UnsignedInteger64(conv), ir::DynamicConstant::Constant(dcv))
if *conv as usize == *dcv =>
{
Constant(con)
}
_ => Bottom,
}
}
}
}
}
/*
* Top level function to inline constant parameters and constant dynamic
* constant parameters. Identifies functions that are:
*
* 1. Not marked as entry.
* 2. At every call site, a particular parameter is always a specific constant
* or dynamic constant.
*
* These functions can have that constant "inlined" - the parameter is removed
* and all uses of the parameter becomes uses of the constant directly.
*/
pub fn const_inline(editors: &mut [FunctionEditor], callgraph: &CallGraph) {
// Run const inlining on each function, starting at the most shallow
// function first, since we want to propagate constants down the call graph.
for func_id in callgraph.topo().into_iter().rev() {
let func = editors[func_id.idx()].func();
if func.entry || callgraph.num_callers(func_id) == 0 {
continue;
}
// Figure out what we know about the parameters to this function.
let mut param_lattice = vec![ParameterLattice::Top; func.param_types.len()];
let mut callers = vec![];
for caller in callgraph.get_callers(func_id) {
let editor = &editors[caller.idx()];
let nodes = &editor.func().nodes;
for id in editor.node_ids() {
if let Some((_, callee, _, args)) = nodes[id.idx()].try_call()
&& callee == func_id
{
if editor.is_mutable(id) {
for (idx, id) in args.into_iter().enumerate() {
let lattice = ParameterLattice::from_node(&nodes[id.idx()], callee);
param_lattice[idx].meet(
lattice,
editor.get_constants(),
editor.get_dynamic_constants(),
);
}
} else {
// If we can't modify the call node in the caller, then
// we can't perform the inlining.
param_lattice = vec![ParameterLattice::Bottom; func.param_types.len()];
}
callers.push((caller, id));
}
}
}
if param_lattice.iter().all(|v| *v == ParameterLattice::Bottom) {
continue;
}
// Replace the arguments.
let editor = &mut editors[func_id.idx()];
let mut param_idx_to_ids: HashMap<usize, Vec<NodeID>> = HashMap::new();
for id in editor.node_ids() {
if let Some(idx) = editor.func().nodes[id.idx()].try_parameter() {
param_idx_to_ids.entry(idx).or_default().push(id);
}
}
let mut params_to_remove = vec![];
let success = editor.edit(|mut edit| {
let mut param_tys = edit.get_param_types().clone();
let mut decrement_index_by = 0;
for idx in 0..param_tys.len() {
if let Some(node) = match param_lattice[idx] {
ParameterLattice::Top => Some(Node::Undef { ty: param_tys[idx] }),
ParameterLattice::Constant(id) => Some(Node::Constant { id }),
ParameterLattice::DynamicConstant(id, _) => {
// Rust moment.
let maybe_cons = edit.get_dynamic_constant(id).try_constant();
if let Some(val) = maybe_cons {
Some(Node::DynamicConstant {
id: edit.add_dynamic_constant(DynamicConstant::Constant(val)),
})
} else {
None
}
}
_ => None,
} && let Some(ids) = param_idx_to_ids.get(&idx)
{
let node = edit.add_node(node);
for id in ids {
edit = edit.replace_all_uses(*id, node)?;
edit = edit.delete_node(*id)?;
}
param_tys.remove(idx - decrement_index_by);
params_to_remove.push(idx);
decrement_index_by += 1;
} else if decrement_index_by != 0
&& let Some(ids) = param_idx_to_ids.get(&idx)
{
let node = edit.add_node(Node::Parameter {
index: idx - decrement_index_by,
});
for id in ids {
edit = edit.replace_all_uses(*id, node)?;
edit = edit.delete_node(*id)?;
}
}
}
edit.set_param_types(param_tys);
Ok(edit)
});
params_to_remove.reverse();
// Update callers.
if success {
for (caller, call) in callers {
let editor = &mut editors[caller.idx()];
let success = editor.edit(|mut edit| {
let Node::Call {
control,
function,
dynamic_constants,
args,
} = edit.get_node(call).clone()
else {
panic!();
};
let mut args = args.into_vec();
for idx in params_to_remove.iter() {
args.remove(*idx);
}
let node = edit.add_node(Node::Call {
control,
function,
dynamic_constants,
args: args.into_boxed_slice(),
});
edit = edit.replace_all_uses(call, node)?;
edit = edit.delete_node(call)?;
Ok(edit)
});
assert!(success);
}
}
}
}
Loading