12
12
//! which do not.
13
13
14
14
use rustc_data_structures:: bitvec:: BitVector ;
15
+ use rustc_data_structures:: control_flow_graph:: dominators:: Dominators ;
15
16
use rustc_data_structures:: indexed_vec:: { Idx , IndexVec } ;
16
17
use rustc:: mir:: { self , Location , TerminatorKind } ;
17
18
use rustc:: mir:: visit:: { Visitor , PlaceContext } ;
@@ -21,7 +22,7 @@ use rustc::ty::layout::LayoutOf;
21
22
use type_of:: LayoutLlvmExt ;
22
23
use super :: FunctionCx ;
23
24
24
- pub fn memory_locals < ' a , ' tcx > ( fx : & FunctionCx < ' a , ' tcx > ) -> BitVector {
25
+ pub fn non_ssa_locals < ' a , ' tcx > ( fx : & FunctionCx < ' a , ' tcx > ) -> BitVector {
25
26
let mir = fx. mir ;
26
27
let mut analyzer = LocalAnalyzer :: new ( fx) ;
27
28
@@ -43,43 +44,60 @@ pub fn memory_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector {
43
44
// (e.g. structs) into an alloca unconditionally, just so
44
45
// that we don't have to deal with having two pathways
45
46
// (gep vs extractvalue etc).
46
- analyzer. mark_as_memory ( mir:: Local :: new ( index) ) ;
47
+ analyzer. not_ssa ( mir:: Local :: new ( index) ) ;
47
48
}
48
49
}
49
50
50
- analyzer. memory_locals
51
+ analyzer. non_ssa_locals
51
52
}
52
53
53
54
struct LocalAnalyzer < ' mir , ' a : ' mir , ' tcx : ' a > {
54
55
fx : & ' mir FunctionCx < ' a , ' tcx > ,
55
- memory_locals : BitVector ,
56
- seen_assigned : BitVector
56
+ dominators : Dominators < mir:: BasicBlock > ,
57
+ non_ssa_locals : BitVector ,
58
+ // The location of the first visited direct assignment to each
59
+ // local, or an invalid location (out of bounds `block` index).
60
+ first_assignment : IndexVec < mir:: Local , Location >
57
61
}
58
62
59
63
impl < ' mir , ' a , ' tcx > LocalAnalyzer < ' mir , ' a , ' tcx > {
60
64
fn new ( fx : & ' mir FunctionCx < ' a , ' tcx > ) -> LocalAnalyzer < ' mir , ' a , ' tcx > {
65
+ let invalid_location =
66
+ mir:: BasicBlock :: new ( fx. mir . basic_blocks ( ) . len ( ) ) . start_location ( ) ;
61
67
let mut analyzer = LocalAnalyzer {
62
68
fx,
63
- memory_locals : BitVector :: new ( fx. mir . local_decls . len ( ) ) ,
64
- seen_assigned : BitVector :: new ( fx. mir . local_decls . len ( ) )
69
+ dominators : fx. mir . dominators ( ) ,
70
+ non_ssa_locals : BitVector :: new ( fx. mir . local_decls . len ( ) ) ,
71
+ first_assignment : IndexVec :: from_elem ( invalid_location, & fx. mir . local_decls )
65
72
} ;
66
73
67
74
// Arguments get assigned to by means of the function being called
68
- for idx in 0 .. fx. mir . arg_count {
69
- analyzer. seen_assigned . insert ( idx + 1 ) ;
75
+ for arg in fx. mir . args_iter ( ) {
76
+ analyzer. first_assignment [ arg ] = mir :: START_BLOCK . start_location ( ) ;
70
77
}
71
78
72
79
analyzer
73
80
}
74
81
75
- fn mark_as_memory ( & mut self , local : mir:: Local ) {
76
- debug ! ( "marking {:?} as memory" , local) ;
77
- self . memory_locals . insert ( local. index ( ) ) ;
82
+ fn first_assignment ( & self , local : mir:: Local ) -> Option < Location > {
83
+ let location = self . first_assignment [ local] ;
84
+ if location. block . index ( ) < self . fx . mir . basic_blocks ( ) . len ( ) {
85
+ Some ( location)
86
+ } else {
87
+ None
88
+ }
78
89
}
79
90
80
- fn mark_assigned ( & mut self , local : mir:: Local ) {
81
- if !self . seen_assigned . insert ( local. index ( ) ) {
82
- self . mark_as_memory ( local) ;
91
+ fn not_ssa ( & mut self , local : mir:: Local ) {
92
+ debug ! ( "marking {:?} as non-SSA" , local) ;
93
+ self . non_ssa_locals . insert ( local. index ( ) ) ;
94
+ }
95
+
96
+ fn assign ( & mut self , local : mir:: Local , location : Location ) {
97
+ if self . first_assignment ( local) . is_some ( ) {
98
+ self . not_ssa ( local) ;
99
+ } else {
100
+ self . first_assignment [ local] = location;
83
101
}
84
102
}
85
103
}
@@ -93,9 +111,9 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
93
111
debug ! ( "visit_assign(block={:?}, place={:?}, rvalue={:?})" , block, place, rvalue) ;
94
112
95
113
if let mir:: Place :: Local ( index) = * place {
96
- self . mark_assigned ( index) ;
114
+ self . assign ( index, location ) ;
97
115
if !self . fx . rvalue_creates_operand ( rvalue) {
98
- self . mark_as_memory ( index) ;
116
+ self . not_ssa ( index) ;
99
117
}
100
118
} else {
101
119
self . visit_place ( place, PlaceContext :: Store , location) ;
@@ -161,7 +179,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
161
179
if layout. is_llvm_immediate ( ) || layout. is_llvm_scalar_pair ( ) {
162
180
// Recurse with the same context, instead of `Projection`,
163
181
// potentially stopping at non-operand projections,
164
- // which would trigger `mark_as_memory ` on locals.
182
+ // which would trigger `not_ssa ` on locals.
165
183
self . visit_place ( & proj. base , context, location) ;
166
184
return ;
167
185
}
@@ -178,35 +196,50 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
178
196
}
179
197
180
198
fn visit_local ( & mut self ,
181
- & index : & mir:: Local ,
199
+ & local : & mir:: Local ,
182
200
context : PlaceContext < ' tcx > ,
183
- _ : Location ) {
201
+ location : Location ) {
184
202
match context {
185
203
PlaceContext :: Call => {
186
- self . mark_assigned ( index ) ;
204
+ self . assign ( local , location ) ;
187
205
}
188
206
189
207
PlaceContext :: StorageLive |
190
208
PlaceContext :: StorageDead |
191
- PlaceContext :: Validate |
209
+ PlaceContext :: Validate => { }
210
+
192
211
PlaceContext :: Copy |
193
- PlaceContext :: Move => { }
212
+ PlaceContext :: Move => {
213
+ // Reads from uninitialized variables (e.g. in dead code, after
214
+ // optimizations) require locals to be in (uninitialized) memory.
215
+ // NB: there can be uninitialized reads of a local visited after
216
+ // an assignment to that local, if they happen on disjoint paths.
217
+ let ssa_read = match self . first_assignment ( local) {
218
+ Some ( assignment_location) => {
219
+ assignment_location. dominates ( location, & self . dominators )
220
+ }
221
+ None => false
222
+ } ;
223
+ if !ssa_read {
224
+ self . not_ssa ( local) ;
225
+ }
226
+ }
194
227
195
228
PlaceContext :: Inspect |
196
229
PlaceContext :: Store |
197
230
PlaceContext :: AsmOutput |
198
231
PlaceContext :: Borrow { .. } |
199
232
PlaceContext :: Projection ( ..) => {
200
- self . mark_as_memory ( index ) ;
233
+ self . not_ssa ( local ) ;
201
234
}
202
235
203
236
PlaceContext :: Drop => {
204
- let ty = mir:: Place :: Local ( index ) . ty ( self . fx . mir , self . fx . cx . tcx ) ;
237
+ let ty = mir:: Place :: Local ( local ) . ty ( self . fx . mir , self . fx . cx . tcx ) ;
205
238
let ty = self . fx . monomorphize ( & ty. to_ty ( self . fx . cx . tcx ) ) ;
206
239
207
240
// Only need the place if we're actually dropping it.
208
241
if self . fx . cx . type_needs_drop ( ty) {
209
- self . mark_as_memory ( index ) ;
242
+ self . not_ssa ( local ) ;
210
243
}
211
244
}
212
245
}
0 commit comments