about summary refs log tree commit diff
path: root/library/core/src
diff options
context:
space:
mode:
authorScott McMurray <scottmcm@users.noreply.github.com>2021-05-30 10:25:41 -0700
committerScott McMurray <scottmcm@users.noreply.github.com>2021-07-08 14:55:54 -0700
commit2456495a260827217d3c612d6c577c2f165c61eb (patch)
treee85ffd47a15f93a5b6f0a6324bb8b747659a2448 /library/core/src
parentd05eafae2fcc05bd64ab094a1352a5c16df3106e (diff)
downloadrust-2456495a260827217d3c612d6c577c2f165c61eb.tar.gz
rust-2456495a260827217d3c612d6c577c2f165c61eb.zip
Stop generating `alloca`s+`memcmp` for simple array equality
Diffstat (limited to 'library/core/src')
-rw-r--r--library/core/src/array/equality.rs53
-rw-r--r--library/core/src/intrinsics.rs16
2 files changed, 67 insertions, 2 deletions
diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs
index dcd78e7a245..6d66b9e2f27 100644
--- a/library/core/src/array/equality.rs
+++ b/library/core/src/array/equality.rs
@@ -5,11 +5,11 @@ where
 {
     #[inline]
     fn eq(&self, other: &[B; N]) -> bool {
-        self[..] == other[..]
+        SpecArrayEq::spec_eq(self, other)
     }
     #[inline]
     fn ne(&self, other: &[B; N]) -> bool {
-        self[..] != other[..]
+        SpecArrayEq::spec_ne(self, other)
     }
 }
 
@@ -109,3 +109,52 @@ where
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Eq, const N: usize> Eq for [T; N] {}
+
+trait SpecArrayEq<Other, const N: usize>: Sized {
+    fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool;
+    fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool;
+}
+
+impl<T: PartialEq<Other>, Other, const N: usize> SpecArrayEq<Other, N> for T {
+    default fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool {
+        a[..] == b[..]
+    }
+    default fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool {
+        a[..] != b[..]
+    }
+}
+
+impl<T: PartialEq<U> + IsRawEqComparable<U>, U, const N: usize> SpecArrayEq<U, N> for T {
+    #[cfg(bootstrap)]
+    fn spec_eq(a: &[T; N], b: &[U; N]) -> bool {
+        a[..] == b[..]
+    }
+    #[cfg(not(bootstrap))]
+    fn spec_eq(a: &[T; N], b: &[U; N]) -> bool {
+        // SAFETY: This is why `IsRawEqComparable` is an `unsafe trait`.
+        unsafe {
+            let b = &*b.as_ptr().cast::<[T; N]>();
+            crate::intrinsics::raw_eq(a, b)
+        }
+    }
+    fn spec_ne(a: &[T; N], b: &[U; N]) -> bool {
+        !Self::spec_eq(a, b)
+    }
+}
+
+/// `U` exists on here mostly because `min_specialization` didn't let me
+/// repeat the `T` type parameter in the above specialization, so instead
+/// the `T == U` constraint comes from the impls on this.
+/// # Safety
+/// - Neither `Self` nor `U` has any padding.
+/// - `Self` and `U` have the same layout.
+/// - `Self: PartialEq<U>` is byte-wise (this means no floats, among other things)
+#[rustc_specialization_trait]
+unsafe trait IsRawEqComparable<U> {}
+
+macro_rules! is_raw_comparable {
+    ($($t:ty),+) => {$(
+        unsafe impl IsRawEqComparable<$t> for $t {}
+    )+};
+}
+is_raw_comparable!(bool, char, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index c5a4bbd3208..7d2c278aa05 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1913,6 +1913,22 @@ extern "rust-intrinsic" {
     /// Allocate at compile time. Should not be called at runtime.
     #[rustc_const_unstable(feature = "const_heap", issue = "79597")]
     pub fn const_allocate(size: usize, align: usize) -> *mut u8;
+
+    /// Determines whether the raw bytes of the two values are equal.
+    ///
+    /// The is particularly handy for arrays, since it allows things like just
+    /// comparing `i96`s instead of forcing `alloca`s for `[6 x i16]`.
+    ///
+    /// Above some backend-decided threshold this will emit calls to `memcmp`,
+    /// like slice equality does, instead of causing massive code size.
+    ///
+    /// # Safety
+    ///
+    /// This doesn't take into account padding, so if `T` has padding
+    /// the result will be `undef`, which cannot be exposed to safe code.
+    #[cfg(not(bootstrap))]
+    #[rustc_const_unstable(feature = "const_intrinsic_raw_eq", issue = "none")]
+    pub fn raw_eq<T>(a: &T, b: &T) -> bool;
 }
 
 // Some functions are defined here because they accidentally got made