Skip to content

Commit

Permalink
Auto merge of rust-lang#35168 - scottcarr:deaggregation, r=nikomatsakis
Browse files Browse the repository at this point in the history
[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.
  • Loading branch information
bors authored Aug 4, 2016
2 parents 271d048 + 06acf16 commit e804a3c
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

Expand Down
116 changes: 116 additions & 0 deletions src/librustc_mir/transform/deaggregator.rs
Original file line number Diff line number Diff line change
@@ -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
}
1 change: 1 addition & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 41 additions & 0 deletions src/test/mir-opt/deaggregator_test.rs
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand Down

0 comments on commit e804a3c

Please sign in to comment.