Skip to content

Commit d6fa338

Browse files
committed
Experiment: spaceship in MIR
As mentioned in <rust-lang#108788 (comment)>, `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.
1 parent 7820b62 commit d6fa338

File tree

25 files changed

+266
-10
lines changed

25 files changed

+266
-10
lines changed

compiler/rustc_codegen_cranelift/src/codegen_i128.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub(crate) fn maybe_codegen<'tcx>(
148148
assert!(!checked);
149149
None
150150
}
151+
BinOp::Cmp => None,
151152
BinOp::Shl | BinOp::Shr => None,
152153
}
153154
}

compiler/rustc_codegen_cranelift/src/num.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,34 @@ 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+
let gt_cc = crate::num::bin_op_to_intcc(BinOp::Gt, signed).unwrap();
50+
let lt_cc = crate::num::bin_op_to_intcc(BinOp::Lt, signed).unwrap();
51+
let gt = fx.bcx.ins().icmp(gt_cc, lhs, rhs);
52+
let lt = fx.bcx.ins().icmp(lt_cc, lhs, rhs);
53+
// Cranelift no longer has a single-bit type, so the comparison results
54+
// are already `I8`s, which we can subtract to get the -1/0/+1 we want.
55+
// See <https://github.com/bytecodealliance/wasmtime/pull/5031>.
56+
let val = fx.bcx.ins().isub(gt, lt);
57+
CValue::by_val(val, fx.layout_of(fx.tcx.types.i8))
58+
}
59+
4360
fn codegen_compare_bin_op<'tcx>(
4461
fx: &mut FunctionCx<'_, '_, 'tcx>,
4562
bin_op: BinOp,
4663
signed: bool,
4764
lhs: Value,
4865
rhs: Value,
4966
) -> CValue<'tcx> {
67+
if bin_op == BinOp::Cmp {
68+
return codegen_three_way_compare(fx, signed, lhs, rhs);
69+
}
70+
5071
let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap();
5172
let val = fx.bcx.ins().icmp(intcc, lhs, rhs);
5273
CValue::by_val(val, fx.layout_of(fx.tcx.types.bool))
@@ -59,7 +80,7 @@ pub(crate) fn codegen_binop<'tcx>(
5980
in_rhs: CValue<'tcx>,
6081
) -> CValue<'tcx> {
6182
match bin_op {
62-
BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => {
83+
BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt | BinOp::Cmp => {
6384
match in_lhs.layout().ty.kind() {
6485
ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => {
6586
let signed = type_sign(in_lhs.layout().ty);

compiler/rustc_codegen_gcc/src/common.rs

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

100+
fn const_i8(&self, i: i8) -> RValue<'gcc> {
101+
self.const_int(self.type_i8(), i as i64)
102+
}
103+
100104
fn const_u32(&self, i: u32) -> RValue<'gcc> {
101105
self.const_uint(self.type_u32(), i as u64)
102106
}

compiler/rustc_codegen_llvm/src/common.rs

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

160+
fn const_i8(&self, i: i8) -> &'ll Value {
161+
self.const_int(self.type_i8(), i as i64)
162+
}
163+
160164
fn const_u32(&self, i: u32) -> &'ll Value {
161165
self.const_uint(self.type_i32(), i as u64)
162166
}

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

Lines changed: 22 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};
@@ -599,6 +600,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
599600
bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs)
600601
}
601602
}
603+
mir::BinOp::Cmp => {
604+
use std::cmp::Ordering;
605+
debug_assert!(!is_float);
606+
// FIXME: To avoid this PR changing behaviour, the operations used
607+
// here are those from <https://github.com/rust-lang/rust/pull/63767>,
608+
// as tested by `tests/codegen/integer-cmp.rs`.
609+
// Something in future might want to pick different ones. For example,
610+
// maybe the ones from Clang's `<=>` operator in C++20 (see
611+
// <https://github.com/llvm/llvm-project/issues/60012>) or once we
612+
// update to new LLVM, something to take advantage of the new folds in
613+
// <https://github.com/llvm/llvm-project/issues/59666>.
614+
let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed);
615+
let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs);
616+
let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs);
617+
let ge = bx.select(
618+
is_ne,
619+
bx.cx().const_i8(Ordering::Greater as i8),
620+
bx.cx().const_i8(Ordering::Equal as i8),
621+
);
622+
bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge)
623+
}
602624
}
603625
}
604626

