Skip to content

Commit e26c0b3

Browse files
committed
Auto merge of rust-lang#126444 - tesuji:gvn-const-arrays, r=<try>
[WIP] gvn: Promote/propagate const local array Rewriting of rust-lang#125916 which used PromoteTemps pass. Fix rust-lang#73825 ### Current status - [ ] Waiting for [consensus](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/Could.20const.20read-only.20arrays.20be.20const.20promoted.3F). r? ghost
2 parents f158600 + 550fb81 commit e26c0b3

6 files changed

+298
-17
lines changed

compiler/rustc_mir_transform/src/gvn.rs

+82-12
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,15 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
366366

367367
#[instrument(level = "trace", skip(self), ret)]
368368
fn eval_to_const(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
369+
use abi::HasDataLayout;
369370
use Value::*;
370-
let op = match *self.get(value) {
371+
// LLVM optimizes the load of `sizeof(size_t) * 2` as a single `mov`,
372+
// which is cheap. Bigger values make more `mov` instructions generated.
373+
// After GVN, it becomes a single load (`lea`) of an address in `.rodata`.
374+
let stack_threshold = self.tcx.data_layout().pointer_size * 2;
375+
let vvalue = self.get(value);
376+
debug!(?vvalue);
377+
let op = match *vvalue {
371378
Opaque(_) => return None,
372379
// Do not bother evaluating repeat expressions. This would uselessly consume memory.
373380
Repeat(..) => return None,
@@ -376,10 +383,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
376383
self.ecx.eval_mir_constant(value, DUMMY_SP, None).ok()?
377384
}
378385
Aggregate(kind, variant, ref fields) => {
386+
debug!(?kind, ?variant, ?fields);
379387
let fields = fields
380388
.iter()
381389
.map(|&f| self.evaluated[f].as_ref())
382-
.collect::<Option<Vec<_>>>()?;
390+
.collect::<Option<Vec<&OpTy<'_>>>>()?;
383391
let ty = match kind {
384392
AggregateTy::Array => {
385393
assert!(fields.len() > 0);
@@ -407,6 +415,35 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
407415
};
408416
let ptr_imm = Immediate::new_pointer_with_meta(data, meta, &self.ecx);
409417
ImmTy::from_immediate(ptr_imm, ty).into()
418+
} else if matches!(kind, AggregateTy::Array) {
419+
if ty.layout.size() <= stack_threshold {
420+
return None;
421+
}
422+
let mut mplace = None;
423+
let alloc_id = self
424+
.ecx
425+
.intern_with_temp_alloc(ty, |ecx, dest| {
426+
for (field_index, op) in fields.iter().copied().enumerate() {
427+
// ignore nested arrays
428+
if let Either::Left(_) = op.as_mplace_or_imm() {
429+
interpret::throw_inval!(TooGeneric);
430+
}
431+
let field_dest = ecx.project_field(dest, field_index)?;
432+
ecx.copy_op(op, &field_dest)?;
433+
}
434+
435+
let dest =
436+
dest.assert_mem_place().map_provenance(|prov| prov.as_immutable());
437+
mplace.replace(dest);
438+
Ok(())
439+
})
440+
.ok()?;
441+
let GlobalAlloc::Memory(_alloc) = self.tcx.global_alloc(alloc_id) else {
442+
bug!()
443+
};
444+
let mplace = mplace.unwrap();
445+
debug!(?mplace);
446+
return Some(mplace.into());
410447
} else if matches!(ty.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) {
411448
let dest = self.ecx.allocate(ty, MemoryKind::Stack).ok()?;
412449
let variant_dest = if let Some(variant) = variant {
@@ -429,6 +466,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
429466
}
430467

431468
Projection(base, elem) => {
469+
debug!(?base, ?elem);
432470
let value = self.evaluated[base].as_ref()?;
433471
let elem = match elem {
434472
ProjectionElem::Deref => ProjectionElem::Deref,
@@ -450,6 +488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
450488
self.ecx.project(value, elem).ok()?
451489
}
452490
Address { place, kind, provenance: _ } => {
491+
debug!(?place, ?kind);
453492
if !place.is_indirect_first_projection() {
454493
return None;
455494
}
@@ -709,7 +748,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
709748
place.projection = self.tcx.mk_place_elems(&projection);
710749
}
711750

712-
trace!(?place);
751+
trace!(after_place = ?place);
713752
}
714753

715754
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
@@ -1233,6 +1272,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
12331272
}
12341273
}
12351274

1275+
#[instrument(level = "trace", skip(ecx), ret)]
12361276
fn op_to_prop_const<'tcx>(
12371277
ecx: &mut InterpCx<'tcx, DummyMachine>,
12381278
op: &OpTy<'tcx>,
@@ -1248,7 +1288,8 @@ fn op_to_prop_const<'tcx>(
12481288
}
12491289

12501290
// Do not synthetize too large constants. Codegen will just memcpy them, which we'd like to avoid.
1251-
if !matches!(op.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..)) {
1291+
if !(op.layout.ty.is_array() || matches!(op.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..)))
1292+
{
12521293
return None;
12531294
}
12541295

