Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove ResultsVisitable #132134

Merged
merged 2 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 119 additions & 41 deletions compiler/rustc_borrowck/src/dataflow.rs
Original file line number Diff line number Diff line change
@@ -1,93 +1,171 @@
use std::fmt;

use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::graph;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, BasicBlock, Body, Location, Place, TerminatorEdges};
use rustc_middle::mir::{
self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::fmt::DebugWithContext;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::{Analysis, Forward, GenKill, Results, ResultsVisitable};
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
use tracing::debug;

use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};

/// The results of the dataflow analyses used by the borrow checker.
pub(crate) struct BorrowckResults<'a, 'tcx> {
pub(crate) borrows: Results<'tcx, Borrows<'a, 'tcx>>,
pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'tcx>>,
pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'tcx>>,
}

/// The transient state of the dataflow analyses used by the borrow checker.
#[derive(Debug)]
pub(crate) struct BorrowckDomain<'a, 'tcx> {
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
// This analysis is different to most others. Its results aren't computed with
// `iterate_to_fixpoint`, but are instead composed from the results of three sub-analyses that are
// computed individually with `iterate_to_fixpoint`.
pub(crate) struct Borrowck<'a, 'tcx> {
pub(crate) borrows: Borrows<'a, 'tcx>,
pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
}

impl<'a, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'tcx> {
type Direction = Forward;
impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
type Domain = BorrowckDomain<'a, 'tcx>;

const NAME: &'static str = "borrowck";

fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
BorrowckDomain {
borrows: self.borrows.analysis.bottom_value(body),
uninits: self.uninits.analysis.bottom_value(body),
ever_inits: self.ever_inits.analysis.bottom_value(body),
borrows: self.borrows.bottom_value(body),
uninits: self.uninits.bottom_value(body),
ever_inits: self.ever_inits.bottom_value(body),
}
}

fn reset_to_block_entry(&self, state: &mut Self::Domain, block: BasicBlock) {
state.borrows.clone_from(self.borrows.entry_set_for_block(block));
state.uninits.clone_from(self.uninits.entry_set_for_block(block));
state.ever_inits.clone_from(self.ever_inits.entry_set_for_block(block));
fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}

fn reconstruct_before_statement_effect(
fn apply_before_statement_effect(
&mut self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_before_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.analysis.apply_before_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.analysis.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
self.borrows.apply_before_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_before_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_before_statement_effect(&mut state.ever_inits, stmt, loc);
}

fn reconstruct_statement_effect(
fn apply_statement_effect(
&mut self,
state: &mut Self::Domain,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.analysis.apply_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.analysis.apply_statement_effect(&mut state.ever_inits, stmt, loc);
self.borrows.apply_statement_effect(&mut state.borrows, stmt, loc);
self.uninits.apply_statement_effect(&mut state.uninits, stmt, loc);
self.ever_inits.apply_statement_effect(&mut state.ever_inits, stmt, loc);
}

fn reconstruct_before_terminator_effect(
fn apply_before_terminator_effect(
&mut self,
state: &mut Self::Domain,
term: &mir::Terminator<'tcx>,
loc: Location,
) {
self.borrows.analysis.apply_before_terminator_effect(&mut state.borrows, term, loc);
self.uninits.analysis.apply_before_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.analysis.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
self.borrows.apply_before_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_before_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_before_terminator_effect(&mut state.ever_inits, term, loc);
}

fn reconstruct_terminator_effect(
fn apply_terminator_effect<'mir>(
&mut self,
state: &mut Self::Domain,
term: &mir::Terminator<'tcx>,
term: &'mir mir::Terminator<'tcx>,
loc: Location,
) -> TerminatorEdges<'mir, 'tcx> {
self.borrows.apply_terminator_effect(&mut state.borrows, term, loc);
self.uninits.apply_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.apply_terminator_effect(&mut state.ever_inits, term, loc);

// This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this
// analysis doesn't use.
TerminatorEdges::None
}

fn apply_call_return_effect(
&mut self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}

fn apply_switch_int_edge_effects(
&mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
) {
self.borrows.analysis.apply_terminator_effect(&mut state.borrows, term, loc);
self.uninits.analysis.apply_terminator_effect(&mut state.uninits, term, loc);
self.ever_inits.analysis.apply_terminator_effect(&mut state.ever_inits, term, loc);
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}
}

impl JoinSemiLattice for BorrowckDomain<'_, '_> {
fn join(&mut self, _other: &Self) -> bool {
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
unreachable!();
}
}

impl<'tcx, C> DebugWithContext<C> for BorrowckDomain<'_, 'tcx>
where
C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
{
fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("borrows: ")?;
self.borrows.fmt_with(ctxt, f)?;
f.write_str(" uninits: ")?;
self.uninits.fmt_with(ctxt, f)?;
f.write_str(" ever_inits: ")?;
self.ever_inits.fmt_with(ctxt, f)?;
Ok(())
}

fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self == old {
return Ok(());
}

if self.borrows != old.borrows {
f.write_str("borrows: ")?;
self.borrows.fmt_diff_with(&old.borrows, ctxt, f)?;
f.write_str("\n")?;
}

if self.uninits != old.uninits {
f.write_str("uninits: ")?;
self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
f.write_str("\n")?;
}

if self.ever_inits != old.ever_inits {
f.write_str("ever_inits: ")?;
self.ever_inits.fmt_diff_with(&old.ever_inits, ctxt, f)?;
f.write_str("\n")?;
}

Ok(())
}
}

/// The transient state of the dataflow analyses used by the borrow checker.
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct BorrowckDomain<'a, 'tcx> {
pub(crate) borrows: <Borrows<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
pub(crate) ever_inits: <EverInitializedPlaces<'a, 'tcx> as Analysis<'tcx>>::Domain,
}

rustc_index::newtype_index! {
#[orderable]
#[debug_format = "bw{}"]
Expand Down
93 changes: 54 additions & 39 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@ use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt, TypingMode};
use rustc_middle::{bug, span_bug};
use rustc_mir_dataflow::Analysis;
use rustc_mir_dataflow::impls::{
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
};
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
};
use rustc_mir_dataflow::{Analysis, EntrySets, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::UNUSED_MUT;
use rustc_span::{Span, Symbol};
use smallvec::SmallVec;
use tracing::{debug, instrument};

use crate::borrow_set::{BorrowData, BorrowSet};
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
use crate::dataflow::{BorrowIndex, BorrowckDomain, BorrowckResults, Borrows};
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
use crate::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName};
use crate::location::LocationTable;
use crate::nll::PoloniusOutput;
Expand Down Expand Up @@ -221,6 +221,10 @@ fn do_mir_borrowck<'tcx>(
consumer_options,
);

