Max-SSA: make cut-block set configurable.
This commit is contained in:
parent
0e2ec8c893
commit
dc177bfed3
|
@ -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.
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,19 +93,39 @@ 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) {
|
||||||
|
for (block, blockdata) in body.blocks.entries_mut() {
|
||||||
|
blockdata.terminator.update_targets(|target| {
|
||||||
|
for &new_arg in &self.new_args[target.block] {
|
||||||
let actual_value = self
|
let actual_value = self
|
||||||
.value_map
|
.value_map
|
||||||
.get(&(pred, new_arg))
|
.get(&(block, new_arg))
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(new_arg);
|
.unwrap_or(new_arg);
|
||||||
target.args.push(actual_value);
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue