rustc_mir_dataflow/impls/
liveness.rs
1use rustc_index::bit_set::DenseBitSet;
2use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
3use rustc_middle::mir::{
4 self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges,
5};
6
7use crate::{Analysis, Backward, GenKill};
8
9pub struct MaybeLiveLocals;
27
28impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
29 type Domain = DenseBitSet<Local>;
30 type Direction = Backward;
31
32 const NAME: &'static str = "liveness";
33
34 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
35 DenseBitSet::new_empty(body.local_decls.len())
37 }
38
39 fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
40 }
42
43 fn apply_primary_statement_effect(
44 &mut self,
45 state: &mut Self::Domain,
46 statement: &mir::Statement<'tcx>,
47 location: Location,
48 ) {
49 TransferFunction(state).visit_statement(statement, location);
50 }
51
52 fn apply_primary_terminator_effect<'mir>(
53 &mut self,
54 state: &mut Self::Domain,
55 terminator: &'mir mir::Terminator<'tcx>,
56 location: Location,
57 ) -> TerminatorEdges<'mir, 'tcx> {
58 TransferFunction(state).visit_terminator(terminator, location);
59 terminator.edges()
60 }
61
62 fn apply_call_return_effect(
63 &mut self,
64 state: &mut Self::Domain,
65 _block: mir::BasicBlock,
66 return_places: CallReturnPlaces<'_, 'tcx>,
67 ) {
68 if let CallReturnPlaces::Yield(resume_place) = return_places {
69 YieldResumeEffect(state).visit_place(
70 &resume_place,
71 PlaceContext::MutatingUse(MutatingUseContext::Yield),
72 Location::START,
73 )
74 } else {
75 return_places.for_each(|place| {
76 if let Some(local) = place.as_local() {
77 state.kill(local);
78 }
79 });
80 }
81 }
82}
83
84pub struct TransferFunction<'a>(pub &'a mut DenseBitSet<Local>);
85
86impl<'tcx> Visitor<'tcx> for TransferFunction<'_> {
87 fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
88 if let PlaceContext::MutatingUse(MutatingUseContext::Yield) = context {
89 return;
92 }
93
94 match DefUse::for_place(*place, context) {
95 Some(DefUse::Def) => {
96 if let PlaceContext::MutatingUse(
97 MutatingUseContext::Call | MutatingUseContext::AsmOutput,
98 ) = context
99 {
100 } else {
105 self.0.kill(place.local);
106 }
107 }
108 Some(DefUse::Use) => self.0.gen_(place.local),
109 None => {}
110 }
111
112 self.visit_projection(place.as_ref(), context, location);
113 }
114
115 fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
116 DefUse::apply(self.0, local.into(), context);
117 }
118}
119
120struct YieldResumeEffect<'a>(&'a mut DenseBitSet<Local>);
121
122impl<'tcx> Visitor<'tcx> for YieldResumeEffect<'_> {
123 fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
124 DefUse::apply(self.0, *place, context);
125 self.visit_projection(place.as_ref(), context, location);
126 }
127
128 fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
129 DefUse::apply(self.0, local.into(), context);
130 }
131}
132
133#[derive(Eq, PartialEq, Clone)]
134enum DefUse {
135 Def,
136 Use,
137}
138
139impl DefUse {
140 fn apply(state: &mut DenseBitSet<Local>, place: Place<'_>, context: PlaceContext) {
141 match DefUse::for_place(place, context) {
142 Some(DefUse::Def) => state.kill(place.local),
143 Some(DefUse::Use) => state.gen_(place.local),
144 None => {}
145 }
146 }
147
148 fn for_place(place: Place<'_>, context: PlaceContext) -> Option<DefUse> {
149 match context {
150 PlaceContext::NonUse(_) => None,
151
152 PlaceContext::MutatingUse(
153 MutatingUseContext::Call
154 | MutatingUseContext::Yield
155 | MutatingUseContext::AsmOutput
156 | MutatingUseContext::Store
157 | MutatingUseContext::Deinit,
158 ) => {
159 if place.is_indirect() {
160 Some(DefUse::Use)
163 } else if place.projection.is_empty() {
164 Some(DefUse::Def)
165 } else {
166 None
167 }
168 }
169
170 PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => {
173 place.is_indirect().then_some(DefUse::Use)
174 }
175
176 PlaceContext::MutatingUse(
178 MutatingUseContext::RawBorrow
179 | MutatingUseContext::Borrow
180 | MutatingUseContext::Drop
181 | MutatingUseContext::Retag,
182 )
183 | PlaceContext::NonMutatingUse(
184 NonMutatingUseContext::RawBorrow
185 | NonMutatingUseContext::Copy
186 | NonMutatingUseContext::Inspect
187 | NonMutatingUseContext::Move
188 | NonMutatingUseContext::PlaceMention
189 | NonMutatingUseContext::FakeBorrow
190 | NonMutatingUseContext::SharedBorrow,
191 ) => Some(DefUse::Use),
192
193 PlaceContext::MutatingUse(MutatingUseContext::Projection)
194 | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
195 unreachable!("A projection could be a def or a use and must be handled separately")
196 }
197 }
198 }
199}
200
201pub struct MaybeTransitiveLiveLocals<'a> {
207 always_live: &'a DenseBitSet<Local>,
208}
209
210impl<'a> MaybeTransitiveLiveLocals<'a> {
211 pub fn new(always_live: &'a DenseBitSet<Local>) -> Self {
216 MaybeTransitiveLiveLocals { always_live }
217 }
218}
219
220impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
221 type Domain = DenseBitSet<Local>;
222 type Direction = Backward;
223
224 const NAME: &'static str = "transitive liveness";
225
226 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
227 DenseBitSet::new_empty(body.local_decls.len())
229 }
230
231 fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
232 }
234
235 fn apply_primary_statement_effect(
236 &mut self,
237 state: &mut Self::Domain,
238 statement: &mir::Statement<'tcx>,
239 location: Location,
240 ) {
241 let destination = match &statement.kind {
243 StatementKind::Assign(assign) => assign.1.is_safe_to_remove().then_some(assign.0),
244 StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
245 Some(**place)
246 }
247 StatementKind::FakeRead(_)
248 | StatementKind::StorageLive(_)
249 | StatementKind::StorageDead(_)
250 | StatementKind::Retag(..)
251 | StatementKind::AscribeUserType(..)
252 | StatementKind::PlaceMention(..)
253 | StatementKind::Coverage(..)
254 | StatementKind::Intrinsic(..)
255 | StatementKind::ConstEvalCounter
256 | StatementKind::BackwardIncompatibleDropHint { .. }
257 | StatementKind::Nop => None,
258 };
259 if let Some(destination) = destination {
260 if !destination.is_indirect()
261 && !state.contains(destination.local)
262 && !self.always_live.contains(destination.local)
263 {
264 return;
266 }
267 }
268 TransferFunction(state).visit_statement(statement, location);
269 }
270
271 fn apply_primary_terminator_effect<'mir>(
272 &mut self,
273 state: &mut Self::Domain,
274 terminator: &'mir mir::Terminator<'tcx>,
275 location: Location,
276 ) -> TerminatorEdges<'mir, 'tcx> {
277 TransferFunction(state).visit_terminator(terminator, location);
278 terminator.edges()
279 }
280
281 fn apply_call_return_effect(
282 &mut self,
283 state: &mut Self::Domain,
284 _block: mir::BasicBlock,
285 return_places: CallReturnPlaces<'_, 'tcx>,
286 ) {
287 if let CallReturnPlaces::Yield(resume_place) = return_places {
288 YieldResumeEffect(state).visit_place(
289 &resume_place,
290 PlaceContext::MutatingUse(MutatingUseContext::Yield),
291 Location::START,
292 )
293 } else {
294 return_places.for_each(|place| {
295 if let Some(local) = place.as_local() {
296 state.remove(local);
297 }
298 });
299 }
300 }
301}