Skip to content

Commit 28a58f2

Browse files
committed
Auto merge of #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 99322d8 + 4067795 commit 28a58f2

11 files changed

+74
-76
lines changed

compiler/rustc_mir_transform/src/gvn.rs

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

131-
let mut state = VnState::new(tcx, param_env, &ssa, &dominators, &body.local_decls);
131+
let mut state = VnState::new(tcx, body, param_env, &ssa, &dominators, &body.local_decls);
132132
ssa.for_each_assignment_mut(
133133
body.basic_blocks.as_mut_preserves_cfg(),
134134
|local, value, location| {
@@ -204,6 +204,7 @@ enum Value<'tcx> {
204204
value: Const<'tcx>,
205205
/// Some constants do not have a deterministic value. To avoid merging two instances of the
206206
/// same `Const`, we assign them an additional integer index.
207+
// `disambiguator` is 0 iff the constant is deterministic.
207208
disambiguator: usize,
208209
},
209210
/// An aggregate value, either tuple/closure/struct/enum.
@@ -266,21 +267,29 @@ struct VnState<'body, 'tcx> {
266267
impl<'body, 'tcx> VnState<'body, 'tcx> {
267268
fn new(
268269
tcx: TyCtxt<'tcx>,
270+
body: &Body<'tcx>,
269271
param_env: ty::ParamEnv<'tcx>,
270272
ssa: &'body SsaLocals,
271273
dominators: &'body Dominators<BasicBlock>,
272274
local_decls: &'body LocalDecls<'tcx>,
273275
) -> Self {
276+
// Compute a rough estimate of the number of values in the body from the number of
277+
// statements. This is meant to reduce the number of allocations, but it's all right if
278+
// we miss the exact amount. We estimate based on 2 values per statement (one in LHS and
279+
// one in RHS) and 4 values per terminator (for call operands).
280+
let num_values =
281+
2 * body.basic_blocks.iter().map(|bbdata| bbdata.statements.len()).sum::<usize>()
282+
+ 4 * body.basic_blocks.len();
274283
VnState {
275284
tcx,
276285
ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine),
277286
param_env,
278287
local_decls,
279288
locals: IndexVec::from_elem(None, local_decls),
280-
rev_locals: IndexVec::default(),
281-
values: FxIndexSet::default(),
282-
evaluated: IndexVec::new(),
283-
next_opaque: Some(0),
289+
rev_locals: IndexVec::with_capacity(num_values),
290+
values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
291+
evaluated: IndexVec::with_capacity(num_values),
292+
next_opaque: Some(1),
284293
feature_unsized_locals: tcx.features().unsized_locals,
285294
ssa,
286295
dominators,
@@ -293,9 +302,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
293302
let (index, new) = self.values.insert_full(value);
294303
let index = VnIndex::from_usize(index);
295304
if new {
305+
// Grow `evaluated` and `rev_locals` here to amortize the allocations.
296306
let evaluated = self.eval_to_const(index);
297307
let _index = self.evaluated.push(evaluated);
298308
debug_assert_eq!(index, _index);
309+
// No need to push to `rev_locals` if we finished listing assignments.
310+
if self.next_opaque.is_some() {
311+
let _index = self.rev_locals.push(SmallVec::new());
312+
debug_assert_eq!(index, _index);
313+
}
299314
}
300315
index
301316
}
@@ -332,7 +347,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
332347
let is_sized = !self.feature_unsized_locals
333348
|| self.local_decls[local].ty.is_sized(self.tcx, self.param_env);
334349
if is_sized {
335-
self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local);
350+
self.rev_locals[value].push(local);
336351
}
337352
}
338353

@@ -346,19 +361,25 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
346361
let next_opaque = self.next_opaque.as_mut()?;
347362
let disambiguator = *next_opaque;
348363
*next_opaque += 1;
364+
// `disambiguator: 0` means deterministic.
365+
debug_assert_ne!(disambiguator, 0);
349366
disambiguator
350367
};
351368
Some(self.insert(Value::Constant { value, disambiguator }))
352369
}
353370

