1 /* Simple atomic operations for multithreading.
2 Copyright (C) 2020-2025 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> */
60 InterlockedCompareExchangePointer ((void * volatile *) vp
,
61 (void *) newval
, (void *) cmp
);
65 /* Some other platform that supports multi-threading.
67 We don't use the C11 <stdatomic.h> (available in GCC >= 4.9) because it would
68 require to link with -latomic. */
70 # if (((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) \
71 || __clang_major__ >= 3) \
72 && HAVE_ATOMIC_COMPARE_AND_SWAP_GCC41)
73 /* Use GCC built-ins (available on many platforms with GCC >= 4.1 or
76 <https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html> */
81 __sync_synchronize ();
85 atomic_compare_and_swap (unsigned int volatile *vp
,
89 return __sync_val_compare_and_swap (vp
, cmp
, newval
);
93 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
97 return __sync_val_compare_and_swap (vp
, cmp
, newval
);
102 /* For older versions of GCC or xlc, use inline assembly.
103 __compare_and_swap and __compare_and_swaplp are not sufficient here. */
106 memory_barrier (void)
108 asm volatile ("sync");
112 atomic_compare_and_swap (unsigned int volatile *vp
,
116 asm volatile ("sync");
120 # if defined __GNUC__ || defined __clang__
127 # else /* another label syntax */
128 ".L01: lwarx %0,0,%1\n"
136 : "r" (vp
), "r" (cmp
), "r" (newval
)
139 asm volatile ("isync");
144 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
148 asm volatile ("sync");
152 # if defined __GNUC__ || defined __clang__
153 # if defined __powerpc64__ || defined __LP64__
168 # else /* another label syntax */
169 # if defined __powerpc64__ || defined __LP64__
170 ".L01: ldarx %0,0,%1\n"
177 ".L01: lwarx %0,0,%1\n"
186 : "r" (vp
), "r" (cmp
), "r" (newval
)
189 asm volatile ("isync");
193 # elif ((defined __GNUC__ || defined __clang__ || defined __SUNPRO_C) && (defined __sparc || defined __i386 || defined __x86_64__)) || (defined __TINYC__ && (defined __i386 || defined __x86_64__))
194 /* For older versions of GCC or clang, use inline assembly.
195 GCC, clang, and the Oracle Studio C 12 compiler understand GCC's extended
196 asm syntax, but the plain Oracle Studio C 11 compiler understands only
200 memory_barrier (void)
202 # if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
203 # if defined __i386 || defined __x86_64__
204 # if defined __TINYC__ && defined __i386
205 /* Cannot use the SSE instruction "mfence" with this compiler. */
206 asm volatile ("lock orl $0,(%esp)");
208 asm volatile ("mfence");
212 asm volatile ("membar 2");
215 # if defined __i386 || defined __x86_64__
225 atomic_compare_and_swap (unsigned int volatile *vp
,
229 # if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
231 # if defined __i386 || defined __x86_64__
232 asm volatile (" lock\n cmpxchgl %3,(%1)"
233 : "=a" (oldval
) : "r" (vp
), "a" (cmp
), "r" (newval
) : "memory");
236 asm volatile (" cas [%1],%2,%3\n"
238 : "=r" (oldval
) : "r" (vp
), "r" (cmp
), "r" (newval
) : "memory");
241 # else /* __SUNPRO_C */
242 # if defined __x86_64__
243 asm (" movl %esi,%eax\n"
244 " lock\n cmpxchgl %edx,(%rdi)");
245 # elif defined __i386
246 asm (" movl 16(%ebp),%ecx\n"
247 " movl 12(%ebp),%eax\n"
248 " movl 8(%ebp),%edx\n"
249 " lock\n cmpxchgl %ecx,(%edx)");
252 asm (" cas [%i0],%i1,%i2\n"
259 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
263 # if defined __GNUC__ || defined __clang__ || __SUNPRO_C >= 0x590 || defined __TINYC__
265 # if defined __x86_64__
266 asm volatile (" lock\n cmpxchgq %3,(%1)"
267 : "=a" (oldval
) : "r" (vp
), "a" (cmp
), "r" (newval
) : "memory");
268 # elif defined __i386
269 asm volatile (" lock\n cmpxchgl %3,(%1)"
270 : "=a" (oldval
) : "r" (vp
), "a" (cmp
), "r" (newval
) : "memory");
272 # if defined __sparc && (defined __sparcv9 || defined __arch64__)
273 asm volatile (" casx [%1],%2,%3\n"
275 : "=r" (oldval
) : "r" (vp
), "r" (cmp
), "r" (newval
) : "memory");
276 # elif defined __sparc
277 asm volatile (" cas [%1],%2,%3\n"
279 : "=r" (oldval
) : "r" (vp
), "r" (cmp
), "r" (newval
) : "memory");
282 # else /* __SUNPRO_C */
283 # if defined __x86_64__
284 asm (" movq %rsi,%rax\n"
285 " lock\n cmpxchgq %rdx,(%rdi)");
286 # elif defined __i386
287 asm (" movl 16(%ebp),%ecx\n"
288 " movl 12(%ebp),%eax\n"
289 " movl 8(%ebp),%edx\n"
290 " lock\n cmpxchgl %ecx,(%edx)");
292 # if defined __sparc && (defined __sparcv9 || defined __arch64__)
293 asm (" casx [%i0],%i1,%i2\n"
295 # elif defined __sparc
296 asm (" cas [%i0],%i1,%i2\n"
303 /* Fallback code. It has some race conditions. The unit test will fail. */
306 memory_barrier (void)
311 atomic_compare_and_swap (unsigned int volatile *vp
,
315 unsigned int oldval
= *vp
;
322 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
326 uintptr_t oldval
= *vp
;
335 /* A platform that does not support multi-threading. */
338 memory_barrier (void)
343 atomic_compare_and_swap (unsigned int volatile *vp
,
347 unsigned int oldval
= *vp
;
354 atomic_compare_and_swap_ptr (uintptr_t volatile *vp
,
358 uintptr_t oldval
= *vp
;