1
Fork 0

GVN borrowed locals too.

This commit is contained in:
Camille GILLOT 2023-12-30 19:04:26 +00:00
parent 5d144e301c
commit 308cc76510
12 changed files with 111 additions and 121 deletions

View file

@ -3,7 +3,6 @@ use rustc_index::IndexSlice;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::borrowed_locals;
use crate::ssa::SsaLocals;
@ -32,8 +31,8 @@ impl<'tcx> MirPass<'tcx> for CopyProp {
}
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let borrowed_locals = borrowed_locals(body);
let ssa = SsaLocals::new(body);
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
let ssa = SsaLocals::new(tcx, body, param_env);
let fully_moved = fully_moved_locals(&ssa, body);
debug!(?fully_moved);
@ -51,7 +50,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
tcx,
copy_classes: ssa.copy_classes(),
fully_moved,
borrowed_locals,
borrowed_locals: ssa.borrowed_locals(),
storage_to_remove,
}
.visit_body_preserves_cfg(body);
@ -101,7 +100,7 @@ struct Replacer<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
fully_moved: BitSet<Local>,
storage_to_remove: BitSet<Local>,
borrowed_locals: BitSet<Local>,
borrowed_locals: &'a BitSet<Local>,
copy_classes: &'a IndexSlice<Local, Local>,
}
@ -112,6 +111,9 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
fn visit_local(&mut self, local: &mut Local, ctxt: PlaceContext, _: Location) {
let new_local = self.copy_classes[*local];
if self.borrowed_locals.contains(*local) || self.borrowed_locals.contains(new_local) {
return;
}
match ctxt {
// Do not modify the local in storage statements.
PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => {}
@ -122,32 +124,14 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
}
}
fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) {
fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
if let Some(new_projection) = self.process_projection(place.projection, loc) {
place.projection = self.tcx().mk_place_elems(&new_projection);
}
let observes_address = match ctxt {
PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow
| NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::AddressOf,
) => true,
// For debuginfo, merging locals is ok.
PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
self.borrowed_locals.contains(place.local)
}
_ => false,
};
if observes_address && !place.is_indirect() {
// We observe the address of `place.local`. Do not replace it.
} else {
self.visit_local(
&mut place.local,
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
loc,
)
}
// Any non-mutating use context is ok.
let ctxt = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
self.visit_local(&mut place.local, ctxt, loc)
}
fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {

View file

@ -121,7 +121,7 @@ impl<'tcx> MirPass<'tcx> for GVN {
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
let ssa = SsaLocals::new(body);
let ssa = SsaLocals::new(tcx, body, param_env);
// Clone dominators as we need them while mutating the body.
let dominators = body.basic_blocks.dominators().clone();

View file

@ -22,7 +22,8 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
}
fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let ssa = SsaLocals::new(body);
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
let ssa = SsaLocals::new(tcx, body, param_env);
let slice_lengths = compute_slice_length(tcx, &ssa, body);
debug!(?slice_lengths);

View file

@ -82,7 +82,8 @@ impl<'tcx> MirPass<'tcx> for ReferencePropagation {
}
fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
let ssa = SsaLocals::new(body);
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
let ssa = SsaLocals::new(tcx, body, param_env);
let mut replacer = compute_replacement(tcx, body, &ssa);
debug!(?replacer.targets);

View file

@ -2,8 +2,9 @@
//! 1/ They are only assigned-to once, either as a function parameter, or in an assign statement;
//! 2/ This single assignment dominates all uses;
//!
//! As a consequence of rule 2, we consider that borrowed locals are not SSA, even if they are
//! `Freeze`, as we do not track that the assignment dominates all uses of the borrow.
//! As we do not track indirect assignments, a local that has its address taken (either by
//! AddressOf or by borrowing) is considered non-SSA. However, it is UB to modify through an
//! immutable borrow of a `Freeze` local. Those can still be considered to be SSA.
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::bit_set::BitSet;
@ -11,6 +12,7 @@ use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::middle::resolve_bound_vars::Set1;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::{ParamEnv, TyCtxt};
pub struct SsaLocals {
/// Assignments to each local. This defines whether the local is SSA.
@ -24,6 +26,8 @@ pub struct SsaLocals {
/// Number of "direct" uses of each local, ie. uses that are not dereferences.
/// We ignore non-uses (Storage statements, debuginfo).
direct_uses: IndexVec<Local, u32>,
/// Set of SSA locals that are immutably borrowed.
borrowed_locals: BitSet<Local>,
}
pub enum AssignedValue<'a, 'tcx> {
@ -33,15 +37,22 @@ pub enum AssignedValue<'a, 'tcx> {
}
impl SsaLocals {
pub fn new<'tcx>(body: &Body<'tcx>) -> SsaLocals {
pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ParamEnv<'tcx>) -> SsaLocals {
let assignment_order = Vec::with_capacity(body.local_decls.len());
let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls);
let dominators = body.basic_blocks.dominators();
let direct_uses = IndexVec::from_elem(0, &body.local_decls);
let mut visitor =
SsaVisitor { body, assignments, assignment_order, dominators, direct_uses };
let borrowed_locals = BitSet::new_empty(body.local_decls.len());
let mut visitor = SsaVisitor {
body,
assignments,
assignment_order,
dominators,
direct_uses,
borrowed_locals,
};
for local in body.args_iter() {
visitor.assignments[local] = Set1::One(DefLocation::Argument);
@ -58,6 +69,16 @@ impl SsaLocals {
visitor.visit_var_debug_info(var_debug_info);
}
// The immutability of shared borrows only works on `Freeze` locals. If the visitor found
// borrows, we need to check the types. For raw pointers and mutable borrows, the locals
// have already been marked as non-SSA.
debug!(?visitor.borrowed_locals);
for local in visitor.borrowed_locals.iter() {
if !body.local_decls[local].ty.is_freeze(tcx, param_env) {
visitor.assignments[local] = Set1::Many;
}
}
debug!(?visitor.assignments);
debug!(?visitor.direct_uses);
@ -70,6 +91,7 @@ impl SsaLocals {
assignments: visitor.assignments,
assignment_order: visitor.assignment_order,
direct_uses: visitor.direct_uses,
borrowed_locals: visitor.borrowed_locals,
// This is filled by `compute_copy_classes`.
copy_classes: IndexVec::default(),
};
@ -174,6 +196,11 @@ impl SsaLocals {
&self.copy_classes
}
/// Set of SSA locals that are immutably borrowed.
pub fn borrowed_locals(&self) -> &BitSet<Local> {
&self.borrowed_locals
}
/// Make a property uniform on a copy equivalence class by removing elements.
pub fn meet_copy_equivalence(&self, property: &mut BitSet<Local>) {
// Consolidate to have a local iff all its copies are.
@ -208,6 +235,8 @@ struct SsaVisitor<'tcx, 'a> {
assignments: IndexVec<Local, Set1<DefLocation>>,
assignment_order: Vec<Local>,
direct_uses: IndexVec<Local, u32>,
// Track locals that are immutably borrowed, so we can check their type is `Freeze` later.
borrowed_locals: BitSet<Local>,
}
impl SsaVisitor<'_, '_> {
@ -232,16 +261,18 @@ impl<'tcx> Visitor<'tcx> for SsaVisitor<'tcx, '_> {
PlaceContext::MutatingUse(MutatingUseContext::Projection)
| PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => bug!(),
// Anything can happen with raw pointers, so remove them.
// We do not verify that all uses of the borrow dominate the assignment to `local`,
// so we have to remove them too.
PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow
| NonMutatingUseContext::FakeBorrow
| NonMutatingUseContext::AddressOf,
)
PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
| PlaceContext::MutatingUse(_) => {
self.assignments[local] = Set1::Many;
}
// Immutable borrows are ok, but we need to delay a check that the type is `Freeze`.
PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow | NonMutatingUseContext::FakeBorrow,
) => {
self.borrowed_locals.insert(local);
self.check_dominates(local, loc);
self.direct_uses[local] += 1;
}
PlaceContext::NonMutatingUse(_) => {
self.check_dominates(local, loc);
self.direct_uses[local] += 1;