diff --git a/library/core/src/slice/specialize.rs b/library/core/src/slice/specialize.rs index 80eb590587f99..17436395fee69 100644 --- a/library/core/src/slice/specialize.rs +++ b/library/core/src/slice/specialize.rs @@ -15,9 +15,54 @@ impl SpecFill for [T] { } impl SpecFill 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 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 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 } diff --git a/tests/codegen-llvm/lib-optimizations/slice_fill.rs b/tests/codegen-llvm/lib-optimizations/slice_fill.rs new file mode 100644 index 0000000000000..2d924ebf726d8 --- /dev/null +++ b/tests/codegen-llvm/lib-optimizations/slice_fill.rs @@ -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], v: MaybeUninit) { + // 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]) { + // 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); +}