Skip to content

Commit 51de2e9

Browse files
Auto merge of #147294 - the8472:slice_fill_memset, r=<try>
specialize `slice::fill` to use memset
2 parents 8b6b15b + cea7f2a commit 51de2e9

File tree

12 files changed

+131
-13
lines changed

12 files changed

+131
-13
lines changed

compiler/rustc_codegen_cranelift/example/mini_core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ pub mod intrinsics {
666666
#[rustc_intrinsic]
667667
pub fn bswap<T>(x: T) -> T;
668668
#[rustc_intrinsic]
669-
pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
669+
pub unsafe fn write_bytes<T, B>(dst: *mut T, val: B, count: usize);
670670
#[rustc_intrinsic]
671671
pub unsafe fn unreachable() -> !;
672672
}

compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ static NUM_REF: &'static u8 = unsafe { &*&raw const NUM };
125125

126126
unsafe fn zeroed<T>() -> T {
127127
let mut uninit = MaybeUninit { uninit: () };
128-
intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0, 1);
128+
intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0u8, 1);
129129
uninit.value.value
130130
}
131131

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
685685

686686
sym::write_bytes | sym::volatile_set_memory => {
687687
intrinsic_args!(fx, args => (dst, val, count); intrinsic);
688+
if val.layout().size.bytes() != 1 {
689+
// incorrect sizes can be encountered on dead branches
690+
fx.bcx.ins().trap(TrapCode::user(1).unwrap());
691+
return Ok(());
692+
};
693+
688694
let val = val.load_scalar(fx);
689695
let count = count.load_scalar(fx);
690696

compiler/rustc_codegen_gcc/example/mini_core.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ pub mod intrinsics {
677677
#[rustc_intrinsic]
678678
pub fn bswap<T>(x: T) -> T;
679679
#[rustc_intrinsic]
680-
pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
680+
pub unsafe fn write_bytes<T, B>(dst: *mut T, val: B, count: usize);
681681
#[rustc_intrinsic]
682682
pub unsafe fn unreachable() -> !;
683683
}

compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsiz
127127

128128
unsafe fn zeroed<T>() -> T {
129129
let mut uninit = MaybeUninit { uninit: () };
130-
intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0, 1);
130+
intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0u8, 1);
131131
uninit.value.value
132132
}
133133

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,12 +198,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
198198
return Ok(());
199199
}
200200
sym::write_bytes => {
201+
// invalid types may be encountered on dead branches after a size check.
202+
if args[1].layout.size.bytes() != 1 {
203+
bx.unreachable_nonterminator();
204+
return Ok(());
205+
}
206+
let val = bx.from_immediate(args[1].immediate());
201207
memset_intrinsic(
202208
bx,
203209
false,
204210
fn_args.type_at(0),
205211
args[0].immediate(),
206-
args[1].immediate(),
212+
val,
207213
args[2].immediate(),
208214
);
209215
return Ok(());

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
use std::assert_matches::assert_matches;
66

7+
use either::Either;
78
use rustc_abi::{FieldIdx, HasDataLayout, Size};
89
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
910
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
@@ -866,18 +867,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
866867
count: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
867868
name: &'static str,
868869
) -> InterpResult<'tcx> {
869-
let layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?;
870+
let dst_layout = self.layout_of(dst.layout.ty.builtin_deref(true).unwrap())?;
871+
let src_layout = self.layout_of(byte.layout.ty)?;
872+
873+
if src_layout.size.bytes_usize() != 1 {
874+
throw_ub_custom!(
875+
fluent::const_eval_scalar_size_mismatch,
876+
target_size = 1,
877+
data_size = src_layout.size.bytes(),
878+
);
879+
}
870880

871881
let dst = self.read_pointer(dst)?;
872-
let byte = self.read_scalar(byte)?.to_u8()?;
882+
873883
let count = self.read_target_usize(count)?;
874884

875885
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
876886
// but no actual allocation can be big enough for the difference to be noticeable.
877887
let len = self
878-
.compute_size_in_bytes(layout.size, count)
888+
.compute_size_in_bytes(dst_layout.size, count)
879889
.ok_or_else(|| err_ub_custom!(fluent::const_eval_size_overflow, name = name))?;
880890

