From 12c7e273309e7fb3c8309e0d6217d96baa7cbc89 Mon Sep 17 00:00:00 2001 From: Mikhail Modin Date: Mon, 19 Feb 2018 19:42:00 +0300 Subject: [PATCH] restore Subslice move out from array after elaborate drops and borrowck --- src/librustc_mir/transform/mod.rs | 1 + .../transform/uniform_array_move_out.rs | 198 +++++++++++++++++- src/test/mir-opt/uniform_array_move_out.rs | 25 +++ 3 files changed, 213 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 7ed250e94c52f..f721cdf714dca 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -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, diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs index 0db5ecf0eb270..e46de17047986 100644 --- a/src/librustc_mir/transform/uniform_array_move_out.rs +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -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; @@ -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: _, @@ -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) } } @@ -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| { @@ -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, @@ -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> + fn check_and_patch<'tcx>(candidate: Location, + items: &Vec)>>, + opt_size: Option, + 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(); + 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, + dead: Option, + use_count: u32, + first_use: Option, +} + +impl LocalUse { + pub fn new() -> Self { + LocalUse{alive: None, dead: None, use_count: 0, first_use: None} + } +} + +struct RestoreDataCollector { + locals_use: IndexVec, + candidates: Vec, +} + +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); + } + } + } + } +} diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs index 4a310255aac57..482b69a59ddbc 100644 --- a/src/test/mir-opt/uniform_array_move_out.rs +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -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