@@ -5,7 +5,7 @@ use super::FunctionCx;
5
5
use crate :: traits:: * ;
6
6
use rustc_data_structures:: graph:: dominators:: Dominators ;
7
7
use rustc_index:: bit_set:: BitSet ;
8
- use rustc_index:: vec:: { Idx , IndexVec } ;
8
+ use rustc_index:: vec:: IndexVec ;
9
9
use rustc_middle:: mir:: traversal;
10
10
use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
11
11
use rustc_middle:: mir:: { self , Location , TerminatorKind } ;
@@ -16,7 +16,29 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
16
16
fx : & FunctionCx < ' a , ' tcx , Bx > ,
17
17
) -> BitSet < mir:: Local > {
18
18
let mir = fx. mir ;
19
- let mut analyzer = LocalAnalyzer :: new ( fx) ;
19
+ let dominators = mir. dominators ( ) ;
20
+ let locals = mir
21
+ . local_decls
22
+ . iter ( )
23
+ . map ( |decl| {
24
+ let ty = fx. monomorphize ( decl. ty ) ;
25
+ let layout = fx. cx . spanned_layout_of ( ty, decl. source_info . span ) ;
26
+ if layout. is_zst ( ) {
27
+ LocalKind :: ZST
28
+ } else if fx. cx . is_backend_immediate ( layout) || fx. cx . is_backend_scalar_pair ( layout) {
29
+ LocalKind :: Unused
30
+ } else {
31
+ LocalKind :: Memory
32
+ }
33
+ } )
34
+ . collect ( ) ;
35
+
36
+ let mut analyzer = LocalAnalyzer { fx, dominators, locals } ;
37
+
38
+ // Arguments get assigned to by means of the function being called
39
+ for arg in mir. args_iter ( ) {
40
+ analyzer. assign ( arg, mir:: START_BLOCK . start_location ( ) ) ;
41
+ }
20
42
21
43
// If there exists a local definition that dominates all uses of that local,
22
44
// the definition should be visited first. Traverse blocks in preorder which
@@ -25,76 +47,45 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
25
47
analyzer. visit_basic_block_data ( bb, data) ;
26
48
}
27
49
28
- for ( local, decl) in mir. local_decls . iter_enumerated ( ) {
29
- let ty = fx. monomorphize ( decl. ty ) ;
30
- debug ! ( "local {:?} has type `{}`" , local, ty) ;
31
- let layout = fx. cx . spanned_layout_of ( ty, decl. source_info . span ) ;
32
- if fx. cx . is_backend_immediate ( layout) {
33
- // These sorts of types are immediates that we can store
34
- // in an Value without an alloca.
35
- } else if fx. cx . is_backend_scalar_pair ( layout) {
36
- // We allow pairs and uses of any of their 2 fields.
37
- } else {
38
- // These sorts of types require an alloca. Note that
39
- // is_llvm_immediate() may *still* be true, particularly
40
- // for newtypes, but we currently force some types
41
- // (e.g., structs) into an alloca unconditionally, just so
42
- // that we don't have to deal with having two pathways
43
- // (gep vs extractvalue etc).
44
- analyzer. not_ssa ( local) ;
50
+ let mut non_ssa_locals = BitSet :: new_empty ( analyzer. locals . len ( ) ) ;
51
+ for ( local, kind) in analyzer. locals . iter_enumerated ( ) {
52
+ if matches ! ( kind, LocalKind :: Memory ) {
53
+ non_ssa_locals. insert ( local) ;
45
54
}
46
55
}
47
56
48
- analyzer. non_ssa_locals
57
+ non_ssa_locals
58
+ }
59
+
60
+ #[ derive( Copy , Clone , PartialEq , Eq ) ]
61
+ enum LocalKind {
62
+ ZST ,
63
+ /// A local that requires an alloca.
64
+ Memory ,
65
+ /// A scalar or a scalar pair local that is neither defined nor used.
66
+ Unused ,
67
+ /// A scalar or a scalar pair local with a single definition that dominates all uses.
68
+ SSA ( mir:: Location ) ,
49
69
}
50
70
51
71
struct LocalAnalyzer < ' mir , ' a , ' tcx , Bx : BuilderMethods < ' a , ' tcx > > {
52
72
fx : & ' mir FunctionCx < ' a , ' tcx , Bx > ,
53
73
dominators : Dominators < mir:: BasicBlock > ,
54
- non_ssa_locals : BitSet < mir:: Local > ,
55
- // The location of the first visited direct assignment to each
56
- // local, or an invalid location (out of bounds `block` index).
57
- first_assignment : IndexVec < mir:: Local , Location > ,
74
+ locals : IndexVec < mir:: Local , LocalKind > ,
58
75
}
59
76
60
77
impl < Bx : BuilderMethods < ' a , ' tcx > > LocalAnalyzer < ' mir , ' a , ' tcx , Bx > {
61
- fn new ( fx : & ' mir FunctionCx < ' a , ' tcx , Bx > ) -> Self {
62
- let invalid_location = mir:: BasicBlock :: new ( fx. mir . basic_blocks ( ) . len ( ) ) . start_location ( ) ;
63
- let dominators = fx. mir . dominators ( ) ;
64
- let mut analyzer = LocalAnalyzer {
65
- fx,
66
- dominators,
67
- non_ssa_locals : BitSet :: new_empty ( fx. mir . local_decls . len ( ) ) ,
68
- first_assignment : IndexVec :: from_elem ( invalid_location, & fx. mir . local_decls ) ,
69
- } ;
70
-
71
- // Arguments get assigned to by means of the function being called
72
- for arg in fx. mir . args_iter ( ) {
73
- analyzer. first_assignment [ arg] = mir:: START_BLOCK . start_location ( ) ;
74
- }
75
-
76
- analyzer
77
- }
78
-
79
- fn first_assignment ( & self , local : mir:: Local ) -> Option < Location > {
80
- let location = self . first_assignment [ local] ;
81
- if location. block . index ( ) < self . fx . mir . basic_blocks ( ) . len ( ) {
82
- Some ( location)
83
- } else {
84
- None
85
- }
86
- }
87
-
88
- fn not_ssa ( & mut self , local : mir:: Local ) {
89
- debug ! ( "marking {:?} as non-SSA" , local) ;
90
- self . non_ssa_locals . insert ( local) ;
91
- }
92
-
93
78
fn assign ( & mut self , local : mir:: Local , location : Location ) {
94
- if self . first_assignment ( local) . is_some ( ) {
95
- self . not_ssa ( local) ;
96
- } else {
97
- self . first_assignment [ local] = location;
79
+ let kind = & mut self . locals [ local] ;
80
+ match * kind {
81
+ LocalKind :: ZST => { }
82
+ LocalKind :: Memory => { }
83
+ LocalKind :: Unused => {
84
+ * kind = LocalKind :: SSA ( location) ;
85
+ }
86
+ LocalKind :: SSA ( _) => {
87
+ * kind = LocalKind :: Memory ;
88
+ }
98
89
}
99
90
}
100
91
@@ -175,11 +166,13 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
175
166
) {
176
167
debug ! ( "visit_assign(place={:?}, rvalue={:?})" , place, rvalue) ;
177
168
178
- if let Some ( index) = place. as_local ( ) {
179
- self . assign ( index, location) ;
180
- let decl_span = self . fx . mir . local_decls [ index] . source_info . span ;
181
- if !self . fx . rvalue_creates_operand ( rvalue, decl_span) {
182
- self . not_ssa ( index) ;
169
+ if let Some ( local) = place. as_local ( ) {
170
+ self . assign ( local, location) ;
171
+ if self . locals [ local] != LocalKind :: Memory {
172
+ let decl_span = self . fx . mir . local_decls [ local] . source_info . span ;
173
+ if !self . fx . rvalue_creates_operand ( rvalue, decl_span) {
174
+ self . locals [ local] = LocalKind :: Memory ;
175
+ }
183
176
}
184
177
} else {
185
178
self . visit_place ( place, PlaceContext :: MutatingUse ( MutatingUseContext :: Store ) , location) ;
@@ -204,32 +197,18 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
204
197
205
198
PlaceContext :: NonMutatingUse (
206
199
NonMutatingUseContext :: Copy | NonMutatingUseContext :: Move ,
207
- ) => {
200
+ ) => match & mut self . locals [ local] {
201
+ LocalKind :: ZST => { }
202
+ LocalKind :: Memory => { }
203
+ LocalKind :: SSA ( def) if def. dominates ( location, & self . dominators ) => { }
208
204
// Reads from uninitialized variables (e.g., in dead code, after
209
205
// optimizations) require locals to be in (uninitialized) memory.
210
206
// N.B., there can be uninitialized reads of a local visited after
211
207
// an assignment to that local, if they happen on disjoint paths.
212
- let ssa_read = match self . first_assignment ( local) {
213
- Some ( assignment_location) => {
214
- assignment_location. dominates ( location, & self . dominators )
215
- }
216
- None => {
217
- debug ! ( "No first assignment found for {:?}" , local) ;
218
- // We have not seen any assignment to the local yet,
219
- // but before marking not_ssa, check if it is a ZST,
220
- // in which case we don't need to initialize the local.
221
- let ty = self . fx . mir . local_decls [ local] . ty ;
222
- let ty = self . fx . monomorphize ( ty) ;
223
-
224
- let is_zst = self . fx . cx . layout_of ( ty) . is_zst ( ) ;
225
- debug ! ( "is_zst: {}" , is_zst) ;
226
- is_zst
227
- }
228
- } ;
229
- if !ssa_read {
230
- self . not_ssa ( local) ;
208
+ kind @ ( LocalKind :: Unused | LocalKind :: SSA ( _) ) => {
209
+ * kind = LocalKind :: Memory ;
231
210
}
232
- }
211
+ } ,
233
212
234
213
PlaceContext :: MutatingUse (
235
214
MutatingUseContext :: Store
@@ -246,16 +225,18 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
246
225
| NonMutatingUseContext :: AddressOf
247
226
| NonMutatingUseContext :: Projection ,
248
227
) => {
249
- self . not_ssa ( local) ;
228
+ self . locals [ local] = LocalKind :: Memory ;
250
229
}
251
230
252
231
PlaceContext :: MutatingUse ( MutatingUseContext :: Drop ) => {
253
- let ty = self . fx . mir . local_decls [ local] . ty ;
254
- let ty = self . fx . monomorphize ( ty) ;
255
-
256
- // Only need the place if we're actually dropping it.
257
- if self . fx . cx . type_needs_drop ( ty) {
258
- self . not_ssa ( local) ;
232
+ let kind = & mut self . locals [ local] ;
233
+ if * kind != LocalKind :: Memory {
234
+ let ty = self . fx . mir . local_decls [ local] . ty ;
235
+ let ty = self . fx . monomorphize ( ty) ;
236
+ if self . fx . cx . type_needs_drop ( ty) {
237
+ // Only need the place if we're actually dropping it.
238
+ * kind = LocalKind :: Memory ;
239
+ }
259
240
}
260
241
}
261
242
}
0 commit comments