@@ -131,6 +131,9 @@ pub struct Scope<'tcx> {
131
131
132
132
/// The cache for drop chain on "generator drop" exit.
133
133
cached_generator_drop : Option < BasicBlock > ,
134
+
135
+ /// The cache for drop chain on "unwind" exit.
136
+ cached_unwind : CachedBlock ,
134
137
}
135
138
136
139
#[ derive( Debug ) ]
@@ -233,8 +236,10 @@ impl<'tcx> Scope<'tcx> {
233
236
self . cached_exits . clear ( ) ;
234
237
235
238
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
237
241
self . cached_generator_drop = None ;
242
+ self . cached_unwind . invalidate ( ) ;
238
243
}
239
244
240
245
if !storage_only && !this_scope_only {
@@ -246,26 +251,6 @@ impl<'tcx> Scope<'tcx> {
246
251
}
247
252
}
248
253
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
-
269
254
/// Given a span and this scope's visibility scope, make a SourceInfo.
270
255
fn source_info ( & self , span : Span ) -> SourceInfo {
271
256
SourceInfo {
@@ -374,7 +359,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
374
359
needs_cleanup : false ,
375
360
drops : vec ! [ ] ,
376
361
cached_generator_drop : None ,
377
- cached_exits : FxHashMap ( )
362
+ cached_exits : FxHashMap ( ) ,
363
+ cached_unwind : CachedBlock :: default ( ) ,
378
364
} ) ;
379
365
}
380
366
@@ -500,15 +486,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
500
486
TerminatorKind :: Goto { target : b } ) ;
501
487
b
502
488
} ;
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
+
503
493
unpack ! ( block = build_scope_drops( & mut self . cfg,
504
494
scope,
505
495
rest,
506
496
block,
507
497
self . arg_count,
508
498
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 ) ;
512
499
}
513
500
514
501
self . cfg . terminate ( block, src_info, TerminatorKind :: GeneratorDrop ) ;
@@ -841,23 +828,45 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
841
828
let source_info = scope. source_info ( drop_data. span ) ;
842
829
match drop_data. kind {
843
830
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.
846
838
let on_diverge = iter. clone ( ) . filter_map ( |dd| {
847
839
match dd. kind {
848
840
DropKind :: Value { cached_block } => Some ( cached_block) ,
849
841
DropKind :: Storage => None
850
842
}
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
+ } )
860
868
} ) ;
869
+
861
870
let next = cfg. start_new_block ( ) ;
862
871
cfg. terminate ( block, source_info, TerminatorKind :: Drop {
863
872
location : drop_data. location . clone ( ) ,
@@ -948,14 +957,21 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
948
957
} ;
949
958
}
950
959
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
+ } ;
959
975
960
976
debug ! ( "build_diverge_scope({:?}, {:?}) = {:?}" , scope, span, target) ;
961
977
0 commit comments