about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/core/src/bool.rs46
-rw-r--r--library/core/src/intrinsics/mod.rs2
2 files changed, 47 insertions, 1 deletions
diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs
index 58a870d2e07..b9590dcc1e7 100644
--- a/library/core/src/bool.rs
+++ b/library/core/src/bool.rs
@@ -60,4 +60,50 @@ impl bool {
     pub fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
         if self { Some(f()) } else { None }
     }
+
+    /// Returns either `true_val` or `false_val` depending on the value of
+    /// `condition`, with a hint to the compiler that `condition` is unlikely
+    /// to be correctly predicted by a CPU’s branch predictor.
+    ///
+    /// This method is functionally equivalent to writing
+    /// ```ignore (this is just for illustrative purposes)
+    /// if b { true_val } else { false_val }
+    /// ```
+    /// but might generate different assembly. In particular, on platforms with
+    /// a conditional move or select instruction (like `cmov` on x86 or `csel`
+    /// on ARM) the optimizer might use these instructions to avoid branches,
+    /// which can benefit performance if the branch predictor is struggling
+    /// with predicting `condition`, such as in an implementation of  binary
+    /// search.
+    ///
+    /// Note however that this lowering is not guaranteed (on any platform) and
+    /// should not be relied upon when trying to write constant-time code. Also
+    /// be aware that this lowering might *decrease* performance if `condition`
+    /// is well-predictable. It is advisable to perform benchmarks to tell if
+    /// this function is useful.
+    ///
+    /// # Examples
+    ///
+    /// Distribute values evenly between two buckets:
+    /// ```
+    /// #![feature(select_unpredictable)]
+    ///
+    /// use std::hash::BuildHasher;
+    ///
+    /// fn append<H: BuildHasher>(hasher: &H, v: i32, bucket_one: &mut Vec<i32>, bucket_two: &mut Vec<i32>) {
+    ///     let hash = hasher.hash_one(&v);
+    ///     let bucket = (hash % 2 == 0).select_unpredictable(bucket_one, bucket_two);
+    ///     bucket.push(v);
+    /// }
+    /// # let hasher = std::collections::hash_map::RandomState::new();
+    /// # let mut bucket_one = Vec::new();
+    /// # let mut bucket_two = Vec::new();
+    /// # append(&hasher, 42, &mut bucket_one, &mut bucket_two);
+    /// # assert_eq!(bucket_one.len() + bucket_two.len(), 1);
+    /// ```
+    #[inline(always)]
+    #[unstable(feature = "select_unpredictable", issue = "133962")]
+    pub fn select_unpredictable<T>(self, true_val: T, false_val: T) -> T {
+        crate::intrinsics::select_unpredictable(self, true_val, false_val)
+    }
 }
diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs
index 802b571c510..f21e8816418 100644
--- a/library/core/src/intrinsics/mod.rs
+++ b/library/core/src/intrinsics/mod.rs
@@ -1544,7 +1544,7 @@ pub const fn unlikely(b: bool) -> bool {
 /// Therefore, implementations must not require the user to uphold
 /// any safety invariants.
 ///
-/// This intrinsic does not have a stable counterpart.
+/// The public form of this instrinsic is [`bool::select_unpredictable`].
 #[unstable(feature = "core_intrinsics", issue = "none")]
 #[rustc_intrinsic]
 #[rustc_nounwind]