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
@ -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::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::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 qualify_consts;
|
||||
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 => {
|
||||
args.extend(["-Z",
|
||||
"dump-mir=all",
|
||||
"-Z",
|
||||
"mir-opt-level=3",
|
||||
"-Z"]
|
||||
.iter()
|
||||
.map(|s| s.to_string()));
|
||||
|
Loading…
x
Reference in New Issue
Block a user