Skip to content

Commit f953157

Browse files
ojedaintel-lab-lkp
authored andcommitted
rust: std_vendor: add {likely,unlikely,cold_path}()
`cold_path` is becoming stable [1] in the upcoming Rust 1.95.0 (expected 2026-04-16). `cold_path()` can be used directly, but it also allows us to provide `likely()` and `unlikely()`, based on `cold_path()`, similar to the C side ones. For instance, given: fn f1(a: i32) -> i32 { if a < 0 { return 123; } 42 } fn f2(a: i32) -> i32 { if likely(a < 0) { return 124; } 42 } fn f3(a: i32) -> i32 { if unlikely(a < 0) { return 125; } 42 } LLVM emits the same code it would for similar C functions: f1: test %edi,%edi mov $0x7b,%ecx mov $0x2a,%eax cmovs %ecx,%eax ret f2: mov $0x7c,%eax test %edi,%edi /-- jns <f2+0xa> | ret \-> mov $0x2a,%eax ret f3: test %edi,%edi /-- js <f3+0xa> | mov $0x2a,%eax | ret \-> mov $0x7d,%eax ret The feature itself, `feature(cold_path)`, was added in Rust 1.86.0 [2]. For context, Rust 1.85.0 is likely going to be our next minimum supported version. Previously, a PR in Rust 1.84.0 [3] fixed a number of issues with the `likely()` and `unlikely()` intrinsics (by implementing them on top of the new `cold_path()` intrinsic). Thus add support for `cold_path()` by applying several approaches: - For Rust >= 1.86.0, `use` directly `core`'s `cold_path()`. - For Rust >= 1.84.0, we could choose to provide a no-op, but given Rust 1.85.0 will likely be our next minimum, do some effort to support the feature by vendoring `core`'s implementation based on the intrinsic. - For older versions, provide a no-op implementation since it is simpler (there was no `cold_path()` intrinsic), since the other intrinsics (`{,un}likely()`) were fixed later and since we will bump the minimum soon anyway. And, for all versions, simply provide `likely()` and `unlikely()` based on `cold_path()`, by vendoring `core`'s version (saving a layer using the intrinsics implementation). In the future, if `likely()` and `unlikely()` become stable, we may want to use them directly as well. Now, in the C side, the `likely()` and `unlikely()` macros come from `compiler.h`, which means it is pretty much available everywhere directly. Thus just add these to the prelude (instead of e.g. re-exporting them in the root or in a new `hint` module). This will also mean less churn when we can remove the `cold_path()` version from `std_vendor` (and potentially the other two too). I tested that Rust 1.84.1 and 1.93.0 both generate the code above, and that Rust 1.83.0 and 1.78.0 do not, as expected. Link: rust-lang/rust#151576 [1] Link: rust-lang/rust#133695 [2] Link: rust-lang/rust#120370 [3] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent ad51bc2 commit f953157

4 files changed

Lines changed: 164 additions & 0 deletions

File tree

init/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,12 @@ config RUSTC_HAS_FILE_WITH_NUL
169169
config RUSTC_HAS_FILE_AS_C_STR
170170
def_bool RUSTC_VERSION >= 109100
171171

172+
config RUSTC_HAS_COLD_PATH_INTRINSIC
173+
def_bool RUSTC_VERSION >= 108400
174+
175+
config RUSTC_HAS_COLD_PATH
176+
def_bool RUSTC_VERSION >= 108600
177+
172178
config PAHOLE_VERSION
173179
int
174180
default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE))

rust/kernel/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@
3737
#![feature(const_ptr_write)]
3838
#![feature(const_refs_to_cell)]
3939
//
40+
// `feature(cold_path)` is stable since Rust 1.95.0 and available since Rust 1.86.0. For Rust 1.84
41+
// and 1.85, use the intrinsic. For older versions, use a no-op.
42+
#![cfg_attr(CONFIG_RUSTC_HAS_COLD_PATH, feature(cold_path))]
43+
#![cfg_attr(
44+
all(not(CONFIG_RUSTC_HAS_COLD_PATH), CONFIG_RUSTC_HAS_COLD_PATH_INTRINSIC),
45+
allow(internal_features),
46+
feature(core_intrinsics)
47+
)]
48+
//
4049
// Expected to become stable.
4150
#![feature(arbitrary_self_types)]
4251
//

