Skip to content

Commit 695da5b

Browse files
authored
Rollup merge of rust-lang#133964 - joboet:select_unpredictable, r=tgross35
core: implement `bool::select_unpredictable` Tracking issue: rust-lang#133962 ACP: rust-lang/libs-team#468
2 parents 7349f6b + 8f3aa35 commit 695da5b

File tree

4 files changed

+86
-3
lines changed

4 files changed

+86
-3
lines changed

library/core/src/bool.rs

+48
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,52 @@ impl bool {
6161
pub fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
6262
if self { Some(f()) } else { None }
6363
}
64+
65+
/// Returns either `true_val` or `false_val` depending on the value of
66+
/// `self`, with a hint to the compiler that `self` is unlikely
67+
/// to be correctly predicted by a CPU’s branch predictor.
68+
///
69+
/// This method is functionally equivalent to
70+
/// ```ignore (this is just for illustrative purposes)
71+
/// fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
72+
/// if b { true_val } else { false_val }
73+
/// }
74+
/// ```
75+
/// but might generate different assembly. In particular, on platforms with
76+
/// a conditional move or select instruction (like `cmov` on x86 or `csel`
77+
/// on ARM) the optimizer might use these instructions to avoid branches,
78+
/// which can benefit performance if the branch predictor is struggling
79+
/// with predicting `condition`, such as in an implementation of binary
80+
/// search.
81+
///
82+
/// Note however that this lowering is not guaranteed (on any platform) and
83+
/// should not be relied upon when trying to write constant-time code. Also
84+
/// be aware that this lowering might *decrease* performance if `condition`
85+
/// is well-predictable. It is advisable to perform benchmarks to tell if
86+
/// this function is useful.
87+
///
88+
/// # Examples
89+
///
90+
/// Distribute values evenly between two buckets:
91+
/// ```
92+
/// #![feature(select_unpredictable)]
93+
///
94+
/// use std::hash::BuildHasher;
95+
///
96+
/// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
97+
/// let hash = hasher.hash_one(&v);
98+
/// let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two);
99+
/// bucket.push(v);
100+
/// }
101+
/// # let hasher = std::collections::hash_map::RandomState::new();
102+
/// # let mut bucket_one = Vec::new();
103+
/// # let mut bucket_two = Vec::new();
104+
/// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
105+
/// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
106+
/// ```
107+
#[inline(always)]
108+
#[unstable(feature = "select_unpredictable", issue = "133962")]
109+
pub fn select_unpredictable<T>(self, true_val: T, false_val: T) -> T {
110+
crate::intrinsics::select_unpredictable(self, true_val, false_val)
111+
}
64112
}

library/core/src/intrinsics/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1545,7 +1545,7 @@ pub const fn unlikely(b: bool) -> bool {
15451545
/// Therefore, implementations must not require the user to uphold
15461546
/// any safety invariants.
15471547
///
1548-
/// This intrinsic does not have a stable counterpart.
1548+
/// The public form of this instrinsic is [`bool::select_unpredictable`].
15491549
#[unstable(feature = "core_intrinsics", issue = "none")]
15501550
#[rustc_intrinsic]
15511551
#[rustc_nounwind]

library/core/src/slice/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#![stable(feature = "rust1", since = "1.0.0")]
88

99
use crate::cmp::Ordering::{self, Equal, Greater, Less};
10-
use crate::intrinsics::{exact_div, select_unpredictable, unchecked_sub};
10+
use crate::intrinsics::{exact_div, unchecked_sub};
1111
use crate::mem::{self, SizedTypeProperties};
1212
use crate::num::NonZero;
1313
use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive};
@@ -2835,7 +2835,7 @@ impl<T> [T] {
28352835
// Binary search interacts poorly with branch prediction, so force
28362836
// the compiler to use conditional moves if supported by the target
28372837
// architecture.
2838-
base = select_unpredictable(cmp == Greater, base, mid);
2838+
base = (cmp == Greater).select_unpredictable(base, mid);
28392839

28402840
// This is imprecise in the case where `size` is odd and the
28412841
// comparison returns Greater: the mid element still gets included
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//@ compile-flags: -O
2+
3+
#![feature(select_unpredictable)]
4+
#![crate_type = "lib"]
5+
6+
#[no_mangle]
7+
pub fn test_int(p: bool, a: u64, b: u64) -> u64 {
8+
// CHECK-LABEL: define{{.*}} @test_int
9+
// CHECK: select i1 %p, i64 %a, i64 %b, !unpredictable
10+
p.select_unpredictable(a, b)
11+
}
12+
13+
#[no_mangle]
14+
pub fn test_pair(p: bool, a: (u64, u64), b: (u64, u64)) -> (u64, u64) {
15+
// CHECK-LABEL: define{{.*}} @test_pair
16+
// CHECK: select i1 %p, {{.*}}, !unpredictable
17+
p.select_unpredictable(a, b)
18+
}
19+
20+
struct Large {
21+
e: [u64; 100],
22+
}
23+
24+
#[no_mangle]
25+
pub fn test_struct(p: bool, a: Large, b: Large) -> Large {
26+
// CHECK-LABEL: define{{.*}} @test_struct
27+
// CHECK: select i1 %p, {{.*}}, !unpredictable
28+
p.select_unpredictable(a, b)
29+
}
30+
31+
#[no_mangle]
32+
pub fn test_zst(p: bool, a: (), b: ()) -> () {
33+
// CHECK-LABEL: define{{.*}} @test_zst
34+
p.select_unpredictable(a, b)
35+
}

0 commit comments

Comments
 (0)