891+
let byte = match self.read_immediate_raw(byte)? {
892+
Either::Left(src_place) => {
893+
// val is not an immediate, possibly uninit.
894+
self.mem_copy_repeatedly(
895+
src_place.ptr(),
896+
dst,
897+
Size::from_bytes(1),
898+
len.bytes(),
899+
/* nonoverlapping: */ false,
900+
)?;
901+
return interp_ok(());
902+
}
903+
Either::Right(imm) => imm,
904+
};
905+
906+
let byte = byte.to_scalar().to_u8()?;
881907
let bytes = std::iter::repeat(byte).take(len.bytes_usize());
882908
self.write_bytes_ptr(dst, bytes)
883909
}

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,10 @@ pub(crate) fn check_intrinsic_type(
341341
let byte_ptr = Ty::new_imm_ptr(tcx, tcx.types.u8);
342342
(0, 0, vec![byte_ptr, byte_ptr, tcx.types.usize], tcx.types.i32)
343343
}
344-
sym::write_bytes | sym::volatile_set_memory => (
344+
sym::write_bytes => {
345+
(2, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(1), tcx.types.usize], tcx.types.unit)
346+
}
347+
sym::volatile_set_memory => (
345348
1,
346349
0,
347350
vec![Ty::new_mut_ptr(tcx, param(0)), tcx.types.u8, tcx.types.usize],

library/core/src/intrinsics/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2882,6 +2882,8 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
28822882
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
28832883

28842884
/// This is an accidentally-stable alias to [`ptr::write_bytes`]; use that instead.
2885+
///
2886+
/// `val` must be 1 byte wide, it is allowed to be uninit.
28852887
// Note (intentionally not in the doc comment): `ptr::write_bytes` adds some extra
28862888
// debug assertions; if you are writing compiler tests or code inside the standard library
28872889
// that wants to avoid those debug assertions, directly call this intrinsic instead.
@@ -2890,7 +2892,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize);
28902892
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
28912893
#[rustc_nounwind]
28922894
#[rustc_intrinsic]
2893-
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
2895+
pub const unsafe fn write_bytes<T, B>(dst: *mut T, val: B, count: usize);
28942896

28952897
/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values.
28962898
///

library/core/src/slice/specialize.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,43 @@ impl<T: Clone> SpecFill<T> for [T] {
1515
}
1616

1717
impl<T: Copy> SpecFill<T> for [T] {
18-
fn spec_fill(&mut self, value: T) {
18+
default fn spec_fill(&mut self, value: T) {
19+
if size_of::<T>() == 1 {
20+
// SAFETY: The pointer is derived from a reference, so it's writable.
21+
// And we checked that T is 1 byte wide.
22+
unsafe {
23+
// use the intrinsic since it allows any T as long as it's 1 byte wide
24+
crate::intrinsics::write_bytes(self.as_mut_ptr(), value, self.len());
25+
}
26+
return;
27+
}
1928
for item in self.iter_mut() {
2029
*item = value;
2130
}
2231
}
2332
}
33+
34+
macro spec_fill_int {
35+
($($type:ty)*) => {$(
36+
impl SpecFill<$type> for [$type] {
37+
#[inline]
38+
fn spec_fill(&mut self, value: $type) {
39+
if crate::intrinsics::is_val_statically_known(value) {
40+
let bytes = value.to_ne_bytes();
41+
if value == <$type>::from_ne_bytes([bytes[0]; size_of::<$type>()]) {
42+
// SAFETY: The pointer is derived from a reference, so it's writable.
43+
unsafe {
44+
crate::intrinsics::write_bytes(self.as_mut_ptr(), bytes[0], self.len());
45+
}
46+
return;
47+
}
48+
}
49+
for item in self.iter_mut() {
50+
*item = value;
51+
}
52+
}
53+
}
54+
)*}
55+
}
56+
57+
spec_fill_int! { u16 i16 u32 i32 u64 i64 u128 i128 usize isize }

0 commit comments

Comments
 (0)