Skip to content

Commit f59ab8e

Browse files
committed
Rollup merge of rust-lang#48355 - mikhail-m1:subslice_pattern_array_drop2, r=nikomatsakis
Fixes rust-lang#47311. r? @nrc
2 parents 0ff9872 + 12c7e27 commit f59ab8e

File tree

3 files changed

+213
-11
lines changed

3 files changed

+213
-11
lines changed

src/librustc_mir/transform/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
257257

258258

259259
// Optimizations begin.
260+
uniform_array_move_out::RestoreSubsliceArrayMoveOut,
260261
inline::Inline,
261262

262263
// Lowering generator control-flow and variables

src/librustc_mir/transform/uniform_array_move_out.rs

+187-11
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@
3434
// and mir statement _11 = move _2[-1 of 1]; replaced by:
3535
// _11 = move _2[2 of 3];
3636
//
37-
// FIXME: convert to Subslice back for performance reason
3837
// FIXME: integrate this transformation to the mir build
3938

4039
use rustc::ty;
4140
use rustc::ty::TyCtxt;
4241
use rustc::mir::*;
43-
use rustc::mir::visit::Visitor;
42+
use rustc::mir::visit::{Visitor, PlaceContext};
4443
use transform::{MirPass, MirSource};
4544
use util::patch::MirPatch;
45+
use rustc_data_structures::indexed_vec::{IndexVec};
4646

4747
pub struct UniformArrayMoveOut;
4848

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