// `flow_inits` is large, so we drop it as soon as possible. This reduces
// peak memory usage significantly on some benchmarks.
drop(flow_inits);

// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests, as well as helping with debugging.
nll::dump_nll_mir(&infcx, body, &regioncx, &opt_closure_req, &borrow_set);
Expand All @@ -229,27 +233,6 @@ fn do_mir_borrowck<'tcx>(
// information.
nll::dump_annotation(&infcx, body, &regioncx, &opt_closure_req, &opaque_type_values, diags);

// The various `flow_*` structures can be large. We drop `flow_inits` here
// so it doesn't overlap with the others below. This reduces peak memory
// usage significantly on some benchmarks.
drop(flow_inits);

let flow_borrows = Borrows::new(tcx, body, &regioncx, &borrow_set).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);
let flow_ever_inits = EverInitializedPlaces::new(body, &move_data).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);

let movable_coroutine =
// The first argument is the coroutine type passed by value
if let Some(local) = body.local_decls.raw.get(1)
Expand Down Expand Up @@ -334,16 +317,11 @@ fn do_mir_borrowck<'tcx>(
// Compute and report region errors, if any.
mbcx.report_region_errors(nll_errors);

let mut results = BorrowckResults {
ever_inits: flow_ever_inits,
uninits: flow_uninits,
borrows: flow_borrows,
};

rustc_mir_dataflow::visit_results(
let mut flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, &regioncx);
visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
&mut results,
&mut flow_results,
&mut mbcx,
);

Expand Down Expand Up @@ -426,6 +404,47 @@ fn do_mir_borrowck<'tcx>(
(result, body_with_facts)
}

fn get_flow_results<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>,
move_data: &'a MoveData<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
) -> Results<'tcx, Borrowck<'a, 'tcx>> {
// We compute these three analyses individually, but them combine them into
// a single results so that `mbcx` can visit them all together.
let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);
let uninits = MaybeUninitializedPlaces::new(tcx, body, move_data).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);
let ever_inits = EverInitializedPlaces::new(body, move_data).iterate_to_fixpoint(
tcx,
body,
Some("borrowck"),
);

let analysis = Borrowck {
borrows: borrows.analysis,
uninits: uninits.analysis,
ever_inits: ever_inits.analysis,
};

assert_eq!(borrows.entry_sets.len(), uninits.entry_sets.len());
assert_eq!(borrows.entry_sets.len(), ever_inits.entry_sets.len());
let entry_sets: EntrySets<'_, Borrowck<'_, '_>> =
itertools::izip!(borrows.entry_sets, uninits.entry_sets, ever_inits.entry_sets)
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
.collect();

Results { analysis, entry_sets }
}

pub(crate) struct BorrowckInferCtxt<'tcx> {
pub(crate) infcx: InferCtxt<'tcx>,
pub(crate) reg_var_to_origin: RefCell<FxIndexMap<ty::RegionVid, RegionCtxt>>,
Expand Down Expand Up @@ -588,14 +607,10 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R>
for MirBorrowckCtxt<'a, '_, 'tcx>
{
type Domain = BorrowckDomain<'a, 'tcx>;

impl<'a, 'tcx> ResultsVisitor<'a, 'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
fn visit_statement_before_primary_effect(
&mut self,
_results: &mut R,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
stmt: &'a Statement<'tcx>,
location: Location,
Expand Down Expand Up @@ -667,7 +682,7 @@ impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R>

fn visit_terminator_before_primary_effect(
&mut self,
_results: &mut R,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
term: &'a Terminator<'tcx>,
loc: Location,
Expand Down Expand Up @@ -780,7 +795,7 @@ impl<'a, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'a, 'tcx, R>

fn visit_terminator_after_primary_effect(
&mut self,
_results: &mut R,
_results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
state: &BorrowckDomain<'a, 'tcx>,
term: &'a Terminator<'tcx>,
loc: Location,
Expand Down
Loading
Loading