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

restore Subslice move out from array after elaborate drops and borrowck #48355

Merged
merged 1 commit into from
Mar 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx


// Optimizations begin.
uniform_array_move_out::RestoreSubsliceArrayMoveOut,
inline::Inline,
instcombine::InstCombine,
deaggregator::Deaggregator,
Expand Down
198 changes: 187 additions & 11 deletions src/librustc_mir/transform/uniform_array_move_out.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
// and mir statement _11 = move _2[-1 of 1]; replaced by:
// _11 = move _2[2 of 3];
//
// FIXME: convert to Subslice back for performance reason
// FIXME: integrate this transformation to the mir build

use rustc::ty;
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::visit::Visitor;
use rustc::mir::visit::{Visitor, PlaceContext};
use transform::{MirPass, MirSource};
use util::patch::MirPatch;
use rustc_data_structures::indexed_vec::{IndexVec};

pub struct UniformArrayMoveOut;

Expand All @@ -67,12 +67,12 @@ struct UniformArrayMoveOutVisitor<'a, 'tcx: 'a> {
}

impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
fn visit_statement(&mut self,
block: BasicBlock,
statement: &Statement<'tcx>,
location: Location) {
if let StatementKind::Assign(ref dst_place,
Rvalue::Use(Operand::Move(ref src_place))) = statement.kind {
fn visit_assign(&mut self,
block: BasicBlock,
dst_place: &Place<'tcx>,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue {
if let Place::Projection(ref proj) = *src_place {
if let ProjectionElem::ConstantIndex{offset: _,
min_length: _,
Expand All @@ -92,7 +92,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
}
}
}
return self.super_statement(block, statement, location);
self.super_assign(block, dst_place, rvalue, location)
}
}

Expand All @@ -104,7 +104,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
item_ty: &'tcx ty::TyS<'tcx>,
size: u32) {
match proj.elem {
// uniform _10 = move _2[:-1];
// uniforms statements like_10 = move _2[:-1];
ProjectionElem::Subslice{from, to} => {
self.patch.make_nop(location);
let temps : Vec<_> = (from..(size-to)).map(|i| {
Expand Down Expand Up @@ -133,7 +133,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
self.patch.add_statement(location, StatementKind::StorageDead(temp));
}
}
// _11 = move _2[-1 of 1];
// uniforms statements like _11 = move _2[-1 of 1];
ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => {
self.patch.make_nop(location);
self.patch.add_assign(location,
Expand All @@ -151,3 +151,179 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
}
}
}

// Restore Subslice move out after analysis
// Example:
//
// next statements:
// StorageLive(_12);
// _12 = move _2[0 of 3];
// StorageLive(_13);
// _13 = move _2[1 of 3];
// _10 = [move _12, move _13]
// StorageDead(_12);
// StorageDead(_13);
//
// replaced by _10 = move _2[:-1];

pub struct RestoreSubsliceArrayMoveOut;

impl MirPass for RestoreSubsliceArrayMoveOut {
fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
_src: MirSource,
mir: &mut Mir<'tcx>) {
let mut patch = MirPatch::new(mir);
{
let mut visitor = RestoreDataCollector {
locals_use: IndexVec::from_elem(LocalUse::new(), &mir.local_decls),
candidates: vec![],
};
visitor.visit_mir(mir);

for candidate in &visitor.candidates {
let statement = &mir[candidate.block].statements[candidate.statement_index];
if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind {
if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval {
let items : Vec<_> = items.iter().map(|item| {
if let Operand::Move(Place::Local(local)) = item {
let local_use = &visitor.locals_use[*local];
let opt_index_and_place = Self::try_get_item_source(local_use, mir);
// each local should be used twice:
// in assign and in aggregate statments
if local_use.use_count == 2 && opt_index_and_place.is_some() {
let (index, src_place) = opt_index_and_place.unwrap();
return Some((local_use, index, src_place));
}
}
None
}).collect();

let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
let opt_size = opt_src_place.and_then(|src_place| {
let src_ty = src_place.ty(mir, tcx).to_ty(tcx);
if let ty::TyArray(_, ref size_o) = src_ty.sty {
size_o.val.to_const_int().and_then(|v| v.to_u64())
} else {
None
}
});
Self::check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place);
}
}
}
}
patch.apply(mir);
}
}

