Auto merge of #116152 - cjgillot:unchunck, r=nnethercote

Only use dense bitsets in dataflow analyses

When a dataflow state has the size close to the number of locals, we should prefer a dense bitset, like we already store locals in a dense vector.
Other occurrences of `ChunkedBitSet` need to be justified by the size of the dataflow state.
This commit is contained in:
bors 2024-01-23 11:56:30 +00:00
commit 0e4243538b
7 changed files with 24 additions and 17 deletions

View File

@ -1,6 +1,6 @@
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::graph::WithSuccessors; use rustc_data_structures::graph::WithSuccessors;
use rustc_index::bit_set::HybridBitSet; use rustc_index::bit_set::BitSet;
use rustc_index::interval::IntervalSet; use rustc_index::interval::IntervalSet;
use rustc_infer::infer::canonical::QueryRegionConstraints; use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::for_liveness; use rustc_infer::infer::outlives::for_liveness;
@ -135,7 +135,7 @@ struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>, cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>,
/// Set of points that define the current local. /// Set of points that define the current local.
defs: HybridBitSet<PointIndex>, defs: BitSet<PointIndex>,
/// Points where the current variable is "use live" -- meaning /// Points where the current variable is "use live" -- meaning
/// that there is a future "full use" that may use its value. /// that there is a future "full use" that may use its value.
@ -158,7 +158,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
let num_points = cx.elements.num_points(); let num_points = cx.elements.num_points();
LivenessResults { LivenessResults {
cx, cx,
defs: HybridBitSet::new_empty(num_points), defs: BitSet::new_empty(num_points),
use_live_at: IntervalSet::new(num_points), use_live_at: IntervalSet::new(num_points),
drop_live_at: IntervalSet::new(num_points), drop_live_at: IntervalSet::new(num_points),
drop_locations: vec![], drop_locations: vec![],

View File

@ -284,7 +284,7 @@ impl<T: Idx> BitSet<T> {
not_already not_already
} }
fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
let (start, end) = inclusive_start_end(range, self.domain_size)?; let (start, end) = inclusive_start_end(range, self.domain_size)?;
let (start_word_index, _) = word_index_and_mask(start); let (start_word_index, _) = word_index_and_mask(start);
let (end_word_index, end_mask) = word_index_and_mask(end); let (end_word_index, end_mask) = word_index_and_mask(end);
@ -1299,7 +1299,7 @@ impl<T: Idx> SparseBitSet<T> {
} }
impl<T: Idx + Ord> SparseBitSet<T> { impl<T: Idx + Ord> SparseBitSet<T> {
fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> { pub fn last_set_in(&self, range: impl RangeBounds<T>) -> Option<T> {
let mut last_leq = None; let mut last_leq = None;
for e in self.iter() { for e in self.iter() {
if range.contains(e) { if range.contains(e) {

View File

@ -305,7 +305,10 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
} }
impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
/// We use a chunked bitset to avoid paying too high a memory footprint.
type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>; type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>;
const NAME: &'static str = "maybe_init"; const NAME: &'static str = "maybe_init";
fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain { fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
@ -437,6 +440,8 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
} }
impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
/// There can be many more `MovePathIndex` than there are locals in a MIR body.
/// We use a chunked bitset to avoid paying too high a memory footprint.
type Domain = ChunkedBitSet<MovePathIndex>; type Domain = ChunkedBitSet<MovePathIndex>;
const NAME: &'static str = "maybe_uninit"; const NAME: &'static str = "maybe_uninit";
@ -636,6 +641,8 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
} }
impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> { impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
/// There can be many more `InitIndex` than there are locals in a MIR body.
/// We use a chunked bitset to avoid paying too high a memory footprint.
type Domain = ChunkedBitSet<InitIndex>; type Domain = ChunkedBitSet<InitIndex>;
const NAME: &'static str = "ever_init"; const NAME: &'static str = "ever_init";

View File

@ -1,4 +1,4 @@
use rustc_index::bit_set::{BitSet, ChunkedBitSet}; use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{ use rustc_middle::mir::{
self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges, self, CallReturnPlaces, Local, Location, Place, StatementKind, TerminatorEdges,
@ -26,14 +26,14 @@ use crate::{Analysis, AnalysisDomain, Backward, GenKill, GenKillAnalysis};
pub struct MaybeLiveLocals; pub struct MaybeLiveLocals;
impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
type Domain = ChunkedBitSet<Local>; type Domain = BitSet<Local>;
type Direction = Backward; type Direction = Backward;
const NAME: &'static str = "liveness"; const NAME: &'static str = "liveness";
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
// bottom = not live // bottom = not live
ChunkedBitSet::new_empty(body.local_decls.len()) BitSet::new_empty(body.local_decls.len())
} }
fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
@ -233,14 +233,14 @@ impl<'a> MaybeTransitiveLiveLocals<'a> {
} }
impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> { impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
type Domain = ChunkedBitSet<Local>; type Domain = BitSet<Local>;
type Direction = Backward; type Direction = Backward;
const NAME: &'static str = "transitive liveness"; const NAME: &'static str = "transitive liveness";
fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain { fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
// bottom = not live // bottom = not live
ChunkedBitSet::new_empty(body.local_decls.len()) BitSet::new_empty(body.local_decls.len())
} }
fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) { fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {

View File

@ -1,5 +1,5 @@
use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor}; use crate::framework::{visit_results, ResultsVisitable, ResultsVisitor};
use rustc_index::bit_set::ChunkedBitSet; use rustc_index::bit_set::BitSet;
use rustc_index::interval::SparseIntervalMatrix; use rustc_index::interval::SparseIntervalMatrix;
use rustc_index::Idx; use rustc_index::Idx;
use rustc_index::IndexVec; use rustc_index::IndexVec;
@ -102,7 +102,7 @@ pub fn save_as_intervals<'tcx, N, R>(
) -> SparseIntervalMatrix<N, PointIndex> ) -> SparseIntervalMatrix<N, PointIndex>
where where
N: Idx, N: Idx,
R: ResultsVisitable<'tcx, FlowState = ChunkedBitSet<N>>, R: ResultsVisitable<'tcx, FlowState = BitSet<N>>,
{ {
let values = SparseIntervalMatrix::new(elements.num_points()); let values = SparseIntervalMatrix::new(elements.num_points());
let mut visitor = Visitor { elements, values }; let mut visitor = Visitor { elements, values };
@ -124,7 +124,7 @@ impl<'mir, 'tcx, R, N> ResultsVisitor<'mir, 'tcx, R> for Visitor<'_, N>
where where
N: Idx, N: Idx,
{ {
type FlowState = ChunkedBitSet<N>; type FlowState = BitSet<N>;
fn visit_statement_after_primary_effect( fn visit_statement_after_primary_effect(
&mut self, &mut self,

View File

@ -12,7 +12,7 @@ use crate::MoveDataParamEnv;
use crate::{Analysis, JoinSemiLattice, ResultsCursor}; use crate::{Analysis, JoinSemiLattice, ResultsCursor};
use rustc_ast::MetaItem; use rustc_ast::MetaItem;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_index::bit_set::ChunkedBitSet; use rustc_index::bit_set::BitSet;
use rustc_middle::mir::MirPass; use rustc_middle::mir::MirPass;
use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::mir::{self, Body, Local, Location};
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
@ -275,7 +275,7 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
&self, &self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
place: mir::Place<'tcx>, place: mir::Place<'tcx>,
flow_state: &ChunkedBitSet<Local>, flow_state: &BitSet<Local>,
call: PeekCall, call: PeekCall,
) { ) {
info!(?place, "peek_at"); info!(?place, "peek_at");

View File

@ -1,7 +1,7 @@
//! See the docs for [`RenameReturnPlace`]. //! See the docs for [`RenameReturnPlace`].
use rustc_hir::Mutability; use rustc_hir::Mutability;
use rustc_index::bit_set::HybridBitSet; use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, BasicBlock, Local, Location}; use rustc_middle::mir::{self, BasicBlock, Local, Location};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
@ -123,7 +123,7 @@ fn find_local_assigned_to_return_place(
body: &mut mir::Body<'_>, body: &mut mir::Body<'_>,
) -> Option<Local> { ) -> Option<Local> {
let mut block = start; let mut block = start;
let mut seen = HybridBitSet::new_empty(body.basic_blocks.len()); let mut seen = BitSet::new_empty(body.basic_blocks.len());
// Iterate as long as `block` has exactly one predecessor that we have not yet visited. // Iterate as long as `block` has exactly one predecessor that we have not yet visited.
while seen.insert(block) { while seen.insert(block) {