]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/deaggregator.rs
3a93bef36c5f77c958f21ac9918ac6e15774cbbd
[rust.git] / src / librustc_mir / transform / deaggregator.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::ty::TyCtxt;
12 use rustc::mir::*;
13 use rustc::mir::transform::{MirPass, MirSource, Pass};
14 use rustc_data_structures::indexed_vec::Idx;
15
16 pub struct Deaggregator;
17
18 impl Pass for Deaggregator {}
19
20 impl<'tcx> MirPass<'tcx> for Deaggregator {
21     fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
22                     source: MirSource, mir: &mut Mir<'tcx>) {
23         let node_id = source.item_id();
24         let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
25         debug!("running on: {:?}", node_path);
26         // we only run when mir_opt_level > 2
27         if tcx.sess.opts.debugging_opts.mir_opt_level <= 2 {
28             return;
29         }
30
31         // Do not trigger on constants.  Could be revised in future
32         if let MirSource::Fn(_) = source {} else { return; }
33         // In fact, we might not want to trigger in other cases.
34         // Ex: when we could use SROA.  See issue #35259
35
36         for bb in mir.basic_blocks_mut() {
37             let mut curr: usize = 0;
38             while let Some(idx) = get_aggregate_statement_index(curr, &bb.statements) {
39                 // do the replacement
40                 debug!("removing statement {:?}", idx);
41                 let src_info = bb.statements[idx].source_info;
42                 let suffix_stmts = bb.statements.split_off(idx+1);
43                 let orig_stmt = bb.statements.pop().unwrap();
44                 let (lhs, rhs) = match orig_stmt.kind {
45                     StatementKind::Assign(ref lhs, ref rhs) => (lhs, rhs),
46                     _ => span_bug!(src_info.span, "expected assign, not {:?}", orig_stmt),
47                 };
48                 let (agg_kind, operands) = match rhs {
49                     &Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
50                     _ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
51                 };
52                 let (adt_def, variant, substs) = match agg_kind {
53                     &AggregateKind::Adt(adt_def, variant, substs, None)
54                         => (adt_def, variant, substs),
55                     _ => span_bug!(src_info.span, "expected struct, not {:?}", rhs),
56                 };
57                 let n = bb.statements.len();
58                 bb.statements.reserve(n + operands.len() + suffix_stmts.len());
59                 for (i, op) in operands.iter().enumerate() {
60                     let ref variant_def = adt_def.variants[variant];
61                     let ty = variant_def.fields[i].ty(tcx, substs);
62                     let rhs = Rvalue::Use(op.clone());
63
64                     let lhs_cast = if adt_def.variants.len() > 1 {
65                         Lvalue::Projection(Box::new(LvalueProjection {
66                             base: lhs.clone(),
67                             elem: ProjectionElem::Downcast(adt_def, variant),
68                         }))
69                     } else {
70                         lhs.clone()
71                     };
72
73                     let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
74                         base: lhs_cast,
75                         elem: ProjectionElem::Field(Field::new(i), ty),
76                     }));
77                     let new_statement = Statement {
78                         source_info: src_info,
79                         kind: StatementKind::Assign(lhs_proj, rhs),
80                     };
81                     debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
82                     bb.statements.push(new_statement);
83                 }
84
85                 // if the aggregate was an enum, we need to set the discriminant
86                 if adt_def.variants.len() > 1 {
87                     let set_discriminant = Statement {
88                         kind: StatementKind::SetDiscriminant {
89                             lvalue: lhs.clone(),
90                             variant_index: variant,
91                         },
92                         source_info: src_info,
93                     };
94                     bb.statements.push(set_discriminant);
95                 };
96
97                 curr = bb.statements.len();
98                 bb.statements.extend(suffix_stmts);
99             }
100         }
101     }
102 }
103
104 fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize,
105                                          statements: &Vec<Statement<'tcx>>)
106                                          -> Option<usize> {
107     for i in start..statements.len() {
108         let ref statement = statements[i];
109         let rhs = match statement.kind {
110             StatementKind::Assign(_, ref rhs) => rhs,
111             _ => continue,
112         };
113         let (kind, operands) = match rhs {
114             &Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
115             _ => continue,
116         };
117         let (adt_def, variant) = match kind {
118             &AggregateKind::Adt(adt_def, variant, _, None) => (adt_def, variant),
119             _ => continue,
120         };
121         if operands.len() == 0 {
122             // don't deaggregate ()
123             continue;
124         }
125         debug!("getting variant {:?}", variant);
126         debug!("for adt_def {:?}", adt_def);
127         return Some(i);
128     };
129     None
130 }