Skip to content

Commit 4f32f47

Browse files
committed
Auto merge of rust-lang#126991 - cjgillot:gvn-prof, r=oli-obk
Accelerate GVN a little This PR addresses a few inefficiencies I've seen in callgrind profiles. Commits are independent. Only the first commit introduces a change in behaviour: we stop substituting some constant pointers. But we keep propagating their contents that have no provenance, so we don't lose much. r? `@saethlin`
2 parents 28e684b + 8279989 commit 4f32f47

11 files changed

+74
-68
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+43-21
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
126126
// Clone dominators as we need them while mutating the body.
127127
let dominators = body.basic_blocks.dominators().clone();
128128

129-
let mut state = VnState::new(tcx, param_env, &ssa, &dominators, &body.local_decls);
129+
let mut state = VnState::new(tcx, body, param_env, &ssa, &dominators, &body.local_decls);
130130
ssa.for_each_assignment_mut(
131131
body.basic_blocks.as_mut_preserves_cfg(),
132132
|local, value, location| {
@@ -202,6 +202,7 @@ enum Value<'tcx> {
202202
value: Const<'tcx>,
203203
/// Some constants do not have a deterministic value. To avoid merging two instances of the
204204
/// same `Const`, we assign them an additional integer index.
205+
// `disambiguator` is 0 iff the constant is deterministic.
205206
disambiguator: usize,
206207
},
207208
/// An aggregate value, either tuple/closure/struct/enum.
@@ -264,21 +265,29 @@ struct VnState<'body, 'tcx> {
264265
impl<'body, 'tcx> VnState<'body, 'tcx> {
265266
fn new(
266267
tcx: TyCtxt<'tcx>,
268+
body: &Body<'tcx>,
267269
param_env: ty::ParamEnv<'tcx>,
268270
ssa: &'body SsaLocals,
269271
dominators: &'body Dominators<BasicBlock>,
270272
local_decls: &'body LocalDecls<'tcx>,
271273
) -> Self {
274+
// Compute a rough estimate of the number of values in the body from the number of
275+
// statements. This is meant to reduce the number of allocations, but it's all right if
276+
// we miss the exact amount. We estimate based on 2 values per statement (one in LHS and
277+
// one in RHS) and 4 values per terminator (for call operands).
278+
let num_values =
279+
2 * body.basic_blocks.iter().map(|bbdata| bbdata.statements.len()).sum::<usize>()
280+
+ 4 * body.basic_blocks.len();
272281
VnState {
273282
tcx,
274283
ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine),
275284
param_env,
276285
local_decls,
277286
locals: IndexVec::from_elem(None, local_decls),
278-
rev_locals: IndexVec::default(),
279-
values: FxIndexSet::default(),
280-
evaluated: IndexVec::new(),
281-
next_opaque: Some(0),
287+
rev_locals: IndexVec::with_capacity(num_values),
288+
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
289+
evaluated: IndexVec::with_capacity(num_values),
290+
next_opaque: Some(1),
282291
feature_unsized_locals: tcx.features().unsized_locals,
283292
ssa,
284293
dominators,
@@ -291,9 +300,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
291300
let (index, new) = self.values.insert_full(value);
292301
let index = VnIndex::from_usize(index);
293302
if new {
303+
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
294304
let evaluated = self.eval_to_const(index);
295305
let _index = self.evaluated.push(evaluated);
296306
debug_assert_eq!(index, _index);
307+
// No need to push to `rev_locals` if we finished listing assignments.
308+
if self.next_opaque.is_some() {
309+
let _index = self.rev_locals.push(SmallVec::new());
310+
debug_assert_eq!(index, _index);
311+
}
297312
}
298313
index
299314
}
@@ -330,7 +345,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
330345
let is_sized = !self.feature_unsized_locals
331346
|| self.local_decls[local].ty.is_sized(self.tcx, self.param_env);
332347
if is_sized {
333-
self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local);
348+
self.rev_locals[value].push(local);
334349
}
335350
}
336351

@@ -344,19 +359,25 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
344359
let next_opaque = self.next_opaque.as_mut()?;
345360
let disambiguator = *next_opaque;
346361
*next_opaque += 1;
362+
// `disambiguator: 0` means deterministic.
363+
debug_assert_ne!(disambiguator, 0);
347364
disambiguator
348365
};
349366
Some(self.insert(Value::Constant { value, disambiguator }))
350367
}
351368

