@@ -131,6 +131,9 @@ pub struct Scope<'tcx> {
131131
132132 /// The cache for drop chain on "generator drop" exit.
133133 cached_generator_drop : Option < BasicBlock > ,
134+
135+ /// The cache for drop chain on "unwind" exit.
136+ cached_unwind : CachedBlock ,
134137}
135138
136139#[ derive( Debug ) ]
@@ -233,8 +236,10 @@ impl<'tcx> Scope<'tcx> {
233236 self . cached_exits . clear ( ) ;
234237
235238 if !storage_only {
236- // the current generator drop ignores storage but refers to top-of-scope
239+ // the current generator drop and unwind ignore
240+ // storage but refer to top-of-scope
237241 self . cached_generator_drop = None ;
242+ self . cached_unwind . invalidate ( ) ;
238243 }
239244
240245 if !storage_only && !this_scope_only {
@@ -246,26 +251,6 @@ impl<'tcx> Scope<'tcx> {
246251 }
247252 }
248253
249- /// Returns the cached entrypoint for diverging exit from this scope.
250- ///
251- /// Precondition: the caches must be fully filled (i.e. diverge_cleanup is called) in order for
252- /// this method to work correctly.
253- fn cached_block ( & self , generator_drop : bool ) -> Option < BasicBlock > {
254- let mut drops = self . drops . iter ( ) . rev ( ) . filter_map ( |data| {
255- match data. kind {
256- DropKind :: Value { cached_block } => {
257- Some ( cached_block. get ( generator_drop) )
258- }
259- DropKind :: Storage => None
260- }
261- } ) ;
262- if let Some ( cached_block) = drops. next ( ) {
263- Some ( cached_block. expect ( "drop cache is not filled" ) )
264- } else {
265- None
266- }
267- }
268-
269254 /// Given a span and this scope's visibility scope, make a SourceInfo.
270255 fn source_info ( & self , span : Span ) -> SourceInfo {
271256 SourceInfo {
@@ -374,7 +359,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
374359 needs_cleanup : false ,
375360 drops : vec ! [ ] ,
376361 cached_generator_drop : None ,
377- cached_exits : FxHashMap ( )
362+ cached_exits : FxHashMap ( ) ,
363+ cached_unwind : CachedBlock :: default ( ) ,
378364 } ) ;
379365 }
380366
@@ -500,15 +486,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
500486 TerminatorKind :: Goto { target : b } ) ;
501487 b
502488 } ;
489+
490+ // End all regions for scopes out of which we are breaking.
491+ self . cfg . push_end_region ( self . hir . tcx ( ) , block, src_info, scope. region_scope ) ;
492+
503493 unpack ! ( block = build_scope_drops( & mut self . cfg,
504494 scope,
505495 rest,
506496 block,
507497 self . arg_count,
508498 true ) ) ;
509-
510- // End all regions for scopes out of which we are breaking.
511- self . cfg . push_end_region ( self . hir . tcx ( ) , block, src_info, scope. region_scope ) ;
512499 }
513500
514501 self . cfg . terminate ( block, src_info, TerminatorKind :: GeneratorDrop ) ;
@@ -841,23 +828,45 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
841828 let source_info = scope. source_info ( drop_data. span ) ;
842829 match drop_data. kind {
843830 DropKind :: Value { .. } => {
844- // Try to find the next block with its cached block
845- // for us to diverge into in case the drop panics.
831+ // Try to find the next block with its cached block for us to
832+ // diverge into, either a previous block in this current scope or
833+ // the top of the previous scope.
834+ //
835+ // If it wasn't for EndRegion, we could just chain all the DropData
836+ // together and pick the first DropKind::Value. Please do that
837+ // when we replace EndRegion with NLL.
846838 let on_diverge = iter. clone ( ) . filter_map ( |dd| {
847839 match dd. kind {
848840 DropKind :: Value { cached_block } => Some ( cached_block) ,
849841 DropKind :: Storage => None
850842 }
851- } ) . map ( |cached_block| {
852- cached_block
853- . get ( generator_drop)
854- . unwrap_or_else ( || span_bug ! ( drop_data. span, "cached block not present?" ) )
855- } ) . next ( ) ;
856- // If there’s no `cached_block`s within current scope,
857- // we must look for one in the enclosing scope.
858- let on_diverge = on_diverge. or_else ( || {
859- earlier_scopes. iter ( ) . rev ( ) . flat_map ( |s| s. cached_block ( generator_drop) ) . next ( )
843+ } ) . next ( ) . or_else ( || {
844+ if earlier_scopes. iter ( ) . any ( |scope| scope. needs_cleanup ) {
845+ // If *any* scope requires cleanup code to be run,
846+ // we must use the cached unwind from the *topmost*
847+ // scope, to ensure all EndRegions from surrounding
848+ // scopes are executed before the drop code runs.
849+ Some ( earlier_scopes. last ( ) . unwrap ( ) . cached_unwind )
850+ } else {
851+ // We don't need any further cleanup, so return None
852+ // to avoid creating a landing pad. We can skip
853+ // EndRegions because all local regions end anyway
854+ // when the function unwinds.
855+ //
856+ // This is an important optimization because LLVM is
857+ // terrible at optimizing landing pads. FIXME: I think
858+ // it would be cleaner and better to do this optimization
859+ // in SimplifyCfg instead of here.
860+ None
861+ }
862+ } ) ;
863+
864+ let on_diverge = on_diverge. map ( |cached_block| {
865+ cached_block. get ( generator_drop) . unwrap_or_else ( || {
866+ span_bug ! ( drop_data. span, "cached block not present?" )
867+ } )
860868 } ) ;
869+
861870 let next = cfg. start_new_block ( ) ;
862871 cfg. terminate ( block, source_info, TerminatorKind :: Drop {
863872 location : drop_data. location . clone ( ) ,
@@ -948,14 +957,21 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
948957 } ;
949958 }
950959
951- // Finally, push the EndRegion block, used by mir-borrowck. (Block
952- // becomes trivial goto after pass that removes all EndRegions.)
953- {
954- let block = cfg. start_new_cleanup_block ( ) ;
955- cfg. push_end_region ( tcx, block, source_info ( span) , scope. region_scope ) ;
956- cfg. terminate ( block, source_info ( span) , TerminatorKind :: Goto { target : target } ) ;
957- target = block
958- }
960+ // Finally, push the EndRegion block, used by mir-borrowck, and set
961+ // `cached_unwind` to point to it (Block becomes trivial goto after
962+ // pass that removes all EndRegions).
963+ target = {
964+ let cached_block = scope. cached_unwind . ref_mut ( generator_drop) ;
965+ if let Some ( cached_block) = * cached_block {
966+ cached_block
967+ } else {
968+ let block = cfg. start_new_cleanup_block ( ) ;
969+ cfg. push_end_region ( tcx, block, source_info ( span) , scope. region_scope ) ;
970+ cfg. terminate ( block, source_info ( span) , TerminatorKind :: Goto { target : target } ) ;
971+ * cached_block = Some ( block) ;
972+ block
973+ }
974+ } ;
959975
960976 debug ! ( "build_diverge_scope({:?}, {:?}) = {:?}" , scope, span, target) ;
961977
0 commit comments