@@ -113,12 +113,20 @@ pub struct Scope<'tcx> {
113
113
// This is expected to go away once `box EXPR` becomes a sugar for placement protocol and gets
114
114
// desugared in some earlier stage.
115
115
free : Option < FreeData < ' tcx > > ,
116
+
117
+ /// The cached block for the cleanups-on-diverge path. This block
118
+ /// contains a block that will just do a RESUME to an appropriate
119
+ /// place. This block does not execute any of the drops or free:
120
+ /// each of those has their own cached-blocks, which will branch
121
+ /// to this point.
122
+ cached_block : Option < BasicBlock >
116
123
}
117
124
118
125
struct DropData < ' tcx > {
119
126
span : Span ,
120
127
value : Lvalue < ' tcx > ,
121
128
// NB: per-drop “cache” is necessary for the build_scope_drops function below.
129
+
122
130
/// The cached block for the cleanups-on-diverge path. This block contains code to run the
123
131
/// current drop and all the preceding drops (i.e. those having lower index in Drop’s
124
132
/// Scope drop array)
@@ -127,10 +135,13 @@ struct DropData<'tcx> {
127
135
128
136
struct FreeData < ' tcx > {
129
137
span : Span ,
138
+
130
139
/// Lvalue containing the allocated box.
131
140
value : Lvalue < ' tcx > ,
141
+
132
142
/// type of item for which the box was allocated for (i.e. the T in Box<T>).
133
143
item_ty : Ty < ' tcx > ,
144
+
134
145
/// The cached block containing code to run the free. The block will also execute all the drops
135
146
/// in the scope.
136
147
cached_block : Option < BasicBlock >
@@ -155,6 +166,7 @@ impl<'tcx> Scope<'tcx> {
155
166
/// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a
156
167
/// larger extent of code.
157
168
fn invalidate_cache ( & mut self ) {
169
+ self . cached_block = None ;
158
170
for dropdata in & mut self . drops {
159
171
dropdata. cached_block = None ;
160
172
}
@@ -234,7 +246,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
234
246
id : id,
235
247
extent : extent,
236
248
drops : vec ! [ ] ,
237
- free : None
249
+ free : None ,
250
+ cached_block : None ,
238
251
} ) ;
239
252
self . scope_auxiliary . push ( ScopeAuxiliary {
240
253
extent : extent,
@@ -288,7 +301,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
288
301
block) ) ;
289
302
if let Some ( ref free_data) = scope. free {
290
303
let next = self . cfg . start_new_block ( ) ;
291
- let free = build_free ( self . hir . tcx ( ) , tmp. clone ( ) , free_data, next) ;
304
+ let free = build_free ( self . hir . tcx ( ) , & tmp, free_data, next) ;
292
305
self . cfg . terminate ( block, scope. id , span, free) ;
293
306
block = next;
294
307
}
@@ -419,20 +432,16 @@ impl<'a,'tcx> Builder<'a,'tcx> {
419
432
}
420
433
let unit_temp = self . get_unit_temp ( ) ;
421
434
let Builder { ref mut hir, ref mut cfg, ref mut scopes, .. } = * self ;
422
- let mut next_block = None ;
423
435
424
436
// Given an array of scopes, we generate these from the outermost scope to the innermost
425
437
// one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
426
438
// generate B0 <- B1 <- B2 in left-to-right order. Control flow of the generated blocks
427
439
// always ends up at a block with the Resume terminator.
428
- for scope in scopes. iter_mut ( ) . filter ( |s| !s. drops . is_empty ( ) || s. free . is_some ( ) ) {
429
- next_block = Some ( build_diverge_scope ( hir. tcx ( ) ,
430
- cfg,
431
- unit_temp. clone ( ) ,
432
- scope,
433
- next_block) ) ;
440
+ if scopes. iter ( ) . any ( |scope| !scope. drops . is_empty ( ) || scope. free . is_some ( ) ) {
441
+ Some ( build_diverge_scope ( hir. tcx ( ) , cfg, & unit_temp, scopes) )
442
+ } else {
443
+ None
434
444
}
435
- scopes. iter ( ) . rev ( ) . flat_map ( |x| x. cached_block ( ) ) . next ( )
436
445
}
437
446
438
447
/// Utility function for *non*-scope code to build their own drops
@@ -525,8 +534,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
525
534
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
526
535
// icache with cold branch code), however to achieve that we either have to rely on rvalue
527
536
// promotion or have some way, in MIR, to create constants.
528
- self . cfg . push_assign ( block, scope_id, span, & tuple, // tuple = (message_arg, file_arg, line_arg);
537
+ self . cfg . push_assign ( block, scope_id, span, & tuple, // [1]
529
538
Rvalue :: Aggregate ( AggregateKind :: Tuple , elems) ) ;
539
+ // [1] tuple = (message_arg, file_arg, line_arg);
530
540
// FIXME: is this region really correct here?
531
541
self . cfg . push_assign ( block, scope_id, span, & tuple_ref, // tuple_ref = &tuple;
532
542
Rvalue :: Ref ( region, BorrowKind :: Shared , tuple) ) ;
@@ -602,58 +612,42 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>,
602
612
603
613
fn build_diverge_scope < ' tcx > ( tcx : & TyCtxt < ' tcx > ,
604
614
cfg : & mut CFG < ' tcx > ,
605
- unit_temp : Lvalue < ' tcx > ,
606
- scope : & mut Scope < ' tcx > ,
607
- target : Option < BasicBlock > )
608
- -> BasicBlock {
609
- debug_assert ! ( !scope. drops. is_empty( ) || scope. free. is_some( ) ) ;
610
-
611
- // First, we build the drops, iterating the drops array in reverse. We do that so that as soon
612
- // as we find a `cached_block`, we know that we’re finished and don’t need to do anything else.
613
- let mut previous = None ;
614
- let mut last_drop_block = None ;
615
- for drop_data in scope. drops . iter_mut ( ) . rev ( ) {
616
- if let Some ( cached_block) = drop_data. cached_block {
617
- if let Some ( ( previous_block, previous_span, previous_value) ) = previous {
618
- cfg. terminate ( previous_block,
619
- scope. id ,
620
- previous_span,
621
- TerminatorKind :: Drop {
622
- value : previous_value,
623
- target : cached_block,
624
- unwind : None
625
- } ) ;
626
- return last_drop_block. unwrap ( ) ;
627
- } else {
628
- return cached_block;
629
- }
630
- } else {
631
- let block = cfg. start_new_cleanup_block ( ) ;
632
- drop_data. cached_block = Some ( block) ;
633
- if let Some ( ( previous_block, previous_span, previous_value) ) = previous {
634
- cfg. terminate ( previous_block,
635
- scope. id ,
636
- previous_span,
637
- TerminatorKind :: Drop {
638
- value : previous_value,
639
- target : block,
640
- unwind : None
641
- } ) ;
642
- } else {
643
- last_drop_block = Some ( block) ;
644
- }
645
- previous = Some ( ( block, drop_data. span , drop_data. value . clone ( ) ) ) ;
646
- }
647
- }
648
-
649
- // Prepare the end target for this chain.
650
- let mut target = target. unwrap_or_else ( ||{
651
- let b = cfg. start_new_cleanup_block ( ) ;
652
- cfg. terminate ( b, scope. id , DUMMY_SP , TerminatorKind :: Resume ) ; // TODO
653
- b
654
- } ) ;
615
+ unit_temp : & Lvalue < ' tcx > ,
616
+ scopes : & mut [ Scope < ' tcx > ] )
617
+ -> BasicBlock
618
+ {
619
+ assert ! ( scopes. len( ) >= 1 ) ;
620
+
621
+ // Build up the drops in **reverse** order. The end result will
622
+ // look like:
623
+ //
624
+ // [drops[n]] -...-> [drops[0]] -> [Free] -> [scopes[..n-1]]
625
+ // | |
626
+ // +------------------------------------+
627
+ // code for scopes[n]
628
+ //
629
+ // The code in this function reads from right to left. At each
630
+ // point, we check for cached blocks representing the
631
+ // remainder. If everything is cached, we'll just walk right to
632
+ // left reading the cached results but never created anything.
633
+
634
+ // To start, translate scopes[1..].
635
+ let ( scope, earlier_scopes) = scopes. split_last_mut ( ) . unwrap ( ) ;
636
+ let mut target = if let Some ( cached_block) = scope. cached_block {
637
+ cached_block
638
+ } else if earlier_scopes. is_empty ( ) {
639
+ // Diverging from the root scope creates a RESUME terminator.
640
+ // FIXME what span to use here?
641
+ let resumeblk = cfg. start_new_cleanup_block ( ) ;
642
+ cfg. terminate ( resumeblk, scope. id , DUMMY_SP , TerminatorKind :: Resume ) ;
643
+ resumeblk
644
+ } else {
645
+ // Diverging from any other scope chains up to the previous scope.
646
+ build_diverge_scope ( tcx, cfg, unit_temp, earlier_scopes)
647
+ } ;
648
+ scope. cached_block = Some ( target) ;
655
649
656
- // Then , build the free branching into the prepared target .
650
+ // Next , build up any free .
657
651
if let Some ( ref mut free_data) = scope. free {
658
652
target = if let Some ( cached_block) = free_data. cached_block {
659
653
cached_block
@@ -665,29 +659,35 @@ fn build_diverge_scope<'tcx>(tcx: &TyCtxt<'tcx>,
665
659
build_free ( tcx, unit_temp, free_data, target) ) ;
666
660
free_data. cached_block = Some ( into) ;
667
661
into
668
- }
669
- } ;
662
+ } ;
663
+ }
670
664
671
- if let Some ( ( previous_block, previous_span, previous_value) ) = previous {
672
- // Finally, branch into that just-built `target` from the `previous_block`.
673
- cfg. terminate ( previous_block,
674
- scope. id ,
675
- previous_span,
676
- TerminatorKind :: Drop {
677
- value : previous_value,
678
- target : target,
679
- unwind : None
680
- } ) ;
681
- last_drop_block. unwrap ( )
682
- } else {
683
- // If `previous.is_none()`, there were no drops in this scope – we return the
684
- // target.
685
- target
665
+ // Next, build up the drops. Here we iterate the vector in
666
+ // *forward* order, so that we generate drops[0] first (right to
667
+ // left in diagram above).
668
+ for drop_data in & mut scope. drops {
669
+ target = if let Some ( cached_block) = drop_data. cached_block {
670
+ cached_block
671
+ } else {
672
+ let block = cfg. start_new_cleanup_block ( ) ;
673
+ cfg. terminate ( block,
674
+ scope. id ,
675
+ drop_data. span ,
676
+ TerminatorKind :: Drop {
677
+ value : drop_data. value . clone ( ) ,
678
+ target : target,
679
+ unwind : None
680
+ } ) ;
681
+ drop_data. cached_block = Some ( block) ;
682
+ block
683
+ } ;
686
684
}
685
+
686
+ target
687
687
}
688
688
689
689
fn build_free < ' tcx > ( tcx : & TyCtxt < ' tcx > ,
690
- unit_temp : Lvalue < ' tcx > ,
690
+ unit_temp : & Lvalue < ' tcx > ,
691
691
data : & FreeData < ' tcx > ,
692
692
target : BasicBlock )
693
693
-> TerminatorKind < ' tcx > {
@@ -707,7 +707,7 @@ fn build_free<'tcx>(tcx: &TyCtxt<'tcx>,
707
707
}
708
708
} ) ,
709
709
args : vec ! [ Operand :: Consume ( data. value. clone( ) ) ] ,
710
- destination : Some ( ( unit_temp, target) ) ,
710
+ destination : Some ( ( unit_temp. clone ( ) , target) ) ,
711
711
cleanup : None
712
712
}
713
713
}
0 commit comments