Skip to content

Commit 5e81d64

Browse files
don't generate partially-undef consts
1 parent 4d635fd commit 5e81d64

File tree

6 files changed

+56
-24
lines changed

6 files changed

+56
-24
lines changed

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
3636
alloc: &'a Allocation,
3737
range: Range<usize>,
3838
) {
39-
/// Allocations larger than this will only be codegen'd as entirely initialized or entirely undef.
40-
/// This avoids compile time regressions when an alloc would have many chunks,
41-
/// e.g. for `[(u64, u8); N]`, which has undef padding in each element.
42-
const MAX_PARTIALLY_UNDEF_SIZE: usize = 1024;
43-
4439
let mut chunks = alloc
4540
.init_mask()
4641
.range_as_init_chunks(Size::from_bytes(range.start), Size::from_bytes(range.end));
@@ -57,21 +52,30 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
5752
}
5853
};
5954

60-
if range.len() > MAX_PARTIALLY_UNDEF_SIZE {
55+
// Generating partially-uninit consts inhibits optimizations, so it is disabled by default.
56+
// See https://github.com/rust-lang/rust/issues/84565.
57+
let allow_partially_uninit =
58+
match cx.sess().opts.debugging_opts.partially_uninit_const_threshold {
59+
Some(max) => range.len() <= max,
60+
None => false,
61+
};
62+
63+
if allow_partially_uninit {
64+
llvals.extend(chunks.map(chunk_to_llval));
65+
} else {
6166
let llval = match (chunks.next(), chunks.next()) {
6267
(Some(chunk), None) => {
6368
// exactly one chunk, either fully init or fully uninit
6469
chunk_to_llval(chunk)
6570
}
6671
_ => {
67-
// partially uninit
72+
// partially uninit, codegen as if it was initialized
73+
// (using some arbitrary value for uninit bytes)
6874
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
6975
cx.const_bytes(bytes)
7076
}
7177
};
7278
llvals.push(llval);
73-
} else {
74-
llvals.extend(chunks.map(chunk_to_llval));
7579
}
7680
}
7781

compiler/rustc_interface/src/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ fn test_debugging_options_tracking_hash() {
743743
tracked!(no_profiler_runtime, true);
744744
tracked!(osx_rpath_install_name, true);
745745
tracked!(panic_abort_tests, true);
746+
tracked!(partially_uninit_const_threshold, Some(123));
746747
tracked!(plt, Some(true));
747748
tracked!(polonius, true);
748749
tracked!(precise_enum_drop_elaboration, false);

compiler/rustc_session/src/options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,6 +1186,9 @@ options! {
11861186
"support compiling tests with panic=abort (default: no)"),
11871187
parse_only: bool = (false, parse_bool, [UNTRACKED],
11881188
"parse only; do not compile, assemble, or link (default: no)"),
1189+
partially_uninit_const_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
1190+
"allow generating const initializers with mixed init/uninit bytes, \
1191+
and set the maximum total size of a const allocation for which this is allowed (default: never)"),
11891192
perf_stats: bool = (false, parse_bool, [UNTRACKED],
11901193
"print some performance-related statistics (default: no)"),
11911194
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],

