Skip to main content

atomic_maybe_uninit/arch/
x86.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*
4x86 and x86_64
5
6Refs:
7- IntelĀ® 64 and IA-32 Architectures Software Developer Manuals
8  https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
9- x86 and amd64 instruction reference
10  https://www.felixcloutier.com/x86
11- portable-atomic
12  https://github.com/taiki-e/portable-atomic
13
14See tests/asm-test/asm/atomic-maybe-uninit for generated assembly.
15*/
16
17#[cfg(target_arch = "x86_64")]
18#[cfg(target_feature = "cmpxchg16b")]
19#[cfg(not(atomic_maybe_uninit_no_outline_atomics))]
20#[cfg(not(target_env = "sgx"))]
21#[cfg_attr(
22    not(test),
23    cfg(not(any(
24        target_feature = "avx",
25        all(
26            not(target_feature = "avx"),
27            any(
28                atomic_maybe_uninit_no_outline_atomics,
29                target_env = "sgx",
30                not(target_feature = "sse"),
31            ),
32        ),
33    )))
34)]
35#[path = "../detect/x86_64.rs"]
36mod detect;
37
38delegate_size!(delegate_load_store);
39delegate_size!(delegate_swap);
40#[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))]
41delegate_size!(delegate_cas);
42
43#[cfg(target_arch = "x86")]
44#[cfg(not(atomic_maybe_uninit_no_cmpxchg8b))]
45#[cfg(all(target_feature = "sse", not(atomic_maybe_uninit_test_prefer_x87_over_sse)))]
46use core::arch::x86::__m128;
47#[cfg(target_arch = "x86")]
48#[cfg(not(atomic_maybe_uninit_no_cmpxchg8b))]
49#[cfg(all(target_feature = "sse2", not(atomic_maybe_uninit_test_prefer_x87_over_sse)))]
50use core::arch::x86::__m128i;
51#[cfg(target_arch = "x86_64")]
52#[cfg(target_feature = "cmpxchg16b")]
53#[cfg(not(all(
54    not(target_feature = "avx"),
55    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
56)))]
57use core::arch::x86_64::__m128i;
58use core::{
59    arch::asm,
60    mem::{self, MaybeUninit},
61    sync::atomic::Ordering,
62};
63
64#[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))]
65use crate::raw::AtomicCompareExchange;
66use crate::raw::{AtomicLoad, AtomicStore, AtomicSwap};
67#[cfg(target_arch = "x86")]
68#[cfg(not(atomic_maybe_uninit_no_cmpxchg8b))]
69use crate::utils::{MaybeUninit64, Pair};
70#[cfg(target_arch = "x86_64")]
71#[cfg(target_feature = "cmpxchg16b")]
72use crate::utils::{MaybeUninit128, Pair};
73
74#[cfg(target_pointer_width = "32")]
75macro_rules! ptr_modifier {
76    () => {
77        ":e"
78    };
79}
80#[cfg(target_pointer_width = "64")]
81macro_rules! ptr_modifier {
82    () => {
83        ""
84    };
85}
86
87// -----------------------------------------------------------------------------
88// Register-width or smaller atomics
89
90macro_rules! atomic {
91    (
92        $ty:ident, $val_reg:ident, $ux_reg:ident, $ux:ident,
93        $zx:literal, $val_modifier:literal, $reg_val_modifier:tt, $zx_val_modifier:tt, $ptr_size:tt,
94        $cmpxchg_cmp_reg:tt
95    ) => {
96        #[cfg(target_arch = "x86")]
97        atomic!($ty, $val_reg, $ux_reg, reg_abcd, $ux, $zx, $val_modifier,
98            $reg_val_modifier, $zx_val_modifier, $ptr_size, $cmpxchg_cmp_reg);
99        #[cfg(target_arch = "x86_64")]
100        atomic!($ty, $val_reg, $ux_reg, reg, $ux, $zx, $val_modifier,
101            $reg_val_modifier, $zx_val_modifier, $ptr_size, $cmpxchg_cmp_reg);
102    };
103    (
104        $ty:ident, $val_reg:ident, $ux_reg:ident, $r_reg:ident, $ux:ident,
105        $zx:literal, $val_modifier:literal, $reg_val_modifier:tt, $zx_val_modifier:tt, $ptr_size:tt,
106        $cmpxchg_cmp_reg:tt
107    ) => {
108        delegate_signed!(delegate_load_store, $ty);
109        delegate_signed!(delegate_swap, $ty);
110        #[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))]
111        delegate_signed!(delegate_cas, $ty);
112        impl AtomicLoad for $ty {
113            #[inline]
114            unsafe fn atomic_load(
115                src: *const MaybeUninit<Self>,
116                _order: Ordering,
117            ) -> MaybeUninit<Self> {
118                debug_assert_atomic_unsafe_precondition!(src, $ty);
119                let out;
120
121                // SAFETY: the caller must uphold the safety contract.
122                // load by MOV has SeqCst semantics.
123                unsafe {
124                    asm!(
125                        concat!("mov", $zx, " {out", $zx_val_modifier, "}, ", $ptr_size, " ptr [{src", ptr_modifier!(), "}]"), // atomic { out = zero_extend(*src) }
126                        src = in(reg) src,
127                        out = lateout(reg) out,
128                        options(nostack, preserves_flags),
129                    );
130                }
131                crate::utils::extend32::$ty::extract(out)
132            }
133        }
134        impl AtomicStore for $ty {
135            #[inline]
136            unsafe fn __atomic_store_impl(
137                dst: *mut MaybeUninit<Self>,
138                mut val: MaybeUninit<Self>,
139                order: Ordering,
140            ) {
141                debug_assert_atomic_unsafe_precondition!(dst, $ty);
142
143                // SAFETY: the caller must uphold the safety contract.
144                unsafe {
145                    match order {
146                        // Relaxed and Release stores are equivalent.
147                        Ordering::Relaxed | Ordering::Release => {
148                            asm!(
149                                concat!("mov ", $ptr_size, " ptr [{dst", ptr_modifier!(), "}], {val", $val_modifier, "}"), // atomic { *dst = val }
150                                dst = in(reg) dst,
151                                val = in($val_reg) val,
152                                options(nostack, preserves_flags),
153                            );
154                        }
155                        #[allow(unused_assignments)] // TODO(gcc): Workaround for rustc_codegen_gcc bug
156                        Ordering::SeqCst => {
157                            asm!(
158                                // SeqCst store is xchg, not mov
159                                concat!("xchg ", $ptr_size, " ptr [{dst", ptr_modifier!(), "}], {val", $val_modifier, "}"), // atomic { _x = *dst; *dst = val; val = _x }
160                                dst = in(reg) dst,
161                                val = inout($val_reg) val,
162                                options(nostack, preserves_flags),
163                            );
164                        }
165                        _ => crate::utils::unreachable_unchecked(),
166                    }
167                }
168            }
169        }
170        impl AtomicSwap for $ty {
171            #[inline]
172            unsafe fn __atomic_swap_impl(
173                dst: *mut MaybeUninit<Self>,
174                val: MaybeUninit<Self>,
175                _order: Ordering,
176            ) -> MaybeUninit<Self> {
177                debug_assert_atomic_unsafe_precondition!(dst, $ty);
178                let out: MaybeUninit<Self>;
179
180                // SAFETY: the caller must uphold the safety contract.
181                // XCHG has SeqCst semantics.
182                unsafe {
183                    asm!(
184                        concat!("xchg ", $ptr_size, " ptr [{dst", ptr_modifier!(), "}], {val", $val_modifier, "}"), // atomic { _x = *dst; *dst = val; val = _x }
185                        dst = in(reg) dst,
186                        val = inout($val_reg) val => out,
187                        options(nostack, preserves_flags),
188                    );
189                }
190                out
191            }
192        }
193        #[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))]
194        impl AtomicCompareExchange for $ty {
195            #[inline]
196            unsafe fn __atomic_compare_exchange_impl(
197                dst: *mut MaybeUninit<Self>,
198                old: MaybeUninit<Self>,
199                new: MaybeUninit<Self>,
200                _success: Ordering,
201                _failure: Ordering,
202            ) -> (MaybeUninit<Self>, bool) {
203                debug_assert_atomic_unsafe_precondition!(dst, $ty);
204                let out: MaybeUninit<Self>;
205
206                // SAFETY: the caller must uphold the safety contract.
207                // CMPXCHG has SeqCst semantics.
208                //
209                // Refs: https://www.felixcloutier.com/x86/cmpxchg
210                unsafe {
211                    let r: MaybeUninit<u32>;
212                    asm!(
213                        concat!("lock cmpxchg ", $ptr_size, " ptr [{dst", ptr_modifier!(), "}], {new", $reg_val_modifier, "}"), // atomic { if *dst == $cmpxchg_cmp_reg { ZF = 1; *dst = new } else { ZF = 0; $cmpxchg_cmp_reg = *dst } }
214                        "sete {r:l}",                                                                                           // r = ZF
215                        dst = in(reg) dst,
216                        // Avoid reg_byte ($val_reg) in new and r to work around cranelift bug with multiple or lateout reg_byte.
217                        new = in($ux_reg) crate::utils::extend32::$ty::$ux(new),
218                        r = lateout($r_reg) r,
219                        inout($cmpxchg_cmp_reg) old => out,
220                        // Do not use `preserves_flags` because CMPXCHG modifies the ZF, CF, PF, AF, SF, and OF flags.
221                        options(nostack),
222                    );
223                    let r = crate::utils::extend32::u8::extract(r).assume_init();
224                    crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test
225                    (out, r != 0)
226                }
227            }
228        }
229    };
230}
231
232#[cfg(target_arch = "x86")]
233atomic!(u8, reg_byte, reg_abcd, uninit, "zx", "", ":l", ":e", "byte", "al");
234#[cfg(target_arch = "x86_64")]
235atomic!(u8, reg_byte, reg, uninit, "zx", "", ":l", ":e", "byte", "al");
236atomic!(u16, reg, reg, identity, "zx", ":x", ":x", ":e", "word", "ax");
237atomic!(u32, reg, reg, identity, "", ":e", ":e", ":e", "dword", "eax");
238#[cfg(target_arch = "x86_64")]
239atomic!(u64, reg, reg, identity, "", "", "", "", "qword", "rax");
240
241// -----------------------------------------------------------------------------
242// 64-bit atomics on x86_32
243//
244// For load/store, we can use MOVQ(SSE2)/MOVLPS(SSE)/FILD&FISTP(x87) instead of CMPXCHG8B.
245// Refs: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/llvm/test/CodeGen/X86/atomic-load-store-wide.ll
246
247#[cfg(all(target_arch = "x86", not(atomic_maybe_uninit_no_cmpxchg8b)))]
248delegate_signed!(delegate_all, u64);
249#[cfg(all(target_arch = "x86", not(atomic_maybe_uninit_no_cmpxchg8b)))]
250impl AtomicLoad for u64 {
251    #[inline]
252    unsafe fn atomic_load(src: *const MaybeUninit<Self>, _order: Ordering) -> MaybeUninit<Self> {
253        debug_assert_atomic_unsafe_precondition!(src, u64);
254
255        #[cfg(all(target_feature = "sse2", not(atomic_maybe_uninit_test_prefer_x87_over_sse)))]
256        // SAFETY: the caller must uphold the safety contract.
257        // cfg guarantees that the CPU supports SSE.
258        // load by MOVQ has SeqCst semantics.
259        //
260        // Refs:
261        // - https://www.felixcloutier.com/x86/movq (SSE2)
262        // - https://www.felixcloutier.com/x86/movd:movq (SSE2)
263        unsafe {
264            let out;
265            asm!(
266                "movq {out}, qword ptr [{src}]", // atomic { out[:] = *src }
267                src = in(reg) src,
268                out = out(xmm_reg) out,
269                options(nostack, preserves_flags),
270            );
271            mem::transmute::<MaybeUninit<__m128i>, [MaybeUninit<Self>; 2]>(out)[0]
272        }
273        #[cfg(all(
274            not(target_feature = "sse2"),
275            target_feature = "sse",
276            not(atomic_maybe_uninit_test_prefer_x87_over_sse),
277        ))]
278        // SAFETY: the caller must uphold the safety contract.
279        // cfg guarantees that the CPU supports SSE.
280        // load by MOVLPS has SeqCst semantics.
281        //
282        // Refs:
283        // - https://www.felixcloutier.com/x86/movlps (SSE)
284        unsafe {
285            let out;
286            asm!(
287                "movlps {out}, qword ptr [{src}]", // atomic { out[:] = *src }
288                src = in(reg) src,
289                out = out(xmm_reg) out,
290                options(nostack, preserves_flags),
291            );
292            mem::transmute::<MaybeUninit<__m128>, [MaybeUninit<Self>; 2]>(out)[0]
293        }
294        #[cfg(all(
295            any(not(target_feature = "sse"), atomic_maybe_uninit_test_prefer_x87_over_sse),
296            all(
297                any(target_feature = "x87", atomic_maybe_uninit_target_feature = "x87"),
298                not(atomic_maybe_uninit_test_prefer_cmpxchg8b_over_x87),
299            ),
300        ))]
301        // SAFETY: the caller must uphold the safety contract.
302        // load by FILD has SeqCst semantics.
303        //
304        // Refs:
305        // - https://www.felixcloutier.com/x86/fild
306        // - https://www.felixcloutier.com/x86/fist:fistp
307        unsafe {
308            let mut out = MaybeUninit::<Self>::uninit();
309            asm!(
310                "fild qword ptr [{src}]",  // atomic { st.push(*src) }
311                "fistp qword ptr [{out}]", // *out = st.pop()
312                src = in(reg) src,
313                out = in(reg) out.as_mut_ptr(),
314                out("st(0)") _,
315                out("st(1)") _,
316                out("st(2)") _,
317                out("st(3)") _,
318                out("st(4)") _,
319                out("st(5)") _,
320                out("st(6)") _,
321                out("st(7)") _,
322                // Do not use `preserves_flags` because FILD and FISTP modify C1 in x87 FPU status word.
323                options(nostack),
324            );
325            out
326        }
327        #[cfg(all(
328            any(not(target_feature = "sse"), atomic_maybe_uninit_test_prefer_x87_over_sse),
329            not(all(
330                any(target_feature = "x87", atomic_maybe_uninit_target_feature = "x87"),
331                not(atomic_maybe_uninit_test_prefer_cmpxchg8b_over_x87),
332            )),
333        ))]
334        // SAFETY: the caller must uphold the safety contract.
335        // CMPXCHG8B has SeqCst semantics.
336        //
337        // Refs: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b
338        unsafe {
339            let (out_lo, out_hi);
340            asm!(
341                "lock cmpxchg8b qword ptr [edi]", // atomic { if *edi == edx:eax { ZF = 1; *edi = ecx:ebx } else { ZF = 0; edx:eax = *edi } }
342                // set old/new args of CMPXCHG8B to 0
343                in("ebx") 0_u32,
344                in("ecx") 0_u32,
345                inout("eax") 0_u32 => out_lo,
346                inout("edx") 0_u32 => out_hi,
347                in("edi") src,
348                // Do not use `preserves_flags` because CMPXCHG8B modifies the ZF flag.
349                options(nostack),
350            );
351            MaybeUninit64 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
352        }
353    }
354}
355#[cfg(all(target_arch = "x86", not(atomic_maybe_uninit_no_cmpxchg8b)))]
356impl AtomicStore for u64 {
357    #[inline]
358    unsafe fn __atomic_store_impl(
359        dst: *mut MaybeUninit<Self>,
360        val: MaybeUninit<Self>,
361        order: Ordering,
362    ) {
363        debug_assert_atomic_unsafe_precondition!(dst, u64);
364
365        #[cfg(all(target_feature = "sse", not(atomic_maybe_uninit_test_prefer_x87_over_sse)))]
366        // SAFETY: the caller must uphold the safety contract.
367        // cfg guarantees that the CPU supports SSE.
368        //
369        // Refs:
370        // - https://www.felixcloutier.com/x86/movlps (SSE)
371        // - https://www.felixcloutier.com/x86/lock
372        // - https://www.felixcloutier.com/x86/or
373        unsafe {
374            let val: MaybeUninit<__m128> = mem::transmute([val, MaybeUninit::uninit()]);
375            match order {
376                // Relaxed and Release stores are equivalent.
377                Ordering::Relaxed | Ordering::Release => {
378                    asm!(
379                        "movlps qword ptr [{dst}], {val}", // atomic { *dst = val[:] }
380                        dst = in(reg) dst,
381                        val = in(xmm_reg) val,
382                        options(nostack, preserves_flags),
383                    );
384                }
385                Ordering::SeqCst => {
386                    let p = core::cell::UnsafeCell::new(MaybeUninit::<u32>::uninit());
387                    asm!(
388                        "movlps qword ptr [{dst}], {val}", // atomic { *dst = val[:] }
389                        // Equivalent to `mfence`, but is up to 3.1x faster on Coffee Lake and up to 2.4x faster on Raptor Lake-H at least in simple cases.
390                        // - https://github.com/taiki-e/portable-atomic/pull/156
391                        // - LLVM uses `lock or` https://godbolt.org/z/vv6rjzfYd
392                        // - Windows uses `xchg` for x86_32 for MemoryBarrier https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-memorybarrier
393                        // - MSVC STL uses `lock inc` https://github.com/microsoft/STL/pull/740
394                        // - boost uses `lock or` https://github.com/boostorg/atomic/commit/559eba81af71386cedd99f170dc6101c6ad7bf22
395                        "xchg dword ptr [{p}], {tmp}",     // fence
396                        dst = in(reg) dst,
397                        val = in(xmm_reg) val,
398                        p = inout(reg) p.get() => _,
399                        tmp = lateout(reg) _,
400                        options(nostack, preserves_flags),
401                    );
402                }
403                _ => crate::utils::unreachable_unchecked(),
404            }
405        }
406        #[cfg(all(
407            any(not(target_feature = "sse"), atomic_maybe_uninit_test_prefer_x87_over_sse),
408            all(
409                any(target_feature = "x87", atomic_maybe_uninit_target_feature = "x87"),
410                not(atomic_maybe_uninit_test_prefer_cmpxchg8b_over_x87),
411            ),
412        ))]
413        // SAFETY: the caller must uphold the safety contract.
414        //
415        // Refs:
416        // - https://www.felixcloutier.com/x86/fild
417        // - https://www.felixcloutier.com/x86/fist:fistp
418        unsafe {
419            match order {
420                // Relaxed and Release stores are equivalent.
421                Ordering::Relaxed | Ordering::Release => {
422                    asm!(
423                        "fild qword ptr [{val}]",  // st.push(*val)
424                        "fistp qword ptr [{dst}]", // atomic { *dst = st.pop() }
425                        dst = in(reg) dst,
426                        val = in(reg) val.as_ptr(),
427                        out("st(0)") _,
428                        out("st(1)") _,
429                        out("st(2)") _,
430                        out("st(3)") _,
431                        out("st(4)") _,
432                        out("st(5)") _,
433                        out("st(6)") _,
434                        out("st(7)") _,
435                        // Do not use `preserves_flags` because FILD and FISTP modify condition code flags in x87 FPU status word.
436                        options(nostack),
437                    );
438                }
439                Ordering::SeqCst => {
440                    let p = core::cell::UnsafeCell::new(MaybeUninit::<u32>::uninit());
441                    asm!(
442                        "fild qword ptr [{val}]",      // st.push(*val)
443                        "fistp qword ptr [{dst}]",     // atomic { *dst = st.pop() }
444                        // Equivalent to `mfence`, but is up to 3.1x faster on Coffee Lake and up to 2.4x faster on Raptor Lake-H at least in simple cases.
445                        // - https://github.com/taiki-e/portable-atomic/pull/156
446                        // - LLVM uses `lock or` https://godbolt.org/z/vv6rjzfYd
447                        // - Windows uses `xchg` for x86_32 for MemoryBarrier https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-memorybarrier
448                        // - MSVC STL uses `lock inc` https://github.com/microsoft/STL/pull/740
449                        // - boost uses `lock or` https://github.com/boostorg/atomic/commit/559eba81af71386cedd99f170dc6101c6ad7bf22
450                        "xchg dword ptr [{p}], {tmp}", // fence
451                        dst = in(reg) dst,
452                        val = in(reg) val.as_ptr(),
453                        p = inout(reg) p.get() => _,
454                        tmp = lateout(reg) _,
455                        out("st(0)") _,
456                        out("st(1)") _,
457                        out("st(2)") _,
458                        out("st(3)") _,
459                        out("st(4)") _,
460                        out("st(5)") _,
461                        out("st(6)") _,
462                        out("st(7)") _,
463                        // Do not use `preserves_flags` because FILD and FISTP modify condition code flags in x87 FPU status word.
464                        options(nostack),
465                    );
466                }
467                _ => crate::utils::unreachable_unchecked(),
468            }
469        }
470        #[cfg(all(
471            any(not(target_feature = "sse"), atomic_maybe_uninit_test_prefer_x87_over_sse),
472            not(all(
473                any(target_feature = "x87", atomic_maybe_uninit_target_feature = "x87"),
474                not(atomic_maybe_uninit_test_prefer_cmpxchg8b_over_x87),
475            )),
476        ))]
477        // SAFETY: the caller must uphold the safety contract.
478        unsafe {
479            // CMPXCHG8B has SeqCst semantics.
480            let _ = order;
481            <Self as AtomicSwap>::__atomic_swap_impl(dst, val, Ordering::SeqCst);
482        }
483    }
484}
485#[cfg(all(target_arch = "x86", not(atomic_maybe_uninit_no_cmpxchg8b)))]
486impl AtomicSwap for u64 {
487    #[inline]
488    unsafe fn __atomic_swap_impl(
489        dst: *mut MaybeUninit<Self>,
490        val: MaybeUninit<Self>,
491        _order: Ordering,
492    ) -> MaybeUninit<Self> {
493        debug_assert_atomic_unsafe_precondition!(dst, u64);
494        let val = MaybeUninit64 { whole: val };
495        let (mut prev_lo, mut prev_hi);
496
497        // SAFETY: the caller must uphold the safety contract.
498        // CMPXCHG8B has SeqCst semantics.
499        //
500        // Refs: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b
501        unsafe {
502            asm!(
503                // This is based on the code generated for the first load in DW RMWs by LLVM,
504                // but it is interesting that they generate code that does mixed-sized atomic access.
505                //
506                // This is not single-copy atomic reads, but this is ok because subsequent
507                // CAS will check for consistency.
508                "mov eax, dword ptr [edi]",           // atomic { eax = *edi }
509                "mov edx, dword ptr [edi + 4]",       // atomic { edx = *edi.byte_add(4) }
510                "2:", // 'retry:
511                    "lock cmpxchg8b qword ptr [edi]", // atomic { if *edi == edx:eax { ZF = 1; *edi = ecx:ebx } else { ZF = 0; edx:eax = *edi } }
512                    "jne 2b",                         // if ZF == 0 { jump 'retry }
513                in("ebx") val.pair.lo,
514                in("ecx") val.pair.hi,
515                out("eax") prev_lo,
516                out("edx") prev_hi,
517                in("edi") dst,
518                // Do not use `preserves_flags` because CMPXCHG8B modifies the ZF flag.
519                options(nostack),
520            );
521            MaybeUninit64 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
522        }
523    }
524}
525#[cfg(all(target_arch = "x86", not(atomic_maybe_uninit_no_cmpxchg8b)))]
526impl AtomicCompareExchange for u64 {
527    #[inline]
528    unsafe fn __atomic_compare_exchange_impl(
529        dst: *mut MaybeUninit<Self>,
530        old: MaybeUninit<Self>,
531        new: MaybeUninit<Self>,
532        _success: Ordering,
533        _failure: Ordering,
534    ) -> (MaybeUninit<Self>, bool) {
535        debug_assert_atomic_unsafe_precondition!(dst, u64);
536        let old = MaybeUninit64 { whole: old };
537        let new = MaybeUninit64 { whole: new };
538        let (prev_lo, prev_hi);
539        let r: u8;
540
541        // SAFETY: the caller must uphold the safety contract.
542        // CMPXCHG8B has SeqCst semantics.
543        //
544        // Refs: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b
545        unsafe {
546            asm!(
547                "lock cmpxchg8b qword ptr [edi]", // atomic { if *edi == edx:eax { ZF = 1; *edi = ecx:ebx } else { ZF = 0; edx:eax = *edi } }
548                "sete cl",                        // cl = ZF
549                in("ebx") new.pair.lo,
550                in("ecx") new.pair.hi,
551                inout("eax") old.pair.lo => prev_lo,
552                inout("edx") old.pair.hi => prev_hi,
553                in("edi") dst,
554                lateout("cl") r,
555                // Do not use `preserves_flags` because CMPXCHG8B modifies the ZF flag.
556                options(nostack),
557            );
558            crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test
559            (MaybeUninit64 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole, r != 0)
560        }
561    }
562}
563
564// -----------------------------------------------------------------------------
565// 128-bit atomics on x86_64
566
567#[cfg(target_arch = "x86_64")]
568#[cfg(target_feature = "cmpxchg16b")]
569macro_rules! atomic128 {
570    () => {
571        // rdi and rsi are call-preserved on Windows.
572        #[cfg(not(windows))]
573        #[cfg(target_pointer_width = "32")]
574        atomic128!("edi", "esi", "rsi");
575        #[cfg(not(windows))]
576        #[cfg(target_pointer_width = "64")]
577        atomic128!("rdi", "rsi", "rsi");
578        #[cfg(windows)]
579        #[cfg(target_pointer_width = "32")]
580        atomic128!("r9d", "r11d", "r8");
581        #[cfg(windows)]
582        #[cfg(target_pointer_width = "64")]
583        atomic128!("r9", "r11", "r8");
584    };
585    ($dst:tt, $cas_dst:tt, $save:tt) => {
586        delegate_signed!(delegate_all, u128);
587        impl AtomicLoad for u128 {
588            #[inline]
589            unsafe fn atomic_load(
590                src: *const MaybeUninit<Self>,
591                _order: Ordering,
592            ) -> MaybeUninit<Self> {
593                // VMOVDQA is atomic when AVX is available.
594                // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104688 for details.
595                //
596                // Refs: https://www.felixcloutier.com/x86/movdqa:vmovdqa32:vmovdqa64
597                #[cfg(not(all(
598                    not(target_feature = "avx"),
599                    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
600                )))]
601                #[target_feature(enable = "avx")]
602                #[inline]
603                unsafe fn atomic_load_avx(
604                    src: *const MaybeUninit<u128>,
605                ) -> MaybeUninit<u128> {
606                    // SAFETY: the caller must guarantee that `src` is valid for reads,
607                    // 16-byte aligned, and that there are no concurrent non-atomic operations.
608                    // load by VMOVDQA has SeqCst semantics.
609                    unsafe {
610                        let out;
611                        asm!(
612                            concat!("vmovdqa {out}, xmmword ptr [{src", ptr_modifier!(), "}]"), // atomic { out = *src }
613                            src = in(reg) src,
614                            out = lateout(xmm_reg) out,
615                            options(nostack, preserves_flags),
616                        );
617                        mem::transmute::<MaybeUninit<__m128i>, MaybeUninit<u128>>(out)
618                    }
619                }
620                #[cfg(not(target_feature = "avx"))]
621                #[inline]
622                unsafe fn atomic_load_cmpxchg16b(
623                    src: *const MaybeUninit<u128>,
624                ) -> MaybeUninit<u128> {
625                    // SAFETY: the caller must guarantee that `src` is valid for both writes and
626                    // reads, 16-byte aligned, and that there are no concurrent non-atomic operations.
627                    // CMPXCHG16B has SeqCst semantics.
628                    //
629                    // Refs: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b
630                    unsafe {
631                        let (out_lo, out_hi);
632                        asm!(
633                            concat!("mov ", $save, ", rbx"), // save rbx which is reserved by LLVM
634                            "xor rbx, rbx", // zeroed rbx
635                            concat!("lock cmpxchg16b xmmword ptr [", $dst, "]"), // atomic { if *$rdi == rdx:rax { ZF = 1; *$rdi = rcx:rbx } else { ZF = 0; rdx:rax = *$rdi } }
636                            concat!("mov rbx, ", $save), // restore rbx
637                            // set old/new args of CMPXCHG16B to 0 (rbx is zeroed after saved to rbx_tmp, to avoid xchg)
638                            out($save) _,
639                            in("rcx") 0_u64,
640                            inout("rax") 0_u64 => out_lo,
641                            inout("rdx") 0_u64 => out_hi,
642                            in($dst) src,
643                            // Do not use `preserves_flags` because CMPXCHG16B modifies the ZF flag.
644                            options(nostack),
645                        );
646                        MaybeUninit128 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
647                    }
648                }
649                debug_assert_atomic_unsafe_precondition!(src, u128);
650
651                #[cfg(target_feature = "avx")]
652                // SAFETY: the caller must uphold the safety contract.
653                // cfg guarantees that the CPU supports AVX.
654                unsafe {
655                    atomic_load_avx(src)
656                }
657                #[cfg(not(target_feature = "avx"))]
658                #[cfg(not(all(
659                    not(target_feature = "avx"),
660                    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
661                )))]
662                // SAFETY: the caller must uphold the safety contract.
663                // cfg guarantees that the CPU supports CMPXCHG16B.
664                unsafe {
665                    ifunc!(unsafe fn(src: *const MaybeUninit<u128>) -> MaybeUninit<u128> {
666                        if detect::detect().avx() {
667                            atomic_load_avx
668                        } else {
669                            atomic_load_cmpxchg16b
670                        }
671                    })
672                }
673                #[cfg(not(target_feature = "avx"))]
674                #[cfg(all(
675                    not(target_feature = "avx"),
676                    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
677                ))]
678                // SAFETY: the caller must uphold the safety contract.
679                // cfg guarantees that the CPU supports CMPXCHG16B.
680                unsafe {
681                    atomic_load_cmpxchg16b(src)
682                }
683            }
684        }
685        impl AtomicStore for u128 {
686            #[inline]
687            unsafe fn __atomic_store_impl(
688                dst: *mut MaybeUninit<Self>,
689                val: MaybeUninit<Self>,
690                order: Ordering,
691            ) {
692                // VMOVDQA is atomic when AVX is available.
693                // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104688 for details.
694                //
695                // Refs: https://www.felixcloutier.com/x86/movdqa:vmovdqa32:vmovdqa64
696                #[cfg(not(all(
697                    not(target_feature = "avx"),
698                    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
699                )))]
700                #[target_feature(enable = "avx")]
701                #[inline]
702                unsafe fn atomic_store_avx(
703                    dst: *mut MaybeUninit<u128>,
704                    val: MaybeUninit<u128>,
705                    order: Ordering,
706                ) {
707                    // SAFETY: the caller must guarantee that `dst` is valid for writes,
708                    // 16-byte aligned, and that there are no concurrent non-atomic operations.
709                    // cfg guarantees that the CPU supports AVX.
710                    unsafe {
711                        let val: MaybeUninit<__m128i> = mem::transmute(val);
712                        match order {
713                            // Relaxed and Release stores are equivalent.
714                            Ordering::Relaxed | Ordering::Release => {
715                                asm!(
716                                    concat!("vmovdqa xmmword ptr [{dst", ptr_modifier!(), "}], {val}"), // atomic { *dst = val }
717                                    dst = in(reg) dst,
718                                    val = in(xmm_reg) val,
719                                    options(nostack, preserves_flags),
720                                );
721                            }
722                            Ordering::SeqCst => {
723                                let p = core::cell::UnsafeCell::new(MaybeUninit::<u64>::uninit());
724                                asm!(
725                                    concat!("vmovdqa xmmword ptr [{dst", ptr_modifier!(), "}], {val}"), // atomic { *dst = val }
726                                    // Equivalent to `mfence`, but is up to 3.1x faster on Coffee Lake and up to 2.4x faster on Raptor Lake-H at least in simple cases.
727                                    // - https://github.com/taiki-e/portable-atomic/pull/156
728                                    // - LLVM uses `lock or` https://godbolt.org/z/vv6rjzfYd
729                                    // - Windows uses `xchg` for x86_32 for MemoryBarrier https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-memorybarrier
730                                    // - MSVC STL uses `lock inc` https://github.com/microsoft/STL/pull/740
731                                    // - boost uses `lock or` https://github.com/boostorg/atomic/commit/559eba81af71386cedd99f170dc6101c6ad7bf22
732                                    concat!("xchg qword ptr [{p", ptr_modifier!(), "}], {tmp}"),        // fence
733                                    dst = in(reg) dst,
734                                    val = in(xmm_reg) val,
735                                    p = in(reg) p.get(),
736                                    tmp = out(reg) _,
737                                    options(nostack, preserves_flags),
738                                );
739                            }
740                            _ => crate::utils::unreachable_unchecked(),
741                        }
742                    }
743                }
744                #[cfg(not(target_feature = "avx"))]
745                #[inline]
746                unsafe fn atomic_store_cmpxchg16b(
747                    dst: *mut MaybeUninit<u128>,
748                    val: MaybeUninit<u128>,
749                ) {
750                    // SAFETY: the caller must uphold the safety contract.
751                    unsafe {
752                        // CMPXCHG16B has SeqCst semantics.
753                        <u128 as AtomicSwap>::__atomic_swap_impl(dst, val, Ordering::SeqCst);
754                    }
755                }
756                debug_assert_atomic_unsafe_precondition!(dst, u128);
757
758                #[cfg(target_feature = "avx")]
759                // SAFETY: the caller must uphold the safety contract.
760                // cfg guarantees that the CPU supports AVX.
761                unsafe {
762                    atomic_store_avx(dst, val, order);
763                }
764                #[cfg(not(target_feature = "avx"))]
765                #[cfg(not(all(
766                    not(target_feature = "avx"),
767                    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
768                )))]
769                // SAFETY: the caller must uphold the safety contract.
770                // cfg guarantees that the CPU supports CMPXCHG16B.
771                unsafe {
772                    fn_alias! {
773                        #[target_feature(enable = "avx")]
774                        unsafe fn(dst: *mut MaybeUninit<u128>, val: MaybeUninit<u128>);
775                        // atomic store by vmovdqa has at least release semantics.
776                        atomic_store_avx_non_seqcst = atomic_store_avx(Ordering::Release);
777                        atomic_store_avx_seqcst = atomic_store_avx(Ordering::SeqCst);
778                    }
779                    match order {
780                        // Relaxed and Release stores are equivalent in all implementations
781                        // that may be called here.
782                        Ordering::Relaxed | Ordering::Release => {
783                            ifunc!(unsafe fn(dst: *mut MaybeUninit<u128>, val: MaybeUninit<u128>) {
784                                if detect::detect().avx() {
785                                    atomic_store_avx_non_seqcst
786                                } else {
787                                    atomic_store_cmpxchg16b
788                                }
789                            });
790                        }
791                        Ordering::SeqCst => {
792                            ifunc!(unsafe fn(dst: *mut MaybeUninit<u128>, val: MaybeUninit<u128>) {
793                                if detect::detect().avx() {
794                                    atomic_store_avx_seqcst
795                                } else {
796                                    atomic_store_cmpxchg16b
797                                }
798                            });
799                        }
800                        _ => crate::utils::unreachable_unchecked(),
801                    }
802                }
803                #[cfg(not(target_feature = "avx"))]
804                #[cfg(all(
805                    not(target_feature = "avx"),
806                    any(atomic_maybe_uninit_no_outline_atomics, target_env = "sgx", not(target_feature = "sse")),
807                ))]
808                // SAFETY: the caller must uphold the safety contract.
809                // cfg guarantees that the CPU supports CMPXCHG16B.
810                unsafe {
811                    // CMPXCHG16B has SeqCst semantics.
812                    let _ = order;
813                    atomic_store_cmpxchg16b(dst, val);
814                }
815            }
816        }
817        impl AtomicSwap for u128 {
818            #[inline]
819            unsafe fn __atomic_swap_impl(
820                dst: *mut MaybeUninit<Self>,
821                val: MaybeUninit<Self>,
822                _order: Ordering,
823            ) -> MaybeUninit<Self> {
824                debug_assert_atomic_unsafe_precondition!(dst, u128);
825                let val = MaybeUninit128 { whole: val };
826                let (mut prev_lo, mut prev_hi);
827
828                // SAFETY: the caller must guarantee that `dst` is valid for both writes and
829                // reads, 16-byte aligned, and that there are no concurrent non-atomic operations.
830                // cfg guarantees that the CPU supports CMPXCHG16B.
831                // CMPXCHG16B has SeqCst semantics.
832                //
833                // Refs: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b
834                unsafe {
835                    asm!(
836                        concat!("xchg ", $save, ", rbx"), // save rbx which is reserved by LLVM
837                        // This is based on the code generated for the first load in DW RMWs by LLVM,
838                        // but it is interesting that they generate code that does mixed-sized atomic access.
839                        //
840                        // This is not single-copy atomic reads, but this is ok because subsequent
841                        // CAS will check for consistency.
842                        concat!("mov rax, qword ptr [", $dst, "]"),              // atomic { rax = *$rdi }
843                        concat!("mov rdx, qword ptr [", $dst, " + 8]"),          // atomic { rdx = *$rdi.byte_add(8) }
844                        "2:", // 'retry:
845                            concat!("lock cmpxchg16b xmmword ptr [", $dst, "]"), // atomic { if *$rdi == rdx:rax { ZF = 1; *$rdi = rcx:rbx } else { ZF = 0; rdx:rax = *$rdi } }
846                            "jne 2b",                                            // if ZF == 0 { jump 'retry }
847                        concat!("mov rbx, ", $save), // restore rbx
848                        inout($save) val.pair.lo => _,
849                        in("rcx") val.pair.hi,
850                        out("rax") prev_lo,
851                        out("rdx") prev_hi,
852                        in($dst) dst,
853                        // Do not use `preserves_flags` because CMPXCHG16B modifies the ZF flag.
854                        options(nostack),
855                    );
856                    MaybeUninit128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
857                }
858            }
859        }
860        impl AtomicCompareExchange for u128 {
861            #[inline]
862            unsafe fn __atomic_compare_exchange_impl(
863                dst: *mut MaybeUninit<Self>,
864                old: MaybeUninit<Self>,
865                new: MaybeUninit<Self>,
866                _success: Ordering,
867                _failure: Ordering,
868            ) -> (MaybeUninit<Self>, bool) {
869                debug_assert_atomic_unsafe_precondition!(dst, u128);
870                let old = MaybeUninit128 { whole: old };
871                let new = MaybeUninit128 { whole: new };
872                let (prev_lo, prev_hi);
873                let r: u8;
874
875                // SAFETY: the caller must guarantee that `dst` is valid for both writes and
876                // reads, 16-byte aligned, and that there are no concurrent non-atomic operations.
877                // cfg guarantees that the CPU supports CMPXCHG16B.
878                // CMPXCHG16B has SeqCst semantics.
879                //
880                // Refs: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b
881                unsafe {
882                    asm!(
883                        "xchg r8, rbx", // save rbx which is reserved by LLVM
884                        concat!("lock cmpxchg16b xmmword ptr [", $cas_dst, "]"), // atomic { if *$rdi == rdx:rax { ZF = 1; *$rdi = rcx:rbx } else { ZF = 0; rdx:rax = *$rdi } }
885                        "sete cl",                                               // cl = ZF
886                        "mov rbx, r8", // restore rbx
887                        inout("r8") new.pair.lo => _,
888                        in("rcx") new.pair.hi,
889                        inout("rax") old.pair.lo => prev_lo,
890                        inout("rdx") old.pair.hi => prev_hi,
891                        in($cas_dst) dst,
892                        lateout("cl") r,
893                        // Do not use `preserves_flags` because CMPXCHG16B modifies the ZF flag.
894                        options(nostack),
895                    );
896                    crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test
897                    (
898                        MaybeUninit128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole,
899                        r != 0
900                    )
901                }
902            }
903        }
904    };
905}
906
907#[cfg(target_arch = "x86_64")]
908#[cfg(target_feature = "cmpxchg16b")]
909atomic128!();
910
911// -----------------------------------------------------------------------------
912// cfg macros
913
914#[macro_export]
915macro_rules! cfg_has_atomic_8 {
916    ($($tt:tt)*) => { $($tt)* };
917}
918#[macro_export]
919macro_rules! cfg_no_atomic_8 {
920    ($($tt:tt)*) => {};
921}
922#[macro_export]
923macro_rules! cfg_has_atomic_16 {
924    ($($tt:tt)*) => { $($tt)* };
925}
926#[macro_export]
927macro_rules! cfg_no_atomic_16 {
928    ($($tt:tt)*) => {};
929}
930#[macro_export]
931macro_rules! cfg_has_atomic_32 {
932    ($($tt:tt)*) => { $($tt)* };
933}
934#[macro_export]
935macro_rules! cfg_no_atomic_32 {
936    ($($tt:tt)*) => {};
937}
938#[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg8b)))]
939#[macro_export]
940macro_rules! cfg_has_atomic_64 {
941    ($($tt:tt)*) => { $($tt)* };
942}
943#[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg8b)))]
944#[macro_export]
945macro_rules! cfg_no_atomic_64 {
946    ($($tt:tt)*) => {};
947}
948#[cfg(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg8b))]
949#[macro_export]
950macro_rules! cfg_has_atomic_64 {
951    ($($tt:tt)*) => {};
952}
953#[cfg(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg8b))]
954#[macro_export]
955macro_rules! cfg_no_atomic_64 {
956    ($($tt:tt)*) => { $($tt)* };
957}
958#[cfg(not(all(target_arch = "x86_64", target_feature = "cmpxchg16b")))]
959#[macro_export]
960macro_rules! cfg_has_atomic_128 {
961    ($($tt:tt)*) => {};
962}
963#[cfg(not(all(target_arch = "x86_64", target_feature = "cmpxchg16b")))]
964#[macro_export]
965macro_rules! cfg_no_atomic_128 {
966    ($($tt:tt)*) => { $($tt)* };
967}
968#[cfg(all(target_arch = "x86_64", target_feature = "cmpxchg16b"))]
969#[macro_export]
970macro_rules! cfg_has_atomic_128 {
971    ($($tt:tt)*) => { $($tt)* };
972}
973#[cfg(all(target_arch = "x86_64", target_feature = "cmpxchg16b"))]
974#[macro_export]
975macro_rules! cfg_no_atomic_128 {
976    ($($tt:tt)*) => {};
977}
978#[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))]
979#[macro_export]
980macro_rules! cfg_has_atomic_cas {
981    ($($tt:tt)*) => { $($tt)* };
982}
983#[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))]
984#[macro_export]
985macro_rules! cfg_no_atomic_cas {
986    ($($tt:tt)*) => {};
987}
988#[cfg(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg))]
989#[macro_export]
990macro_rules! cfg_has_atomic_cas {
991    ($($tt:tt)*) => {};
992}
993#[cfg(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg))]
994#[macro_export]
995macro_rules! cfg_no_atomic_cas {
996    ($($tt:tt)*) => { $($tt)* };
997}