Skip to content

Commit 3da115a

Browse files
committed
Add+Use mir::BinOp::Cmp
1 parent 744c664 commit 3da115a

35 files changed

+514
-112
lines changed

compiler/rustc_codegen_cranelift/src/codegen_i128.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub(crate) fn maybe_codegen<'tcx>(
6868
Some(CValue::by_val(ret_val, lhs.layout()))
6969
}
7070
}
71-
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
71+
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None,
7272
BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
7373
}
7474
}
@@ -134,6 +134,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
134134
BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
135135
BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
136136
BinOp::Div | BinOp::Rem => unreachable!(),
137+
BinOp::Cmp => unreachable!(),
137138
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
138139
BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
139140
}

compiler/rustc_codegen_cranelift/src/num.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,33 @@ pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option<IntCC> {
4040
})
4141
}
4242

43+
fn codegen_three_way_compare<'tcx>(
44+
fx: &mut FunctionCx<'_, '_, 'tcx>,
45+
signed: bool,
46+
lhs: Value,
47+
rhs: Value,
48+
) -> CValue<'tcx> {
49+
// This emits `(lhs > rhs) - (lhs < rhs)`, which is cranelift's preferred form per
50+
// <https://github.com/bytecodealliance/wasmtime/blob/8052bb9e3b792503b225f2a5b2ba3bc023bff462/cranelift/codegen/src/prelude_opt.isle#L41-L47>
51+
let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap();
52+
let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap();
53+
let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs);
54+
let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs);
55+
let val = fx.bcx.ins().isub(gt, lt);
56+
CValue::by_val(val, fx.layout_of(fx.tcx.ty_ordering_enum(Some(fx.mir.span))))
57+
}
58+
4359
fn codegen_compare_bin_op<'tcx>(
4460
fx: &mut FunctionCx<'_, '_, 'tcx>,
4561
bin_op: BinOp,
4662
signed: bool,
4763
lhs: Value,
4864
rhs: Value,
4965
) -> CValue<'tcx> {
66+
if bin_op == BinOp::Cmp {
67+
return codegen_three_way_compare(fx, signed, lhs, rhs);
68+
}
69+
5070
let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap();
5171
let val = fx.bcx.ins().icmp(intcc, lhs, rhs);
5272
CValue::by_val(val, fx.layout_of(fx.tcx.types.bool))
@@ -59,7 +79,7 @@ pub(crate) fn codegen_binop<'tcx>(
5979
in_rhs: CValue<'tcx>,
6080
) -> CValue<'tcx> {
6181
match bin_op {
62-
BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
82+
BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => {
6383
match in_lhs.layout().ty.kind() {
6484
ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => {
6585
let signed = type_sign(in_lhs.layout().ty);
@@ -160,7 +180,7 @@ pub(crate) fn codegen_int_binop<'tcx>(
160180
}
161181
BinOp::Offset => unreachable!("Offset is not an integer operation"),
162182
// Compare binops handles by `codegen_binop`.
163-
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
183+
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Cmp => {
164184
unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty);
165185
}
166186
};

compiler/rustc_codegen_gcc/src/common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
9494
self.const_int(self.type_i32(), i as i64)
9595
}
9696

97+
fn const_i8(&self, i: i8) -> RValue<'gcc> {
98+
self.const_int(self.type_i8(), i as i64)
99+
}
100+
97101
fn const_u32(&self, i: u32) -> RValue<'gcc> {
98102
self.const_uint(self.type_u32(), i as u64)
99103
}

compiler/rustc_codegen_llvm/src/common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
160160
self.const_int(self.type_i32(), i as i64)
161161
}
162162

163+
fn const_i8(&self, i: i8) -> &'ll Value {
164+
self.const_int(self.type_i8(), i as i64)
165+
}
166+
163167
fn const_u32(&self, i: u32) -> &'ll Value {
164168
self.const_uint(self.type_i32(), i as u64)
165169
}

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::common::{self, IntPredicate};
77
use crate::traits::*;
88
use crate::MemFlags;
99

