@@ -83,9 +83,10 @@ use rustc::middle::region;
83
83
use rustc:: ty:: Ty ;
84
84
use rustc:: hir;
85
85
use rustc:: mir:: * ;
86
- use syntax_pos:: { Span } ;
86
+ use syntax_pos:: { Span , DUMMY_SP } ;
87
87
use rustc_data_structures:: fx:: FxHashMap ;
88
88
use std:: collections:: hash_map:: Entry ;
89
+ use std:: mem;
89
90
90
91
#[ derive( Debug ) ]
91
92
pub struct Scope < ' tcx > {
@@ -98,16 +99,10 @@ pub struct Scope<'tcx> {
98
99
/// the span of that region_scope
99
100
region_scope_span : Span ,
100
101
101
- /// Whether there's anything to do for the cleanup path, that is,
102
+ /// Whether we need landing pads for the cleanup path, that is,
102
103
/// when unwinding through this scope. This includes destructors,
103
- /// but not StorageDead statements, which don't get emitted at all
104
- /// for unwinding, for several reasons:
105
- /// * clang doesn't emit llvm.lifetime.end for C++ unwinding
106
- /// * LLVM's memory dependency analysis can't handle it atm
107
- /// * polluting the cleanup MIR with StorageDead creates
108
- /// landing pads even though there's no actual destructors
109
- /// * freeing up stack space has no effect during unwinding
110
- needs_cleanup : bool ,
104
+ /// but not StorageDead statements.
105
+ cleanup_may_panic : bool ,
111
106
112
107
/// set of places to drop when exiting this scope. This starts
113
108
/// out empty but grows as variables are declared during the
@@ -348,7 +343,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
348
343
source_scope : vis_scope,
349
344
region_scope : region_scope. 0 ,
350
345
region_scope_span : region_scope. 1 . span ,
351
- needs_cleanup : false ,
346
+ cleanup_may_panic : false ,
352
347
drops : vec ! [ ] ,
353
348
cached_generator_drop : None ,
354
349
cached_exits : Default :: default ( ) ,
@@ -411,7 +406,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
411
406
412
407
// If we are emitting a `drop` statement, we need to have the cached
413
408
// diverge cleanup pads ready in case that drop panics.
414
- let may_panic = self . scopes [ ( len - scope_count) ..] . iter ( ) . any ( |s| s. needs_cleanup ) ;
409
+ let may_panic = self . scopes [ ( len - scope_count) ..] . iter ( ) . any ( |s| s. cleanup_may_panic ) ;
415
410
if may_panic {
416
411
self . diverge_cleanup ( ) ;
417
412
}
@@ -466,10 +461,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
466
461
/// This path terminates in GeneratorDrop. Returns the start of the path.
467
462
/// None indicates there’s no cleanup to do at this point.
468
463
pub fn generator_drop_cleanup ( & mut self ) -> Option < BasicBlock > {
469
- if !self . scopes . iter ( ) . any ( |scope| scope. needs_cleanup ) {
470
- return None ;
471
- }
472
-
473
464
// Fill in the cache for unwinds
474
465
self . diverge_cleanup_gen ( true ) ;
475
466
@@ -480,10 +471,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
480
471
let result = block;
481
472
482
473
while let Some ( scope) = scopes. next ( ) {
483
- if !scope. needs_cleanup {
484
- continue ;
485
- }
486
-
487
474
block = if let Some ( b) = scope. cached_generator_drop {
488
475
self . cfg . terminate ( block, src_info,
489
476
TerminatorKind :: Goto { target : b } ) ;
@@ -623,7 +610,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
623
610
624
611
// Schedule an abort block - this is used for some ABIs that cannot unwind
625
612
pub fn schedule_abort ( & mut self ) -> BasicBlock {
626
- self . scopes [ 0 ] . needs_cleanup = true ;
613
+ self . scopes [ 0 ] . cleanup_may_panic = true ;
627
614
let abortblk = self . cfg . start_new_cleanup_block ( ) ;
628
615
let source_info = self . scopes [ 0 ] . source_info ( self . fn_span ) ;
629
616
self . cfg . terminate ( abortblk, source_info, TerminatorKind :: Abort ) ;
@@ -735,7 +722,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
735
722
scope. invalidate_cache ( !needs_drop, this_scope) ;
736
723
if this_scope {
737
724
if let DropKind :: Value { .. } = drop_kind {
738
- scope. needs_cleanup = true ;
725
+ scope. cleanup_may_panic = true ;
739
726
}
740
727
741
728
let region_scope_span = region_scope. span ( self . hir . tcx ( ) ,
@@ -900,12 +887,6 @@ fn build_scope_drops<'tcx>(
900
887
// drops panic (panicking while unwinding will abort, so there's no need for
901
888
// another set of arrows). The drops for the unwind path should have already
902
889
// been generated by `diverge_cleanup_gen`.
903
- //
904
- // The code in this function reads from right to left.
905
- // Storage dead drops have to be done left to right (since we can only push
906
- // to the end of a Vec). So, we find the next drop and then call
907
- // push_storage_deads which will iterate backwards through them so that
908
- // they are added in the correct order.
909
890
910
891
let mut unwind_blocks = scope. drops . iter ( ) . rev ( ) . filter_map ( |drop_data| {
911
892
if let DropKind :: Value { cached_block } = drop_data. kind {
@@ -936,11 +917,6 @@ fn build_scope_drops<'tcx>(
936
917
block = next;
937
918
}
938
919
DropKind :: Storage => {
939
- // We do not need to emit StorageDead for generator drops
940
- if generator_drop {
941
- continue
942
- }
943
-
944
920
// Drop the storage for both value and storage drops.
945
921
// Only temps and vars need their storage dead.
946
922
match drop_data. location {
@@ -958,12 +934,12 @@ fn build_scope_drops<'tcx>(
958
934
block. unit ( )
959
935
}
960
936
961
- fn build_diverge_scope < ' tcx > ( cfg : & mut CFG < ' tcx > ,
962
- span : Span ,
963
- scope : & mut Scope < ' tcx > ,
964
- mut target : BasicBlock ,
965
- generator_drop : bool )
966
- -> BasicBlock
937
+ fn build_diverge_scope ( cfg : & mut CFG < ' tcx > ,
938
+ span : Span ,
939
+ scope : & mut Scope < ' tcx > ,
940
+ mut target : BasicBlock ,
941
+ generator_drop : bool )
942
+ -> BasicBlock
967
943
{
968
944
// Build up the drops in **reverse** order. The end result will
969
945
// look like:
@@ -981,7 +957,13 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
981
957
scope : source_scope
982
958
} ;
983
959
984
- // Next, build up the drops. Here we iterate the vector in
960
+ // We keep track of StorageDead statements to prepend to our current block
961
+ // and store them here, in reverse order.
962
+ let mut storage_deads = vec ! [ ] ;
963
+
964
+ let mut target_built_by_us = false ;
965
+
966
+ // Build up the drops. Here we iterate the vector in
985
967
// *forward* order, so that we generate drops[0] first (right to
986
968
// left in diagram above).
987
969
for ( j, drop_data) in scope. drops . iter_mut ( ) . enumerate ( ) {
@@ -994,28 +976,70 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>,
994
976
// frozen stack)? In particular, the intent may have been to
995
977
// match the behavior of clang, but on inspection eddyb says
996
978
// this is not what clang does.
997
- let cached_block = match drop_data. kind {
998
- DropKind :: Value { ref mut cached_block } => cached_block. ref_mut ( generator_drop) ,
999
- DropKind :: Storage => continue
1000
- } ;
1001
- target = if let Some ( cached_block) = * cached_block {
1002
- cached_block
1003
- } else {
1004
- let block = cfg. start_new_cleanup_block ( ) ;
1005
- cfg. terminate ( block, source_info ( drop_data. span ) ,
1006
- TerminatorKind :: Drop {
1007
- location : drop_data. location . clone ( ) ,
1008
- target,
1009
- unwind : None
1010
- } ) ;
1011
- * cached_block = Some ( block) ;
1012
- block
979
+ match drop_data. kind {
980
+ DropKind :: Storage => {
981
+ // Only temps and vars need their storage dead.
982
+ match drop_data. location {
983
+ Place :: Base ( PlaceBase :: Local ( index) ) => {
984
+ storage_deads. push ( Statement {
985
+ source_info : source_info ( drop_data. span ) ,
986
+ kind : StatementKind :: StorageDead ( index)
987
+ } ) ;
988
+ }
989
+ _ => unreachable ! ( ) ,
990
+ } ;
991
+ }
992
+ DropKind :: Value { ref mut cached_block } => {
993
+ let cached_block = cached_block. ref_mut ( generator_drop) ;
994
+ target = if let Some ( cached_block) = * cached_block {
995
+ storage_deads. clear ( ) ;
996
+ target_built_by_us = false ;
997
+ cached_block
998
+ } else {
999
+ push_storage_deads (
1000
+ cfg, & mut target, & mut storage_deads, target_built_by_us, source_scope) ;
1001
+ let block = cfg. start_new_cleanup_block ( ) ;
1002
+ cfg. terminate ( block, source_info ( drop_data. span ) ,
1003
+ TerminatorKind :: Drop {
1004
+ location : drop_data. location . clone ( ) ,
1005
+ target,
1006
+ unwind : None
1007
+ } ) ;
1008
+ * cached_block = Some ( block) ;
1009
+ target_built_by_us = true ;
1010
+ block
1011
+ } ;
1012
+ }
1013
1013
} ;
1014
1014
}
1015
-
1015
+ push_storage_deads ( cfg , & mut target , & mut storage_deads , target_built_by_us , source_scope ) ;
1016
1016
* scope. cached_unwind . ref_mut ( generator_drop) = Some ( target) ;
1017
1017
1018
+ assert ! ( storage_deads. is_empty( ) ) ;
1018
1019
debug ! ( "build_diverge_scope({:?}, {:?}) = {:?}" , scope, span, target) ;
1019
1020
1020
1021
target
1021
1022
}
1023
+
1024
+ fn push_storage_deads ( cfg : & mut CFG < ' tcx > ,
1025
+ target : & mut BasicBlock ,
1026
+ storage_deads : & mut Vec < Statement < ' tcx > > ,
1027
+ target_built_by_us : bool ,
1028
+ source_scope : SourceScope ) {
1029
+ if storage_deads. is_empty ( ) { return ; }
1030
+ if !target_built_by_us {
1031
+ // We cannot add statements to an existing block, so we create a new
1032
+ // block for our StorageDead statements.
1033
+ let block = cfg. start_new_cleanup_block ( ) ;
1034
+ let source_info = SourceInfo { span : DUMMY_SP , scope : source_scope } ;
1035
+ cfg. terminate ( block, source_info, TerminatorKind :: Goto { target : * target } ) ;
1036
+ * target = block;
1037
+ }
1038
+ let statements = & mut cfg. block_data_mut ( * target) . statements ;
1039
+ storage_deads. reverse ( ) ;
1040
+ debug ! ( "push_storage_deads({:?}), storage_deads={:?}, statements={:?}" ,
1041
+ * target, storage_deads, statements) ;
1042
+ storage_deads. append ( statements) ;
1043
+ mem:: swap ( statements, storage_deads) ;
1044
+ assert ! ( storage_deads. is_empty( ) ) ;
1045
+ }
0 commit comments