1//! Computes the points where each local must have a distinct allocation.
2//!
3//! The result is a [`SparseIntervalMatrix`] with one row per local. Two locals
4//! may share the same address only if their rows are disjoint. To model MIR
5//! statements where a source operand and destination place may share an
6//! address, each statement and terminator is split into an early point, where
7//! operands are read, and a late point, where destinations are written.
8//!
9//! A local live range starts at the late point of any statement or terminator
10//! that writes to it without a `Deref` projection. It ends at `StorageLive` or
11//! `StorageDead`, or at the early point of a whole-local move operand or the
12//! last use of that local on a control-flow path (only for locals whose address
13//! is never observed). This happens at `StorageLive` because it resets a local
14//! to an uninitialized state.
15//!
16//! `Call` terminators are handled specially: move operands are kept live
17//! through the late point of the terminator so they conflict with each other
18//! and with the destination place. This matches the runtime behavior where the
19//! place is donated to the callee for the duration of the call.
20//!
21//! The analysis consists of three passes:
22//!
23//! 1. A backward value-liveness analysis which identifies the last point at
24//! which each local is directly used in each control flow path. This is used
25//! to shorten the lifetime of unborrowed locals to their last use.
26//! 2. A forward allocation-liveness analysis which uses that information
27//! together with writes, moves, and storage markers to compute which
28//! allocations may be live on entry to each block.
29//! 3. A final walk using those entry states which constructs the matrix and
30//! distinguishes whether each effect happens at the early or late point of a
31//! statement or terminator.
3233use rustc_index::IndexVec;
34use rustc_index::bit_set::DenseBitSet;
35use rustc_index::interval::SparseIntervalMatrix;
36use rustc_middle::mir::visit::{
37MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, VisitPlacesWith,
38Visitor,
39};
40use rustc_middle::mir::{self, BasicBlock, Local, Location, MirDumper, PassWhere, Place};
41use rustc_middle::ty::TyCtxt;
42use tracing::trace;
4344use crate::impls::{DefUse, MaybeLiveLocals, borrowed_locals};
45use crate::points::{DenseLocationMap, PointIndex};
46use crate::{Analysis, GenKill, ResultsVisitor, visit_results};
4748/// Visits locals that are implicitly allocated by a statement or terminator as
49/// part of destination place evaluation.
50pub struct VisitAllocatedLocalsWith<F>(pub F);
5152impl<'tcx, F> Visitor<'tcx> for VisitAllocatedLocalsWith<F>
53where
54F: FnMut(Local, PlaceContext),
55{
56fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _: Location) {
57match DefUse::for_place(*place, context) {
58 DefUse::Def | DefUse::PartialWrite => (self.0)(place.local, context),
59 DefUse::Use | DefUse::NonUse => {}
60 }
61 }
62}
6364/// Visits locals that are implicitly freed by being evaluated as a bare-local
65/// move operand or through a storage statement.
66pub struct VisitDeallocatedLocalsWith<F>(pub F);
6768impl<'tcx, F> Visitor<'tcx> for VisitDeallocatedLocalsWith<F>
69where
70F: FnMut(Local, PlaceContext),
71{
72fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, location: Location) {
73match statement.kind {
74// StorageLive resets a local to the live-but-unallocated state,
75 // which also has the effect of freeing its allocation.
76mir::StatementKind::StorageLive(local) => {
77 (self.0)(local, PlaceContext::NonUse(NonUseContext::StorageLive));
78 }
79 mir::StatementKind::StorageDead(local) => {
80 (self.0)(local, PlaceContext::NonUse(NonUseContext::StorageDead));
81 }
82_ => self.super_statement(statement, location),
83 }
84 }
8586fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, _: Location) {
87if let mir::Operand::Move(place) = operand88 && let Some(local) = place.as_local()
89 {
90 (self.0)(local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move));
91 }
92 }
93}
9495////////////////////////////////////////////////////////////////////////////////
96// Backward dataflow pass
97//
98// This pass computes "kill points" for each local, indicating the location of
99// their last use in a particular control flow branch. These are later used in
100// the forward pass to end the live range of locals that are never borrowed at
101// their last direct use.
102//
103// This pass ignores borrowed locals: we can't determine their last use from
104// syntactic uses of the base local, so we conservatively treat them as always
105// live in the backwards pass and don't emit kill points for them. The main
106// forward pass still kills at `StorageDead` and whole-local moves, which is
107// when their allocation is freed.
108//
109// This pass has 2 outputs: a set of kill points that mark the last use
110// locations of locals and a per-block bitset indicating which locals are live
111// on entry to that block. The latter acts as a set of kill points at block
112// entry for all locals not in the bit set.
113114struct KillPoints<'a> {
115 live_on_entry: IndexVec<BasicBlock, DenseBitSet<Local>>,
116 kill_points_map: IndexVec<PointIndex, &'a [(Local, Location)]>,
117}
118119impl<'a> KillPoints<'a> {
120fn compute<'tcx>(
121 tcx: TyCtxt<'tcx>,
122 body: &mir::Body<'tcx>,
123 pass_name: Option<&'static str>,
124 points: &DenseLocationMap,
125 kill_points: &'a mut Vec<(Local, Location)>,
126 ) -> Self {
127let maybe_live_locals = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, pass_name);
128let borrowed_locals = borrowed_locals(body);
129130// Initialize all borrowed locals as live on entry. We never try to kill
131 // those.
132let mut live_on_entry =
133IndexVec::from_elem_n(borrowed_locals.clone(), body.basic_blocks.len());
134135// Collect kill points and live-on-entry states from the results of
136 // MaybeLiveLocals.
137kill_points.clear();
138let mut visitor = KillPointsVisitor {
139kill_points,
140 live_on_entry: &mut live_on_entry,
141 borrowed_locals: &borrowed_locals,
142 };
143visit_results(
144body,
145 mir::traversal::reachable(body).map(|(block, _)| block),
146&maybe_live_locals,
147&mut visitor,
148 );
149{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs:149",
"rustc_mir_dataflow::impls::precise_liveness",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs"),
::tracing_core::__macro_support::Option::Some(149u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::impls::precise_liveness"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("kill_points")
}> =
::tracing::__macro_support::FieldName::new("kill_points");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&kill_points)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!(?kill_points);
150{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs:150",
"rustc_mir_dataflow::impls::precise_liveness",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs"),
::tracing_core::__macro_support::Option::Some(150u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::impls::precise_liveness"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("live_on_entry")
}> =
::tracing::__macro_support::FieldName::new("live_on_entry");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&live_on_entry)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!(?live_on_entry);
151152// Create a mapping of `PointIndex` to the set of killed locals at that
153 // location.
154let mut kill_points_map = IndexVec::from_elem_n(&[][..], points.num_points());
155for chunk in kill_points.chunk_by(|a, b| a.1 == b.1) {
156let point = points.point_from_location(chunk[0].1);
157{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs:157",
"rustc_mir_dataflow::impls::precise_liveness",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_mir_dataflow/src/impls/precise_liveness.rs"),
::tracing_core::__macro_support::Option::Some(157u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_dataflow::impls::precise_liveness"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Kill points at {0:?}: {1:?}",
chunk[0].1, chunk) as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("Kill points at {:?}: {:?}", chunk[0].1, chunk);
158if true {
if !kill_points_map[point].is_empty() {
::core::panicking::panic("assertion failed: kill_points_map[point].is_empty()")
};
};debug_assert!(kill_points_map[point].is_empty());
159 kill_points_map[point] = chunk;
160 }
161162Self { live_on_entry, kill_points_map }
163 }
164}
165166struct KillPointsVisitor<'a> {
167 kill_points: &'a mut Vec<(Local, Location)>,
168 live_on_entry: &'a mut IndexVec<BasicBlock, DenseBitSet<Local>>,
169 borrowed_locals: &'a DenseBitSet<Local>,
170}
171172impl<'tcx> ResultsVisitor<'tcx, MaybeLiveLocals> for KillPointsVisitor<'_> {
173fn visit_block_exit(&mut self, state: &DenseBitSet<Local>, block: BasicBlock) {
174// Borrowed locals are already marked as live when live_on_entry was
175 // initialized. This adds the non-borrowed locals that we have
176 // determined are live on entry to this block.
177self.live_on_entry[block].union(state);
178 }
179180fn visit_after_early_statement_effect(
181&mut self,
182 state: &DenseBitSet<Local>,
183 statement: &mir::Statement<'tcx>,
184 location: Location,
185 ) {
186VisitPlacesWith(|place: Place<'tcx>, ctxt| {
187// Ignore non-uses.
188match ctxt {
189 PlaceContext::NonMutatingUse(_) | PlaceContext::MutatingUse(_) => {}
190 PlaceContext::NonUse(_) => return,
191 }
192193// If a local is used in a statement but is dead after it then this
194 // location is a kill point. Don't emit a kill point for borrowed
195 // locals.
196if !state.contains(place.local) && !self.borrowed_locals.contains(place.local) {
197self.kill_points.push((place.local, location));
198 }
199 })
200 .visit_statement(statement, location);
201 }
202203fn visit_after_early_terminator_effect(
204&mut self,
205 state: &DenseBitSet<Local>,
206 terminator: &mir::Terminator<'tcx>,
207 location: Location,
208 ) {
209VisitPlacesWith(|place: Place<'tcx>, ctxt| {
210// Ignore non-uses (they don't do anything) and edge uses
211 // (implicitly killed though live_on_entry at the start of the
212 // corresponding successor).
213match ctxt {
214 PlaceContext::MutatingUse(
215 MutatingUseContext::AsmOutput216 | MutatingUseContext::Call217 | MutatingUseContext::Yield,
218 )
219 | PlaceContext::NonUse(_) => return,
220 PlaceContext::NonMutatingUse(_) | PlaceContext::MutatingUse(_) => {}
221 }
222223// If a local is used in a terminator but is dead after it then this
224 // location is a kill point. Don't emit a kill point for borrowed
225 // locals.
226if !state.contains(place.local) && !self.borrowed_locals.contains(place.local) {
227self.kill_points.push((place.local, location));
228 }
229 })
230 .visit_terminator(terminator, location);
231 }
232}
233234////////////////////////////////////////////////////////////////////////////////
235// Forward dataflow pass
236//
237// This is a simpler version of the main pass in `liveness_matrix`. It only
238// needs to determine local liveness at block edges and doesn't distinguish
239// between "early" and "late" effects. However it must still preserve the
240// ordering of effects: moved operands are killed before destinations are
241// generated.
242243struct PreciseLiveness<'a> {
244 kill_points: &'a KillPoints<'a>,
245 points: &'a DenseLocationMap,
246}
247248impl PreciseLiveness<'_> {
249fn apply_block_start_effect(&self, state: &mut DenseBitSet<Local>, block: BasicBlock) {
250// Kill any unborrowed locals that are dead at the start of this block.
251 // Notably this kills any dead results produced by a predecessor's
252 // terminator. Borrowed locals are always included in live_on_entry.
253state.intersect(&self.kill_points.live_on_entry[block]);
254 }
255}
256257impl<'tcx> Analysis<'tcx> for PreciseLiveness<'_> {
258type Domain = DenseBitSet<Local>;
259260const NAME: &'static str = "precise_liveness";
261262fn bottom_value(&self, body: &mir::Body<'tcx>) -> DenseBitSet<Local> {
263DenseBitSet::new_empty(body.local_decls.len())
264 }
265266fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut DenseBitSet<Local>) {
267// Function arguments start out as live.
268for arg in body.args_iter() {
269 state.gen_(arg);
270 }
271 }
272273fn apply_primary_statement_effect(
274&self,
275 state: &mut DenseBitSet<Local>,
276 statement: &mir::Statement<'tcx>,
277 location: Location,
278 ) {
279if location.statement_index == 0 {
280self.apply_block_start_effect(state, location.block);
281 }
282283VisitDeallocatedLocalsWith(|local, _| state.kill(local))
284 .visit_statement(statement, location);
285VisitAllocatedLocalsWith(|local, _| state.gen_(local)).visit_statement(statement, location);
286287// Apply kill points at this statement: if a variable is dead then it
288 // doesn't need storage.
289let point = self.points.point_from_location(location);
290for &(local, _) in self.kill_points.kill_points_map[point] {
291 state.kill(local);
292 }
293 }
294295fn apply_primary_terminator_effect(
296&self,
297 state: &mut DenseBitSet<Local>,
298 terminator: &mir::Terminator<'tcx>,
299 location: Location,
300 ) {
301if location.statement_index == 0 {
302self.apply_block_start_effect(state, location.block);
303 }
304305VisitDeallocatedLocalsWith(|local, _| state.kill(local))
306 .visit_terminator(terminator, location);
307308// Gen destination places.
309VisitAllocatedLocalsWith(|local, ctxt| {
310// The resume place of a Yield is only evaluated after the coroutine
311 // is resumed. This is in contrast to call destinations and inline
312 // asm outputs which are evaluated before the call and therefore
313 // remain allocated on both normal and unwind edges.
314if ctxt != PlaceContext::MutatingUse(MutatingUseContext::Yield) {
315state.gen_(local);
316 }
317 })
318 .visit_terminator(terminator, location);
319 }
320321fn apply_call_return_effect(
322&self,
323 state: &mut DenseBitSet<Local>,
324 _block: BasicBlock,
325 return_places: mir::CallReturnPlaces<'_, 'tcx>,
326 ) {
327if let mir::CallReturnPlaces::Yield(place) = return_places328 && !place.is_indirect()
329 {
330state.gen_(place.local);
331 }
332 }
333}
334335////////////////////////////////////////////////////////////////////////////////
336// Matrix construction
337338/// Different "phases" of a single MIR statement, used to describe how
339/// overlapping operands are handled.
340///
341/// As a general rule, source operands are read in the `Early` phase and
342/// destination places are written in the `Late` phase.
343#[derive(#[automatically_derived]
impl ::core::marker::Copy for SplitPointEffect { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for SplitPointEffect { }
#[automatically_derived]
impl ::core::clone::Clone for SplitPointEffect {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for SplitPointEffect {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f,
match self {
SplitPointEffect::Early => "Early",
SplitPointEffect::Late => "Late",
})
}
}Debug, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for SplitPointEffect { }
#[automatically_derived]
impl ::core::cmp::PartialEq for SplitPointEffect {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SplitPointEffect { }Eq)]
344pub enum SplitPointEffect {
345 Early = 0,
346 Late = 1,
347}
348349#[automatically_derived]
impl ::core::marker::Copy for SplitPointIndex { }
impl SplitPointIndex {
#[doc = r" Maximum value the index can take, as a `u32`."]
pub const MAX_AS_U32: u32 = 0xFFFF_FF00;
#[doc = r" Maximum value the index can take."]
pub const MAX: Self = Self::from_u32(0xFFFF_FF00);
#[doc = r" Zero value of the index."]
pub const ZERO: Self = Self::from_u32(0);
#[doc = r" Creates a new index from a given `usize`."]
#[doc = r""]
#[doc = r" # Panics"]
#[doc = r""]
#[doc = r" Will panic if `value` exceeds `MAX`."]
#[inline]
pub const fn from_usize(value: usize) -> Self {
if !(value <= (0xFFFF_FF00 as usize)) {
::core::panicking::panic("assertion failed: value <= (0xFFFF_FF00 as usize)")
};
unsafe { Self::from_u32_unchecked(value as u32) }
}
#[doc = r" Creates a new index from a given `u32`."]
#[doc = r""]
#[doc = r" # Panics"]
#[doc = r""]
#[doc = r" Will panic if `value` exceeds `MAX`."]
#[inline]
pub const fn from_u32(value: u32) -> Self {
if !(value <= 0xFFFF_FF00) {
::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
};
unsafe { Self::from_u32_unchecked(value) }
}
#[doc = r" Creates a new index from a given `u16`."]
#[doc = r""]
#[doc = r" # Panics"]
#[doc = r""]
#[doc = r" Will panic if `value` exceeds `MAX`."]
#[inline]
pub const fn from_u16(value: u16) -> Self {
let value = value as u32;
if !(value <= 0xFFFF_FF00) {
::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
};
unsafe { Self::from_u32_unchecked(value) }
}
#[doc = r" Creates a new index from a given `u32`."]
#[doc = r""]
#[doc = r" # Safety"]
#[doc = r""]
#[doc =
r" The provided value must be less than or equal to the maximum value for the newtype."]
#[doc =
r" Providing a value outside this range is undefined due to layout restrictions."]
#[doc = r""]
#[doc = r" Prefer using `from_u32`."]
#[inline]
pub const unsafe fn from_u32_unchecked(value: u32) -> Self {
Self {
private_use_as_methods_instead: unsafe {
std::mem::transmute(value)
},
}
}
#[doc = r" Extracts the value of this index as a `usize`."]
#[inline]
pub const fn index(self) -> usize { self.as_usize() }
#[doc = r" Extracts the value of this index as a `u32`."]
#[inline]
pub const fn as_u32(self) -> u32 {
unsafe { std::mem::transmute(self.private_use_as_methods_instead) }
}
#[doc = r" Extracts the value of this index as a `usize`."]
#[inline]
pub const fn as_usize(self) -> usize { self.as_u32() as usize }
}
impl std::ops::Add<usize> for SplitPointIndex {
type Output = Self;
#[inline]
fn add(self, other: usize) -> Self {
Self::from_usize(self.index() + other)
}
}
impl std::ops::AddAssign<usize> for SplitPointIndex {
#[inline]
fn add_assign(&mut self, other: usize) { *self = *self + other; }
}
impl rustc_index::Idx for SplitPointIndex {
#[inline]
fn new(value: usize) -> Self { Self::from_usize(value) }
#[inline]
fn index(self) -> usize { self.as_usize() }
}
impl ::std::iter::Step for SplitPointIndex {
#[inline]
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
<usize as
::std::iter::Step>::steps_between(&Self::index(*start),
&Self::index(*end))
}
#[inline]
fn forward_checked(start: Self, u: usize) -> Option<Self> {
Self::index(start).checked_add(u).map(Self::from_usize)
}
#[inline]
fn backward_checked(start: Self, u: usize) -> Option<Self> {
Self::index(start).checked_sub(u).map(Self::from_usize)
}
#[inline]
fn forward_overflowing(start: Self, u: usize) -> (Self, bool) {
let (s, o) = Self::index(start).overflowing_add(u);
(Self::from_usize(s), o)
}
#[inline]
fn backward_overflowing(start: Self, u: usize) -> (Self, bool) {
let (s, o) = Self::index(start).overflowing_sub(u);
(Self::from_usize(s), o)
}
}
impl ::std::cmp::Ord for SplitPointIndex {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_u32().cmp(&other.as_u32())
}
}
impl ::std::cmp::PartialOrd for SplitPointIndex {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<SplitPointIndex> for u32 {
#[inline]
fn from(v: SplitPointIndex) -> u32 { v.as_u32() }
}
impl From<SplitPointIndex> for usize {
#[inline]
fn from(v: SplitPointIndex) -> usize { v.as_usize() }
}
impl From<usize> for SplitPointIndex {
#[inline]
fn from(value: usize) -> Self { Self::from_usize(value) }
}
impl From<u32> for SplitPointIndex {
#[inline]
fn from(value: u32) -> Self { Self::from_u32(value) }
}
impl ::std::cmp::Eq for SplitPointIndex {}
impl ::std::cmp::PartialEq for SplitPointIndex {
fn eq(&self, other: &Self) -> bool { self.as_u32().eq(&other.as_u32()) }
}
impl ::std::marker::StructuralPartialEq for SplitPointIndex {}
impl ::std::hash::Hash for SplitPointIndex {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
self.as_u32().hash(state)
}
}
impl ::std::fmt::Debug for SplitPointIndex {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("SplitPointIndex({0})", self.as_u32()))
}
}rustc_index::newtype_index! {
350/// A `PointIndex` with the lower bit encoding early/late inside a
351 /// statement.
352 ///
353 /// This is used to model overlap constraints within a MIR statement: if a
354 /// source/destination are allowed to overlap then the source is read in
355 /// `SplitPointEffect::Early` and the write is done in
356 /// `SplitPointEffect::Late`.
357#[orderable]
358 #[debug_format = "SplitPointIndex({})"]
359pub struct SplitPointIndex {}
360}361362impl SplitPointIndex {
363pub fn new(point: PointIndex, effect: SplitPointEffect) -> SplitPointIndex {
364let index = (point.as_u32() << 1) | (effectas u32);
365SplitPointIndex::from_u32(index)
366 }
367368pub fn point(self) -> PointIndex {
369PointIndex::from_u32(self.as_u32() >> 1)
370 }
371372pub fn effect(self) -> SplitPointEffect {
373match self.as_u32() & 1 {
3740 => SplitPointEffect::Early,
3751 => SplitPointEffect::Late,
376_ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
377 }
378 }
379}
380381/// Helper type to construct a `SparseIntervalMatrix`.
382struct MatrixBuilder {
383 matrix: SparseIntervalMatrix<Local, SplitPointIndex>,
384 range_start: IndexVec<Local, Option<SplitPointIndex>>,
385386// Track locals that have been live at any point in a block so that at the
387 // end of a block we don't need to iterate over all locals. This
388 // significantly speeds up matrix building.
389maybe_live_locals: Vec<Local>,
390}
391392impl MatrixBuilder {
393fn gen_(&mut self, local: Local, point: PointIndex, effect: SplitPointEffect) {
394let split_point = SplitPointIndex::new(point, effect);
395396// No-op if the local is already live.
397if self.range_start[local].is_none() {
398self.range_start[local] = Some(split_point);
399self.maybe_live_locals.push(local);
400 }
401 }
402403fn kill(&mut self, local: Local, point: PointIndex, effect: SplitPointEffect) {
404let end = SplitPointIndex::new(point, effect);
405406// No-op if the local is already dead.
407if let Some(start) = self.range_start[local].take() {
408if true {
if !(end >= start) {
::core::panicking::panic("assertion failed: end >= start")
};
};debug_assert!(end >= start);
409self.matrix.append_range(local, start..=end);
410 }
411 }
412413fn kill_all(&mut self, point: PointIndex, effect: SplitPointEffect) {
414while let Some(local) = self.maybe_live_locals.pop() {
415self.kill(local, point, effect);
416 }
417 }
418419fn kill_all_except(&mut self, except: Local, point: PointIndex, effect: SplitPointEffect) {
420while let Some(local) = self.maybe_live_locals.pop() {
421if local != except {
422self.kill(local, point, effect);
423 }
424 }
425self.maybe_live_locals.push(except);
426 }
427}
428429pub fn liveness_matrix<'tcx>(
430 tcx: TyCtxt<'tcx>,
431 body: &mir::Body<'tcx>,
432 points: &DenseLocationMap,
433 pass_name: Option<&'static str>,
434) -> SparseIntervalMatrix<Local, SplitPointIndex> {
435let mut kill_points_vec = ::alloc::vec::Vec::new()vec![];
436let kill_points = KillPoints::compute(tcx, body, pass_name, points, &mut kill_points_vec);
437let mut results = PreciseLiveness { kill_points: &kill_points, points }
438 .iterate_to_fixpoint(tcx, body, pass_name);
439440let mut builder = MatrixBuilder {
441 matrix: SparseIntervalMatrix::new(points.num_points() * 2),
442 range_start: IndexVec::from_elem_n(None, body.local_decls.len()),
443 maybe_live_locals: Vec::new(),
444 };
445for (block, block_data) in body.basic_blocks.iter_enumerated() {
446// We can mutate the state in-place since we're not using it any more
447 // after this point.
448let state = &mut results.entry_states[block];
449450// Notably this kills any dead results produced by a predecessor's
451 // terminator.
452state.intersect(&kill_points.live_on_entry[block]);
453454// Gen any locals that are live at the start of the block. If this block
455 // only consists of a return terminator then only gen the return place
456 // instead. This ensures that StorageDead for all other locals are
457 // inserted before the return terminator.
458let terminator = block_data.terminator();
459if let mir::TerminatorKind::Return = terminator.kind
460 && block_data.statements.is_empty()
461 {
462if state.contains(mir::RETURN_PLACE) {
463 builder.gen_(mir::RETURN_PLACE, points.entry_point(block), SplitPointEffect::Early);
464 }
465 } else {
466for local in state.iter() {
467 builder.gen_(local, points.entry_point(block), SplitPointEffect::Early);
468 }
469 }
470471for (statement_index, statement) in block_data.statements.iter().enumerate() {
472let location = Location { block, statement_index };
473let point = points.point_from_location(location);
474475// Kill moved locals at the early point.
476VisitDeallocatedLocalsWith(|local, _| {
477 builder.kill(local, point, SplitPointEffect::Early);
478 })
479 .visit_statement(statement, location);
480481// Kill any locals which are no longer used after this statement.
482for &(local, _) in kill_points.kill_points_map[point] {
483 builder.kill(local, point, SplitPointEffect::Early);
484 }
485486// Gen destination places at the late point.
487VisitAllocatedLocalsWith(|local, _| {
488 builder.gen_(local, point, SplitPointEffect::Late);
489 })
490 .visit_statement(statement, location);
491492// Kill any dead destination places: they will only appear at the
493 // late point of the statement they are generated in, which is
494 // sufficient for determining overlap.
495for &(local, _) in kill_points.kill_points_map[point] {
496 builder.kill(local, point, SplitPointEffect::Late);
497 }
498 }
499500// If this block ends in a return terminator, end all live ranges before
501 // the terminator so that StorageDead statements are inserted before it.
502 //
503 // This is useful after inlining so that the lifetime of locals in the
504 // inlined callee don't extend past the call in the caller.
505if let mir::TerminatorKind::Return = terminator.kind
506 && !block_data.statements.is_empty()
507 {
508// Blocks with only a return terminator are handled above.
509let location = Location { block, statement_index: block_data.statements.len() - 1 };
510let point = points.point_from_location(location);
511 builder.kill_all_except(mir::RETURN_PLACE, point, SplitPointEffect::Late);
512 }
513514let location = Location { block, statement_index: block_data.statements.len() };
515let point = points.point_from_location(location);
516517// Kill moved locals at the early point.
518VisitDeallocatedLocalsWith(|local, _| {
519 builder.kill(local, point, SplitPointEffect::Early);
520 })
521 .visit_terminator(terminator, location);
522523// Kill any locals which are no longer used after this terminator.
524for &(local, _) in kill_points.kill_points_map[point] {
525 builder.kill(local, point, SplitPointEffect::Early);
526 }
527528// Gen destination places at the late point.
529VisitAllocatedLocalsWith(|local, _| {
530 builder.gen_(local, point, SplitPointEffect::Late);
531 })
532 .visit_terminator(terminator, location);
533534// Move arguments to a call are treated specially: the place that they
535 // represent is passed directly to the callee, which means that they are
536 // not allowed to alias any other move operand or the destination place.
537 // This is represented here by extending their live range to the late
538 // part, making it overlap with that of the destination place.
539 //
540 // Notably, this *doesn't* apply to TailCall.
541if let mir::TerminatorKind::Call {
542 func: _,
543 args,
544 destination: _,
545 target: _,
546 unwind: _,
547 call_source: _,
548 fn_span: _,
549 } = &terminator.kind
550 {
551for arg in args {
552if let mir::Operand::Move(place) = arg.node
553 && !place.is_indirect()
554 {
555 builder.gen_(place.local, point, SplitPointEffect::Late);
556 }
557 }
558 }
559560// End the lifetimes of all locals at the end of the block. Successor
561 // blocks (which may not be contiguous in the index space!) will
562 // initialize the lifetimes again from their entry state.
563builder.kill_all(point, SplitPointEffect::Late);
564 }
565566builder.matrix
567}
568569pub fn dump_liveness_matrix<'tcx>(
570 tcx: TyCtxt<'tcx>,
571 body: &mir::Body<'tcx>,
572 pass_name: &'static str,
573 points: &DenseLocationMap,
574 matrix: &SparseIntervalMatrix<Local, SplitPointIndex>,
575) {
576let locals_live_at = |split_point| {
577matrix.rows().filter(|&r| matrix.contains(r, split_point)).collect::<Vec<_>>()
578 };
579580if let Some(dumper) = MirDumper::new(tcx, pass_name, body) {
581let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
582if let PassWhere::BeforeLocation(loc) = pass_where {
583let point = points.point_from_location(loc);
584let split_point = SplitPointIndex::new(point, SplitPointEffect::Early);
585let live = locals_live_at(split_point);
586w.write_fmt(format_args!(" // {0:?}-early => {1:?}\n", loc, live))writeln!(w, " // {loc:?}-early => {live:?}")?;
587let split_point = SplitPointIndex::new(point, SplitPointEffect::Late);
588let live = locals_live_at(split_point);
589w.write_fmt(format_args!(" // {0:?}-late => {1:?}\n", loc, live))writeln!(w, " // {loc:?}-late => {live:?}")?;
590 }
591Ok(())
592 };
593594dumper.set_extra_data(extra_data).dump_mir(body)
595 }
596}