@@ -1302,21 +1343,42 @@ fn op_to_prop_const<'tcx>(
13021343

13031344
impl<'tcx> VnState<'_, 'tcx> {
13041345
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1346+
#[instrument(level = "trace", skip(self, index), ret)]
13051347
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
13061348
// This was already constant in MIR, do not change it.
1307-
if let Value::Constant { value, disambiguator: _ } = *self.get(index)
1349+
let value = self.get(index);
1350+
debug!(?index, ?value);
1351+
match value {
13081352
// If the constant is not deterministic, adding an additional mention of it in MIR will
13091353
// not give the same value as the former mention.
1310-
&& value.is_deterministic()
1311-
{
1312-
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value });
1354+
Value::Constant { value, disambiguator: _ } if value.is_deterministic() => {
1355+
return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: *value });
1356+
}
1357+
// ignore nested arrays
1358+
Value::Aggregate(AggregateTy::Array, _, fields) => {
1359+
for f in fields {
1360+
if let Value::Constant { value: Const::Val(const_, _), .. } = self.get(*f)
1361+
&& let ConstValue::Indirect { .. } = const_
1362+
{
1363+
return None;
1364+
}
1365+
}
1366+
}
1367+
// ignore promoted arrays
1368+
Value::Projection(index, ProjectionElem::Deref) => {
1369+
if let Value::Constant { value: Const::Val(const_, ty), .. } = self.get(*index)
1370+
&& let ConstValue::Scalar(Scalar::Ptr(..)) = const_
1371+
&& let ty::Ref(region, ty, _mutability) = ty.kind()
1372+
&& region.is_erased()
1373+
&& ty.is_array()
1374+
{
1375+
return None;
1376+
}
1377+
}
1378+
_ => {}
13131379
}
13141380

13151381
let op = self.evaluated[index].as_ref()?;
1316-
if op.layout.is_unsized() {
1317-
// Do not attempt to propagate unsized locals.
1318-
return None;
1319-
}
13201382

13211383
let value = op_to_prop_const(&mut self.ecx, op)?;
13221384

@@ -1326,6 +1388,10 @@ impl<'tcx> VnState<'_, 'tcx> {
13261388
assert!(!value.may_have_provenance(self.tcx, op.layout.size));
13271389

13281390
let const_ = Const::Val(value, op.layout.ty);
1391+
// cache the propagated const
1392+
if let Some(new_index) = self.insert_constant(const_) {
1393+
self.values.swap_indices(index.as_usize(), new_index.as_usize());
1394+
}
13291395
Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_ })
13301396
}
13311397

@@ -1353,6 +1419,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
13531419
self.simplify_operand(operand, location);
13541420
}
13551421

1422+
#[instrument(level = "trace", skip(self, stmt))]
13561423
fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, location: Location) {
13571424
if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
13581425
self.simplify_place_projection(lhs, location);
@@ -1366,8 +1433,10 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
13661433
.as_local()
13671434
.and_then(|local| self.locals[local])
13681435
.or_else(|| self.simplify_rvalue(rvalue, location));
1436+
debug!(?value);
13691437
let Some(value) = value else { return };
13701438

1439+
debug!(before_rvalue = ?rvalue);
13711440
if let Some(const_) = self.try_as_constant(value) {
13721441
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
13731442
} else if let Some(local) = self.try_as_local(value, location)
@@ -1376,6 +1445,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
13761445
*rvalue = Rvalue::Use(Operand::Copy(local.into()));
13771446
self.reused_locals.insert(local);
13781447
}
1448+
debug!(after_rvalue = ?rvalue);
13791449

