Skip to content

Commit

Permalink
compute union-find of locals flowing into the output of statics
Browse files Browse the repository at this point in the history
Co-authored-by: lqd <remy.rakic+github@gmail.com>
Co-authored-by: nikomatsakis <niko@alum.mit.edu>
  • Loading branch information
nikomatsakis and lqd committed Aug 2, 2018
1 parent db54765 commit 341a07c
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 24 deletions.
161 changes: 161 additions & 0 deletions src/librustc_mir/borrow_check/nll/escaping_locals.rs
@@ -0,0 +1,161 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A MIR walk gathering a union-crfind of assigned locals, for the purpose of locating the ones
//! escaping into the output.

use rustc::mir::visit::Visitor;
use rustc::mir::*;

use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::unify as ut;

crate struct EscapingLocals {
unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
}

impl EscapingLocals {
crate fn compute(mir: &Mir<'_>) -> Self {
let mut visitor = GatherAssignedLocalsVisitor::new();
visitor.visit_mir(mir);

EscapingLocals { unification_table: visitor.unification_table }
}

/// True if `local` is known to escape into static
/// memory.
crate fn escapes_into_return(&mut self, local: Local) -> bool {
let return_place = AssignedLocal::from(RETURN_PLACE);
let other_place = AssignedLocal::from(local);
self.unification_table.unioned(return_place, other_place)
}
}

/// The MIR visitor gathering the union-find of the locals used in
/// assignments.
struct GatherAssignedLocalsVisitor {
unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
struct AssignedLocal(u32);

impl ut::UnifyKey for AssignedLocal {
type Value = ();

fn index(&self) -> u32 {
self.0
}

fn from_index(i: u32) -> AssignedLocal {
AssignedLocal(i)
}

fn tag() -> &'static str {
"AssignedLocal"
}
}

impl From<Local> for AssignedLocal {
fn from(item: Local) -> Self {
// newtype_indexes use usize but are u32s.
assert!(item.index() < ::std::u32::MAX as usize);
AssignedLocal(item.index() as u32)
}
}

impl GatherAssignedLocalsVisitor {
fn new() -> Self {
Self {
unification_table: ut::UnificationTable::new(),
}
}

fn union_locals_if_needed(&mut self, lvalue: Option<Local>, rvalue: Option<Local>) {
if let Some(lvalue) = lvalue {
if let Some(rvalue) = rvalue {
if lvalue != rvalue {
self.unification_table
.union(AssignedLocal::from(lvalue), AssignedLocal::from(rvalue));
}
}
}
}
}

// Returns the potential `Local` associated to this `Place` or `PlaceProjection`
fn find_local_in_place(place: &Place) -> Option<Local> {
match place {
Place::Local(local) => Some(*local),

// If you do e.g. `x = a.f` then only *part* of the type of
// `a` escapes into `x` (the part contained in `f`); if `a`'s
// type has regions that don't appear in `f`, those might not
// escape.
Place::Projection(..) => None,

Place::Static { .. } | Place::Promoted { .. } => None,
}
}

// Returns the potential `Local` in this `Operand`.
fn find_local_in_operand(op: &Operand) -> Option<Local> {
// Conservatively check a subset of `Operand`s we know our
// benchmarks track, for example `html5ever`.
match op {
Operand::Copy(place) | Operand::Move(place) => find_local_in_place(place),
Operand::Constant(_) => None,
}
}

impl<'tcx> Visitor<'tcx> for GatherAssignedLocalsVisitor {
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
// We need as many union-find keys as there are locals
for _ in 0..mir.local_decls.len() {
self.unification_table.new_key(());
}

self.super_mir(mir);
}

