Skip to content

[perf] test MCP510 #113382

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

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
12 changes: 2 additions & 10 deletions compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,14 @@ fn propagate_loans_between_points(
});
}

let Some(current_live_regions) = live_regions.row(current_point) else {
// There are no constraints to add: there are no live regions at the current point.
return;
};
let Some(next_live_regions) = live_regions.row(next_point) else {
// There are no constraints to add: there are no live regions at the next point.
return;
};

for region in next_live_regions.iter() {
if !current_live_regions.contains(region) {
continue;
}

// `region` is indeed live at both points, add a constraint between them, according to
// variance.
// `region` could be live at the current point, and is live at the next point: add a
// constraint between them, according to variance.
if let Some(&direction) = live_region_variances.get(&region) {
add_liveness_constraint(
region,
Expand Down
149 changes: 5 additions & 144 deletions compiler/rustc_borrowck/src/polonius/loan_liveness.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,25 @@
use std::collections::{BTreeMap, BTreeSet};

use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{
Body, Local, Location, Place, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_middle::ty::RegionVid;
use rustc_mir_dataflow::points::PointIndex;

use super::{LiveLoans, LocalizedOutlivesConstraintSet};
use crate::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::dataflow::BorrowIndex;
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::{BorrowSet, PlaceConflictBias, places_conflict};

/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
/// traversing the full graph of constraints that combines:
/// - the localized constraints (the physical edges),
/// - with the constraints that hold at all points (the logical edges).
pub(super) fn compute_loan_liveness<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
liveness: &LivenessValues,
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
) -> LiveLoans {
let mut live_loans = LiveLoans::new(borrow_set.len());

// FIXME: it may be preferable for kills to be encoded in the edges themselves, to simplify and
// likely make traversal (and constraint generation) more efficient. We also display kills on
// edges when visualizing the constraint graph anyways.
let kills = collect_kills(body, tcx, borrow_set);

// Create the full graph with the physical edges we've localized earlier, and the logical edges
// of constraints that hold at all points.
let logical_constraints =
Expand All @@ -60,7 +46,9 @@ pub(super) fn compute_loan_liveness<'tcx>(
}

// Record the loan as being live on entry to this point.
live_loans.insert(node.point, loan_idx);
if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) {
live_loans.insert(node.point, loan_idx);
}

// Here, we have a conundrum. There's currently a weakness in our theory, in that
// we're using a single notion of reachability to represent what used to be _two_
Expand Down Expand Up @@ -111,21 +99,7 @@ pub(super) fn compute_loan_liveness<'tcx>(
// borrowck implementation in a-mir-formality, fuzzing, or manually crafting
// counter-examples.

// Continuing traversal will depend on whether the loan is killed at this point, and
// whether we're time-traveling.
let current_location = liveness.location_from_point(node.point);
let is_loan_killed =
kills.get(&current_location).is_some_and(|kills| kills.contains(&loan_idx));

for succ in graph.outgoing_edges(node) {
// If the loan is killed at this point, it is killed _on exit_. But only during
// forward traversal.
if is_loan_killed {
let destination = liveness.location_from_point(succ.point);
if current_location.is_predecessor_of(destination, body) {
continue;
}
}
stack.push(succ);
}
}
Expand Down Expand Up @@ -192,116 +166,3 @@ impl LocalizedConstraintGraph {
physical_edges.chain(materialized_edges)
}
}

/// Traverses the MIR and collects kills.
fn collect_kills<'tcx>(
body: &Body<'tcx>,
tcx: TyCtxt<'tcx>,
borrow_set: &BorrowSet<'tcx>,
) -> BTreeMap<Location, BTreeSet<BorrowIndex>> {
let mut collector = KillsCollector { borrow_set, tcx, body, kills: BTreeMap::default() };
for (block, data) in body.basic_blocks.iter_enumerated() {
collector.visit_basic_block_data(block, data);
}
collector.kills
}

struct KillsCollector<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,

/// The set of loans killed at each location.
kills: BTreeMap<Location, BTreeSet<BorrowIndex>>,
}

