• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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