rust/kernel/prelude.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ pub use super::{
9898
pr_notice,
9999
pr_warn,
100100
static_assert,
101+
std_vendor::{
102+
cold_path,
103+
likely,
104+
unlikely, //
105+
},
101106
str::{
102107
CStrExt as _, //
103108
},

rust/kernel/std_vendor.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,147 @@ macro_rules! dbg {
162162
($($crate::dbg!($val)),+,)
163163
};
164164
}
165+
166+
/// Hints to the compiler that a branch condition is likely to be true.
167+
/// Returns the value passed to it.
168+
///
169+
/// It can be used with `if` or boolean `match` expressions.
170+
///
171+
/// When used outside of a branch condition, it may still influence a nearby branch, but
172+
/// probably will not have any effect.
173+
///
174+
/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to
175+
/// compound expressions, such as `likely(a && b)`. When applied to compound expressions, it has
176+
/// the following effect:
177+
/// ```text
178+
/// likely(!a) => !unlikely(a)
179+
/// likely(a && b) => likely(a) && likely(b)
180+
/// likely(a || b) => a || likely(b)
181+
/// ```
182+
///
183+
/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code.
184+
///
185+
/// # Examples
186+
///
187+
/// ```
188+
/// fn foo(x: i32) {
189+
/// if likely(x > 0) {
190+
/// pr_info!("this branch is likely to be taken\n");
191+
/// } else {
192+
/// pr_info!("this branch is unlikely to be taken\n");
193+
/// }
194+
///
195+
/// match likely(x > 0) {
196+
/// true => pr_info!("this branch is likely to be taken\n"),
197+
/// false => pr_info!("this branch is unlikely to be taken\n"),
198+
/// }
199+
///
200+
/// // Use outside of a branch condition may still influence a nearby branch
201+
/// let cond = likely(x != 0);
202+
/// if cond {
203+
/// pr_info!("this branch is likely to be taken\n");
204+
/// }
205+
/// }
206+
/// ```
207+
// This implementation is taken from `core::intrinsics::likely()`, not the `hint` wrapper.
208+
#[inline(always)]
209+
pub const fn likely(b: bool) -> bool {
210+
if b {
211+
true
212+
} else {
213+
cold_path();
214+
false
215+
}
216+
}
217+
218+
/// Hints to the compiler that a branch condition is unlikely to be true.
219+
/// Returns the value passed to it.
220+
///
221+
/// It can be used with `if` or boolean `match` expressions.
222+
///
223+
/// When used outside of a branch condition, it may still influence a nearby branch, but
224+
/// probably will not have any effect.
225+
///
226+
/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to
227+
/// compound expressions, such as `unlikely(a && b)`. When applied to compound expressions, it has
228+
/// the following effect:
229+
/// ```text
230+
/// unlikely(!a) => !likely(a)
231+
/// unlikely(a && b) => a && unlikely(b)
232+
/// unlikely(a || b) => unlikely(a) || unlikely(b)
233+
/// ```
234+
///
235+
/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code.
236+
///
237+
/// # Examples
238+
///
239+
/// ```
240+
/// fn foo(x: i32) {
241+
/// if unlikely(x > 0) {
242+
/// pr_info!("this branch is unlikely to be taken\n");
243+
/// } else {
244+
/// pr_info!("this branch is likely to be taken\n");
245+
/// }
246+
///
247+
/// match unlikely(x > 0) {
248+
/// true => pr_info!("this branch is unlikely to be taken\n"),
249+
/// false => pr_info!("this branch is likely to be taken\n"),
250+
/// }
251+
///
252+
/// // Use outside of a branch condition may still influence a nearby branch
253+
/// let cond = unlikely(x != 0);
254+
/// if cond {
255+
/// pr_info!("this branch is likely to be taken\n");
256+
/// }
257+
/// }
258+
/// ```
259+
// This implementation is taken from `core::intrinsics::unlikely()`, not the `hint` wrapper.
260+
#[inline(always)]
261+
pub const fn unlikely(b: bool) -> bool {
262+
if b {
263+
cold_path();
264+
true
265+
} else {
266+
false
267+
}
268+
}
269+
270+
/// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may
271+
/// choose to optimize paths that are not cold at the expense of paths that are cold.
272+
///
273+
/// Note that like all hints, the exact effect to codegen is not guaranteed. Using `cold_path`
274+
/// can actually *decrease* performance if the branch is called more than expected. It is advisable
275+
/// to perform benchmarks to tell if this function is useful.
276+
///
277+
/// # Examples
278+
///
279+
/// ```
280+
/// fn foo(x: &[i32]) {
281+
/// if let Some(first) = x.first() {
282+
/// // this is the fast path
283+
/// } else {
284+
/// // this path is unlikely
285+
/// cold_path();
286+
/// }
287+
/// }
288+
///
289+
/// fn bar(x: i32) -> i32 {
290+
/// match x {
291+
/// 1 => 10,
292+
/// 2 => 100,
293+
/// 3 => { cold_path(); 1000 }, // this branch is unlikely
294+
/// _ => { cold_path(); 10000 }, // this is also unlikely
295+
/// }
296+
/// }
297+
/// ```
298+
///
299+
/// See also the [`likely()`] and [`unlikely()`] functions, which are similar to the C side macros.
300+
#[cfg(not(CONFIG_RUSTC_HAS_COLD_PATH))]
301+
#[inline(always)]
302+
pub const fn cold_path() {
303+
#[cfg(CONFIG_RUSTC_HAS_COLD_PATH_INTRINSIC)]
304+
core::intrinsics::cold_path()
305+
}
306+
307+
#[cfg(CONFIG_RUSTC_HAS_COLD_PATH)]
308+
pub use core::hint::cold_path;

0 commit comments

Comments
 (0)