1 /* Linux-specific atomic operations for PA Linux.
2 Copyright (C) 2008 Free Software Foundation, Inc.
3 Based on code contributed by CodeSourcery for ARM EABI Linux.
4 Modifications for PA Linux by Helge Deller <deller@gmx.de>
6 This file is part of GCC.
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 In addition to the permissions in the GNU General Public License, the
14 Free Software Foundation gives you unlimited permission to link the
15 compiled version of this file into combinations with other programs,
16 and to distribute those combinations without any restriction coming
17 from the use of this file. (The General Public License restrictions
18 do apply in other respects; for example, they cover modification of
19 the file, and distribution when not linked into a combine
22 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
23 WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 You should have received a copy of the GNU General Public License
28 along with GCC; see the file COPYING. If not, write to the Free
29 Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
32 /* FIXME: work around build failure for hppa64-linux-gnu target. */
40 /* All PA-RISC implementations supported by linux have strongly
41 ordered loads and stores. Only cache flushes and purges can be
42 delayed. The data cache implementations are all globally
43 coherent. Thus, there is no need to synchonize memory accesses.
45 GCC automatically issues a asm memory barrier when it encounters
46 a __sync_synchronize builtin. Thus, we do not need to define this
49 We implement byte, short and int versions of each atomic operation
50 using the kernel helper defined below. There is no support for
51 64-bit operations yet. */
53 /* A privileged instruction to crash a userspace program with SIGILL. */
54 #define ABORT_INSTRUCTION asm ("iitlbp %r0,(%sr0, %r0)")
56 /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace). */
57 #define LWS_CAS (sizeof(unsigned long) == 4 ? 0 : 1)
59 /* Kernel helper for compare-and-exchange a 32-bit value. */
61 __kernel_cmpxchg (int oldval, int newval, int *mem)
63 register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
64 register long lws_ret asm("r28");
65 register long lws_errno asm("r21");
66 register int lws_old asm("r25") = oldval;
67 register int lws_new asm("r24") = newval;
68 asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t"
70 : "=r" (lws_ret), "=r" (lws_errno), "=r" (lws_mem),
71 "=r" (lws_old), "=r" (lws_new)
72 : "i" (LWS_CAS), "2" (lws_mem), "3" (lws_old), "4" (lws_new)
73 : "r1", "r20", "r22", "r23", "r29", "r31", "memory"
75 if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
80 #define HIDDEN __attribute__ ((visibility ("hidden")))
82 /* Big endian masks */
83 #define INVERT_MASK_1 24
84 #define INVERT_MASK_2 16
87 #define MASK_2 0xffffu
89 #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP) \
91 __sync_fetch_and_##OP##_4 (int *ptr, int val) \
97 failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr); \
98 } while (failure != 0); \
103 FETCH_AND_OP_WORD (add, , +)
104 FETCH_AND_OP_WORD (sub, , -)
105 FETCH_AND_OP_WORD (or, , |)
106 FETCH_AND_OP_WORD (and, , &)
107 FETCH_AND_OP_WORD (xor, , ^)
108 FETCH_AND_OP_WORD (nand, ~, &)
110 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
111 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
113 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
114 subword-sized quantities. */
116 #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \
118 NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val) \
120 int *wordptr = (int *) ((unsigned long) ptr & ~3); \
121 unsigned int mask, shift, oldval, newval; \
124 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
125 mask = MASK_##WIDTH << shift; \
129 newval = ((PFX_OP ((oldval & mask) >> shift) \
130 INF_OP (unsigned int) val) << shift) & mask; \
131 newval |= oldval & ~mask; \
132 failure = __kernel_cmpxchg (oldval, newval, wordptr); \
133 } while (failure != 0); \
135 return (RETURN & mask) >> shift; \
138 SUBWORD_SYNC_OP (add, , +, short, 2, oldval)
139 SUBWORD_SYNC_OP (sub, , -, short, 2, oldval)
140 SUBWORD_SYNC_OP (or, , |, short, 2, oldval)
141 SUBWORD_SYNC_OP (and, , &, short, 2, oldval)
142 SUBWORD_SYNC_OP (xor, , ^, short, 2, oldval)
143 SUBWORD_SYNC_OP (nand, ~, &, short, 2, oldval)
145 SUBWORD_SYNC_OP (add, , +, char, 1, oldval)
146 SUBWORD_SYNC_OP (sub, , -, char, 1, oldval)
147 SUBWORD_SYNC_OP (or, , |, char, 1, oldval)
148 SUBWORD_SYNC_OP (and, , &, char, 1, oldval)
149 SUBWORD_SYNC_OP (xor, , ^, char, 1, oldval)
150 SUBWORD_SYNC_OP (nand, ~, &, char, 1, oldval)
152 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP) \
154 __sync_##OP##_and_fetch_4 (int *ptr, int val) \
160 failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr); \
161 } while (failure != 0); \
163 return PFX_OP tmp INF_OP val; \
166 OP_AND_FETCH_WORD (add, , +)
167 OP_AND_FETCH_WORD (sub, , -)
168 OP_AND_FETCH_WORD (or, , |)
169 OP_AND_FETCH_WORD (and, , &)
170 OP_AND_FETCH_WORD (xor, , ^)
171 OP_AND_FETCH_WORD (nand, ~, &)
173 SUBWORD_SYNC_OP (add, , +, short, 2, newval)
174 SUBWORD_SYNC_OP (sub, , -, short, 2, newval)
175 SUBWORD_SYNC_OP (or, , |, short, 2, newval)
176 SUBWORD_SYNC_OP (and, , &, short, 2, newval)
177 SUBWORD_SYNC_OP (xor, , ^, short, 2, newval)
178 SUBWORD_SYNC_OP (nand, ~, &, short, 2, newval)
180 SUBWORD_SYNC_OP (add, , +, char, 1, newval)
181 SUBWORD_SYNC_OP (sub, , -, char, 1, newval)
182 SUBWORD_SYNC_OP (or, , |, char, 1, newval)
183 SUBWORD_SYNC_OP (and, , &, char, 1, newval)
184 SUBWORD_SYNC_OP (xor, , ^, char, 1, newval)
185 SUBWORD_SYNC_OP (nand, ~, &, char, 1, newval)
188 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
190 int actual_oldval, fail;
194 actual_oldval = *ptr;
196 if (oldval != actual_oldval)
197 return actual_oldval;
199 fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
206 #define SUBWORD_VAL_CAS(TYPE, WIDTH) \
208 __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
211 int *wordptr = (int *)((unsigned long) ptr & ~3), fail; \
212 unsigned int mask, shift, actual_oldval, actual_newval; \
214 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
215 mask = MASK_##WIDTH << shift; \
219 actual_oldval = *wordptr; \
221 if (((actual_oldval & mask) >> shift) != (unsigned int) oldval) \
222 return (actual_oldval & mask) >> shift; \
224 actual_newval = (actual_oldval & ~mask) \
225 | (((unsigned int) newval << shift) & mask); \
227 fail = __kernel_cmpxchg (actual_oldval, actual_newval, \
235 SUBWORD_VAL_CAS (short, 2)
236 SUBWORD_VAL_CAS (char, 1)
238 typedef unsigned char bool;
241 __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
243 int failure = __kernel_cmpxchg (oldval, newval, ptr);
244 return (failure == 0);
247 #define SUBWORD_BOOL_CAS(TYPE, WIDTH) \
249 __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
253 = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval); \
254 return (oldval == actual_oldval); \
257 SUBWORD_BOOL_CAS (short, 2)
258 SUBWORD_BOOL_CAS (char, 1)
261 __sync_lock_test_and_set_4 (int *ptr, int val)
267 failure = __kernel_cmpxchg (oldval, val, ptr);
268 } while (failure != 0);
273 #define SUBWORD_TEST_AND_SET(TYPE, WIDTH) \
275 __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val) \
278 unsigned int oldval, newval, shift, mask; \
279 int *wordptr = (int *) ((unsigned long) ptr & ~3); \
281 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
282 mask = MASK_##WIDTH << shift; \
286 newval = (oldval & ~mask) \
287 | (((unsigned int) val << shift) & mask); \
288 failure = __kernel_cmpxchg (oldval, newval, wordptr); \
289 } while (failure != 0); \
291 return (oldval & mask) >> shift; \
294 SUBWORD_TEST_AND_SET (short, 2)
295 SUBWORD_TEST_AND_SET (char, 1)
297 #define SYNC_LOCK_RELEASE(TYPE, WIDTH) \
299 __sync_lock_release_##WIDTH (TYPE *ptr) \
304 SYNC_LOCK_RELEASE (int, 4)
305 SYNC_LOCK_RELEASE (short, 2)
306 SYNC_LOCK_RELEASE (char, 1)