add transform for uniform array move out
This commit is contained in:
parent
6cf081c8c5
commit
31253d5557
6 changed files with 302 additions and 1 deletions
|
@ -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,
|
||||
|
|
153
src/librustc_mir/transform/uniform_array_move_out.rs
Normal file
153
src/librustc_mir/transform/uniform_array_move_out.rs
Normal file
|
@ -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 }}))));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
@ -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 {}",
|
||||
|
|
|
@ -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
src/test/mir-opt/uniform_array_move_out.rs
Normal file
59
src/test/mir-opt/uniform_array_move_out.rs
Normal file
|
@ -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
|
|
@ -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<F>(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));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue