Skip to main content

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}