1 // This pass converts move out from array by Subslice and
2 // ConstIndex{.., from_end: true} to ConstIndex move out(s) from begin
3 // of array. It allows detect error by mir borrowck and elaborate
4 // drops for array without additional work.
8 // let a = [ box 1,box 2, box 3];
15 // mir statement _10 = move _2[:-1]; replaced by:
17 // _12 = move _2[0 of 3];
19 // _13 = move _2[1 of 3];
20 // _10 = [move _12, move _13]
24 // and mir statement _11 = move _2[-1 of 1]; replaced by:
25 // _11 = move _2[2 of 3];
27 // FIXME: integrate this transformation to the mir build
30 use rustc::ty::TyCtxt;
32 use rustc::mir::visit::{Visitor, PlaceContext, NonUseContext};
33 use rustc_data_structures::indexed_vec::{IndexVec};
34 use crate::transform::{MirPass, MirSource};
35 use crate::util::patch::MirPatch;
37 pub struct UniformArrayMoveOut;
39 impl<'tcx> MirPass<'tcx> for UniformArrayMoveOut {
40 fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
41 let mut patch = MirPatch::new(body);
42 let param_env = tcx.param_env(src.def_id());
44 let mut visitor = UniformArrayMoveOutVisitor{body, patch: &mut patch, tcx, param_env};
45 visitor.visit_body(body);
51 struct UniformArrayMoveOutVisitor<'a, 'tcx> {
53 patch: &'a mut MirPatch<'tcx>,
55 param_env: ty::ParamEnv<'tcx>,
58 impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
59 fn visit_assign(&mut self,
60 dst_place: &Place<'tcx>,
61 rvalue: &Rvalue<'tcx>,
63 if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue {
64 if let box [proj_base @ .., elem] = &src_place.projection {
65 if let ProjectionElem::ConstantIndex{offset: _,
67 from_end: false} = elem {
68 // no need to transformation
71 Place::ty_from(&src_place.base, proj_base, self.body, self.tcx).ty;
72 if let ty::Array(item_ty, const_size) = place_ty.sty {
73 if let Some(size) = const_size.try_eval_usize(self.tcx, self.param_env) {
74 assert!(size <= u32::max_value() as u64,
75 "uniform array move out doesn't supported
76 for array bigger then u32");
81 &src_place.projection,
91 self.super_assign(dst_place, rvalue, location)
95 impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
98 dst_place: &Place<'tcx>,
99 base: &PlaceBase<'tcx>,
100 proj: &[PlaceElem<'tcx>],
101 item_ty: &'tcx ty::TyS<'tcx>,
103 if let [proj_base @ .., elem] = proj {
105 // uniforms statements like_10 = move _2[:-1];
106 ProjectionElem::Subslice{from, to} => {
107 self.patch.make_nop(location);
108 let temps : Vec<_> = (*from..(size-*to)).map(|i| {
110 self.patch.new_temp(item_ty, self.body.source_info(location).span);
111 self.patch.add_statement(location, StatementKind::StorageLive(temp));
113 let mut projection = proj_base.to_vec();
114 projection.push(ProjectionElem::ConstantIndex {
119 self.patch.add_assign(location,
125 projection: projection.into_boxed_slice(),
132 self.patch.add_assign(
136 box AggregateKind::Array(item_ty),
138 |x| Operand::Move(Place::from(*x))
143 self.patch.add_statement(location, StatementKind::StorageDead(temp));
146 // uniforms statements like _11 = move _2[-1 of 1];
147 ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => {
148 self.patch.make_nop(location);
150 let mut projection = proj_base.to_vec();
151 projection.push(ProjectionElem::ConstantIndex {
152 offset: size - offset,
156 self.patch.add_assign(location,
162 projection: projection.into_boxed_slice(),
174 // Restore Subslice move out after analysis
179 // _12 = move _2[0 of 3];
181 // _13 = move _2[1 of 3];
182 // _10 = [move _12, move _13]
186 // replaced by _10 = move _2[:-1];
188 pub struct RestoreSubsliceArrayMoveOut;
190 impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut {
191 fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
192 let mut patch = MirPatch::new(body);
193 let param_env = tcx.param_env(src.def_id());
195 let mut visitor = RestoreDataCollector {
196 locals_use: IndexVec::from_elem(LocalUse::new(), &body.local_decls),
199 visitor.visit_body(body);
201 for candidate in &visitor.candidates {
202 let statement = &body[candidate.block].statements[candidate.statement_index];
203 if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind {
204 if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = **rval {
205 let items : Vec<_> = items.iter().map(|item| {
206 if let Operand::Move(Place {
207 base: PlaceBase::Local(local),
210 let local_use = &visitor.locals_use[*local];
211 let opt_index_and_place =
212 Self::try_get_item_source(local_use, body);
213 // each local should be used twice:
214 // in assign and in aggregate statements
215 if local_use.use_count == 2 && opt_index_and_place.is_some() {
216 let (index, src_place) = opt_index_and_place.unwrap();
217 return Some((local_use, index, src_place));
223 let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
224 let opt_size = opt_src_place.and_then(|src_place| {
226 Place::ty_from(src_place.base, src_place.projection, body, tcx).ty;
227 if let ty::Array(_, ref size_o) = src_ty.sty {
228 size_o.try_eval_usize(tcx, param_env)
233 Self::check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place);
242 impl RestoreSubsliceArrayMoveOut {
243 // Checks that source has size, all locals are inited from same source place and
244 // indices is an integer interval. If all checks pass do the replacent.
245 // items are Vec<Option<LocalUse, index in source array, source place for init local>>
246 fn check_and_patch<'tcx>(candidate: Location,
247 items: &[Option<(&LocalUse, u32, PlaceRef<'_, 'tcx>)>],
248 opt_size: Option<u64>,
249 patch: &mut MirPatch<'tcx>,
250 dst_place: &Place<'tcx>) {
251 let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
253 if opt_size.is_some() && items.iter().all(
254 |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {
255 let src_place = opt_src_place.unwrap();
257 let indices: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
258 for i in 1..indices.len() {
259 if indices[i - 1] + 1 != indices[i] {
264 let min = *indices.first().unwrap();
265 let max = *indices.last().unwrap();
268 let locals_use = item.unwrap().0;
269 patch.make_nop(locals_use.alive.unwrap());
270 patch.make_nop(locals_use.dead.unwrap());
271 patch.make_nop(locals_use.first_use.unwrap());
273 patch.make_nop(candidate);
274 let size = opt_size.unwrap() as u32;
276 let mut projection = src_place.projection.to_vec();
277 projection.push(ProjectionElem::Subslice { from: min, to: size - max - 1 });
281 Rvalue::Use(Operand::Move(Place {
282 base: src_place.base.clone(),
283 projection: projection.into_boxed_slice(),
289 fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse,
290 body: &'a Body<'tcx>) -> Option<(u32, PlaceRef<'a, 'tcx>)> {
291 if let Some(location) = local_use.first_use {
292 let block = &body[location.block];
293 if block.statements.len() > location.statement_index {
294 let statement = &block.statements[location.statement_index];
295 if let StatementKind::Assign(
297 base: PlaceBase::Local(_),
300 box Rvalue::Use(Operand::Move(Place {
302 projection: box [.., ProjectionElem::ConstantIndex {
303 offset, min_length: _, from_end: false
306 ) = &statement.kind {
307 // FIXME remove once we can use slices patterns
308 if let StatementKind::Assign(
310 box Rvalue::Use(Operand::Move(Place {
312 projection: box [proj_base @ .., _],
314 ) = &statement.kind {
315 return Some((*offset, PlaceRef {
317 projection: proj_base,
327 #[derive(Copy, Clone, Debug)]
329 alive: Option<Location>,
330 dead: Option<Location>,
332 first_use: Option<Location>,
336 pub fn new() -> Self {
337 LocalUse{alive: None, dead: None, use_count: 0, first_use: None}
341 struct RestoreDataCollector {
342 locals_use: IndexVec<Local, LocalUse>,
343 candidates: Vec<Location>,
346 impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
347 fn visit_assign(&mut self,
349 rvalue: &Rvalue<'tcx>,
350 location: Location) {
351 if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue {
352 self.candidates.push(location);
354 self.super_assign(place, rvalue, location)
357 fn visit_local(&mut self,
359 context: PlaceContext,
360 location: Location) {
361 let local_use = &mut self.locals_use[*local];
363 PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location),
364 PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location),
366 local_use.use_count += 1;
367 if local_use.first_use.is_none() {
368 local_use.first_use = Some(location);