13801450
return;
13811451
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// issue: <https://github.com/rust-lang/rust/issues/73825>
2+
//@ compile-flags: -C opt-level=1
3+
#![crate_type = "lib"]
4+
5+
// CHECK-LABEL: @foo
6+
// CHECK-NEXT: {{.*}}:
7+
// CHECK-NEXT: and
8+
// CHECK-NEXT: getelementptr inbounds
9+
// CHECK-NEXT: load i32
10+
// CHECK-NEXT: ret i32
11+
#[no_mangle]
12+
#[rustfmt::skip]
13+
pub fn foo(x: usize) -> i32 {
14+
let base: [i32; 64] = [
15+
67, 754, 860, 559, 368, 870, 548, 972,
16+
141, 731, 351, 664, 32, 4, 996, 741,
17+
203, 292, 237, 480, 151, 940, 777, 540,
18+
143, 587, 747, 65, 152, 517, 882, 880,
19+
712, 595, 370, 901, 237, 53, 789, 785,
20+
912, 650, 896, 367, 316, 392, 62, 473,
21+
675, 691, 281, 192, 445, 970, 225, 425,
22+
628, 324, 322, 206, 912, 867, 462, 92
23+
];
24+
base[x % 64]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
- // MIR for `main` before GVN
2+
+ // MIR for `main` after GVN
3+
4+
fn main() -> () {
5+
let mut _0: ();
6+
let _1: [i32; 32];
7+
let mut _4: [i32; 12];
8+
let mut _5: [i32; 12];
9+
let mut _7: &[i32; 32];
10+
let _8: [i32; 32];
11+
let _9: ();
12+
let mut _10: [u16; 32];
13+
let mut _12: [f32; 8];
14+
let _13: [[i32; 3]; 3];
15+
let mut _14: [i32; 3];
16+
let mut _15: [i32; 3];
17+
let mut _16: [i32; 3];
18+
scope 1 {
19+
debug _arr => _1;
20+
let _2: [i32; 32];
21+
scope 2 {
22+
debug _barr => _2;
23+
let _3: [[i32; 12]; 2];
24+
scope 3 {
25+
debug _foo => _3;
26+
let _6: [i32; 32];
27+
let mut _17: &[i32; 32];
28+
scope 4 {
29+
debug _darr => _6;
30+
let _11: F32x8;
31+
scope 5 {
32+
debug _f => _11;
33+
}
34+
}
35+
}
36+
}
37+
}
38+
39+
bb0: {
40+
StorageLive(_1);
41+
- _1 = [const 255_i32, const 105_i32, const 15_i32, const 39_i32, const 62_i32, const 251_i32, const 191_i32, const 178_i32, const 9_i32, const 4_i32, const 56_i32, const 221_i32, const 193_i32, const 164_i32, const 194_i32, const 197_i32, const 6_i32, const 243_i32, const 218_i32, const 171_i32, const 87_i32, const 247_i32, const 104_i32, const 159_i32, const 22_i32, const 157_i32, const 105_i32, const 31_i32, const 96_i32, const 173_i32, const 50_i32, const 1_i32];
42+
+ _1 = const [255_i32, 105_i32, 15_i32, 39_i32, 62_i32, 251_i32, 191_i32, 178_i32, 9_i32, 4_i32, 56_i32, 221_i32, 193_i32, 164_i32, 194_i32, 197_i32, 6_i32, 243_i32, 218_i32, 171_i32, 87_i32, 247_i32, 104_i32, 159_i32, 22_i32, 157_i32, 105_i32, 31_i32, 96_i32, 173_i32, 50_i32, 1_i32];
43+
StorageLive(_2);
44+
- _2 = [const 255_i32, const 105_i32, const 15_i32, const 39_i32, const 62_i32, const 251_i32, const 191_i32, const 178_i32, const 9_i32, const 4_i32, const 56_i32, const 221_i32, const 193_i32, const 164_i32, const 194_i32, const 197_i32, const 6_i32, const 243_i32, const 218_i32, const 171_i32, const 87_i32, const 247_i32, const 104_i32, const 159_i32, const 22_i32, const 157_i32, const 105_i32, const 31_i32, const 96_i32, const 173_i32, const 50_i32, const 1_i32];
45+
+ _2 = const [255_i32, 105_i32, 15_i32, 39_i32, 62_i32, 251_i32, 191_i32, 178_i32, 9_i32, 4_i32, 56_i32, 221_i32, 193_i32, 164_i32, 194_i32, 197_i32, 6_i32, 243_i32, 218_i32, 171_i32, 87_i32, 247_i32, 104_i32, 159_i32, 22_i32, 157_i32, 105_i32, 31_i32, 96_i32, 173_i32, 50_i32, 1_i32];
46+
StorageLive(_3);
47+
StorageLive(_4);
48+
- _4 = [const 255_i32, const 105_i32, const 15_i32, const 39_i32, const 62_i32, const 251_i32, const 191_i32, const 178_i32, const 9_i32, const 4_i32, const 56_i32, const 221_i32];
49+
+ _4 = const [255_i32, 105_i32, 15_i32, 39_i32, 62_i32, 251_i32, 191_i32, 178_i32, 9_i32, 4_i32, 56_i32, 221_i32];
50+
StorageLive(_5);
51+
- _5 = [const 193_i32, const 164_i32, const 194_i32, const 197_i32, const 6_i32, const 243_i32, const 218_i32, const 171_i32, const 87_i32, const 247_i32, const 104_i32, const 42_i32];
52+
- _3 = [move _4, move _5];
53+
+ _5 = const [193_i32, 164_i32, 194_i32, 197_i32, 6_i32, 243_i32, 218_i32, 171_i32, 87_i32, 247_i32, 104_i32, 42_i32];
54+
+ _3 = [const [255_i32, 105_i32, 15_i32, 39_i32, 62_i32, 251_i32, 191_i32, 178_i32, 9_i32, 4_i32, 56_i32, 221_i32], const [193_i32, 164_i32, 194_i32, 197_i32, 6_i32, 243_i32, 218_i32, 171_i32, 87_i32, 247_i32, 104_i32, 42_i32]];
55+
StorageDead(_5);
56+
StorageDead(_4);
57+
StorageLive(_6);
58+
StorageLive(_7);
59+
_17 = const main::promoted[0];
60+
_7 = &(*_17);
61+
- _6 = (*_7);
62+
+ _6 = (*_17);
63+
StorageDead(_7);
64+
StorageLive(_9);
65+
StorageLive(_10);
66+
- _10 = [const 255_u16, const 105_u16, const 15_u16, const 39_u16, const 62_u16, const 251_u16, const 191_u16, const 178_u16, const 9_u16, const 4_u16, const 56_u16, const 221_u16, const 193_u16, const 164_u16, const 194_u16, const 197_u16, const 6_u16, const 243_u16, const 218_u16, const 171_u16, const 87_u16, const 247_u16, const 104_u16, const 159_u16, const 22_u16, const 157_u16, const 105_u16, const 31_u16, const 96_u16, const 173_u16, const 50_u16, const 1_u16];
67+
- _9 = consume(move _10) -> [return: bb1, unwind continue];
68+
+ _10 = const [255_u16, 105_u16, 15_u16, 39_u16, 62_u16, 251_u16, 191_u16, 178_u16, 9_u16, 4_u16, 56_u16, 221_u16, 193_u16, 164_u16, 194_u16, 197_u16, 6_u16, 243_u16, 218_u16, 171_u16, 87_u16, 247_u16, 104_u16, 159_u16, 22_u16, 157_u16, 105_u16, 31_u16, 96_u16, 173_u16, 50_u16, 1_u16];
69+
+ _9 = consume(const [255_u16, 105_u16, 15_u16, 39_u16, 62_u16, 251_u16, 191_u16, 178_u16, 9_u16, 4_u16, 56_u16, 221_u16, 193_u16, 164_u16, 194_u16, 197_u16, 6_u16, 243_u16, 218_u16, 171_u16, 87_u16, 247_u16, 104_u16, 159_u16, 22_u16, 157_u16, 105_u16, 31_u16, 96_u16, 173_u16, 50_u16, 1_u16]) -> [return: bb1, unwind continue];
70+
}
71+
72+
bb1: {
73+
StorageDead(_10);
74+
StorageDead(_9);
75+
StorageLive(_11);
76+
StorageLive(_12);
77+
- _12 = [const 1f32, const 2f32, const 3f32, const 1f32, const 1f32, const 1f32, const 1f32, const 42f32];
78+
- _11 = F32x8(move _12);
79+
+ _12 = const [1f32, 2f32, 3f32, 1f32, 1f32, 1f32, 1f32, 42f32];
80+
+ _11 = F32x8(const [1f32, 2f32, 3f32, 1f32, 1f32, 1f32, 1f32, 42f32]);
81+
StorageDead(_12);
82+
StorageLive(_13);
83+
StorageLive(_14);
84+
_14 = [const 1_i32, const 0_i32, const 0_i32];
85+
StorageLive(_15);
86+
_15 = [const 0_i32, const 1_i32, const 0_i32];
87+
StorageLive(_16);
88+
_16 = [const 0_i32, const 0_i32, const 1_i32];
89+
_13 = [move _14, move _15, move _16];
90+
StorageDead(_16);
91+
StorageDead(_15);
92+
StorageDead(_14);
93+
StorageDead(_13);
94+
_0 = const ();
95+
StorageDead(_11);
96+
StorageDead(_6);
97+
StorageDead(_3);
98+
StorageDead(_2);
99+
StorageDead(_1);
100+
return;
101+
}
102+
+ }
103+
+
104+
+ ALLOC0 (size: 32, align: 4) {
105+
+ 0x00 │ 00 00 80 3f 00 00 00 40 00 00 40 40 00 00 80 3f │ ...?...@..@@...?
106+
+ 0x10 │ 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 28 42 │ ...?...?...?..(B
107+
+ }
108+
+
109+
+ ALLOC1 (size: 64, align: 2) {
110+
+ 0x00 │ ff 00 69 00 0f 00 27 00 3e 00 fb 00 bf 00 b2 00 │ ..i...'.>.......
111+
+ 0x10 │ 09 00 04 00 38 00 dd 00 c1 00 a4 00 c2 00 c5 00 │ ....8...........
112+
+ 0x20 │ 06 00 f3 00 da 00 ab 00 57 00 f7 00 68 00 9f 00 │ ........W...h...
113+
+ 0x30 │ 16 00 9d 00 69 00 1f 00 60 00 ad 00 32 00 01 00 │ ....i...`...2...
114+
+ }
115+
+
116+
+ ALLOC2 (size: 48, align: 4) {
117+
+ 0x00 │ c1 00 00 00 a4 00 00 00 c2 00 00 00 c5 00 00 00 │ ................
118+
+ 0x10 │ 06 00 00 00 f3 00 00 00 da 00 00 00 ab 00 00 00 │ ................
119+
+ 0x20 │ 57 00 00 00 f7 00 00 00 68 00 00 00 2a 00 00 00 │ W.......h...*...
120+
+ }
121+
+
122+
+ ALLOC3 (size: 48, align: 4) {
123+
+ 0x00 │ ff 00 00 00 69 00 00 00 0f 00 00 00 27 00 00 00 │ ....i.......'...
124+
+ 0x10 │ 3e 00 00 00 fb 00 00 00 bf 00 00 00 b2 00 00 00 │ >...............
125+
+ 0x20 │ 09 00 00 00 04 00 00 00 38 00 00 00 dd 00 00 00 │ ........8.......
126+
+ }
127+
+
128+
+ ALLOC4 (size: 128, align: 4) {
129+
+ 0x00 │ ff 00 00 00 69 00 00 00 0f 00 00 00 27 00 00 00 │ ....i.......'...
130+
+ 0x10 │ 3e 00 00 00 fb 00 00 00 bf 00 00 00 b2 00 00 00 │ >...............
131+
+ 0x20 │ 09 00 00 00 04 00 00 00 38 00 00 00 dd 00 00 00 │ ........8.......
132+
+ 0x30 │ c1 00 00 00 a4 00 00 00 c2 00 00 00 c5 00 00 00 │ ................
133+
+ 0x40 │ 06 00 00 00 f3 00 00 00 da 00 00 00 ab 00 00 00 │ ................
134+
+ 0x50 │ 57 00 00 00 f7 00 00 00 68 00 00 00 9f 00 00 00 │ W.......h.......
135+
+ 0x60 │ 16 00 00 00 9d 00 00 00 69 00 00 00 1f 00 00 00 │ ........i.......
136+
+ 0x70 │ 60 00 00 00 ad 00 00 00 32 00 00 00 01 00 00 00 │ `.......2.......
137+
}
138+

0 commit comments

Comments
 (0)