Skip to content

Commit 30c71de

Browse files
committed
Add a new compare_bytes intrinsic instead of calling memcmp directly
1 parent 3194be1 commit 30c71de

File tree

2 files changed

+40
-15
lines changed

2 files changed

+40
-15
lines changed

core/src/intrinsics.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,25 @@ extern "rust-intrinsic" {
23852385
#[rustc_nounwind]
23862386
pub fn raw_eq<T>(a: &T, b: &T) -> bool;
23872387

2388+
/// Lexicographically compare `[left, left + bytes)` and `[right, right + bytes)`
2389+
/// as unsigned bytes, returning negative if `left` is less, zero if all the
2390+
/// bytes match, or positive if `right` is greater.
2391+
///
2392+
/// This underlies things like `<[u8]>::cmp`, and will usually lower to `memcmp`.
2393+
///
2394+
/// # Safety
2395+
///
2396+
/// `left` and `right` must each be [valid] for reads of `bytes` bytes.
2397+
///
2398+
/// Note that this applies to the whole range, not just until the first byte
2399+
/// that differs. That allows optimizations that can read in large chunks.
2400+
///
2401+
/// [valid]: crate::ptr#safety
2402+
#[cfg(not(bootstrap))]
2403+
#[rustc_const_unstable(feature = "const_intrinsic_compare_bytes", issue = "none")]
2404+
#[rustc_nounwind]
2405+
pub fn compare_bytes(left: *const u8, right: *const u8, bytes: usize) -> i32;
2406+
23882407
/// See documentation of [`std::hint::black_box`] for details.
23892408
///
23902409
/// [`std::hint::black_box`]: crate::hint::black_box
@@ -2825,3 +2844,18 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
28252844
write_bytes(dst, val, count)
28262845
}
28272846
}
2847+
2848+
/// Backfill for bootstrap
2849+
#[cfg(bootstrap)]
2850+
pub unsafe fn compare_bytes(left: *const u8, right: *const u8, bytes: usize) -> i32 {
2851+
extern "C" {
2852+
fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> crate::ffi::c_int;
2853+
}
2854+
2855+
if bytes != 0 {
2856+
// SAFETY: Since bytes is non-zero, the caller has met `memcmp`'s requirements.
2857+
unsafe { memcmp(left, right, bytes).into() }
2858+
} else {
2859+
0
2860+
}
2861+
}

core/src/slice/cmp.rs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
//! Comparison traits for `[T]`.
22
33
use crate::cmp::{self, BytewiseEq, Ordering};
4-
use crate::ffi;
4+
use crate::intrinsics::compare_bytes;
55
use crate::mem;
66

77
use super::from_raw_parts;
88
use super::memchr;
99

10-
extern "C" {
11-
/// Calls implementation provided memcmp.
12-
///
13-
/// Interprets the data as u8.
14-
///
15-
/// Returns 0 for equal, < 0 for less than and > 0 for greater
16-
/// than.
17-
fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> ffi::c_int;
18-
}
19-
2010
#[stable(feature = "rust1", since = "1.0.0")]
2111
impl<A, B> PartialEq<[B]> for [A]
2212
where
@@ -74,7 +64,8 @@ where
7464
}
7565
}
7666

77-
// Use memcmp for bytewise equality when the types allow
67+
// When each element can be compared byte-wise, we can compare all the bytes
68+
// from the whole size in one call to the intrinsics.
7869
impl<A, B> SlicePartialEq<B> for [A]
7970
where
8071
A: BytewiseEq<B>,
@@ -88,7 +79,7 @@ where
8879
// The two slices have been checked to have the same size above.
8980
unsafe {
9081
let size = mem::size_of_val(self);
91-
memcmp(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0
82+
compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0
9283
}
9384
}
9485
}
@@ -183,7 +174,7 @@ impl<A: Ord> SliceOrd for A {
183174
}
184175
}
185176

186-
// memcmp compares a sequence of unsigned bytes lexicographically.
177+
// `compare_bytes` compares a sequence of unsigned bytes lexicographically.
187178
// this matches the order we want for [u8], but no others (not even [i8]).
188179
impl SliceOrd for u8 {
189180
#[inline]
@@ -195,7 +186,7 @@ impl SliceOrd for u8 {
195186
// SAFETY: `left` and `right` are references and are thus guaranteed to be valid.
196187
// We use the minimum of both lengths which guarantees that both regions are
197188
// valid for reads in that interval.
198-
let mut order = unsafe { memcmp(left.as_ptr(), right.as_ptr(), len) as isize };
189+
let mut order = unsafe { compare_bytes(left.as_ptr(), right.as_ptr(), len) as isize };
199190
if order == 0 {
200191
order = diff;
201192
}

0 commit comments

Comments
 (0)