Max-SSA: make cut-block set configurable.

This commit is contained in:
Chris Fallin 2023-04-08 22:26:19 -07:00
parent 0e2ec8c893
commit dc177bfed3
4 changed files with 63 additions and 30 deletions

View file

@ -49,7 +49,7 @@ fuzz_target!(
let mut opt_module = parsed_module.clone(); let mut opt_module = parsed_module.clone();
opt_module.per_func_body(|body| body.optimize()); opt_module.per_func_body(|body| body.optimize());
opt_module.per_func_body(|body| body.convert_to_max_ssa()); opt_module.per_func_body(|body| body.convert_to_max_ssa(None));
let mut opt_ctx = InterpContext::new(&opt_module).unwrap(); let mut opt_ctx = InterpContext::new(&opt_module).unwrap();
// Allow a little leeway for opts to not actually optimize. // Allow a little leeway for opts to not actually optimize.

View file

@ -67,7 +67,7 @@ fn apply_options(opts: &Options, module: &mut Module) -> Result<()> {
module.per_func_body(|body| body.optimize()); module.per_func_body(|body| body.optimize());
} }
if opts.max_ssa { if opts.max_ssa {
module.per_func_body(|body| body.convert_to_max_ssa()); module.per_func_body(|body| body.convert_to_max_ssa(None));
} }
Ok(()) Ok(())
} }

View file

