Skip to content

Commit c0b8c43

Browse files
committed
Auto merge of #32210 - Aatch:mir-traversal, r=nikomatsakis
rBreak Critical Edges and other MIR work This PR is built on top of #32080. This adds the basic depth-first traversals for MIR, preorder, postorder and reverse postorder. The MIR blocks are now translated using reverse postorder. There is also a transform for breaking critical edges, which includes the edges from `invoke`d calls (`Drop` and `Call`), to account for the fact that we can't add code after an `invoke`. It also stops generating the intermediate block (since the transform essentially does it if necessary already). The kinds of cases this deals with are difficult to produce, so the test is the one I managed to get. However, it seems to bootstrap with `-Z orbit`, which it didn't before my changes.
2 parents 0894b06 + 605bc04 commit c0b8c43

File tree

10 files changed

+539
-49
lines changed

10 files changed

+539
-49
lines changed

src/librustc_data_structures/bitvec.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use std::iter::FromIterator;
12+
1113
/// A very simple BitVector type.
14+
#[derive(Clone)]
1215
pub struct BitVector {
1316
data: Vec<u64>,
1417
}
@@ -50,7 +53,9 @@ impl BitVector {
5053
pub fn grow(&mut self, num_bits: usize) {
5154
let num_words = u64s(num_bits);
5255
let extra_words = self.data.len() - num_words;
53-
self.data.extend((0..extra_words).map(|_| 0));
56+
if extra_words > 0 {
57+
self.data.extend((0..extra_words).map(|_| 0));
58+
}
5459
}
5560

5661
/// Iterates over indexes of set bits in a sorted order
@@ -93,6 +98,27 @@ impl<'a> Iterator for BitVectorIter<'a> {
9398
}
9499
}
95100

101+
impl FromIterator<bool> for BitVector {
102+
fn from_iter<I>(iter: I) -> BitVector where I: IntoIterator<Item=bool> {
103+
let iter = iter.into_iter();
104+
let (len, _) = iter.size_hint();
105+
// Make the minimum length for the bitvector 64 bits since that's
106+
// the smallest non-zero size anyway.
107+
let len = if len < 64 { 64 } else { len };
108+
let mut bv = BitVector::new(len);
109+
for (idx, val) in iter.enumerate() {
110+
if idx > len {
111+
bv.grow(idx);
112+
}
113+
if val {
114+
bv.insert(idx);
115+
}
116+
}
117+
118+
bv
119+
}
120+
}
121+
96122
/// A "bit matrix" is basically a square matrix of booleans
97123
/// represented as one gigantic bitvector. In other words, it is as if
98124
/// you have N bitvectors, each of length N. Note that `elements` here is `N`/

src/librustc_driver/driver.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -881,10 +881,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
881881
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
882882
passes.push_pass(box mir::transform::type_check::TypeckMir);
883883
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
884-
// Late passes
885-
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
886884
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
887-
passes.push_pass(box mir::transform::erase_regions::EraseRegions);
888885
// And run everything.
889886
passes.run_passes(tcx, &mut mir_map);
890887
});
@@ -937,16 +934,25 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
937934

