From 31253d5557f8c161ac97028a3966d617491b86a6 Mon Sep 17 00:00:00 2001 From: Mikhail Modin Date: Wed, 31 Jan 2018 23:34:13 +0300 Subject: [PATCH] add transform for uniform array move out --- src/librustc_mir/transform/mod.rs | 3 + .../transform/uniform_array_move_out.rs | 153 ++++++++++++++++++ src/librustc_mir/util/patch.rs | 12 +- .../borrowck/borrowck-move-out-from-array.rs | 30 ++++ src/test/mir-opt/uniform_array_move_out.rs | 59 +++++++ src/test/run-pass/dynamic-drop.rs | 46 ++++++ 6 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 src/librustc_mir/transform/uniform_array_move_out.rs create mode 100644 src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs create mode 100644 src/test/mir-opt/uniform_array_move_out.rs diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 563405fccc970..7ed250e94c52f 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -44,6 +44,7 @@ pub mod copy_prop; pub mod generator; pub mod inline; pub mod lower_128bit; +pub mod uniform_array_move_out; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -197,6 +198,7 @@ fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Stea simplify::SimplifyCfg::new("initial"), type_check::TypeckMir, rustc_peek::SanityCheck, + uniform_array_move_out::UniformArrayMoveOut, ]; tcx.alloc_steal_mir(mir) } @@ -253,6 +255,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx lower_128bit::Lower128Bit, + // Optimizations begin. inline::Inline, instcombine::InstCombine, diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs new file mode 100644 index 0000000000000..0db5ecf0eb270 --- /dev/null +++ b/src/librustc_mir/transform/uniform_array_move_out.rs @@ -0,0 +1,153 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This pass converts move out from array by Subslice and +// ConstIndex{.., from_end: true} to ConstIndex move out(s) from begin +// of array. It allows detect error by mir borrowck and elaborate +// drops for array without additional work. +// +// Example: +// +// let a = [ box 1,box 2, box 3]; +// if b { +// let [_a.., _] = a; +// } else { +// let [.., _b] = a; +// } +// +// mir statement _10 = move _2[:-1]; replaced by: +// 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); +// +// 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 transform::{MirPass, MirSource}; +use util::patch::MirPatch; + +pub struct UniformArrayMoveOut; + +impl MirPass for UniformArrayMoveOut { + 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 = UniformArrayMoveOutVisitor{mir, patch: &mut patch, tcx}; + visitor.visit_mir(mir); + } + patch.apply(mir); + } +} + +struct UniformArrayMoveOutVisitor<'a, 'tcx: 'a> { + mir: &'a Mir<'tcx>, + patch: &'a mut MirPatch<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +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 { + if let Place::Projection(ref proj) = *src_place { + if let ProjectionElem::ConstantIndex{offset: _, + min_length: _, + from_end: false} = proj.elem { + // no need to transformation + } else { + let place_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx); + if let ty::TyArray(item_ty, const_size) = place_ty.sty { + if let Some(size) = const_size.val.to_const_int().and_then(|v| v.to_u64()) { + assert!(size <= (u32::max_value() as u64), + "unform array move out doesn't supported + for array bigger then u32"); + self.uniform(location, dst_place, proj, item_ty, size as u32); + } + } + + } + } + } + return self.super_statement(block, statement, location); + } +} + +impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> { + fn uniform(&mut self, + location: Location, + dst_place: &Place<'tcx>, + proj: &PlaceProjection<'tcx>, + item_ty: &'tcx ty::TyS<'tcx>, + size: u32) { + match proj.elem { + // uniform _10 = move _2[:-1]; + ProjectionElem::Subslice{from, to} => { + self.patch.make_nop(location); + let temps : Vec<_> = (from..(size-to)).map(|i| { + let temp = self.patch.new_temp(item_ty, self.mir.source_info(location).span); + self.patch.add_statement(location, StatementKind::StorageLive(temp)); + self.patch.add_assign(location, + Place::Local(temp), + Rvalue::Use( + Operand::Move( + Place::Projection(box PlaceProjection{ + base: proj.base.clone(), + elem: ProjectionElem::ConstantIndex{ + offset: i, + min_length: size, + from_end: false} + })))); + temp + }).collect(); + self.patch.add_assign(location, + dst_place.clone(), + Rvalue::Aggregate(box AggregateKind::Array(item_ty), + temps.iter().map( + |x| Operand::Move(Place::Local(*x))).collect() + )); + for temp in temps { + self.patch.add_statement(location, StatementKind::StorageDead(temp)); + } + } + // _11 = move _2[-1 of 1]; + ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => { + self.patch.make_nop(location); + self.patch.add_assign(location, + dst_place.clone(), + Rvalue::Use( + Operand::Move( + Place::Projection(box PlaceProjection{ + base: proj.base.clone(), + elem: ProjectionElem::ConstantIndex{ + offset: size - offset, + min_length: size, + from_end: false }})))); + } + _ => {} + } + } +} diff --git a/src/librustc_mir/util/patch.rs b/src/librustc_mir/util/patch.rs index 9da593fb48e3b..f1bdcfcd22f93 100644 --- a/src/librustc_mir/util/patch.rs +++ b/src/librustc_mir/util/patch.rs @@ -23,6 +23,7 @@ pub struct MirPatch<'tcx> { new_locals: Vec>, resume_block: BasicBlock, next_local: usize, + make_nop: Vec, } impl<'tcx> MirPatch<'tcx> { @@ -33,7 +34,8 @@ impl<'tcx> MirPatch<'tcx> { new_statements: vec![], new_locals: vec![], next_local: mir.local_decls.len(), - resume_block: START_BLOCK + resume_block: START_BLOCK, + make_nop: vec![] }; // make sure the MIR we create has a resume block. It is @@ -131,7 +133,15 @@ impl<'tcx> MirPatch<'tcx> { self.add_statement(loc, StatementKind::Assign(place, rv)); } + pub fn make_nop(&mut self, loc: Location) { + self.make_nop.push(loc); + } + pub fn apply(self, mir: &mut Mir<'tcx>) { + debug!("MirPatch: make nops at: {:?}", self.make_nop); + for loc in self.make_nop { + mir.make_statement_nop(loc); + } debug!("MirPatch: {:?} new temps, starting from index {}: {:?}", self.new_locals.len(), mir.local_decls.len(), self.new_locals); debug!("MirPatch: {} new blocks, starting from index {}", diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs b/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs new file mode 100644 index 0000000000000..e011617346232 --- /dev/null +++ b/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs @@ -0,0 +1,30 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: ast mir +//[mir]compile-flags: -Z borrowck=mir + +#![feature(box_syntax, slice_patterns, advanced_slice_patterns)] + +fn move_out_from_begin_and_end() { + let a = [box 1, box 2]; + let [_, _x] = a; + let [.., _y] = a; //[ast]~ ERROR [E0382] + //[mir]~^ ERROR [E0382] +} + +fn move_out_by_const_index_and_subslice() { + let a = [box 1, box 2]; + let [_x, _] = a; + let [_y..] = a; //[ast]~ ERROR [E0382] + //[mir]~^ ERROR [E0382] +} + +fn main() {} diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs new file mode 100644 index 0000000000000..4a310255aac57 --- /dev/null +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -0,0 +1,59 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_syntax, slice_patterns, advanced_slice_patterns)] + +fn move_out_from_end() { + let a = [box 1, box 2]; + let [.., _y] = a; +} + +fn move_out_by_subslice() { + let a = [box 1, box 2]; + let [_y..] = a; +} + +fn main() { + move_out_by_subslice(); + move_out_from_end(); +} + +// END RUST SOURCE + +// START rustc.move_out_from_end.UniformArrayMoveOut.before.mir +// StorageLive(_6); +// _6 = move _1[-1 of 1]; +// _0 = (); +// END rustc.move_out_from_end.UniformArrayMoveOut.before.mir + +// START rustc.move_out_from_end.UniformArrayMoveOut.after.mir +// StorageLive(_6); +// _6 = move _1[1 of 2]; +// nop; +// _0 = (); +// END rustc.move_out_from_end.UniformArrayMoveOut.after.mir + +// START rustc.move_out_by_subslice.UniformArrayMoveOut.before.mir +// StorageLive(_6); +// _6 = move _1[0:]; +// END rustc.move_out_by_subslice.UniformArrayMoveOut.before.mir + +// START rustc.move_out_by_subslice.UniformArrayMoveOut.after.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); +// nop; +// _0 = (); +// END rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 09318e7256fd7..6a1078caec0fa 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -222,6 +222,43 @@ fn slice_pattern_one_of(a: &Allocator, i: usize) { }; } +fn subslice_pattern_from_end(a: &Allocator, arg: bool) { + let a = [a.alloc(), a.alloc(), a.alloc()]; + if arg { + let[.., _x, _] = a; + } else { + let[_, _y..] = a; + } +} + +fn subslice_pattern_from_end_with_drop(a: &Allocator, arg: bool, arg2: bool) { + let a = [a.alloc(), a.alloc(), a.alloc(), a.alloc(), a.alloc()]; + if arg2 { + drop(a); + return; + } + + if arg { + let[.., _x, _] = a; + } else { + let[_, _y..] = a; + } +} + +fn slice_pattern_reassign(a: &Allocator) { + let mut ar = [a.alloc(), a.alloc()]; + let[_, _x] = ar; + ar = [a.alloc(), a.alloc()]; + let[.., _y] = ar; +} + +fn subslice_pattern_reassign(a: &Allocator) { + let mut ar = [a.alloc(), a.alloc(), a.alloc()]; + let[_, _, _x] = ar; + ar = [a.alloc(), a.alloc(), a.alloc()]; + let[_, _y..] = ar; +} + fn run_test(mut f: F) where F: FnMut(&Allocator) { @@ -300,5 +337,14 @@ fn main() { run_test(|a| slice_pattern_one_of(a, 2)); run_test(|a| slice_pattern_one_of(a, 3)); + run_test(|a| subslice_pattern_from_end(a, true)); + run_test(|a| subslice_pattern_from_end(a, false)); + run_test(|a| subslice_pattern_from_end_with_drop(a, true, true)); + run_test(|a| subslice_pattern_from_end_with_drop(a, true, false)); + run_test(|a| subslice_pattern_from_end_with_drop(a, false, true)); + run_test(|a| subslice_pattern_from_end_with_drop(a, false, false)); + run_test(|a| slice_pattern_reassign(a)); + run_test(|a| subslice_pattern_reassign(a)); + run_test_nopanic(|a| union1(a)); }