Skip to content

Commit 83476be

Browse files
committed
Treat undef bytes as equal to any other byte
1 parent f0904ee commit 83476be

File tree

6 files changed

+87
-5
lines changed

6 files changed

+87
-5
lines changed

compiler/rustc_codegen_gcc/src/common.rs

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
6464
if type_is_pointer(typ) { self.context.new_null(typ) } else { self.const_int(typ, 0) }
6565
}
6666

67+
fn is_undef(&self, _val: RValue<'gcc>) -> bool {
68+
// FIXME: actually check for undef
69+
false
70+
}
71+
6772
fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> {
6873
let local = self.current_func.borrow().expect("func").new_local(None, typ, "undefined");
6974
if typ.is_struct().is_some() {

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/rvalue.rs

+24-2
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,6 +93,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
9393
return;
9494
}
9595

96+
// If `v` is an integer constant whose value is just a single byte repeated N times,
97+
// emit a `memset` filling the entire `dest` with that byte.
9698
let try_init_all_same = |bx: &mut Bx, v| {
9799
let start = dest.val.llval;
98100
let size = bx.const_usize(dest.layout.size.bytes());
@@ -117,13 +119,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
117119
false
118120
};
119121

122+
trace!(?cg_elem.val);
120123
match cg_elem.val {
121124
OperandValue::Immediate(v) => {
122125
if try_init_all_same(bx, v) {
123126
return;
124127
}
125128
}
126-
_ => (),
129+
OperandValue::Pair(a, b) => {
130+
let a_is_undef = bx.cx().is_undef(a);
131+
match (a_is_undef, bx.cx().is_undef(b)) {
132+
// Can happen for uninit unions
133+
(true, true) => {
134+
// FIXME: can we produce better output here?
135+
}
136+
(false, true) | (true, false) => {
137+
let val = if a_is_undef { b } else { a };
138+
if try_init_all_same(bx, val) {
139+
return;
140+
}
141+
}
142+
(false, false) => {
143+
// FIXME: if both are the same value, use try_init_all_same
144+
}
145+
}
146+
}
147+
OperandValue::ZeroSized => unreachable!("checked above"),
148+
OperandValue::Ref(..) => {}
127149
}
128150

129151
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

tests/codegen/slice-init.rs

+52-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#![crate_type = "lib"]
44

5+
use std::mem::MaybeUninit;
6+
57
// CHECK-LABEL: @zero_sized_elem
68
#[no_mangle]
79
pub fn zero_sized_elem() {
@@ -76,17 +78,64 @@ pub fn u16_init_one_bytes() -> [u16; N] {
7678
[const { u16::from_be_bytes([1, 1]) }; N]
7779
}
7880

79-
// FIXME: undef bytes can just be initialized with the same value as the
80-
// defined bytes, if the defines bytes are all the same.
8181
// CHECK-LABEL: @option_none_init
8282
#[no_mangle]
8383
pub fn option_none_init() -> [Option<u8>; N] {
84+
// CHECK-NOT: select
85+
// CHECK-NOT: br
86+
// CHECK-NOT: switch
87+
// CHECK-NOT: icmp
88+
// CHECK: call void @llvm.memset.p0
89+
[None; N]
90+
}
91+
92+
// If there is partial provenance or some bytes are initialized and some are not,
93+
// we can't really do better than initialize bytes or groups of bytes together.
94+
// CHECK-LABEL: @option_maybe_uninit_init
95+
#[no_mangle]
96+
pub fn option_maybe_uninit_init() -> [MaybeUninit<u16>; N] {
8497
// CHECK-NOT: select
8598
// CHECK: br label %repeat_loop_header{{.*}}
8699
// CHECK-NOT: switch
87100
// CHECK: icmp
88101
// CHECK-NOT: call void @llvm.memset.p0
89-
[None; N]
102+
[const {
103+
let mut val: MaybeUninit<u16> = MaybeUninit::uninit();
104+
let ptr = val.as_mut_ptr() as *mut u8;
105+
unsafe {
106+
ptr.write(0);
107+
}
108+
val
109+
}; N]
110+
}
111+
112+
#[repr(packed)]
113+
struct Packed {
114+
start: u8,
115+
ptr: &'static (),
116+
rest: u16,
117+
rest2: u8,
118+
}
119+
120+
// If there is partial provenance or some bytes are initialized and some are not,
121+
// we can't really do better than initialize bytes or groups of bytes together.
122+
// CHECK-LABEL: @option_maybe_uninit_provenance
123+
#[no_mangle]
124+
pub fn option_maybe_uninit_provenance() -> [MaybeUninit<Packed>; N] {
125+
// CHECK-NOT: select
126+
// CHECK: br label %repeat_loop_header{{.*}}
127+
// CHECK-NOT: switch
128+
// CHECK: icmp
129+
// CHECK-NOT: call void @llvm.memset.p0
130+
[const {
131+
let mut val: MaybeUninit<Packed> = MaybeUninit::uninit();
132+
unsafe {
133+
let ptr = &raw mut (*val.as_mut_ptr()).ptr;
134+
static HAS_ADDR: () = ();
135+
ptr.write_unaligned(&HAS_ADDR);
136+
}
137+
val
138+
}; N]
90139
}
91140

92141
// Use an opaque function to prevent rustc from removing useless drops.

0 commit comments

Comments
 (0)