6969
impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
70-
fn visit_statement(&mut self,
71-
block: BasicBlock,
72-
statement: &Statement<'tcx>,
73-
location: Location) {
74-
if let StatementKind::Assign(ref dst_place,
75-
Rvalue::Use(Operand::Move(ref src_place))) = statement.kind {
70+
fn visit_assign(&mut self,
71+
block: BasicBlock,
72+
dst_place: &Place<'tcx>,
73+
rvalue: &Rvalue<'tcx>,
74+
location: Location) {
75+
if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue {
7676
if let Place::Projection(ref proj) = *src_place {
7777
if let ProjectionElem::ConstantIndex{offset: _,
7878
min_length: _,
@@ -92,7 +92,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
9292
}
9393
}
9494
}
95-
return self.super_statement(block, statement, location);
95+
self.super_assign(block, dst_place, rvalue, location)
9696
}
9797
}
9898

@@ -104,7 +104,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
104104
item_ty: &'tcx ty::TyS<'tcx>,
105105
size: u32) {
106106
match proj.elem {
107-
// uniform _10 = move _2[:-1];
107+
// uniforms statements like_10 = move _2[:-1];
108108
ProjectionElem::Subslice{from, to} => {
109109
self.patch.make_nop(location);
110110
let temps : Vec<_> = (from..(size-to)).map(|i| {
@@ -133,7 +133,7 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
133133
self.patch.add_statement(location, StatementKind::StorageDead(temp));
134134
}
135135
}
136-
// _11 = move _2[-1 of 1];
136+
// uniforms statements like _11 = move _2[-1 of 1];
137137
ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => {
138138
self.patch.make_nop(location);
139139
self.patch.add_assign(location,
@@ -151,3 +151,179 @@ impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
151151
}
152152
}
153153
}
154+
155+
// Restore Subslice move out after analysis
156+
// Example:
157+
//
158+
// next statements:
159+
// StorageLive(_12);
160+
// _12 = move _2[0 of 3];
161+
// StorageLive(_13);
162+
// _13 = move _2[1 of 3];
163+
// _10 = [move _12, move _13]
164+
// StorageDead(_12);
165+
// StorageDead(_13);
166+
//
167+
// replaced by _10 = move _2[:-1];
168+
169+
pub struct RestoreSubsliceArrayMoveOut;
170+
171+
impl MirPass for RestoreSubsliceArrayMoveOut {
172+
fn run_pass<'a, 'tcx>(&self,
173+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
174+
_src: MirSource,
175+
mir: &mut Mir<'tcx>) {
176+
let mut patch = MirPatch::new(mir);
177+
{
178+
let mut visitor = RestoreDataCollector {
179+
locals_use: IndexVec::from_elem(LocalUse::new(), &mir.local_decls),
180+
candidates: vec![],
181+
};
182+
visitor.visit_mir(mir);
183+
184+
for candidate in &visitor.candidates {
185+
let statement = &mir[candidate.block].statements[candidate.statement_index];
186+
if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind {
187+
if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval {
188+
let items : Vec<_> = items.iter().map(|item| {
189+
if let Operand::Move(Place::Local(local)) = item {
190+
let local_use = &visitor.locals_use[*local];
191+
let opt_index_and_place = Self::try_get_item_source(local_use, mir);
192+
// each local should be used twice:
193+
// in assign and in aggregate statments
194+
if local_use.use_count == 2 && opt_index_and_place.is_some() {
195+
let (index, src_place) = opt_index_and_place.unwrap();
196+
return Some((local_use, index, src_place));
197+
}
198+
}
199+
None
200+
}).collect();
201+
202+
let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
203+
let opt_size = opt_src_place.and_then(|src_place| {
204+
let src_ty = src_place.ty(mir, tcx).to_ty(tcx);
205+
if let ty::TyArray(_, ref size_o) = src_ty.sty {
206+
size_o.val.to_const_int().and_then(|v| v.to_u64())
207+
} else {
208+
None
209+
}
210+
});
211+
Self::check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place);
212+
}
213+
}
214+
}
215+
}
216+
patch.apply(mir);
217+
}
218+
}
219+
220+
impl RestoreSubsliceArrayMoveOut {
221+
// Checks that source has size, all locals are inited from same source place and
222+
// indices is an integer interval. If all checks pass do the replacent.
223+
// items are Vec<Option<LocalUse, index in source array, source place for init local>>
224+
fn check_and_patch<'tcx>(candidate: Location,
225+
items: &Vec<Option<(&LocalUse, u32, &Place<'tcx>)>>,
226+
opt_size: Option<u64>,
227+
patch: &mut MirPatch<'tcx>,
228+
dst_place: &Place<'tcx>) {
229+
let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
230+
231+
if opt_size.is_some() && items.iter().all(
232+
|l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {
233+
234+
let indicies: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
235+
for i in 1..indicies.len() {
236+
if indicies[i - 1] + 1 != indicies[i] {
237+
return;
238+
}
239+
}
240+
241+
let min = *indicies.first().unwrap();
242+
let max = *indicies.last().unwrap();
243+
244+
for item in items {
245+
let locals_use = item.unwrap().0;
246+
patch.make_nop(locals_use.alive.unwrap());
247+
patch.make_nop(locals_use.dead.unwrap());
248+
patch.make_nop(locals_use.first_use.unwrap());
249+
}
250+
patch.make_nop(candidate);
251+
let size = opt_size.unwrap() as u32;
252+
patch.add_assign(candidate,
253+
dst_place.clone(),
254+
Rvalue::Use(
255+
Operand::Move(
256+
Place::Projection(box PlaceProjection{
257+
base: opt_src_place.unwrap().clone(),
258+
elem: ProjectionElem::Subslice{
259+
from: min, to: size - max - 1}}))));
260+
}
261+
}
262+
263+
fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse,
264+
mir: &'a Mir<'tcx>) -> Option<(u32, &'a Place<'tcx>)> {
265+
if let Some(location) = local_use.first_use {
266+
let block = &mir[location.block];
267+
if block.statements.len() > location.statement_index {
268+
let statement = &block.statements[location.statement_index];
269+
if let StatementKind::Assign(
270+
Place::Local(_),
271+
Rvalue::Use(Operand::Move(Place::Projection(box PlaceProjection{
272+
ref base, elem: ProjectionElem::ConstantIndex{
273+
offset, min_length: _, from_end: false}})))) = statement.kind {
274+
return Some((offset, base))
275+
}
276+
}
277+
}
278+
None
279+
}
280+
}
281+
282+
#[derive(Copy, Clone, Debug)]
283+
struct LocalUse {
284+
alive: Option<Location>,
285+
dead: Option<Location>,
286+
use_count: u32,
287+
first_use: Option<Location>,
288+
}
289+
290+
impl LocalUse {
291+
pub fn new() -> Self {
292+
LocalUse{alive: None, dead: None, use_count: 0, first_use: None}
293+
}
294+
}
295+
296+
struct RestoreDataCollector {
297+
locals_use: IndexVec<Local, LocalUse>,
298+
candidates: Vec<Location>,
299+
}
300+
301+
impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
302+
fn visit_assign(&mut self,
303+
block: BasicBlock,
304+
place: &Place<'tcx>,
305+
rvalue: &Rvalue<'tcx>,
306+
location: Location) {
307+
if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue {
308+
self.candidates.push(location);
309+
}
310+
self.super_assign(block, place, rvalue, location)
311+
}
312+
313+
fn visit_local(&mut self,
314+
local: &Local,
315+
context: PlaceContext<'tcx>,
316+
location: Location) {
317+
let local_use = &mut self.locals_use[*local];
318+
match context {
319+
PlaceContext::StorageLive => local_use.alive = Some(location),
320+
PlaceContext::StorageDead => local_use.dead = Some(location),
321+
_ => {
322+
local_use.use_count += 1;
323+
if local_use.first_use.is_none() {
324+
local_use.first_use = Some(location);
325+
}
326+
}
327+
}
328+
}
329+
}

src/test/mir-opt/uniform_array_move_out.rs

+25
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,28 @@ fn main() {
5757
// nop;
5858
// _0 = ();
5959
// END rustc.move_out_by_subslice.UniformArrayMoveOut.after.mir
60+
61+
// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir
62+
// StorageLive(_6);
63+
// StorageLive(_7);
64+
// _7 = move _1[0 of 2];
65+
// StorageLive(_8);
66+
// _8 = move _1[1 of 2];
67+
// _6 = [move _7, move _8];
68+
// StorageDead(_7);
69+
// StorageDead(_8);
70+
// _0 = ();
71+
// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.before.mir
72+
73+
// START rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir
74+
// StorageLive(_6);
75+
// nop;
76+
// nop;
77+
// nop;
78+
// nop;
79+
// _6 = move _1[0:];
80+
// nop;
81+
// nop;
82+
// nop;
83+
// _0 = ();
84+
// END rustc.move_out_by_subslice.RestoreSubsliceArrayMoveOut.after.mir

0 commit comments

Comments
 (0)