Skip to content

Commit 4e3e7ce

Browse files
authored
Rollup merge of rust-lang#147457 - the8472:slice_fill_memset2, r=RalfJung,joboet
specialize slice::fill to use memset when possible It helps const eval performance rust-lang/miri#4616, debug builds and the gcc backend. Previously attempted in rust-lang#83245 but reverted due to unsoundness rust-lang#87891 around potentially-uninitialized types. This PR only handles primitives where the problem does not arise. split off from rust-lang#147294
2 parents 473a74a + 99ab27f commit 4e3e7ce

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

library/core/src/slice/specialize.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,54 @@ 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) {
1919
for item in self.iter_mut() {
2020
*item = value;
2121
}
2222
}
2323
}
24+
25+
impl SpecFill<u8> for [u8] {
26+
fn spec_fill(&mut self, value: u8) {
27+
// SAFETY: The pointer is derived from a reference, so it's writable.
28+
unsafe {
29+
crate::intrinsics::write_bytes(self.as_mut_ptr(), value, self.len());
30+
}
31+
}
32+
}
33+
34+
impl SpecFill<i8> for [i8] {
35+
fn spec_fill(&mut self, value: i8) {
36+
// SAFETY: The pointer is derived from a reference, so it's writable.
37+
unsafe {
38+
crate::intrinsics::write_bytes(self.as_mut_ptr(), value.cast_unsigned(), self.len());
39+
}
40+
}
41+
}
42+
43+
macro spec_fill_int {
44+
($($type:ty)*) => {$(
45+
impl SpecFill<$type> for [$type] {
46+
#[inline]
47+
fn spec_fill(&mut self, value: $type) {
48+
// We always take this fastpath in Miri for long slices as the manual `for`
49+
// loop can be prohibitively slow.
50+
if (cfg!(miri) && self.len() > 32) || crate::intrinsics::is_val_statically_known(value) {
51+
let bytes = value.to_ne_bytes();
52+
if value == <$type>::from_ne_bytes([bytes[0]; size_of::<$type>()]) {
53+
// SAFETY: The pointer is derived from a reference, so it's writable.
54+
unsafe {
55+
crate::intrinsics::write_bytes(self.as_mut_ptr(), bytes[0], self.len());
56+
}
57+
return;
58+
}
59+
}
60+
for item in self.iter_mut() {
61+
*item = value;
62+
}
63+
}
64+
}
65+
)*}
66+
}
67+
68+
spec_fill_int! { u16 i16 u32 i32 u64 i64 u128 i128 usize isize }
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ compile-flags: -Copt-level=3
2+
#![crate_type = "lib"]
3+
4+
use std::mem::MaybeUninit;
5+
6+
// CHECK-LABEL: @slice_fill_pass_undef
7+
#[no_mangle]
8+
pub fn slice_fill_pass_undef(s: &mut [MaybeUninit<u8>], v: MaybeUninit<u8>) {
9+
// CHECK: tail call void @llvm.memset.{{.*}}(ptr nonnull align 1 %s.0, i8 %v, {{.*}} %s.1, i1 false)
10+
// CHECK: ret
11+
s.fill(v);
12+
}
13+
14+
// CHECK-LABEL: @slice_fill_uninit
15+
#[no_mangle]
16+
pub fn slice_fill_uninit(s: &mut [MaybeUninit<u8>]) {
17+
// CHECK-NOT: call
18+
// CHECK: ret void
19+
s.fill(MaybeUninit::uninit());
20+
}
21+
22+
// CHECK-LABEL: @slice_wide_memset
23+
#[no_mangle]
24+
pub fn slice_wide_memset(s: &mut [u16]) {
25+
// CHECK: tail call void @llvm.memset.{{.*}}(ptr nonnull align 2 %s.0, i8 -1
26+
// CHECK: ret
27+
s.fill(0xFFFF);
28+
}

0 commit comments

Comments
 (0)