impl RestoreSubsliceArrayMoveOut {
// Checks that source has size, all locals are inited from same source place and
// indices is an integer interval. If all checks pass do the replacent.
// items are Vec<Option<LocalUse, index in source array, source place for init local>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be nice to put something like:

items are `Vec<Option<...>>`

to help people parse what's going on here.

fn check_and_patch<'tcx>(candidate: Location,
items: &Vec<Option<(&LocalUse, u32, &Place<'tcx>)>>,
opt_size: Option<u64>,
patch: &mut MirPatch<'tcx>,
dst_place: &Place<'tcx>) {
let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);

if opt_size.is_some() && items.iter().all(
|l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {

let indicies: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indices

for i in 1..indicies.len() {
if indicies[i - 1] + 1 != indicies[i] {
return;
}
}

let min = *indicies.first().unwrap();
let max = *indicies.last().unwrap();

for item in items {
let locals_use = item.unwrap().0;
patch.make_nop(locals_use.alive.unwrap());
patch.make_nop(locals_use.dead.unwrap());
patch.make_nop(locals_use.first_use.unwrap());
}
patch.make_nop(candidate);
let size = opt_size.unwrap() as u32;
patch.add_assign(candidate,
dst_place.clone(),
Rvalue::Use(
Operand::Move(
Place::Projection(box PlaceProjection{
base: opt_src_place.unwrap().clone(),
elem: ProjectionElem::Subslice{
from: min, to: size - max - 1}}))));
}
}

fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse,
mir: &'a Mir<'tcx>) -> Option<(u32, &'a Place<'tcx>)> {
if let Some(location) = local_use.first_use {
let block = &mir[location.block];
if block.statements.len() > location.statement_index {
let statement = &block.statements[location.statement_index];
if let StatementKind::Assign(
Place::Local(_),
Rvalue::Use(Operand::Move(Place::Projection(box PlaceProjection{
ref base, elem: ProjectionElem::ConstantIndex{
offset, min_length: _, from_end: false}})))) = statement.kind {
return Some((offset, base))
}
}
}
None
}
}

#[derive(Copy, Clone, Debug)]
struct LocalUse {
alive: Option<Location>,
dead: Option<Location>,
use_count: u32,
first_use: Option<Location>,
}

impl LocalUse {
pub fn new() -> Self {
LocalUse{alive: None, dead: None, use_count: 0, first_use: None}
}
}

struct RestoreDataCollector {
locals_use: IndexVec<Local, LocalUse>,
candidates: Vec<Location>,
}

impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
fn visit_assign(&mut self,
block: BasicBlock,
place: &Place<'tcx>,
rvalue: &Rvalue<'tcx>,
location: Location) {
if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue {
self.candidates.push(location);
}
self.super_assign(block, place, rvalue, location)
}

fn visit_local(&mut self,
local: &Local,
context: PlaceContext<'tcx>,
location: Location) {
let local_use = &mut self.locals_use[*local];
match context {
PlaceContext::StorageLive => local_use.alive = Some(location),
PlaceContext::StorageDead => local_use.dead = Some(location),
_ => {
local_use.use_count += 1;
if local_use.first_use.is_none() {
local_use.first_use = Some(location);
}
}
}
}
}
25 changes: 25 additions & 0 deletions src/test/mir-opt/uniform_array_move_out.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,28 @@ fn main() {
// nop;
// _0 = ();
// END rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir

// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir
// StorageLive(_6);
// StorageLive(_7);
// _7 = move _1[0 of 2];
// StorageLive(_8);
// _8 = move _1[1 of 2];
// _6 = [move _7, move _8];
// StorageDead(_7);
// StorageDead(_8);
// _0 = ();
// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir

// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir
// StorageLive(_6);
// nop;
// nop;
// nop;
// nop;
// _6 = move _1[0:];
// nop;
// nop;
// nop;
// _0 = ();
// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir