Skip to content

Commit 1bb91be

Browse files
committed
Auto merge of #30044 - nikomatsakis:issue-29466, r=arielb1
The graph extent mechanism is not good. I have some ideas for a better replacement, but this PR simply removes it. It also stops recursing on statement scopes and processes them using an "on the heap" stack, which fixes #29466. r? @dotdash
2 parents 1b9a13e + 1fe7525 commit 1bb91be

File tree

9 files changed

+3711
-139
lines changed

9 files changed

+3711
-139
lines changed

src/librustc_mir/build/cfg.rs

-7
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ impl<'tcx> CFG<'tcx> {
2626
&mut self.basic_blocks[blk.index()]
2727
}
2828

29-
pub fn end_point(&self, block: BasicBlock) -> ExecutionPoint {
30-
ExecutionPoint {
31-
block: block,
32-
statement: self.block_data(block).statements.len() as u32,
33-
}
34-
}
35-
3629
pub fn start_new_block(&mut self) -> BasicBlock {
3730
let node_index = self.basic_blocks.len();
3831
self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge));

src/librustc_mir/build/expr/as_rvalue.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
7070
this.cfg.push_assign(block, expr_span, &result, rvalue);
7171

7272
// schedule a shallow free of that memory, lest we unwind:
73-
let extent = this.extent_of_innermost_scope().unwrap();
73+
let extent = this.extent_of_innermost_scope();
7474
this.schedule_drop(expr_span, extent, DropKind::Free, &result, value_ty);
7575

7676
// initialize the box contents:

src/librustc_mir/build/expr/into.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
206206
}
207207
ExprKind::Return { value } => {
208208
unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
209-
let extent = this.extent_of_outermost_scope().unwrap();
209+
let extent = this.extent_of_outermost_scope();
210210
this.exit_scope(expr_span, extent, block, END_BLOCK);
211211
this.cfg.start_new_block().unit()
212212
}

src/librustc_mir/build/matches/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
4242
// suitable extent for all of the bindings in this match. It's
4343
// easiest to do this up front because some of these arms may
4444
// be unreachable or reachable multiple times.
45-
let var_extent = self.extent_of_innermost_scope().unwrap();
45+
let var_extent = self.extent_of_innermost_scope();
4646
for arm in &arms {
4747
self.declare_bindings(var_extent, &arm.patterns[0]);
4848
}

src/librustc_mir/build/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use syntax::codemap::Span;
1919

