2929//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
3030//! future.
3131
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 } ;
3434use rustc:: mir:: transform:: { MirPass , MirSource , Pass } ;
35+ use rustc:: mir:: visit:: MutVisitor ;
3536use rustc:: ty:: TyCtxt ;
3637use rustc_data_structures:: indexed_vec:: Idx ;
38+ use transform:: qualify_consts;
3739
3840pub struct CopyPropagation ;
3941
4042impl Pass for CopyPropagation { }
4143
4244impl < ' 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+
4476 loop {
4577 let mut def_use_analysis = DefUseAnalysis :: new ( mir) ;
4678 def_use_analysis. analyze ( mir) ;
@@ -50,7 +82,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
5082 let dest_local = Local :: new ( dest_local_index) ;
5183 debug ! ( "Considering destination local: {}" , mir. format_local( dest_local) ) ;
5284
53- let src_local ;
85+ let action ;
5486 let location;
5587 {
5688 // The destination must have exactly one def.
@@ -88,66 +120,114 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
88120 } ;
89121
90122 // 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+ }
97138 }
98139 _ => {
99140 debug ! ( " Can't copy-propagate local: source use is not an \
100141 assignment") ;
101142 continue
102143 }
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
142144 }
143145 }
144146
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.
146224 //
147225 // First, remove all markers.
148226 //
149227 // 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) ) ;
151231 for lvalue_use in & def_use_analysis. local_info ( dest_local) . defs_and_uses {
152232 if lvalue_use. context . is_storage_marker ( ) {
153233 mir. make_statement_nop ( lvalue_use. location )
@@ -159,22 +239,96 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
159239 }
160240 }
161241
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.
163243 let src_lvalue = Lvalue :: from_local ( mir, src_local) ;
164244 def_use_analysis. replace_all_defs_and_uses_with ( dest_local, mir, src_lvalue) ;
165245
166246 // Finally, zap the now-useless assignment instruction.
247+ debug ! ( " Deleting assignment" ) ;
167248 mir. make_statement_nop ( location) ;
168249
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
173251 }
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+ }
176326 }
327+ Operand :: Constant ( _) => return ,
177328 }
329+
330+ * operand = Operand :: Constant ( self . constant . clone ( ) ) ;
331+ self . uses_replaced += 1
178332 }
179333}
180334
0 commit comments