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#[cfg(branches_stable)]
11#[inline(always)]
12#[cold]
13const fn cold_and_empty() {}
14
15/// Aborts the execution of the process immediately and without any cleanup.
16///
17/// This function is used to indicate a critical and unrecoverable error in the program.
18/// It terminates the process immediately without performing any cleanup or running destructors.
19///
20/// This function is safe to call, so it does not require an unsafe block.
21/// Therefore, implementations must not require the user to uphold any safety invariants.
22///
23/// If the std feature is enabled, this function calls std::process::abort()
24/// which is a more user-friendly and stable way of aborting the process.
25///
26/// If the std feature is disabled, this function panics by calling panic!().
27/// In this case, by using `extern "C"` this function is guaranteed to not unwind.
28#[cold]
29pub extern "C" fn abort() -> ! {
30    #[cfg(branches_stable)]
31    {
32        #[cfg(not(feature = "std"))]
33        unreachable!();
34        #[cfg(feature = "std")]
35        std::process::abort();
36    }
37    #[cfg(branches_nightly)]
38    core::intrinsics::abort()
39}
40
41/// Informs the optimizer that a condition is always true.
42///
43/// If the condition is actually false, the behavior is undefined.
44///
45/// This intrinsic doesn't generate any code. Instead, it tells the optimizer
46/// to preserve the condition for optimization passes. This can interfere with
47/// optimization of surrounding code and reduce performance, so avoid using it
48/// if the optimizer can already discover the invariant on its own or if it
49/// doesn't enable any significant optimizations.
50///
51/// # Safety
52///
53/// This intrinsic is marked unsafe because it can result in undefined behavior
54/// if the condition passed to it is false.
55#[inline(always)]
56pub unsafe fn assume(b: bool) {
57    #[cfg(branches_stable)]
58    {
59        // Rust >= 1.81.0: use the newer `assert_unchecked` hint.
60        #[cfg(rustc_ge_1_81_0)]
61        {
62            core::hint::assert_unchecked(b)
63        }
64        // Rust < 1.81.0: fall back to the older `unreachable_unchecked`.
65        #[cfg(not(rustc_ge_1_81_0))]
66        {
67            if !b {
68                core::hint::unreachable_unchecked()
69            }
70        }
71    }
72    #[cfg(branches_nightly)]
73    core::intrinsics::assume(b)
74}
75
76/// Hints to the compiler that the branch condition is likely to be true.
77/// Returns the value passed to it.
78///
79/// This intrinsic is primarily used with `if` statements.
80/// Using it in other contexts may not have any effect.
81///
82/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
83/// Therefore, implementations must not require the user to uphold any safety invariants.
84#[inline(always)]
85pub fn likely(b: bool) -> bool {
86    #[cfg(branches_stable)]
87    {
88        if !b {
89            cold_and_empty();
90        }
91        b
92    }
93    #[cfg(branches_nightly)]
94    core::intrinsics::likely(b)
95}
96
97/// Marks a code block as cold, indicating to the compiler that it is unlikely to be called.
98/// This can help the compiler optimize for the common case.
99///
100/// This function does not take any arguments and does not return any value.
101/// It is primarily used to mark functions or code paths that are rarely executed,
102/// such as error handling or panic paths.
103
104/// Example: marking the `None` variant of an `Option` as unlikely.
105///
106/// In many hot paths an `Option<T>` is expected to be `Some`.  
107/// By marking the `None` arm using `mark_unlikely` we give the optimizer a hint
108/// that this branch is rarely taken.
109///
110/// ```rust
111/// use branches::{mark_unlikely};
112///
113/// #[derive(Debug)]
114/// enum Status {
115///     Ok(i32),
116///     Err(String),
117/// }
118///
119/// fn get_value(status: Status) -> i32 {
120///     match status {
121///         Status::Ok(v) => v,
122///         // The error case is rare, hint the compiler accordingly.
123///         Status::Err(err) => {
124///             mark_unlikely();
125///             eprintln!("unexpected error: {:?}", err);
126///             -1
127///         }
128///     }
129/// }
130/// ```
131#[cold]
132#[inline(always)]
133pub const fn mark_unlikely() {}
134
135/// Hints to the compiler that the branch condition is unlikely to be true.
136/// Returns the value passed to it.
137///
138/// This intrinsic is primarily used with `if` statements.
139/// Using it in other contexts may not have any effect.
140///
141/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
142/// Therefore, implementations must not require the user to uphold any safety invariants.
143#[inline(always)]
144pub fn unlikely(b: bool) -> bool {
145    #[cfg(branches_stable)]
146    {
147        if b {
148            cold_and_empty();
149        }
150        b
151    }
152    #[cfg(branches_nightly)]
153    core::intrinsics::unlikely(b)
154}
155
156/// Prefetches data for reading into the cache.
157///
158/// This function hints to the CPU that the data at the given address
159/// will be read soon, allowing the CPU to load the data into the cache
160/// in advance. This can improve performance by reducing cache misses.
161///
162/// # Arguments
163///
164/// * `addr` - A pointer to the data to prefetch.
165/// * `LOCALITY` - The cache locality to prefetch into.
166#[inline(always)]
167#[cfg(feature = "prefetch")]
168pub fn prefetch_read_data<T, const LOCALITY: i32>(addr: *const T) {
169    #[cfg(branches_stable)]
170    {
171        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
172        unsafe {
173            match LOCALITY {
174                0 => core::arch::asm!(
175                    "prefetcht0 [{}]",
176                    in(reg) addr,
177                    options(nostack, readonly, preserves_flags)
178                ), // L1 cache
179                1 => core::arch::asm!(
180                    "prefetcht1 [{}]",
181                    in(reg) addr,
182                    options(nostack, readonly, preserves_flags)
183                ), // L2 cache
184                2 => core::arch::asm!(
185                    "prefetcht2 [{}]",
186                    in(reg) addr,
187                    options(nostack, readonly, preserves_flags)
188                ), // L3 cache
189                _ => core::arch::asm!(
190                    "prefetchnta [{}]",
191                    in(reg) addr,
192                    options(nostack, readonly, preserves_flags)
193                ), // Non-temporal
194            }
195        }
196
197        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
198        unsafe {
199            match LOCALITY {
200                0 => core::arch::asm!(
201                    "prfm pldl1keep, [{}]",
202                    in(reg) addr,
203                    options(nostack, readonly, preserves_flags)
204                ), // L1 cache
205                1 => core::arch::asm!(
206                    "prfm pldl2keep, [{}]",
207                    in(reg) addr,
208                    options(nostack, readonly, preserves_flags)
209                ), // L2 cache
210                _ => core::arch::asm!(
211                    "prfm pldl3keep, [{}]",
212                    in(reg) addr,
213                    options(nostack, readonly, preserves_flags)
214                ), // L3 or non-temporal
215            }
216        }
217
218        #[cfg(target_arch = "riscv64")]
219        unsafe {
220            core::arch::asm!(
221                "prefetch [{}]",
222                in(reg) addr,
223                options(nostack, readonly, preserves_flags)
224            );
225        }
226
227        // this requires unstable asm feature, uncomment when stabilized
228        //#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
229        //unsafe {
230        //    core::arch::asm!(
231        //        "dcbt 0, {}",
232        //        in(reg) addr,
233        //        options(nostack, readonly, preserves_flags)
234        //    );
235        //}
236    }
237    #[cfg(branches_nightly)]
238    core::intrinsics::prefetch_read_data::<_, LOCALITY>(addr)
239}
240
241/// Prefetches data for writing into the cache.
242///
243/// This function hints to the CPU that the data at the given address
244/// will be written soon, allowing the CPU to load the data into the cache
245/// in advance. This can improve performance by reducing cache misses.
246///
247/// # Arguments
248///
249/// * `addr` - A pointer to the data to prefetch.
250/// * `LOCALITY` - The cache locality to prefetch into.
251#[inline(always)]
252#[cfg(feature = "prefetch")]
253pub fn prefetch_write_data<T, const LOCALITY: i32>(addr: *const T) {
254    #[cfg(branches_stable)]
255    {
256        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
257        unsafe {
258            core::arch::asm!(
259                "prefetchw [{}]",
260                in(reg) addr,
261                options(nostack, readonly, preserves_flags)
262            ) // Write-prefetch for L1/L2/L3 cache
263        }
264
265        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
266        unsafe {
267            core::arch::asm!(
268                "prfm pstl1keep, [{}]",
269                in(reg) addr,
270                options(nostack, readonly, preserves_flags)
271            ); // Write-prefetch for L1 cache
272        }
273
274        #[cfg(target_arch = "riscv64")]
275        unsafe {
276            core::arch::asm!(
277                "prefetchw [{}]",
278                in(reg) addr,
279                options(nostack, readonly, preserves_flags)
280            );
281        }
282
283        // this requires unstable asm feature, uncomment when stabilized
284        //#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
285        //unsafe {
286        //    core::arch::asm!(
287        //        "dcbtst 0, {}",
288        //        in(reg) addr,
289        //        options(nostack, readonly, preserves_flags)
290        //    ); // Write-prefetch
291        // }
292    }
293    #[cfg(branches_nightly)]
294    core::intrinsics::prefetch_write_data::<_, LOCALITY>(addr)
295}