Skip to content

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

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

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