10+
use rustc_hir as hir;
1011
use rustc_middle::mir;
1112
use rustc_middle::mir::Operand;
1213
use rustc_middle::ty::cast::{CastTy, IntTy};
@@ -882,6 +883,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
882883
bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs)
883884
}
884885
}
886+
mir::BinOp::Cmp => {
887+
use std::cmp::Ordering;
888+
debug_assert!(!is_float);
889+
let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed);
890+
if bx.cx().tcx().sess.opts.optimize == OptLevel::No {
891+
// FIXME: This actually generates tighter assembly, and is a classic trick
892+
// <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>
893+
// However, as of 2023-11 it optimizes worse in things like derived
894+
// `PartialOrd`, so only use it in debug for now. Once LLVM can handle it
895+
// better (see <https://github.com/llvm/llvm-project/issues/73417>), it'll
896+
// be worth trying it in optimized builds as well.
897+
let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs);
898+
let gtext = bx.zext(is_gt, bx.type_i8());
899+
let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
900+
let ltext = bx.zext(is_lt, bx.type_i8());
901+
bx.unchecked_ssub(gtext, ltext)
902+
} else {
903+
// These operations are those expected by `tests/codegen/integer-cmp.rs`,
904+
// from <https://github.com/rust-lang/rust/pull/63767>.
905+
let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
906+
let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs);
907+
let ge = bx.select(
908+
is_ne,
909+
bx.cx().const_i8(Ordering::Greater as i8),
910+
bx.cx().const_i8(Ordering::Equal as i8),
911+
);
912+
bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge)
913+
}
914+
}
885915
}
886916
}
887917

compiler/rustc_codegen_ssa/src/traits/consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
1919
fn const_bool(&self, val: bool) -> Self::Value;
2020
fn const_i16(&self, i: i16) -> Self::Value;
2121
fn const_i32(&self, i: i32) -> Self::Value;
22+
fn const_i8(&self, i: i8) -> Self::Value;
2223
fn const_u32(&self, i: u32) -> Self::Value;
2324
fn const_u64(&self, i: u64) -> Self::Value;
2425
fn const_u128(&self, i: u128) -> Self::Value;

compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,13 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
235235
Self::from_scalar(Scalar::from_bool(b), layout)
236236
}
237237

238+
#[inline]
239+
pub fn from_ordering(c: std::cmp::Ordering, tcx: TyCtxt<'tcx>) -> Self {
240+
let ty = tcx.ty_ordering_enum(None);
241+
let layout = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)).unwrap();
242+
Self::from_scalar(Scalar::from_i8(c as i8), layout)
243+
}
244+
238245
#[inline]
239246
pub fn to_const_int(self) -> ConstInt {
240247
assert!(self.layout.ty.is_integral());

compiler/rustc_const_eval/src/interpret/operator.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6161
}
6262

6363
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
64+
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> (ImmTy<'tcx, M::Provenance>, bool) {
65+
let res = Ord::cmp(&lhs, &rhs);
66+
return (ImmTy::from_ordering(res, *self.tcx), false);
67+
}
68+
6469
fn binary_char_op(
6570
&self,
6671
bin_op: mir::BinOp,
@@ -69,6 +74,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6974
) -> (ImmTy<'tcx, M::Provenance>, bool) {
7075
use rustc_middle::mir::BinOp::*;
7176

77+
if bin_op == Cmp {
78+
return self.three_way_compare(l, r);
79+
}
80+
7281
let res = match bin_op {
7382
Eq => l == r,
7483
Ne => l != r,
@@ -231,6 +240,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
231240
let r = self.sign_extend(r, right_layout) as i128;
232241
return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false));
233242
}
243+
if bin_op == Cmp {
244+
let l = self.sign_extend(l, left_layout) as i128;
245+
let r = self.sign_extend(r, right_layout) as i128;
246+
return Ok(self.three_way_compare(l, r));
247+
}
234248
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
235249
Div if r == 0 => throw_ub!(DivisionByZero),
236250
Rem if r == 0 => throw_ub!(RemainderByZero),
@@ -270,6 +284,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
270284
}
271285
}
272286

287+
if bin_op == Cmp {
288+
return Ok(self.three_way_compare(l, r));
289+
}
290+
273291
let val = match bin_op {
274292
Eq => ImmTy::from_bool(l == r, *self.tcx),
275293
Ne => ImmTy::from_bool(l != r, *self.tcx),

compiler/rustc_const_eval/src/transform/validate.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
987987
)
988988
}
989989
}
990+
Cmp => {
991+
for x in [a, b] {
992+
check_kinds!(
993+
x,
994+
"Cannot three-way compare non-integer type {:?}",
995+
ty::Char | ty::Uint(..) | ty::Int(..)
996+
)
997+
}
998+
}
990999
AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr
9911000
| ShrUnchecked => {
9921001
for x in [a, b] {

compiler/rustc_const_eval/src/util/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn binop_left_homogeneous(op: mir::BinOp) -> bool {
1919
match op {
2020
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
2121
| BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
22-
Eq | Ne | Lt | Le | Gt | Ge => false,
22+
Eq | Ne | Lt | Le | Gt | Ge | Cmp => false,
2323
}
2424
}
2525

@@ -30,7 +30,7 @@ pub fn binop_right_homogeneous(op: mir::BinOp) -> bool {
3030
use rustc_middle::mir::BinOp::*;
3131
match op {
3232
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
33-
| BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
33+
| BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge | Cmp => true,
3434
Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
3535
}
3636
}

0 commit comments

Comments
 (0)