Skip to content

Commit e299ff9

Browse files
committed
specialize slice::fill to use memset when possible
LLVM generally can do this on its own, but it helps miri and other backends.
1 parent df9c27d commit e299ff9

File tree

2 files changed

+63
-1
lines changed

2 files changed

+63
-1
lines changed

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 }
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)