rust-lang/rust · #161081

Add intrinsics for integer minimum and maximum

scottmcm · merged Aug 31, 202610 files · 184 + / 1
compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs20 + / 0
@@ -630,6 +630,26 @@ fn codegen_regular_intrinsic_call<'tcx>(             let res = crate::num::codegen_int_binop(fx, BinOp::Div, x, y);             ret.write_cvalue(fx, res);         }+        // FIXME: remove the guard here once `umin.i128` and friends are supported+        // cc https://github.com/bytecodealliance/wasmtime/issues/13790+        sym::integer_max | sym::integer_min if ret.layout().size <= Size::from_bits(64) => {+            intrinsic_args!(fx, args => (lhs, rhs); intrinsic);++            assert_eq!(lhs.layout().ty, rhs.layout().ty);+            let signed = type_sign(lhs.layout().ty);+            let lhs = lhs.load_scalar(fx);+            let rhs = rhs.load_scalar(fx);+            let res = match (intrinsic, signed) {+                (sym::integer_max, false) => fx.bcx.ins().umax(lhs, rhs),+                (sym::integer_max, true) => fx.bcx.ins().smax(lhs, rhs),+                (sym::integer_min, false) => fx.bcx.ins().umin(lhs, rhs),+                (sym::integer_min, true) => fx.bcx.ins().smin(lhs, rhs),+                _ => unreachable!(),+            };++            let res = CValue::by_val(res, ret.layout());+            ret.write_cvalue(fx, res);+        }         sym::saturating_add | sym::saturating_sub => {             intrinsic_args!(fx, args => (lhs, rhs); intrinsic); 
compiler/rustc_codegen_llvm/src/intrinsic.rs14 + / 0
@@ -476,6 +476,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {             | sym::ctpop             | sym::bswap             | sym::bitreverse+            | sym::integer_max+            | sym::integer_min             | sym::saturating_add             | sym::saturating_sub             | sym::unchecked_funnel_shl@@ -520,6 +522,18 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {                     sym::bitreverse => {                         self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])                     }+                    sym::integer_min | sym::integer_max => {+                        let lhs = args[0].immediate();+                        let rhs = args[1].immediate();+                        let llvm_name = match (name, signed) {+                            (sym::integer_max, false) => "llvm.umax",+                            (sym::integer_max, true) => "llvm.smax",+                            (sym::integer_min, false) => "llvm.umin",+                            (sym::integer_min, true) => "llvm.smin",+                            _ => bug!(),+                        };+                        self.call_intrinsic(llvm_name, &[llty], &[lhs, rhs])+                    }                     sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {                         let is_left = name == sym::unchecked_funnel_shl;                         let lhs = args[0].immediate();
compiler/rustc_codegen_llvm/src/lib.rs2 + / 0
@@ -333,6 +333,8 @@ impl CodegenBackend for LlvmCodegenBackend {             sym::unchecked_funnel_shl,             sym::unchecked_funnel_shr,             sym::carrying_mul_add,+            sym::integer_max,+            sym::integer_min,              // Fallback via libm, but the LLVM intrinsic is used instead.             sym::sinf16, sym::sinf32, sym::sinf64,
compiler/rustc_hir_analysis/src/check/intrinsic.rs3 + / 0
@@ -135,6 +135,8 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi         | sym::frem_algebraic         | sym::fsub_algebraic         | sym::gpu_launch_sized_workgroup_mem+        | sym::integer_max+        | sym::integer_min         | sym::is_val_statically_known         | sym::log2f16         | sym::log2f32@@ -602,6 +604,7 @@ pub(crate) fn check_intrinsic_type(             vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],             tcx.types.usize,         ),+        sym::integer_max | sym::integer_min => (1, 0, vec![param(0), param(0)], param(0)),         sym::unchecked_div | sym::unchecked_rem | sym::exact_div | sym::disjoint_bitor => {             (1, 0, vec![param(0), param(0)], param(0))         }
compiler/rustc_span/src/symbol.rs2 + / 0
@@ -1145,6 +1145,8 @@ symbols! {         instruction_set,         instrument_fn,         integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below+        integer_max,+        integer_min,         integral,         internal,         internal_eq_trait_method_impls,
library/core/src/cmp.rs32 + / 1
@@ -2298,8 +2298,37 @@ mod impls {      partial_ord_impl! { f16 f32 f64 f128 } +    macro_rules! min_max_impl {+        (char) => {+            #[inline]+            fn min(self, other: Self) -> Self {+                let c = u32::min(self as u32, other as u32);+                // SAFETY: it's one of the inputs+                unsafe { char::from_u32_unchecked(c) }+            }++            #[inline]+            fn max(self, other: Self) -> Self {+                let c = u32::max(self as u32, other as u32);+                // SAFETY: it's one of the inputs+                unsafe { char::from_u32_unchecked(c) }+            }+        };+        ($t:ident) => {+            #[inline]+            fn min(self, other: Self) -> Self {+                crate::intrinsics::integer_min(self, other)+            }++            #[inline]+            fn max(self, other: Self) -> Self {+                crate::intrinsics::integer_max(self, other)+            }+        };+    }+     macro_rules! ord_impl {-        ($($t:ty)*) => ($(+        ($($t:ident)*) => ($(             #[stable(feature = "rust1", since = "1.0.0")]             #[rustc_const_unstable(feature = "const_cmp", issue = "143800")]             const impl PartialOrd for $t {@@ -2338,6 +2367,8 @@ mod impls {                         self                     }                 }++                min_max_impl!($t);             }         )*)     }
library/core/src/intrinsics/bounds.rs18 + / 0
@@ -109,3 +109,21 @@ const unsafe impl FloatPrimitive for f128 {         f128::from_bits(bits)     } }++/// Built-in integer types (i8, i16, .., i128, isize, u8, u16, .., u128, usize).+///+/// Intentionally does not include other integer-repr types like `bool` or `char`.+///+/// # Safety+/// Must actually *be* such a type.+#[rustc_const_unstable(feature = "core_intrinsics", issue = "none")]+pub const unsafe trait IntegerPrimitive: Copy + [const] Ord {}++macro_rules! impl_integer_primitive {+    ($($t:ty),*) => {$(+        #[rustc_const_unstable(feature = "core_intrinsics", issue = "none")]+        const unsafe impl IntegerPrimitive for $t {}+    )*};+}+impl_integer_primitive!(i8, i16, i32, i64, i128, isize);+impl_integer_primitive!(u8, u16, u32, u64, u128, usize);
library/core/src/intrinsics/mod.rs28 + / 0
@@ -1841,6 +1841,34 @@ pub const fn fdiv_algebraic<T: bounds::FloatPrimitive>(a: T, b: T) -> T; #[rustc_intrinsic] pub const fn frem_algebraic<T: bounds::FloatPrimitive>(a: T, b: T) -> T; +/// Integer `min`imum, signed or unsigned depending on `T`.+///+/// Allowed only on `uN`, `iN`, `usize`, and `isize`.+/// (Not on `bool` nor on `char`.)+///+/// Stabilized as [`u16::min`] and [`i64::min`] and similar.+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]+#[rustc_nounwind]+#[rustc_intrinsic]+#[miri::intrinsic_fallback_is_spec]+pub const fn integer_min<T: [const] bounds::IntegerPrimitive>(a: T, b: T) -> T {+    if a < b { a } else { b }+}++/// Integer `max`imum, signed or unsigned depending on `T`.+///+/// Allowed only on `uN`, `iN`, `usize`, and `isize`.+/// (Not on `bool` nor on `char`.)+///+/// Stabilized as [`u16::max`] and [`i64::max`] and similar.+#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]+#[rustc_nounwind]+#[rustc_intrinsic]+#[miri::intrinsic_fallback_is_spec]+pub const fn integer_max<T: [const] bounds::IntegerPrimitive>(a: T, b: T) -> T {+    if a < b { b } else { a }+}+ /// Returns the number of bits set in an integer type `T` /// /// Note that, unlike most intrinsics, this is safe to call;
library/coretests/tests/cmp.rs14 + / 0
@@ -27,6 +27,20 @@ fn test_mut_int_totalord() {     assert_eq!((&mut 12).cmp(&&mut -5), Greater); } +#[test]+fn test_max_min_signedness() {+    use std::cmp::{max, min};+    // Check the "same" 8-bit values where the signedness of the operation matters+    assert_eq!(max::<u8>(0, 255), 255);+    assert_eq!(max::<u8>(255, 0), 255);+    assert_eq!(min::<u8>(0, 255), 0);+    assert_eq!(min::<u8>(255, 0), 0);+    assert_eq!(max::<i8>(0, -1), 0);+    assert_eq!(max::<i8>(-1, 0), 0);+    assert_eq!(min::<i8>(0, -1), -1);+    assert_eq!(min::<i8>(-1, 0), -1);+}+ #[test] fn test_ord_max_min() {     assert_eq!(1.max(2), 2);
tests/codegen-llvm/intrinsics/integer_min_max.rsadded51 + / 0
@@ -0,0 +1,51 @@+//@ compile-flags: -C opt-level=3 -C no-prepopulate-passes++#![crate_type = "lib"]++#[unsafe(no_mangle)]+pub fn i16_min(a: i16, b: i16) -> i16 {+    // CHECK-LABEL: i16_min+    // CHECK: [[M:%.+]] = call i16 @llvm.smin.i16(i16 %a, i16 %b)+    // CHECK-NEXT: ret i16 [[M]]+    std::cmp::min(a, b)+}++#[unsafe(no_mangle)]+pub fn i32_max(a: i32, b: i32) -> i32 {+    // CHECK-LABEL: i32_max+    // CHECK: [[M:%.+]] = call i32 @llvm.smax.i32(i32 %a, i32 %b)+    // CHECK-NEXT: ret i32 [[M]]+    std::cmp::max(a, b)+}++#[unsafe(no_mangle)]+pub fn u8_min(a: u8, b: u8) -> u8 {+    // CHECK-LABEL: u8_min+    // CHECK: [[M:%.+]] = call i8 @llvm.umin.i8(i8 %a, i8 %b)+    // CHECK-NEXT: ret i8 [[M]]+    std::cmp::min(a, b)+}++#[unsafe(no_mangle)]+pub fn u16_max(a: u16, b: u16) -> u16 {+    // CHECK-LABEL: u16_max+    // CHECK: [[M:%.+]] = call i16 @llvm.umax.i16(i16 %a, i16 %b)+    // CHECK-NEXT: ret i16 [[M]]+    std::cmp::max(a, b)+}++#[unsafe(no_mangle)]+pub fn char_min(a: char, b: char) -> char {+    // CHECK-LABEL: char_min+    // CHECK: [[M:%.+]] = call i32 @llvm.umin.i32(i32 %a, i32 %b)+    // CHECK: ret i32 [[M]]+    std::cmp::min(a, b)+}++#[unsafe(no_mangle)]+pub fn char_max(a: char, b: char) -> char {+    // CHECK-LABEL: char_max+    // CHECK: [[M:%.+]] = call i32 @llvm.umax.i32(i32 %a, i32 %b)+    // CHECK: ret i32 [[M]]+    std::cmp::max(a, b)+}