rust-lang/rust · #161275
Refactor `core::cmp::{smallest, largest}` & add `mir-opt` test
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)+}