352369
fn insert_bool(&mut self, flag: bool) -> VnIndex {
353370
// Booleans are deterministic.
354-
self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 })
371+
let value = Const::from_bool(self.tcx, flag);
372+
debug_assert!(value.is_deterministic());
373+
self.insert(Value::Constant { value, disambiguator: 0 })
355374
}
356375

357376
fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
358-
self.insert_constant(Const::from_scalar(self.tcx, scalar, ty))
359-
.expect("scalars are deterministic")
377+
// Scalars are deterministic.
378+
let value = Const::from_scalar(self.tcx, scalar, ty);
379+
debug_assert!(value.is_deterministic());
380+
self.insert(Value::Constant { value, disambiguator: 0 })
360381
}
361382

362383
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
@@ -669,7 +690,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
669690
fn simplify_place_projection(&mut self, place: &mut Place<'tcx>, location: Location) {
670691
// If the projection is indirect, we treat the local as a value, so can replace it with
671692
// another local.
672-
if place.is_indirect()
693+
if place.is_indirect_first_projection()
673694
&& let Some(base) = self.locals[place.local]
674695
&& let Some(new_local) = self.try_as_local(base, location)
675696
&& place.local != new_local
@@ -771,10 +792,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
771792
location: Location,
772793
) -> Option<VnIndex> {
773794
match *operand {
774-
Operand::Constant(ref mut constant) => {
775-
let const_ = constant.const_.normalize(self.tcx, self.param_env);
776-
self.insert_constant(const_)
777-
}
795+
Operand::Constant(ref constant) => self.insert_constant(constant.const_),
778796
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
779797
let value = self.simplify_place_value(place, location)?;
780798
if let Some(const_) = self.try_as_constant(value) {
@@ -1369,8 +1387,13 @@ fn op_to_prop_const<'tcx>(
13691387
// If this constant has scalar ABI, return it as a `ConstValue::Scalar`.
13701388
if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi
13711389
&& let Ok(scalar) = ecx.read_scalar(op)
1372-
&& scalar.try_to_scalar_int().is_ok()
13731390
{
1391+
if !scalar.try_to_scalar_int().is_ok() {
1392+
// Check that we do not leak a pointer.
1393+
// Those pointers may lose part of their identity in codegen.
1394+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1395+
return None;
1396+
}
13741397
return Some(ConstValue::Scalar(scalar));
13751398
}
13761399

@@ -1434,12 +1457,11 @@ impl<'tcx> VnState<'_, 'tcx> {
14341457

14351458
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
14361459
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
1437-
// This was already constant in MIR, do not change it.
1438-
if let Value::Constant { value, disambiguator: _ } = *self.get(index)
1439-
// If the constant is not deterministic, adding an additional mention of it in MIR will
1440-
// not give the same value as the former mention.
1441-
&& value.is_deterministic()
1442-
{
1460+
// This was already constant in MIR, do not change it. If the constant is not
1461+
// deterministic, adding an additional mention of it in MIR will not give the same value as
1462+
// the former mention.
1463+
if let Value::Constant { value, disambiguator: 0 } = *self.get(index) {
1464+
debug_assert!(value.is_deterministic());
14431465
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
14441466
}
14451467

tests/incremental/hashes/for_loops.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ pub fn change_iterable() {
103103
}
104104

105105
#[cfg(not(any(cfail1,cfail4)))]
106-
#[rustc_clean(cfg="cfail2", except="opt_hir_owner_nodes, promoted_mir, optimized_mir")]
106+
#[rustc_clean(cfg="cfail2", except="opt_hir_owner_nodes, promoted_mir")]
107107
#[rustc_clean(cfg="cfail3")]
108-
#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes, promoted_mir, optimized_mir")]
108+
#[rustc_clean(cfg="cfail5", except="opt_hir_owner_nodes, promoted_mir")]
109109
#[rustc_clean(cfg="cfail6")]
110110
pub fn change_iterable() {
111111
let mut _x = 0;

tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff

+3-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222
StorageLive(_3);
2323
StorageLive(_4);
2424
_9 = const main::promoted[0];
25-
- _4 = _9;
25+
_4 = _9;
2626
- _3 = _4;
2727
- _2 = move _3 as &[u32] (PointerCoercion(Unsize));
28-
+ _4 = const {ALLOC0<imm>: &[u32; 3]};
29-
+ _3 = const {ALLOC0<imm>: &[u32; 3]};
30-
+ _2 = const {ALLOC0<imm>: &[u32; 3]} as &[u32] (PointerCoercion(Unsize));
28+
+ _3 = _9;
29+
+ _2 = _9 as &[u32] (PointerCoercion(Unsize));
3130
StorageDead(_3);
3231
StorageLive(_6);
3332
_6 = const 1_usize;

tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff

+3-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222
StorageLive(_3);
2323
StorageLive(_4);
2424
_9 = const main::promoted[0];
25-
- _4 = _9;
25+
_4 = _9;
2626
- _3 = _4;
2727
- _2 = move _3 as &[u32] (PointerCoercion(Unsize));
28-
+ _4 = const {ALLOC0<imm>: &[u32; 3]};
29-
+ _3 = const {ALLOC0<imm>: &[u32; 3]};
30-
+ _2 = const {ALLOC0<imm>: &[u32; 3]} as &[u32] (PointerCoercion(Unsize));
28+
+ _3 = _9;
29+
+ _2 = _9 as &[u32] (PointerCoercion(Unsize));
3130
StorageDead(_3);
3231
StorageLive(_6);
3332
_6 = const 1_usize;

tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff

+3-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222
StorageLive(_3);
2323
StorageLive(_4);
2424
_9 = const main::promoted[0];
25-
- _4 = _9;
25+
_4 = _9;
2626
- _3 = _4;
2727
- _2 = move _3 as &[u32] (PointerCoercion(Unsize));
28-
+ _4 = const {ALLOC0<imm>: &[u32; 3]};
29-
+ _3 = const {ALLOC0<imm>: &[u32; 3]};
30-
+ _2 = const {ALLOC0<imm>: &[u32; 3]} as &[u32] (PointerCoercion(Unsize));
28+
+ _3 = _9;
29+
+ _2 = _9 as &[u32] (PointerCoercion(Unsize));
3130
StorageDead(_3);
3231
StorageLive(_6);
3332
_6 = const 1_usize;

tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff

+3-4
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@
2222
StorageLive(_3);
2323
StorageLive(_4);
2424
_9 = const main::promoted[0];
25-
- _4 = _9;
25+
_4 = _9;
2626
- _3 = _4;
2727
- _2 = move _3 as &[u32] (PointerCoercion(Unsize));
28-
+ _4 = const {ALLOC0<imm>: &[u32; 3]};
29-
+ _3 = const {ALLOC0<imm>: &[u32; 3]};
30-
+ _2 = const {ALLOC0<imm>: &[u32; 3]} as &[u32] (PointerCoercion(Unsize));
28+
+ _3 = _9;
29+
+ _2 = _9 as &[u32] (PointerCoercion(Unsize));
3130
StorageDead(_3);
3231
StorageLive(_6);
3332
_6 = const 1_usize;

tests/mir-opt/const_prop/slice_len.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
fn main() {
88
// CHECK-LABEL: fn main(
99
// CHECK: debug a => [[a:_.*]];
10-
// CHECK: [[slice:_.*]] = const {{.*}} as &[u32] (PointerCoercion(Unsize));
10+
// CHECK: [[slice:_.*]] = {{.*}} as &[u32] (PointerCoercion(Unsize));
1111
// CHECK: assert(const true,
1212
// CHECK: [[a]] = const 2_u32;
1313
let a = (&[1u32, 2, 3] as &[u32])[1];

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff

+4-7
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,12 @@
7373
StorageLive(_6);
7474
StorageLive(_7);
7575
_9 = const main::promoted[0];
76-
- _7 = _9;
77-
+ _7 = const {ALLOC1<imm>: &std::alloc::Global};
76+
_7 = _9;
7877
StorageLive(_8);
7978
- _8 = _1;
8079
- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb4, unwind unreachable];
8180
+ _8 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
82-
+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
81+
+ _6 = std::alloc::Global::alloc_impl(_9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
8382
}
8483

8584
bb4: {
@@ -119,11 +118,9 @@
119118
+ nop;
120119
return;
121120
}
122-
}
121+
+ }
123122
+
124123
+ ALLOC0 (size: 8, align: 4) {
125124
+ 00 00 00 00 __ __ __ __ │ ....░░░░
126-
+ }
127-
+
128-
+ ALLOC1 (size: 0, align: 1) {}
125+
}
129126

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff

+4-7
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,22 @@
8181
StorageLive(_6);
8282
StorageLive(_7);
8383
_9 = const main::promoted[0];
84-
- _7 = _9;
85-
+ _7 = const {ALLOC1<imm>: &std::alloc::Global};
84+
_7 = _9;
8685
StorageLive(_8);
8786
- _8 = _1;
8887
- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb5, unwind continue];
8988
+ _8 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
90-
+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
89+
+ _6 = std::alloc::Global::alloc_impl(_9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
9190
}
9291

9392
bb5: {
9493
StorageDead(_8);
9594
StorageDead(_7);
9695
_5 = Result::<NonNull<[u8]>, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue];
9796
}
98-
}
97+
+ }
9998
+
10099
+ ALLOC0 (size: 8, align: 4) {
101100
+ 00 00 00 00 __ __ __ __ │ ....░░░░
102-
+ }
103-
+
104-
+ ALLOC1 (size: 0, align: 1) {}
101+
}
105102

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff

