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.
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.
11 use rustc::ty::TyCtxt;
13 use rustc::mir::transform::{MirPass, MirSource, Pass};
14 use rustc_data_structures::indexed_vec::Idx;
16 pub struct Deaggregator;
18 impl Pass for Deaggregator {}
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 {
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
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) {
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),
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),
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),
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());
64 let lhs_cast = if adt_def.variants.len() > 1 {
65 Lvalue::Projection(Box::new(LvalueProjection {
67 elem: ProjectionElem::Downcast(adt_def, variant),
73 let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
75 elem: ProjectionElem::Field(Field::new(i), ty),
77 let new_statement = Statement {
78 source_info: src_info,
79 kind: StatementKind::Assign(lhs_proj, rhs),
81 debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
82 bb.statements.push(new_statement);
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 {
90 variant_index: variant,
92 source_info: src_info,
94 bb.statements.push(set_discriminant);
97 curr = bb.statements.len();
98 bb.statements.extend(suffix_stmts);
104 fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize,
105 statements: &Vec<Statement<'tcx>>)
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,
113 let (kind, operands) = match rhs {
114 &Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
117 let (adt_def, variant) = match kind {
118 &AggregateKind::Adt(adt_def, variant, _, None) => (adt_def, variant),
121 if operands.len() == 0 {
122 // don't deaggregate ()
125 debug!("getting variant {:?}", variant);
126 debug!("for adt_def {:?}", adt_def);