Skip to content

Commit 1a410f7

Browse files
committed
Fix MaybeUninit codegen using GVN
1 parent 4ddbb60 commit 1a410f7

File tree

11 files changed

+105
-16
lines changed

11 files changed

+105
-16
lines changed

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2424
) {
2525
match *rvalue {
2626
mir::Rvalue::Use(ref operand) => {
27+
if let mir::Operand::Constant(const_op) = operand {
28+
let val = self.eval_mir_constant(&const_op);
29+
if val.all_bytes_uninit(self.cx.tcx()) {
30+
return;
31+
}
32+
}
2733
let cg_operand = self.codegen_operand(bx, operand);
2834
// Crucially, we do *not* use `OperandValue::Ref` for types with
2935
// `BackendRepr::Scalar | BackendRepr::ScalarPair`. This ensures we match the MIR

compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,10 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
522522
pub(super) fn op(&self) -> &Operand<Prov> {
523523
&self.op
524524
}
525+
526+
pub fn is_immediate_uninit(&self) -> bool {
527+
matches!(self.op, Operand::Immediate(Immediate::Uninit))
528+
}
525529
}
526530

527531
impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {

compiler/rustc_middle/src/mir/pretty.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,13 @@ fn pretty_print_const_value_tcx<'tcx>(
18611861
return Ok(());
18621862
}
18631863

1864+
// Printing [MaybeUninit<u8>::uninit(); N] or any other aggregate where all fields are uninit
1865+
// becomes very verbose. This special case makes the dump terse and clear.
1866+
if ct.all_bytes_uninit(tcx) {
1867+
fmt.write_str("<uninit>")?;
1868+
return Ok(());
1869+
}
1870+
18641871
let u8_type = tcx.types.u8;
18651872
match (ct, ty.kind()) {
18661873
// Byte/string slices, printed as (byte) string literals.

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -570,9 +570,19 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
570570
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
571571

572572
Opaque(_) => return None,
573-
// Do not bother evaluating repeat expressions. This would uselessly consume memory.
574-
Repeat(..) => return None,
575573

574+
// In general, evaluating repeat expressions just consumes a lot of memory.
575+
// But in the special case that the element is just Immediate::Uninit, we can evaluate
576+
// it without extra memory! If we don't propagate uninit values like this, LLVM can get
577+
// very confused: https://github.com/rust-lang/rust/issues/139355
578+
Repeat(value, _count) => {
579+
let value = self.eval_to_const(value)?;
580+
if value.is_immediate_uninit() {
581+
ImmTy::uninit(ty).into()
582+
} else {
583+
return None;
584+
}
585+
}
576586
Constant { ref value, disambiguator: _ } => {
577587
self.ecx.eval_mir_constant(value, DUMMY_SP, None).discard_err()?
578588
}
@@ -608,8 +618,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
608618
}
609619
Union(active_field, field) => {
610620
let field = self.eval_to_const(field)?;
611-
if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
612-
{
621+
if field.layout.layout.is_zst() {
622+
ImmTy::from_immediate(Immediate::Uninit, ty).into()
623+
} else if matches!(
624+
ty.backend_repr,
625+
BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)
626+
) {
613627
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
614628
let field_dest = self.ecx.project_field(&dest, active_field).discard_err()?;
615629
self.ecx.copy_op(field, &field_dest).discard_err()?;
@@ -1711,7 +1725,11 @@ fn op_to_prop_const<'tcx>(
17111725

17121726
// Do not synthetize too large constants. Codegen will just memcpy them, which we'd like to
17131727
// avoid.
1714-
if !matches!(op.layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) {
1728+
// But we *do* want to synthesize any size constant if it is entirely uninit because that
1729+
// benefits codegen, which has special handling for them.
1730+
if !op.is_immediate_uninit()
1731+
&& !matches!(op.layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
1732+
{
17151733
return None;
17161734
}
17171735

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// This is a regression test for https://github.com/rust-lang/rust/issues/139355
2+
3+
#![crate_type = "lib"]
4+
5+
use std::mem::MaybeUninit;
6+
7+
#[no_mangle]
8+
pub fn create_uninit_array() -> [[MaybeUninit<u8>; 4]; 200] {
9+
// CHECK-LABEL: create_uninit_array
10+
// CHECK-NEXT: start:
11+
// CHECK-NEXT: ret void
12+
[[MaybeUninit::<u8>::uninit(); 4]; 200]
13+
}

tests/codegen-llvm/uninit-consts.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,17 @@ pub struct PartiallyUninit {
1111
y: MaybeUninit<[u8; 10]>,
1212
}
1313

14-
// CHECK: [[FULLY_UNINIT:@.*]] = private unnamed_addr constant [10 x i8] undef
15-
1614
// CHECK: [[PARTIALLY_UNINIT:@.*]] = private unnamed_addr constant <{ [4 x i8], [12 x i8] }> <{ [4 x i8] c"{{\\EF\\BE\\AD\\DE|\\DE\\AD\\BE\\EF}}", [12 x i8] undef }>, align 4
1715

1816
// This shouldn't contain undef, since it contains more chunks
1917
// than the default value of uninit_const_chunk_threshold.
2018
// CHECK: [[UNINIT_PADDING_HUGE:@.*]] = private unnamed_addr constant [32768 x i8] c"{{.+}}", align 4
2119

22-
// CHECK: [[FULLY_UNINIT_HUGE:@.*]] = private unnamed_addr constant [16384 x i8] undef
23-
2420
// CHECK-LABEL: @fully_uninit
2521
#[no_mangle]
2622
pub const fn fully_uninit() -> MaybeUninit<[u8; 10]> {
2723
const M: MaybeUninit<[u8; 10]> = MaybeUninit::uninit();
28-
// CHECK: call void @llvm.memcpy.{{.+}}(ptr align 1 %_0, ptr align 1 {{.*}}[[FULLY_UNINIT]]{{.*}}, i{{(32|64)}} 10, i1 false)
24+
// CHECK: ret void
2925
M
3026
}
3127

@@ -49,6 +45,6 @@ pub const fn uninit_padding_huge() -> [(u32, u8); 4096] {
4945
#[no_mangle]
5046
pub const fn fully_uninit_huge() -> MaybeUninit<[u32; 4096]> {
5147
const F: MaybeUninit<[u32; 4096]> = MaybeUninit::uninit();
52-
// CHECK: call void @llvm.memcpy.{{.+}}(ptr align 4 %_0, ptr align 4 {{.*}}[[FULLY_UNINIT_HUGE]]{{.*}}, i{{(32|64)}} 16384, i1 false)
48+
// CHECK: ret void
5349
F
5450
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ compile-flags: -O
2+
3+
use std::mem::MaybeUninit;
4+
5+
// EMIT_MIR maybe_uninit.u8_array.GVN.diff
6+
pub fn u8_array() -> [MaybeUninit<u8>; 8] {
7+
// CHECK: fn u8_array(
8+
// CHECK: _0 = const <uninit>;
9+
[MaybeUninit::uninit(); 8]
10+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
- // MIR for `u8_array` before GVN
2+
+ // MIR for `u8_array` after GVN
3+
4+
fn u8_array() -> [MaybeUninit<u8>; 8] {
5+
let mut _0: [std::mem::MaybeUninit<u8>; 8];
6+
let mut _1: std::mem::MaybeUninit<u8>;
7+
scope 1 (inlined MaybeUninit::<u8>::uninit) {
8+
}
9+
10+
bb0: {
11+
StorageLive(_1);
12+
- _1 = MaybeUninit::<u8> { uninit: const () };
13+
- _0 = [move _1; 8];
14+
+ _1 = const <uninit>;
15+
+ _0 = const <uninit>;
16+
StorageDead(_1);
17+
return;
18+
}
19+
+ }
20+
+
21+
+ ALLOC0 (size: 8, align: 1) {
22+
+ __ __ __ __ __ __ __ __ │ ░░░░░░░░
23+
+ }
24+
+
25+
+ ALLOC1 (size: 1, align: 1) {
26+
+ __ │ ░
27+
}
28+

tests/mir-opt/const_prop/transmute.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ pub unsafe fn invalid_bool() -> bool {
4343
// EMIT_MIR transmute.undef_union_as_integer.GVN.diff
4444
pub unsafe fn undef_union_as_integer() -> u32 {
4545
// CHECK-LABEL: fn undef_union_as_integer(
46-
// CHECK: _1 = const Union32
47-
// CHECK: _0 = const {{.*}}: u32;
46+
// CHECK: _0 = const <uninit>;
4847
union Union32 {
4948
value: u32,
5049
unit: (),

tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@
1515
+ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }};
1616
StorageDead(_2);
1717
- _0 = move _1 as u32 (Transmute);
18-
+ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32;
18+
+ _0 = const Indirect { alloc_id: ALLOC1, offset: Size(0 bytes) }: u32;
1919
StorageDead(_1);
2020
return;
2121
}
2222
+ }
2323
+
24+
+ ALLOC1 (size: 4, align: 4) {
25+
+ __ __ __ __ │ ░░░░
26+
+ }
27+
+
2428
+ ALLOC0 (size: 4, align: 4) {
2529
+ __ __ __ __ │ ░░░░
2630
}

0 commit comments

Comments
 (0)