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

[WIP] Implement a "place unification" MIR optimization (aka source/destination propagation aka NRVO). #47954

Closed
wants to merge 9 commits into from
142 changes: 142 additions & 0 deletions src/librustc_mir/analysis/accesses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2018 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.

use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::Idx;
use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::ty;
use syntax::ast;
use analysis::borrows::MaybeBorrowed;
use analysis::dataflow::{do_dataflow, BitDenotation, BlockSets, DebugFormatted};
use analysis::eventflow::{Backward, Events, EventFlowResults, Forward, PastAndFuture};
use analysis::locations::FlatLocations;

pub struct Accesses<'a> {
pub results: PastAndFuture<EventFlowResults<'a, Forward, Local>,
EventFlowResults<'a, Backward, Local>>
}

impl<'a> Accesses<'a> {
pub fn collect(mir: &Mir, flat_locations: &'a FlatLocations) -> Self {
let borrows = ty::tls::with(|tcx| {
do_dataflow(tcx, mir, ast::DUMMY_NODE_ID, &[],
&IdxSetBuf::new_empty(mir.basic_blocks().len()),
MaybeBorrowed::new(mir),
|_, path| DebugFormatted::new(&path))
});

let mut collector = AccessPathCollector {
location: Location {
block: START_BLOCK,
statement_index: !0
},
accesses: Events::new(mir, flat_locations, mir.local_decls.len()),
maybe_borrowed: IdxSetBuf::new_empty(0)
};

// FIXME(eddyb) introduce a seeker for this (like in eventflow),
// maybe reusing `dataflow::at_location(::FlowAtLocation)`.
// That would remove the need for asserting the location.

for (block, data) in mir.basic_blocks().iter_enumerated() {
collector.location.block = block;
collector.maybe_borrowed = borrows.sets().on_entry_set_for(block.index()).to_owned();

let on_entry = &mut collector.maybe_borrowed.clone();
let kill_set = &mut collector.maybe_borrowed.clone();
for (i, statement) in data.statements.iter().enumerate() {
collector.location.statement_index = i;
borrows.operator().before_statement_effect(&mut BlockSets {
on_entry,
kill_set,
gen_set: &mut collector.maybe_borrowed,
}, collector.location);
// FIXME(eddyb) get rid of temporary with NLL/2phi.
let location = collector.location;
collector.visit_statement(block, statement, location);
borrows.operator().statement_effect(&mut BlockSets {
on_entry,
kill_set,
gen_set: &mut collector.maybe_borrowed,
}, collector.location);
}

if let Some(ref terminator) = data.terminator {
collector.location.statement_index = data.statements.len();
borrows.operator().before_terminator_effect(&mut BlockSets {
on_entry,
kill_set,
gen_set: &mut collector.maybe_borrowed,
}, collector.location);
// FIXME(eddyb) get rid of temporary with NLL/2phi.
let location = collector.location;
collector.visit_terminator(block, terminator, location);
}
}
// All arguments have been accessed prior to the call to this function.
let results = collector.accesses.flow(mir.args_iter());
Accesses { results }
}
}

struct AccessPathCollector<'a, 'b, 'tcx: 'a> {
accesses: Events<'a, 'b, 'tcx, Local>,
location: Location,
maybe_borrowed: IdxSetBuf<Local>
}

impl<'a, 'b, 'tcx> AccessPathCollector<'a, 'b, 'tcx> {
fn access_anything_borrowed(&mut self, location: Location) {
assert_eq!(self.location, location);

// FIXME(eddyb) OR `maybe_borrowed` into the accesses for performance.
for path in self.maybe_borrowed.iter() {
self.accesses.insert_at(path, location);
}
}
}

impl<'a, 'b, 'tcx> Visitor<'tcx> for AccessPathCollector<'a, 'b, 'tcx> {
fn visit_local(&mut self,
&local: &Local,
context: PlaceContext,
location: Location) {
if context.is_use() {
self.accesses.insert_at(local, location);
}
}

fn visit_projection_elem(&mut self,
elem: &PlaceElem<'tcx>,
context: PlaceContext<'tcx>,
location: Location) {
if let ProjectionElem::Deref = *elem {
self.access_anything_borrowed(location);
}
self.super_projection_elem(elem, context, location);
}

fn visit_terminator_kind(&mut self,
block: BasicBlock,
kind: &TerminatorKind<'tcx>,
location: Location) {
match *kind {
TerminatorKind::Call { .. } => {
self.access_anything_borrowed(location);
}
TerminatorKind::Return => {
self.visit_local(&RETURN_PLACE, PlaceContext::Move, location);
}
_ => {}
}
self.super_terminator_kind(block, kind, location);
}
}
110 changes: 110 additions & 0 deletions src/librustc_mir/analysis/borrows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2018 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.

use rustc_data_structures::indexed_set::IdxSet;
use rustc_data_structures::bitslice::BitwiseOperator;
use rustc::mir::*;
use rustc::mir::visit::Visitor;
use analysis::dataflow::{BitDenotation, BlockSets, InitialFlow};

#[derive(Copy, Clone)]
pub struct MaybeBorrowed<'a, 'tcx: 'a> {
mir: &'a Mir<'tcx>
}

impl<'a, 'tcx: 'a> MaybeBorrowed<'a, 'tcx> {
pub fn new(mir: &'a Mir<'tcx>) -> Self {
MaybeBorrowed { mir }
}
}

