@@ -4,11 +4,12 @@ use rustc_middle::ty::Ty;
4
4
use rustc_span:: Span ;
5
5
use tracing:: debug;
6
6
7
- /// This struct represents a patch to MIR, which can add
8
- /// new statements and basic blocks and patch over block
9
- /// terminators.
7
+ /// This struct lets you "patch" a MIR body, i.e. modify it. You can queue up
8
+ /// various changes, such as the addition of new statements and basic blocks
9
+ /// and replacement of terminators, and then apply the queued changes all at
10
+ /// once with `apply`. This is useful for MIR transformation passes.
10
11
pub ( crate ) struct MirPatch < ' tcx > {
11
- patch_map : IndexVec < BasicBlock , Option < TerminatorKind < ' tcx > > > ,
12
+ term_patch_map : IndexVec < BasicBlock , Option < TerminatorKind < ' tcx > > > ,
12
13
new_blocks : Vec < BasicBlockData < ' tcx > > ,
13
14
new_statements : Vec < ( Location , StatementKind < ' tcx > ) > ,
14
15
new_locals : Vec < LocalDecl < ' tcx > > ,
@@ -24,9 +25,10 @@ pub(crate) struct MirPatch<'tcx> {
24
25
}
25
26
26
27
impl < ' tcx > MirPatch < ' tcx > {
28
+ /// Creates a new, empty patch.
27
29
pub ( crate ) fn new ( body : & Body < ' tcx > ) -> Self {
28
30
let mut result = MirPatch {
29
- patch_map : IndexVec :: from_elem ( None , & body. basic_blocks ) ,
31
+ term_patch_map : IndexVec :: from_elem ( None , & body. basic_blocks ) ,
30
32
new_blocks : vec ! [ ] ,
31
33
new_statements : vec ! [ ] ,
32
34
new_locals : vec ! [ ] ,
@@ -141,10 +143,12 @@ impl<'tcx> MirPatch<'tcx> {
141
143
bb
142
144
}
143
145
144
- pub ( crate ) fn is_patched ( & self , bb : BasicBlock ) -> bool {
145
- self . patch_map [ bb] . is_some ( )
146
+ /// Has a replacement of this block's terminator been queued in this patch?
147
+ pub ( crate ) fn is_term_patched ( & self , bb : BasicBlock ) -> bool {
148
+ self . term_patch_map [ bb] . is_some ( )
146
149
}
147
150
151
+ /// Queues the addition of a new temporary with additional local info.
148
152
pub ( crate ) fn new_local_with_info (
149
153
& mut self ,
150
154
ty : Ty < ' tcx > ,
@@ -159,6 +163,7 @@ impl<'tcx> MirPatch<'tcx> {
159
163
Local :: new ( index)
160
164
}
161
165
166
+ /// Queues the addition of a new temporary.
162
167
pub ( crate ) fn new_temp ( & mut self , ty : Ty < ' tcx > , span : Span ) -> Local {
163
168
let index = self . next_local ;
164
169
self . next_local += 1 ;
@@ -174,29 +179,46 @@ impl<'tcx> MirPatch<'tcx> {
174
179
self . new_locals [ new_local_idx] . ty
175
180
}
176
181
182
+ /// Queues the addition of a new basic block.
177
183
pub ( crate ) fn new_block ( & mut self , data : BasicBlockData < ' tcx > ) -> BasicBlock {
178
- let block = BasicBlock :: new ( self . patch_map . len ( ) ) ;
184
+ let block = BasicBlock :: new ( self . term_patch_map . len ( ) ) ;
179
185
debug ! ( "MirPatch: new_block: {:?}: {:?}" , block, data) ;
180
186
self . new_blocks . push ( data) ;
181
- self . patch_map . push ( None ) ;
187
+ self . term_patch_map . push ( None ) ;
182
188
block
183
189
}
184
190
191
+ /// Queues the replacement of a block's terminator.
185
192
pub ( crate ) fn patch_terminator ( & mut self , block : BasicBlock , new : TerminatorKind < ' tcx > ) {
186
- assert ! ( self . patch_map [ block] . is_none( ) ) ;
193
+ assert ! ( self . term_patch_map [ block] . is_none( ) ) ;
187
194
debug ! ( "MirPatch: patch_terminator({:?}, {:?})" , block, new) ;
188
- self . patch_map [ block] = Some ( new) ;
195
+ self . term_patch_map [ block] = Some ( new) ;
189
196
}
190
197
198
+ /// Queues the insertion of a statement at a given location. The statement
199
+ /// currently at that location, and all statements that follow, are shifted
200
+ /// down. If multiple statements are queued for addition at the same
201
+ /// location, the final statement order after calling `apply` will match
202
+ /// the queue insertion order.
203
+ ///
204
+ /// E.g. if we have `s0` at location `loc` and do these calls:
205
+ ///
206
+ /// p.add_statement(loc, s1);
207
+ /// p.add_statement(loc, s2);
208
+ /// p.apply(body);
209
+ ///
210
+ /// then the final order will be `s1, s2, s0`, with `s1` at `loc`.
191
211
pub ( crate ) fn add_statement ( & mut self , loc : Location , stmt : StatementKind < ' tcx > ) {
192
212
debug ! ( "MirPatch: add_statement({:?}, {:?})" , loc, stmt) ;
193
213
self . new_statements . push ( ( loc, stmt) ) ;
194
214
}
195
215
216
+ /// Like `add_statement`, but specialized for assignments.
196
217
pub ( crate ) fn add_assign ( & mut self , loc : Location , place : Place < ' tcx > , rv : Rvalue < ' tcx > ) {
197
218
self . add_statement ( loc, StatementKind :: Assign ( Box :: new ( ( place, rv) ) ) ) ;
198
219
}
199
220
221
+ /// Applies the queued changes.
200
222
pub ( crate ) fn apply ( self , body : & mut Body < ' tcx > ) {
201
223
debug ! (
202
224
"MirPatch: {:?} new temps, starting from index {}: {:?}" ,
@@ -209,21 +231,24 @@ impl<'tcx> MirPatch<'tcx> {
209
231
self . new_blocks. len( ) ,
210
232
body. basic_blocks. len( )
211
233
) ;
212
- let bbs = if self . patch_map . is_empty ( ) && self . new_blocks . is_empty ( ) {
234
+ let bbs = if self . term_patch_map . is_empty ( ) && self . new_blocks . is_empty ( ) {
213
235
body. basic_blocks . as_mut_preserves_cfg ( )
214
236
} else {
215
237
body. basic_blocks . as_mut ( )
216
238
} ;
217
239
bbs. extend ( self . new_blocks ) ;
218
240
body. local_decls . extend ( self . new_locals ) ;
219
- for ( src, patch) in self . patch_map . into_iter_enumerated ( ) {
241
+ for ( src, patch) in self . term_patch_map . into_iter_enumerated ( ) {
220
242
if let Some ( patch) = patch {
221
243
debug ! ( "MirPatch: patching block {:?}" , src) ;
222
244
bbs[ src] . terminator_mut ( ) . kind = patch;
223
245
}
224
246
}
225
247
226
248
let mut new_statements = self . new_statements ;
249
+
250
+ // This must be a stable sort to provide the ordering described in the
251
+ // comment for `add_statement`.
227
252
new_statements. sort_by_key ( |s| s. 0 ) ;
228
253
229
254
let mut delta = 0 ;
0 commit comments