1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2
3 /*
4 64-bit atomic implementation on riscv32 using amocas.d (DWCAS).
5
6 Note: On Miri and ThreadSanitizer which do not support inline assembly, we don't use
7 this module and use fallback implementation instead.
8
9 Refs:
10 - RISC-V Instruction Set Manual
11 https://github.com/riscv/riscv-isa-manual/tree/riscv-isa-release-8b9dc50-2024-08-30
12 "Zacas" Extension for Atomic Compare-and-Swap (CAS) Instructions
13 https://github.com/riscv/riscv-isa-manual/blob/riscv-isa-release-8b9dc50-2024-08-30/src/zacas.adoc
14 - RISC-V Atomics ABI Specification
15 https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-atomic.adoc
16
17 Generated asm:
18 - riscv32 (+experimental-zacas) https://godbolt.org/z/d3f6EsG3f
19 */
20
21 // TODO: merge duplicated code with atomic128/riscv64.rs
22
23 include!("macros.rs");
24
25 #[cfg(not(any(
26 target_feature = "experimental-zacas",
27 portable_atomic_target_feature = "experimental-zacas",
28 )))]
29 #[path = "../fallback/outline_atomics.rs"]
30 mod fallback;
31
32 #[cfg(not(portable_atomic_no_outline_atomics))]
33 #[cfg(any(test, portable_atomic_outline_atomics))] // TODO(riscv): currently disabled by default
34 #[cfg(any(
35 test,
36 not(any(
37 target_feature = "experimental-zacas",
38 portable_atomic_target_feature = "experimental-zacas",
39 )),
40 ))]
41 #[cfg(any(target_os = "linux", target_os = "android"))]
42 #[path = "../detect/riscv_linux.rs"]
43 mod detect;
44
45 use core::{arch::asm, sync::atomic::Ordering};
46
47 use crate::utils::{Pair, U64};
48
49 macro_rules! debug_assert_zacas {
50 () => {
51 #[cfg(not(any(
52 target_feature = "experimental-zacas",
53 portable_atomic_target_feature = "experimental-zacas",
54 )))]
55 {
56 debug_assert!(detect::detect().has_zacas());
57 }
58 };
59 }
60
61 // LLVM doesn't support `.option arch, +zabha` directive as of LLVM 19 because it is experimental.
62 // So, we currently always using .insn directive.
63 // `.insn <value>` directive requires LLVM 19.
64 // https://github.com/llvm/llvm-project/commit/2a086dce691e3cc34a2fc27f4fb255bb2cbbfac9
65 // // https://github.com/riscv-non-isa/riscv-asm-manual/blob/ad0de8c004e29c9a7ac33cfd054f4d4f9392f2fb/src/asm-manual.adoc#arch
66 // macro_rules! start_zacas {
67 // () => {
68 // ".option push\n.option arch, +zacas"
69 // };
70 // }
71 // macro_rules! end_zacas {
72 // () => {
73 // ".option pop"
74 // };
75 // }
76
77 // LLVM doesn't support `.option arch, +zabha` directive as of LLVM 19 because it is experimental.
78 // So, we currently always using .insn directive.
79 // macro_rules! atomic_rmw_amocas_order {
80 // ($op:ident, $order:ident) => {
81 // atomic_rmw_amocas_order!($op, $order, failure = $order)
82 // };
83 // ($op:ident, $order:ident, failure = $failure:ident) => {
84 // match $order {
85 // Ordering::Relaxed => $op!("", ""),
86 // Ordering::Acquire => $op!("", ".aq"),
87 // Ordering::Release => $op!("", ".rl"),
88 // Ordering::AcqRel => $op!("", ".aqrl"),
89 // Ordering::SeqCst if $failure == Ordering::SeqCst => $op!("fence rw,rw", ".aqrl"),
90 // Ordering::SeqCst => $op!("", ".aqrl"),
91 // _ => unreachable!(),
92 // }
93 // };
94 // }
95 macro_rules! atomic_rmw_amocas_order_insn {
96 ($op:ident, $order:ident) => {
97 atomic_rmw_amocas_order_insn!($op, $order, failure = $order)
98 };
99 ($op:ident, $order:ident, failure = $failure:ident) => {
100 match $order {
101 Ordering::Relaxed => $op!("", "8"),
102 Ordering::Acquire => $op!("", "c"),
103 Ordering::Release => $op!("", "a"),
104 Ordering::AcqRel => $op!("", "e"),
105 Ordering::SeqCst if $failure == Ordering::SeqCst => $op!("fence rw,rw", "e"),
106 Ordering::SeqCst => $op!("", "e"),
107 _ => unreachable!(),
108 }
109 };
110 }
111
112 // If zacas is available at compile-time, we can always use zacas_fn.
113 #[cfg(any(
114 target_feature = "experimental-zacas",
115 portable_atomic_target_feature = "experimental-zacas",
116 ))]
117 use atomic_load_zacas as atomic_load;
118 // Otherwise, we need to do run-time detection and can use zacas_fn only if zacas is available.
119 #[cfg(not(any(
120 target_feature = "experimental-zacas",
121 portable_atomic_target_feature = "experimental-zacas",
122 )))]
123 #[inline]
atomic_load(src: *mut u64, order: Ordering) -> u64124 unsafe fn atomic_load(src: *mut u64, order: Ordering) -> u64 {
125 fn_alias! {
126 // inline(never) is just a hint and also not strictly necessary
127 // because we use ifunc helper macro, but used for clarity.
128 #[inline(never)]
129 unsafe fn(src: *mut u64) -> u64;
130 atomic_load_zacas_relaxed = atomic_load_zacas(Ordering::Relaxed);
131 atomic_load_zacas_acquire = atomic_load_zacas(Ordering::Acquire);
132 atomic_load_zacas_seqcst = atomic_load_zacas(Ordering::SeqCst);
133 }
134 // SAFETY: the caller must uphold the safety contract.
135 // we only calls atomic_load_zacas if zacas is available.
136 unsafe {
137 match order {
138 Ordering::Relaxed => {
139 ifunc!(unsafe fn(src: *mut u64) -> u64 {
140 if detect::detect().has_zacas() {
141 atomic_load_zacas_relaxed
142 } else {
143 fallback::atomic_load_non_seqcst
144 }
145 })
146 }
147 Ordering::Acquire => {
148 ifunc!(unsafe fn(src: *mut u64) -> u64 {
149 if detect::detect().has_zacas() {
150 atomic_load_zacas_acquire
151 } else {
152 fallback::atomic_load_non_seqcst
153 }
154 })
155 }
156 Ordering::SeqCst => {
157 ifunc!(unsafe fn(src: *mut u64) -> u64 {
158 if detect::detect().has_zacas() {
159 atomic_load_zacas_seqcst
160 } else {
161 fallback::atomic_load_seqcst
162 }
163 })
164 }
165 _ => unreachable!(),
166 }
167 }
168 }
169 #[inline]
atomic_load_zacas(src: *mut u64, order: Ordering) -> u64170 unsafe fn atomic_load_zacas(src: *mut u64, order: Ordering) -> u64 {
171 debug_assert!(src as usize % 8 == 0);
172 debug_assert_zacas!();
173
174 // SAFETY: the caller must uphold the safety contract.
175 unsafe {
176 let (out_lo, out_hi);
177 // LLVM doesn't support `.option arch, +zabha` directive as of LLVM 19 because it is experimental.
178 // So, we currently always using .insn directive.
179 // macro_rules! load {
180 // ($fence:tt, $asm_order:tt) => {
181 // asm!(
182 // start_zacas!(),
183 // $fence,
184 // concat!("amocas.d", $asm_order, " a2, a2, 0({src})"),
185 // end_zacas!(),
186 // src = in(reg) ptr_reg!(src),
187 // inout("a2") 0_u32 => out_lo,
188 // inout("a3") 0_u32 => out_hi,
189 // options(nostack, preserves_flags),
190 // )
191 // };
192 // }
193 // atomic_rmw_amocas_order!(load, order);
194 macro_rules! load {
195 ($fence:tt, $insn_order:tt) => {
196 asm!(
197 $fence,
198 // 4: 2{8,c,a,e}c5362f amocas.d{,.aq,.rl,.aqrl} a2, a2, (a0)
199 concat!(".insn 0x2", $insn_order, "c5362f"),
200 in("a0") ptr_reg!(src),
201 inout("a2") 0_u32 => out_lo,
202 inout("a3") 0_u32 => out_hi,
203 options(nostack, preserves_flags),
204 )
205 };
206 }
207 atomic_rmw_amocas_order_insn!(load, order);
208 U64 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
209 }
210 }
211
212 #[inline]
atomic_store(dst: *mut u64, val: u64, order: Ordering)213 unsafe fn atomic_store(dst: *mut u64, val: u64, order: Ordering) {
214 // SAFETY: the caller must uphold the safety contract.
215 unsafe {
216 atomic_swap(dst, val, order);
217 }
218 }
219
220 #[inline]
atomic_compare_exchange( dst: *mut u64, old: u64, new: u64, success: Ordering, failure: Ordering, ) -> Result<u64, u64>221 unsafe fn atomic_compare_exchange(
222 dst: *mut u64,
223 old: u64,
224 new: u64,
225 success: Ordering,
226 failure: Ordering,
227 ) -> Result<u64, u64> {
228 #[cfg(any(
229 target_feature = "experimental-zacas",
230 portable_atomic_target_feature = "experimental-zacas",
231 ))]
232 // SAFETY: the caller must uphold the safety contract.
233 // cfg guarantees that zacas instructions are available at compile-time.
234 let (prev, ok) = unsafe { atomic_compare_exchange_zacas(dst, old, new, success, failure) };
235 #[cfg(not(any(
236 target_feature = "experimental-zacas",
237 portable_atomic_target_feature = "experimental-zacas",
238 )))]
239 let (prev, ok) = {
240 fn_alias! {
241 // inline(never) is just a hint and also not strictly necessary
242 // because we use ifunc helper macro, but used for clarity.
243 #[inline(never)]
244 unsafe fn(dst: *mut u64, old: u64, new: u64) -> (u64, bool);
245 zacas_relaxed_fn = atomic_compare_exchange_zacas(Ordering::Relaxed, Ordering::Relaxed);
246 zacas_acquire_fn = atomic_compare_exchange_zacas(Ordering::Acquire, Ordering::Acquire);
247 zacas_release_fn = atomic_compare_exchange_zacas(Ordering::Release, Ordering::Relaxed);
248 zacas_acqrel_fn = atomic_compare_exchange_zacas(Ordering::AcqRel, Ordering::Acquire);
249 zacas_seqcst_fn = atomic_compare_exchange_zacas(Ordering::SeqCst, Ordering::SeqCst);
250 }
251 let order = crate::utils::upgrade_success_ordering(success, failure);
252 // SAFETY: the caller must uphold the safety contract.
253 // we only calls atomic_compare_exchange_zacas if zacas is available.
254 unsafe {
255 match order {
256 Ordering::Relaxed => {
257 ifunc!(unsafe fn(dst: *mut u64, old: u64, new: u64) -> (u64, bool) {
258 if detect::detect().has_zacas() {
259 zacas_relaxed_fn
260 } else {
261 fallback::atomic_compare_exchange_non_seqcst
262 }
263 })
264 }
265 Ordering::Acquire => {
266 ifunc!(unsafe fn(dst: *mut u64, old: u64, new: u64) -> (u64, bool) {
267 if detect::detect().has_zacas() {
268 zacas_acquire_fn
269 } else {
270 fallback::atomic_compare_exchange_non_seqcst
271 }
272 })
273 }
274 Ordering::Release => {
275 ifunc!(unsafe fn(dst: *mut u64, old: u64, new: u64) -> (u64, bool) {
276 if detect::detect().has_zacas() {
277 zacas_release_fn
278 } else {
279 fallback::atomic_compare_exchange_non_seqcst
280 }
281 })
282 }
283 Ordering::AcqRel => {
284 ifunc!(unsafe fn(dst: *mut u64, old: u64, new: u64) -> (u64, bool) {
285 if detect::detect().has_zacas() {
286 zacas_acqrel_fn
287 } else {
288 fallback::atomic_compare_exchange_non_seqcst
289 }
290 })
291 }
292 Ordering::SeqCst => {
293 ifunc!(unsafe fn(dst: *mut u64, old: u64, new: u64) -> (u64, bool) {
294 if detect::detect().has_zacas() {
295 zacas_seqcst_fn
296 } else {
297 fallback::atomic_compare_exchange_seqcst
298 }
299 })
300 }
301 _ => unreachable!(),
302 }
303 }
304 };
305 if ok {
306 Ok(prev)
307 } else {
308 Err(prev)
309 }
310 }
311 #[inline]
atomic_compare_exchange_zacas( dst: *mut u64, old: u64, new: u64, success: Ordering, failure: Ordering, ) -> (u64, bool)312 unsafe fn atomic_compare_exchange_zacas(
313 dst: *mut u64,
314 old: u64,
315 new: u64,
316 success: Ordering,
317 failure: Ordering,
318 ) -> (u64, bool) {
319 debug_assert!(dst as usize % 8 == 0);
320 debug_assert_zacas!();
321 let order = crate::utils::upgrade_success_ordering(success, failure);
322
323 // SAFETY: the caller must uphold the safety contract.
324 let prev = unsafe {
325 let old = U64 { whole: old };
326 let new = U64 { whole: new };
327 let (prev_lo, prev_hi);
328 // LLVM doesn't support `.option arch, +zabha` directive as of LLVM 19 because it is experimental.
329 // So, we currently always using .insn directive.
330 // macro_rules! cmpxchg {
331 // ($fence:tt, $asm_order:tt) => {
332 // asm!(
333 // start_zacas!(),
334 // $fence,
335 // concat!("amocas.d", $asm_order, " a4, a2, 0({dst})"),
336 // end_zacas!(),
337 // dst = in(reg) ptr_reg!(dst),
338 // // must be allocated to even/odd register pair
339 // inout("a4") old.pair.lo => prev_lo,
340 // inout("a5") old.pair.hi => prev_hi,
341 // // must be allocated to even/odd register pair
342 // in("a2") new.pair.lo,
343 // in("a3") new.pair.hi,
344 // options(nostack, preserves_flags),
345 // )
346 // };
347 // }
348 // atomic_rmw_amocas_order!(cmpxchg, order, failure = failure);
349 macro_rules! cmpxchg {
350 ($fence:tt, $insn_order:tt) => {
351 asm!(
352 $fence,
353 // 10: 2{8,c,a,e}c5372f amocas.d{,.aq,.rl,.aqrl} a4, a2, (a0)
354 concat!(".insn 0x2", $insn_order, "c5372f"),
355 in("a0") ptr_reg!(dst),
356 // must be allocated to even/odd register pair
357 inout("a4") old.pair.lo => prev_lo,
358 inout("a5") old.pair.hi => prev_hi,
359 // must be allocated to even/odd register pair
360 in("a2") new.pair.lo,
361 in("a3") new.pair.hi,
362 options(nostack, preserves_flags),
363 )
364 };
365 }
366 atomic_rmw_amocas_order_insn!(cmpxchg, order, failure = failure);
367 U64 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
368 };
369 (prev, prev == old)
370 }
371
372 // amocas is always strong.
373 use atomic_compare_exchange as atomic_compare_exchange_weak;
374
375 // 64-bit atomic load by two 32-bit atomic loads. (see arm_linux.rs for more)
376 #[inline]
byte_wise_atomic_load(src: *const u64) -> u64377 unsafe fn byte_wise_atomic_load(src: *const u64) -> u64 {
378 // SAFETY: the caller must uphold the safety contract.
379 unsafe {
380 let (out_lo, out_hi);
381 asm!(
382 "lw {out_lo}, ({src})",
383 "lw {out_hi}, 4({src})",
384 src = in(reg) ptr_reg!(src),
385 out_lo = out(reg) out_lo,
386 out_hi = out(reg) out_hi,
387 options(pure, nostack, preserves_flags, readonly),
388 );
389 U64 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
390 }
391 }
392
393 #[inline(always)]
atomic_update_zacas<F>(dst: *mut u64, order: Ordering, mut f: F) -> u64 where F: FnMut(u64) -> u64,394 unsafe fn atomic_update_zacas<F>(dst: *mut u64, order: Ordering, mut f: F) -> u64
395 where
396 F: FnMut(u64) -> u64,
397 {
398 // SAFETY: the caller must uphold the safety contract.
399 unsafe {
400 let mut prev = byte_wise_atomic_load(dst);
401 loop {
402 let next = f(prev);
403 match atomic_compare_exchange_weak(dst, prev, next, order, Ordering::Relaxed) {
404 Ok(x) => return x,
405 Err(x) => prev = x,
406 }
407 }
408 }
409 }
410
411 macro_rules! select_atomic_rmw {
412 (
413 unsafe fn $name:ident(dst: *mut u64 $(, $($arg:tt)*)?) $(-> $ret_ty:ty)? {
414 $($zacas_fn_body:tt)*
415 }
416 zacas = $zacas_fn:ident;
417 non_seqcst_fallback = $non_seqcst_fallback_fn:ident;
418 seqcst_fallback = $seqcst_fallback_fn:ident;
419 ) => {
420 #[inline]
421 unsafe fn $zacas_fn(dst: *mut u64 $(, $($arg)*)?, order: Ordering) $(-> $ret_ty)? {
422 // SAFETY: the caller must uphold the safety contract.
423 unsafe { atomic_update_zacas(dst, order, $($zacas_fn_body)*) }
424 }
425 // If zacas is available at compile-time, we can always use zacas_fn.
426 #[cfg(any(
427 target_feature = "experimental-zacas",
428 portable_atomic_target_feature = "experimental-zacas",
429 ))]
430 use $zacas_fn as $name;
431 // Otherwise, we need to do run-time detection and can use zacas_fn only if zacas is available.
432 #[cfg(not(any(
433 target_feature = "experimental-zacas",
434 portable_atomic_target_feature = "experimental-zacas",
435 )))]
436 #[inline]
437 unsafe fn $name(dst: *mut u64 $(, $($arg)*)?, order: Ordering) $(-> $ret_ty)? {
438 fn_alias! {
439 // inline(never) is just a hint and also not strictly necessary
440 // because we use ifunc helper macro, but used for clarity.
441 #[inline(never)]
442 unsafe fn(dst: *mut u64 $(, $($arg)*)?) $(-> $ret_ty)?;
443 zacas_relaxed_fn = $zacas_fn(Ordering::Relaxed);
444 zacas_acquire_fn = $zacas_fn(Ordering::Acquire);
445 zacas_release_fn = $zacas_fn(Ordering::Release);
446 zacas_acqrel_fn = $zacas_fn(Ordering::AcqRel);
447 zacas_seqcst_fn = $zacas_fn(Ordering::SeqCst);
448 }
449 // SAFETY: the caller must uphold the safety contract.
450 // we only calls zacas_fn if zacas is available.
451 unsafe {
452 match order {
453 Ordering::Relaxed => {
454 ifunc!(unsafe fn(dst: *mut u64 $(, $($arg)*)?) $(-> $ret_ty)? {
455 if detect::detect().has_zacas() {
456 zacas_relaxed_fn
457 } else {
458 fallback::$non_seqcst_fallback_fn
459 }
460 })
461 }
462 Ordering::Acquire => {
463 ifunc!(unsafe fn(dst: *mut u64 $(, $($arg)*)?) $(-> $ret_ty)? {
464 if detect::detect().has_zacas() {
465 zacas_acquire_fn
466 } else {
467 fallback::$non_seqcst_fallback_fn
468 }
469 })
470 }
471 Ordering::Release => {
472 ifunc!(unsafe fn(dst: *mut u64 $(, $($arg)*)?) $(-> $ret_ty)? {
473 if detect::detect().has_zacas() {
474 zacas_release_fn
475 } else {
476 fallback::$non_seqcst_fallback_fn
477 }
478 })
479 }
480 Ordering::AcqRel => {
481 ifunc!(unsafe fn(dst: *mut u64 $(, $($arg)*)?) $(-> $ret_ty)? {
482 if detect::detect().has_zacas() {
483 zacas_acqrel_fn
484 } else {
485 fallback::$non_seqcst_fallback_fn
486 }
487 })
488 }
489 Ordering::SeqCst => {
490 ifunc!(unsafe fn(dst: *mut u64 $(, $($arg)*)?) $(-> $ret_ty)? {
491 if detect::detect().has_zacas() {
492 zacas_seqcst_fn
493 } else {
494 fallback::$seqcst_fallback_fn
495 }
496 })
497 }
498 _ => unreachable!(),
499 }
500 }
501 }
502 };
503 }
504
505 select_atomic_rmw! {
506 unsafe fn atomic_swap(dst: *mut u64, val: u64) -> u64 {
507 |_| val
508 }
509 zacas = atomic_swap_zacas;
510 non_seqcst_fallback = atomic_swap_non_seqcst;
511 seqcst_fallback = atomic_swap_seqcst;
512 }
513 select_atomic_rmw! {
514 unsafe fn atomic_add(dst: *mut u64, val: u64) -> u64 {
515 |x| x.wrapping_add(val)
516 }
517 zacas = atomic_add_zacas;
518 non_seqcst_fallback = atomic_add_non_seqcst;
519 seqcst_fallback = atomic_add_seqcst;
520 }
521 select_atomic_rmw! {
522 unsafe fn atomic_sub(dst: *mut u64, val: u64) -> u64 {
523 |x| x.wrapping_sub(val)
524 }
525 zacas = atomic_sub_zacas;
526 non_seqcst_fallback = atomic_sub_non_seqcst;
527 seqcst_fallback = atomic_sub_seqcst;
528 }
529 select_atomic_rmw! {
530 unsafe fn atomic_and(dst: *mut u64, val: u64) -> u64 {
531 |x| x & val
532 }
533 zacas = atomic_and_zacas;
534 non_seqcst_fallback = atomic_and_non_seqcst;
535 seqcst_fallback = atomic_and_seqcst;
536 }
537 select_atomic_rmw! {
538 unsafe fn atomic_nand(dst: *mut u64, val: u64) -> u64 {
539 |x| !(x & val)
540 }
541 zacas = atomic_nand_zacas;
542 non_seqcst_fallback = atomic_nand_non_seqcst;
543 seqcst_fallback = atomic_nand_seqcst;
544 }
545 select_atomic_rmw! {
546 unsafe fn atomic_or(dst: *mut u64, val: u64) -> u64 {
547 |x| x | val
548 }
549 zacas = atomic_or_zacas;
550 non_seqcst_fallback = atomic_or_non_seqcst;
551 seqcst_fallback = atomic_or_seqcst;
552 }
553 select_atomic_rmw! {
554 unsafe fn atomic_xor(dst: *mut u64, val: u64) -> u64 {
555 |x| x ^ val
556 }
557 zacas = atomic_xor_zacas;
558 non_seqcst_fallback = atomic_xor_non_seqcst;
559 seqcst_fallback = atomic_xor_seqcst;
560 }
561 select_atomic_rmw! {
562 unsafe fn atomic_max(dst: *mut u64, val: u64) -> u64 {
563 |x| {
564 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
565 { core::cmp::max(x as i64, val as i64) as u64 }
566 }
567 }
568 zacas = atomic_max_zacas;
569 non_seqcst_fallback = atomic_max_non_seqcst;
570 seqcst_fallback = atomic_max_seqcst;
571 }
572 select_atomic_rmw! {
573 unsafe fn atomic_umax(dst: *mut u64, val: u64) -> u64 {
574 |x| core::cmp::max(x, val)
575 }
576 zacas = atomic_umax_zacas;
577 non_seqcst_fallback = atomic_umax_non_seqcst;
578 seqcst_fallback = atomic_umax_seqcst;
579 }
580 select_atomic_rmw! {
581 unsafe fn atomic_min(dst: *mut u64, val: u64) -> u64 {
582 |x| {
583 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
584 { core::cmp::min(x as i64, val as i64) as u64 }
585 }
586 }
587 zacas = atomic_min_zacas;
588 non_seqcst_fallback = atomic_min_non_seqcst;
589 seqcst_fallback = atomic_min_seqcst;
590 }
591 select_atomic_rmw! {
592 unsafe fn atomic_umin(dst: *mut u64, val: u64) -> u64 {
593 |x| core::cmp::min(x, val)
594 }
595 zacas = atomic_umin_zacas;
596 non_seqcst_fallback = atomic_umin_non_seqcst;
597 seqcst_fallback = atomic_umin_seqcst;
598 }
599 select_atomic_rmw! {
600 unsafe fn atomic_not(dst: *mut u64) -> u64 {
601 |x| !x
602 }
603 zacas = atomic_not_zacas;
604 non_seqcst_fallback = atomic_not_non_seqcst;
605 seqcst_fallback = atomic_not_seqcst;
606 }
607 select_atomic_rmw! {
608 unsafe fn atomic_neg(dst: *mut u64) -> u64 {
609 u64::wrapping_neg
610 }
611 zacas = atomic_neg_zacas;
612 non_seqcst_fallback = atomic_neg_non_seqcst;
613 seqcst_fallback = atomic_neg_seqcst;
614 }
615
616 #[inline]
is_lock_free() -> bool617 fn is_lock_free() -> bool {
618 #[cfg(any(
619 target_feature = "experimental-zacas",
620 portable_atomic_target_feature = "experimental-zacas",
621 ))]
622 {
623 // zacas is available at compile-time.
624 true
625 }
626 #[cfg(not(any(
627 target_feature = "experimental-zacas",
628 portable_atomic_target_feature = "experimental-zacas",
629 )))]
630 {
631 detect::detect().has_zacas()
632 }
633 }
634 const IS_ALWAYS_LOCK_FREE: bool = cfg!(any(
635 target_feature = "experimental-zacas",
636 portable_atomic_target_feature = "experimental-zacas",
637 ));
638
639 atomic64!(AtomicI64, i64, atomic_max, atomic_min);
640 atomic64!(AtomicU64, u64, atomic_umax, atomic_umin);
641
642 #[allow(clippy::undocumented_unsafe_blocks, clippy::wildcard_imports)]
643 #[cfg(test)]
644 mod tests {
645 use super::*;
646
647 test_atomic_int!(i64);
648 test_atomic_int!(u64);
649
650 // load/store/swap implementation is not affected by signedness, so it is
651 // enough to test only unsigned types.
652 stress_test!(u64);
653 }
654