Skip to content

Commit 20997f6

Browse files
committed
Auto merge of #83698 - erikdesjardins:undefconst, r=RalfJung,oli-obk
Use undef for uninitialized bytes in constants Fixes #83657 This generates good code when the const is fully uninit, e.g. ```rust #[no_mangle] pub const fn fully_uninit() -> MaybeUninit<[u8; 10]> { const M: MaybeUninit<[u8; 10]> = MaybeUninit::uninit(); M } ``` generates ```asm fully_uninit: ret ``` as you would expect. There is no improvement, however, when it's partially uninit, e.g. ```rust pub struct PartiallyUninit { x: u64, y: MaybeUninit<[u8; 10]> } #[no_mangle] pub const fn partially_uninit() -> PartiallyUninit { const X: PartiallyUninit = PartiallyUninit { x: 0xdeadbeefcafe, y: MaybeUninit::uninit() }; X } ``` generates ```asm partially_uninit: mov rax, rdi mov rcx, qword ptr [rip + .L__unnamed_1+16] mov qword ptr [rdi + 16], rcx movups xmm0, xmmword ptr [rip + .L__unnamed_1] movups xmmword ptr [rdi], xmm0 ret .L__unnamed_1: .asciz "\376\312\357\276\255\336\000" .zero 16 .size .L__unnamed_1, 24 ``` which copies a bunch of zeros in place of the undef bytes, the same as before this change. Edit: generating partially-undef constants isn't viable at the moment anyways due to #84565, so it's disabled
2 parents 3b3ce37 + adf3b01 commit 20997f6

File tree

9 files changed

+615
-173
lines changed

9 files changed

+615
-173
lines changed

compiler/rustc_codegen_llvm/src/consts.rs

+58-13
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,74 @@ use rustc_codegen_ssa::traits::*;
1111
use rustc_hir::def_id::DefId;
1212
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
1313
use rustc_middle::mir::interpret::{
14-
read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer, Scalar as InterpScalar,
14+
read_target_uint, Allocation, ErrorHandled, GlobalAlloc, InitChunk, Pointer,
15+
Scalar as InterpScalar,
1516
};
1617
use rustc_middle::mir::mono::MonoItem;
1718
use rustc_middle::ty::{self, Instance, Ty};
1819
use rustc_middle::{bug, span_bug};
1920
use rustc_target::abi::{
2021
AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size, WrappingRange,
2122
};
23+
use std::ops::Range;
2224
use tracing::debug;
2325

2426
pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll Value {
2527
let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1);
2628
let dl = cx.data_layout();
2729
let pointer_size = dl.pointer_size.bytes() as usize;
2830

31+
// Note: this function may call `inspect_with_uninit_and_ptr_outside_interpreter`,
32+
// so `range` must be within the bounds of `alloc` and not contain or overlap a relocation.
33+
fn append_chunks_of_init_and_uninit_bytes<'ll, 'a, 'b>(
34+
llvals: &mut Vec<&'ll Value>,
35+
cx: &'a CodegenCx<'ll, 'b>,
36+
alloc: &'a Allocation,
37+
range: Range<usize>,
38+
) {
39+
let mut chunks = alloc
40+
.init_mask()
41+
.range_as_init_chunks(Size::from_bytes(range.start), Size::from_bytes(range.end));
42+
43+
let chunk_to_llval = move |chunk| match chunk {
44+
InitChunk::Init(range) => {
45+
let range = (range.start.bytes() as usize)..(range.end.bytes() as usize);
46+
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
47+
cx.const_bytes(bytes)
48+
}
49+
InitChunk::Uninit(range) => {
50+
let len = range.end.bytes() - range.start.bytes();
51+
cx.const_undef(cx.type_array(cx.type_i8(), len))
52+
}
53+
};
54+
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 {
66+
let llval = match (chunks.next(), chunks.next()) {
67+
(Some(chunk), None) => {
68+
// exactly one chunk, either fully init or fully uninit
69+
chunk_to_llval(chunk)
70+
}
71+
_ => {
72+
// partially uninit, codegen as if it was initialized
73+
// (using some arbitrary value for uninit bytes)
74+
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
75+
cx.const_bytes(bytes)
76+
}
77+
};
78+
llvals.push(llval);
79+
}
80+
}
81+
2982
let mut next_offset = 0;
3083
for &(offset, alloc_id) in alloc.relocations().iter() {
3184
let offset = offset.bytes();
@@ -34,12 +87,8 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
3487
if offset > next_offset {
3588
// This `inspect` is okay since we have checked that it is not within a relocation, it
3689
// is within the bounds of the allocation, and it doesn't affect interpreter execution
37-
// (we inspect the result after interpreter execution). Any undef byte is replaced with
38-
// some arbitrary byte value.
39-
//
40-
// FIXME: relay undef bytes to codegen as undef const bytes
41-
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset);
42-
llvals.push(cx.const_bytes(bytes));
90+
// (we inspect the result after interpreter execution).
91+
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, next_offset..offset);
4392
}
4493
let ptr_offset = read_target_uint(
4594
dl.endian,
@@ -70,12 +119,8 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
70119
let range = next_offset..alloc.len();
71120
// This `inspect` is okay since we have check that it is after all relocations, it is
72121
// within the bounds of the allocation, and it doesn't affect interpreter execution (we
73-
// inspect the result after interpreter execution). Any undef byte is replaced with some
74-
// arbitrary byte value.
75-
//
76-
// FIXME: relay undef bytes to codegen as undef const bytes
77-
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
78-
llvals.push(cx.const_bytes(bytes));
122+
// inspect the result after interpreter execution).
123+
append_chunks_of_init_and_uninit_bytes(&mut llvals, cx, alloc, range);
79124
}
80125

81126
cx.const_struct(&llvals, true)

compiler/rustc_interface/src/tests.rs

+1
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);

0 commit comments

Comments
 (0)