Skip to content

Commit 98c5649

Browse files
use undef for uninitialized bytes in constants
1 parent 65b44b0 commit 98c5649

File tree

5 files changed

+118
-16
lines changed

5 files changed

+118
-16
lines changed

compiler/rustc_codegen_llvm/src/consts.rs

+30-13
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,47 @@ use crate::value::Value;
88
use cstr::cstr;
99
use libc::c_uint;
1010
use rustc_codegen_ssa::traits::*;
11+
use rustc_data_structures::captures::Captures;
1112
use rustc_hir::def_id::DefId;
1213
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
1314
use rustc_middle::mir::interpret::{
14-
read_target_uint, Allocation, ErrorHandled, GlobalAlloc, Pointer,
15+
read_target_uint, Allocation, ErrorHandled, GlobalAlloc, InitChunk, Pointer,
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::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size};
21+
use std::ops::Range;
2022
use tracing::debug;
2123

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

29+
// Note: this function may call `inspect_with_uninit_and_ptr_outside_interpreter`,
30+
// so `range` must be within the bounds of `alloc` and not within a relocation.
31+
fn chunks_of_init_and_uninit_bytes<'ll, 'a, 'b>(
32+
cx: &'a CodegenCx<'ll, 'b>,
33+
alloc: &'a Allocation,
34+
range: Range<usize>,
35+
) -> impl Iterator<Item = &'ll Value> + Captures<'a> + Captures<'b> {
36+
alloc
37+
.init_mask()
38+
.range_as_init_chunks(Size::from_bytes(range.start), Size::from_bytes(range.end))
39+
.map(move |chunk| match chunk {
40+
InitChunk::Init(range) => {
41+
let range = (range.start.bytes() as usize)..(range.end.bytes() as usize);
42+
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
43+
cx.const_bytes(bytes)
44+
}
45+
InitChunk::Uninit(range) => {
46+
let len = range.end.bytes() - range.start.bytes();
47+
cx.const_undef(cx.type_array(cx.type_i8(), len))
48+
}
49+
})
50+
}
51+
2752
let mut next_offset = 0;
2853
for &(offset, ((), alloc_id)) in alloc.relocations().iter() {
2954
let offset = offset.bytes();
@@ -32,12 +57,8 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
3257
if offset > next_offset {
3358
// This `inspect` is okay since we have checked that it is not within a relocation, it
3459
// is within the bounds of the allocation, and it doesn't affect interpreter execution
35-
// (we inspect the result after interpreter execution). Any undef byte is replaced with
36-
// some arbitrary byte value.
37-
//
38-
// FIXME: relay undef bytes to codegen as undef const bytes
39-
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset);
40-
llvals.push(cx.const_bytes(bytes));
60+
// (we inspect the result after interpreter execution).
61+
llvals.extend(chunks_of_init_and_uninit_bytes(cx, alloc, next_offset..offset));
4162
}
4263
let ptr_offset = read_target_uint(
4364
dl.endian,
@@ -65,12 +86,8 @@ pub fn const_alloc_to_llvm(cx: &CodegenCx<'ll, '_>, alloc: &Allocation) -> &'ll
6586
let range = next_offset..alloc.len();
6687
// This `inspect` is okay since we have check that it is after all relocations, it is
6788
// within the bounds of the allocation, and it doesn't affect interpreter execution (we
68-
// inspect the result after interpreter execution). Any undef byte is replaced with some
69-
// arbitrary byte value.
70-
//
71-
// FIXME: relay undef bytes to codegen as undef const bytes
72-
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range);
73-
llvals.push(cx.const_bytes(bytes));
89+
// inspect the result after interpreter execution).
90+
llvals.extend(chunks_of_init_and_uninit_bytes(cx, alloc, range));
7491
}
7592

7693
cx.const_struct(&llvals, true)

compiler/rustc_middle/src/mir/interpret/allocation.rs

+51
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,13 @@ impl InitMask {
775775
}
776776
}
777777