@ -4,6 +4,7 @@ use crate::entity::{EntityRef, EntityVec, PerEntity};
use crate::frontend::parse_body; use crate::frontend::parse_body;
use crate::ir::SourceLoc; use crate::ir::SourceLoc;
use anyhow::Result; use anyhow::Result;
use std::collections::HashSet;
/// A declaration of a function: there is one `FuncDecl` per `Func` /// A declaration of a function: there is one `FuncDecl` per `Func`
/// index. /// index.
@ -50,10 +51,10 @@ impl<'a> FuncDecl<'a> {
} }
} }
pub fn convert_to_max_ssa(&mut self) { pub fn convert_to_max_ssa(&mut self, cut_blocks: Option<HashSet<Block>>) {
match self { match self {
FuncDecl::Body(_, _, body) => { FuncDecl::Body(_, _, body) => {
body.convert_to_max_ssa(); body.convert_to_max_ssa(cut_blocks);
} }
_ => {} _ => {}
} }
@ -150,9 +151,9 @@ impl FunctionBody {
crate::passes::empty_blocks::run(self); crate::passes::empty_blocks::run(self);
} }
pub fn convert_to_max_ssa(&mut self) { pub fn convert_to_max_ssa(&mut self, cut_blocks: Option<HashSet<Block>>) {
let cfg = crate::cfg::CFGInfo::new(self); let cfg = crate::cfg::CFGInfo::new(self);
crate::passes::maxssa::run(self, &cfg); crate::passes::maxssa::run(self, cut_blocks, &cfg);
} }
pub fn add_block(&mut self) -> Block { pub fn add_block(&mut self) -> Block {

View file

@ -7,13 +7,16 @@
use crate::cfg::CFGInfo; use crate::cfg::CFGInfo;
use crate::entity::PerEntity; use crate::entity::PerEntity;
use crate::ir::{Block, FunctionBody, Value, ValueDef}; use crate::ir::{Block, FunctionBody, Value, ValueDef};
use std::collections::{BTreeSet, HashMap}; use std::collections::{BTreeSet, HashMap, HashSet};
pub fn run(body: &mut FunctionBody, cfg: &CFGInfo) { pub fn run(body: &mut FunctionBody, cut_blocks: Option<HashSet<Block>>, cfg: &CFGInfo) {
MaxSSAPass::new().run(body, cfg); MaxSSAPass::new(cut_blocks).run(body, cfg);
} }
struct MaxSSAPass { struct MaxSSAPass {
/// Blocks at which all live values must cross through blockparams
/// (or if None, then all blocks).
cut_blocks: Option<HashSet<Block>>,
/// Additional block args that must be passed to each block, in /// Additional block args that must be passed to each block, in
/// order. Value numbers are *original* values. /// order. Value numbers are *original* values.
new_args: PerEntity<Block, Vec<Value>>, new_args: PerEntity<Block, Vec<Value>>,
@ -23,8 +26,9 @@ struct MaxSSAPass {
} }
impl MaxSSAPass { impl MaxSSAPass {
fn new() -> Self { fn new(cut_blocks: Option<HashSet<Block>>) -> Self {
Self { Self {
cut_blocks,
new_args: PerEntity::default(), new_args: PerEntity::default(),
value_map: HashMap::new(), value_map: HashMap::new(),
} }
@ -76,7 +80,7 @@ impl MaxSSAPass {
} }
self.new_args[block].push(value); self.new_args[block].push(value);
// Create a blockparam. // Create a placeholder value.
let ty = body.values[value].ty().unwrap(); let ty = body.values[value].ty().unwrap();
let blockparam = body.add_blockparam(block, ty); let blockparam = body.add_blockparam(block, ty);
self.value_map.insert((block, value), blockparam); self.value_map.insert((block, value), blockparam);
@ -89,24 +93,44 @@ impl MaxSSAPass {
let pred = body.blocks[block].preds[i]; let pred = body.blocks[block].preds[i];
self.visit_use(body, cfg, pred, value); self.visit_use(body, cfg, pred, value);
} }
// If all preds have the same value, and this is not a
// cut-block, rewrite the blockparam to an alias instead.
if !self.is_cut_block(block) {
if let Some(pred_value) = iter_all_same(
body.blocks[block]
.preds
.iter()
.map(|&pred| *self.value_map.get(&(pred, value)).unwrap_or(&value))
.filter(|&val| val != blockparam),
) {
body.blocks[block].params.pop();
self.new_args[block].pop();
body.values[blockparam] = ValueDef::Alias(pred_value);
self.value_map.insert((block, value), pred_value);
}
}
} }
fn update_preds(&mut self, body: &mut FunctionBody, block: Block) { fn is_cut_block(&self, block: Block) -> bool {
for i in 0..body.blocks[block].preds.len() { self.cut_blocks
let pred = body.blocks[block].preds[i]; .as_ref()
let pred_succ_idx = body.blocks[block].pos_in_pred_succ[i]; .map(|cut_blocks| cut_blocks.contains(&block))
body.blocks[pred] .unwrap_or(true)
.terminator }
.update_target(pred_succ_idx, |target| {
for &new_arg in &self.new_args[block] { fn update_branch_args(&mut self, body: &mut FunctionBody) {
let actual_value = self for (block, blockdata) in body.blocks.entries_mut() {
.value_map blockdata.terminator.update_targets(|target| {
.get(&(pred, new_arg)) for &new_arg in &self.new_args[target.block] {
.copied() let actual_value = self
.unwrap_or(new_arg); .value_map
target.args.push(actual_value); .get(&(block, new_arg))
} .copied()
}); .unwrap_or(new_arg);
target.args.push(actual_value);
}
});
} }
} }
@ -147,11 +171,19 @@ impl MaxSSAPass {
} }
fn update(&mut self, body: &mut FunctionBody) { fn update(&mut self, body: &mut FunctionBody) {
self.update_branch_args(body);
for block in body.blocks.iter() { for block in body.blocks.iter() {
if self.new_args[block].len() > 0 {
self.update_preds(body, block);
}
self.update_uses(body, block); self.update_uses(body, block);
} }
} }
} }
fn iter_all_same<Item: PartialEq + Eq + Copy, I: Iterator<Item = Item>>(iter: I) -> Option<Item> {
let mut item = None;
for val in iter {
if *item.get_or_insert(val) != val {
return None;
}
}
item
}