]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/uniform_array_move_out.rs
Changes the type `mir::Mir` into `mir::Body`
[rust.git] / src / librustc_mir / transform / uniform_array_move_out.rs
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.
5 //
6 // Example:
7 //
8 // let a = [ box 1,box 2, box 3];
9 // if b {
10 //  let [_a.., _] = a;
11 // } else {
12 //  let [.., _b] = a;
13 // }
14 //
15 //  mir statement _10 = move _2[:-1]; replaced by:
16 //  StorageLive(_12);
17 //  _12 = move _2[0 of 3];
18 //  StorageLive(_13);
19 //  _13 = move _2[1 of 3];
20 //  _10 = [move _12, move _13]
21 //  StorageDead(_12);
22 //  StorageDead(_13);
23 //
24 //  and mir statement _11 = move _2[-1 of 1]; replaced by:
25 //  _11 = move _2[2 of 3];
26 //
27 // FIXME: integrate this transformation to the mir build
28
29 use rustc::ty;
30 use rustc::ty::TyCtxt;
31 use rustc::mir::*;
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;
36
37 pub struct UniformArrayMoveOut;
38
39 impl MirPass for UniformArrayMoveOut {
40     fn run_pass<'a, 'tcx>(&self,
41                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
42                           _src: MirSource<'tcx>,
43                           mir: &mut Body<'tcx>) {
44         let mut patch = MirPatch::new(mir);
45         {
46             let mut visitor = UniformArrayMoveOutVisitor{mir, patch: &mut patch, tcx};
47             visitor.visit_body(mir);
48         }
49         patch.apply(mir);
50     }
51 }
52
53 struct UniformArrayMoveOutVisitor<'a, 'tcx: 'a> {
54     mir: &'a Body<'tcx>,
55     patch: &'a mut MirPatch<'tcx>,
56     tcx: TyCtxt<'a, 'tcx, 'tcx>,
57 }
58
59 impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
60     fn visit_assign(&mut self,
61                     dst_place: &Place<'tcx>,
62                     rvalue: &Rvalue<'tcx>,
63                     location: Location) {
64         if let Rvalue::Use(Operand::Move(ref src_place)) = rvalue {
65             if let Place::Projection(ref proj) = *src_place {
66                 if let ProjectionElem::ConstantIndex{offset: _,
67                                                      min_length: _,
68                                                      from_end: false} = proj.elem {
69                     // no need to transformation
70                 } else {
71                     let place_ty = proj.base.ty(self.mir, self.tcx).ty;
72                     if let ty::Array(item_ty, const_size) = place_ty.sty {
73                         if let Some(size) = const_size.assert_usize(self.tcx) {
74                             assert!(size <= u32::max_value() as u64,
75                                     "uniform array move out doesn't supported
76                                      for array bigger then u32");
77                             self.uniform(location, dst_place, proj, item_ty, size as u32);
78                         }
79                     }
80
81                 }
82             }
83         }
84         self.super_assign(dst_place, rvalue, location)
85     }
86 }
87
88 impl<'a, 'tcx> UniformArrayMoveOutVisitor<'a, 'tcx> {
89     fn uniform(&mut self,
90                location: Location,
91                dst_place: &Place<'tcx>,
92                proj: &Projection<'tcx>,
93                item_ty: &'tcx ty::TyS<'tcx>,
94                size: u32) {
95         match proj.elem {
96             // uniforms statements like_10 = move _2[:-1];
97             ProjectionElem::Subslice{from, to} => {
98                 self.patch.make_nop(location);
99                 let temps : Vec<_> = (from..(size-to)).map(|i| {
100                     let temp = self.patch.new_temp(item_ty, self.mir.source_info(location).span);
101                     self.patch.add_statement(location, StatementKind::StorageLive(temp));
102                     self.patch.add_assign(location,
103                                           Place::Base(PlaceBase::Local(temp)),
104                                           Rvalue::Use(
105                                               Operand::Move(
106                                                   Place::Projection(box Projection{
107                                                       base: proj.base.clone(),
108                                                       elem: ProjectionElem::ConstantIndex{
109                                                           offset: i,
110                                                           min_length: size,
111                                                           from_end: false}
112                                                   }))));
113                     temp
114                 }).collect();
115                 self.patch.add_assign(
116                     location,
117                     dst_place.clone(),
118                     Rvalue::Aggregate(
119                         box AggregateKind::Array(item_ty),
120                         temps.iter().map(
121                             |x| Operand::Move(Place::Base(PlaceBase::Local(*x)))
122                         ).collect()
123                     )
124                 );
125                 for temp in temps {
126                     self.patch.add_statement(location, StatementKind::StorageDead(temp));
127                 }
128             }
129             // uniforms statements like _11 = move _2[-1 of 1];
130             ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => {
131                 self.patch.make_nop(location);
132                 self.patch.add_assign(location,
133                                       dst_place.clone(),
134                                       Rvalue::Use(
135                                           Operand::Move(
136                                               Place::Projection(box Projection{
137                                                   base: proj.base.clone(),
138                                                   elem: ProjectionElem::ConstantIndex{
139                                                       offset: size - offset,
140                                                       min_length: size,
141                                                       from_end: false }}))));
142             }
143             _ => {}
144         }
145     }
146 }
147
148 // Restore Subslice move out after analysis
149 // Example:
150 //
151 //  next statements:
152 //   StorageLive(_12);
153 //   _12 = move _2[0 of 3];
154 //   StorageLive(_13);
155 //   _13 = move _2[1 of 3];
156 //   _10 = [move _12, move _13]
157 //   StorageDead(_12);
158 //   StorageDead(_13);
159 //
160 // replaced by _10 = move _2[:-1];
161
162 pub struct RestoreSubsliceArrayMoveOut;
163
164 impl MirPass for RestoreSubsliceArrayMoveOut {
165     fn run_pass<'a, 'tcx>(&self,
166                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
167                           _src: MirSource<'tcx>,
168                           mir: &mut Body<'tcx>) {
169         let mut patch = MirPatch::new(mir);
170         {
171             let mut visitor = RestoreDataCollector {
172                 locals_use: IndexVec::from_elem(LocalUse::new(), &mir.local_decls),
173                 candidates: vec![],
174             };
175             visitor.visit_body(mir);
176
177             for candidate in &visitor.candidates {
178                 let statement = &mir[candidate.block].statements[candidate.statement_index];
179                 if let StatementKind::Assign(ref dst_place, ref rval) = statement.kind {
180                     if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = **rval {
181                         let items : Vec<_> = items.iter().map(|item| {
182                             if let Operand::Move(Place::Base(PlaceBase::Local(local))) = item {
183                                 let local_use = &visitor.locals_use[*local];
184                                 let opt_index_and_place = Self::try_get_item_source(local_use, mir);
185                                 // each local should be used twice:
186                                 //  in assign and in aggregate statements
187                                 if local_use.use_count == 2 && opt_index_and_place.is_some() {
188                                     let (index, src_place) = opt_index_and_place.unwrap();
189                                     return Some((local_use, index, src_place));
190                                 }
191                             }
192                             None
193                         }).collect();
194
195                         let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
196                         let opt_size = opt_src_place.and_then(|src_place| {
197                             let src_ty = src_place.ty(mir, tcx).ty;
198                             if let ty::Array(_, ref size_o) = src_ty.sty {
199                                 size_o.assert_usize(tcx)
200                             } else {
201                                 None
202                             }
203                         });
204                         Self::check_and_patch(*candidate, &items, opt_size, &mut patch, dst_place);
205                     }
206                 }
207             }
208         }
209         patch.apply(mir);
210     }
211 }
212
213 impl RestoreSubsliceArrayMoveOut {
214     // Checks that source has size, all locals are inited from same source place and
215     // indices is an integer interval. If all checks pass do the replacent.
216     // items are Vec<Option<LocalUse, index in source array, source place for init local>>
217     fn check_and_patch<'tcx>(candidate: Location,
218                              items: &[Option<(&LocalUse, u32, &Place<'tcx>)>],
219                              opt_size: Option<u64>,
220                              patch: &mut MirPatch<'tcx>,
221                              dst_place: &Place<'tcx>) {
222         let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
223
224         if opt_size.is_some() && items.iter().all(
225             |l| l.is_some() && l.unwrap().2 == opt_src_place.unwrap()) {
226
227             let indices: Vec<_> = items.iter().map(|x| x.unwrap().1).collect();
228             for i in 1..indices.len() {
229                 if indices[i - 1] + 1 != indices[i] {
230                     return;
231                 }
232             }
233
234             let min = *indices.first().unwrap();
235             let max = *indices.last().unwrap();
236
237             for item in items {
238                 let locals_use = item.unwrap().0;
239                 patch.make_nop(locals_use.alive.unwrap());
240                 patch.make_nop(locals_use.dead.unwrap());
241                 patch.make_nop(locals_use.first_use.unwrap());
242             }
243             patch.make_nop(candidate);
244             let size = opt_size.unwrap() as u32;
245             patch.add_assign(candidate,
246                              dst_place.clone(),
247                              Rvalue::Use(
248                                  Operand::Move(
249                                      Place::Projection(box Projection{
250                                          base: opt_src_place.unwrap().clone(),
251                                          elem: ProjectionElem::Subslice{
252                                              from: min, to: size - max - 1}}))));
253         }
254     }
255
256     fn try_get_item_source<'a, 'tcx>(local_use: &LocalUse,
257                                      mir: &'a Body<'tcx>) -> Option<(u32, &'a Place<'tcx>)> {
258         if let Some(location) = local_use.first_use {
259             let block = &mir[location.block];
260             if block.statements.len() > location.statement_index {
261                 let statement = &block.statements[location.statement_index];
262                 if let StatementKind::Assign(
263                     Place::Base(PlaceBase::Local(_)),
264                     box Rvalue::Use(Operand::Move(Place::Projection(box Projection{
265                         ref base, elem: ProjectionElem::ConstantIndex{
266                             offset, min_length: _, from_end: false}})))) = statement.kind {
267                     return Some((offset, base))
268                 }
269             }
270         }
271         None
272     }
273 }
274
275 #[derive(Copy, Clone, Debug)]
276 struct LocalUse {
277     alive: Option<Location>,
278     dead: Option<Location>,
279     use_count: u32,
280     first_use: Option<Location>,
281 }
282
283 impl LocalUse {
284     pub fn new() -> Self {
285         LocalUse{alive: None, dead: None, use_count: 0, first_use: None}
286     }
287 }
288
289 struct RestoreDataCollector {
290     locals_use: IndexVec<Local, LocalUse>,
291     candidates: Vec<Location>,
292 }
293
294 impl<'tcx> Visitor<'tcx> for RestoreDataCollector {
295     fn visit_assign(&mut self,
296                     place: &Place<'tcx>,
297                     rvalue: &Rvalue<'tcx>,
298                     location: Location) {
299         if let Rvalue::Aggregate(box AggregateKind::Array(_), _) = *rvalue {
300             self.candidates.push(location);
301         }
302         self.super_assign(place, rvalue, location)
303     }
304
305     fn visit_local(&mut self,
306                    local: &Local,
307                    context: PlaceContext,
308                    location: Location) {
309         let local_use = &mut self.locals_use[*local];
310         match context {
311             PlaceContext::NonUse(NonUseContext::StorageLive) => local_use.alive = Some(location),
312             PlaceContext::NonUse(NonUseContext::StorageDead) => local_use.dead = Some(location),
313             _ => {
314                 local_use.use_count += 1;
315                 if local_use.first_use.is_none() {
316                     local_use.first_use = Some(location);
317                 }
318             }
319         }
320     }
321 }