8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- //! This is Constant-Simplify propagation pass. This is a composition of three distinct
12
- //! dataflow passes: alias-propagation, constant-propagation and terminator simplification.
11
+ //! This is Constant-Simplify propagation pass. This is a composition of two distinct
12
+ //! passes: constant-propagation and terminator simplification.
13
13
//!
14
- //! All these are very similar in their nature:
14
+ //! Note that having these passes interleaved results in a strictly more potent optimisation pass
15
+ //! which is able to optimise CFG in a way which two passes couldn’t do separately even if they
16
+ //! were run indefinitely one after another.
17
+ //!
18
+ //! Both these passes are very similar in their nature:
15
19
//!
16
20
//! | Constant | Simplify |
17
21
//! |----------------|-----------|-----------|
18
22
//! | Lattice Domain | Lvalue | Lvalue |
19
23
//! | Lattice Value | Constant | Constant |
20
24
//! | Transfer | x = const | x = const |
21
25
//! | Rewrite | x → const | T(x) → T' |
22
- //! | Bottom | {} | {} |
26
+ //! | Top | {} | {} |
23
27
//! | Join | intersect | intersect |
28
+ //! |----------------|-----------|-----------|
24
29
//!
25
- //! For all of them we will be using a lattice of `HashMap<Lvalue, Either<Lvalue, Constant, Top>>`.
30
+ //! It might be a good idea to eventually interleave move propagation here, as it has very
31
+ //! similar lattice to the constant propagation pass.
26
32
27
33
use rustc_data_structures:: fnv:: FnvHashMap ;
28
34
use rustc:: middle:: const_val:: ConstVal ;
35
+ use rustc:: hir:: def_id:: DefId ;
29
36
use rustc:: mir:: repr:: * ;
30
37
use rustc:: mir:: tcx:: binop_ty;
31
38
use rustc:: mir:: transform:: { Pass , MirPass , MirSource } ;
32
39
use rustc:: mir:: visit:: { MutVisitor } ;
33
40
use rustc:: ty:: TyCtxt ;
34
41
use rustc_const_eval:: { eval_const_binop, eval_const_unop, cast_const} ;
35
- use std:: collections:: hash_map:: Entry ;
36
42
37
43
use super :: dataflow:: * ;
38
44
39
- pub struct CsPropagate ;
45
+ pub struct ConstPropagate ;
40
46
41
- impl Pass for CsPropagate { }
47
+ impl Pass for ConstPropagate { }
42
48
43
- impl < ' tcx > MirPass < ' tcx > for CsPropagate {
49
+ impl < ' tcx > MirPass < ' tcx > for ConstPropagate {
44
50
fn run_pass < ' a > ( & mut self , tcx : TyCtxt < ' a , ' tcx , ' tcx > , _: MirSource , mir : & mut Mir < ' tcx > ) {
45
- * mir = Dataflow :: forward ( mir, CsTransfer { tcx : tcx } ,
51
+ * mir = Dataflow :: forward ( mir,
52
+ ConstTransfer { tcx : tcx } ,
46
53
ConstRewrite { tcx : tcx } . and_then ( SimplifyRewrite ) ) ;
47
54
}
48
55
}
49
56
50
- #[ derive( PartialEq , Debug , Clone ) ]
51
- enum Either < ' tcx > {
52
- Lvalue ( Lvalue < ' tcx > ) ,
53
- Const ( Constant < ' tcx > ) ,
54
- Top
57
+ #[ derive( Debug , Clone ) ]
58
+ struct ConstLattice < ' tcx > {
59
+ statics : FnvHashMap < DefId , Constant < ' tcx > > ,
60
+ // key must not be a static or a projection.
61
+ locals : FnvHashMap < Lvalue < ' tcx > , Constant < ' tcx > > ,
55
62
}
56
63
57
- impl < ' tcx > Lattice for Either < ' tcx > {
58
- fn bottom ( ) -> Self { unimplemented ! ( ) }
59
- fn join ( & mut self , other : Self ) -> bool {
60
- if !other. eq ( self ) {
61
- if Either :: Top . eq ( self ) {
62
- false
63
- } else {
64
- * self = Either :: Top ;
65
- true
66
- }
67
- } else {
68
- false
64
+ impl < ' tcx > ConstLattice < ' tcx > {
65
+ fn new ( ) -> ConstLattice < ' tcx > {
66
+ ConstLattice {
67
+ statics : FnvHashMap ( ) ,
68
+ locals : FnvHashMap ( )
69
69
}
70
70
}
71
- }
72
-
73
- #[ derive( Debug , Clone ) ]
74
- struct CsLattice < ' tcx > {
75
- values : FnvHashMap < Lvalue < ' tcx > , Either < ' tcx > >
76
- }
77
71
78
- impl < ' tcx > CsLattice < ' tcx > {
79
- fn insert ( & mut self , key : & Lvalue < ' tcx > , val : Either < ' tcx > ) {
72
+ fn insert ( & mut self , key : & Lvalue < ' tcx > , val : Constant < ' tcx > ) {
80
73
// FIXME: HashMap has no way to insert stuff without cloning the key even if it exists
81
74
// already.
82
75
match * key {
83
- // Do not bother with statics – global state.
84
- Lvalue :: Static ( _) => { }
76
+ Lvalue :: Static ( defid) => self . statics . insert ( defid, val) ,
85
77
// I feel like this could be handled, but needs special care. For example in code like
86
78
// this:
87
79
//
@@ -95,77 +87,90 @@ impl<'tcx> CsLattice<'tcx> {
95
87
// projections of var and not just var itself. Currently we handle this by not
96
88
// keeping any knowledge about projections at all, but I think eventually we
97
89
// want to do so.
98
- Lvalue :: Projection ( _) => { } ,
99
- _ => match self . values . entry ( key. clone ( ) ) {
100
- Entry :: Vacant ( e) => {
101
- e. insert ( val) ;
102
- }
103
- Entry :: Occupied ( mut e) => {
104
- e. get_mut ( ) . join ( val) ;
105
- }
106
- }
107
- }
90
+ Lvalue :: Projection ( _) => None ,
91
+ _ => self . locals . insert ( key. clone ( ) , val)
92
+ } ;
108
93
}
109
- fn remove ( & mut self , key : & Lvalue < ' tcx > ) -> Option < Either < ' tcx > > {
110
- self . values . remove ( key)
94
+
95
+ fn get ( & self , key : & Lvalue < ' tcx > ) -> Option < & Constant < ' tcx > > {
96
+ match * key {
97
+ Lvalue :: Static ( ref defid) => self . statics . get ( defid) ,
98
+ Lvalue :: Projection ( _) => None ,
99
+ _ => self . locals . get ( key) ,
100
+ }
111
101
}
102
+
112
103
fn top ( & mut self , key : & Lvalue < ' tcx > ) {
113
- self . insert ( key, Either :: Top ) ;
104
+ match * key {
105
+ Lvalue :: Static ( ref defid) => { self . statics . remove ( defid) ; }
106
+ Lvalue :: Projection ( _) => { }
107
+ _ => { self . locals . remove ( key) ; }
108
+ }
114
109
}
115
110
}
116
111
117
- impl < ' tcx > Lattice for CsLattice < ' tcx > {
118
- fn bottom ( ) -> Self { CsLattice { values : FnvHashMap ( ) } }
119
- fn join ( & mut self , mut other : Self ) -> bool {
120
- // Calculate inteersection this way:
121
- let mut changed = false ;
122
- // First, drain the self.values into a list of equal values common to both maps.
123
- let mut common_keys = vec ! [ ] ;
124
- for ( key, mut value) in self . values . drain ( ) {
125
- match other. values . remove ( & key) {
126
- // self had the key, but not other, so removing
127
- None => {
128
- changed = true ;
129
- }
130
- Some ( ov) => {
131
- changed |= value. join ( ov) ;
112
+ fn intersect_map < K , V > ( this : & mut FnvHashMap < K , V > , mut other : FnvHashMap < K , V > ) -> bool
113
+ where K : Eq + :: std:: hash:: Hash , V : PartialEq
114
+ {
115
+ let mut changed = false ;
116
+ // Calculate inteersection this way:
117
+ // First, drain the self.values into a list of equal values common to both maps.
118
+ let mut common_keys = vec ! [ ] ;
119
+ for ( key, value) in this. drain ( ) {
120
+ match other. remove ( & key) {
121
+ None => {
122
+ // self had the key, but not other, so it is Top (i.e. removed)
123
+ changed = true ;
124
+ }
125
+ Some ( ov) => {
126
+ // Similarly, if the values are not equal…
127
+ changed |= if ov != value {
128
+ true
129
+ } else {
132
130
common_keys. push ( ( key, value) ) ;
131
+ false
133
132
}
134
133
}
135
134
}
136
- // Now, put each common key with equal value back into the map.
137
- for ( key, value) in common_keys {
138
- self . values . insert ( key, value) ;
139
- }
140
- changed
135
+ }
136
+ // Now, put each common key with equal value back into the map.
137
+ for ( key, value) in common_keys {
138
+ this. insert ( key, value) ;
139
+ }
140
+ changed
141
+ }
142
+
143
+ impl < ' tcx > Lattice for ConstLattice < ' tcx > {
144
+ fn bottom ( ) -> Self { unimplemented ! ( ) }
145
+ fn join ( & mut self , other : Self ) -> bool {
146
+ intersect_map ( & mut self . locals , other. locals ) |
147
+ intersect_map ( & mut self . statics , other. statics )
141
148
}
142
149
}
143
150
144
- struct CsTransfer < ' a , ' tcx : ' a > {
151
+ struct ConstTransfer < ' a , ' tcx : ' a > {
145
152
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
146
153
}
147
154
148
- impl < ' a , ' tcx > Transfer < ' tcx > for CsTransfer < ' a , ' tcx > {
149
- type Lattice = CsLattice < ' tcx > ;
155
+ impl < ' a , ' tcx > Transfer < ' tcx > for ConstTransfer < ' a , ' tcx > {
156
+ type Lattice = ConstLattice < ' tcx > ;
150
157
type TerminatorReturn = Vec < Self :: Lattice > ;
151
158
152
159
fn stmt ( & self , s : & Statement < ' tcx > , mut lat : Self :: Lattice )
153
160
-> Self :: Lattice
154
161
{
155
162
let StatementKind :: Assign ( ref lval, ref rval) = s. kind ;
156
163
match * rval {
157
- Rvalue :: Use ( Operand :: Consume ( ref nlval) ) => {
158
- lat. insert ( lval, Either :: Lvalue ( nlval. clone ( ) ) ) ;
159
- // Consider moved.
160
- lat. remove ( nlval) ;
164
+ Rvalue :: Use ( Operand :: Consume ( _) ) => {
165
+ lat. top ( lval) ;
161
166
} ,
162
167
Rvalue :: Use ( Operand :: Constant ( ref cnst) ) => {
163
- lat. insert ( lval, Either :: Const ( cnst. clone ( ) ) ) ;
168
+ lat. insert ( lval, cnst. clone ( ) ) ;
164
169
} ,
165
170
// We do not want to deal with references and pointers here. Not yet and not without
166
171
// a way to query stuff about reference/pointer aliasing.
167
172
Rvalue :: Ref ( _, _, ref referee) => {
168
- lat. remove ( lval) ;
173
+ lat. top ( lval) ;
169
174
lat. top ( referee) ;
170
175
}
171
176
// FIXME: should calculate length of statically sized arrays and store it.
@@ -182,14 +187,16 @@ impl<'a, 'tcx> Transfer<'tcx> for CsTransfer<'a, 'tcx> {
182
187
Rvalue :: Box ( _) => { lat. top ( lval) ; }
183
188
// Not handled, but could be. Disaggregation helps to not bother with this.
184
189
Rvalue :: Aggregate ( ..) => { lat. top ( lval) ; }
185
- // Not handled, invalidate any knowledge about any variables used by this. Dangerous
190
+ // Not handled, invalidate any knowledge about any variables touched by this. Dangerous
186
191
// stuff and other dragons be here.
187
192
Rvalue :: InlineAsm { ref outputs, ref inputs, asm : _ } => {
188
193
lat. top ( lval) ;
189
194
for output in outputs { lat. top ( output) ; }
190
195
for input in inputs {
191
196
if let Operand :: Consume ( ref lval) = * input { lat. top ( lval) ; }
192
197
}
198
+ // Clear the statics, because inline assembly may mutate global state at will.
199
+ lat. statics . clear ( ) ;
193
200
}
194
201
} ;
195
202
lat
@@ -200,11 +207,11 @@ impl<'a, 'tcx> Transfer<'tcx> for CsTransfer<'a, 'tcx> {
200
207
{
201
208
let span = t. source_info . span ;
202
209
let succ_count = t. successors ( ) . len ( ) ;
203
- let bool_const = |b : bool | Either :: Const ( Constant {
210
+ let bool_const = |b : bool | Constant {
204
211
span : span,
205
212
ty : self . tcx . mk_bool ( ) ,
206
213
literal : Literal :: Value { value : ConstVal :: Bool ( b) } ,
207
- } ) ;
214
+ } ;
208
215
match t. kind {
209
216
TerminatorKind :: If { cond : Operand :: Consume ( ref lval) , .. } => {
210
217
let mut falsy = lat. clone ( ) ;
@@ -215,46 +222,42 @@ impl<'a, 'tcx> Transfer<'tcx> for CsTransfer<'a, 'tcx> {
215
222
TerminatorKind :: SwitchInt { ref discr, ref values, switch_ty, .. } => {
216
223
let mut vec: Vec < _ > = values. iter ( ) . map ( |val| {
217
224
let mut branch = lat. clone ( ) ;
218
- branch. insert ( discr, Either :: Const ( Constant {
225
+ branch. insert ( discr, Constant {
219
226
span : span,
220
227
ty : switch_ty,
221
228
literal : Literal :: Value { value : val. clone ( ) }
222
- } ) ) ;
229
+ } ) ;
223
230
branch
224
231
} ) . collect ( ) ;
225
232
vec. push ( lat) ;
226
233
vec
227
234
}
228
235
TerminatorKind :: Drop { ref location, .. } => {
229
- lat. remove ( location) ;
236
+ lat. top ( location) ;
237
+ // See comment in Call.
238
+ lat. statics . clear ( ) ;
230
239
vec ! [ lat; succ_count]
231
240
}
232
- TerminatorKind :: DropAndReplace { ref location, ref unwind , ref value, .. } => {
241
+ TerminatorKind :: DropAndReplace { ref location, ref value, .. } => {
233
242
match * value {
234
- Operand :: Consume ( ref lval) => {
235
- lat. remove ( location) ;
236
- lat. remove ( lval) ;
237
- } ,
238
- Operand :: Constant ( ref cnst) => {
239
- lat. insert ( location, Either :: Const ( cnst. clone ( ) ) ) ;
240
- }
241
- }
242
- if unwind. is_some ( ) {
243
- let mut unwind = lat. clone ( ) ;
244
- unwind. remove ( location) ;
245
- vec ! [ lat, unwind]
246
- } else {
247
- vec ! [ lat]
243
+ Operand :: Consume ( _) => lat. top ( location) ,
244
+ Operand :: Constant ( ref cnst) => lat. insert ( location, cnst. clone ( ) ) ,
248
245
}
246
+ // See comment in Call.
247
+ lat. statics . clear ( ) ;
248
+ vec ! [ lat; succ_count]
249
249
}
250
250
TerminatorKind :: Call { ref destination, ref args, .. } => {
251
251
for arg in args {
252
252
if let Operand :: Consume ( ref lval) = * arg {
253
253
// FIXME: Probably safe to not remove any non-projection lvals.
254
- lat. remove ( lval) ;
254
+ lat. top ( lval) ;
255
255
}
256
256
}
257
257
destination. as_ref ( ) . map ( |& ( ref lval, _) | lat. top ( lval) ) ;
258
+ // Clear all knowledge about statics, because call may mutate any global state
259
+ // without us knowing about it.
260
+ lat. statics . clear ( ) ;
258
261
vec ! [ lat; succ_count]
259
262
}
260
263
TerminatorKind :: Assert { ref cond, expected, ref cleanup, .. } => {
@@ -288,35 +291,31 @@ struct ConstRewrite<'a, 'tcx: 'a> {
288
291
tcx : TyCtxt < ' a , ' tcx , ' tcx >
289
292
}
290
293
impl < ' a , ' tcx , T > Rewrite < ' tcx , T > for ConstRewrite < ' a , ' tcx >
291
- where T : Transfer < ' tcx , Lattice =CsLattice < ' tcx > >
294
+ where T : Transfer < ' tcx , Lattice =ConstLattice < ' tcx > >
292
295
{
293
296
fn stmt ( & self , stmt : & Statement < ' tcx > , fact : & T :: Lattice ) -> StatementChange < ' tcx > {
294
297
let mut stmt = stmt. clone ( ) ;
295
- let mut vis = RewriteConstVisitor ( & fact. values ) ;
298
+ let mut vis = RewriteConstVisitor ( & fact) ;
296
299
vis. visit_statement ( START_BLOCK , & mut stmt) ;
297
300
ConstEvalVisitor ( self . tcx ) . visit_statement ( START_BLOCK , & mut stmt) ;
298
301
StatementChange :: Statement ( stmt)
299
302
}
300
303
301
304
fn term ( & self , term : & Terminator < ' tcx > , fact : & T :: Lattice ) -> TerminatorChange < ' tcx > {
302
305
let mut term = term. clone ( ) ;
303
- let mut vis = RewriteConstVisitor ( & fact. values ) ;
306
+ let mut vis = RewriteConstVisitor ( & fact) ;
304
307
vis. visit_terminator ( START_BLOCK , & mut term) ;
305
308
ConstEvalVisitor ( self . tcx ) . visit_terminator ( START_BLOCK , & mut term) ;
306
309
TerminatorChange :: Terminator ( term)
307
310
}
308
311
}
309
312
310
- struct RewriteConstVisitor < ' a , ' tcx : ' a > ( & ' a FnvHashMap < Lvalue < ' tcx > , Either < ' tcx > > ) ;
313
+ struct RewriteConstVisitor < ' a , ' tcx : ' a > ( & ' a ConstLattice < ' tcx > ) ;
311
314
impl < ' a , ' tcx > MutVisitor < ' tcx > for RewriteConstVisitor < ' a , ' tcx > {
312
315
fn visit_operand ( & mut self , op : & mut Operand < ' tcx > ) {
313
316
// To satisy borrow checker, modify `op` after inspecting it
314
317
let repl = if let Operand :: Consume ( ref lval) = * op {
315
- if let Some ( & Either :: Const ( ref c) ) = self . 0 . get ( lval) {
316
- Some ( c. clone ( ) )
317
- } else {
318
- None
319
- }
318
+ self . 0 . get ( lval) . cloned ( )
320
319
} else {
321
320
None
322
321
} ;
0 commit comments