branches/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2// `doc = include_str!(...)` needs rustc 1.54, and the nested cfg_attr is
3// load-bearing: pre-1.54 parsers reject this grammar even inside a disabled
4// cfg_attr, but never validate the token tree of an inner list-form attribute.
5#![cfg_attr(
6 rustc_ge_1_54_0,
7 cfg_attr(all(), doc = include_str!("../README.md"))
8)]
9#![cfg_attr(
10 not(rustc_ge_1_54_0),
11 doc = "Branch prediction hints (`likely`, `unlikely`, `mark_unlikely`), control-flow \
12 assumptions (`assume`), `abort`, and CPU cache prefetch helpers for stable Rust. \
13 See the project README for the full documentation."
14)]
15#![warn(missing_docs, missing_debug_implementations)]
16#![cfg_attr(branches_nightly, feature(core_intrinsics))]
17#![cfg_attr(branches_nightly, allow(internal_features))]
18// Provides branch detection functions for Rust, using built-in Rust features
19// on stable and core::intrinsics on nightly.
20
21// No one likes to visit this function.
22//
23// The hint only works while LLVM sees a call to a `#[cold]` function inside
24// the branch, so inlining the empty body kills it -- and since ~1.76 rustc's
25// cross-crate MIR inlining does that on its own unless `#[inline(never)]`
26// stops it. Under `branches_cold_weights` (rustc 1.84+ at -O2/-O3, see
27// build.rs) the optimizer converts the call into `!prof` branch weights as it
28// inlines, so the call may vanish from callers; at other opt levels no
29// weights are recorded and the call has to stay out of line.
30#[cfg(all(branches_stable, not(rustc_ge_1_95_0)))]
31#[cfg_attr(not(branches_cold_weights), inline(never))]
32#[cold]
33const fn cold_and_empty() {}
34
35#[cfg(all(branches_stable, rustc_ge_1_95_0))]
36use core::hint::cold_path as cold_and_empty;
37
38/// Aborts the execution of the process immediately and without any cleanup.
39///
40/// This function is used to indicate a critical and unrecoverable error in the program.
41/// It terminates the process immediately without performing any cleanup or running destructors.
42///
43/// This function is safe to call, so it does not require an unsafe block.
44/// Therefore, implementations must not require the user to uphold any safety invariants.
45///
46/// If the std feature is enabled, this function calls `std::process::abort()`
47/// on every channel, which raises `SIGABRT` on Unix and honors registered
48/// abort handlers.
49///
50/// If the std feature is disabled, this function executes a trap instruction
51/// on nightly, and panics by calling `panic!()` on stable. In the panicking
52/// case, `extern "C"` guarantees this function does not unwind, but actual
53/// termination depends on the registered panic handler: a handler that loops
54/// forever (common in embedded code) hangs instead of aborting.
55#[cold]
56pub extern "C" fn abort() -> ! {
57 #[cfg(feature = "std")]
58 std::process::abort();
59 #[cfg(all(not(feature = "std"), branches_nightly))]
60 core::intrinsics::abort();
61 #[cfg(all(not(feature = "std"), branches_stable))]
62 panic!("branches::abort() called");
63}
64
65/// Informs the optimizer that a condition is always true.
66///
67/// If the condition is actually false, the behavior is undefined.
68///
69/// This intrinsic doesn't generate any code. Instead, it tells the optimizer
70/// to preserve the condition for optimization passes. This can interfere with
71/// optimization of surrounding code and reduce performance, so avoid using it
72/// if the optimizer can already discover the invariant on its own or if it
73/// doesn't enable any significant optimizations.
74///
75/// This function is a `const fn`, so a const fn can carry invariants like
76/// `assume(len <= CAP)`. Rust older than 1.57 has no const-legal way to
77/// state an assumption (`core::hint::unreachable_unchecked` became callable
78/// in const fn in 1.57), so on rustc 1.51-1.56 this compiles to a no-op and
79/// the hint is dropped. From rustc 1.59 the hint is exactly as effective as
80/// writing the check by hand; 1.57-1.58 may keep a cheap residual compare in
81/// some loop shapes.
82///
83/// # Safety
84///
85/// This intrinsic is marked unsafe because it can result in undefined behavior
86/// if the condition passed to it is false.
87#[inline(always)]
88pub const unsafe fn assume(b: bool) {
89 let _ = b;
90 // Rust >= 1.81.0: use the newer `assert_unchecked` hint. Clippy cannot
91 // see that the cfg guarantees the API exists.
92 #[cfg(all(branches_stable, rustc_ge_1_81_0))]
93 #[allow(clippy::incompatible_msrv)]
94 {
95 core::hint::assert_unchecked(b)
96 }
97 // Rust 1.57-1.80: `unreachable_unchecked`, const-callable since 1.57.
98 #[cfg(all(branches_stable, rustc_ge_1_57_0, not(rustc_ge_1_81_0)))]
99 {
100 if !b {
101 core::hint::unreachable_unchecked()
102 }
103 }
104 #[cfg(all(branches_nightly, rustc_ge_1_57_0))]
105 core::intrinsics::assume(b)
106 // Pre-1.57 compilers, stable or nightly, fall through to the no-op.
107}
108
109/// Hints to the compiler that the branch condition is likely to be true.
110/// Returns the value passed to it.
111///
112/// This intrinsic is primarily used with `if` statements.
113/// Using it in other contexts may not have any effect.
114///
115/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
116/// Therefore, implementations must not require the user to uphold any safety invariants.
117#[must_use = "the hint only takes effect when the returned value is used as a branch condition"]
118#[inline(always)]
119pub fn likely(b: bool) -> bool {
120 // On 1.95+ `cold_and_empty` is `cold_path`; clippy can't see the cfg.
121 #[cfg(branches_stable)]
122 #[allow(clippy::incompatible_msrv)]
123 {
124 if !b {
125 cold_and_empty();
126 }
127 b
128 }
129 #[cfg(branches_nightly)]
130 core::intrinsics::likely(b)
131}
132
133/// Marks a code block as cold, indicating to the compiler that it is unlikely to be called.
134/// This can help the compiler optimize for the common case.
135///
136/// This function does not take any arguments and does not return any value.
137/// It is primarily used to mark functions or code paths that are rarely executed,
138/// such as error handling or panic paths.
139///
140/// Example: marking the error variant of a match as unlikely.
141///
142/// In many hot paths a value is expected to be the success variant.
143/// By marking the error arm using `mark_unlikely` we give the optimizer a hint
144/// that this branch is rarely taken.
145///
146/// ```rust
147/// use branches::{mark_unlikely};
148///
149/// #[derive(Debug)]
150/// enum Status {
151/// Ok(i32),
152/// Err(String),
153/// }
154///
155/// fn get_value(status: Status) -> i32 {
156/// match status {
157/// Status::Ok(v) => v,
158/// // The error case is rare, hint the compiler accordingly.
159/// Status::Err(err) => {
160/// mark_unlikely();
161/// eprintln!("unexpected error: {:?}", err);
162/// -1
163/// }
164/// }
165/// }
166/// ```
167// Same rules as `cold_and_empty` above.
168#[cfg(not(rustc_ge_1_95_0))]
169#[cold]
170#[cfg_attr(not(branches_cold_weights), inline(never))]
171pub const fn mark_unlikely() {}
172/// Marks a code block as cold, indicating to the compiler that it is unlikely to be called.
173/// This can help the compiler optimize for the common case.
174///
175/// This function does not take any arguments and does not return any value.
176/// It is primarily used to mark functions or code paths that are rarely executed,
177/// such as error handling or panic paths.
178///
179/// Example: marking the error variant of a match as unlikely.
180///
181/// In many hot paths a value is expected to be the success variant.
182/// By marking the error arm using `mark_unlikely` we give the optimizer a hint
183/// that this branch is rarely taken.
184///
185/// ```rust
186/// use branches::{mark_unlikely};
187///
188/// #[derive(Debug)]
189/// enum Status {
190/// Ok(i32),
191/// Err(String),
192/// }
193///
194/// fn get_value(status: Status) -> i32 {
195/// match status {
196/// Status::Ok(v) => v,
197/// // The error case is rare, hint the compiler accordingly.
198/// Status::Err(err) => {
199/// mark_unlikely();
200/// eprintln!("unexpected error: {:?}", err);
201/// -1
202/// }
203/// }
204/// }
205/// ```
206#[cfg(rustc_ge_1_95_0)]
207pub use core::hint::cold_path as mark_unlikely;
208
209/// Hints to the compiler that the branch condition is unlikely to be true.
210/// Returns the value passed to it.
211///
212/// This intrinsic is primarily used with `if` statements.
213/// Using it in other contexts may not have any effect.
214///
215/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
216/// Therefore, implementations must not require the user to uphold any safety invariants.
217#[must_use = "the hint only takes effect when the returned value is used as a branch condition"]
218#[inline(always)]
219pub fn unlikely(b: bool) -> bool {
220 // On 1.95+ `cold_and_empty` is `cold_path`; clippy can't see the cfg.
221 #[cfg(branches_stable)]
222 #[allow(clippy::incompatible_msrv)]
223 {
224 if b {
225 cold_and_empty();
226 }
227 b
228 }
229 #[cfg(branches_nightly)]
230 core::intrinsics::unlikely(b)
231}
232
233/// Prefetches data for reading into the cache.
234///
235/// This function hints to the CPU that the data at the given address
236/// will be read soon, allowing the CPU to load the data into the cache
237/// in advance. This can improve performance by reducing cache misses.
238///
239/// Prefetching is only a hint and never affects the observable behavior of
240/// the program: it is safe to call with any pointer, including dangling or
241/// out-of-bounds pointers.
242///
243/// # Arguments
244///
245/// * `addr` - A pointer to the data to prefetch.
246/// * `LOCALITY` - The cache level to prefetch into: `0` = L1, `1` = L2,
247/// `2` = L3, any other value = non-temporal. The convention is identical
248/// on stable and nightly toolchains.
249///
250/// # Supported architectures
251///
252/// On stable, the hint is emitted on rustc 1.59 or newer (the release that
253/// stabilized inline assembly) for `x86`/`x86_64` (with the `sse` target
254/// feature, enabled by default on `x86_64` and `i686` targets), `aarch64`,
255/// and `riscv64` when compiled with the `zicbop` target feature
256/// (`-C target-feature=+zicbop`); on rustc 1.84 or newer for `s390x`; and
257/// on rustc 1.95 or newer for `powerpc`/`powerpc64` (the releases that
258/// stabilized inline assembly for those architectures). `s390x`,
259/// `powerpc` and `powerpc64` have a single prefetch instruction with no
260/// cache-level selection, so `LOCALITY` is ignored there. On other targets,
261/// and on stable compilers older than the listed versions, this compiles to
262/// a no-op. On nightly, the hint is lowered by LLVM for every architecture
263/// that supports one.
264#[inline(always)]
265#[cfg(feature = "prefetch")]
266pub fn prefetch_read_data<T, const LOCALITY: i32>(addr: *const T) {
267 let _ = addr;
268 #[cfg(branches_stable)]
269 {
270 // Inline assembly was stabilized in Rust 1.59, so older stable
271 // compilers stay a no-op instead of failing to build.
272 #[cfg(all(
273 rustc_ge_1_59_0,
274 any(target_arch = "x86", target_arch = "x86_64"),
275 target_feature = "sse"
276 ))]
277 unsafe {
278 match LOCALITY {
279 0 => core::arch::asm!(
280 "prefetcht0 [{}]",
281 in(reg) addr,
282 options(nostack, readonly, preserves_flags)
283 ), // L1 cache
284 1 => core::arch::asm!(
285 "prefetcht1 [{}]",
286 in(reg) addr,
287 options(nostack, readonly, preserves_flags)
288 ), // L2 cache
289 2 => core::arch::asm!(
290 "prefetcht2 [{}]",
291 in(reg) addr,
292 options(nostack, readonly, preserves_flags)
293 ), // L3 cache
294 _ => core::arch::asm!(
295 "prefetchnta [{}]",
296 in(reg) addr,
297 options(nostack, readonly, preserves_flags)
298 ), // Non-temporal
299 }
300 }
301
302 // `prfm` only exists on AArch64; 32-bit ARM would need `pld`, which
303 // not every 32-bit ARM target supports, so arm stays a no-op.
304 #[cfg(all(rustc_ge_1_59_0, target_arch = "aarch64"))]
305 unsafe {
306 match LOCALITY {
307 0 => core::arch::asm!(
308 "prfm pldl1keep, [{}]",
309 in(reg) addr,
310 options(nostack, readonly, preserves_flags)
311 ), // L1 cache
312 1 => core::arch::asm!(
313 "prfm pldl2keep, [{}]",
314 in(reg) addr,
315 options(nostack, readonly, preserves_flags)
316 ), // L2 cache
317 2 => core::arch::asm!(
318 "prfm pldl3keep, [{}]",
319 in(reg) addr,
320 options(nostack, readonly, preserves_flags)
321 ), // L3 cache
322 _ => core::arch::asm!(
323 "prfm pldl1strm, [{}]",
324 in(reg) addr,
325 options(nostack, readonly, preserves_flags)
326 ), // Non-temporal (streaming)
327 }
328 }
329
330 // The Zicbop extension is not part of the baseline riscv64gc target,
331 // so the instruction is only emitted when the feature is enabled.
332 #[cfg(all(rustc_ge_1_59_0, target_arch = "riscv64", target_feature = "zicbop"))]
333 unsafe {
334 core::arch::asm!(
335 "prefetch.r 0({})",
336 in(reg) addr,
337 options(nostack, readonly, preserves_flags)
338 );
339 }
340
341 // s390x inline assembly was stabilized in Rust 1.84, so older
342 // compilers stay a no-op instead of failing to build. `pfd` has no
343 // locality levels (LLVM ignores locality on SystemZ too), and the
344 // address must go in an address register: `r0` in a base register
345 // slot reads as the literal zero, not as the register.
346 #[cfg(all(rustc_ge_1_84_0, target_arch = "s390x"))]
347 unsafe {
348 core::arch::asm!(
349 "pfd 1, 0({})",
350 in(reg_addr) addr,
351 options(nostack, readonly, preserves_flags)
352 ); // Prefetch for load
353 }
354
355 // PowerPC inline assembly was stabilized in Rust 1.95, so older
356 // compilers stay a no-op instead of failing to build. `dcbt` carries
357 // no locality levels either, matching LLVM's lowering. The register
358 // holds the `RB` operand, where `r0` keeps its normal meaning.
359 #[cfg(all(
360 rustc_ge_1_95_0,
361 any(target_arch = "powerpc", target_arch = "powerpc64")
362 ))]
363 unsafe {
364 core::arch::asm!(
365 "dcbt 0, {}",
366 in(reg) addr,
367 options(nostack, readonly, preserves_flags)
368 );
369 }
370 }
371 #[cfg(branches_nightly)]
372 {
373 // `core::intrinsics` uses the opposite locality convention
374 // (0 = no locality .. 3 = maximally local), so translate to keep
375 // stable and nightly behavior identical. The catch-all arm also
376 // keeps out-of-range values from reaching LLVM, which only accepts
377 // 0..=3 and crashes otherwise.
378 match LOCALITY {
379 0 => core::intrinsics::prefetch_read_data::<_, 3>(addr),
380 1 => core::intrinsics::prefetch_read_data::<_, 2>(addr),
381 2 => core::intrinsics::prefetch_read_data::<_, 1>(addr),
382 _ => core::intrinsics::prefetch_read_data::<_, 0>(addr),
383 }
384 }
385}
386
387/// Prefetches data for writing into the cache.
388///
389/// This function hints to the CPU that the data at the given address
390/// will be written soon, allowing the CPU to load the data into the cache
391/// in advance. This can improve performance by reducing cache misses.
392///
393/// Prefetching is only a hint and never affects the observable behavior of
394/// the program: it is safe to call with any pointer, including dangling or
395/// out-of-bounds pointers.
396///
397/// # Arguments
398///
399/// * `addr` - A pointer to the data to prefetch.
400/// * `LOCALITY` - The cache level to prefetch into: `0` = L1, `1` = L2,
401/// `2` = L3, any other value = non-temporal. The convention is identical
402/// on stable and nightly toolchains. On `x86_64` there is a single
403/// write-prefetch instruction, so `LOCALITY` is ignored there.
404///
405/// # Supported architectures
406///
407/// On stable, the hint is emitted on rustc 1.59 or newer (the release that
408/// stabilized inline assembly) for `x86`/`x86_64` (with the `sse` target
409/// feature, enabled by default on `x86_64` and `i686` targets), `aarch64`,
410/// and `riscv64` when compiled with the `zicbop` target feature
411/// (`-C target-feature=+zicbop`); on rustc 1.84 or newer for `s390x`; and
412/// on rustc 1.95 or newer for `powerpc`/`powerpc64` (the releases that
413/// stabilized inline assembly for those architectures). `s390x`,
414/// `powerpc` and `powerpc64` have a single write-prefetch instruction with
415/// no cache-level selection, so `LOCALITY` is ignored there. On other
416/// targets, and on stable compilers older than the listed versions, this
417/// compiles to a no-op. On nightly, the hint is lowered by LLVM for every
418/// architecture that supports one.
419#[inline(always)]
420#[cfg(feature = "prefetch")]
421pub fn prefetch_write_data<T, const LOCALITY: i32>(addr: *const T) {
422 let _ = addr;
423 #[cfg(branches_stable)]
424 {
425 // Inline assembly was stabilized in Rust 1.59, so older stable
426 // compilers stay a no-op instead of failing to build.
427 #[cfg(all(rustc_ge_1_59_0, target_arch = "x86_64"))]
428 unsafe {
429 core::arch::asm!(
430 "prefetchw [{}]",
431 in(reg) addr,
432 options(nostack, readonly, preserves_flags)
433 ) // Write-prefetch for L1/L2/L3 cache
434 }
435
436 // 32-bit x86: `prefetchw` faults on CPUs without the PRFCHW/3DNow!
437 // extension, so fall back to a plain read prefetch into L1, the same
438 // strategy GCC and Clang use for `__builtin_prefetch(p, 1)` there.
439 #[cfg(all(rustc_ge_1_59_0, target_arch = "x86", target_feature = "sse"))]
440 unsafe {
441 core::arch::asm!(
442 "prefetcht0 [{}]",
443 in(reg) addr,
444 options(nostack, readonly, preserves_flags)
445 )
446 }
447
448 // `prfm` only exists on AArch64; 32-bit ARM would need `pldw`, which
449 // requires the MP extension, so arm stays a no-op.
450 #[cfg(all(rustc_ge_1_59_0, target_arch = "aarch64"))]
451 unsafe {
452 match LOCALITY {
453 0 => core::arch::asm!(
454 "prfm pstl1keep, [{}]",
455 in(reg) addr,
456 options(nostack, readonly, preserves_flags)
457 ), // L1 cache
458 1 => core::arch::asm!(
459 "prfm pstl2keep, [{}]",
460 in(reg) addr,
461 options(nostack, readonly, preserves_flags)
462 ), // L2 cache
463 2 => core::arch::asm!(
464 "prfm pstl3keep, [{}]",
465 in(reg) addr,
466 options(nostack, readonly, preserves_flags)
467 ), // L3 cache
468 _ => core::arch::asm!(
469 "prfm pstl1strm, [{}]",
470 in(reg) addr,
471 options(nostack, readonly, preserves_flags)
472 ), // Non-temporal (streaming)
473 }
474 }
475
476 // The Zicbop extension is not part of the baseline riscv64gc target,
477 // so the instruction is only emitted when the feature is enabled.
478 #[cfg(all(rustc_ge_1_59_0, target_arch = "riscv64", target_feature = "zicbop"))]
479 unsafe {
480 core::arch::asm!(
481 "prefetch.w 0({})",
482 in(reg) addr,
483 options(nostack, readonly, preserves_flags)
484 );
485 }
486
487 // s390x inline assembly was stabilized in Rust 1.84, so older
488 // compilers stay a no-op instead of failing to build. `pfd` has no
489 // locality levels (LLVM ignores locality on SystemZ too), and the
490 // address must go in an address register: `r0` in a base register
491 // slot reads as the literal zero, not as the register.
492 #[cfg(all(rustc_ge_1_84_0, target_arch = "s390x"))]
493 unsafe {
494 core::arch::asm!(
495 "pfd 2, 0({})",
496 in(reg_addr) addr,
497 options(nostack, readonly, preserves_flags)
498 ); // Prefetch for store
499 }
500
501 // PowerPC inline assembly was stabilized in Rust 1.95, so older
502 // compilers stay a no-op instead of failing to build. `dcbtst` carries
503 // no locality levels either, matching LLVM's lowering. The register
504 // holds the `RB` operand, where `r0` keeps its normal meaning.
505 #[cfg(all(
506 rustc_ge_1_95_0,
507 any(target_arch = "powerpc", target_arch = "powerpc64")
508 ))]
509 unsafe {
510 core::arch::asm!(
511 "dcbtst 0, {}",
512 in(reg) addr,
513 options(nostack, readonly, preserves_flags)
514 ); // Write-prefetch
515 }
516 }
517 #[cfg(branches_nightly)]
518 {
519 // `core::intrinsics` uses the opposite locality convention
520 // (0 = no locality .. 3 = maximally local), so translate to keep
521 // stable and nightly behavior identical. The catch-all arm also
522 // keeps out-of-range values from reaching LLVM, which only accepts
523 // 0..=3 and crashes otherwise.
524 match LOCALITY {
525 0 => core::intrinsics::prefetch_write_data::<_, 3>(addr),
526 1 => core::intrinsics::prefetch_write_data::<_, 2>(addr),
527 2 => core::intrinsics::prefetch_write_data::<_, 1>(addr),
528 _ => core::intrinsics::prefetch_write_data::<_, 0>(addr),
529 }
530 }
531}
532
533// Non-generic instantiations of every architecture-specific code path.
534// Not part of the public API: only compiled when CI passes
535// `RUSTFLAGS="--cfg branches_check_asm"`, so that plain library cross-builds
536// (which never monomorphize the generic prefetch functions) still assemble
537// the inline assembly for the target architecture.
538#[cfg(branches_check_asm)]
539#[doc(hidden)]
540pub fn __branches_check_asm(addr: *const u8, cond: bool) -> bool {
541 let _ = addr;
542 #[cfg(feature = "prefetch")]
543 {
544 prefetch_read_data::<_, 0>(addr);
545 prefetch_read_data::<_, 1>(addr);
546 prefetch_read_data::<_, 2>(addr);
547 prefetch_read_data::<_, 3>(addr);
548 prefetch_read_data::<_, { -1 }>(addr);
549 prefetch_write_data::<_, 0>(addr);
550 prefetch_write_data::<_, 1>(addr);
551 prefetch_write_data::<_, 2>(addr);
552 prefetch_write_data::<_, 3>(addr);
553 prefetch_write_data::<_, { -1 }>(addr);
554 }
555 if unlikely(!cond) {
556 mark_unlikely();
557 }
558 likely(cond)
559}