Skip to content

Commit c9fcede

Browse files
committed
rustc_mir: optimize the deaggregator's expansion of statements.
1 parent d773d95 commit c9fcede

File tree

4 files changed

+104
-57
lines changed

4 files changed

+104
-57
lines changed

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#![feature(underscore_lifetimes)]
7070
#![feature(universal_impl_trait)]
7171
#![feature(trace_macros)]
72+
#![feature(trusted_len)]
7273
#![feature(catch_expr)]
7374
#![feature(test)]
7475

src/librustc/mir/mod.rs

+62-3
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ use std::ascii;
3434
use std::borrow::{Cow};
3535
use std::cell::Ref;
3636
use std::fmt::{self, Debug, Formatter, Write};
37-
use std::{iter, u32};
37+
use std::{iter, mem, u32};
3838
use std::ops::{Index, IndexMut};
3939
use std::rc::Rc;
4040
use std::vec::IntoIter;
4141
use syntax::ast::{self, Name};
4242
use syntax::symbol::InternedString;
43-
use syntax_pos::Span;
43+
use syntax_pos::{Span, DUMMY_SP};
4444

4545
mod cache;
4646
pub mod tcx;
@@ -984,11 +984,62 @@ impl<'tcx> BasicBlockData<'tcx> {
984984
pub fn retain_statements<F>(&mut self, mut f: F) where F: FnMut(&mut Statement) -> bool {
985985
for s in &mut self.statements {
986986
if !f(s) {
987-
s.kind = StatementKind::Nop;
987+
s.make_nop();
988988
}
989989
}
990990
}
991991

992+
pub fn expand_statements<F, I>(&mut self, mut f: F)
993+
where F: FnMut(&mut Statement<'tcx>) -> Option<I>,
994+
I: iter::TrustedLen<Item = Statement<'tcx>>
995+
{
996+
// Gather all the iterators we'll need to splice in, and their positions.
997+
let mut splices: Vec<(usize, I)> = vec![];
998+
let mut extra_stmts = 0;
999+
for (i, s) in self.statements.iter_mut().enumerate() {
1000+
if let Some(mut new_stmts) = f(s) {
1001+
if let Some(first) = new_stmts.next() {
1002+
// We can already store the first new statement.
1003+
*s = first;
1004+
1005+
// Save the other statements for optimized splicing.
1006+
let remaining = new_stmts.size_hint().0;
1007+
if remaining > 0 {
1008+
splices.push((i + 1 + extra_stmts, new_stmts));
1009+
extra_stmts += remaining;
1010+
}
1011+
} else {
1012+
s.make_nop();
1013+
}
1014+
}
1015+
}
1016+
1017+
// Splice in the new statements, from the end of the block.
1018+
// FIXME(eddyb) This could be more efficient with a "gap buffer"
1019+
// where a range of elements ("gap") is left uninitialized, with
1020+
// splicing adding new elements to the end of that gap and moving
1021+
// existing elements from before the gap to the end of the gap.
1022+
// For now, this is safe code, emulating a gap but initializing it.
1023+
let mut gap = self.statements.len()..self.statements.len()+extra_stmts;
1024+
self.statements.resize(gap.end, Statement {
1025+
source_info: SourceInfo {
1026+
span: DUMMY_SP,
1027+
scope: ARGUMENT_VISIBILITY_SCOPE
1028+
},
1029+
kind: StatementKind::Nop
1030+
});
1031+
for (splice_start, new_stmts) in splices.into_iter().rev() {
1032+
let splice_end = splice_start + new_stmts.size_hint().0;
1033+
while gap.end > splice_end {
1034+
gap.start -= 1;
1035+
gap.end -= 1;
1036+
self.statements.swap(gap.start, gap.end);
1037+
}
1038+
self.statements.splice(splice_start..splice_end, new_stmts);
1039+
gap.end = splice_start;
1040+
}
1041+
}
1042+
9921043
pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> {
9931044
if index < self.statements.len() {
9941045
&self.statements[index]
@@ -1157,6 +1208,14 @@ impl<'tcx> Statement<'tcx> {
11571208
pub fn make_nop(&mut self) {
11581209
self.kind = StatementKind::Nop
11591210
}
1211+
1212+
/// Changes a statement to a nop and returns the original statement.
1213+
pub fn replace_nop(&mut self) -> Self {
1214+
Statement {
1215+
source_info: self.source_info,
1216+
kind: mem::replace(&mut self.kind, StatementKind::Nop)
1217+
}
1218+
}
11601219
}
11611220

11621221
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]

src/librustc_mir/transform/deaggregator.rs

+26-43
Original file line numberDiff line numberDiff line change
@@ -39,32 +39,27 @@ impl MirPass for Deaggregator {
3939
}
4040
}
4141

42-
let can_deaggregate = |statement: &Statement| {
43-
if let StatementKind::Assign(_, ref rhs) = statement.kind {
44-
if let Rvalue::Aggregate(ref kind, _) = *rhs {
45-
// FIXME(#48193) Deaggregate arrays when it's cheaper to do so.
46-
if let AggregateKind::Array(_) = **kind {
47-
return false;
48-
}
49-
return true;
50-
}
51-
}
52-
53-
false
54-
};
55-
5642
let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
43+
let local_decls = &*local_decls;
5744
for bb in basic_blocks {
58-
let mut start = 0;
59-
while let Some(i) = bb.statements[start..].iter().position(&can_deaggregate) {
60-
let i = start + i;
45+
bb.expand_statements(|stmt| {
46+
// FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL).
47+
if let StatementKind::Assign(_, ref rhs) = stmt.kind {
48+
if let Rvalue::Aggregate(ref kind, _) = *rhs {
49+
// FIXME(#48193) Deaggregate arrays when it's cheaper to do so.
50+
if let AggregateKind::Array(_) = **kind {
51+
return None;
52+
}
53+
} else {
54+
return None;
55+
}
56+
} else {
57+
return None;
58+
}
6159

62-
// FIXME(eddyb) this is probably more expensive than it should be.
63-
// Ideally we'd move the block's statements all at once.
64-
let suffix_stmts = bb.statements.split_off(i + 1);
65-
let orig_stmt = bb.statements.pop().unwrap();
66-
let source_info = orig_stmt.source_info;
67-
let (mut lhs, kind, operands) = match orig_stmt.kind {
60+
let stmt = stmt.replace_nop();
61+
let source_info = stmt.source_info;
62+
let (mut lhs, kind, operands) = match stmt.kind {
6863
StatementKind::Assign(lhs, Rvalue::Aggregate(kind, operands))
6964
=> (lhs, kind, operands),
7065
_ => bug!()
@@ -88,17 +83,11 @@ impl MirPass for Deaggregator {
8883
_ => None
8984
};
9085

91-
let new_total_count = bb.statements.len() +
92-
operands.len() +
93-
(set_discriminant.is_some() as usize) +
94-
suffix_stmts.len();
95-
bb.statements.reserve(new_total_count);
96-
97-
for (j, op) in operands.into_iter().enumerate() {
86+
Some(operands.into_iter().enumerate().map(move |(i, op)| {
9887
let lhs_field = if let AggregateKind::Array(_) = *kind {
9988
// FIXME(eddyb) `offset` should be u64.
100-
let offset = j as u32;
101-
assert_eq!(offset as usize, j);
89+
let offset = i as u32;
90+
assert_eq!(offset as usize, i);
10291
lhs.clone().elem(ProjectionElem::ConstantIndex {
10392
offset,
10493
// FIXME(eddyb) `min_length` doesn't appear to be used.
@@ -107,21 +96,15 @@ impl MirPass for Deaggregator {
10796
})
10897
} else {
10998
let ty = op.ty(local_decls, tcx);
110-
let field = Field::new(active_field_index.unwrap_or(j));
99+
let field = Field::new(active_field_index.unwrap_or(i));
111100
lhs.clone().field(field, ty)
112101
};
113-
bb.statements.push(Statement {
102+
Statement {
114103
source_info,
115104
kind: StatementKind::Assign(lhs_field, Rvalue::Use(op)),
116-
});
117-
}
118-
119-
// If the aggregate was an enum, we need to set the discriminant.
120-
bb.statements.extend(set_discriminant);
121-
122-
start = bb.statements.len();
123-
bb.statements.extend(suffix_stmts);
124-
}
105+
}
106+
}).chain(set_discriminant))
107+
});
125108
}
126109
}
127110
}

src/test/mir-opt/copy_propagation_arg.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ fn main() {
7878
// bb1: {
7979
// StorageDead(_3);
8080
// _1 = const 5u8;
81+
// ...
8182
// return;
8283
// }
8384
// END rustc.bar.CopyPropagation.before.mir
@@ -91,6 +92,7 @@ fn main() {
9192
// ...
9293
// _1 = const 5u8;
9394
// ...
95+
// return;
9496
// }
9597
// END rustc.bar.CopyPropagation.after.mir
9698
// START rustc.baz.CopyPropagation.before.mir
@@ -99,6 +101,7 @@ fn main() {
99101
// _2 = _1;
100102
// _1 = move _2;
101103
// StorageDead(_2);
104+
// ...
102105
// return;
103106
// }
104107
// END rustc.baz.CopyPropagation.before.mir
@@ -108,21 +111,22 @@ fn main() {
108111
// _2 = _1;
109112
// _1 = move _2;
110113
// ...
114+
// return;
111115
// }
112116
// END rustc.baz.CopyPropagation.after.mir
113117
// START rustc.arg_src.CopyPropagation.before.mir
114118
// bb0: {
115-
// ...
116-
// _3 = _1;
117-
// _2 = move _3;
118-
// ...
119-
// _1 = const 123i32;
120-
// ...
121-
// _4 = _2;
122-
// _0 = move _4;
123-
// ...
124-
// return;
125-
// }
119+
// ...
120+
// _3 = _1;
121+
// _2 = move _3;
122+
// ...
123+
// _1 = const 123i32;
124+
// ...
125+
// _4 = _2;
126+
// _0 = move _4;
127+
// ...
128+
// return;
129+
// }
126130
// END rustc.arg_src.CopyPropagation.before.mir
127131
// START rustc.arg_src.CopyPropagation.after.mir
128132
// bb0: {

0 commit comments

Comments
 (0)