Skip to content

Commit

Permalink
polonius: add generation of liveneness-related facts
Browse files Browse the repository at this point in the history
Notably contains an ugly hack to generate initialization information for
variables that will go away when we have that functionality in Polonius.
  • Loading branch information
Albin Stjerna committed Jul 12, 2019
1 parent e775bf3 commit ac0a3d1
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 20 deletions.
7 changes: 7 additions & 0 deletions src/librustc/mir/mod.rs
Expand Up @@ -9,6 +9,7 @@ use crate::hir::def_id::DefId;
use crate::hir::{self, InlineAsm as HirInlineAsm};
use crate::mir::interpret::{ConstValue, InterpError, Scalar};
use crate::mir::visit::MirVisitable;
use polonius_engine::Atom;
use rustc_data_structures::bit_set::BitMatrix;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
Expand Down Expand Up @@ -600,6 +601,12 @@ newtype_index! {
}
}

impl Atom for Local {
fn index(self) -> usize {
Idx::index(self)
}
}

/// Classifies locals into categories. See `Body::local_kind`.
#[derive(PartialEq, Eq, Debug, HashStable)]
pub enum LocalKind {
Expand Down
8 changes: 5 additions & 3 deletions src/librustc_mir/borrow_check/flows.rs
Expand Up @@ -3,7 +3,7 @@
//! FIXME: this might be better as a "generic" fixed-point combinator,
//! but is not as ugly as it is right now.

use rustc::mir::{BasicBlock, Location};
use rustc::mir::{BasicBlock, Local, Location};
use rustc::ty::RegionVid;
use rustc_data_structures::bit_set::BitIter;

Expand All @@ -21,22 +21,24 @@ use either::Either;
use std::fmt;
use std::rc::Rc;

crate type PoloniusOutput = Output<RegionVid, BorrowIndex, LocationIndex, Local>;

// (forced to be `pub` due to its use as an associated type below.)
crate struct Flows<'b, 'tcx> {
borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>,
pub uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'tcx>>,
pub ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'tcx>>,

/// Polonius Output
pub polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
pub polonius_output: Option<Rc<PoloniusOutput>>,
}

impl<'b, 'tcx> Flows<'b, 'tcx> {
crate fn new(
borrows: FlowAtLocation<'tcx, Borrows<'b, 'tcx>>,
uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'tcx>>,
ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'tcx>>,
polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
polonius_output: Option<Rc<PoloniusOutput>>,
) -> Self {
Flows {
borrows,
Expand Down
9 changes: 8 additions & 1 deletion src/librustc_mir/borrow_check/nll/facts.rs
Expand Up @@ -2,6 +2,7 @@ use crate::borrow_check::location::{LocationIndex, LocationTable};
use crate::dataflow::indexes::BorrowIndex;
use polonius_engine::AllFacts as PoloniusAllFacts;
use polonius_engine::Atom;
use rustc::mir::Local;
use rustc::ty::{RegionVid, TyCtxt};
use rustc_data_structures::indexed_vec::Idx;
use std::error::Error;
Expand All @@ -10,7 +11,7 @@ use std::fs::{self, File};
use std::io::Write;
use std::path::Path;

crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex>;
crate type AllFacts = PoloniusAllFacts<RegionVid, BorrowIndex, LocationIndex, Local>;

crate trait AllFactsExt {
/// Returns `true` if there is a need to gather `AllFacts` given the
Expand Down Expand Up @@ -60,6 +61,12 @@ impl AllFactsExt for AllFacts {
outlives,
region_live_at,
invalidates,
var_used,
var_defined,
var_drop_used,
var_uses_region,
var_drops_region,
var_initialized_on_exit,
])
}
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_mir/borrow_check/nll/mod.rs
Expand Up @@ -11,7 +11,7 @@ use crate::transform::MirSource;
use crate::borrow_check::Upvar;
use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Body};
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Local, Body};
use rustc::ty::{self, RegionKind, RegionVid};
use rustc_errors::Diagnostic;
use std::fmt::Debug;
Expand Down Expand Up @@ -84,7 +84,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
errors_buffer: &mut Vec<Diagnostic>,
) -> (
RegionInferenceContext<'tcx>,
Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex, Local>>>,
Option<ClosureRegionRequirements<'tcx>>,
) {
let mut all_facts = if AllFacts::enabled(infcx.tcx) {
Expand Down
13 changes: 4 additions & 9 deletions src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs
Expand Up @@ -15,6 +15,7 @@ use std::rc::Rc;
use super::TypeChecker;

mod local_use_map;
mod polonius;
mod trace;

/// Combines liveness analysis with initialization analysis to
Expand Down Expand Up @@ -57,15 +58,9 @@ pub(super) fn generate<'tcx>(
};

if !live_locals.is_empty() {
trace::trace(
typeck,
body,
elements,
flow_inits,
move_data,
live_locals,
location_table,
);
trace::trace(typeck, body, elements, flow_inits, move_data, live_locals, location_table);

polonius::populate_var_liveness_facts(typeck, body, location_table);
}
}

