rustc_mir_dataflow/impls/
liveness.rs1use rustc_index::bit_set::DenseBitSet;
2use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
3use rustc_middle::mir::{self, CallReturnPlaces, Local, Location, Place, StatementKind};
4
5use crate::{Analysis, Backward, GenKill};
6
7pub struct MaybeLiveLocals;
25
26impl<'tcx> Analysis<'tcx> for MaybeLiveLocals {
27 type Domain = DenseBitSet<Local>;
28 type Direction = Backward;
29
30 const NAME: &'static str = "liveness";
31
32 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
33 DenseBitSet::new_empty(body.local_decls.len())
35 }
36
37 fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
38 }
40
41 fn apply_primary_statement_effect(
42 &self,
43 state: &mut Self::Domain,
44 statement: &mir::Statement<'tcx>,
45 location: Location,
46 ) {
47 LivenessTransferFunction(state).visit_statement(statement, location);
48 }
49
50 fn apply_primary_terminator_effect(
51 &self,
52 state: &mut Self::Domain,
53 terminator: &mir::Terminator<'tcx>,
54 location: Location,
55 ) {
56 LivenessTransferFunction(state).visit_terminator(terminator, location);
57 }
58
59 fn apply_call_return_effect(
60 &self,
61 state: &mut Self::Domain,
62 _block: mir::BasicBlock,
63 return_places: CallReturnPlaces<'_, 'tcx>,
64 ) {
65 if let CallReturnPlaces::Yield(resume_place) = return_places {
66 YieldResumeEffect(state).visit_place(
67 &resume_place,
68 PlaceContext::MutatingUse(MutatingUseContext::Yield),
69 Location::START,
70 )
71 } else {
72 return_places.for_each(|place| {
73 if let Some(local) = place.as_local() {
74 state.kill(local);
75 }
76 });
77 }
78 }
79}
80
81pub struct LivenessTransferFunction<'a, I>(pub &'a mut I);
82
83impl<'tcx, I> Visitor<'tcx> for LivenessTransferFunction<'_, I>
84where
85 I: GenKill<Local>,
86{
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 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 DefUse::Use => self.0.gen_(place.local),
109 DefUse::PartialWrite | DefUse::NonUse => {}
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
133pub enum DefUse {
134 Def,
136 Use,
138 PartialWrite,
140 NonUse,
142}
143
144impl DefUse {
145 fn apply(state: &mut impl GenKill<Local>, place: Place<'_>, context: PlaceContext) {
146 match DefUse::for_place(place, context) {
147 DefUse::Def => state.kill(place.local),
148 DefUse::Use => state.gen_(place.local),
149 DefUse::PartialWrite | DefUse::NonUse => {}
150 }
151 }
152
153 pub fn for_place(place: Place<'_>, context: PlaceContext) -> DefUse {
154 match context {
155 PlaceContext::NonUse(_) => DefUse::NonUse,
156
157 PlaceContext::MutatingUse(
158 MutatingUseContext::Call
159 | MutatingUseContext::Yield
160 | MutatingUseContext::AsmOutput
161 | MutatingUseContext::Store,
162 ) => {
163 if place.is_indirect() {
165 DefUse::Use
166 } else if place.projection.is_empty() {
167 DefUse::Def
168 } else {
169 DefUse::PartialWrite
170 }
171 }
172
173 PlaceContext::MutatingUse(MutatingUseContext::SetDiscriminant) => {
176 if place.is_indirect() { DefUse::Use } else { DefUse::PartialWrite }
177 }
178
179 PlaceContext::MutatingUse(
181 MutatingUseContext::RawBorrow
182 | MutatingUseContext::Borrow
183 | MutatingUseContext::Drop
184 | MutatingUseContext::Retag,
185 )
186 | PlaceContext::NonMutatingUse(
187 NonMutatingUseContext::RawBorrow
188 | NonMutatingUseContext::Copy
189 | NonMutatingUseContext::Inspect
190 | NonMutatingUseContext::Move
191 | NonMutatingUseContext::PlaceMention
192 | NonMutatingUseContext::FakeBorrow
193 | NonMutatingUseContext::SharedBorrow,
194 ) => DefUse::Use,
195
196 PlaceContext::MutatingUse(MutatingUseContext::Projection)
197 | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
198 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("A projection could be a def or a use and must be handled separately")));
}unreachable!("A projection could be a def or a use and must be handled separately")
199 }
200 }
201 }
202}
203
204pub struct MaybeTransitiveLiveLocals<'a> {
211 always_live: &'a DenseBitSet<Local>,
212 debuginfo_locals: &'a DenseBitSet<Local>,
213}
214
215impl<'a> MaybeTransitiveLiveLocals<'a> {
216 pub fn new(
221 always_live: &'a DenseBitSet<Local>,
222 debuginfo_locals: &'a DenseBitSet<Local>,
223 ) -> Self {
224 MaybeTransitiveLiveLocals { always_live, debuginfo_locals }
225 }
226
227 pub fn can_be_removed_if_dead<'tcx>(
228 stmt_kind: &StatementKind<'tcx>,
229 always_live: &DenseBitSet<Local>,
230 debuginfo_locals: &DenseBitSet<Local>,
231 ) -> Option<Place<'tcx>> {
232 let destination = match stmt_kind {
234 StatementKind::Assign((place, rvalue)) => (rvalue.is_safe_to_remove()
235 && (!debuginfo_locals.contains(place.local)
238 || (place.as_local().is_some() && stmt_kind.as_debuginfo().is_some())))
239 .then_some(*place),
240 StatementKind::SetDiscriminant { place, .. } => {
241 (!debuginfo_locals.contains(place.local)).then_some(**place)
242 }
243 StatementKind::FakeRead(_)
244 | StatementKind::StorageLive(_)
245 | StatementKind::StorageDead(_)
246 | StatementKind::AscribeUserType(..)
247 | StatementKind::PlaceMention(..)
248 | StatementKind::Coverage(..)
249 | StatementKind::Intrinsic(..)
250 | StatementKind::ConstEvalCounter
251 | StatementKind::BackwardIncompatibleDropHint { .. }
252 | StatementKind::Nop => None,
253 };
254 if let Some(destination) = destination
255 && !destination.is_indirect()
256 && !always_live.contains(destination.local)
257 {
258 return Some(destination);
259 }
260 None
261 }
262}
263
264impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
265 type Domain = DenseBitSet<Local>;
266 type Direction = Backward;
267
268 const NAME: &'static str = "transitive liveness";
269
270 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
271 MaybeLiveLocals.bottom_value(body)
272 }
273
274 fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
275 MaybeLiveLocals.initialize_start_block(body, state)
276 }
277
278 fn apply_primary_statement_effect(
279 &self,
280 state: &mut Self::Domain,
281 statement: &mir::Statement<'tcx>,
282 location: Location,
283 ) {
284 if let Some(destination) =
286 Self::can_be_removed_if_dead(&statement.kind, self.always_live, self.debuginfo_locals)
287 && !state.contains(destination.local)
288 {
289 return;
291 }
292
293 MaybeLiveLocals.apply_primary_statement_effect(state, statement, location);
294 }
295
296 fn apply_primary_terminator_effect(
297 &self,
298 state: &mut Self::Domain,
299 terminator: &mir::Terminator<'tcx>,
300 location: Location,
301 ) {
302 MaybeLiveLocals.apply_primary_terminator_effect(state, terminator, location)
303 }
304
305 fn apply_call_return_effect(
306 &self,
307 state: &mut Self::Domain,
308 block: mir::BasicBlock,
309 return_places: CallReturnPlaces<'_, 'tcx>,
310 ) {
311 MaybeLiveLocals.apply_call_return_effect(state, block, return_places);
312 }
313}