// This visitor has a similar structure to the `Borrows` dataflow computation with respect to kills,
// and the datalog polonius fact generation for the `loan_killed_at` relation.
impl<'tcx> KillsCollector<'_, 'tcx> {
/// Records the borrows on the specified place as `killed`. For example, when assigning to a
/// local, or on a call's return destination.
fn record_killed_borrows_for_place(&mut self, place: Place<'tcx>, location: Location) {
// For the reasons described in graph traversal, we also filter out kills
// unreachable from the loan's introduction point, as they would stop traversal when
// e.g. checking for reachability in the subset graph through invariance constraints
// higher up.
let filter_unreachable_kills = |loan| {
let introduction = self.borrow_set[loan].reserve_location;
let reachable = introduction.is_predecessor_of(location, self.body);
reachable
};

let other_borrows_of_local = self
.borrow_set
.local_map
.get(&place.local)
.into_iter()
.flat_map(|bs| bs.iter())
.copied();

// If the borrowed place is a local with no projections, all other borrows of this
// local must conflict. This is purely an optimization so we don't have to call
// `places_conflict` for every borrow.
if place.projection.is_empty() {
if !self.body.local_decls[place.local].is_ref_to_static() {
self.kills
.entry(location)
.or_default()
.extend(other_borrows_of_local.filter(|&loan| filter_unreachable_kills(loan)));
}
return;
}

// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
// pair of array indices are not equal, so that when `places_conflict` returns true, we
// will be assured that two places being compared definitely denotes the same sets of
// locations.
let definitely_conflicting_borrows = other_borrows_of_local
.filter(|&i| {
places_conflict(
self.tcx,
self.body,
self.borrow_set[i].borrowed_place,
place,
PlaceConflictBias::NoOverlap,
)
})
.filter(|&loan| filter_unreachable_kills(loan));

self.kills.entry(location).or_default().extend(definitely_conflicting_borrows);
}

/// Records the borrows on the specified local as `killed`.
fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
self.kills.entry(location).or_default().extend(borrow_indices.iter());
}
}
}

impl<'tcx> Visitor<'tcx> for KillsCollector<'_, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
// Make sure there are no remaining borrows for locals that have gone out of scope.
if let StatementKind::StorageDead(local) = statement.kind {
self.record_killed_borrows_for_local(local, location);
}

self.super_statement(statement, location);
}

fn visit_assign(&mut self, place: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
// When we see `X = ...`, then kill borrows of `(*X).foo` and so forth.
self.record_killed_borrows_for_place(*place, location);
self.super_assign(place, rvalue, location);
}

fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
// A `Call` terminator's return value can be a local which has borrows, so we need to record
// those as killed as well.
if let TerminatorKind::Call { destination, .. } = terminator.kind {
self.record_killed_borrows_for_place(destination, location);
}

self.super_terminator(terminator, location);
}
}
2 changes: 0 additions & 2 deletions compiler/rustc_borrowck/src/polonius/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ impl PoloniusContext {
// Now that we have a complete graph, we can compute reachability to trace the liveness of
// loans for the next step in the chain, the NLL loan scope and active loans computations.
let live_loans = compute_loan_liveness(
tcx,
body,
regioncx.liveness_constraints(),
regioncx.outlives_constraints(),
borrow_set,
Expand Down
14 changes: 1 addition & 13 deletions compiler/rustc_borrowck/src/polonius/typeck_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ pub(super) fn convert_typeck_constraints<'tcx>(
tcx,
body,
stmt,
liveness,
&outlives_constraint,
location,
point,
universal_regions,
)
Expand Down Expand Up @@ -78,9 +76,7 @@ fn localize_statement_constraint<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
stmt: &Statement<'tcx>,
liveness: &LivenessValues,
outlives_constraint: &OutlivesConstraint<'tcx>,
current_location: Location,
current_point: PointIndex,
universal_regions: &UniversalRegions<'tcx>,
) -> LocalizedOutlivesConstraint {
Expand Down Expand Up @@ -119,16 +115,8 @@ fn localize_statement_constraint<'tcx>(
"there should be no common regions between the LHS and RHS of an assignment"
);

