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(unstable, feature(core_intrinsics))]
5#![cfg_attr(unstable, 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(not(unstable))]
11#[inline(always)]
12#[cold]
13fn 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(not(unstable))]
31 {
32 #[cfg(not(feature = "std"))]
33 unreachable!();
34 #[cfg(feature = "std")]
35 std::process::abort();
36 }
37 #[cfg(unstable)]
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(not(unstable))]
58 if !b {
59 core::hint::unreachable_unchecked();
60 }
61 #[cfg(unstable)]
62 core::intrinsics::assume(b)
63}
64
65/// Hints to the compiler that the branch condition is likely to be true.
66/// Returns the value passed to it.
67///
68/// This intrinsic is primarily used with `if` statements.
69/// Using it in other contexts may not have any effect.
70///
71/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
72/// Therefore, implementations must not require the user to uphold any safety invariants.
73#[inline(always)]
74pub fn likely(b: bool) -> bool {
75 #[cfg(not(unstable))]
76 {
77 if !b {
78 cold_and_empty();
79 }
80 b
81 }
82 #[cfg(unstable)]
83 core::intrinsics::likely(b)
84}
85
86/// Hints to the compiler that the branch condition is unlikely to be true.
87/// Returns the value passed to it.
88///
89/// This intrinsic is primarily used with `if` statements.
90/// Using it in other contexts may not have any effect.
91///
92/// Unlike most intrinsics, this function is safe to call and doesn't require an `unsafe` block.
93/// Therefore, implementations must not require the user to uphold any safety invariants.
94#[inline(always)]
95pub fn unlikely(b: bool) -> bool {
96 #[cfg(not(unstable))]
97 {
98 if b {
99 cold_and_empty();
100 }
101 b
102 }
103 #[cfg(unstable)]
104 core::intrinsics::unlikely(b)
105}
106
107/// Prefetches data for reading into the cache.
108///
109/// This function hints to the CPU that the data at the given address
110/// will be read soon, allowing the CPU to load the data into the cache
111/// in advance. This can improve performance by reducing cache misses.
112///
113/// # Arguments
114///
115/// * `addr` - A pointer to the data to prefetch.
116/// * `LOCALITY` - The cache locality to prefetch into.
117#[inline(always)]
118pub fn prefetch_read_data<T, const LOCALITY: i32>(addr: *const T) {
119 #[cfg(not(unstable))]
120 {
121 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
122 unsafe {
123 match LOCALITY {
124 0 => core::arch::asm!(
125 "prefetcht0 [{}]",
126 in(reg) addr,
127 options(nostack, readonly, preserves_flags)
128 ), // L1 cache
129 1 => core::arch::asm!(
130 "prefetcht1 [{}]",
131 in(reg) addr,
132 options(nostack, readonly, preserves_flags)
133 ), // L2 cache
134 2 => core::arch::asm!(
135 "prefetcht2 [{}]",
136 in(reg) addr,
137 options(nostack, readonly, preserves_flags)
138 ), // L3 cache
139 _ => core::arch::asm!(
140 "prefetchnta [{}]",
141 in(reg) addr,
142 options(nostack, readonly, preserves_flags)
143 ), // Non-temporal
144 }
145 }
146
147 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
148 unsafe {
149 match LOCALITY {
150 0 => core::arch::asm!(
151 "prfm pldl1keep, [{}]",
152 in(reg) addr,
153 options(nostack, readonly, preserves_flags)
154 ), // L1 cache
155 1 => core::arch::asm!(
156 "prfm pldl2keep, [{}]",
157 in(reg) addr,
158 options(nostack, readonly, preserves_flags)
159 ), // L2 cache
160 _ => core::arch::asm!(
161 "prfm pldl3keep, [{}]",
162 in(reg) addr,
163 options(nostack, readonly, preserves_flags)
164 ), // L3 or non-temporal
165 }
166 }
167
168 #[cfg(target_arch = "riscv64")]
169 unsafe {
170 core::arch::asm!(
171 "prefetch [{}]",
172 in(reg) addr,
173 options(nostack, readonly, preserves_flags)
174 );
175 }
176
177 #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
178 unsafe {
179 core::arch::asm!(
180 "dcbt 0, {}",
181 in(reg) addr,
182 options(nostack, readonly, preserves_flags)
183 );
184 }
185 }
186 #[cfg(unstable)]
187 core::intrinsics::prefetch_read_data::<_, LOCALITY>(addr)
188}
189
190/// Prefetches data for writing into the cache.
191///
192/// This function hints to the CPU that the data at the given address
193/// will be written soon, allowing the CPU to load the data into the cache
194/// in advance. This can improve performance by reducing cache misses.
195///
196/// # Arguments
197///
198/// * `addr` - A pointer to the data to prefetch.
199/// * `LOCALITY` - The cache locality to prefetch into.
200#[inline(always)]
201pub fn prefetch_write_data<T, const LOCALITY: i32>(addr: *const T) {
202 #[cfg(not(unstable))]
203 {
204 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
205 unsafe {
206 core::arch::asm!(
207 "prefetchw [{}]",
208 in(reg) addr,
209 options(nostack, readonly, preserves_flags)
210 ) // Write-prefetch for L1/L2/L3 cache
211 }
212
213 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
214 unsafe {
215 core::arch::asm!(
216 "prfm pstl1keep, [{}]",
217 in(reg) addr,
218 options(nostack, readonly, preserves_flags)
219 ); // Write-prefetch for L1 cache
220 }
221
222 #[cfg(target_arch = "riscv64")]
223 unsafe {
224 core::arch::asm!(
225 "prefetchw [{}]",
226 in(reg) addr,
227 options(nostack, readonly, preserves_flags)
228 );
229 }
230
231 #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
232 unsafe {
233 core::arch::asm!(
234 "dcbtst 0, {}",
235 in(reg) addr,
236 options(nostack, readonly, preserves_flags)
237 ); // Write-prefetch
238 }
239 }
240 #[cfg(unstable)]
241 core::intrinsics::prefetch_write_data::<_, LOCALITY>(addr)
242}
243
244// tests
245#[cfg(test)]
246mod test;