354371
fn insert_bool(&mut self, flag: bool) -> VnIndex {
355372
// Booleans are deterministic.
356-
self.insert(Value::Constant { value: Const::from_bool(self.tcx, flag), disambiguator: 0 })
373+
let value = Const::from_bool(self.tcx, flag);
374+
debug_assert!(value.is_deterministic());
375+
self.insert(Value::Constant { value, disambiguator: 0 })
357376
}
358377

359378
fn insert_scalar(&mut self, scalar: Scalar, ty: Ty<'tcx>) -> VnIndex {
360-
self.insert_constant(Const::from_scalar(self.tcx, scalar, ty))
361-
.expect("scalars are deterministic")
379+
// Scalars are deterministic.
380+
let value = Const::from_scalar(self.tcx, scalar, ty);
381+
debug_assert!(value.is_deterministic());
382+
self.insert(Value::Constant { value, disambiguator: 0 })
362383
}
363384

364385
fn insert_tuple(&mut self, values: Vec<VnIndex>) -> VnIndex {
@@ -671,7 +692,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
671692
fn simplify_place_projection(&mut self, place: &mut Place<'tcx>, location: Location) {
672693
// If the projection is indirect, we treat the local as a value, so can replace it with
673694
// another local.
674-
if place.is_indirect()
695+
if place.is_indirect_first_projection()
675696
&& let Some(base) = self.locals[place.local]
676697
&& let Some(new_local) = self.try_as_local(base, location)
677698
&& place.local != new_local
@@ -773,10 +794,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
773794
location: Location,
774795
) -> Option<VnIndex> {
775796
match *operand {
776-
Operand::Constant(ref mut constant) => {
777-
let const_ = constant.const_.normalize(self.tcx, self.param_env);
778-
self.insert_constant(const_)
779-
}
797+
Operand::Constant(ref constant) => self.insert_constant(constant.const_),
780798
Operand::Copy(ref mut place) | Operand::Move(ref mut place) => {
781799
let value = self.simplify_place_value(place, location)?;
782800
if let Some(const_) = self.try_as_constant(value) {
@@ -1371,8 +1389,13 @@ fn op_to_prop_const<'tcx>(
13711389
// If this constant has scalar ABI, return it as a `ConstValue::Scalar`.
13721390
if let Abi::Scalar(abi::Scalar::Initialized { .. }) = op.layout.abi
13731391
&& let Ok(scalar) = ecx.read_scalar(op)
1374-
&& scalar.try_to_scalar_int().is_ok()
13751392
{
1393+
if !scalar.try_to_scalar_int().is_ok() {
1394+
// Check that we do not leak a pointer.
1395+
// Those pointers may lose part of their identity in codegen.
1396+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1397+
return None;
1398+
}
13761399
return Some(ConstValue::Scalar(scalar));
13771400
}
13781401

@@ -1436,12 +1459,11 @@ impl<'tcx> VnState<'_, 'tcx> {
14361459

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

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-6
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;
@@ -50,6 +49,4 @@
5049
return;
5150
}
5251
}
53-
+
54-
+ ALLOC0 (size: 12, align: 4) { .. }
5552

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

+3-6
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;
@@ -50,6 +49,4 @@
5049
return;
5150
}
5251
}
53-
+
54-
+ ALLOC0 (size: 12, align: 4) { .. }
5552

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

+3-6
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;
@@ -50,6 +49,4 @@
5049
return;
5150
}
5251
}
53-
+
54-
+ ALLOC0 (size: 12, align: 4) { .. }
5552

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

+3-6
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;
@@ -50,6 +49,4 @@
5049
return;
5150
}
5251
}
53-
+
54-
+ ALLOC0 (size: 12, align: 4) { .. }
5552

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)