Stackification improvements.
This commit is contained in:
parent
0947940c43
commit
99b309a36d
|
@ -2,13 +2,12 @@
|
||||||
//! control flow out of a CFG.
|
//! control flow out of a CFG.
|
||||||
|
|
||||||
use crate::{cfg::CFGInfo, ir::*};
|
use crate::{cfg::CFGInfo, ir::*};
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Shape {
|
pub enum Shape {
|
||||||
Block { head: BlockId, children: Vec<Shape> },
|
Block { head: BlockId, children: Vec<Shape> },
|
||||||
Loop { head: BlockId, children: Vec<Shape> },
|
Loop { head: BlockId, children: Vec<Shape> },
|
||||||
Leaf { block: BlockId, succs: Vec<BlockId> },
|
Leaf { block: BlockId },
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +58,69 @@ enum BlockPoint {
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Shape {
|
||||||
|
/// Finds the next shape in the sequence, returning the shape and
|
||||||
|
/// the remaining starting block ID / region list.
|
||||||
|
fn get_one_shape<'a>(
|
||||||
|
start: OrderedBlockId,
|
||||||
|
order: &[BlockId],
|
||||||
|
regions: &'a [Region],
|
||||||
|
) -> Option<(Shape, OrderedBlockId, &'a [Region])> {
|
||||||
|
log::trace!("get_one_shape: start {} regions {:?}", start, regions);
|
||||||
|
if start >= order.len() {
|
||||||
|
None
|
||||||
|
} else if regions.is_empty() || start < regions[0].start().block {
|
||||||
|
Some((
|
||||||
|
Shape::Leaf {
|
||||||
|
block: order[start],
|
||||||
|
},
|
||||||
|
start + 1,
|
||||||
|
®ions,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
assert_eq!(start, regions[0].start().block);
|
||||||
|
let end = regions[0].end();
|
||||||
|
let region_end = regions
|
||||||
|
.iter()
|
||||||
|
.position(|region| region.start() > end)
|
||||||
|
.unwrap_or(regions.len());
|
||||||
|
let subregions = ®ions[1..region_end];
|
||||||
|
let (children, next_start) = Self::get_shapes(start, end.block, order, subregions);
|
||||||
|
let shape = if let Region::Forward(..) = ®ions[0] {
|
||||||
|
Shape::Block {
|
||||||
|
head: order[start],
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Shape::Loop {
|
||||||
|
head: order[start],
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some((shape, next_start, ®ions[region_end..]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shapes<'a>(
|
||||||
|
start: OrderedBlockId,
|
||||||
|
end: OrderedBlockId,
|
||||||
|
order: &[BlockId],
|
||||||
|
mut regions: &'a [Region],
|
||||||
|
) -> (Vec<Shape>, OrderedBlockId) {
|
||||||
|
log::trace!("get_shapes: start {} regions {:?}", start, regions);
|
||||||
|
let mut shapes = vec![];
|
||||||
|
let mut block = start;
|
||||||
|
while block < end {
|
||||||
|
let (shape, next_start, next_regions) =
|
||||||
|
Self::get_one_shape(block, order, regions).unwrap();
|
||||||
|
shapes.push(shape);
|
||||||
|
block = next_start;
|
||||||
|
regions = next_regions;
|
||||||
|
}
|
||||||
|
(shapes, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Region {
|
impl Region {
|
||||||
fn start(&self) -> RegionEndpoint {
|
fn start(&self) -> RegionEndpoint {
|
||||||
match self {
|
match self {
|
||||||
|
@ -87,10 +149,10 @@ impl Shape {
|
||||||
pub fn compute(f: &FunctionBody, cfg: &CFGInfo) -> Self {
|
pub fn compute(f: &FunctionBody, cfg: &CFGInfo) -> Self {
|
||||||
// Process all non-contiguous edges in RPO block order. For
|
// Process all non-contiguous edges in RPO block order. For
|
||||||
// forward and backward edges, emit Regions.
|
// forward and backward edges, emit Regions.
|
||||||
debug!("f = {:?}", f);
|
log::trace!("f = {:?}", f);
|
||||||
debug!("cfg = {:?}", cfg);
|
log::trace!("cfg = {:?}", cfg);
|
||||||
let order = cfg.rpo();
|
let order = cfg.rpo();
|
||||||
debug!("rpo = {:?}", order);
|
log::trace!("rpo = {:?}", order);
|
||||||
|
|
||||||
assert_eq!(order[0], 0); // Entry block should come first.
|
assert_eq!(order[0], 0); // Entry block should come first.
|
||||||
let mut regions = vec![];
|
let mut regions = vec![];
|
||||||
|
@ -112,95 +174,112 @@ impl Shape {
|
||||||
// regions where necessary and duplicating where we find
|
// regions where necessary and duplicating where we find
|
||||||
// irreducible control flow.
|
// irreducible control flow.
|
||||||
regions.sort_by_key(|r| r.start());
|
regions.sort_by_key(|r| r.start());
|
||||||
debug!("regions = {:?}", regions);
|
log::trace!("regions = {:?}", regions);
|
||||||
|
|
||||||
// Examine each region in the sequence, determining whether it
|
// Examine each region in the sequence, determining whether it
|
||||||
// is properly nested with respect to all overlapping regions.
|
// is properly nested with respect to all overlapping regions.
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i + 1 < regions.len() {
|
while i < regions.len() {
|
||||||
i += 1;
|
let this_i = i;
|
||||||
let prev = regions[i - 1];
|
for prev_i in 0..i {
|
||||||
let this = regions[i];
|
let prev = regions[prev_i];
|
||||||
debug!("examining: {:?} -> {:?}", prev, this);
|
let this = regions[i];
|
||||||
|
log::trace!("examining: {:?} -> {:?}", prev, this);
|
||||||
|
|
||||||
if !prev.overlaps(&this) {
|
if !prev.overlaps(&this) {
|
||||||
debug!(" -> no overlap");
|
log::trace!(" -> no overlap");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important invariant: none of these
|
||||||
|
// merging/extension operations alter the sorted
|
||||||
|
// order, because at worst they "pull back" the start
|
||||||
|
// of the second region (`this`) to the start of the
|
||||||
|
// first (`prev`). If the list was sorted by
|
||||||
|
// region-start before, it will be after this edit.
|
||||||
|
let did_edit = match (prev, this) {
|
||||||
|
(a, b) if a == b => {
|
||||||
|
regions.remove(i);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(Region::Backward(a, b), Region::Backward(c, d)) if a == c => {
|
||||||
|
// Merge by extending end.
|
||||||
|
regions[prev_i] = Region::Backward(a, std::cmp::max(b, d));
|
||||||
|
regions.remove(i);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(Region::Backward(a, b), Region::Backward(c, d))
|
||||||
|
if a < c && c <= b && b < d =>
|
||||||
|
{
|
||||||
|
// Extend outer Backward to nest the inner one.
|
||||||
|
regions[prev_i] = Region::Backward(a, d);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(Region::Backward(a, b), Region::Forward(c, d)) if a <= c && c <= b => {
|
||||||
|
// Put the Forward before the Backward (extend its
|
||||||
|
// start) to ensure proper nesting.
|
||||||
|
regions[prev_i] = Region::Forward(a, d);
|
||||||
|
regions.remove(i);
|
||||||
|
regions.insert(prev_i + 1, Region::Backward(a, b));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(Region::Forward(a, b), Region::Backward(c, d)) if b > c && b <= d && a < c => {
|
||||||
|
panic!("Irreducible CFG");
|
||||||
|
}
|
||||||
|
(Region::Forward(a, b), Region::Forward(c, d)) if b == d => {
|
||||||
|
// Merge.
|
||||||
|
regions[prev_i] = Region::Forward(std::cmp::min(a, c), b);
|
||||||
|
regions.remove(i);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(Region::Forward(a, b), Region::Forward(c, d)) if a <= c && b < d => {
|
||||||
|
regions[prev_i] = Region::Forward(a, d);
|
||||||
|
regions.remove(i);
|
||||||
|
regions.insert(prev_i + 1, Region::Forward(a, b));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if did_edit {
|
||||||
|
// Back up to re-examine at prev_i.
|
||||||
|
i = prev_i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Important invariant: none of these merging/extension
|
if i == this_i {
|
||||||
// operations alter the sorted order, because at worst
|
i += 1;
|
||||||
// they "pull back" the start of the second region
|
|
||||||
// (`this`) to the start of the first (`prev`). If the
|
|
||||||
// list was sorted by region-start before, it will be
|
|
||||||
// after this edit.
|
|
||||||
let did_edit = match (prev, this) {
|
|
||||||
(a, b) if a == b => {
|
|
||||||
regions.remove(i);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Region::Backward(a, b), Region::Backward(c, d)) if a == c => {
|
|
||||||
// Merge by extending end.
|
|
||||||
regions[i - 1] = Region::Backward(a, std::cmp::max(b, d));
|
|
||||||
regions.remove(i);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Region::Backward(a, b), Region::Backward(c, d)) if a < c && c <= b && b < d => {
|
|
||||||
// Extend outer Backward to nest the inner one.
|
|
||||||
regions[i - 1] = Region::Backward(a, d);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Region::Backward(a, b), Region::Forward(c, d)) if a <= c && c <= b => {
|
|
||||||
// Put the Forward before the Backward (extend its
|
|
||||||
// start) to ensure proper nesting.
|
|
||||||
regions[i - 1] = Region::Forward(a, d);
|
|
||||||
regions[i] = Region::Backward(a, b);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Region::Forward(a, b), Region::Backward(c, d)) if b > c && b <= d && a < c => {
|
|
||||||
panic!("Irreducible CFG");
|
|
||||||
}
|
|
||||||
(Region::Forward(a, b), Region::Forward(c, d)) if b == d => {
|
|
||||||
// Merge.
|
|
||||||
regions[i - 1] = Region::Forward(std::cmp::min(a, c), b);
|
|
||||||
regions.remove(i);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Region::Forward(a, b), Region::Forward(c, d)) if a <= c && b < d => {
|
|
||||||
regions[i - 1] = Region::Forward(a, d);
|
|
||||||
regions[i] = Region::Forward(a, b);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if did_edit {
|
|
||||||
// Back up to re-examine i-1 vs i-2, unless we
|
|
||||||
// were examining i=0 vs i=1 already.
|
|
||||||
i = std::cmp::max(2, i) - 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("after stackifying: {:?}", regions);
|
log::trace!("after stackifying: {:?}", regions);
|
||||||
|
|
||||||
// Ensure the regions properly nest.
|
// Ensure the regions properly nest.
|
||||||
let mut stack: Vec<Region> = vec![];
|
#[cfg(debug_assertions)]
|
||||||
for region in ®ions {
|
{
|
||||||
while let Some(top) = stack.last() {
|
let mut stack: Vec<Region> = vec![];
|
||||||
if top.contains(region) {
|
for region in ®ions {
|
||||||
stack.push(region.clone());
|
log::trace!("checking region nest: {:?} (stack = {:?})", region, stack);
|
||||||
break;
|
while let Some(top) = stack.last() {
|
||||||
} else if region.contains(top) {
|
log::trace!(" -> top = {:?}", top);
|
||||||
stack.pop();
|
if top.contains(region) {
|
||||||
} else if region.overlaps(top) {
|
stack.push(region.clone());
|
||||||
panic!(
|
log::trace!(" -> push");
|
||||||
"Non-nested region: {:?} in nest: {:?} (overall: {:?})",
|
break;
|
||||||
region, stack, regions
|
} else if region.overlaps(top) {
|
||||||
);
|
panic!(
|
||||||
|
"Non-nested region: {:?} (overlaps {:?}) in nest: {:?} (overall: {:?})",
|
||||||
|
region, top, stack, regions
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::trace!(" -> pop");
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if stack.is_empty() {
|
||||||
|
stack.push(region.clone());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if stack.is_empty() {
|
|
||||||
stack.push(region.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +287,17 @@ impl Shape {
|
||||||
// as we compute the RPO. Track the current nesting, and
|
// as we compute the RPO. Track the current nesting, and
|
||||||
// traverse more than once if needed.
|
// traverse more than once if needed.
|
||||||
|
|
||||||
Shape::None
|
// Build the final shape description.
|
||||||
|
let (shapes, _) = Shape::get_shapes(0, order.len(), &order[..], ®ions[..]);
|
||||||
|
let root = if shapes.len() == 1 {
|
||||||
|
shapes.into_iter().next().unwrap()
|
||||||
|
} else {
|
||||||
|
Shape::Block {
|
||||||
|
head: 0,
|
||||||
|
children: shapes,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log::trace!("shape: {:?}", root);
|
||||||
|
root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue