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
4039use rustc:: ty;
4140use rustc:: ty:: TyCtxt ;
4241use rustc:: mir:: * ;
43- use rustc:: mir:: visit:: Visitor ;
42+ use rustc:: mir:: visit:: { Visitor , PlaceContext } ;
4443use transform:: { MirPass , MirSource } ;
4544use util:: patch:: MirPatch ;
45+ use rustc_data_structures:: indexed_vec:: { IndexVec } ;
4646
4747pub struct UniformArrayMoveOut ;
4848
@@ -67,12 +67,12 @@ struct UniformArrayMoveOutVisitor<'a, 'tcx: 'a> {
6767}
6868
6969impl < ' 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+ }
0 commit comments