@@ -23,12 +23,6 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi
23
23
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
24
24
pub struct MaybeLiveLocals ;
25
25
26
- impl MaybeLiveLocals {
27
- fn transfer_function < ' a , T > ( & self , trans : & ' a mut T ) -> TransferFunction < ' a , T > {
28
- TransferFunction ( trans)
29
- }
30
- }
31
-
32
26
impl < ' tcx > AnalysisDomain < ' tcx > for MaybeLiveLocals {
33
27
type Domain = ChunkedBitSet < Local > ;
34
28
type Direction = Backward ;
@@ -54,7 +48,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
54
48
statement : & mir:: Statement < ' tcx > ,
55
49
location : Location ,
56
50
) {
57
- self . transfer_function ( trans) . visit_statement ( statement, location) ;
51
+ TransferFunction ( trans) . visit_statement ( statement, location) ;
58
52
}
59
53
60
54
fn terminator_effect (
@@ -63,7 +57,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
63
57
terminator : & mir:: Terminator < ' tcx > ,
64
58
location : Location ,
65
59
) {
66
- self . transfer_function ( trans) . visit_terminator ( terminator, location) ;
60
+ TransferFunction ( trans) . visit_terminator ( terminator, location) ;
67
61
}
68
62
69
63
fn call_return_effect (
@@ -85,9 +79,11 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
85
79
_resume_block : mir:: BasicBlock ,
86
80
resume_place : mir:: Place < ' tcx > ,
87
81
) {
88
- if let Some ( local) = resume_place. as_local ( ) {
89
- trans. kill ( local) ;
90
- }
82
+ YieldResumeEffect ( trans) . visit_place (
83
+ & resume_place,
84
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Yield ) ,
85
+ Location :: START ,
86
+ )
91
87
}
92
88
}
93
89
@@ -98,28 +94,51 @@ where
98
94
T : GenKill < Local > ,
99
95
{
100
96
fn visit_place ( & mut self , place : & mir:: Place < ' tcx > , context : PlaceContext , location : Location ) {
101
- let local = place . local ;
102
-
103
- // We purposefully do not call `super_place` here to avoid calling `visit_local` for this
104
- // place with one of the `Projection` variants of `PlaceContext`.
105
- self . visit_projection ( place . as_ref ( ) , context , location ) ;
97
+ if let PlaceContext :: MutatingUse ( MutatingUseContext :: Yield ) = context {
98
+ // The resume place is evaluated and assigned to only after generator resumes, so its
99
+ // effect is handled separately in `yield_resume_effect`.
100
+ return ;
101
+ }
106
102
107
103
match DefUse :: for_place ( * place, context) {
108
- Some ( DefUse :: Def ) => self . 0 . kill ( local) ,
109
- Some ( DefUse :: Use ) => self . 0 . gen ( local) ,
104
+ Some ( DefUse :: Def ) => {
105
+ if let PlaceContext :: MutatingUse (
106
+ MutatingUseContext :: Call | MutatingUseContext :: AsmOutput ,
107
+ ) = context
108
+ {
109
+ // For the associated terminators, this is only a `Def` when the terminator returns
110
+ // "successfully." As such, we handle this case separately in `call_return_effect`
111
+ // above. However, if the place looks like `*_5`, this is still unconditionally a use of
112
+ // `_5`.
113
+ } else {
114
+ self . 0 . kill ( place. local ) ;
115
+ }
116
+ }
117
+ Some ( DefUse :: Use ) => self . 0 . gen ( place. local ) ,
110
118
None => { }
111
119
}
120
+
121
+ self . visit_projection ( place. as_ref ( ) , context, location) ;
112
122
}
113
123
114
124
fn visit_local ( & mut self , local : Local , context : PlaceContext , _: Location ) {
115
- // Because we do not call `super_place` above, `visit_local` is only called for locals that
116
- // do not appear as part of a `Place` in the MIR. This handles cases like the implicit use
117
- // of the return place in a `Return` terminator or the index in an `Index` projection.
118
- match DefUse :: for_place ( local. into ( ) , context) {
119
- Some ( DefUse :: Def ) => self . 0 . kill ( local) ,
120
- Some ( DefUse :: Use ) => self . 0 . gen ( local) ,
121
- None => { }
122
- }
125
+ DefUse :: apply ( self . 0 , local. into ( ) , context) ;
126
+ }
127
+ }
128
+
129
+ struct YieldResumeEffect < ' a , T > ( & ' a mut T ) ;
130
+
131
+ impl < ' tcx , T > Visitor < ' tcx > for YieldResumeEffect < ' _ , T >
132
+ where
133
+ T : GenKill < Local > ,
134
+ {
135
+ fn visit_place ( & mut self , place : & mir:: Place < ' tcx > , context : PlaceContext , location : Location ) {
136
+ DefUse :: apply ( self . 0 , * place, context) ;
137
+ self . visit_projection ( place. as_ref ( ) , context, location) ;
138
+ }
139
+
140
+ fn visit_local ( & mut self , local : Local , context : PlaceContext , _: Location ) {
141
+ DefUse :: apply ( self . 0 , local. into ( ) , context) ;
123
142
}
124
143
}
125
144
@@ -130,11 +149,25 @@ enum DefUse {
130
149
}
131
150
132
151
impl DefUse {
152
+ fn apply < ' tcx > ( trans : & mut impl GenKill < Local > , place : Place < ' tcx > , context : PlaceContext ) {
153
+ match DefUse :: for_place ( place, context) {
154
+ Some ( DefUse :: Def ) => trans. kill ( place. local ) ,
155
+ Some ( DefUse :: Use ) => trans. gen ( place. local ) ,
156
+ None => { }
157
+ }
158
+ }
159
+
133
160
fn for_place < ' tcx > ( place : Place < ' tcx > , context : PlaceContext ) -> Option < DefUse > {
134
161
match context {
135
162
PlaceContext :: NonUse ( _) => None ,
136
163
137
- PlaceContext :: MutatingUse ( MutatingUseContext :: Store | MutatingUseContext :: Deinit ) => {
164
+ PlaceContext :: MutatingUse (
165
+ MutatingUseContext :: Call
166
+ | MutatingUseContext :: Yield
167
+ | MutatingUseContext :: AsmOutput
168
+ | MutatingUseContext :: Store
169
+ | MutatingUseContext :: Deinit ,
170
+ ) => {
138
171
if place. is_indirect ( ) {
139
172
// Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a
140
173
// use.
@@ -152,16 +185,6 @@ impl DefUse {
152
185
place. is_indirect ( ) . then_some ( DefUse :: Use )
153
186
}
154
187
155
- // For the associated terminators, this is only a `Def` when the terminator returns
156
- // "successfully." As such, we handle this case separately in `call_return_effect`
157
- // above. However, if the place looks like `*_5`, this is still unconditionally a use of
158
- // `_5`.
159
- PlaceContext :: MutatingUse (
160
- MutatingUseContext :: Call
161
- | MutatingUseContext :: Yield
162
- | MutatingUseContext :: AsmOutput ,
163
- ) => place. is_indirect ( ) . then_some ( DefUse :: Use ) ,
164
-
165
188
// All other contexts are uses...
166
189
PlaceContext :: MutatingUse (
167
190
MutatingUseContext :: AddressOf
@@ -290,8 +313,10 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
290
313
_resume_block : mir:: BasicBlock ,
291
314
resume_place : mir:: Place < ' tcx > ,
292
315
) {
293
- if let Some ( local) = resume_place. as_local ( ) {
294
- trans. remove ( local) ;
295
- }
316
+ YieldResumeEffect ( trans) . visit_place (
317
+ & resume_place,
318
+ PlaceContext :: MutatingUse ( MutatingUseContext :: Yield ) ,
319
+ Location :: START ,
320
+ )
296
321
}
297
322
}
0 commit comments