impl<'a, 'tcx> BitDenotation for MaybeBorrowed<'a, 'tcx> {
type Idx = Local;
fn name() -> &'static str { "maybe_borrowed" }
fn bits_per_block(&self) -> usize {
self.mir.local_decls.len()
}

fn start_block_effect(&self, _sets: &mut IdxSet<Local>) {
// Nothing is borrowed on function entry
}

fn statement_effect(&self,
sets: &mut BlockSets<Local>,
location: Location) {
match self.mir[location.block].statements[location.statement_index].kind {
StatementKind::Assign(_, Rvalue::Ref(.., ref place)) => {
// Ignore `place`s based on a dereference, when `gen`-ing borrows,
// as the resulting reference can't actually point to a local path
// that isn't already borrowed, and definitely not the base reference.
let mut base = place;
while let Place::Projection(ref proj) = *base {
if let ProjectionElem::Deref = proj.elem {
break;
}
base = &proj.base;
}

if let Place::Local(local) = *base {
sets.gen(&local);
}
}
StatementKind::StorageDead(local) => {
sets.kill(&local);
}
// FIXME(eddyb) cancel all borrows on `yield` (unless the generator is immovable).
_ => {}
}

let mut moves = MoveCollector { sets };
moves.visit_location(self.mir, location);
}

fn terminator_effect(&self,
sets: &mut BlockSets<Local>,
location: Location) {
let mut moves = MoveCollector { sets };
moves.visit_location(self.mir, location);
}

fn propagate_call_return(&self,
_in_out: &mut IdxSet<Local>,
_call_bb: BasicBlock,
_dest_bb: BasicBlock,
_dest_place: &Place) {
// Nothing to do when a call returns successfully
}
}

impl<'a, 'tcx> BitwiseOperator for MaybeBorrowed<'a, 'tcx> {
#[inline]
fn join(&self, pred1: usize, pred2: usize) -> usize {
pred1 | pred2 // "maybe" means we union effects of both preds
}
}

impl<'a, 'tcx> InitialFlow for MaybeBorrowed<'a, 'tcx> {
#[inline]
fn bottom_value() -> bool {
false // bottom = unborrowed
}
}

struct MoveCollector<'a, 'b: 'a> {
sets: &'a mut BlockSets<'b, Local>
}

impl<'a, 'b, 'tcx> Visitor<'tcx> for MoveCollector<'a, 'b> {
fn visit_operand(&mut self, operand: &Operand, _: Location) {
if let Operand::Move(Place::Local(local)) = *operand {
self.sets.kill(&local);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use rustc::mir::{BasicBlock, Location};
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
use rustc_data_structures::indexed_vec::Idx;

use dataflow::{BitDenotation, BlockSets, DataflowResults};
use dataflow::move_paths::{HasMoveData, MovePathIndex};
use analysis::dataflow::{BitDenotation, BlockSets, DataflowResults};
use analysis::dataflow::move_paths::{HasMoveData, MovePathIndex};

use std::iter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub use super::*;

use rustc::mir::*;
use rustc::mir::visit::Visitor;
use dataflow::BitDenotation;
use analysis::dataflow::BitDenotation;

/// This calculates if any part of a MIR local could have previously been borrowed.
/// This means that once a local has been borrowed, its bit will always be set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use rustc_data_structures::bitslice::{BitwiseOperator};
use rustc_data_structures::indexed_set::{IdxSet};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};

use dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use dataflow::indexes::{BorrowIndex, ReserveOrActivateIndex};
use analysis::dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use analysis::dataflow::indexes::{BorrowIndex, ReserveOrActivateIndex};
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
pub use super::*;

use rustc::mir::*;
use dataflow::BitDenotation;
use analysis::dataflow::BitDenotation;

#[derive(Copy, Clone)]
pub struct MaybeStorageLive<'a, 'tcx: 'a> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ pub struct MoveDataParamEnv<'gcx, 'tcx> {
pub(crate) param_env: ty::ParamEnv<'gcx>,
}

pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
dead_unwinds: &IdxSet<BasicBlock>,
bd: BD,
p: P)
-> DataflowResults<BD>
pub(crate) fn do_dataflow<BD, P>(tcx: TyCtxt,
mir: &Mir,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
dead_unwinds: &IdxSet<BasicBlock>,
bd: BD,
p: P)
-> DataflowResults<BD>
where BD: BitDenotation + InitialFlow,
P: Fn(&BD, BD::Idx) -> DebugFormatted
{
Expand All @@ -139,7 +139,7 @@ pub(crate) fn do_dataflow<'a, 'gcx, 'tcx, BD, P>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
{
pub(crate) fn run<P>(self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
tcx: TyCtxt,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
p: P) -> DataflowResults<BD>
Expand Down Expand Up @@ -513,7 +513,7 @@ pub struct BlockSets<'a, E: Idx> {
}

impl<'a, E:Idx> BlockSets<'a, E> {
fn gen(&mut self, e: &E) {
pub(crate) fn gen(&mut self, e: &E) {
self.gen_set.add(e);
self.kill_set.remove(e);
}
Expand All @@ -538,7 +538,7 @@ impl<'a, E:Idx> BlockSets<'a, E> {
}
}

fn kill(&mut self, e: &E) {
pub(crate) fn kill(&mut self, e: &E) {
self.gen_set.remove(e);
self.kill_set.add(e);
}
Expand Down
File renamed without changes.
Loading