Skip to content

Commit ca1798b

Browse files
committed
Auto merge of rust-lang#135335 - oli-obk:push-zxwssomxxtnq, r=<try>
Treat undef bytes as equal to any other byte Basically since `undef` can be any byte, it can also be the byte(s) that are in the non-undef parts of a value. So we can just treat the `undef` at not being there and only look at the initialized bytes and memset over them fixes rust-lang#104290 based on rust-lang#135258
2 parents 252b07b + e64a5a0 commit ca1798b

File tree

8 files changed

+103
-33
lines changed

8 files changed

+103
-33
lines changed

compiler/rustc_codegen_llvm/src/common.rs

+4
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
126126
unsafe { llvm::LLVMGetUndef(t) }
127127
}
128128

129+
fn is_undef(&self, v: &'ll Value) -> bool {
130+
unsafe { llvm::LLVMIsUndef(v) == True }
131+
}
132+
129133
fn const_poison(&self, t: &'ll Type) -> &'ll Value {
130134
unsafe { llvm::LLVMGetPoison(t) }
131135
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,7 @@ unsafe extern "C" {
917917
pub fn LLVMMetadataTypeInContext(C: &Context) -> &Type;
918918

919919
// Operations on all values
920+
pub fn LLVMIsUndef(Val: &Value) -> Bool;
920921
pub fn LLVMTypeOf(Val: &Value) -> &Type;
921922
pub fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char;
922923
pub fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t);

compiler/rustc_codegen_ssa/src/mir/operand.rs

+30-22
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
205205
assert!(alloc_align >= layout.align.abi);
206206

207207
let read_scalar = |start, size, s: abi::Scalar, ty| {
208+
let range = alloc_range(start, size);
208209
match alloc.0.read_scalar(
209210
bx,
210-
alloc_range(start, size),
211+
range,
211212
/*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)),
212213
) {
213-
Ok(val) => bx.scalar_to_backend(val, s, ty),
214-
Err(_) => bx.const_poison(ty),
214+
Ok(val) => Some(bx.scalar_to_backend(val, s, ty)),
215+
Err(_) => {
216+
if alloc.0.provenance().range_empty(range, &bx.tcx())
217+
&& alloc.0.init_mask().is_range_initialized(range).unwrap_err() == range
218+
{
219+
Some(bx.const_undef(ty))
220+
} else {
221+
None
222+
}
223+
}
215224
}
216225
};
217226

@@ -222,16 +231,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
222231
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
223232
// like a `Scalar` (or `ScalarPair`).
224233
match layout.backend_repr {
225-
BackendRepr::Scalar(s @ abi::Scalar::Initialized { .. }) => {
234+
BackendRepr::Scalar(s) => {
226235
let size = s.size(bx);
227236
assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
228-
let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout));
229-
OperandRef { val: OperandValue::Immediate(val), layout }
237+
if let Some(val) = read_scalar(offset, size, s, bx.immediate_backend_type(layout)) {
238+
return OperandRef { val: OperandValue::Immediate(val), layout };
239+
}
230240
}
231-
BackendRepr::ScalarPair(
232-
a @ abi::Scalar::Initialized { .. },
233-
b @ abi::Scalar::Initialized { .. },
234-
) => {
241+
BackendRepr::ScalarPair(a, b) => {
235242
let (a_size, b_size) = (a.size(bx), b.size(bx));
236243
let b_offset = (offset + a_size).align_to(b.align(bx).abi);
237244
assert!(b_offset.bytes() > 0);
@@ -247,20 +254,21 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
247254
b,
248255
bx.scalar_pair_element_backend_type(layout, 1, true),
249256
);
250-
OperandRef { val: OperandValue::Pair(a_val, b_val), layout }
251-
}
252-
_ if layout.is_zst() => OperandRef::zero_sized(layout),
253-
_ => {
254-
// Neither a scalar nor scalar pair. Load from a place
255-
// FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the
256-
// same `ConstAllocation`?
257-
let init = bx.const_data_from_alloc(alloc);
258-
let base_addr = bx.static_addr_of(init, alloc_align, None);
259-
260-
let llval = bx.const_ptr_byte_offset(base_addr, offset);
261-
bx.load_operand(PlaceRef::new_sized(llval, layout))
257+
if let (Some(a_val), Some(b_val)) = (a_val, b_val) {
258+
return OperandRef { val: OperandValue::Pair(a_val, b_val), layout };
259+
}
262260
}
261+
_ if layout.is_zst() => return OperandRef::zero_sized(layout),
262+
_ => {}
263263
}
264+
// Neither a scalar nor scalar pair. Load from a place
265+
// FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the
266+
// same `ConstAllocation`?
267+
let init = bx.const_data_from_alloc(alloc);
268+
let base_addr = bx.static_addr_of(init, alloc_align, None);
269+
270+
let llval = bx.const_ptr_byte_offset(base_addr, offset);
271+
bx.load_operand(PlaceRef::new_sized(llval, layout))
264272
}
265273

266274
/// Asserts that this operand refers to a scalar and returns

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+42-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
88
use rustc_middle::{bug, mir, span_bug};
99
use rustc_session::config::OptLevel;
1010
use rustc_span::{DUMMY_SP, Span};
11-
use tracing::{debug, instrument};
11+
use tracing::{debug, instrument, trace};
1212

1313
use super::FunctionCx;
1414
use super::operand::{OperandRef, OperandValue};
@@ -93,23 +93,57 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
9393
return;
9494
}
9595

96-
if let OperandValue::Immediate(v) = cg_elem.val {
96+
let try_init_all_same = |bx: &mut Bx, v| {
9797
let start = dest.val.llval;
9898
let size = bx.const_usize(dest.layout.size.bytes());
9999

100-
// Use llvm.memset.p0i8.* to initialize all zero arrays
101-
if bx.cx().const_to_opt_u128(v, false) == Some(0) {
102-
let fill = bx.cx().const_u8(0);
103-
bx.memset(start, fill, size, dest.val.align, MemFlags::empty());
104-
return;
100+
// Use llvm.memset.p0i8.* to initialize all same byte arrays
101+
if let Some(int) = bx.cx().const_to_opt_u128(v, false) {
102+
let bytes = &int.to_le_bytes()[..cg_elem.layout.size.bytes_usize()];
103+
let first = bytes[0];
104+
if bytes[1..].iter().all(|&b| b == first) {
105+
let fill = bx.cx().const_u8(first);
106+
bx.memset(start, fill, size, dest.val.align, MemFlags::empty());
107+
return true;
108+
}
105109
}
106110

107111
// Use llvm.memset.p0i8.* to initialize byte arrays
108112
let v = bx.from_immediate(v);
109113
if bx.cx().val_ty(v) == bx.cx().type_i8() {
110114
bx.memset(start, v, size, dest.val.align, MemFlags::empty());
111-
return;
115+
return true;
116+
}
117+
false
118+
};
119+
120+
trace!(?cg_elem.val);
121+
match cg_elem.val {
122+
OperandValue::Immediate(v) => {
123+
if try_init_all_same(bx, v) {
124+
return;
125+
}
126+
}
127+
OperandValue::Pair(a, b) => {
128+
let a_is_undef = bx.cx().is_undef(a);
129+
match (a_is_undef, bx.cx().is_undef(b)) {
130+
// Can happen for uninit unions
131+
(true, true) => {
132+
// FIXME: can we produce better output here?
133+
}
134+
(false, true) | (true, false) => {
135+
let val = if a_is_undef { b } else { b };
136+
if try_init_all_same(bx, val) {
137+
return;
138+
}
139+
}
140+
(false, false) => {
141+
// FIXME: if both are the same value, use try_init_all_same
142+
}
143+
}
112144
}
145+
OperandValue::ZeroSized => unreachable!("checked above"),
146+
OperandValue::Ref(..) => {}
113147
}
114148

115149
let count = self

compiler/rustc_codegen_ssa/src/traits/consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub trait ConstCodegenMethods<'tcx>: BackendTypes {
99
/// Generate an uninitialized value (matching uninitialized memory in MIR).
1010
/// Whether memory is initialized or not is tracked byte-for-byte.
1111
fn const_undef(&self, t: Self::Type) -> Self::Value;
12+
fn is_undef(&self, v: Self::Value) -> bool;
1213
/// Generate a fake value. Poison always affects the entire value, even if just a single byte is
1314
/// poison. This can only be used in codepaths that are already UB, i.e., UB-free Rust code
1415
/// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ impl AllocError {
222222
}
223223

224224
/// The information that makes up a memory access: offset and size.
225-
#[derive(Copy, Clone)]
225+
#[derive(Copy, Clone, PartialEq)]
226226
pub struct AllocRange {
227227
pub start: Size,
228228
pub size: Size,

tests/codegen/overaligned-constant.rs

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ pub fn overaligned_constant() {
1717
// CHECK-LABEL: @overaligned_constant
1818
// CHECK: [[full:%_.*]] = alloca [32 x i8], align 8
1919
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[full]], ptr align 8 @0, i64 32, i1 false)
20-
// CHECK: %b.0 = load i32, ptr @0, align 4
21-
// CHECK: %b.1 = load i32, ptr getelementptr inbounds ({{.*}}), align 4
2220
let mut s = S(1);
2321

2422
s.0 = 3;

tests/codegen/slice-init.rs

+24
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,30 @@ pub fn nonzero_integer_array() {
6363
opaque(&x);
6464
}
6565

66+
const N: usize = 100;
67+
68+
// CHECK-LABEL: @u16_init_one_bytes
69+
#[no_mangle]
70+
pub fn u16_init_one_bytes() -> [u16; N] {
71+
// CHECK-NOT: select
72+
// CHECK-NOT: br
73+
// CHECK-NOT: switch
74+
// CHECK-NOT: icmp
75+
// CHECK: call void @llvm.memset.p0
76+
[const { u16::from_be_bytes([1, 1]) }; N]
77+
}
78+
79+
// CHECK-LABEL: @option_none_init
80+
#[no_mangle]
81+
pub fn option_none_init() -> [Option<u8>; N] {
82+
// CHECK-NOT: select
83+
// CHECK-NOT: br
84+
// CHECK-NOT: switch
85+
// CHECK-NOT: icmp
86+
// CHECK: call void @llvm.memset.p0
87+
[None; N]
88+
}
89+
6690
// Use an opaque function to prevent rustc from removing useless drops.
6791
#[inline(never)]
6892
pub fn opaque(_: impl Sized) {}

0 commit comments

Comments
 (0)