fn visit_assign(
&mut self,
block: BasicBlock,
place: &Place<'tcx>,
rvalue: &Rvalue<'tcx>,
location: Location,
) {
let local = find_local_in_place(place);

// Conservatively check a subset of `Rvalue`s we know our
// benchmarks track, for example `html5ever`.
match rvalue {
Rvalue::Use(op) => self.union_locals_if_needed(local, find_local_in_operand(op)),
Rvalue::Ref(_, _, place) => {
self.union_locals_if_needed(local, find_local_in_place(place))
}

Rvalue::Cast(kind, op, _) => match kind {
CastKind::Unsize => self.union_locals_if_needed(local, find_local_in_operand(op)),
_ => (),
},

Rvalue::Aggregate(_, ops) => {
for rvalue in ops.iter().map(find_local_in_operand) {
self.union_locals_if_needed(local, rvalue);
}
}

_ => (),
};

self.super_assign(block, place, rvalue, location);
}
}
36 changes: 24 additions & 12 deletions src/librustc_mir/borrow_check/nll/liveness_map.rs
Expand Up @@ -16,9 +16,10 @@
//! liveness code so that it only operates over variables with regions in their
//! types, instead of all variables.

use borrow_check::nll::escaping_locals::EscapingLocals;
use rustc::mir::{Local, Mir};
use rustc::ty::TypeFoldable;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::mir::{Mir, Local};
use util::liveness::LiveVariableMap;

use rustc_data_structures::indexed_vec::Idx;
Expand All @@ -29,14 +30,13 @@ use rustc_data_structures::indexed_vec::Idx;
crate struct NllLivenessMap {
/// For each local variable, contains either None (if the type has no regions)
/// or Some(i) with a suitable index.
pub from_local: IndexVec<Local, Option<LocalWithRegion>>,
/// For each LocalWithRegion, maps back to the original Local index.
pub to_local: IndexVec<LocalWithRegion, Local>,
from_local: IndexVec<Local, Option<LocalWithRegion>>,

/// For each LocalWithRegion, maps back to the original Local index.
to_local: IndexVec<LocalWithRegion, Local>,
}

