Skip to main content

branches/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![doc = include_str!("../README.md")]
3#![warn(missing_docs, missing_debug_implementations)]
4#![cfg_attr(branches_nightly, feature(core_intrinsics))]
5#![cfg_attr(branches_nightly, allow(internal_features))]
6// Provides branch detection functions for Rust, using built-in Rust features
7// on stable and core::intrinsics on nightly.
8
9// No one likes to visit this function.
10//
11// It must stay an out-of-line call: the whole trick relies on LLVM seeing a
12// call to a `#[cold]` function inside the branch. Any form of inlining
13// (`#[inline]` or `#[inline(always)]`) removes the call during optimization
14// and with it the hint, turning `likely`/`unlikely` into no-ops.
15#[cfg(all(branches_stable, not(rustc_ge_1_95_0)))]
16#[inline(never)]
17#[cold]
18const fn cold_and_empty() {}
19
20#[cfg(all(branches_stable, rustc_ge_1_95_0))]
21use core::hint::cold_path as cold_and_empty;
22
23/// Aborts the execution of the process immediately and without any cleanup.
24///
25/// This function is used to indicate a critical and unrecoverable error in the program.
26/// It terminates the process immediately without performing any cleanup or running destructors.
27///
28/// This function is safe to call, so it does not require an unsafe block.
29/// Therefore, implementations must not require the user to uphold any safety invariants.
30///
31/// If the std feature is enabled, this function calls `std::process::abort()`
32/// on every channel, which raises `SIGABRT` on Unix and honors registered
33/// abort handlers.
34///
35/// If the std feature is disabled, this function executes a trap instruction
36/// on nightly, and panics by calling `panic!()` on stable. In the panicking
37/// case, `extern "C"` guarantees this function does not unwind, but actual
38/// termination depends on the registered panic handler: a handler that loops
39/// forever (common in embedded code) hangs instead of aborting.
40#[cold]
41pub extern "C" fn abort() -> ! {
42    #[cfg(feature = "std")]
43    std::process::abort();
44    #[cfg(all(not(feature = "std"), branches_nightly))]
45    core::intrinsics::abort();
46    #[cfg(all(not(feature = "std"), branches_stable))]
47    panic!("branches::abort() called");
48}
49
50/// Informs the optimizer that a condition is always true.
51///
52/// If the condition is actually false, the behavior is undefined.
53///
54/// This intrinsic doesn't generate any code. Instead, it tells the optimizer
55/// to preserve the condition for optimization passes. This can interfere with
56/// optimization of surrounding code and reduce performance, so avoid using it
57/// if the optimizer can already discover the invariant on its own or if it
58/// doesn't enable any significant optimizations.
59///
60/// # Safety
61///
62/// This intrinsic is marked unsafe because it can result in undefined behavior
63/// if the condition passed to it is false.
64#[inline(always)]
65pub unsafe fn assume(b: bool) {
66    #[cfg(branches_stable)]
67    {
68        // Rust >= 1.81.0: use the newer `assert_unchecked` hint.
69        #[cfg(rustc_ge_1_81_0)]
70        {
71            core::hint::assert_unchecked(b)
72        }
73        // Rust < 1.81.0: fall back to the older `unreachable_unchecked`.
74        #[cfg(not(rustc_ge_1_81_0))]
75        {
76            if !b {
77                core::hint::unreachable_unchecked()
78            }
79        }
80    }
81    #[cfg(branches_nightly)]
82    core::intrinsics::assume(b)
83}
84
85/// Hints to the compiler that the branch condition is likely to be true.
86/// Returns the value passed to it.
87///
88/// This intrinsic is primarily used with `if` statements.
89/// Using it in other contexts may not have any effect.
90///
91/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
92/// Therefore, implementations must not require the user to uphold any safety invariants.
93#[must_use = "the hint only takes effect when the returned value is used as a branch condition"]
94#[inline(always)]
95pub fn likely(b: bool) -> bool {
96    #[cfg(branches_stable)]
97    {
98        if !b {
99            cold_and_empty();
100        }
101        b
102    }
103    #[cfg(branches_nightly)]
104    core::intrinsics::likely(b)
105}
106
107/// Marks a code block as cold, indicating to the compiler that it is unlikely to be called.
108/// This can help the compiler optimize for the common case.
109///
110/// This function does not take any arguments and does not return any value.
111/// It is primarily used to mark functions or code paths that are rarely executed,
112/// such as error handling or panic paths.
113///
114/// Example: marking the error variant of a match as unlikely.
115///
116/// In many hot paths a value is expected to be the success variant.
117/// By marking the error arm using `mark_unlikely` we give the optimizer a hint
118/// that this branch is rarely taken.
119///
120/// ```rust
121/// use branches::{mark_unlikely};
122///
123/// #[derive(Debug)]
124/// enum Status {
125///     Ok(i32),
126///     Err(String),
127/// }
128///
129/// fn get_value(status: Status) -> i32 {
130///     match status {
131///         Status::Ok(v) => v,
132///         // The error case is rare, hint the compiler accordingly.
133///         Status::Err(err) => {
134///             mark_unlikely();
135///             eprintln!("unexpected error: {:?}", err);
136///             -1
137///         }
138///     }
139/// }
140/// ```
141#[cfg(not(rustc_ge_1_95_0))]
142#[cold]
143#[inline(never)]
144pub const fn mark_unlikely() {}
145/// Marks a code block as cold, indicating to the compiler that it is unlikely to be called.
146/// This can help the compiler optimize for the common case.
147///
148/// This function does not take any arguments and does not return any value.
149/// It is primarily used to mark functions or code paths that are rarely executed,
150/// such as error handling or panic paths.
151///
152/// Example: marking the error variant of a match as unlikely.
153///
154/// In many hot paths a value is expected to be the success variant.
155/// By marking the error arm using `mark_unlikely` we give the optimizer a hint
156/// that this branch is rarely taken.
157///
158/// ```rust
159/// use branches::{mark_unlikely};
160///
161/// #[derive(Debug)]
162/// enum Status {
163///     Ok(i32),
164///     Err(String),
165/// }
166///
167/// fn get_value(status: Status) -> i32 {
168///     match status {
169///         Status::Ok(v) => v,
170///         // The error case is rare, hint the compiler accordingly.
171///         Status::Err(err) => {
172///             mark_unlikely();
173///             eprintln!("unexpected error: {:?}", err);
174///             -1
175///         }
176///     }
177/// }
178/// ```
179#[cfg(rustc_ge_1_95_0)]
180pub use core::hint::cold_path as mark_unlikely;
181
182/// Hints to the compiler that the branch condition is unlikely to be true.
183/// Returns the value passed to it.
184///
185/// This intrinsic is primarily used with `if` statements.
186/// Using it in other contexts may not have any effect.
187///
188/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
189/// Therefore, implementations must not require the user to uphold any safety invariants.
190#[must_use = "the hint only takes effect when the returned value is used as a branch condition"]
191#[inline(always)]
192pub fn unlikely(b: bool) -> bool {
193    #[cfg(branches_stable)]
194    {
195        if b {
196            cold_and_empty();
197        }
198        b
199    }
200    #[cfg(branches_nightly)]
201    core::intrinsics::unlikely(b)
202}
203
204/// Prefetches data for reading into the cache.
205///
206/// This function hints to the CPU that the data at the given address
207/// will be read soon, allowing the CPU to load the data into the cache
208/// in advance. This can improve performance by reducing cache misses.
209///
210/// Prefetching is only a hint and never affects the observable behavior of
211/// the program: it is safe to call with any pointer, including dangling or
212/// out-of-bounds pointers.
213///
214/// # Arguments
215///
216/// * `addr` - A pointer to the data to prefetch.
217/// * `LOCALITY` - The cache level to prefetch into: `0` = L1, `1` = L2,
218///   `2` = L3, any other value = non-temporal. The convention is identical
219///   on stable and nightly toolchains.
220///
221/// # Supported architectures
222///
223/// On stable, the hint is emitted on `x86`/`x86_64` (with the `sse` target
224/// feature, enabled by default on `x86_64` and `i686` targets), `aarch64`,
225/// and `riscv64` when compiled with the `zicbop` target feature
226/// (`-C target-feature=+zicbop`). On other targets this compiles to a
227/// no-op. On nightly, the hint is lowered by LLVM for every architecture
228/// that supports one.
229#[inline(always)]
230#[cfg(feature = "prefetch")]
231pub fn prefetch_read_data<T, const LOCALITY: i32>(addr: *const T) {
232    let _ = addr;
233    #[cfg(branches_stable)]
234    {
235        #[cfg(all(
236            any(target_arch = "x86", target_arch = "x86_64"),
237            target_feature = "sse"
238        ))]
239        unsafe {
240            match LOCALITY {
241                0 => core::arch::asm!(
242                    "prefetcht0 [{}]",
243                    in(reg) addr,
244                    options(nostack, readonly, preserves_flags)
245                ), // L1 cache
246                1 => core::arch::asm!(
247                    "prefetcht1 [{}]",
248                    in(reg) addr,
249                    options(nostack, readonly, preserves_flags)
250                ), // L2 cache
251                2 => core::arch::asm!(
252                    "prefetcht2 [{}]",
253                    in(reg) addr,
254                    options(nostack, readonly, preserves_flags)
255                ), // L3 cache
256                _ => core::arch::asm!(
257                    "prefetchnta [{}]",
258                    in(reg) addr,
259                    options(nostack, readonly, preserves_flags)
260                ), // Non-temporal
261            }
262        }
263
264        // `prfm` only exists on AArch64; 32-bit ARM would need `pld`, which
265        // not every 32-bit ARM target supports, so arm stays a no-op.
266        #[cfg(target_arch = "aarch64")]
267        unsafe {
268            match LOCALITY {
269                0 => core::arch::asm!(
270                    "prfm pldl1keep, [{}]",
271                    in(reg) addr,
272                    options(nostack, readonly, preserves_flags)
273                ), // L1 cache
274                1 => core::arch::asm!(
275                    "prfm pldl2keep, [{}]",
276                    in(reg) addr,
277                    options(nostack, readonly, preserves_flags)
278                ), // L2 cache
279                2 => core::arch::asm!(
280                    "prfm pldl3keep, [{}]",
281                    in(reg) addr,
282                    options(nostack, readonly, preserves_flags)
283                ), // L3 cache
284                _ => core::arch::asm!(
285                    "prfm pldl1strm, [{}]",
286                    in(reg) addr,
287                    options(nostack, readonly, preserves_flags)
288                ), // Non-temporal (streaming)
289            }
290        }
291
292        // The Zicbop extension is not part of the baseline riscv64gc target,
293        // so the instruction is only emitted when the feature is enabled.
294        #[cfg(all(target_arch = "riscv64", target_feature = "zicbop"))]
295        unsafe {
296            core::arch::asm!(
297                "prefetch.r 0({})",
298                in(reg) addr,
299                options(nostack, readonly, preserves_flags)
300            );
301        }
302
303        // this requires unstable asm feature, uncomment when stabilized
304        //#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
305        //unsafe {
306        //    core::arch::asm!(
307        //        "dcbt 0, {}",
308        //        in(reg) addr,
309        //        options(nostack, readonly, preserves_flags)
310        //    );
311        //}
312    }
313    #[cfg(branches_nightly)]
314    {
315        // `core::intrinsics` uses the opposite locality convention
316        // (0 = no locality .. 3 = maximally local), so translate to keep
317        // stable and nightly behavior identical. The catch-all arm also
318        // keeps out-of-range values from reaching LLVM, which only accepts
319        // 0..=3 and crashes otherwise.
320        match LOCALITY {
321            0 => core::intrinsics::prefetch_read_data::<_, 3>(addr),
322            1 => core::intrinsics::prefetch_read_data::<_, 2>(addr),
323            2 => core::intrinsics::prefetch_read_data::<_, 1>(addr),
324            _ => core::intrinsics::prefetch_read_data::<_, 0>(addr),
325        }
326    }
327}
328
329/// Prefetches data for writing into the cache.
330///
331/// This function hints to the CPU that the data at the given address
332/// will be written soon, allowing the CPU to load the data into the cache
333/// in advance. This can improve performance by reducing cache misses.
334///
335/// Prefetching is only a hint and never affects the observable behavior of
336/// the program: it is safe to call with any pointer, including dangling or
337/// out-of-bounds pointers.
338///
339/// # Arguments
340///
341/// * `addr` - A pointer to the data to prefetch.
342/// * `LOCALITY` - The cache level to prefetch into: `0` = L1, `1` = L2,
343///   `2` = L3, any other value = non-temporal. The convention is identical
344///   on stable and nightly toolchains. On `x86_64` there is a single
345///   write-prefetch instruction, so `LOCALITY` is ignored there.
346///
347/// # Supported architectures
348///
349/// On stable, the hint is emitted on `x86`/`x86_64` (with the `sse` target
350/// feature, enabled by default on `x86_64` and `i686` targets), `aarch64`,
351/// and `riscv64` when compiled with the `zicbop` target feature
352/// (`-C target-feature=+zicbop`). On other targets this compiles to a
353/// no-op. On nightly, the hint is lowered by LLVM for every architecture
354/// that supports one.
355#[inline(always)]
356#[cfg(feature = "prefetch")]
357pub fn prefetch_write_data<T, const LOCALITY: i32>(addr: *const T) {
358    let _ = addr;
359    #[cfg(branches_stable)]
360    {
361        #[cfg(target_arch = "x86_64")]
362        unsafe {
363            core::arch::asm!(
364                "prefetchw [{}]",
365                in(reg) addr,
366                options(nostack, readonly, preserves_flags)
367            ) // Write-prefetch for L1/L2/L3 cache
368        }
369
370        // 32-bit x86: `prefetchw` faults on CPUs without the PRFCHW/3DNow!
371        // extension, so fall back to a plain read prefetch into L1, the same
372        // strategy GCC and Clang use for `__builtin_prefetch(p, 1)` there.
373        #[cfg(all(target_arch = "x86", target_feature = "sse"))]
374        unsafe {
375            core::arch::asm!(
376                "prefetcht0 [{}]",
377                in(reg) addr,
378                options(nostack, readonly, preserves_flags)
379            )
380        }
381
382        // `prfm` only exists on AArch64; 32-bit ARM would need `pldw`, which
383        // requires the MP extension, so arm stays a no-op.
384        #[cfg(target_arch = "aarch64")]
385        unsafe {
386            match LOCALITY {
387                0 => core::arch::asm!(
388                    "prfm pstl1keep, [{}]",
389                    in(reg) addr,
390                    options(nostack, readonly, preserves_flags)
391                ), // L1 cache
392                1 => core::arch::asm!(
393                    "prfm pstl2keep, [{}]",
394                    in(reg) addr,
395                    options(nostack, readonly, preserves_flags)
396                ), // L2 cache
397                2 => core::arch::asm!(
398                    "prfm pstl3keep, [{}]",
399                    in(reg) addr,
400                    options(nostack, readonly, preserves_flags)
401                ), // L3 cache
402                _ => core::arch::asm!(
403                    "prfm pstl1strm, [{}]",
404                    in(reg) addr,
405                    options(nostack, readonly, preserves_flags)
406                ), // Non-temporal (streaming)
407            }
408        }
409
410        // The Zicbop extension is not part of the baseline riscv64gc target,
411        // so the instruction is only emitted when the feature is enabled.
412        #[cfg(all(target_arch = "riscv64", target_feature = "zicbop"))]
413        unsafe {
414            core::arch::asm!(
415                "prefetch.w 0({})",
416                in(reg) addr,
417                options(nostack, readonly, preserves_flags)
418            );
419        }
420
421        // this requires unstable asm feature, uncomment when stabilized
422        //#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
423        //unsafe {
424        //    core::arch::asm!(
425        //        "dcbtst 0, {}",
426        //        in(reg) addr,
427        //        options(nostack, readonly, preserves_flags)
428        //    ); // Write-prefetch
429        // }
430    }
431    #[cfg(branches_nightly)]
432    {
433        // `core::intrinsics` uses the opposite locality convention
434        // (0 = no locality .. 3 = maximally local), so translate to keep
435        // stable and nightly behavior identical. The catch-all arm also
436        // keeps out-of-range values from reaching LLVM, which only accepts
437        // 0..=3 and crashes otherwise.
438        match LOCALITY {
439            0 => core::intrinsics::prefetch_write_data::<_, 3>(addr),
440            1 => core::intrinsics::prefetch_write_data::<_, 2>(addr),
441            2 => core::intrinsics::prefetch_write_data::<_, 1>(addr),
442            _ => core::intrinsics::prefetch_write_data::<_, 0>(addr),
443        }
444    }
445}
446
447// Non-generic instantiations of every architecture-specific code path.
448// Not part of the public API: only compiled when CI passes
449// `RUSTFLAGS="--cfg branches_check_asm"`, so that plain library cross-builds
450// (which never monomorphize the generic prefetch functions) still assemble
451// the inline assembly for the target architecture.
452#[cfg(branches_check_asm)]
453#[doc(hidden)]
454pub fn __branches_check_asm(addr: *const u8, cond: bool) -> bool {
455    let _ = addr;
456    #[cfg(feature = "prefetch")]
457    {
458        prefetch_read_data::<_, 0>(addr);
459        prefetch_read_data::<_, 1>(addr);
460        prefetch_read_data::<_, 2>(addr);
461        prefetch_read_data::<_, 3>(addr);
462        prefetch_read_data::<_, { -1 }>(addr);
463        prefetch_write_data::<_, 0>(addr);
464        prefetch_write_data::<_, 1>(addr);
465        prefetch_write_data::<_, 2>(addr);
466        prefetch_write_data::<_, 3>(addr);
467        prefetch_write_data::<_, { -1 }>(addr);
468    }
469    if unlikely(!cond) {
470        mark_unlikely();
471    }
472    likely(cond)
473}