@@ -4,9 +4,14 @@ use crate::transform::{MirPass, MirSource};
44use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
55use rustc_hir:: Mutability ;
66use rustc_index:: vec:: Idx ;
7- use rustc_middle:: mir:: visit:: { MutVisitor , Visitor } ;
87use rustc_middle:: mir:: {
9- BinOp , Body , Constant , Local , Location , Operand , Place , PlaceRef , ProjectionElem , Rvalue ,
8+ visit:: PlaceContext ,
9+ visit:: { MutVisitor , Visitor } ,
10+ Statement ,
11+ } ;
12+ use rustc_middle:: mir:: {
13+ BinOp , Body , BorrowKind , Constant , Local , Location , Operand , Place , PlaceRef , ProjectionElem ,
14+ Rvalue ,
1015} ;
1116use rustc_middle:: ty:: { self , TyCtxt } ;
1217use std:: mem;
@@ -71,10 +76,36 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
7176 * rvalue = Rvalue :: Use ( operand) ;
7277 }
7378
79+ if let Some ( place) = self . optimizations . unneeded_deref . remove ( & location) {
80+ debug ! ( "unneeded_deref: replacing {:?} with {:?}" , rvalue, place) ;
81+ * rvalue = Rvalue :: Use ( Operand :: Copy ( place) ) ;
82+ }
83+
7484 self . super_rvalue ( rvalue, location)
7585 }
7686}
7787
88+ struct MutatingUseVisitor {
89+ has_mutating_use : bool ,
90+ local_to_look_for : Local ,
91+ }
92+
93+ impl MutatingUseVisitor {
94+ fn has_mutating_use_in_stmt ( local : Local , stmt : & Statement < ' tcx > , location : Location ) -> bool {
95+ let mut _self = Self { has_mutating_use : false , local_to_look_for : local } ;
96+ _self. visit_statement ( stmt, location) ;
97+ _self. has_mutating_use
98+ }
99+ }
100+
101+ impl < ' tcx > Visitor < ' tcx > for MutatingUseVisitor {
102+ fn visit_local ( & mut self , local : & Local , context : PlaceContext , _: Location ) {
103+ if * local == self . local_to_look_for {
104+ self . has_mutating_use |= context. is_mutating_use ( ) ;
105+ }
106+ }
107+ }
108+
78109/// Finds optimization opportunities on the MIR.
79110struct OptimizationFinder < ' b , ' tcx > {
80111 body : & ' b Body < ' tcx > ,
@@ -87,6 +118,85 @@ impl OptimizationFinder<'b, 'tcx> {
87118 OptimizationFinder { body, tcx, optimizations : OptimizationList :: default ( ) }
88119 }
89120
121+ fn find_deref_of_address ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) -> Option < ( ) > {
122+ // Look for the sequence
123+ //
124+ // _2 = &_1;
125+ // ...
126+ // _5 = (*_2);
127+ //
128+ // which we can replace the last statement with `_5 = _1;` to avoid the load of `_2`.
129+ if let Rvalue :: Use ( op) = rvalue {
130+ let local_being_derefed = match op. place ( ) ?. as_ref ( ) {
131+ PlaceRef { local, projection : [ ProjectionElem :: Deref ] } => Some ( local) ,
132+ _ => None ,
133+ } ?;
134+
135+ let stmt_index = location. statement_index ;
136+ // Look behind for statement that assigns the local from a address of operator.
137+ // 6 is chosen as a heuristic determined by seeing the number of times
138+ // the optimization kicked in compiling rust std.
139+ let lower_index = stmt_index. saturating_sub ( 6 ) ;
140+ let statements_to_look_in = self . body . basic_blocks ( ) [ location. block ] . statements
141+ [ lower_index..stmt_index]
142+ . iter ( )
143+ . rev ( ) ;
144+ for stmt in statements_to_look_in {
145+ match & stmt. kind {
146+ // Exhaustive match on statements to detect conditions that warrant we bail out of the optimization.
147+ rustc_middle:: mir:: StatementKind :: Assign ( box ( l, r) )
148+ if l. local == local_being_derefed =>
149+ {
150+ match r {
151+ // Looking for immutable reference e.g _local_being_deref = &_1;
152+ Rvalue :: Ref (
153+ _,
154+ // Only apply the optimization if it is an immutable borrow.
155+ BorrowKind :: Shared ,
156+ place_taken_address_of,
157+ ) => {
158+ self . optimizations
159+ . unneeded_deref
160+ . insert ( location, * place_taken_address_of) ;
161+ return Some ( ( ) ) ;
162+ }
163+
164+ // We found an assignment of `local_being_deref` that is not an immutable ref, e.g the following sequence
165+ // _2 = &_1;
166+ // _3 = &5
167+ // _2 = _3; <-- this means it is no longer valid to replace the last statement with `_5 = _1;`
168+ // _5 = (*_2);
169+ _ => return None ,
170+ }
171+ }
172+
173+ // Inline asm can do anything, so bail out of the optimization.
174+ rustc_middle:: mir:: StatementKind :: LlvmInlineAsm ( _) => return None ,
175+
176+ // Check that `local_being_deref` is not being used in a mutating way which can cause misoptimization.
177+ rustc_middle:: mir:: StatementKind :: Assign ( box ( _, _) )
178+ | rustc_middle:: mir:: StatementKind :: Coverage ( _)
179+ | rustc_middle:: mir:: StatementKind :: Nop
180+ | rustc_middle:: mir:: StatementKind :: FakeRead ( _, _)
181+ | rustc_middle:: mir:: StatementKind :: StorageLive ( _)
182+ | rustc_middle:: mir:: StatementKind :: StorageDead ( _)
183+ | rustc_middle:: mir:: StatementKind :: Retag ( _, _)
184+ | rustc_middle:: mir:: StatementKind :: AscribeUserType ( _, _)
185+ | rustc_middle:: mir:: StatementKind :: SetDiscriminant { .. } => {
186+ if MutatingUseVisitor :: has_mutating_use_in_stmt (
187+ local_being_derefed,
188+ stmt,
189+ location,
190+ ) {
191+ return None ;
192+ }
193+ }
194+ }
195+ }
196+ }
197+ Some ( ( ) )
198+ }
199+
90200 fn find_unneeded_equality_comparison ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
91201 // find Ne(_place, false) or Ne(false, _place)
92202 // or Eq(_place, true) or Eq(true, _place)
@@ -153,6 +263,8 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
153263 }
154264 }
155265
266+ let _ = self . find_deref_of_address ( rvalue, location) ;
267+
156268 self . find_unneeded_equality_comparison ( rvalue, location) ;
157269
158270 self . super_rvalue ( rvalue, location)
@@ -164,4 +276,5 @@ struct OptimizationList<'tcx> {
164276 and_stars : FxHashSet < Location > ,
165277 arrays_lengths : FxHashMap < Location , Constant < ' tcx > > ,
166278 unneeded_equality_comparison : FxHashMap < Location , Operand < ' tcx > > ,
279+ unneeded_deref : FxHashMap < Location , Place < ' tcx > > ,
167280}
0 commit comments