@@ -94,6 +94,7 @@ use rustc::ty::{Ty, TyCtxt};
9494use rustc:: mir:: repr:: * ;
9595use syntax:: codemap:: Span ;
9696use rustc_data_structures:: indexed_vec:: Idx ;
97+ use rustc_data_structures:: fnv:: FnvHashMap ;
9798
9899pub struct Scope < ' tcx > {
99100 /// the scope-id within the scope_auxiliary
@@ -127,12 +128,8 @@ pub struct Scope<'tcx> {
127128 /// stage.
128129 free : Option < FreeData < ' tcx > > ,
129130
130- /// The cached block for the cleanups-on-diverge path. This block
131- /// contains a block that will just do a RESUME to an appropriate
132- /// place. This block does not execute any of the drops or free:
133- /// each of those has their own cached-blocks, which will branch
134- /// to this point.
135- cached_block : Option < BasicBlock >
131+ /// The cache for drop chain on “normal” exit into a particular BasicBlock.
132+ cached_exits : FnvHashMap < ( BasicBlock , CodeExtent ) , BasicBlock > ,
136133}
137134
138135struct DropData < ' tcx > {
@@ -172,7 +169,7 @@ pub struct LoopScope {
172169 pub continue_block : BasicBlock ,
173170 /// Block to branch into when the loop terminates (either by being `break`-en out from, or by
174171 /// having its condition to become false)
175- pub break_block : BasicBlock , // where to go on a `break
172+ pub break_block : BasicBlock ,
176173 /// Indicates the reachability of the break_block for this loop
177174 pub might_break : bool
178175}
@@ -183,7 +180,7 @@ impl<'tcx> Scope<'tcx> {
183180 /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
184181 /// larger extent of code.
185182 fn invalidate_cache ( & mut self ) {
186- self . cached_block = None ;
183+ self . cached_exits = FnvHashMap ( ) ;
187184 for dropdata in & mut self . drops {
188185 dropdata. cached_block = None ;
189186 }
@@ -192,7 +189,7 @@ impl<'tcx> Scope<'tcx> {
192189 }
193190 }
194191
195- /// Returns the cached block for this scope.
192+ /// Returns the cached entrypoint for diverging exit from this scope.
196193 ///
197194 /// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for
198195 /// this method to work correctly.
@@ -270,7 +267,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
270267 extent : extent,
271268 drops : vec ! [ ] ,
272269 free : None ,
273- cached_block : None ,
270+ cached_exits : FnvHashMap ( )
274271 } ) ;
275272 self . scope_auxiliary . push ( ScopeAuxiliary {
276273 extent : extent,
@@ -314,13 +311,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
314311 . unwrap_or_else ( ||{
315312 span_bug ! ( span, "extent {:?} does not enclose" , extent)
316313 } ) ;
317-
314+ let len = self . scopes . len ( ) ;
315+ assert ! ( scope_count < len, "should not use `exit_scope` to pop ALL scopes" ) ;
318316 let tmp = self . get_unit_temp ( ) ;
319- for ( idx, ref scope) in self . scopes . iter ( ) . enumerate ( ) . rev ( ) . take ( scope_count) {
320- unpack ! ( block = build_scope_drops( & mut self . cfg,
321- scope,
322- & self . scopes[ ..idx] ,
323- block) ) ;
317+ {
318+ let mut rest = & mut self . scopes [ ( len - scope_count) ..] ;
319+ while let Some ( ( scope, rest_) ) = { rest} . split_last_mut ( ) {
320+ rest = rest_;
321+ block = if let Some ( & e) = scope. cached_exits . get ( & ( target, extent) ) {
322+ self . cfg . terminate ( block, scope. source_info ( span) ,
323+ TerminatorKind :: Goto { target : e } ) ;
324+ return ;
325+ } else {
326+ let b = self . cfg . start_new_block ( ) ;
327+ self . cfg . terminate ( block, scope. source_info ( span) ,
328+ TerminatorKind :: Goto { target : b } ) ;
329+ scope. cached_exits . insert ( ( target, extent) , b) ;
330+ b
331+ } ;
332+ unpack ! ( block = build_scope_drops( & mut self . cfg, scope, rest, block) ) ;
324333 if let Some ( ref free_data) = scope. free {
325334 let next = self . cfg . start_new_block ( ) ;
326335 let free = build_free ( self . hir . tcx ( ) , & tmp, free_data, next) ;
@@ -331,14 +340,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
331340 . postdoms
332341 . push ( self . cfg . current_location ( block) ) ;
333342 }
334-
335- assert ! ( scope_count < self . scopes. len( ) ,
336- "should never use `exit_scope` to pop *ALL* scopes" ) ;
337- let scope = self . scopes . iter ( ) . rev ( ) . skip ( scope_count)
338- . next ( )
339- . unwrap ( ) ;
340- self . cfg . terminate ( block,
341- scope. source_info ( span) ,
343+ }
344+ let scope = & self . scopes [ len - scope_count] ;
345+ self . cfg . terminate ( block, scope. source_info ( span) ,
342346 TerminatorKind :: Goto { target : target } ) ;
343347 }
344348
@@ -506,10 +510,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
506510 resumeblk
507511 } ;
508512
509- for scope in scopes {
513+ for scope in scopes. iter_mut ( ) . filter ( |s| !s . drops . is_empty ( ) || s . free . is_some ( ) ) {
510514 target = build_diverge_scope ( hir. tcx ( ) , cfg, & unit_temp, scope, target) ;
511515 }
512-
513516 Some ( target)
514517 }
515518
@@ -534,7 +537,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
534537 next_target. unit ( )
535538 }
536539
537-
540+ /// Utility function for *non*-scope code to build their own drops
538541 pub fn build_drop_and_replace ( & mut self ,
539542 block : BasicBlock ,
540543 span : Span ,
0 commit comments