From 6fcc3dc1289fd0f917e53025b21ef37acd0311c2 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 1 Sep 2019 18:58:29 -0700 Subject: [PATCH] Even more optimal Ord implementation for integers --- src/libcore/cmp.rs | 29 +++++++++++++++---- src/test/codegen/integer-cmp.rs | 50 +++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 607427a85d67a..88ddf83d94233 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -638,6 +638,22 @@ impl PartialOrd for Ordering { fn partial_cmp(&self, other: &Ordering) -> Option { (*self as i32).partial_cmp(&(*other as i32)) } + #[inline] + fn lt(&self, other: &Ordering) -> bool { + (*self as i32).lt(&(*other as i32)) + } + #[inline] + fn le(&self, other: &Ordering) -> bool { + (*self as i32).le(&(*other as i32)) + } + #[inline] + fn gt(&self, other: &Ordering) -> bool { + (*self as i32).gt(&(*other as i32)) + } + #[inline] + fn ge(&self, other: &Ordering) -> bool { + (*self as i32).ge(&(*other as i32)) + } } /// Trait for values that can be compared for a sort-order. @@ -1012,11 +1028,14 @@ mod impls { impl 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 } + // Like in `signum`, this approach allows the result to be + // computed with a simpler subtraction instead of needing a + // conditional move (which, as of 2018 x86 hardware, are never + // predicted), for a small cycles & code size improvement. + // See for more info. + let diff = (*self > *other) as i8 - (*self < *other) as i8; + // Sound because Ordering's three variants are {-1, 0, 1}. + unsafe { crate::mem::transmute(diff) } } } )*) diff --git a/src/test/codegen/integer-cmp.rs b/src/test/codegen/integer-cmp.rs index 8ada3cf09d073..131945966c255 100644 --- a/src/test/codegen/integer-cmp.rs +++ b/src/test/codegen/integer-cmp.rs @@ -1,5 +1,5 @@ // This is test for more optimal Ord implementation for integers. -// See for more info. +// See for more info. // compile-flags: -C opt-level=3 @@ -10,19 +10,59 @@ use std::cmp::Ordering; // CHECK-LABEL: @cmp_signed #[no_mangle] pub fn cmp_signed(a: i64, b: i64) -> Ordering { +// CHECK: icmp sgt +// CHECK: zext i1 // CHECK: icmp slt -// CHECK: icmp ne // CHECK: zext i1 -// CHECK: select i1 +// CHECK: sub nsw +// CHECK-NOT: select a.cmp(&b) } // CHECK-LABEL: @cmp_unsigned #[no_mangle] pub fn cmp_unsigned(a: u32, b: u32) -> Ordering { +// CHECK: icmp ugt +// CHECK: zext i1 // CHECK: icmp ult -// CHECK: icmp ne // CHECK: zext i1 -// CHECK: select i1 +// CHECK: sub nsw +// CHECK-NOT: select a.cmp(&b) } + +// CHECK-LABEL: @cmp_signed_lt +#[no_mangle] +pub fn cmp_signed_lt(a: &i64, b: &i64) -> bool { +// CHECK: icmp slt +// CHECK-NOT: sub +// CHECK-NOT: select + Ord::cmp(a, b) < Ordering::Equal +} + +// CHECK-LABEL: @cmp_unsigned_lt +#[no_mangle] +pub fn cmp_unsigned_lt(a: &u32, b: &u32) -> bool { +// CHECK: icmp ult +// CHECK-NOT: sub +// CHECK-NOT: select + Ord::cmp(a, b) < Ordering::Equal +} + +// CHECK-LABEL: @cmp_signed_eq +#[no_mangle] +pub fn cmp_signed_eq(a: &i64, b: &i64) -> bool { +// CHECK: icmp eq +// CHECK-NOT: sub +// CHECK-NOT: select + Ord::cmp(a, b) == Ordering::Equal +} + +// CHECK-LABEL: @cmp_unsigned_eq +#[no_mangle] +pub fn cmp_unsigned_eq(a: &u32, b: &u32) -> bool { +// CHECK: icmp eq +// CHECK-NOT: sub +// CHECK-NOT: select + Ord::cmp(a, b) == Ordering::Equal +}