specialize slice::fill to use memset when possible

LLVM generally can do this on its own, but it helps miri and other backends.
This commit is contained in:
The 8472
2025-10-03 13:43:11 +02:00
parent 2cb4e7dce8
commit 99ab27f90c
2 changed files with 74 additions and 1 deletions

View File

@@ -15,9 +15,54 @@ impl<T: Clone> SpecFill<T> for [T] {
}
impl<T: Copy> SpecFill<T> for [T] {
fn spec_fill(&mut self, value: T) {
default fn spec_fill(&mut self, value: T) {
for item in self.iter_mut() {
*item = value;
}
}
}
impl SpecFill<u8> for [u8] {
fn spec_fill(&mut self, value: u8) {
// SAFETY: The pointer is derived from a reference, so it's writable.
unsafe {
crate::intrinsics::write_bytes(self.as_mut_ptr(), value, self.len());
}
}
}
impl SpecFill<i8> for [i8] {
fn spec_fill(&mut self, value: i8) {
// SAFETY: The pointer is derived from a reference, so it's writable.
unsafe {
crate::intrinsics::write_bytes(self.as_mut_ptr(), value.cast_unsigned(), self.len());
}
}
}
macro spec_fill_int {
($($type:ty)*) => {$(
impl SpecFill<$type> for [$type] {
#[inline]
fn spec_fill(&mut self, value: $type) {
// We always take this fastpath in Miri for long slices as the manual `for`
// loop can be prohibitively slow.
if (cfg!(miri) && self.len() > 32) || crate::intrinsics::is_val_statically_known(value) {
let bytes = value.to_ne_bytes();
if value == <$type>::from_ne_bytes([bytes[0]; size_of::<$type>()]) {
// SAFETY: The pointer is derived from a reference, so it's writable.
unsafe {
crate::intrinsics::write_bytes(self.as_mut_ptr(), bytes[0], self.len());
}
return;
}
}
for item in self.iter_mut() {
*item = value;
}
}
}
)*}
}
spec_fill_int! { u16 i16 u32 i32 u64 i64 u128 i128 usize isize }

View File

@@ -0,0 +1,28 @@
//@ compile-flags: -Copt-level=3
#![crate_type = "lib"]
use std::mem::MaybeUninit;
// CHECK-LABEL: @slice_fill_pass_undef
#[no_mangle]
pub fn slice_fill_pass_undef(s: &mut [MaybeUninit<u8>], v: MaybeUninit<u8>) {
// CHECK: tail call void @llvm.memset.{{.*}}(ptr nonnull align 1 %s.0, i8 %v, {{.*}} %s.1, i1 false)
// CHECK: ret
s.fill(v);
}
// CHECK-LABEL: @slice_fill_uninit
#[no_mangle]
pub fn slice_fill_uninit(s: &mut [MaybeUninit<u8>]) {
// CHECK-NOT: call
// CHECK: ret void
s.fill(MaybeUninit::uninit());
}
// CHECK-LABEL: @slice_wide_memset
#[no_mangle]
pub fn slice_wide_memset(s: &mut [u16]) {
// CHECK: tail call void @llvm.memset.{{.*}}(ptr nonnull align 2 %s.0, i8 -1
// CHECK: ret
s.fill(0xFFFF);
}