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}