compiler/rustc_codegen_ssa/src/traits/consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
1414
fn const_bool(&self, val: bool) -> Self::Value;
1515
fn const_i16(&self, i: i16) -> Self::Value;
1616
fn const_i32(&self, i: i32) -> Self::Value;
17+
fn const_i8(&self, i: i8) -> Self::Value;
1718
fn const_u32(&self, i: u32) -> Self::Value;
1819
fn const_u64(&self, i: u64) -> Self::Value;
1920
fn const_usize(&self, i: u64) -> Self::Value;

compiler/rustc_const_eval/src/interpret/operator.rs

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

6161
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
62+
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) {
63+
let res = Ord::cmp(&lhs, &rhs) as i8;
64+
return (Scalar::from_i8(res), false, self.tcx.ty_ordering_enum(None));
65+
}
66+
6267
fn binary_char_op(
6368
&self,
6469
bin_op: mir::BinOp,
@@ -67,6 +72,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6772
) -> (Scalar<M::Provenance>, bool, Ty<'tcx>) {
6873
use rustc_middle::mir::BinOp::*;
6974

75+
if bin_op == Cmp {
76+
return self.three_way_compare(l, r);
77+
}
78+
7079
let res = match bin_op {
7180
Eq => l == r,
7281
Ne => l != r,
@@ -211,6 +220,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
211220
let r = self.sign_extend(r, right_layout) as i128;
212221
return Ok((Scalar::from_bool(op(&l, &r)), false, self.tcx.types.bool));
213222
}
223+
if bin_op == Cmp {
224+
let l = self.sign_extend(l, left_layout) as i128;
225+
let r = self.sign_extend(r, right_layout) as i128;
226+
return Ok(self.three_way_compare(l, r));
227+
}
214228
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
215229
Div if r == 0 => throw_ub!(DivisionByZero),
216230
Rem if r == 0 => throw_ub!(RemainderByZero),
@@ -250,6 +264,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
250264
}
251265
}
252266

267+
if bin_op == Cmp {
268+
return Ok(self.three_way_compare(l, r));
269+
}
270+
253271
let (val, ty) = match bin_op {
254272
Eq => (Scalar::from_bool(l == r), self.tcx.types.bool),
255273
Ne => (Scalar::from_bool(l != r), self.tcx.types.bool),

compiler/rustc_const_eval/src/interpret/step.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ fn binop_left_homogeneous(op: mir::BinOp) -> bool {
1717
use rustc_middle::mir::BinOp::*;
1818
match op {
1919
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
20-
Eq | Ne | Lt | Le | Gt | Ge => false,
20+
Eq | Ne | Lt | Le | Gt | Ge | Cmp => false,
2121
}
2222
}
2323
/// 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 {
2626
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
2727
use rustc_middle::mir::BinOp::*;
2828
match op {
29-
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
29+
Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge
30+
| Cmp => true,
3031
Offset | Shl | Shr => false,
3132
}
3233
}

compiler/rustc_const_eval/src/transform/promote_consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ impl<'tcx> Validator<'_, 'tcx> {
568568
| BinOp::Lt
569569
| BinOp::Ge
570570
| BinOp::Gt
571+
| BinOp::Cmp
571572
| BinOp::Offset
572573
| BinOp::Add
573574
| BinOp::Sub

compiler/rustc_const_eval/src/transform/validate.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
483483
);
484484
}
485485
}
486+
Cmp => {
487+
for x in [a, b] {
488+
check_kinds!(
489+
x,
490+
"Cannot three-way compare non-integer type {:?}",
491+
ty::Char | ty::Uint(..) | ty::Int(..)
492+
)
493+
}
494+
}
486495
Shl | Shr => {
487496
for x in [a, b] {
488497
check_kinds!(

0 commit comments

Comments
 (0)