Auto merge of #35168 - scottcarr:deaggregation, r=nikomatsakis
[MIR] Deaggregate structs to enable further optimizations Currently, we generate MIR like: ``` tmp0 = ...; tmp1 = ...; tmp3 = Foo { a: ..., b: ... }; ``` This PR implements "deaggregation," i.e.: ``` tmp3.0 = ... tmp3.1 = ... ``` Currently, the code only deaggregates structs, not enums. My understanding is that we do not have MIR to set the discriminant of an enum.
This commit is contained in:
commit
e804a3cf25
5 changed files with 162 additions and 0 deletions
|
@ -995,6 +995,8 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
|
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
|
||||||
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"));
|
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"));
|
||||||
|
|
||||||
|
passes.push_pass(box mir::transform::deaggregator::Deaggregator);
|
||||||
|
|
||||||
passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
|
passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
|
||||||
passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
|
passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
|
||||||
|
|
||||||
|
|
116
src/librustc_mir/transform/deaggregator.rs
Normal file
116
src/librustc_mir/transform/deaggregator.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -17,3 +17,4 @@ pub mod add_call_guards;
|
||||||
pub mod promote_consts;
|
pub mod promote_consts;
|
||||||
pub mod qualify_consts;
|
pub mod qualify_consts;
|
||||||
pub mod dump_mir;
|
pub mod dump_mir;
|
||||||
|
pub mod deaggregator;
|
||||||
|
|
41
src/test/mir-opt/deaggregator_test.rs
Normal file
41
src/test/mir-opt/deaggregator_test.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
struct Baz {
|
||||||
|
x: usize,
|
||||||
|
y: f32,
|
||||||
|
z: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(a: usize) -> Baz {
|
||||||
|
Baz { x: a, y: 0.0, z: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
// END RUST SOURCE
|
||||||
|
// START rustc.node13.Deaggregator.before.mir
|
||||||
|
// bb0: {
|
||||||
|
// var0 = arg0; // scope 0 at main.rs:8:8: 8:9
|
||||||
|
// tmp0 = var0; // scope 1 at main.rs:9:14: 9:15
|
||||||
|
// return = Baz { x: tmp0, y: const F32(0), z: const false }; // scope ...
|
||||||
|
// goto -> bb1; // scope 1 at main.rs:8:1: 10:2
|
||||||
|
// }
|
||||||
|
// END rustc.node13.Deaggregator.before.mir
|
||||||
|
// START rustc.node13.Deaggregator.after.mir
|
||||||
|
// bb0: {
|
||||||
|
// var0 = arg0; // scope 0 at main.rs:8:8: 8:9
|
||||||
|
// tmp0 = var0; // scope 1 at main.rs:9:14: 9:15
|
||||||
|
// (return.0: usize) = tmp0; // scope 1 at main.rs:9:5: 9:34
|
||||||
|
// (return.1: f32) = const F32(0); // scope 1 at main.rs:9:5: 9:34
|
||||||
|
// (return.2: bool) = const false; // scope 1 at main.rs:9:5: 9:34
|
||||||
|
// goto -> bb1; // scope 1 at main.rs:8:1: 10:2
|
||||||
|
// }
|
||||||
|
// END rustc.node13.Deaggregator.after.mir
|
|
@ -1340,6 +1340,8 @@ actual:\n\
|
||||||
MirOpt => {
|
MirOpt => {
|
||||||
args.extend(["-Z",
|
args.extend(["-Z",
|
||||||
"dump-mir=all",
|
"dump-mir=all",
|
||||||
|
"-Z",
|
||||||
|
"mir-opt-level=3",
|
||||||
"-Z"]
|
"-Z"]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.to_string()));
|
.map(|s| s.to_string()));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue