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,19 @@ pub(crate) struct MirPatch<'tcx> {
22
23
terminate_block: Option<(BasicBlock, UnwindTerminateReason)>,
23
24
body_span: Span,
24
25
next_local: usize,
26
+ next_block: usize,
25
27
}
26
28
27
29
impl<'tcx> MirPatch<'tcx> {
28
30
/// Creates a new, empty patch.
29
31
pub(crate) fn new(body: &Body<'tcx>) -> Self {
30
32
let mut result = MirPatch {
31
- term_patch_map: IndexVec::from_elem(None, &body.basic_blocks ),
33
+ term_patch_map: Default::default( ),
32
34
new_blocks: vec![],
33
35
new_statements: vec![],
34
36
new_locals: vec![],
35
37
next_local: body.local_decls.len(),
38
+ next_block: body.basic_blocks.len(),
36
39
resume_block: None,
37
40
unreachable_cleanup_block: None,
38
41
unreachable_no_cleanup_block: None,
@@ -141,7 +144,7 @@ impl<'tcx> MirPatch<'tcx> {
141
144
142
145
/// Has a replacement of this block's terminator been queued in this patch?
143
146
pub(crate) fn is_term_patched(&self, bb: BasicBlock) -> bool {
144
- self.term_patch_map[bb].is_some( )
147
+ self.term_patch_map.contains_key(&bb )
145
148
}
146
149
147
150
/// Universal getter for block data, either it is in 'old' blocks or in patched ones
@@ -194,18 +197,17 @@ impl<'tcx> MirPatch<'tcx> {
194
197
195
198
/// Queues the addition of a new basic block.
196
199
pub(crate) fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
197
- let block = self.term_patch_map.next_index( );
200
+ let block = BasicBlock::from_usize( self.next_block + self.new_blocks.len() );
198
201
debug!("MirPatch: new_block: {:?}: {:?}", block, data);
199
202
self.new_blocks.push(data);
200
- self.term_patch_map.push(None);
201
203
block
202
204
}
203
205
204
206
/// Queues the replacement of a block's terminator.
205
207
pub(crate) fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
206
- assert!(self.term_patch_map[block].is_none( ));
208
+ assert!(! self.term_patch_map.contains_key(&block ));
207
209
debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
208
- self.term_patch_map[ block] = Some( new);
210
+ self.term_patch_map.insert( block, new);
209
211
}
210
212
211
213
/// Queues the insertion of a statement at a given location. The statement
@@ -244,18 +246,20 @@ impl<'tcx> MirPatch<'tcx> {
244
246
self.new_blocks.len(),
245
247
body.basic_blocks.len()
246
248
);
247
- let bbs = if self.term_patch_map.iter().all(Option::is_none) && self.new_blocks.is_empty() {
249
+ debug_assert_eq!(self.next_block, body.basic_blocks.len());
250
+ let bbs = if self.term_patch_map.is_empty() && self.new_blocks.is_empty() {
248
251
body.basic_blocks.as_mut_preserves_cfg()
249
252
} else {
250
253
body.basic_blocks.as_mut()
251
254
};
252
255
bbs.extend(self.new_blocks);
253
256
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
- }
257
+
258
+ // The order in which we patch terminators does not change the result.
259
+ #[allow(rustc::potential_query_instability)]
260
+ for (src, patch) in self.term_patch_map {
261
+ debug!("MirPatch: patching block {:?}", src);
262
+ bbs[src].terminator_mut().kind = patch;
259
263
}
260
264
261
265
let mut new_statements = self.new_statements;
0 commit comments