1 /* Simple atomic operations for multithreading.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2021. */
22 #include "simple-atomic.h"
24 #if 0x590 <= __SUNPRO_C && __STDC__
28 #if defined _WIN32 && ! defined __CYGWIN__
37 <https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-memorybarrier> */
42 atomic_compare_and_swap (unsigned int volatile *vp
,
46 /* InterlockedCompareExchange
47 <https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchange> */
48 return InterlockedCompareExchange ((LONG
volatile *) vp
,
49 (LONG
) newval
, (LONG
) cmp
);
53 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
57 /* InterlockedCompareExchangePointer
58 <https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedcompareexchangepointer> */
59 return InterlockedCompareExchangePointer ((void * volatile *) vp
,
60 (void *) newval
, (void *) cmp
);
64 /* Some other platform that supports multi-threading.
66 We don't use the C11 <stdatomic.h> (available in GCC >= 4.9) because it would
67 require to link with -latomic. */
69 # if (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \
70 || __clang_major__ >= 3) \
71 && HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41)
72 /* Use GCC built-ins (available on many platforms with GCC >= 4.1 or
75 <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */
80 __sync_synchronize ();
84 atomic_compare_and_swap (unsigned int volatile *vp
,
88 return __sync_val_compare_and_swap (vp
, cmp
, newval
);
92 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
96 return __sync_val_compare_and_swap (vp
, cmp
, newval
);
101 /* For older versions of GCC or xlc, use inline assembly.
102 __compare_and_swap and __compare_and_swaplp are not sufficient here. */
105 memory_barrier (void)
107 asm volatile ("sync");
111 atomic_compare_and_swap (unsigned int volatile *vp
,
115 asm volatile ("sync");
119 # if defined __GNUC__ || defined __clang__
126 # else /* another label syntax */
127 ".L01: lwarx %0,0,%1\n"
135 : "r" (vp
), "r" (cmp
), "r" (newval
)
138 asm volatile ("isync");
143 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
147 asm volatile ("sync");
151 # if defined __GNUC__ || defined __clang__
152 # if defined __powerpc64__ || defined __LP64__
167 # else /* another label syntax */
168 # if defined __powerpc64__ || defined __LP64__
169 ".L01: ldarx %0,0,%1\n"
176 ".L01: lwarx %0,0,%1\n"
185 : "r" (vp
), "r" (cmp
), "r" (newval
)
188 asm volatile ("isync");
192 # elif ((defined __GNUC__ || defined __clang__ || defined __SUNPRO_C) && (defined __sparc || defined __i386 || defined __x86_64__)) || (defined __TINYC__ && (defined __i386 || defined __x86_64__))
193 /* For older versions of GCC or clang, use inline assembly.
194 GCC, clang, and the Oracle Studio C 12 compiler understand GCC's extended
195 asm syntax, but the plain Oracle Studio C 11 compiler understands only
199 memory_barrier (void)
201 # if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
202 # if defined __i386 || defined __x86_64__
203 # if defined __TINYC__ && defined __i386
204 /* Cannot use the SSE instruction "mfence" with this compiler. */
205 asm volatile ("lock orl $0,(%esp)");
207 asm volatile ("mfence");
211 asm volatile ("membar 2");
214 # if defined __i386 || defined __x86_64__
224 atomic_compare_and_swap (unsigned int volatile *vp
,
228 # if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
230 # if defined __i386 || defined __x86_64__
231 asm volatile (" lock\n cmpxchgl %3,(%1)"
232 : "=a" (oldval
) : "r" (vp
), "a" (cmp
), "r" (newval
) : "memory");
235 asm volatile (" cas [%1],%2,%3\n"
237 : "=r" (oldval
) : "r" (vp
), "r" (cmp
), "r" (newval
) : "memory");
240 # else /* __SUNPRO_C */
241 # if defined __x86_64__
242 asm (" movl %esi,%eax\n"
243 " lock\n cmpxchgl %edx,(%rdi)");
244 # elif defined __i386
245 asm (" movl 16(%ebp),%ecx\n"
246 " movl 12(%ebp),%eax\n"
247 " movl 8(%ebp),%edx\n"
248 " lock\n cmpxchgl %ecx,(%edx)");
251 asm (" cas [%i0],%i1,%i2\n"
258 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
262 # if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
264 # if defined __x86_64__
265 asm volatile (" lock\n cmpxchgq %3,(%1)"
266 : "=a" (oldval
) : "r" (vp
), "a" (cmp
), "r" (newval
) : "memory");
267 # elif defined __i386
268 asm volatile (" lock\n cmpxchgl %3,(%1)"
269 : "=a" (oldval
) : "r" (vp
), "a" (cmp
), "r" (newval
) : "memory");
271 # if defined __sparc && (defined __sparcv9 || defined __arch64__)
272 asm volatile (" casx [%1],%2,%3\n"
274 : "=r" (oldval
) : "r" (vp
), "r" (cmp
), "r" (newval
) : "memory");
275 # elif defined __sparc
276 asm volatile (" cas [%1],%2,%3\n"
278 : "=r" (oldval
) : "r" (vp
), "r" (cmp
), "r" (newval
) : "memory");
281 # else /* __SUNPRO_C */
282 # if defined __x86_64__
283 asm (" movq %rsi,%rax\n"
284 " lock\n cmpxchgq %rdx,(%rdi)");
285 # elif defined __i386
286 asm (" movl 16(%ebp),%ecx\n"
287 " movl 12(%ebp),%eax\n"
288 " movl 8(%ebp),%edx\n"
289 " lock\n cmpxchgl %ecx,(%edx)");
291 # if defined __sparc && (defined __sparcv9 || defined __arch64__)
292 asm (" casx [%i0],%i1,%i2\n"
294 # elif defined __sparc
295 asm (" cas [%i0],%i1,%i2\n"
302 /* Fallback code. It has some race conditions. The unit test will fail. */
305 memory_barrier (void)
310 atomic_compare_and_swap (unsigned int volatile *vp
,
314 unsigned int oldval
= *vp
;
321 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
325 uintptr_t oldval
= *vp
;
334 /* A platform that does not support multi-threading. */
337 memory_barrier (void)
342 atomic_compare_and_swap (unsigned int volatile *vp
,
346 unsigned int oldval
= *vp
;
353 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
357 uintptr_t oldval
= *vp
;