778+
/// Returns an iterator, yielding a range of byte indexes for each contiguous region
779+
/// of initialized or uninitialized bytes inside the range `start..end` (end-exclusive).
780+
#[inline]
781+
pub fn range_as_init_chunks(&self, start: Size, end: Size) -> InitChunkIter<'_> {
782+
InitChunkIter::new(self, start, end)
783+
}
784+
778785
pub fn set_range(&mut self, start: Size, end: Size, new_state: bool) {
779786
let len = self.len;
780787
if end > len {
@@ -867,6 +874,50 @@ impl InitMask {
867874
}
868875
}
869876

877+
/// Yields [`InitChunk`]s. See [`InitMask::range_as_init_chunks`].
878+
pub struct InitChunkIter<'a> {
879+
init_mask: &'a InitMask,
880+
/// The current byte index into `init_mask`.
881+
start: Size,
882+
/// The end byte index into `init_mask`.
883+
end: Size,
884+
}
885+
886+
/// A contiguous chunk of initialized or uninitialized memory.
887+
pub enum InitChunk {
888+
Init(Range<Size>),
889+
Uninit(Range<Size>),
890+
}
891+
892+
impl<'a> InitChunkIter<'a> {
893+
fn new(init_mask: &'a InitMask, start: Size, end: Size) -> Self {
894+
assert!(start <= end);
895+
assert!(end <= init_mask.len);
896+
Self { init_mask, start, end }
897+
}
898+
}
899+
900+
impl<'a> Iterator for InitChunkIter<'a> {
901+
type Item = InitChunk;
902+
903+
fn next(&mut self) -> Option<Self::Item> {
904+
if self.start >= self.end {
905+
return None;
906+
}
907+
908+
let is_init = self.init_mask.get(self.start);
909+
let end_of_chunk = (self.start.bytes()..self.end.bytes())
910+
.map(Size::from_bytes)
911+
.find(|&i| self.init_mask.get(i) != is_init)
912+
.unwrap_or(self.end);
913+
let range = self.start..end_of_chunk;
914+
915+
self.start = end_of_chunk;
916+
917+
Some(if is_init { InitChunk::Init(range) } else { InitChunk::Uninit(range) })
918+
}
919+
}
920+
870921
#[inline]
871922
fn bit_index(bits: Size) -> (usize, usize) {
872923
let bits = bits.bytes();

compiler/rustc_middle/src/mir/interpret/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ pub use self::error::{
125125

126126
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar, ScalarMaybeUninit};
127127

128-
pub use self::allocation::{Allocation, AllocationExtra, InitMask, Relocations};
128+
pub use self::allocation::{
129+
Allocation, AllocationExtra, InitChunk, InitChunkIter, InitMask, Relocations,
130+
};
129131

130132
pub use self::pointer::{Pointer, PointerArithmetic};
131133

src/test/codegen/consts.rs

+2-2
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 (<{ [8 x i8] }>, <{ [8 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 (<{ [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)
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 (<{ [8 x i8] }>, <{ [8 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 (<{ [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)
5555
*&E::A(0)
5656
}

src/test/codegen/uninit-consts.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// compile-flags: -C no-prepopulate-passes
2+
// ignore-tidy-linelength
3+
4+
// Check that we use undef (and not zero) for uninitialized bytes in constants.
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+
// CHECK: [[FULLY_UNINIT:@[0-9]+]] = private unnamed_addr constant <{ [10 x i8] }> 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+
// CHECK-LABEL: @fully_uninit
19+
#[no_mangle]
20+
pub const fn fully_uninit() -> MaybeUninit<[u8; 10]> {
21+
const M: MaybeUninit<[u8; 10]> = MaybeUninit::uninit();
22+
// CHECK: call void @llvm.memcpy.p0i8.p0i8.i{{(32|64)}}(i8* align 1 %1, i8* align 1 getelementptr inbounds (<{ [10 x i8] }>, <{ [10 x i8] }>* [[FULLY_UNINIT]], i32 0, i32 0, i32 0), i{{(32|64)}} 10, i1 false)
23+
M
24+
}
25+
26+
// CHECK-LABEL: @partially_uninit
27+
#[no_mangle]
28+
pub const fn partially_uninit() -> PartiallyUninit {
29+
const X: PartiallyUninit = PartiallyUninit { x: 0xdeadbeef, y: MaybeUninit::uninit() };
30+
// 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)
31+
X
32+
}

0 commit comments

Comments
 (0)