From d6fa33815da0b453b9ccafe3cdfe73c984bf85b7 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 5 Mar 2023 20:19:41 -0800 Subject: [PATCH] Experiment: spaceship in MIR As mentioned in , `Ord::cmp` on primitives generates a large amount of MIR, preventing (or at least discouraging) it from mir-inlining. Let's see whether making it a MIR primitive helps stuff -- derived `(Partial)Ord` in particular, if we're lucky. --- .../src/codegen_i128.rs | 1 + compiler/rustc_codegen_cranelift/src/num.rs | 23 +++++++++++- compiler/rustc_codegen_gcc/src/common.rs | 4 ++ compiler/rustc_codegen_llvm/src/common.rs | 4 ++ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 22 +++++++++++ .../rustc_codegen_ssa/src/traits/consts.rs | 1 + .../src/interpret/operator.rs | 18 +++++++++ .../rustc_const_eval/src/interpret/step.rs | 5 ++- .../src/transform/promote_consts.rs | 1 + .../src/transform/validate.rs | 9 +++++ compiler/rustc_hir/src/lang_items.rs | 1 + .../rustc_hir_analysis/src/check/intrinsic.rs | 5 +++ .../rustc_middle/src/mir/interpret/value.rs | 5 +++ compiler/rustc_middle/src/mir/syntax.rs | 2 + compiler/rustc_middle/src/mir/tcx.rs | 6 +++ compiler/rustc_middle/src/ty/context.rs | 7 ++++ .../src/lower_intrinsics.rs | 6 ++- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_ty_utils/src/consts.rs | 2 +- library/core/src/cmp.rs | 18 ++++++--- library/core/src/intrinsics.rs | 12 ++++++ tests/mir-opt/lower_intrinsics.rs | 15 ++++++++ ...hree_way_compare_char.LowerIntrinsics.diff | 37 +++++++++++++++++++ ...ee_way_compare_signed.LowerIntrinsics.diff | 34 +++++++++++++++++ ..._way_compare_unsigned.LowerIntrinsics.diff | 37 +++++++++++++++++++ 25 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.diff create mode 100644 tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.diff diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index 40bfe70771c19..ec9f5f813ea84 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -148,6 +148,7 @@ pub(crate) fn maybe_codegen<'tcx>( assert!(!checked); None } + BinOp::Cmp => None, BinOp::Shl | BinOp::Shr => None, } } diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs index c058ece96d8e3..48c1d312999a8 100644 --- a/compiler/rustc_codegen_cranelift/src/num.rs +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -40,6 +40,23 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { }) } +fn codegen_three_way_compare<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + signed: bool, + lhs: Value, + rhs: Value, +) -> CValue<'tcx> { + let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap(); + let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap(); + let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs); + let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs); + // Cranelift no longer has a single-bit type, so the comparison results + // are already `I8`s, which we can subtract to get the -1/0/+1 we want. + // See . + let val = fx.bcx.ins().isub(gt, lt); + CValue::by_val(val, fx.layout_of(fx.tcx.types.i8)) +} + fn codegen_compare_bin_op<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, bin_op: BinOp, @@ -47,6 +64,10 @@ fn codegen_compare_bin_op<'tcx>( lhs: Value, rhs: Value, ) -> CValue<'tcx> { + if bin_op == BinOp::Cmp { + return codegen_three_way_compare(fx, signed, lhs, rhs); + } + let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap(); let val = fx.bcx.ins().icmp(intcc, lhs, rhs); CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) @@ -59,7 +80,7 @@ pub(crate) fn codegen_binop<'tcx>( in_rhs: CValue<'tcx>, ) -> CValue<'tcx> { match bin_op { - BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => { match in_lhs.layout().ty.kind() { ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { let signed = type_sign(in_lhs.layout().ty); diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index c939da9cec3c2..8d1f71712a4a8 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -97,6 +97,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> RValue<'gcc> { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> RValue<'gcc> { self.const_uint(self.type_u32(), i as u64) } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index b0a9a30ab463b..b821055b03bf5 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -157,6 +157,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i8(&self, i: i8) -> &'ll Value { + self.const_int(self.type_i8(), i as i64) + } + fn const_u32(&self, i: u32) -> &'ll Value { self.const_uint(self.type_i32(), i as u64) } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 3d856986fb4f7..403bcb443e790 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -7,6 +7,7 @@ use crate::common::{self, IntPredicate}; use crate::traits::*; use crate::MemFlags; +use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::mir::Operand; use rustc_middle::ty::cast::{CastTy, IntTy}; @@ -599,6 +600,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs) } } + mir::BinOp::Cmp => { + use std::cmp::Ordering; + debug_assert!(!is_float); + // FIXME: To avoid this PR changing behaviour, the operations used + // here are those from , + // as tested by `tests/codegen/integer-cmp.rs`. + // Something in future might want to pick different ones. For example, + // maybe the ones from Clang's `<=>` operator in C++20 (see + // ) or once we + // update to new LLVM, something to take advantage of the new folds in + // . + let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); + let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs); + let ge = bx.select( + is_ne, + bx.cx().const_i8(Ordering::Greater as i8), + bx.cx().const_i8(Ordering::Equal as i8), + ); + bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) + } } } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index fdc7a30e841ed..2ee662c4e5f77 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -14,6 +14,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_bool(&self, val: bool) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; + fn const_i8(&self, i: i8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; fn const_u64(&self, i: u64) -> Self::Value; fn const_usize(&self, i: u64) -> Self::Value; diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 4decfe863e634..0c16cbf8855c2 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -59,6 +59,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + fn three_way_compare(&self, lhs: T, rhs: T) -> (Scalar, bool, Ty<'tcx>) { + let res = Ord::cmp(&lhs, &rhs) as i8; + return (Scalar::from_i8(res), false, self.tcx.ty_ordering_enum(None)); + } + fn binary_char_op( &self, bin_op: mir::BinOp, @@ -67,6 +72,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> (Scalar, bool, Ty<'tcx>) { use rustc_middle::mir::BinOp::*; + if bin_op == Cmp { + return self.three_way_compare(l, r); + } + let res = match bin_op { Eq => l == r, Ne => l != r, @@ -211,6 +220,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let r = self.sign_extend(r, right_layout) as i128; return Ok((Scalar::from_bool(op(&l, &r)), false, self.tcx.types.bool)); } + if bin_op == Cmp { + let l = self.sign_extend(l, left_layout) as i128; + let r = self.sign_extend(r, right_layout) as i128; + return Ok(self.three_way_compare(l, r)); + } let op: Option (i128, bool)> = match bin_op { Div if r == 0 => throw_ub!(DivisionByZero), Rem if r == 0 => throw_ub!(RemainderByZero), @@ -250,6 +264,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } + if bin_op == Cmp { + return Ok(self.three_way_compare(l, r)); + } + let (val, ty) = match bin_op { Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), Ne => (Scalar::from_bool(l != r), self.tcx.types.bool), diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 6863435e50878..763e6f45d2386 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -17,7 +17,7 @@ fn binop_left_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true, - Eq | Ne | Lt | Le | Gt | Ge => false, + Eq | Ne | Lt | Le | Gt | Ge | Cmp => false, } } /// Classify whether an operator is "right-homogeneous", i.e., the RHS has the @@ -26,7 +26,8 @@ fn binop_left_homogeneous(op: mir::BinOp) -> bool { fn binop_right_homogeneous(op: mir::BinOp) -> bool { use rustc_middle::mir::BinOp::*; match op { - Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true, + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge + | Cmp => true, Offset | Shl | Shr => false, } } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 3f3b66b0645a8..8fb7745bbcd0a 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -568,6 +568,7 @@ impl<'tcx> Validator<'_, 'tcx> { | BinOp::Lt | BinOp::Ge | BinOp::Gt + | BinOp::Cmp | BinOp::Offset | BinOp::Add | BinOp::Sub diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index fb37eb79a335f..70f099bc2af1d 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -483,6 +483,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } + Cmp => { + for x in [a, b] { + check_kinds!( + x, + "Cannot three-way compare non-integer type {:?}", + ty::Char | ty::Uint(..) | ty::Int(..) + ) + } + } Shl | Shr => { for x in [a, b] { check_kinds!( diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 60fa5a99e103c..1205365e3e0c5 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -221,6 +221,7 @@ language_item_table! { Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + OrderingEnum, sym::Ordering, ordering_enum, Target::Enum, GenericRequirement::Exact(0); PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 054284cced5ad..9fcc29e8f297a 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -99,6 +99,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir | sym::cttz | sym::bswap | sym::bitreverse + | sym::three_way_compare | sym::discriminant_value | sym::type_id | sym::likely @@ -316,6 +317,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { | sym::bswap | sym::bitreverse => (1, vec![param(0)], param(0)), + sym::three_way_compare => { + (1, vec![param(0), param(0)], tcx.ty_ordering_enum(Some(it.span))) + } + sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { (1, vec![param(0), param(0)], tcx.mk_tup(&[param(0), tcx.types.bool])) } diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 36dbbe4bf7765..821deb279b994 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -257,6 +257,11 @@ impl Scalar { .unwrap_or_else(|| bug!("Signed value {:#x} does not fit in {} bits", i, size.bits())) } + #[inline] + pub fn from_i8(i: i8) -> Self { + Self::from_int(i, Size::from_bits(8)) + } + #[inline] pub fn from_i32(i: i32) -> Self { Self::from_int(i, Size::from_bits(32)) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index ae09562a85e98..82fcbed6fe0a5 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1271,6 +1271,8 @@ pub enum BinOp { Ge, /// The `>` operator (greater than) Gt, + /// The `<=>` operator (three-way comparison, like `Ord::cmp`) + Cmp, /// The `ptr.offset` operator Offset, } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 0aa2c500f51fb..d6d3c3c7b6ff0 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -260,6 +260,11 @@ impl<'tcx> BinOp { &BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => { tcx.types.bool } + &BinOp::Cmp => { + // these should be integer-like types of the same size. + assert_eq!(lhs_ty, rhs_ty); + tcx.ty_ordering_enum(None) + } } } } @@ -301,6 +306,7 @@ impl BinOp { BinOp::Gt => hir::BinOpKind::Gt, BinOp::Le => hir::BinOpKind::Le, BinOp::Ge => hir::BinOpKind::Ge, + BinOp::Cmp => unreachable!(), BinOp::Offset => unreachable!(), } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 70091477e39f4..d64ed39b472e2 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -716,6 +716,13 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Gets a `Ty` representing the [`LangItem::OrderingEnum`] + #[track_caller] + pub fn ty_ordering_enum(self, span: Option) -> Ty<'tcx> { + let ordering_enum = self.require_lang_item(hir::LangItem::OrderingEnum, span); + self.type_of(ordering_enum).no_bound_vars().unwrap() + } + /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed` #[track_caller] pub fn ty_error(self, reported: ErrorGuaranteed) -> Ty<'tcx> { diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index f596cc1808fa2..a18e5e7f59e1d 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -81,7 +81,10 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { drop(args); terminator.kind = TerminatorKind::Goto { target }; } - sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { + sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::three_way_compare => { if let Some(target) = *target { let lhs; let rhs; @@ -94,6 +97,7 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::wrapping_add => BinOp::Add, sym::wrapping_sub => BinOp::Sub, sym::wrapping_mul => BinOp::Mul, + sym::three_way_compare => BinOp::Cmp, _ => bug!("unexpected intrinsic"), }; block.statements.push(Statement { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 6272bf7f25eb1..fd9cc6c0e7f1a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1473,6 +1473,7 @@ symbols! { thread, thread_local, thread_local_macro, + three_way_compare, thumb2, thumb_mode: "thumb-mode", tmm_reg, diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index f2635271609b8..c5033577715bb 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -78,7 +78,7 @@ fn check_binop(op: mir::BinOp) -> bool { use mir::BinOp::*; match op { Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne - | Ge | Gt => true, + | Ge | Gt | Cmp => true, Offset => false, } } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 5b5f55d0e65b0..3051a6367f249 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -335,6 +335,7 @@ pub struct AssertParamIsEq { #[derive(Clone, Copy, Eq, Debug, Hash)] #[derive_const(PartialOrd, Ord, PartialEq)] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), lang = "Ordering")] #[repr(i8)] pub enum Ordering { /// An ordering where a compared value is less than another. @@ -1410,11 +1411,18 @@ mod impls { impl const Ord for $t { #[inline] fn cmp(&self, other: &$t) -> Ordering { - // The order here is important to generate more optimal assembly. - // See for more info. - if *self < *other { Less } - else if *self == *other { Equal } - else { Greater } + #[cfg(bootstrap)] + { + // The order here is important to generate more optimal assembly. + // See for more info. + if *self < *other { Less } + else if *self == *other { Equal } + else { Greater } + } + #[cfg(not(bootstrap))] + { + crate::intrinsics::three_way_compare(*self, *other) + } } } )*) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 18a90599c4db9..ebafa58598dab 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1802,6 +1802,18 @@ extern "rust-intrinsic" { #[rustc_safe_intrinsic] pub fn bitreverse(x: T) -> T; + /// Does a three-way comparison between the two integer arguments. + /// + /// This is included as an intrinsic as it's useful to let it be one thing + /// in MIR, rather than the multiple checks and switches that make its IR + /// large and difficult to optimize. + /// + /// The stabilized version of this intrinsic is [`Ord::cmp`]. + #[cfg(not(bootstrap))] + #[rustc_const_unstable(feature = "const_cmp", issue = "92391")] + #[rustc_safe_intrinsic] + pub fn three_way_compare(lhs: T, rhs: T) -> crate::cmp::Ordering; + /// Performs checked integer addition. /// /// Note that, unlike most intrinsics, this is safe to call; diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 7147be43ca5e3..3beea982006db 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -79,3 +79,18 @@ pub fn with_overflow(a: i32, b: i32) { let _y = core::intrinsics::sub_with_overflow(a, b); let _z = core::intrinsics::mul_with_overflow(a, b); } + +// EMIT_MIR lower_intrinsics.three_way_compare_char.LowerIntrinsics.diff +pub fn three_way_compare_char(a: char, b: char) { + let _x = core::intrinsics::three_way_compare(a, b); +} + +// EMIT_MIR lower_intrinsics.three_way_compare_signed.LowerIntrinsics.diff +pub fn three_way_compare_signed(a: i16, b: i16) { + core::intrinsics::three_way_compare(a, b); +} + +// EMIT_MIR lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.diff +pub fn three_way_compare_unsigned(a: u32, b: u32) { + let _x = core::intrinsics::three_way_compare(a, b); +} diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.diff new file mode 100644 index 0000000000000..231568ccddb4f --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.three_way_compare_char.LowerIntrinsics.diff @@ -0,0 +1,37 @@ +- // MIR for `three_way_compare_char` before LowerIntrinsics ++ // MIR for `three_way_compare_char` after LowerIntrinsics + + fn three_way_compare_char(_1: char, _2: char) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:31: +0:32 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:+0:40: +0:41 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:49: +0:49 + let _3: std::cmp::Ordering; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let mut _4: char; // in scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + let mut _5: char; // in scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + StorageLive(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 + _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 +- _3 = three_way_compare::(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:85:14: 85:49 +- // + literal: Const { ty: extern "rust-intrinsic" fn(char, char) -> std::cmp::Ordering {three_way_compare::}, val: Value() } ++ _3 = Cmp(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 ++ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:54: +1:55 + StorageDead(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:54: +1:55 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:49: +2:2 + StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:+2:1: +2:2 + return; // scope 0 at $DIR/lower_intrinsics.rs:+2:2: +2:2 + } + } + diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.diff new file mode 100644 index 0000000000000..e1a0b0316e030 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.three_way_compare_signed.LowerIntrinsics.diff @@ -0,0 +1,34 @@ +- // MIR for `three_way_compare_signed` before LowerIntrinsics ++ // MIR for `three_way_compare_signed` after LowerIntrinsics + + fn three_way_compare_signed(_1: i16, _2: i16) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:33: +0:34 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:+0:41: +0:42 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:49: +0:49 + let _3: std::cmp::Ordering; // in scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:46 + let mut _4: i16; // in scope 0 at $DIR/lower_intrinsics.rs:+1:41: +1:42 + let mut _5: i16; // in scope 0 at $DIR/lower_intrinsics.rs:+1:44: +1:45 + + bb0: { + StorageLive(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:46 + StorageLive(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:41: +1:42 + _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:41: +1:42 + StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:44: +1:45 + _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:44: +1:45 +- _3 = three_way_compare::(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:46 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:90:5: 90:40 +- // + literal: Const { ty: extern "rust-intrinsic" fn(i16, i16) -> std::cmp::Ordering {three_way_compare::}, val: Value() } ++ _3 = Cmp(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:46 ++ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:46 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:45: +1:46 + StorageDead(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:45: +1:46 + StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:49: +2:2 + return; // scope 0 at $DIR/lower_intrinsics.rs:+2:2: +2:2 + } + } + diff --git a/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.diff new file mode 100644 index 0000000000000..1a75c79a8122f --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.three_way_compare_unsigned.LowerIntrinsics.diff @@ -0,0 +1,37 @@ +- // MIR for `three_way_compare_unsigned` before LowerIntrinsics ++ // MIR for `three_way_compare_unsigned` after LowerIntrinsics + + fn three_way_compare_unsigned(_1: u32, _2: u32) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:35: +0:36 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:+0:43: +0:44 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:51: +0:51 + let _3: std::cmp::Ordering; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let mut _4: u32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + let mut _5: u32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + StorageLive(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 + _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 +- _3 = three_way_compare::(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:95:14: 95:49 +- // + literal: Const { ty: extern "rust-intrinsic" fn(u32, u32) -> std::cmp::Ordering {three_way_compare::}, val: Value() } ++ _3 = Cmp(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 ++ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:54: +1:55 + StorageDead(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:54: +1:55 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:51: +2:2 + StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:+2:1: +2:2 + return; // scope 0 at $DIR/lower_intrinsics.rs:+2:2: +2:2 + } + } +