Expand Down
94 changes: 94 additions & 0 deletions src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs
@@ -0,0 +1,94 @@
use crate::borrow_check::location::{LocationIndex, LocationTable};
use crate::util::liveness::{categorize, DefUse};
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{Body, Local, Location};
use rustc::ty::subst::Kind;
use rustc::ty::Ty;

use super::TypeChecker;

type VarPointRelations = Vec<(Local, LocationIndex)>;

struct LivenessPointFactsExtractor<'me> {
var_defined: &'me mut VarPointRelations,
var_used: &'me mut VarPointRelations,
location_table: &'me LocationTable,
}

// A Visitor to walk through the MIR and extract point-wise facts
impl LivenessPointFactsExtractor<'_> {
fn location_to_index(&self, location: Location) -> LocationIndex {
self.location_table.mid_index(location)
}

fn insert_def(&mut self, local: Local, location: Location) {
debug!("LivenessFactsExtractor::insert_def()");
self.var_defined.push((local, self.location_to_index(location)));
}

fn insert_use(&mut self, local: Local, location: Location) {
debug!("LivenessFactsExtractor::insert_use()");
self.var_used.push((local, self.location_to_index(location)));
}
}

impl Visitor<'tcx> for LivenessPointFactsExtractor<'_> {
fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) {
match categorize(context) {
Some(DefUse::Def) => self.insert_def(local, location),
Some(DefUse::Use) => self.insert_use(local, location),
_ => (),
// NOTE: Drop handling is now done in trace()
}
}
}

fn add_var_uses_regions(typeck: &mut TypeChecker<'_, 'tcx>, local: Local, ty: Ty<'tcx>) {
debug!("add_regions(local={:?}, type={:?})", local, ty);
typeck.tcx().for_each_free_region(&ty, |region| {
let region_vid = typeck.borrowck_context.universal_regions.to_region_vid(region);
debug!("add_regions for region {:?}", region_vid);
if let Some(facts) = typeck.borrowck_context.all_facts {
facts.var_uses_region.push((local, region_vid));
}
});
}

pub(super) fn populate_var_liveness_facts(
typeck: &mut TypeChecker<'_, 'tcx>,
mir: &Body<'tcx>,
location_table: &LocationTable,
) {
debug!("populate_var_liveness_facts()");

if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
LivenessPointFactsExtractor {
var_defined: &mut facts.var_defined,
var_used: &mut facts.var_used,
location_table,
}
.visit_body(mir);
}

for (local, local_decl) in mir.local_decls.iter_enumerated() {
add_var_uses_regions(typeck, local, local_decl.ty);
}
}

// For every potentially drop()-touched region `region` in `local`'s type
// (`kind`), emit a Polonius `var_drops_region(local, region)` fact.
pub(super) fn add_var_drops_regions(
typeck: &mut TypeChecker<'_, 'tcx>,
local: Local,
kind: &Kind<'tcx>,
) {
debug!("add_var_drops_region(local={:?}, kind={:?}", local, kind);
let tcx = typeck.tcx();

tcx.for_each_free_region(kind, |drop_live_region| {
let region_vid = typeck.borrowck_context.universal_regions.to_region_vid(drop_live_region);
if let Some(facts) = typeck.borrowck_context.all_facts.as_mut() {
facts.var_drops_region.push((local, region_vid));
};
});
}
82 changes: 77 additions & 5 deletions src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
@@ -1,13 +1,14 @@
use crate::borrow_check::location::LocationTable;
use crate::borrow_check::nll::region_infer::values::{self, PointIndex, RegionValueElements};
use crate::borrow_check::nll::type_check::liveness::local_use_map::LocalUseMap;
use crate::borrow_check::nll::type_check::liveness::polonius;
use crate::borrow_check::nll::type_check::NormalizeLocation;
use crate::borrow_check::nll::type_check::TypeChecker;
use crate::dataflow::indexes::MovePathIndex;
use crate::dataflow::move_paths::MoveData;
use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces};
use rustc::infer::canonical::QueryRegionConstraints;
use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, Body};
use rustc::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
use rustc::traits::query::type_op::outlives::DropckOutlives;
use rustc::traits::query::type_op::TypeOp;
Expand Down Expand Up @@ -130,6 +131,12 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
for local in live_locals {
self.reset_local_state();
self.add_defs_for(local);

// FIXME: this is temporary until we can generate our own initialization
if self.cx.typeck.borrowck_context.all_facts.is_some() {
self.add_polonius_var_initialized_on_exit_for(local)
}

self.compute_use_live_points_for(local);
self.compute_drop_live_points_for(local);

Expand All @@ -150,6 +157,63 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
}
}

