Skip to main content

rustc_mir_dataflow/impls/
precise_liveness.rs

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.
32
33use rustc_index::IndexVec;
34use rustc_index::bit_set::DenseBitSet;
35use rustc_index::interval::SparseIntervalMatrix;
36use rustc_middle::mir::visit::{
37    MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext, VisitPlacesWith,
38    Visitor,
39};
40use rustc_middle::mir::{self, BasicBlock, Local, Location, MirDumper, PassWhere, Place};
41use rustc_middle::ty::TyCtxt;
42use tracing::trace;
43
44use crate::impls::{DefUse, MaybeLiveLocals, borrowed_locals};
45use crate::points::{DenseLocationMap, PointIndex};
46use crate::{Analysis, GenKill, ResultsVisitor, visit_results};
47
48/// Visits locals that are implicitly allocated by a statement or terminator as
49/// part of destination place evaluation.
50pub struct VisitAllocatedLocalsWith<F>(pub F);
51
52impl<'tcx, F> Visitor<'tcx> for VisitAllocatedLocalsWith<F>
53where
54    F: FnMut(Local, PlaceContext),
55{
56    fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _: Location) {
57        match DefUse::for_place(*place, context) {
58            DefUse::Def | DefUse::PartialWrite => (self.0)(place.local, context),
59            DefUse::Use | DefUse::NonUse => {}
60        }
61    }
62}
63
64/// 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);
67
68impl<'tcx, F> Visitor<'tcx> for VisitDeallocatedLocalsWith<F>
69where
70    F: FnMut(Local, PlaceContext),
71{
72    fn visit_statement(&mut self, statement: &mir::Statement<'tcx>, location: Location) {
73        match statement.kind {
74            // StorageLive resets a local to the live-but-unallocated state,
75            // which also has the effect of freeing its allocation.
76            mir::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    }
85
86    fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, _: Location) {
87        if let mir::Operand::Move(place) = operand
88            && let Some(local) = place.as_local()
89        {
90            (self.0)(local, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move));
91        }
92    }
93}
94
95////////////////////////////////////////////////////////////////////////////////
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.
113
114struct KillPoints<'a> {
115    live_on_entry: IndexVec<BasicBlock, DenseBitSet<Local>>,
116    kill_points_map: IndexVec<PointIndex, &'a [(Local, Location)]>,
117}
118
119impl<'a> KillPoints<'a> {
120    fn 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 {
127        let maybe_live_locals = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, pass_name);
128        let borrowed_locals = borrowed_locals(body);
129
130        // Initialize all borrowed locals as live on entry. We never try to kill
131        // those.
132        let mut live_on_entry =
133            IndexVec::from_elem_n(borrowed_locals.clone(), body.basic_blocks.len());
134
135        // Collect kill points and live-on-entry states from the results of
136        // MaybeLiveLocals.
137        kill_points.clear();
138        let mut visitor = KillPointsVisitor {
139            kill_points,
140            live_on_entry: &mut live_on_entry,
141            borrowed_locals: &borrowed_locals,
142        };
143        visit_results(
144            body,
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);
151
152        // Create a mapping of `PointIndex` to the set of killed locals at that
153        // location.
154        let mut kill_points_map = IndexVec::from_elem_n(&[][..], points.num_points());
155        for chunk in kill_points.chunk_by(|a, b| a.1 == b.1) {
156            let 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);
158            if 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        }
161
162        Self { live_on_entry, kill_points_map }
163    }
164}
165
166struct 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}
171
172impl<'tcx> ResultsVisitor<'tcx, MaybeLiveLocals> for KillPointsVisitor<'_> {
173    fn 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.
177        self.live_on_entry[block].union(state);
178    }
179
180    fn visit_after_early_statement_effect(
181        &mut self,
182        state: &DenseBitSet<Local>,
183        statement: &mir::Statement<'tcx>,
184        location: Location,
185    ) {
186        VisitPlacesWith(|place: Place<'tcx>, ctxt| {
187            // Ignore non-uses.
188            match ctxt {
189                PlaceContext::NonMutatingUse(_) | PlaceContext::MutatingUse(_) => {}
190                PlaceContext::NonUse(_) => return,
191            }
192
193            // 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.
196            if !state.contains(place.local) && !self.borrowed_locals.contains(place.local) {
197                self.kill_points.push((place.local, location));
198            }
199        })
200        .visit_statement(statement, location);
201    }
202
203    fn visit_after_early_terminator_effect(
204        &mut self,
205        state: &DenseBitSet<Local>,
206        terminator: &mir::Terminator<'tcx>,
207        location: Location,
208    ) {
209        VisitPlacesWith(|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).
213            match ctxt {
214                PlaceContext::MutatingUse(
215                    MutatingUseContext::AsmOutput
216                    | MutatingUseContext::Call
217                    | MutatingUseContext::Yield,
218                )
219                | PlaceContext::NonUse(_) => return,
220                PlaceContext::NonMutatingUse(_) | PlaceContext::MutatingUse(_) => {}
221            }
222
223            // 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.
226            if !state.contains(place.local) && !self.borrowed_locals.contains(place.local) {
227                self.kill_points.push((place.local, location));
228            }
229        })
230        .visit_terminator(terminator, location);
231    }
232}
233
234////////////////////////////////////////////////////////////////////////////////
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.
242
243struct PreciseLiveness<'a> {
244    kill_points: &'a KillPoints<'a>,
245    points: &'a DenseLocationMap,
246}
247
248impl PreciseLiveness<'_> {
249    fn 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.
253        state.intersect(&self.kill_points.live_on_entry[block]);
254    }
255}
256
257impl<'tcx> Analysis<'tcx> for PreciseLiveness<'_> {
258    type Domain = DenseBitSet<Local>;
259
260    const NAME: &'static str = "precise_liveness";
261
262    fn bottom_value(&self, body: &mir::Body<'tcx>) -> DenseBitSet<Local> {
263        DenseBitSet::new_empty(body.local_decls.len())
264    }
265
266    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut DenseBitSet<Local>) {
267        // Function arguments start out as live.
268        for arg in body.args_iter() {
269            state.gen_(arg);
270        }
271    }
272
273    fn apply_primary_statement_effect(
274        &self,
275        state: &mut DenseBitSet<Local>,
276        statement: &mir::Statement<'tcx>,
277        location: Location,
278    ) {
279        if location.statement_index == 0 {
280            self.apply_block_start_effect(state, location.block);
281        }
282
283        VisitDeallocatedLocalsWith(|local, _| state.kill(local))
284            .visit_statement(statement, location);
285        VisitAllocatedLocalsWith(|local, _| state.gen_(local)).visit_statement(statement, location);
286
287        // Apply kill points at this statement: if a variable is dead then it
288        // doesn't need storage.
289        let point = self.points.point_from_location(location);
290        for &(local, _) in self.kill_points.kill_points_map[point] {
291            state.kill(local);
292        }
293    }
294
295    fn apply_primary_terminator_effect(
296        &self,
297        state: &mut DenseBitSet<Local>,
298        terminator: &mir::Terminator<'tcx>,
299        location: Location,
300    ) {
301        if location.statement_index == 0 {
302            self.apply_block_start_effect(state, location.block);
303        }
304
305        VisitDeallocatedLocalsWith(|local, _| state.kill(local))
306            .visit_terminator(terminator, location);
307
308        // Gen destination places.
309        VisitAllocatedLocalsWith(|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.
314            if ctxt != PlaceContext::MutatingUse(MutatingUseContext::Yield) {
315                state.gen_(local);
316            }
317        })
318        .visit_terminator(terminator, location);
319    }
320
321    fn apply_call_return_effect(
322        &self,
323        state: &mut DenseBitSet<Local>,
324        _block: BasicBlock,
325        return_places: mir::CallReturnPlaces<'_, 'tcx>,
326    ) {
327        if let mir::CallReturnPlaces::Yield(place) = return_places
328            && !place.is_indirect()
329        {
330            state.gen_(place.local);
331        }
332    }
333}
334
335////////////////////////////////////////////////////////////////////////////////
336// Matrix construction
337
338/// 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}
348
349#[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({})"]
359    pub struct SplitPointIndex {}
360}
361
362impl SplitPointIndex {
363    pub fn new(point: PointIndex, effect: SplitPointEffect) -> SplitPointIndex {
364        let index = (point.as_u32() << 1) | (effect as u32);
365        SplitPointIndex::from_u32(index)
366    }
367
368    pub fn point(self) -> PointIndex {
369        PointIndex::from_u32(self.as_u32() >> 1)
370    }
371
372    pub fn effect(self) -> SplitPointEffect {
373        match self.as_u32() & 1 {
374            0 => SplitPointEffect::Early,
375            1 => SplitPointEffect::Late,
376            _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
377        }
378    }
379}
380
381/// Helper type to construct a `SparseIntervalMatrix`.
382struct MatrixBuilder {
383    matrix: SparseIntervalMatrix<Local, SplitPointIndex>,
384    range_start: IndexVec<Local, Option<SplitPointIndex>>,
385
386    // 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.
389    maybe_live_locals: Vec<Local>,
390}
391
392impl MatrixBuilder {
393    fn gen_(&mut self, local: Local, point: PointIndex, effect: SplitPointEffect) {
394        let split_point = SplitPointIndex::new(point, effect);
395
396        // No-op if the local is already live.
397        if self.range_start[local].is_none() {
398            self.range_start[local] = Some(split_point);
399            self.maybe_live_locals.push(local);
400        }
401    }
402
403    fn kill(&mut self, local: Local, point: PointIndex, effect: SplitPointEffect) {
404        let end = SplitPointIndex::new(point, effect);
405
406        // No-op if the local is already dead.
407        if let Some(start) = self.range_start[local].take() {
408            if true {
    if !(end >= start) {
        ::core::panicking::panic("assertion failed: end >= start")
    };
};debug_assert!(end >= start);
409            self.matrix.append_range(local, start..=end);
410        }
411    }
412
413    fn kill_all(&mut self, point: PointIndex, effect: SplitPointEffect) {
414        while let Some(local) = self.maybe_live_locals.pop() {
415            self.kill(local, point, effect);
416        }
417    }
418
419    fn kill_all_except(&mut self, except: Local, point: PointIndex, effect: SplitPointEffect) {
420        while let Some(local) = self.maybe_live_locals.pop() {
421            if local != except {
422                self.kill(local, point, effect);
423            }
424        }
425        self.maybe_live_locals.push(except);
426    }
427}
428
429pub 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> {
435    let mut kill_points_vec = ::alloc::vec::Vec::new()vec![];
436    let kill_points = KillPoints::compute(tcx, body, pass_name, points, &mut kill_points_vec);
437    let mut results = PreciseLiveness { kill_points: &kill_points, points }
438        .iterate_to_fixpoint(tcx, body, pass_name);
439
440    let 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    };
445    for (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.
448        let state = &mut results.entry_states[block];
449
450        // Notably this kills any dead results produced by a predecessor's
451        // terminator.
452        state.intersect(&kill_points.live_on_entry[block]);
453
454        // 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.
458        let terminator = block_data.terminator();
459        if let mir::TerminatorKind::Return = terminator.kind
460            && block_data.statements.is_empty()
461        {
462            if state.contains(mir::RETURN_PLACE) {
463                builder.gen_(mir::RETURN_PLACE, points.entry_point(block), SplitPointEffect::Early);
464            }
465        } else {
466            for local in state.iter() {
467                builder.gen_(local, points.entry_point(block), SplitPointEffect::Early);
468            }
469        }
470
471        for (statement_index, statement) in block_data.statements.iter().enumerate() {
472            let location = Location { block, statement_index };
473            let point = points.point_from_location(location);
474
475            // Kill moved locals at the early point.
476            VisitDeallocatedLocalsWith(|local, _| {
477                builder.kill(local, point, SplitPointEffect::Early);
478            })
479            .visit_statement(statement, location);
480
481            // Kill any locals which are no longer used after this statement.
482            for &(local, _) in kill_points.kill_points_map[point] {
483                builder.kill(local, point, SplitPointEffect::Early);
484            }
485
486            // Gen destination places at the late point.
487            VisitAllocatedLocalsWith(|local, _| {
488                builder.gen_(local, point, SplitPointEffect::Late);
489            })
490            .visit_statement(statement, location);
491
492            // 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.
495            for &(local, _) in kill_points.kill_points_map[point] {
496                builder.kill(local, point, SplitPointEffect::Late);
497            }
498        }
499
500        // 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.
505        if let mir::TerminatorKind::Return = terminator.kind
506            && !block_data.statements.is_empty()
507        {
508            // Blocks with only a return terminator are handled above.
509            let location = Location { block, statement_index: block_data.statements.len() - 1 };
510            let point = points.point_from_location(location);
511            builder.kill_all_except(mir::RETURN_PLACE, point, SplitPointEffect::Late);
512        }
513
514        let location = Location { block, statement_index: block_data.statements.len() };
515        let point = points.point_from_location(location);
516
517        // Kill moved locals at the early point.
518        VisitDeallocatedLocalsWith(|local, _| {
519            builder.kill(local, point, SplitPointEffect::Early);
520        })
521        .visit_terminator(terminator, location);
522
523        // Kill any locals which are no longer used after this terminator.
524        for &(local, _) in kill_points.kill_points_map[point] {
525            builder.kill(local, point, SplitPointEffect::Early);
526        }
527
528        // Gen destination places at the late point.
529        VisitAllocatedLocalsWith(|local, _| {
530            builder.gen_(local, point, SplitPointEffect::Late);
531        })
532        .visit_terminator(terminator, location);
533
534        // 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.
541        if let mir::TerminatorKind::Call {
542            func: _,
543            args,
544            destination: _,
545            target: _,
546            unwind: _,
547            call_source: _,
548            fn_span: _,
549        } = &terminator.kind
550        {
551            for arg in args {
552                if let mir::Operand::Move(place) = arg.node
553                    && !place.is_indirect()
554                {
555                    builder.gen_(place.local, point, SplitPointEffect::Late);
556                }
557            }
558        }
559
560        // 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.
563        builder.kill_all(point, SplitPointEffect::Late);
564    }
565
566    builder.matrix
567}
568
569pub 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) {
576    let locals_live_at = |split_point| {
577        matrix.rows().filter(|&r| matrix.contains(r, split_point)).collect::<Vec<_>>()
578    };
579
580    if let Some(dumper) = MirDumper::new(tcx, pass_name, body) {
581        let extra_data = &|pass_where, w: &mut dyn std::io::Write| {
582            if let PassWhere::BeforeLocation(loc) = pass_where {
583                let point = points.point_from_location(loc);
584                let split_point = SplitPointIndex::new(point, SplitPointEffect::Early);
585                let live = locals_live_at(split_point);
586                w.write_fmt(format_args!("        // {0:?}-early => {1:?}\n", loc, live))writeln!(w, "        // {loc:?}-early => {live:?}")?;
587                let split_point = SplitPointIndex::new(point, SplitPointEffect::Late);
588                let live = locals_live_at(split_point);
589                w.write_fmt(format_args!("        // {0:?}-late => {1:?}\n", loc, live))writeln!(w, "        // {loc:?}-late => {live:?}")?;
590            }
591            Ok(())
592        };
593
594        dumper.set_extra_data(extra_data).dump_mir(body)
595    }
596}