2020
struct Builder<'a, 'tcx: 'a> {
2121
hir: Cx<'a, 'tcx>,
22-
extents: FnvHashMap<CodeExtent, Vec<GraphExtent>>,
2322
cfg: CFG<'tcx>,
2423
scopes: Vec<scope::Scope<'tcx>>,
2524
loop_scopes: Vec<scope::LoopScope>,
@@ -92,7 +91,6 @@ pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>,
9291
let mut builder = Builder {
9392
hir: hir,
9493
cfg: cfg,
95-
extents: FnvHashMap(),
9694
scopes: vec![],
9795
loop_scopes: vec![],
9896
temp_decls: temp_decls,
@@ -117,7 +115,6 @@ pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>,
117115

118116
Mir {
119117
basic_blocks: builder.cfg.basic_blocks,
120-
extents: builder.extents,
121118
var_decls: builder.var_decls,
122119
arg_decls: arg_decls,
123120
temp_decls: builder.temp_decls,

src/librustc_mir/build/scope.rs

+31-43
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ use syntax::codemap::Span;
9494

9595
pub struct Scope<'tcx> {
9696
extent: CodeExtent,
97-
exits: Vec<ExecutionPoint>,
9897
drops: Vec<(DropKind, Span, Lvalue<'tcx>)>,
9998
cached_block: Option<BasicBlock>,
10099
}
@@ -116,7 +115,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
116115
-> BlockAnd<R>
117116
where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>
118117
{
119-
let extent = self.extent_of_innermost_scope().unwrap();
118+
let extent = self.extent_of_innermost_scope();
120119
let loop_scope = LoopScope {
121120
extent: extent.clone(),
122121
continue_block: loop_block,
@@ -128,60 +127,51 @@ impl<'a,'tcx> Builder<'a,'tcx> {
128127
r
129128
}
130129

131-
/// Start a scope. The closure `f` should translate the contents
132-
/// of the scope. See module comment for more details.
133-
pub fn in_scope<F, R>(&mut self, extent: CodeExtent, block: BasicBlock, f: F) -> BlockAnd<R>
130+
/// Convenience wrapper that pushes a scope and then executes `f`
131+
/// to build its contents, popping the scope afterwards.
132+
pub fn in_scope<F, R>(&mut self, extent: CodeExtent, mut block: BasicBlock, f: F) -> BlockAnd<R>
134133
where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>
135134
{
136135
debug!("in_scope(extent={:?}, block={:?})", extent, block);
136+
self.push_scope(extent, block);
137+
let rv = unpack!(block = f(self));
138+
self.pop_scope(extent, block);
139+
debug!("in_scope: exiting extent={:?} block={:?}", extent, block);
140+
block.and(rv)
141+
}
137142

138-
let start_point = self.cfg.end_point(block);
143+
/// Push a scope onto the stack. You can then build code in this
144+
/// scope and call `pop_scope` afterwards. Note that these two
145+
/// calls must be paired; using `in_scope` as a convenience
146+
/// wrapper maybe preferable.
147+
pub fn push_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
148+
debug!("push_scope({:?}, {:?})", extent, block);
139149

140150
// push scope, execute `f`, then pop scope again
141151
self.scopes.push(Scope {
142152
extent: extent.clone(),
143153
drops: vec![],
144-
exits: vec![],
145154
cached_block: None,
146155
});
147-
let BlockAnd(fallthrough_block, rv) = f(self);
148-
let mut scope = self.scopes.pop().unwrap();
156+
}
157+
158+
/// Pops a scope, which should have extent `extent`, adding any
159+
/// drops onto the end of `block` that are needed. This must
160+
/// match 1-to-1 with `push_scope`.
161+
pub fn pop_scope(&mut self, extent: CodeExtent, block: BasicBlock) {
162+
debug!("pop_scope({:?}, {:?})", extent, block);
163+
let scope = self.scopes.pop().unwrap();
164+
165+
assert_eq!(scope.extent, extent);
149166

150167
// add in any drops needed on the fallthrough path (any other
151168
// exiting paths, such as those that arise from `break`, will
152169
// have drops already)
153170
for (kind, span, lvalue) in scope.drops {
154-
self.cfg.push_drop(fallthrough_block, span, kind, &lvalue);
171+
self.cfg.push_drop(block, span, kind, &lvalue);
155172
}
156-
157-
// add the implicit fallthrough edge
158-
scope.exits.push(self.cfg.end_point(fallthrough_block));
159-
160-
// compute the extent from start to finish and store it in the graph
161-
let graph_extent = self.graph_extent(start_point, scope.exits);
162-
self.extents.entry(extent)
163-
.or_insert(vec![])
164-
.push(graph_extent);
165-
166-
debug!("in_scope: exiting extent={:?} fallthrough_block={:?}", extent, fallthrough_block);
167-
fallthrough_block.and(rv)
168173
}
169174

170-
/// Creates a graph extent (SEME region) from an entry point and
171-
/// exit points.
172-
fn graph_extent(&self, entry: ExecutionPoint, exits: Vec<ExecutionPoint>) -> GraphExtent {
173-
if exits.len() == 1 && entry.block == exits[0].block {
174-
GraphExtent {
175-
entry: entry,
176-
exit: GraphExtentExit::Statement(exits[0].statement),
177-
}
178-
} else {
179-
GraphExtent {
180-
entry: entry,
181-
exit: GraphExtentExit::Points(exits),
182-
}
183-
}
184-
}
185175

186176
/// Finds the loop scope for a given label. This is used for
187177
/// resolving `break` and `continue`.
@@ -232,8 +222,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
232222
for &(kind, drop_span, ref lvalue) in &scope.drops {
233223
self.cfg.push_drop(block, drop_span, kind, lvalue);
234224
}
235-
236-
scope.exits.push(self.cfg.end_point(block));
237225
}
238226

239227
self.cfg.terminate(block, Terminator::Goto { target: target });
@@ -272,12 +260,12 @@ impl<'a,'tcx> Builder<'a,'tcx> {
272260
}
273261
}
274262

275-
pub fn extent_of_innermost_scope(&self) -> Option<CodeExtent> {
276-
self.scopes.last().map(|scope| scope.extent)
263+
pub fn extent_of_innermost_scope(&self) -> CodeExtent {
264+
self.scopes.last().map(|scope| scope.extent).unwrap()
277265
}
278266

279-
pub fn extent_of_outermost_scope(&self) -> Option<CodeExtent> {
280-
self.scopes.first().map(|scope| scope.extent)
267+
pub fn extent_of_outermost_scope(&self) -> CodeExtent {
268+
self.scopes.first().map(|scope| scope.extent).unwrap()
281269
}
282270
}
283271

src/librustc_mir/build/stmt.rs

+58-36
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,70 @@ use repr::*;
1414

1515
impl<'a,'tcx> Builder<'a,'tcx> {
1616
pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<'tcx>>) -> BlockAnd<()> {
17-
for stmt in stmts {
18-
unpack!(block = self.stmt(block, stmt));
19-
}
20-
block.unit()
21-
}
22-
23-
pub fn stmt(&mut self, mut block: BasicBlock, stmt: StmtRef<'tcx>) -> BlockAnd<()> {
17+
// This convoluted structure is to avoid using recursion as we walk down a list
18+
// of statements. Basically, the structure we get back is something like:
19+
//
20+
// let x = <init> in {
21+
// let y = <init> in {
22+
// expr1;
23+
// expr2;
24+
// }
25+
// }
26+
//
27+
// To process this, we keep a stack of (Option<CodeExtent>,
28+
// vec::IntoIter<Stmt>) pairs. At each point we pull off the
29+
// top most pair and extract one statement from the
30+
// iterator. Once it's complete, we pop the scope from the
31+
// first half the pair.
2432
let this = self;
25-
let Stmt { span, kind } = this.hir.mirror(stmt);
26-
match kind {
27-
StmtKind::Let { remainder_scope,
28-
init_scope,
29-
pattern,
30-
initializer: Some(initializer),
31-
stmts } => {
32-
this.in_scope(remainder_scope, block, |this| {
33-
unpack!(block = this.in_scope(init_scope, block, |this| {
34-
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
33+
let mut stmt_lists = vec![(None, stmts.into_iter())];
34+
while !stmt_lists.is_empty() {
35+
let stmt = {
36+
let &mut (_, ref mut stmts) = stmt_lists.last_mut().unwrap();
37+
stmts.next()
38+
};
39+
40+
let stmt = match stmt {
41+
Some(stmt) => stmt,
42+
None => {
43+
let (extent, _) = stmt_lists.pop().unwrap();
44+
if let Some(extent) = extent {
45+
this.pop_scope(extent, block);
46+
}
47+
continue
48+
}
49+
};
50+
51+
let Stmt { span, kind } = this.hir.mirror(stmt);
52+
match kind {
53+
StmtKind::Let { remainder_scope, init_scope, pattern, initializer, stmts } => {
54+
this.push_scope(remainder_scope, block);
55+
stmt_lists.push((Some(remainder_scope), stmts.into_iter()));
56+
unpack!(block = this.in_scope(init_scope, block, move |this| {
57+
// FIXME #30046 ^~~~
58+
match initializer {
59+
Some(initializer) => {
60+
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
61+
}
62+
None => {
63+
this.declare_bindings(remainder_scope, &pattern);
64+
block.unit()
65+
}
66+
}
3567
}));
36-
this.stmts(block, stmts)
37-
})
38-
}
68+
}
3969

40-
StmtKind::Let { remainder_scope, init_scope, pattern, initializer: None, stmts } => {
41-
this.in_scope(remainder_scope, block, |this| {
42-
unpack!(block = this.in_scope(init_scope, block, |this| {
43-
this.declare_bindings(remainder_scope, &pattern);
70+
StmtKind::Expr { scope, expr } => {
71+
unpack!(block = this.in_scope(scope, block, |this| {
72+
let expr = this.hir.mirror(expr);
73+
let temp = this.temp(expr.ty.clone());
74+
unpack!(block = this.into(&temp, block, expr));
75+
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
4476
block.unit()
4577
}));
46-
this.stmts(block, stmts)
47-
})
48-
}
49-
50-
StmtKind::Expr { scope, expr } => {
51-
this.in_scope(scope, block, |this| {
52-
let expr = this.hir.mirror(expr);
53-
let temp = this.temp(expr.ty.clone());
54-
unpack!(block = this.into(&temp, block, expr));
55-
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
56-
block.unit()
57-
})
78+
}
5879
}
5980
}
81+
block.unit()
6082
}
6183
}

src/librustc_mir/repr.rs

+12-47
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010

1111
use rustc::middle::const_eval::ConstVal;
1212
use rustc::middle::def_id::DefId;
13-
use rustc::middle::region::CodeExtent;
1413
use rustc::middle::subst::Substs;
1514
use rustc::middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty};
1615
use rustc_back::slice;
17-
use rustc_data_structures::fnv::FnvHashMap;
1816
use rustc_front::hir::InlineAsm;
1917
use syntax::ast::Name;
2018
use syntax::codemap::Span;
@@ -23,15 +21,24 @@ use std::u32;
2321

2422
/// Lowered representation of a single function.
2523
pub struct Mir<'tcx> {
24+
/// List of basic blocks. References to basic block use a newtyped index type `BasicBlock`
25+
/// that indexes into this vector.
2626
pub basic_blocks: Vec<BasicBlockData<'tcx>>,
2727

28+
/// Return type of the function.
2829
pub return_ty: FnOutput<'tcx>,
2930

30-
// for every node id
31-
pub extents: FnvHashMap<CodeExtent, Vec<GraphExtent>>,
32-
31+
/// Variables: these are stack slots corresponding to user variables. They may be
32+
/// assigned many times.
3333
pub var_decls: Vec<VarDecl<'tcx>>,
34+
35+
/// Args: these are stack slots corresponding to the input arguments.
3436
pub arg_decls: Vec<ArgDecl<'tcx>>,
37+
38+
/// Temp declarations: stack slots that for temporaries created by
39+
/// the compiler. These are assigned once, but they are not SSA
40+
/// values in that it is possible to borrow them and mutate them
41+
/// through the resulting reference.
3542
pub temp_decls: Vec<TempDecl<'tcx>>,
3643
}
3744

@@ -147,48 +154,6 @@ pub struct ArgDecl<'tcx> {
147154
pub ty: Ty<'tcx>,
148155
}
149156

150-
///////////////////////////////////////////////////////////////////////////
151-
// Graph extents
152-
153-
/// A moment in the flow of execution. It corresponds to a point in
154-
/// between two statements:
155-
///
156-
/// BB[block]:
157-
/// <--- if statement == 0
158-
/// STMT[0]
159-
/// <--- if statement == 1
160-
/// STMT[1]
161-
/// ...
162-
/// <--- if statement == n-1
163-
/// STMT[n-1]
164-
/// <--- if statement == n
165-
///
166-
/// where the block has `n` statements.
167-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
168-
pub struct ExecutionPoint {
169-
pub block: BasicBlock,
170-
pub statement: u32,
171-
}
172-
173-
/// A single-entry-multiple-exit region in the graph. We build one of
174-
/// these for every node-id during MIR construction. By construction
175-
/// we are assured that the entry dominates all points within, and
176-
/// that, for every interior point X, it is postdominated by some exit.
177-
pub struct GraphExtent {
178-
pub entry: ExecutionPoint,
179-
pub exit: GraphExtentExit,
180-
}
181-
182-
pub enum GraphExtentExit {
183-
/// `Statement(X)`: a very common special case covering a span
184-
/// that is local to a single block. It starts at the entry point
185-
/// and extends until the start of statement `X` (non-inclusive).
186-
Statement(u32),
187-
188-
/// The more general case where the exits are a set of points.
189-
Points(Vec<ExecutionPoint>),
190-
}
191-
192157
///////////////////////////////////////////////////////////////////////////
193158
// BasicBlock
194159

0 commit comments

Comments
 (0)