88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
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.
1313//!
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:
1519//!
1620//! | Constant | Simplify |
1721//! |----------------|-----------|-----------|
1822//! | Lattice Domain | Lvalue | Lvalue |
1923//! | Lattice Value | Constant | Constant |
2024//! | Transfer | x = const | x = const |
2125//! | Rewrite | x → const | T(x) → T' |
22- //! | Bottom | {} | {} |
26+ //! | Top | {} | {} |
2327//! | Join | intersect | intersect |
28+ //! |----------------|-----------|-----------|
2429//!
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.
2632
2733use rustc_data_structures:: fnv:: FnvHashMap ;
2834use rustc:: middle:: const_val:: ConstVal ;
35+ use rustc:: hir:: def_id:: DefId ;
2936use rustc:: mir:: repr:: * ;
3037use rustc:: mir:: tcx:: binop_ty;
3138use rustc:: mir:: transform:: { Pass , MirPass , MirSource } ;
3239use rustc:: mir:: visit:: { MutVisitor } ;
3340use rustc:: ty:: TyCtxt ;
3441use rustc_const_eval:: { eval_const_binop, eval_const_unop, cast_const} ;
35- use std:: collections:: hash_map:: Entry ;
3642
3743use super :: dataflow:: * ;
3844
39- pub struct CsPropagate ;
45+ pub struct ConstPropagate ;
4046
41- impl Pass for CsPropagate { }
47+ impl Pass for ConstPropagate { }
4248
43- impl < ' tcx > MirPass < ' tcx > for CsPropagate {
49+ impl < ' tcx > MirPass < ' tcx > for ConstPropagate {
4450 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 } ,
4653 ConstRewrite { tcx : tcx } . and_then ( SimplifyRewrite ) ) ;
4754 }
4855}
4956
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 > > ,
5562}
5663
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 ( )
6969 }
7070 }
71- }
72-
73- #[ derive( Debug , Clone ) ]
74- struct CsLattice < ' tcx > {
75- values : FnvHashMap < Lvalue < ' tcx > , Either < ' tcx > >
76- }
7771
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 > ) {
8073 // FIXME: HashMap has no way to insert stuff without cloning the key even if it exists
8174 // already.
8275 match * key {
83- // Do not bother with statics – global state.
84- Lvalue :: Static ( _) => { }
76+ Lvalue :: Static ( defid) => self . statics . insert ( defid, val) ,
8577 // I feel like this could be handled, but needs special care. For example in code like
8678 // this:
8779 //
@@ -95,77 +87,90 @@ impl<'tcx> CsLattice<'tcx> {
9587 // projections of var and not just var itself. Currently we handle this by not
9688 // keeping any knowledge about projections at all, but I think eventually we
9789 // 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+ } ;
10893 }
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+ }
111101 }
102+
112103 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+ }
114109 }
115110}
116111
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 {
132130 common_keys. push ( ( key, value) ) ;
131+ false
133132 }
134133 }
135134 }
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 )
141148 }
142149}
143150
144- struct CsTransfer < ' a , ' tcx : ' a > {
151+ struct ConstTransfer < ' a , ' tcx : ' a > {
145152 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
146153}
147154
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 > ;
150157 type TerminatorReturn = Vec < Self :: Lattice > ;
151158
152159 fn stmt ( & self , s : & Statement < ' tcx > , mut lat : Self :: Lattice )
153160 -> Self :: Lattice
154161 {
155162 let StatementKind :: Assign ( ref lval, ref rval) = s. kind ;
156163 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) ;
161166 } ,
162167 Rvalue :: Use ( Operand :: Constant ( ref cnst) ) => {
163- lat. insert ( lval, Either :: Const ( cnst. clone ( ) ) ) ;
168+ lat. insert ( lval, cnst. clone ( ) ) ;
164169 } ,
165170 // We do not want to deal with references and pointers here. Not yet and not without
166171 // a way to query stuff about reference/pointer aliasing.
167172 Rvalue :: Ref ( _, _, ref referee) => {
168- lat. remove ( lval) ;
173+ lat. top ( lval) ;
169174 lat. top ( referee) ;
170175 }
171176 // FIXME: should calculate length of statically sized arrays and store it.
@@ -182,14 +187,16 @@ impl<'a, 'tcx> Transfer<'tcx> for CsTransfer<'a, 'tcx> {
182187 Rvalue :: Box ( _) => { lat. top ( lval) ; }
183188 // Not handled, but could be. Disaggregation helps to not bother with this.
184189 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
186191 // stuff and other dragons be here.
187192 Rvalue :: InlineAsm { ref outputs, ref inputs, asm : _ } => {
188193 lat. top ( lval) ;
189194 for output in outputs { lat. top ( output) ; }
190195 for input in inputs {
191196 if let Operand :: Consume ( ref lval) = * input { lat. top ( lval) ; }
192197 }
198+ // Clear the statics, because inline assembly may mutate global state at will.
199+ lat. statics . clear ( ) ;
193200 }
194201 } ;
195202 lat
@@ -200,11 +207,11 @@ impl<'a, 'tcx> Transfer<'tcx> for CsTransfer<'a, 'tcx> {
200207 {
201208 let span = t. source_info . span ;
202209 let succ_count = t. successors ( ) . len ( ) ;
203- let bool_const = |b : bool | Either :: Const ( Constant {
210+ let bool_const = |b : bool | Constant {
204211 span : span,
205212 ty : self . tcx . mk_bool ( ) ,
206213 literal : Literal :: Value { value : ConstVal :: Bool ( b) } ,
207- } ) ;
214+ } ;
208215 match t. kind {
209216 TerminatorKind :: If { cond : Operand :: Consume ( ref lval) , .. } => {
210217 let mut falsy = lat. clone ( ) ;
@@ -215,46 +222,42 @@ impl<'a, 'tcx> Transfer<'tcx> for CsTransfer<'a, 'tcx> {
215222 TerminatorKind :: SwitchInt { ref discr, ref values, switch_ty, .. } => {
216223 let mut vec: Vec < _ > = values. iter ( ) . map ( |val| {
217224 let mut branch = lat. clone ( ) ;
218- branch. insert ( discr, Either :: Const ( Constant {
225+ branch. insert ( discr, Constant {
219226 span : span,
220227 ty : switch_ty,
221228 literal : Literal :: Value { value : val. clone ( ) }
222- } ) ) ;
229+ } ) ;
223230 branch
224231 } ) . collect ( ) ;
225232 vec. push ( lat) ;
226233 vec
227234 }
228235 TerminatorKind :: Drop { ref location, .. } => {
229- lat. remove ( location) ;
236+ lat. top ( location) ;
237+ // See comment in Call.
238+ lat. statics . clear ( ) ;
230239 vec ! [ lat; succ_count]
231240 }
232- TerminatorKind :: DropAndReplace { ref location, ref unwind , ref value, .. } => {
241+ TerminatorKind :: DropAndReplace { ref location, ref value, .. } => {
233242 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 ( ) ) ,
248245 }
246+ // See comment in Call.
247+ lat. statics . clear ( ) ;
248+ vec ! [ lat; succ_count]
249249 }
250250 TerminatorKind :: Call { ref destination, ref args, .. } => {
251251 for arg in args {
252252 if let Operand :: Consume ( ref lval) = * arg {
253253 // FIXME: Probably safe to not remove any non-projection lvals.
254- lat. remove ( lval) ;
254+ lat. top ( lval) ;
255255 }
256256 }
257257 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 ( ) ;
258261 vec ! [ lat; succ_count]
259262 }
260263 TerminatorKind :: Assert { ref cond, expected, ref cleanup, .. } => {
@@ -288,35 +291,31 @@ struct ConstRewrite<'a, 'tcx: 'a> {
288291 tcx : TyCtxt < ' a , ' tcx , ' tcx >
289292}
290293impl < ' 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 > >
292295{
293296 fn stmt ( & self , stmt : & Statement < ' tcx > , fact : & T :: Lattice ) -> StatementChange < ' tcx > {
294297 let mut stmt = stmt. clone ( ) ;
295- let mut vis = RewriteConstVisitor ( & fact. values ) ;
298+ let mut vis = RewriteConstVisitor ( & fact) ;
296299 vis. visit_statement ( START_BLOCK , & mut stmt) ;
297300 ConstEvalVisitor ( self . tcx ) . visit_statement ( START_BLOCK , & mut stmt) ;
298301 StatementChange :: Statement ( stmt)
299302 }
300303
301304 fn term ( & self , term : & Terminator < ' tcx > , fact : & T :: Lattice ) -> TerminatorChange < ' tcx > {
302305 let mut term = term. clone ( ) ;
303- let mut vis = RewriteConstVisitor ( & fact. values ) ;
306+ let mut vis = RewriteConstVisitor ( & fact) ;
304307 vis. visit_terminator ( START_BLOCK , & mut term) ;
305308 ConstEvalVisitor ( self . tcx ) . visit_terminator ( START_BLOCK , & mut term) ;
306309 TerminatorChange :: Terminator ( term)
307310 }
308311}
309312
310- struct RewriteConstVisitor < ' a , ' tcx : ' a > ( & ' a FnvHashMap < Lvalue < ' tcx > , Either < ' tcx > > ) ;
313+ struct RewriteConstVisitor < ' a , ' tcx : ' a > ( & ' a ConstLattice < ' tcx > ) ;
311314impl < ' a , ' tcx > MutVisitor < ' tcx > for RewriteConstVisitor < ' a , ' tcx > {
312315 fn visit_operand ( & mut self , op : & mut Operand < ' tcx > ) {
313316 // To satisy borrow checker, modify `op` after inspecting it
314317 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 ( )
320319 } else {
321320 None
322321 } ;
0 commit comments