1
- use rustc_index:: { Idx , IndexVec } ;
1
+ use rustc_data_structures:: fx:: FxHashMap ;
2
+ use rustc_index:: Idx ;
2
3
use rustc_middle:: mir:: * ;
3
4
use rustc_middle:: ty:: Ty ;
4
5
use rustc_span:: Span ;
@@ -9,7 +10,7 @@ use tracing::debug;
9
10
/// and replacement of terminators, and then apply the queued changes all at
10
11
/// once with `apply`. This is useful for MIR transformation passes.
11
12
pub ( crate ) struct MirPatch < ' tcx > {
12
- term_patch_map : IndexVec < BasicBlock , Option < TerminatorKind < ' tcx > > > ,
13
+ term_patch_map : FxHashMap < BasicBlock , TerminatorKind < ' tcx > > ,
13
14
new_blocks : Vec < BasicBlockData < ' tcx > > ,
14
15
new_statements : Vec < ( Location , StatementKind < ' tcx > ) > ,
15
16
new_locals : Vec < LocalDecl < ' tcx > > ,
@@ -22,17 +23,21 @@ pub(crate) struct MirPatch<'tcx> {
22
23
terminate_block : Option < ( BasicBlock , UnwindTerminateReason ) > ,
23
24
body_span : Span ,
24
25
next_local : usize ,
26
+ /// The number of blocks at the start of the transformation. New blocks
27
+ /// get appended at the end.
28
+ next_block : usize ,
25
29
}
26
30
27
31
impl < ' tcx > MirPatch < ' tcx > {
28
32
/// Creates a new, empty patch.
29
33
pub ( crate ) fn new ( body : & Body < ' tcx > ) -> Self {
30
34
let mut result = MirPatch {
31
- term_patch_map : IndexVec :: from_elem ( None , & body . basic_blocks ) ,
35
+ term_patch_map : Default :: default ( ) ,
32
36
new_blocks : vec ! [ ] ,
33
37
new_statements : vec ! [ ] ,
34
38
new_locals : vec ! [ ] ,
35
39
next_local : body. local_decls . len ( ) ,
40
+ next_block : body. basic_blocks . len ( ) ,
36
41
resume_block : None ,
37
42
unreachable_cleanup_block : None ,
38
43
unreachable_no_cleanup_block : None ,
@@ -141,7 +146,7 @@ impl<'tcx> MirPatch<'tcx> {
141
146
142
147
/// Has a replacement of this block's terminator been queued in this patch?
143
148
pub ( crate ) fn is_term_patched ( & self , bb : BasicBlock ) -> bool {
144
- self . term_patch_map [ bb ] . is_some ( )
149
+ self . term_patch_map . contains_key ( & bb )
145
150
}
146
151
147
152
/// Universal getter for block data, either it is in 'old' blocks or in patched ones
@@ -194,18 +199,17 @@ impl<'tcx> MirPatch<'tcx> {
194
199
195
200
/// Queues the addition of a new basic block.
196
201
pub ( crate ) fn new_block ( & mut self , data : BasicBlockData < ' tcx > ) -> BasicBlock {
197
- let block = self . term_patch_map . next_index ( ) ;
202
+ let block = BasicBlock :: from_usize ( self . next_block + self . new_blocks . len ( ) ) ;
198
203
debug ! ( "MirPatch: new_block: {:?}: {:?}" , block, data) ;
199
204
self . new_blocks . push ( data) ;
200
- self . term_patch_map . push ( None ) ;
201
205
block
202
206
}
203
207
204
208
/// Queues the replacement of a block's terminator.
205
209
pub ( crate ) fn patch_terminator ( & mut self , block : BasicBlock , new : TerminatorKind < ' tcx > ) {
206
- assert ! ( self . term_patch_map[ block ] . is_none ( ) ) ;
210
+ assert ! ( ! self . term_patch_map. contains_key ( & block ) ) ;
207
211
debug ! ( "MirPatch: patch_terminator({:?}, {:?})" , block, new) ;
208
- self . term_patch_map [ block] = Some ( new) ;
212
+ self . term_patch_map . insert ( block, new) ;
209
213
}
210
214
211
215
/// Queues the insertion of a statement at a given location. The statement
@@ -244,18 +248,20 @@ impl<'tcx> MirPatch<'tcx> {
244
248
self . new_blocks. len( ) ,
245
249
body. basic_blocks. len( )
246
250
) ;
251
+ debug_assert_eq ! ( self . next_block, body. basic_blocks. len( ) ) ;
247
252
let bbs = if self . term_patch_map . is_empty ( ) && self . new_blocks . is_empty ( ) {
248
253
body. basic_blocks . as_mut_preserves_cfg ( )
249
254
} else {
250
255
body. basic_blocks . as_mut ( )
251
256
} ;
252
257
bbs. extend ( self . new_blocks ) ;
253
258
body. local_decls . extend ( self . new_locals ) ;
254
- for ( src, patch) in self . term_patch_map . into_iter_enumerated ( ) {
255
- if let Some ( patch) = patch {
256
- debug ! ( "MirPatch: patching block {:?}" , src) ;
257
- bbs[ src] . terminator_mut ( ) . kind = patch;
258
- }
259
+
260
+ // The order in which we patch terminators does not change the result.
261
+ #[ allow( rustc:: potential_query_instability) ]
262
+ for ( src, patch) in self . term_patch_map {
263
+ debug ! ( "MirPatch: patching block {:?}" , src) ;
264
+ bbs[ src] . terminator_mut ( ) . kind = patch;
259
265
}
260
266
261
267
let mut new_statements = self . new_statements ;
@@ -273,8 +279,8 @@ impl<'tcx> MirPatch<'tcx> {
273
279
}
274
280
debug ! ( "MirPatch: adding statement {:?} at loc {:?}+{}" , stmt, loc, delta) ;
275
281
loc. statement_index += delta;
276
- let source_info = Self :: source_info_for_index ( & body [ loc. block ] , loc) ;
277
- body [ loc. block ]
282
+ let source_info = Self :: source_info_for_index ( & bbs [ loc. block ] , loc) ;
283
+ bbs [ loc. block ]
278
284
. statements
279
285
. insert ( loc. statement_index , Statement :: new ( source_info, stmt) ) ;
280
286
delta += 1 ;
0 commit comments