blob: fccd4a607fdcf1cd2e9a520b7ee0efe7b2d5cecb [file] [log] [blame]
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::ty::TyCtxt;
use rustc::mir::repr::*;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::VariantKind;
pub struct Deaggregator;
impl Pass for Deaggregator {}
impl<'tcx> MirPass<'tcx> for Deaggregator {
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: MirSource, mir: &mut Mir<'tcx>) {
let node_id = source.item_id();
let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
debug!("running on: {:?}", node_path);
// we only run when mir_opt_level > 1
match tcx.sess.opts.debugging_opts.mir_opt_level {
Some(0) |
Some(1) |
None => { return; },
_ => {}
};
// Do not trigger on constants. Could be revised in future
if let MirSource::Fn(_) = source {} else { return; }
// In fact, we might not want to trigger in other cases.
// Ex: when we could use SROA. See issue #35259
let mut curr: usize = 0;
for bb in mir.basic_blocks_mut() {
let idx = match get_aggregate_statement(curr, &bb.statements) {
Some(idx) => idx,
None => continue,
};
// do the replacement
debug!("removing statement {:?}", idx);
let src_info = bb.statements[idx].source_info;
let suffix_stmts = bb.statements.split_off(idx+1);
let orig_stmt = bb.statements.pop().unwrap();
let StatementKind::Assign(ref lhs, ref rhs) = orig_stmt.kind;
let (agg_kind, operands) = match rhs {
&Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
_ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
};
let (adt_def, variant, substs) = match agg_kind {
&AggregateKind::Adt(adt_def, variant, substs) => (adt_def, variant, substs),
_ => span_bug!(src_info.span, "expected struct, not {:?}", rhs),
};
let n = bb.statements.len();
bb.statements.reserve(n + operands.len() + suffix_stmts.len());
for (i, op) in operands.iter().enumerate() {
let ref variant_def = adt_def.variants[variant];
let ty = variant_def.fields[i].ty(tcx, substs);
let rhs = Rvalue::Use(op.clone());
// since we don't handle enums, we don't need a cast
let lhs_cast = lhs.clone();
// FIXME we cannot deaggregate enums issue: #35186
let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
base: lhs_cast,
elem: ProjectionElem::Field(Field::new(i), ty),
}));
let new_statement = Statement {
source_info: src_info,
kind: StatementKind::Assign(lhs_proj, rhs),
};
debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
bb.statements.push(new_statement);
}
curr = bb.statements.len();
bb.statements.extend(suffix_stmts);
}
}
}
fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize,
statements: &Vec<Statement<'tcx>>)
-> Option<usize> {
for i in curr..statements.len() {
let ref statement = statements[i];
let StatementKind::Assign(_, ref rhs) = statement.kind;
let (kind, operands) = match rhs {
&Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
_ => continue,
};
let (adt_def, variant) = match kind {
&AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
_ => continue,
};
if operands.len() == 0 || adt_def.variants.len() > 1 {
// don't deaggregate ()
// don't deaggregate enums ... for now
continue;
}
debug!("getting variant {:?}", variant);
debug!("for adt_def {:?}", adt_def);
let variant_def = &adt_def.variants[variant];
if variant_def.kind == VariantKind::Struct {
return Some(i);
}
};
None
}