Skip to content

Commit aba0251

Browse files
committed
Replace a recursive algorithm with an iterative one and a stack.
1 parent 66b97dc commit aba0251

File tree

1 file changed

+43
-26
lines changed

1 file changed

+43
-26
lines changed

src/librustc_mir/transform/simplify.rs

+43-26
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use rustc_index::vec::{Idx, IndexVec};
3333
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
3434
use rustc_middle::mir::*;
3535
use rustc_middle::ty::TyCtxt;
36+
use smallvec::SmallVec;
3637
use std::borrow::Cow;
3738

3839
pub struct SimplifyCfg {
@@ -172,9 +173,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
172173
}
173174
}
174175

175-
// Collapse a goto chain starting from `start`
176-
fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
177-
let mut terminator = match self.basic_blocks[*start] {
176+
/// This function will return `None` if
177+
/// * the block has statements
178+
/// * the block has a terminator other than `goto`
179+
/// * the block has no terminator (meaning some other part of the current optimization stole it)
180+
fn take_terminator_if_simple_goto(&mut self, bb: BasicBlock) -> Option<Terminator<'tcx>> {
181+
match self.basic_blocks[bb] {
178182
BasicBlockData {
179183
ref statements,
180184
terminator:
@@ -183,32 +187,45 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
183187
} if statements.is_empty() => terminator.take(),
184188
// if `terminator` is None, this means we are in a loop. In that
185189
// case, let all the loop collapse to its entry.
186-
_ => return,
187-
};
188-
189-
let target = match terminator {
190-
Some(Terminator { kind: TerminatorKind::Goto { ref mut target }, .. }) => {
191-
self.collapse_goto_chain(target, changed);
192-
*target
193-
}
194-
_ => unreachable!(),
195-
};
196-
self.basic_blocks[*start].terminator = terminator;
197-
198-
debug!("collapsing goto chain from {:?} to {:?}", *start, target);
199-
200-
*changed |= *start != target;
190+
_ => None,
191+
}
192+
}
201193

202-
if self.pred_count[*start] == 1 {
203-
// This is the last reference to *start, so the pred-count to
204-
// to target is moved into the current block.
205-
self.pred_count[*start] = 0;
206-
} else {
207-
self.pred_count[target] += 1;
208-
self.pred_count[*start] -= 1;
194+
/// Collapse a goto chain starting from `start`
195+
fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
196+
// Using `SmallVec` here, because in some logs on libcore oli-obk saw many single-element
197+
// goto chains. We should probably benchmark different sizes.
198+
let mut terminators: SmallVec<[_; 1]> = Default::default();
199+
let mut current = *start;
200+
while let Some(terminator) = self.take_terminator_if_simple_goto(current) {
201+
let target = match terminator {
202+
Terminator { kind: TerminatorKind::Goto { target }, .. } => target,
203+
_ => unreachable!(),
204+
};
205+
terminators.push((current, terminator));
206+
current = target;
209207
}
208+
let last = current;
209+
*start = last;
210+
while let Some((current, mut terminator)) = terminators.pop() {
211+
let target = match terminator {
212+
Terminator { kind: TerminatorKind::Goto { ref mut target }, .. } => target,
213+
_ => unreachable!(),
214+
};
215+
*target = last;
216+
debug!("collapsing goto chain from {:?} to {:?}", current, target);
210217

211-
*start = target;
218+
if self.pred_count[current] == 1 {
219+
// This is the last reference to current, so the pred-count to
220+
// to target is moved into the current block.
221+
self.pred_count[current] = 0;
222+
} else {
223+
self.pred_count[*target] += 1;
224+
self.pred_count[current] -= 1;
225+
}
226+
*changed = true;
227+
self.basic_blocks[current].terminator = Some(terminator);
228+
}
212229
}
213230

214231
// merge a block with 1 `goto` predecessor to its parent

0 commit comments

Comments
 (0)