@@ -4,9 +4,14 @@ use crate::transform::{MirPass, MirSource};
4
4
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
5
5
use rustc_hir:: Mutability ;
6
6
use rustc_index:: vec:: Idx ;
7
- use rustc_middle:: mir:: visit:: { MutVisitor , Visitor } ;
8
7
use 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 ,
10
15
} ;
11
16
use rustc_middle:: ty:: { self , TyCtxt } ;
12
17
use std:: mem;
@@ -71,10 +76,36 @@ impl<'tcx> MutVisitor<'tcx> for InstCombineVisitor<'tcx> {
71
76
* rvalue = Rvalue :: Use ( operand) ;
72
77
}
73
78
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
+
74
84
self . super_rvalue ( rvalue, location)
75
85
}
76
86
}
77
87
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
+
78
109
/// Finds optimization opportunities on the MIR.
79
110
struct OptimizationFinder < ' b , ' tcx > {
80
111
body : & ' b Body < ' tcx > ,
@@ -87,6 +118,85 @@ impl OptimizationFinder<'b, 'tcx> {
87
118
OptimizationFinder { body, tcx, optimizations : OptimizationList :: default ( ) }
88
119
}
89
120
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
+
90
200
fn find_unneeded_equality_comparison ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
91
201
// find Ne(_place, false) or Ne(false, _place)
92
202
// or Eq(_place, true) or Eq(true, _place)
@@ -153,6 +263,8 @@ impl Visitor<'tcx> for OptimizationFinder<'b, 'tcx> {
153
263
}
154
264
}
155
265
266
+ let _ = self . find_deref_of_address ( rvalue, location) ;
267
+
156
268
self . find_unneeded_equality_comparison ( rvalue, location) ;
157
269
158
270
self . super_rvalue ( rvalue, location)
@@ -164,4 +276,5 @@ struct OptimizationList<'tcx> {
164
276
and_stars : FxHashSet < Location > ,
165
277
arrays_lengths : FxHashMap < Location , Constant < ' tcx > > ,
166
278
unneeded_equality_comparison : FxHashMap < Location , Operand < ' tcx > > ,
279
+ unneeded_deref : FxHashMap < Location , Place < ' tcx > > ,
167
280
}
0 commit comments