Skip to content

Commit e804a3c

Browse files
authored
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.
2 parents 271d048 + 06acf16 commit e804a3c

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed

src/librustc_driver/driver.rs

+2
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,8 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
995995
passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
996996
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"));
997997

998+
passes.push_pass(box mir::transform::deaggregator::Deaggregator);
999+
9981000
passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
9991001
passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
10001002

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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.
4+
//
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.
10+
11+
use rustc::ty::TyCtxt;
12+
use rustc::mir::repr::*;
13+
use rustc::mir::transform::{MirPass, MirSource, Pass};
14+
use rustc_data_structures::indexed_vec::Idx;
15+
use rustc::ty::VariantKind;
16+
17+
pub struct Deaggregator;
18+
19+
impl Pass for Deaggregator {}
20+
21+
impl<'tcx> MirPass<'tcx> for Deaggregator {
22+
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
23+
source: MirSource, mir: &mut Mir<'tcx>) {
24+
let node_id = source.item_id();
25+
let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id));
26+
debug!("running on: {:?}", node_path);
27+
// we only run when mir_opt_level > 1
28+
match tcx.sess.opts.debugging_opts.mir_opt_level {
29+
Some(0) |
30+
Some(1) |
31+
None => { return; },
32+
_ => {}
33+
};
34+
35+
// Do not trigger on constants. Could be revised in future
36+
if let MirSource::Fn(_) = source {} else { return; }
37+
// In fact, we might not want to trigger in other cases.
38+
// Ex: when we could use SROA. See issue #35259
39+
40+
let mut curr: usize = 0;
41+
for bb in mir.basic_blocks_mut() {
42+
let idx = match get_aggregate_statement(curr, &bb.statements) {
43+
Some(idx) => idx,
44+
None => continue,
45+
};
46+
// do the replacement
47+
debug!("removing statement {:?}", idx);
48+
let src_info = bb.statements[idx].source_info;
49+
let suffix_stmts = bb.statements.split_off(idx+1);
50+
let orig_stmt = bb.statements.pop().unwrap();
51+
let StatementKind::Assign(ref lhs, ref rhs) = orig_stmt.kind;
52+
let (agg_kind, operands) = match rhs {
53+
&Rvalue::Aggregate(ref agg_kind, ref operands) => (agg_kind, operands),
54+
_ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
55+
};
56+
let (adt_def, variant, substs) = match agg_kind {
57+
&AggregateKind::Adt(adt_def, variant, substs) => (adt_def, variant, substs),
58+
_ => span_bug!(src_info.span, "expected struct, not {:?}", rhs),
59+
};
60+
let n = bb.statements.len();
61+
bb.statements.reserve(n + operands.len() + suffix_stmts.len());
62+
for (i, op) in operands.iter().enumerate() {
63+
let ref variant_def = adt_def.variants[variant];
64+
let ty = variant_def.fields[i].ty(tcx, substs);
65+
let rhs = Rvalue::Use(op.clone());
66+
67+
// since we don't handle enums, we don't need a cast
68+
let lhs_cast = lhs.clone();
69+
70+
// FIXME we cannot deaggregate enums issue: #35186
71+
72+
let lhs_proj = Lvalue::Projection(Box::new(LvalueProjection {
73+
base: lhs_cast,
74+
elem: ProjectionElem::Field(Field::new(i), ty),
75+
}));
76+
let new_statement = Statement {
77+
source_info: src_info,
78+
kind: StatementKind::Assign(lhs_proj, rhs),
79+
};
80+
debug!("inserting: {:?} @ {:?}", new_statement, idx + i);
81+
bb.statements.push(new_statement);
82+
}
83+
curr = bb.statements.len();
84+
bb.statements.extend(suffix_stmts);
85+
}
86+
}
87+
}
88+
89+
fn get_aggregate_statement<'a, 'tcx, 'b>(curr: usize,
90+
statements: &Vec<Statement<'tcx>>)
91+
-> Option<usize> {
92+
for i in curr..statements.len() {
93+
let ref statement = statements[i];
94+
let StatementKind::Assign(_, ref rhs) = statement.kind;
95+
let (kind, operands) = match rhs {
96+
&Rvalue::Aggregate(ref kind, ref operands) => (kind, operands),
97+
_ => continue,
98+
};
99+
let (adt_def, variant) = match kind {
100+
&AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
101+
_ => continue,
102+
};
103+
if operands.len() == 0 || adt_def.variants.len() > 1 {
104+
// don't deaggregate ()
105+
// don't deaggregate enums ... for now
106+
continue;
107+
}
108+
debug!("getting variant {:?}", variant);
109+
debug!("for adt_def {:?}", adt_def);
110+
let variant_def = &adt_def.variants[variant];
111+
if variant_def.kind == VariantKind::Struct {
112+
return Some(i);
113+
}
114+
};
115+
None
116+
}

src/librustc_mir/transform/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ pub mod add_call_guards;
1717
pub mod promote_consts;
1818
pub mod qualify_consts;
1919
pub mod dump_mir;
20+
pub mod deaggregator;

src/test/mir-opt/deaggregator_test.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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.
4+
//
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.
10+
11+
struct Baz {
12+
x: usize,
13+
y: f32,
14+
z: bool,
15+
}
16+
17+
fn bar(a: usize) -> Baz {
18+
Baz { x: a, y: 0.0, z: false }
19+
}
20+
21+
fn main() {}
22+
23+
// END RUST SOURCE
24+
// START rustc.node13.Deaggregator.before.mir
25+
// bb0: {
26+
// var0 = arg0; // scope 0 at main.rs:8:8: 8:9
27+
// tmp0 = var0; // scope 1 at main.rs:9:14: 9:15
28+
// return = Baz { x: tmp0, y: const F32(0), z: const false }; // scope ...
29+
// goto -> bb1; // scope 1 at main.rs:8:1: 10:2
30+
// }
31+
// END rustc.node13.Deaggregator.before.mir
32+
// START rustc.node13.Deaggregator.after.mir
33+
// bb0: {
34+
// var0 = arg0; // scope 0 at main.rs:8:8: 8:9
35+
// tmp0 = var0; // scope 1 at main.rs:9:14: 9:15
36+
// (return.0: usize) = tmp0; // scope 1 at main.rs:9:5: 9:34
37+
// (return.1: f32) = const F32(0); // scope 1 at main.rs:9:5: 9:34
38+
// (return.2: bool) = const false; // scope 1 at main.rs:9:5: 9:34
39+
// goto -> bb1; // scope 1 at main.rs:8:1: 10:2
40+
// }
41+
// END rustc.node13.Deaggregator.after.mir

src/tools/compiletest/src/runtest.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,8 @@ actual:\n\
13401340
MirOpt => {
13411341
args.extend(["-Z",
13421342
"dump-mir=all",
1343+
"-Z",
1344+
"mir-opt-level=3",
13431345
"-Z"]
13441346
.iter()
13451347
.map(|s| s.to_string()));

0 commit comments

Comments
 (0)