Skip to content

Commit

Permalink
Auto merge of #47926 - mikhail-m1:subslice_pattern_array_drop2, r=nik…
Browse files Browse the repository at this point in the history
…omatsakis

add transform for uniform array move out

reworked second step for fix #34708
previous try #46686
r? @nikomatsakis
  • Loading branch information
bors committed Feb 17, 2018
2 parents 507a46a + 31253d5 commit b85bd51
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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,
Expand Down
153 changes: 153 additions & 0 deletions src/librustc_mir/transform/uniform_array_move_out.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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 }}))));
}
_ => {}
}
}
}
12 changes: 11 additions & 1 deletion src/librustc_mir/util/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct MirPatch<'tcx> {
new_locals: Vec<LocalDecl<'tcx>>,
resume_block: BasicBlock,
next_local: usize,
make_nop: Vec<Location>,
}

impl<'tcx> MirPatch<'tcx> {
Expand All @@ -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
Expand Down Expand Up @@ -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 {}",
Expand Down
30 changes: 30 additions & 0 deletions src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

// 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() {}
59 changes: 59 additions & 0 deletions src/test/mir-opt/uniform_array_move_out.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

#![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
46 changes: 46 additions & 0 deletions src/test/run-pass/dynamic-drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,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<F>(mut f: F)
where F: FnMut(&Allocator)
{
Expand Down Expand Up @@ -303,5 +340,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));
}

0 comments on commit b85bd51

Please sign in to comment.