rust-lang/rust · #161275

Refactor `core::cmp::{smallest, largest}` & add `mir-opt` test

bushrat011899 · merged Sep 24, 20264 files · 115 + / 49 −
library/core/src/cmp.rs40 + / 49 −
@@ -1967,24 +1967,19 @@ macro impl_tuples($($mac:ident,)+) { }  /// Implementation detail for [`smallest`] and [`largest`].-/// Marker indicating that `Self` is a tuple where all members are of the same type.+/// Marker indicating that `Self` is a tuple where all members are of the type `T`.+/// Cannot be an associated type as we require the empty tuple to implement this trait+/// for all types `T`. #[diagnostic::on_unimplemented(message = "`{Self}` is not a homogeneous tuple")] #[unstable(feature = "cmp_splat_internals", issue = "160728")] #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]-const trait HomogeneousTuple: crate::marker::Tuple {-    /// The type of each item in this tuple.-    type Item;-}+const trait HomogeneousTuple<T>: crate::marker::Tuple {}  /// Implements [`HomogeneousTuple`] for a provided tuple.-macro impl_homogeneous_tuple($($($x:ident,)+)?) {-    $(-        #[unstable(feature = "cmp_splat_internals", issue = "160728")]-        #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]-        const impl<T> HomogeneousTuple for ($(${ignore($x)}T,)+) {-            type Item = T;-        }-    )?+macro impl_homogeneous_tuple($($x:ident,)*) {+    #[unstable(feature = "cmp_splat_internals", issue = "160728")]+    #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]+    const impl<T> HomogeneousTuple<T> for ($(${ignore($x)}T,)*) { } }  impl_tuples! {@@ -2040,36 +2035,34 @@ impl_tuples! { #[expect(private_bounds, reason = "`SmallestArgs` is an internal implementation detail")] #[cfg(not(test))] // FIXME: splat interacts poorly with the double linking of `core` in tests pub const fn smallest<T: [const] Ord + [const] Destruct>(-    #[rustc_splat] args: impl [const] SmallestArgs<Item = T>,+    v1: T,+    #[rustc_splat] args: impl [const] SmallestArgs<T>, ) -> T {-    SmallestArgs::smallest(args)+    SmallestArgs::smallest(v1, args) }  /// Implementation detail for [`smallest`]. #[diagnostic::on_unimplemented(message = "`{Self}` is not a valid set of arguments for `smallest`")] #[unstable(feature = "cmp_splat_internals", issue = "160728")] #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]-const trait SmallestArgs: HomogeneousTuple {+const trait SmallestArgs<T>: HomogeneousTuple<T> {     /// Reduces all elements of a homogeneous tuple to its smallest value.-    fn smallest(self) -> Self::Item;+    fn smallest(v1: T, args: Self) -> T; }  /// Implements [`SmallestArgs`] for a provided tuple if applicable.-macro impl_smallest_args($($x:ident, $($($y:ident,)+)?)?) {-    $(-        #[unstable(feature = "cmp_splat_internals", issue = "160728")]-        #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]-        const impl<T> SmallestArgs for (T, $($(${ignore($y)}T,)+)?)-        $(where T: [const] Destruct + [const] Ord, $(${ignore($y)})+)?-        {-            #[inline]-            fn smallest(self) -> Self::Item {-                let ($x, $($($y,)+)?) = self;-                $($(let $x = $x.min($y);)+)?-                $x-            }+macro impl_smallest_args($($x:ident,)*) {+    #[unstable(feature = "cmp_splat_internals", issue = "160728")]+    #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]+    const impl<T> SmallestArgs<T> for ($(${ignore($x)}T,)*)+    where+        T: [const] Destruct + [const] Ord,+    {+        #[inline(always)] // improves unoptimised codegen+        fn smallest(v1: T, ($($x,)*): Self) -> T {+            v1$(.min($x))*         }-    )?+    } }  impl_tuples! {@@ -2125,36 +2118,34 @@ impl_tuples! { #[expect(private_bounds, reason = "`LargestArgs` is an internal implementation detail")] #[cfg(not(test))] // FIXME: splat interacts poorly with the double linking of `core` in tests pub const fn largest<T: [const] Ord + [const] Destruct>(-    #[rustc_splat] args: impl [const] LargestArgs<Item = T>,+    v1: T,+    #[rustc_splat] args: impl [const] LargestArgs<T>, ) -> T {-    LargestArgs::largest(args)+    LargestArgs::largest(v1, args) }  /// Implementation detail for [`largest`]. #[diagnostic::on_unimplemented(message = "`{Self}` is not a valid set of arguments for `largest`")] #[unstable(feature = "cmp_splat_internals", issue = "160728")] #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]-const trait LargestArgs: HomogeneousTuple {+const trait LargestArgs<T>: HomogeneousTuple<T> {     /// Reduces all elements of a homogeneous tuple to its largest value.-    fn largest(self) -> Self::Item;+    fn largest(v1: T, args: Self) -> T; }  /// Implements [`LargestArgs`] for a provided tuple if applicable.-macro impl_largest_args($($x:ident, $($($y:ident,)+)?)?) {-    $(-        #[unstable(feature = "cmp_splat_internals", issue = "160728")]-        #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]-        const impl<T> LargestArgs for (T, $($(${ignore($y)}T,)+)?)-        $(where T: [const] Destruct + [const] Ord, $(${ignore($y)})+)?-        {-            #[inline]-            fn largest(self) -> Self::Item {-                let ($x, $($($y,)+)?) = self;-                $($(let $x = $x.max($y);)+)?-                $x-            }+macro impl_largest_args($($x:ident,)*) {+    #[unstable(feature = "cmp_splat_internals", issue = "160728")]+    #[rustc_const_unstable(feature = "cmp_splat_internals", issue = "160728")]+    const impl<T> LargestArgs<T> for ($(${ignore($x)}T,)*)+    where+        T: [const] Destruct + [const] Ord,+    {+        #[inline(always)] // improves unoptimised codegen+        fn largest(v1: T, ($($x,)*): Self) -> T {+            v1$(.max($x))*         }-    )?+    } }  impl_tuples! {
tests/mir-opt/pre-codegen/cmp_splat.min_direct_u8.runtime-optimized.after.miradded19 + / 0 −
@@ -0,0 +1,19 @@+// MIR for `min_direct_u8` after runtime-optimized++fn min_direct_u8(_1: u8, _2: u8) -> u8 {+    debug x => _1;+    debug y => _2;+    let mut _0: u8;+    scope 1 (inlined std::cmp::min::<u8>) {+        scope 2 (inlined std::cmp::impls::<impl Ord for u8>::min) {+        }+    }++    bb0: {+        _0 = integer_min::<u8>(move _1, move _2) -> [return: bb1, unwind unreachable];+    }++    bb1: {+        return;+    }+}
tests/mir-opt/pre-codegen/cmp_splat.min_splatted_u8.runtime-optimized.after.miradded23 + / 0 −
@@ -0,0 +1,23 @@+// MIR for `min_splatted_u8` after runtime-optimized++fn min_splatted_u8(_1: u8, _2: u8) -> u8 {+    debug x => _1;+    debug y => _2;+    let mut _0: u8;+    scope 1 (inlined smallest::<u8, (u8,)>) {+        scope 2 (inlined <(u8,) as std::cmp::SmallestArgs<u8>>::smallest) {+            scope 3 {+                scope 4 (inlined std::cmp::impls::<impl Ord for u8>::min) {+                }+            }+        }+    }++    bb0: {+        _0 = integer_min::<u8>(move _1, move _2) -> [return: bb1, unwind unreachable];+    }++    bb1: {+        return;+    }+}
tests/mir-opt/pre-codegen/cmp_splat.rsadded33 + / 0 −
@@ -0,0 +1,33 @@+#![feature(cmp_splat)]++// Comparing the MIR of a splat-compatible function with it's direct counterpart.+// This test is to ensure MIR optimizations are able to remove the added layers+// of indirection.++// EMIT_MIR cmp_splat.min_direct_u8.runtime-optimized.after.mir+pub fn min_direct_u8(x: u8, y: u8) -> u8 {+    // CHECK-LABEL: fn min_direct_u8(+    // CHECK: debug x => _1;+    // CHECK: debug y => _2;+    // CHECK: bb0: {+    // CHECK-NEXT: _0 = integer_min::<u8>(move _1, move _2) -> [return: bb1, unwind unreachable];+    // CHECK-NEXT: }+    // CHECK: bb1: {+    // CHECK-NEXT: return;+    // CHECK-NEXT: }+    std::cmp::min(x, y)+}++// EMIT_MIR cmp_splat.min_splatted_u8.runtime-optimized.after.mir+pub fn min_splatted_u8(x: u8, y: u8) -> u8 {+    // CHECK-LABEL: fn min_splatted_u8(+    // CHECK: debug x => _1;+    // CHECK: debug y => _2;+    // CHECK: bb0: {+    // CHECK-NEXT: _0 = integer_min::<u8>(move _1, move _2) -> [return: bb1, unwind unreachable];+    // CHECK-NEXT: }+    // CHECK: bb1: {+    // CHECK-NEXT: return;+    // CHECK-NEXT: }+    std::cmp::smallest(x, y)+}