src/test/codegen/consts.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ pub fn inline_enum_const() -> E<i8, i16> {
4343
#[no_mangle]
4444
pub fn low_align_const() -> E<i16, [i16; 3]> {
4545
// Check that low_align_const and high_align_const use the same constant
46-
// CHECK: memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 2 %1, i8* align 2 getelementptr inbounds (<{ [4 x i8], [4 x i8] }>, <{ [4 x i8], [4 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i{{(32|64)}} 8, i1 false)
46+
// CHECK: memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 2 %1, i8* align 2 getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i{{(32|64)}} 8, i1 false)
4747
*&E::A(0)
4848
}
4949

5050
// CHECK-LABEL: @high_align_const
5151
#[no_mangle]
5252
pub fn high_align_const() -> E<i16, i32> {
5353
// Check that low_align_const and high_align_const use the same constant
54-
// CHECK: memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [4 x i8], [4 x i8] }>, <{ [4 x i8], [4 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i{{(32|64)}} 8, i1 false)
54+
// CHECK: memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [8 x i8] }>, <{ [8 x i8] }>* [[LOW_HIGH]], i32 0, i32 0, i32 0), i{{(32|64)}} 8, i1 false)
5555
*&E::A(0)
5656
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// compile-flags: -C no-prepopulate-passes -Z partially_uninit_const_threshold=1024
2+
3+
// Like uninit-consts.rs, but tests that we correctly generate partially-uninit consts
4+
// when the (disabled by default) partially_uninit_const_threshold flag is used.
5+
6+
#![crate_type = "lib"]
7+
8+
use std::mem::MaybeUninit;
9+
10+
pub struct PartiallyUninit {
11+
x: u32,
12+
y: MaybeUninit<[u8; 10]>
13+
}
14+
15+
// This should be partially undef.
16+
// CHECK: [[PARTIALLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [4 x i8], [12 x i8] }> <{ [4 x i8] c"\EF\BE\AD\DE", [12 x i8] undef }>, align 4
17+
18+
// This shouldn't contain undef, since it's larger than the 1024 byte limit.
19+
// CHECK: [[UNINIT_PADDING_HUGE:@[0-9]+]] = private unnamed_addr constant <{ [32768 x i8] }> <{ [32768 x i8] c"{{.+}}" }>, align 4
20+
21+
// CHECK-LABEL: @partially_uninit
22+
#[no_mangle]
23+
pub const fn partially_uninit() -> PartiallyUninit {
24+
const X: PartiallyUninit = PartiallyUninit { x: 0xdeadbeef, y: MaybeUninit::uninit() };
25+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [4 x i8], [12 x i8] }>, <{ [4 x i8], [12 x i8] }>* [[PARTIALLY_UNINIT]], i32 0, i32 0, i32 0), i{{(32|64)}} 16, i1 false)
26+
X
27+
}
28+
29+
// CHECK-LABEL: @uninit_padding_huge
30+
#[no_mangle]
31+
pub const fn uninit_padding_huge() -> [(u32, u8); 4096] {
32+
const X: [(u32, u8); 4096] = [(123, 45); 4096];
33+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [32768 x i8] }>, <{ [32768 x i8] }>* [[UNINIT_PADDING_HUGE]], i32 0, i32 0, i32 0), i{{(32|64)}} 32768, i1 false)
34+
X
35+
}

src/test/codegen/uninit-consts.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,9 @@ pub struct PartiallyUninit {
1212
}
1313

1414
// CHECK: [[FULLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [10 x i8] }> undef
15-
// CHECK: [[PARTIALLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [4 x i8], [12 x i8] }> <{ [4 x i8] c"\EF\BE\AD\DE", [12 x i8] undef }>, align 4
15+
// CHECK: [[PARTIALLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [16 x i8] }> <{ [16 x i8] c"\EF\BE\AD\DE\00\00\00\00\00\00\00\00\00\00\00\00" }>, align 4
1616
// CHECK: [[FULLY_UNINIT_HUGE:@[0-9]+]] = private unnamed_addr constant <{ [16384 x i8] }> undef
1717

18-
// This shouldn't contain undef, since generating huge partially undef constants is expensive.
19-
// CHECK: [[UNINIT_PADDING_HUGE:@[0-9]+]] = private unnamed_addr constant <{ [32768 x i8] }> <{ [32768 x i8] c"{{.+}}" }>, align 4
20-
2118
// CHECK-LABEL: @fully_uninit
2219
#[no_mangle]
2320
pub const fn fully_uninit() -> MaybeUninit<[u8; 10]> {
@@ -30,7 +27,7 @@ pub const fn fully_uninit() -> MaybeUninit<[u8; 10]> {
3027
#[no_mangle]
3128
pub const fn partially_uninit() -> PartiallyUninit {
3229
const X: PartiallyUninit = PartiallyUninit { x: 0xdeadbeef, y: MaybeUninit::uninit() };
33-
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [4 x i8], [12 x i8] }>, <{ [4 x i8], [12 x i8] }>* [[PARTIALLY_UNINIT]], i32 0, i32 0, i32 0), i{{(32|64)}} 16, i1 false)
30+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [16 x i8] }>, <{ [16 x i8] }>* [[PARTIALLY_UNINIT]], i32 0, i32 0, i32 0), i{{(32|64)}} 16, i1 false)
3431
X
3532
}
3633

@@ -41,11 +38,3 @@ pub const fn fully_uninit_huge() -> MaybeUninit<[u32; 4096]> {
4138
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [16384 x i8] }>, <{ [16384 x i8] }>* [[FULLY_UNINIT_HUGE]], i32 0, i32 0, i32 0), i{{(32|64)}} 16384, i1 false)
4239
F
4340
}
44-
45-
// CHECK-LABEL: @uninit_padding_huge
46-
#[no_mangle]
47-
pub const fn uninit_padding_huge() -> [(u32, u8); 4096] {
48-
const X: [(u32, u8); 4096] = [(123, 45); 4096];
49-
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 4 %1, i8* align 4 getelementptr inbounds (<{ [32768 x i8] }>, <{ [32768 x i8] }>* [[UNINIT_PADDING_HUGE]], i32 0, i32 0, i32 0), i{{(32|64)}} 32768, i1 false)
50-
X
51-
}

0 commit comments

Comments
 (0)