// WARNING: panics if self.cx.typeck.borrowck_context.all_facts != None
//
// FIXME: this analysis (the initialization tracking) should be
// done in Polonius, but isn't yet.
fn add_polonius_var_initialized_on_exit_for(&mut self, local: Local) {
let move_path = self.cx.move_data.rev_lookup.find_local(local);
let facts = self.cx.typeck.borrowck_context.all_facts.as_mut().unwrap();
for block in self.cx.body.basic_blocks().indices() {
debug!("polonius: generating initialization facts for {:?} in {:?}", local, block);

// iterate through the block, applying the effects of each statement
// up to and including location, and populate `var_initialized_on_exit`
self.cx.flow_inits.reset_to_entry_of(block);
let start_location = Location { block, statement_index: 0 };
self.cx.flow_inits.apply_local_effect(start_location);

for statement_index in 0..self.cx.body[block].statements.len() {
let current_location = Location { block, statement_index };

self.cx.flow_inits.reconstruct_statement_effect(current_location);

// statement has not yet taken effect:
if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.start_index(current_location)));
}

// statement has now taken effect
self.cx.flow_inits.apply_local_effect(current_location);

if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.mid_index(current_location)));
}
}

let terminator_location = self.cx.body.terminator_loc(block);

if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.start_index(terminator_location)));
}

// apply the effects of the terminator and push it if needed
self.cx.flow_inits.reset_to_exit_of(block);

if self.cx.flow_inits.has_any_child_of(move_path).is_some() {
facts
.var_initialized_on_exit
.push((local, self.cx.location_table.mid_index(terminator_location)));
}
}
}

/// Clear the value of fields that are "per local variable".
fn reset_local_state(&mut self) {
self.defs.clear();
Expand Down Expand Up @@ -211,6 +275,11 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
debug_assert_eq!(self.cx.body.terminator_loc(location.block), location,);

if self.cx.initialized_at_terminator(location.block, mpi) {
// FIXME: this analysis (the initialization tracking) should be
// done in Polonius, but isn't yet.
if let Some(facts) = self.cx.typeck.borrowck_context.all_facts {
facts.var_drop_used.push((local, self.cx.location_table.mid_index(location)));
}
if self.drop_live_at.insert(drop_point) {
self.drop_locations.push(location);
self.stack.push(drop_point);
Expand Down Expand Up @@ -487,6 +556,8 @@ impl LivenessContext<'_, '_, '_, 'tcx> {
live_at,
self.location_table,
);

polonius::add_var_drops_regions(&mut self.typeck, dropped_local, &kind);
}
}

Expand All @@ -505,14 +576,15 @@ impl LivenessContext<'_, '_, '_, 'tcx> {

let tcx = typeck.tcx();
tcx.for_each_free_region(&value, |live_region| {
let live_region_vid = typeck.borrowck_context
.universal_regions
.to_region_vid(live_region);
typeck.borrowck_context
let live_region_vid =
typeck.borrowck_context.universal_regions.to_region_vid(live_region);
typeck
.borrowck_context
.constraints
.liveness_constraints
.add_elements(live_region_vid, live_at);

// FIXME: remove this when we can generate our own region-live-at reliably
if let Some(facts) = typeck.borrowck_context.all_facts {
for point in live_at.iter() {
let loc = elements.to_location(point);
Expand Down

0 comments on commit ac0a3d1

Please sign in to comment.