From: Miguel Ojeda <ojeda@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>, Nathan Chancellor <nathan@kernel.org>
Cc: "Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
rust-for-linux@vger.kernel.org,
"Nick Desaulniers" <nick.desaulniers+lkml@gmail.com>,
"Bill Wendling" <morbo@google.com>,
"Justin Stitt" <justinstitt@google.com>,
llvm@lists.linux.dev
Subject: [PATCH v2 2/2] rust: std_vendor: add `likely()`, `unlikely()` and `cold_path()`
Date: Mon, 6 Apr 2026 11:58:20 +0200 [thread overview]
Message-ID: <20260406095820.465994-2-ojeda@kernel.org> (raw)
In-Reply-To: <20260406095820.465994-1-ojeda@kernel.org>
`feature(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].
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). So we could use that, in principle,
for Rust 1.85.0. However, it is just a single version, and it is simpler
to avoid intrinsics. Instead, approximate it with `#[cold]` [4].
Thus add support for `cold_path()` by applying several approaches:
- For Rust >= 1.86.0, `use` directly `core`'s `cold_path()`.
- For Rust 1.85, provide a `#[cold]` no-op, and vendor `core`s
documentation.
And, for all versions, simply provide `likely()` and `unlikely()` based
on `cold_path()`, by vendoring `core`'s unstable ones (the one from
`intrinsics`, not the `hint` wrapper, to save a layer).
In the future, if `likely()` and `unlikely()` become stable [5], 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.93.0 generate the code above and, in a previous
version of the patch, that Rust 1.83.0 and 1.78.0 do not, as expected.
Link: https://github.com/rust-lang/rust/pull/151576 [1]
Link: https://github.com/rust-lang/rust/pull/133695 [2]
Link: https://github.com/rust-lang/rust/pull/120370 [3]
Link: https://lore.kernel.org/rust-for-linux/DGA6GUR58QJ7.1XZZ0P4VZNW86@garyguo.net/ [4]
Link: https://github.com/rust-lang/rust/issues/151619 [5]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
v1: https://lore.kernel.org/rust-for-linux/20260208224659.18406-3-ojeda@kernel.org/
v2:
- Drop the intrinsics and use `#[cold]` for Rust 1.85.0.
- Sorted after `feature(file_with_nul)` since now we know which was
the stable version when it happened (Rust 1.92.0).
- Rebased and reworded accordingly.
init/Kconfig | 3 +
rust/kernel/lib.rs | 4 ++
rust/kernel/prelude.rs | 5 ++
rust/kernel/std_vendor.rs | 142 ++++++++++++++++++++++++++++++++++++++
4 files changed, 154 insertions(+)
diff --git a/init/Kconfig b/init/Kconfig
index d9b795f70a38..f4c3368bf585 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -178,6 +178,9 @@ config LD_CAN_USE_KEEP_IN_OVERLAY
# https://github.com/llvm/llvm-project/pull/130661
def_bool LD_IS_BFD || LLD_VERSION >= 210000
+config RUSTC_HAS_COLD_PATH
+ def_bool RUSTC_VERSION >= 108600
+
config RUSTC_HAS_SPAN_FILE
def_bool RUSTC_VERSION >= 108800
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 0fa9d820fe7c..0615f741fcd4 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -26,6 +26,10 @@
// `feature(file_with_nul)` is stable since Rust 1.92.0. Before Rust 1.89.0, it did not exist, so
// enable it conditionally.
#![cfg_attr(CONFIG_RUSTC_HAS_FILE_WITH_NUL, feature(file_with_nul))]
+//
+// `feature(cold_path)` is stable since Rust 1.95.0. Before Rust 1.86.0, it did not exist, so
+// enable it conditionally.
+#![cfg_attr(CONFIG_RUSTC_HAS_COLD_PATH, feature(cold_path))]
// Ensure conditional compilation based on the kernel configuration works;
// otherwise we may silently break things like initcall handling.
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 44edf72a4a24..f5d6d9d9b629 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -97,6 +97,11 @@
pr_notice,
pr_warn,
static_assert,
+ std_vendor::{
+ cold_path,
+ likely,
+ unlikely, //
+ },
str::CStrExt as _,
try_init,
try_pin_init,
diff --git a/rust/kernel/std_vendor.rs b/rust/kernel/std_vendor.rs
index abbab5050cc5..58d2db5b46a4 100644
--- a/rust/kernel/std_vendor.rs
+++ b/rust/kernel/std_vendor.rs
@@ -162,3 +162,145 @@ macro_rules! dbg {
($($crate::dbg!($val)),+,)
};
}
+
+/// Hints to the compiler that a branch condition is likely to be true.
+/// Returns the value passed to it.
+///
+/// It can be used with `if` or boolean `match` expressions.
+///
+/// When used outside of a branch condition, it may still influence a nearby branch, but
+/// probably will not have any effect.
+///
+/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to
+/// compound expressions, such as `likely(a && b)`. When applied to compound expressions, it has
+/// the following effect:
+/// ```text
+/// likely(!a) => !unlikely(a)
+/// likely(a && b) => likely(a) && likely(b)
+/// likely(a || b) => a || likely(b)
+/// ```
+///
+/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code.
+///
+/// # Examples
+///
+/// ```
+/// fn foo(x: i32) {
+/// if likely(x > 0) {
+/// pr_info!("this branch is likely to be taken\n");
+/// } else {
+/// pr_info!("this branch is unlikely to be taken\n");
+/// }
+///
+/// match likely(x > 0) {
+/// true => pr_info!("this branch is likely to be taken\n"),
+/// false => pr_info!("this branch is unlikely to be taken\n"),
+/// }
+///
+/// // Use outside of a branch condition may still influence a nearby branch
+/// let cond = likely(x != 0);
+/// if cond {
+/// pr_info!("this branch is likely to be taken\n");
+/// }
+/// }
+/// ```
+// This implementation is taken from `core::intrinsics::likely()`, not the `hint` wrapper.
+#[inline(always)]
+pub const fn likely(b: bool) -> bool {
+ if b {
+ true
+ } else {
+ cold_path();
+ false
+ }
+}
+
+/// Hints to the compiler that a branch condition is unlikely to be true.
+/// Returns the value passed to it.
+///
+/// It can be used with `if` or boolean `match` expressions.
+///
+/// When used outside of a branch condition, it may still influence a nearby branch, but
+/// probably will not have any effect.
+///
+/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to
+/// compound expressions, such as `unlikely(a && b)`. When applied to compound expressions, it has
+/// the following effect:
+/// ```text
+/// unlikely(!a) => !likely(a)
+/// unlikely(a && b) => a && unlikely(b)
+/// unlikely(a || b) => unlikely(a) || unlikely(b)
+/// ```
+///
+/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code.
+///
+/// # Examples
+///
+/// ```
+/// fn foo(x: i32) {
+/// if unlikely(x > 0) {
+/// pr_info!("this branch is unlikely to be taken\n");
+/// } else {
+/// pr_info!("this branch is likely to be taken\n");
+/// }
+///
+/// match unlikely(x > 0) {
+/// true => pr_info!("this branch is unlikely to be taken\n"),
+/// false => pr_info!("this branch is likely to be taken\n"),
+/// }
+///
+/// // Use outside of a branch condition may still influence a nearby branch
+/// let cond = unlikely(x != 0);
+/// if cond {
+/// pr_info!("this branch is likely to be taken\n");
+/// }
+/// }
+/// ```
+// This implementation is taken from `core::intrinsics::unlikely()`, not the `hint` wrapper.
+#[inline(always)]
+pub const fn unlikely(b: bool) -> bool {
+ if b {
+ cold_path();
+ true
+ } else {
+ false
+ }
+}
+
+/// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may
+/// choose to optimize paths that are not cold at the expense of paths that are cold.
+///
+/// Note that like all hints, the exact effect to codegen is not guaranteed. Using `cold_path`
+/// can actually *decrease* performance if the branch is called more than expected. It is advisable
+/// to perform benchmarks to tell if this function is useful.
+///
+/// # Examples
+///
+/// ```
+/// fn foo(x: &[i32]) {
+/// if let Some(first) = x.first() {
+/// // this is the fast path
+/// } else {
+/// // this path is unlikely
+/// cold_path();
+/// }
+/// }
+///
+/// fn bar(x: i32) -> i32 {
+/// match x {
+/// 1 => 10,
+/// 2 => 100,
+/// 3 => { cold_path(); 1000 }, // this branch is unlikely
+/// _ => { cold_path(); 10000 }, // this is also unlikely
+/// }
+/// }
+/// ```
+///
+/// See also the [`likely()`] and [`unlikely()`] functions, which are similar to the C side macros.
+#[cfg(not(CONFIG_RUSTC_HAS_COLD_PATH))]
+#[inline(always)]
+#[cold]
+pub const fn cold_path() {}
+
+#[cfg(CONFIG_RUSTC_HAS_COLD_PATH)]
+pub use core::hint::cold_path;
--
2.53.0
next prev parent reply other threads:[~2026-04-06 9:59 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-06 9:58 [PATCH v2 1/2] rust: kernel: update `file_with_nul` comment Miguel Ojeda
2026-04-06 9:58 ` Miguel Ojeda [this message]
2026-04-06 14:46 ` [PATCH v2 2/2] rust: std_vendor: add `likely()`, `unlikely()` and `cold_path()` Gary Guo
2026-04-07 8:27 ` Alice Ryhl
2026-04-07 10:39 ` Miguel Ojeda
2026-04-06 14:40 ` [PATCH v2 1/2] rust: kernel: update `file_with_nul` comment Gary Guo
2026-04-06 15:32 ` Boqun Feng
2026-04-07 8:24 ` Alice Ryhl
2026-04-07 10:40 ` Miguel Ojeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260406095820.465994-2-ojeda@kernel.org \
--to=ojeda@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=justinstitt@google.com \
--cc=llvm@lists.linux.dev \
--cc=lossin@kernel.org \
--cc=morbo@google.com \
--cc=nathan@kernel.org \
--cc=nick.desaulniers+lkml@gmail.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.