+4-7
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,12 @@
7373
StorageLive(_6);
7474
StorageLive(_7);
7575
_9 = const main::promoted[0];
76-
- _7 = _9;
77-
+ _7 = const {ALLOC1<imm>: &std::alloc::Global};
76+
_7 = _9;
7877
StorageLive(_8);
7978
- _8 = _1;
8079
- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb4, unwind unreachable];
8180
+ _8 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
82-
+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
81+
+ _6 = std::alloc::Global::alloc_impl(_9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
8382
}
8483

8584
bb4: {
@@ -119,11 +118,9 @@
119118
+ nop;
120119
return;
121120
}
122-
}
121+
+ }
123122
+
124123
+ ALLOC0 (size: 16, align: 8) {
125124
+ 00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ │ ........░░░░░░░░
126-
+ }
127-
+
128-
+ ALLOC1 (size: 0, align: 1) {}
125+
}
129126

tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff

+4-7
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,22 @@
8181
StorageLive(_6);
8282
StorageLive(_7);
8383
_9 = const main::promoted[0];
84-
- _7 = _9;
85-
+ _7 = const {ALLOC1<imm>: &std::alloc::Global};
84+
_7 = _9;
8685
StorageLive(_8);
8786
- _8 = _1;
8887
- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb5, unwind continue];
8988
+ _8 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
90-
+ _6 = std::alloc::Global::alloc_impl(const {ALLOC1<imm>: &std::alloc::Global}, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
89+
+ _6 = std::alloc::Global::alloc_impl(_9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
9190
}
9291

9392
bb5: {
9493
StorageDead(_8);
9594
StorageDead(_7);
9695
_5 = Result::<NonNull<[u8]>, std::alloc::AllocError>::unwrap(move _6) -> [return: bb1, unwind continue];
9796
}
98-
}
97+
+ }
9998
+
10099
+ ALLOC0 (size: 16, align: 8) {
101100
+ 00 00 00 00 00 00 00 00 __ __ __ __ __ __ __ __ │ ........░░░░░░░░
102-
+ }
103-
+
104-
+ ALLOC1 (size: 0, align: 1) {}
101+
}
105102

0 commit comments

Comments
 (0)