// As mentioned earlier, we should be tracking these better upstream but: we want to
// relate the types on entry to the type of the place on exit. That is, outlives
// constraints on the RHS are on entry, and outlives constraints to/from the LHS are on
// exit (i.e. on entry to the successor location).
let lhs_ty = body.local_decls[lhs.local].ty;
let successor_location = Location {
block: current_location.block,
statement_index: current_location.statement_index + 1,
};
let successor_point = liveness.point_from_location(successor_location);
let successor_point = current_point;
compute_constraint_direction(
tcx,
outlives_constraint,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3385,13 +3385,13 @@ impl PatchableFunctionEntry {
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
pub enum Polonius {
/// The default value: disabled.
#[default]
Off,

/// Legacy version, using datalog and the `polonius-engine` crate. Historical value for `-Zpolonius`.
Legacy,

/// In-tree prototype, extending the NLL infrastructure.
#[default]
Next,
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,7 @@ impl<'test> TestCx<'test> {

match self.config.compare_mode {
Some(CompareMode::Polonius) => {
rustc.args(&["-Zpolonius"]);
rustc.args(&["-Zpolonius=next"]);
}
Some(CompareMode::NextSolver) => {
rustc.args(&["-Znext-solver"]);
Expand Down
7 changes: 0 additions & 7 deletions tests/crashes/135646.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `ADecoder<'a>`
--> $DIR/dropck-normalize-errors.rs:15:28
--> $DIR/dropck-normalize-errors.rs:19:28
|
LL | fn make_a_decoder<'a>() -> ADecoder<'a> {
| ^^^^^^^^^^^^ within `ADecoder<'a>`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct`
|
help: this trait has no implementations, consider adding one
--> $DIR/dropck-normalize-errors.rs:7:1
--> $DIR/dropck-normalize-errors.rs:11:1
|
LL | trait NonImplementedTrait {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: required because it appears within the type `BDecoder`
--> $DIR/dropck-normalize-errors.rs:26:12
--> $DIR/dropck-normalize-errors.rs:30:12
|
LL | pub struct BDecoder {
| ^^^^^^^^
note: required because it appears within the type `ADecoder<'a>`
--> $DIR/dropck-normalize-errors.rs:12:12
--> $DIR/dropck-normalize-errors.rs:16:12
|
LL | pub struct ADecoder<'a> {
| ^^^^^^^^
= note: the return type of a function must have a statically known size

error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied in `BDecoder`
--> $DIR/dropck-normalize-errors.rs:23:20
--> $DIR/dropck-normalize-errors.rs:27:20
|
LL | type Decoder = BDecoder;
| ^^^^^^^^ within `BDecoder`, the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct`
|
help: this trait has no implementations, consider adding one
--> $DIR/dropck-normalize-errors.rs:7:1
--> $DIR/dropck-normalize-errors.rs:11:1
|
LL | trait NonImplementedTrait {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
note: required because it appears within the type `BDecoder`
--> $DIR/dropck-normalize-errors.rs:26:12
--> $DIR/dropck-normalize-errors.rs:30:12
|
LL | pub struct BDecoder {
| ^^^^^^^^
note: required by a bound in `Decode::Decoder`
--> $DIR/dropck-normalize-errors.rs:4:5
--> $DIR/dropck-normalize-errors.rs:8:5
|
LL | type Decoder;
| ^^^^^^^^^^^^^ required by this bound in `Decode::Decoder`
Expand All @@ -48,25 +48,25 @@ LL | type Decoder: ?Sized;
| ++++++++

error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied
--> $DIR/dropck-normalize-errors.rs:27:22
--> $DIR/dropck-normalize-errors.rs:31:22
|
LL | non_implemented: <NonImplementedStruct as NonImplementedTrait>::Assoc,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct`
|
help: this trait has no implementations, consider adding one
--> $DIR/dropck-normalize-errors.rs:7:1
--> $DIR/dropck-normalize-errors.rs:11:1
|
LL | trait NonImplementedTrait {
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `NonImplementedStruct: NonImplementedTrait` is not satisfied
--> $DIR/dropck-normalize-errors.rs:15:28
--> $DIR/dropck-normalize-errors.rs:19:28
|
LL | fn make_a_decoder<'a>() -> ADecoder<'a> {
| ^^^^^^^^^^^^ the trait `NonImplementedTrait` is not implemented for `NonImplementedStruct`
|
help: this trait has no implementations, consider adding one
--> $DIR/dropck-normalize-errors.rs:7:1
--> $DIR/dropck-normalize-errors.rs:11:1
|
LL | trait NonImplementedTrait {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
Loading
Loading