29
29
//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
30
30
//! future.
31
31
32
- use def_use:: DefUseAnalysis ;
33
- use rustc:: mir:: repr:: { Local , Lvalue , Mir , Operand , Rvalue , StatementKind } ;
32
+ use def_use:: { DefUseAnalysis , MirSummary } ;
33
+ use rustc:: mir:: repr:: { Constant , Local , Location , Lvalue , Mir , Operand , Rvalue , StatementKind } ;
34
34
use rustc:: mir:: transform:: { MirPass , MirSource , Pass } ;
35
+ use rustc:: mir:: visit:: MutVisitor ;
35
36
use rustc:: ty:: TyCtxt ;
36
37
use rustc_data_structures:: indexed_vec:: Idx ;
38
+ use transform:: qualify_consts;
37
39
38
40
pub struct CopyPropagation ;
39
41
40
42
impl Pass for CopyPropagation { }
41
43
42
44
impl < ' tcx > MirPass < ' tcx > for CopyPropagation {
43
- fn run_pass < ' a > ( & mut self , _: TyCtxt < ' a , ' tcx , ' tcx > , _: MirSource , mir : & mut Mir < ' tcx > ) {
45
+ fn run_pass < ' a > ( & mut self ,
46
+ tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
47
+ source : MirSource ,
48
+ mir : & mut Mir < ' tcx > ) {
49
+ match source {
50
+ MirSource :: Const ( _) => {
51
+ // Don't run on constants, because constant qualification might reject the
52
+ // optimized IR.
53
+ return
54
+ }
55
+ MirSource :: Static ( ..) | MirSource :: Promoted ( ..) => {
56
+ // Don't run on statics and promoted statics, because trans might not be able to
57
+ // evaluate the optimized IR.
58
+ return
59
+ }
60
+ MirSource :: Fn ( function_node_id) => {
61
+ if qualify_consts:: is_const_fn ( tcx, tcx. map . local_def_id ( function_node_id) ) {
62
+ // Don't run on const functions, as, again, trans might not be able to evaluate
63
+ // the optimized IR.
64
+ return
65
+ }
66
+ }
67
+ }
68
+
69
+ // We only run when the MIR optimization level is at least 1. This avoids messing up debug
70
+ // info.
71
+ match tcx. sess . opts . debugging_opts . mir_opt_level {
72
+ Some ( 0 ) | None => return ,
73
+ _ => { }
74
+ }
75
+
44
76
loop {
45
77
let mut def_use_analysis = DefUseAnalysis :: new ( mir) ;
46
78
def_use_analysis. analyze ( mir) ;
@@ -50,7 +82,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
50
82
let dest_local = Local :: new ( dest_local_index) ;
51
83
debug ! ( "Considering destination local: {}" , mir. format_local( dest_local) ) ;
52
84
53
- let src_local ;
85
+ let action ;
54
86
let location;
55
87
{
56
88
// The destination must have exactly one def.
@@ -88,66 +120,114 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
88
120
} ;
89
121
90
122
// That use of the source must be an assignment.
91
- let src_lvalue = match statement. kind {
92
- StatementKind :: Assign (
93
- ref dest_lvalue,
94
- Rvalue :: Use ( Operand :: Consume ( ref src_lvalue) ) )
95
- if Some ( dest_local) == mir. local_index ( dest_lvalue) => {
96
- src_lvalue
123
+ match statement. kind {
124
+ StatementKind :: Assign ( ref dest_lvalue, Rvalue :: Use ( ref operand) ) if
125
+ Some ( dest_local) == mir. local_index ( dest_lvalue) => {
126
+ let maybe_action = match * operand {
127
+ Operand :: Consume ( ref src_lvalue) => {
128
+ Action :: local_copy ( mir, & def_use_analysis, src_lvalue)
129
+ }
130
+ Operand :: Constant ( ref src_constant) => {
131
+ Action :: constant ( src_constant)
132
+ }
133
+ } ;
134
+ match maybe_action {
135
+ Some ( this_action) => action = this_action,
136
+ None => continue ,
137
+ }
97
138
}
98
139
_ => {
99
140
debug ! ( " Can't copy-propagate local: source use is not an \
100
141
assignment") ;
101
142
continue
102
143
}
103
- } ;
104
- src_local = match mir. local_index ( src_lvalue) {
105
- Some ( src_local) => src_local,
106
- None => {
107
- debug ! ( " Can't copy-propagate local: source is not a local" ) ;
108
- continue
109
- }
110
- } ;
111
-
112
- // There must be exactly one use of the source used in a statement (not in a
113
- // terminator).
114
- let src_use_info = def_use_analysis. local_info ( src_local) ;
115
- let src_use_count = src_use_info. use_count ( ) ;
116
- if src_use_count == 0 {
117
- debug ! ( " Can't copy-propagate local: no uses" ) ;
118
- continue
119
- }
120
- if src_use_count != 1 {
121
- debug ! ( " Can't copy-propagate local: {} uses" , src_use_info. use_count( ) ) ;
122
- continue
123
- }
124
-
125
- // Verify that the source doesn't change in between. This is done
126
- // conservatively for now, by ensuring that the source has exactly one
127
- // mutation. The goal is to prevent things like:
128
- //
129
- // DEST = SRC;
130
- // SRC = X;
131
- // USE(DEST);
132
- //
133
- // From being misoptimized into:
134
- //
135
- // SRC = X;
136
- // USE(SRC);
137
- let src_def_count = src_use_info. def_count_not_including_drop ( ) ;
138
- if src_def_count != 1 {
139
- debug ! ( " Can't copy-propagate local: {} defs of src" ,
140
- src_use_info. def_count_not_including_drop( ) ) ;
141
- continue
142
144
}
143
145
}
144
146
145
- // If all checks passed, then we can eliminate the destination and the assignment.
147
+ changed = action. perform ( mir, & def_use_analysis, dest_local, location) || changed;
148
+ // FIXME(pcwalton): Update the use-def chains to delete the instructions instead of
149
+ // regenerating the chains.
150
+ break
151
+ }
152
+ if !changed {
153
+ break
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ enum Action < ' tcx > {
160
+ PropagateLocalCopy ( Local ) ,
161
+ PropagateConstant ( Constant < ' tcx > ) ,
162
+ }
163
+
164
+ impl < ' tcx > Action < ' tcx > {
165
+ fn local_copy ( mir : & Mir < ' tcx > , def_use_analysis : & DefUseAnalysis , src_lvalue : & Lvalue < ' tcx > )
166
+ -> Option < Action < ' tcx > > {
167
+ // The source must be a local.
168
+ let src_local = match mir. local_index ( src_lvalue) {
169
+ Some ( src_local) => src_local,
170
+ None => {
171
+ debug ! ( " Can't copy-propagate local: source is not a local" ) ;
172
+ return None
173
+ }
174
+ } ;
175
+
176
+ // We're trying to copy propagate a local.
177
+ // There must be exactly one use of the source used in a statement (not in a terminator).
178
+ let src_use_info = def_use_analysis. local_info ( src_local) ;
179
+ let src_use_count = src_use_info. use_count ( ) ;
180
+ if src_use_count == 0 {
181
+ debug ! ( " Can't copy-propagate local: no uses" ) ;
182
+ return None
183
+ }
184
+ if src_use_count != 1 {
185
+ debug ! ( " Can't copy-propagate local: {} uses" , src_use_info. use_count( ) ) ;
186
+ return None
187
+ }
188
+
189
+ // Verify that the source doesn't change in between. This is done conservatively for now,
190
+ // by ensuring that the source has exactly one mutation. The goal is to prevent things
191
+ // like:
192
+ //
193
+ // DEST = SRC;
194
+ // SRC = X;
195
+ // USE(DEST);
196
+ //
197
+ // From being misoptimized into:
198
+ //
199
+ // SRC = X;
200
+ // USE(SRC);
201
+ let src_def_count = src_use_info. def_count_not_including_drop ( ) ;
202
+ if src_def_count != 1 {
203
+ debug ! ( " Can't copy-propagate local: {} defs of src" ,
204
+ src_use_info. def_count_not_including_drop( ) ) ;
205
+ return None
206
+ }
207
+
208
+ Some ( Action :: PropagateLocalCopy ( src_local) )
209
+ }
210
+
211
+ fn constant ( src_constant : & Constant < ' tcx > ) -> Option < Action < ' tcx > > {
212
+ Some ( Action :: PropagateConstant ( ( * src_constant) . clone ( ) ) )
213
+ }
214
+
215
+ fn perform ( self ,
216
+ mir : & mut Mir < ' tcx > ,
217
+ def_use_analysis : & DefUseAnalysis < ' tcx > ,
218
+ dest_local : Local ,
219
+ location : Location )
220
+ -> bool {
221
+ match self {
222
+ Action :: PropagateLocalCopy ( src_local) => {
223
+ // Eliminate the destination and the assignment.
146
224
//
147
225
// First, remove all markers.
148
226
//
149
227
// FIXME(pcwalton): Don't do this. Merge live ranges instead.
150
- debug ! ( " Replacing all uses of {}" , mir. format_local( dest_local) ) ;
228
+ debug ! ( " Replacing all uses of {} with {} (local)" ,
229
+ mir. format_local( dest_local) ,
230
+ mir. format_local( src_local) ) ;
151
231
for lvalue_use in & def_use_analysis. local_info ( dest_local) . defs_and_uses {
152
232
if lvalue_use. context . is_storage_marker ( ) {
153
233
mir. make_statement_nop ( lvalue_use. location )
@@ -159,22 +239,96 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
159
239
}
160
240
}
161
241
162
- // Now replace all uses of the destination local with the source local.
242
+ // Replace all uses of the destination local with the source local.
163
243
let src_lvalue = Lvalue :: from_local ( mir, src_local) ;
164
244
def_use_analysis. replace_all_defs_and_uses_with ( dest_local, mir, src_lvalue) ;
165
245
166
246
// Finally, zap the now-useless assignment instruction.
247
+ debug ! ( " Deleting assignment" ) ;
167
248
mir. make_statement_nop ( location) ;
168
249
169
- changed = true ;
170
- // FIXME(pcwalton): Update the use-def chains to delete the instructions instead of
171
- // regenerating the chains.
172
- break
250
+ true
173
251
}
174
- if !changed {
175
- break
252
+ Action :: PropagateConstant ( src_constant) => {
253
+ // First, remove all markers.
254
+ //
255
+ // FIXME(pcwalton): Don't do this. Merge live ranges instead.
256
+ debug ! ( " Replacing all uses of {} with {:?} (constant)" ,
257
+ mir. format_local( dest_local) ,
258
+ src_constant) ;
259
+ let dest_local_info = def_use_analysis. local_info ( dest_local) ;
260
+ for lvalue_use in & dest_local_info. defs_and_uses {
261
+ if lvalue_use. context . is_storage_marker ( ) {
262
+ mir. make_statement_nop ( lvalue_use. location )
263
+ }
264
+ }
265
+
266
+ // Replace all uses of the destination local with the constant.
267
+ let mut visitor = ConstantPropagationVisitor :: new ( MirSummary :: new ( mir) ,
268
+ dest_local,
269
+ src_constant) ;
270
+ for dest_lvalue_use in & dest_local_info. defs_and_uses {
271
+ visitor. visit_location ( mir, dest_lvalue_use. location )
272
+ }
273
+
274
+ // Zap the assignment instruction if we eliminated all the uses. We won't have been
275
+ // able to do that if the destination was used in a projection, because projections
276
+ // must have lvalues on their LHS.
277
+ let use_count = dest_local_info. use_count ( ) ;
278
+ if visitor. uses_replaced == use_count {
279
+ debug ! ( " {} of {} use(s) replaced; deleting assignment" ,
280
+ visitor. uses_replaced,
281
+ use_count) ;
282
+ mir. make_statement_nop ( location) ;
283
+ true
284
+ } else if visitor. uses_replaced == 0 {
285
+ debug ! ( " No uses replaced; not deleting assignment" ) ;
286
+ false
287
+ } else {
288
+ debug ! ( " {} of {} use(s) replaced; not deleting assignment" ,
289
+ visitor. uses_replaced,
290
+ use_count) ;
291
+ true
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ struct ConstantPropagationVisitor < ' tcx > {
299
+ dest_local : Local ,
300
+ constant : Constant < ' tcx > ,
301
+ mir_summary : MirSummary ,
302
+ uses_replaced : usize ,
303
+ }
304
+
305
+ impl < ' tcx > ConstantPropagationVisitor < ' tcx > {
306
+ fn new ( mir_summary : MirSummary , dest_local : Local , constant : Constant < ' tcx > )
307
+ -> ConstantPropagationVisitor < ' tcx > {
308
+ ConstantPropagationVisitor {
309
+ dest_local : dest_local,
310
+ constant : constant,
311
+ mir_summary : mir_summary,
312
+ uses_replaced : 0 ,
313
+ }
314
+ }
315
+ }
316
+
317
+ impl < ' tcx > MutVisitor < ' tcx > for ConstantPropagationVisitor < ' tcx > {
318
+ fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
319
+ self . super_operand ( operand, location) ;
320
+
321
+ match * operand {
322
+ Operand :: Consume ( ref lvalue) => {
323
+ if self . mir_summary . local_index ( lvalue) != Some ( self . dest_local ) {
324
+ return
325
+ }
176
326
}
327
+ Operand :: Constant ( _) => return ,
177
328
}
329
+
330
+ * operand = Operand :: Constant ( self . constant . clone ( ) ) ;
331
+ self . uses_replaced += 1
178
332
}
179
333
}
180
334
0 commit comments