about summary refs log tree commit diff
path: root/library/core/src
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2023-08-02 12:45:52 -0700
committerScott McMurray <scottmcm@users.noreply.github.com>2023-08-06 15:47:40 -0700
commit502af03445f045dade14f14e754803f02c2c5e24 (patch)
treefbeb208e1c6d7df46431f1412404e8b48dfd5349 /library/core/src
parent85fbb571497d13cfb828de9b0d3e78656b9203c1 (diff)
downloadrust-502af03445f045dade14f14e754803f02c2c5e24.tar.gz
rust-502af03445f045dade14f14e754803f02c2c5e24.zip
Add a new `compare_bytes` intrinsic instead of calling `memcmp` directly
Diffstat (limited to 'library/core/src')
-rw-r--r--library/core/src/intrinsics.rs34
-rw-r--r--library/core/src/slice/cmp.rs21
2 files changed, 40 insertions, 15 deletions
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d042aaf3084..9ef2c7cde02 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -2385,6 +2385,25 @@ extern "rust-intrinsic" {
     #[rustc_nounwind]
     pub fn raw_eq<T>(a: &T, b: &T) -> bool;
 
+    /// Lexicographically compare `[left, left + bytes)` and `[right, right + bytes)`
+    /// as unsigned bytes, returning negative if `left` is less, zero if all the
+    /// bytes match, or positive if `right` is greater.
+    ///
+    /// This underlies things like `<[u8]>::cmp`, and will usually lower to `memcmp`.
+    ///
+    /// # Safety
+    ///
+    /// `left` and `right` must each be [valid] for reads of `bytes` bytes.
+    ///
+    /// Note that this applies to the whole range, not just until the first byte
+    /// that differs.  That allows optimizations that can read in large chunks.
+    ///
+    /// [valid]: crate::ptr#safety
+    #[cfg(not(bootstrap))]
+    #[rustc_const_unstable(feature = "const_intrinsic_compare_bytes", issue = "none")]
+    #[rustc_nounwind]
+    pub fn compare_bytes(left: *const u8, right: *const u8, bytes: usize) -> i32;
+
     /// See documentation of [`std::hint::black_box`] for details.
     ///
     /// [`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) {
         write_bytes(dst, val, count)
     }
 }
+
+/// Backfill for bootstrap
+#[cfg(bootstrap)]
+pub unsafe fn compare_bytes(left: *const u8, right: *const u8, bytes: usize) -> i32 {
+    extern "C" {
+        fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> crate::ffi::c_int;
+    }
+
+    if bytes != 0 {
+        // SAFETY: Since bytes is non-zero, the caller has met `memcmp`'s requirements.
+        unsafe { memcmp(left, right, bytes).into() }
+    } else {
+        0
+    }
+}
diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs
index 7601dd3c756..075347b80d0 100644
--- a/library/core/src/slice/cmp.rs
+++ b/library/core/src/slice/cmp.rs
@@ -1,22 +1,12 @@
 //! Comparison traits for `[T]`.
 
 use crate::cmp::{self, BytewiseEq, Ordering};
-use crate::ffi;
+use crate::intrinsics::compare_bytes;
 use crate::mem;
 
 use super::from_raw_parts;
 use super::memchr;
 
-extern "C" {
-    /// Calls implementation provided memcmp.
-    ///
-    /// Interprets the data as u8.
-    ///
-    /// Returns 0 for equal, < 0 for less than and > 0 for greater
-    /// than.
-    fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> ffi::c_int;
-}
-
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<A, B> PartialEq<[B]> for [A]
 where
@@ -74,7 +64,8 @@ where
     }
 }
 
-// Use memcmp for bytewise equality when the types allow
+// When each element can be compared byte-wise, we can compare all the bytes
+// from the whole size in one call to the intrinsics.
 impl<A, B> SlicePartialEq<B> for [A]
 where
     A: BytewiseEq<B>,
@@ -88,7 +79,7 @@ where
         // The two slices have been checked to have the same size above.
         unsafe {
             let size = mem::size_of_val(self);
-            memcmp(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0
+            compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0
         }
     }
 }
@@ -183,7 +174,7 @@ impl<A: Ord> SliceOrd for A {
     }
 }
 
-// memcmp compares a sequence of unsigned bytes lexicographically.
+// `compare_bytes` compares a sequence of unsigned bytes lexicographically.
 // this matches the order we want for [u8], but no others (not even [i8]).
 impl SliceOrd for u8 {
     #[inline]
@@ -195,7 +186,7 @@ impl SliceOrd for u8 {
         // SAFETY: `left` and `right` are references and are thus guaranteed to be valid.
         // We use the minimum of both lengths which guarantees that both regions are
         // valid for reads in that interval.
-        let mut order = unsafe { memcmp(left.as_ptr(), right.as_ptr(), len) as isize };
+        let mut order = unsafe { compare_bytes(left.as_ptr(), right.as_ptr(), len) as isize };
         if order == 0 {
             order = diff;
         }