impl LiveVariableMap for NllLivenessMap {

fn from_local(&self, local: Local) -> Option<Self::LiveVar> {
self.from_local[local]
}
Expand All @@ -55,21 +55,33 @@ impl LiveVariableMap for NllLivenessMap {
impl NllLivenessMap {
/// Iterates over the variables in Mir and assigns each Local whose type contains
/// regions a LocalWithRegion index. Returns a map for converting back and forth.
pub fn compute(mir: &Mir) -> Self {
crate fn compute(mir: &Mir<'_>) -> Self {
let mut escaping_locals = EscapingLocals::compute(mir);

let mut to_local = IndexVec::default();
let from_local: IndexVec<Local,Option<_>> = mir
let from_local: IndexVec<Local, Option<_>> = mir
.local_decls
.iter_enumerated()
.map(|(local, local_decl)| {
if local_decl.ty.has_free_regions() {
if escaping_locals.escapes_into_return(local) {
// If the local escapes into the return value,
// then the return value will force all of the
// regions in its type to outlive free regions
// (e.g., `'static`) and hence liveness is not
// needed. This is particularly important for big
// statics.
None
} else if local_decl.ty.has_free_regions() {
Some(to_local.push(local))
} else {
None
}
else {
None
}
}).collect();

Self { from_local, to_local }
Self {
from_local,
to_local,
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/librustc_mir/borrow_check/nll/mod.rs
Expand Up @@ -39,7 +39,9 @@ use polonius_engine::{Algorithm, Output};
use util as mir_util;
use util::pretty::{self, ALIGN};

mod constraints;
mod constraint_generation;
mod escaping_locals;
pub mod explain_borrow;
mod facts;
mod invalidation;
Expand All @@ -49,8 +51,6 @@ crate mod type_check;
mod universal_regions;
crate mod liveness_map;

mod constraints;

use self::facts::AllFacts;
use self::region_infer::RegionInferenceContext;
use self::universal_regions::UniversalRegions;
Expand Down Expand Up @@ -120,6 +120,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
location_table,
borrow_set,
&liveness,
&liveness_map,
&mut all_facts,
flow_inits,
move_data,
Expand Down
15 changes: 8 additions & 7 deletions src/librustc_mir/borrow_check/nll/type_check/liveness.rs
Expand Up @@ -37,17 +37,18 @@ pub(super) fn generate<'gcx, 'tcx>(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
liveness: &LivenessResults<LocalWithRegion>,
liveness_map: &NllLivenessMap,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) {
let mut generator = TypeLivenessGenerator {
cx,
mir,
liveness,
liveness_map,
flow_inits,
move_data,
drop_data: FxHashMap(),
map: &NllLivenessMap::compute(mir),
};

for bb in mir.basic_blocks().indices() {
Expand All @@ -65,10 +66,10 @@ where
cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
mir: &'gen Mir<'tcx>,
liveness: &'gen LivenessResults<LocalWithRegion>,
liveness_map: &'gen NllLivenessMap,
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
move_data: &'gen MoveData<'tcx>,
drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
map: &'gen NllLivenessMap,
}

struct DropData<'tcx> {
Expand All @@ -86,9 +87,9 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo

self.liveness
.regular
.simulate_block(self.mir, bb, self.map, |location, live_locals| {
.simulate_block(self.mir, bb, self.liveness_map, |location, live_locals| {
for live_local in live_locals.iter() {
let local = self.map.from_live_var(live_local);
let local = self.liveness_map.from_live_var(live_local);
let live_local_ty = self.mir.local_decls[local].ty;
Self::push_type_live_constraint(&mut self.cx, live_local_ty, location);
}
Expand All @@ -97,7 +98,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
let mut all_live_locals: Vec<(Location, Vec<LocalWithRegion>)> = vec![];
self.liveness
.drop
.simulate_block(self.mir, bb, self.map, |location, live_locals| {
.simulate_block(self.mir, bb, self.liveness_map, |location, live_locals| {
all_live_locals.push((location, live_locals.iter().collect()));
});
debug!(
Expand All @@ -124,7 +125,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
});
}

let local = self.map.from_live_var(live_local);
let local = self.liveness_map.from_live_var(live_local);
let mpi = self.move_data.rev_lookup.find_local(local);
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
debug!(
Expand All @@ -133,7 +134,7 @@ impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flo
self.move_data.move_paths[initialized_child]
);

let local = self.map.from_live_var(live_local);
let local = self.liveness_map.from_live_var(live_local);
let live_local_ty = self.mir.local_decls[local].ty;
self.add_drop_live_constraint(live_local, live_local_ty, location);
}
Expand Down
10 changes: 7 additions & 3 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Expand Up @@ -15,9 +15,12 @@ use borrow_check::borrow_set::BorrowSet;
use borrow_check::location::LocationTable;
use borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint};
use borrow_check::nll::facts::AllFacts;
use borrow_check::nll::region_infer::values::{RegionValueElements, LivenessValues};
use borrow_check::nll::liveness_map::NllLivenessMap;
use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
use borrow_check::nll::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
use borrow_check::nll::type_check::free_region_relations::{
CreateResult, UniversalRegionRelations,
};
use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::LocalWithRegion;
use borrow_check::nll::ToRegionVid;
Expand Down Expand Up @@ -116,6 +119,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
location_table: &LocationTable,
borrow_set: &BorrowSet<'tcx>,
liveness: &LivenessResults<LocalWithRegion>,
liveness_map: &NllLivenessMap,
all_facts: &mut Option<AllFacts>,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
Expand Down Expand Up @@ -166,7 +170,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
Some(&mut borrowck_context),
Some(errors_buffer),
|cx| {
liveness::generate(cx, mir, liveness, flow_inits, move_data);
liveness::generate(cx, mir, liveness, liveness_map, flow_inits, move_data);
cx.equate_inputs_and_outputs(
mir,
mir_def_id,
Expand Down

0 comments on commit 341a07c

Please sign in to comment.