Skip to content

Commit

Permalink
Rollup merge of #114382 - scottmcm:compare-bytes-intrinsic, r=cjgillot
Browse files Browse the repository at this point in the history
Add a new `compare_bytes` intrinsic instead of calling `memcmp` directly

As discussed in #113435, this lets the backends be the place that can have the "don't call the function if n == 0" logic, if it's needed for the target.  (I didn't actually *add* those checks, though, since as I understood it we didn't actually need them on known targets?)

Doing this also let me make it `const` (unstable), which I don't think `extern "C" fn memcmp` can be.

cc `@RalfJung` `@Amanieu`
  • Loading branch information
matthiaskrgr authored Aug 7, 2023
2 parents 99cae0f + e56b825 commit 3ed1189
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 15 deletions.
34 changes: 34 additions & 0 deletions core/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
21 changes: 6 additions & 15 deletions core/src/slice/cmp.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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>,
Expand All @@ -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
}
}
}
Expand Down Expand Up @@ -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]
Expand All @@ -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;
}
Expand Down

0 comments on commit 3ed1189

Please sign in to comment.