938935
/// Run the translation phase to LLVM, after which the AST and analysis can
939936
pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>,
940-
mir_map: MirMap<'tcx>,
941-
analysis: ty::CrateAnalysis)
942-
-> trans::CrateTranslation {
937+
mut mir_map: MirMap<'tcx>,
938+
analysis: ty::CrateAnalysis) -> trans::CrateTranslation {
943939
let time_passes = tcx.sess.time_passes();
944940

945941
time(time_passes,
946942
"resolving dependency formats",
947943
|| dependency_format::calculate(&tcx.sess));
948944

949-
// Option dance to work around the lack of stack once closures.
945+
// Run the passes that transform the MIR into a more suitable for translation
946+
// to LLVM code.
947+
time(time_passes, "Prepare MIR codegen passes", || {
948+
let mut passes = ::rustc::mir::transform::Passes::new();
949+
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
950+
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
951+
passes.push_pass(box mir::transform::erase_regions::EraseRegions);
952+
passes.push_pass(box mir::transform::break_critical_edges::BreakCriticalEdges);
953+
passes.run_passes(tcx, &mut mir_map);
954+
});
955+
950956
time(time_passes,
951957
"translation",
952958
move || trans::trans_crate(tcx, &mir_map, analysis))

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ mod hair;
4242
pub mod mir_map;
4343
pub mod pretty;
4444
pub mod transform;
45+
pub mod traversal;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use rustc::ty::TyCtxt;
12+
use rustc::mir::repr::*;
13+
use rustc::mir::transform::{MirPass, Pass};
14+
use syntax::ast::NodeId;
15+
16+
use rustc_data_structures::bitvec::BitVector;
17+
18+
use traversal;
19+
20+
pub struct BreakCriticalEdges;
21+
22+
/**
23+
* Breaks critical edges in the MIR.
24+
*
25+
* Critical edges are edges that are neither the only edge leaving a
26+
* block, nor the only edge entering one.
27+
*
28+
* When you want something to happen "along" an edge, you can either
29+
* do at the end of the predecessor block, or at the start of the
30+
* successor block. Critical edges have to be broken in order to prevent
31+
* "edge actions" from affecting other edges.
32+
*
33+
* This function will break those edges by inserting new blocks along them.
34+
*
35+
* A special case is Drop and Call terminators with unwind/cleanup successors,
36+
* They use `invoke` in LLVM, which terminates a block, meaning that code cannot
37+
* be inserted after them, so even if an edge is the only edge leaving a block
38+
* like that, we still insert blocks if the edge is one of many entering the
39+
* target.
40+
*
41+
* NOTE: Simplify CFG will happily undo most of the work this pass does.
42+
*
43+
*/
44+
45+
impl<'tcx> MirPass<'tcx> for BreakCriticalEdges {
46+
fn run_pass(&mut self, _: &TyCtxt<'tcx>, _: NodeId, mir: &mut Mir<'tcx>) {
47+
break_critical_edges(mir);
48+
}
49+
}
50+
51+
impl Pass for BreakCriticalEdges {}
52+
53+
fn break_critical_edges(mir: &mut Mir) {
54+
let mut pred_count = vec![0u32; mir.basic_blocks.len()];
55+
56+
// Build the precedecessor map for the MIR
57+
for (_, data) in traversal::preorder(mir) {
58+
if let Some(ref term) = data.terminator {
59+
for &tgt in term.successors().iter() {
60+
pred_count[tgt.index()] += 1;
61+
}
62+
}
63+
}
64+
65+
let cleanup_map : BitVector = mir.basic_blocks
66+
.iter().map(|bb| bb.is_cleanup).collect();
67+
68+
// We need a place to store the new blocks generated
69+
let mut new_blocks = Vec::new();
70+
71+
let bbs = mir.all_basic_blocks();
72+
let cur_len = mir.basic_blocks.len();
73+
74+
for &bb in &bbs {
75+
let data = mir.basic_block_data_mut(bb);
76+
77+
if let Some(ref mut term) = data.terminator {
78+
let is_invoke = term_is_invoke(term);
79+
let term_span = term.span;
80+
let term_scope = term.scope;
81+
let succs = term.successors_mut();
82+
if succs.len() > 1 || (succs.len() > 0 && is_invoke) {
83+
for tgt in succs {
84+
let num_preds = pred_count[tgt.index()];
85+
if num_preds > 1 {
86+
// It's a critical edge, break it
87+
let goto = Terminator {
88+
span: term_span,
89+
scope: term_scope,
90+
kind: TerminatorKind::Goto { target: *tgt }
91+
};
92+
let mut data = BasicBlockData::new(Some(goto));
93+
data.is_cleanup = cleanup_map.contains(tgt.index());
94+
95+
// Get the index it will be when inserted into the MIR
96+
let idx = cur_len + new_blocks.len();
97+
new_blocks.push(data);
98+
*tgt = BasicBlock::new(idx);
99+
}
100+
}
101+
}
102+
}
103+
}
104+
105+
debug!("Broke {} N edges", new_blocks.len());
106+
107+
mir.basic_blocks.extend_from_slice(&new_blocks);
108+
}
109+
110+
// Returns true if the terminator would use an invoke in LLVM.
111+
fn term_is_invoke(term: &Terminator) -> bool {
112+
match term.kind {
113+
TerminatorKind::Call { cleanup: Some(_), .. } |
114+
TerminatorKind::Drop { unwind: Some(_), .. } => true,
115+
_ => false
116+
}
117+
}

src/librustc_mir/transform/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ pub mod simplify_cfg;
1313
pub mod erase_regions;
1414
pub mod no_landing_pads;
1515
pub mod type_check;
16+
pub mod break_critical_edges;

0 commit comments

Comments
 (0)