linux-atomic.c revision 1.3 1 1.1 mrg /* Linux-specific atomic operations for PA Linux.
2 1.3 mrg Copyright (C) 2008-2015 Free Software Foundation, Inc.
3 1.1 mrg Based on code contributed by CodeSourcery for ARM EABI Linux.
4 1.1 mrg Modifications for PA Linux by Helge Deller <deller (at) gmx.de>
5 1.1 mrg
6 1.1 mrg This file is part of GCC.
7 1.1 mrg
8 1.1 mrg GCC is free software; you can redistribute it and/or modify it under
9 1.1 mrg the terms of the GNU General Public License as published by the Free
10 1.1 mrg Software Foundation; either version 3, or (at your option) any later
11 1.1 mrg version.
12 1.1 mrg
13 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 1.1 mrg for more details.
17 1.1 mrg
18 1.1 mrg Under Section 7 of GPL version 3, you are granted additional
19 1.1 mrg permissions described in the GCC Runtime Library Exception, version
20 1.1 mrg 3.1, as published by the Free Software Foundation.
21 1.1 mrg
22 1.1 mrg You should have received a copy of the GNU General Public License and
23 1.1 mrg a copy of the GCC Runtime Library Exception along with this program;
24 1.1 mrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 1.1 mrg <http://www.gnu.org/licenses/>. */
26 1.1 mrg
27 1.1 mrg #define EFAULT 14
28 1.1 mrg #define EBUSY 16
29 1.1 mrg #define ENOSYS 251
30 1.1 mrg
31 1.1 mrg /* All PA-RISC implementations supported by linux have strongly
32 1.1 mrg ordered loads and stores. Only cache flushes and purges can be
33 1.1 mrg delayed. The data cache implementations are all globally
34 1.1 mrg coherent. Thus, there is no need to synchonize memory accesses.
35 1.1 mrg
36 1.1 mrg GCC automatically issues a asm memory barrier when it encounters
37 1.1 mrg a __sync_synchronize builtin. Thus, we do not need to define this
38 1.1 mrg builtin.
39 1.1 mrg
40 1.1 mrg We implement byte, short and int versions of each atomic operation
41 1.1 mrg using the kernel helper defined below. There is no support for
42 1.1 mrg 64-bit operations yet. */
43 1.1 mrg
44 1.1 mrg /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace). */
45 1.3 mrg #define LWS_CAS (sizeof(long) == 4 ? 0 : 1)
46 1.1 mrg
47 1.1 mrg /* Kernel helper for compare-and-exchange a 32-bit value. */
48 1.1 mrg static inline long
49 1.3 mrg __kernel_cmpxchg (int *mem, int oldval, int newval)
50 1.1 mrg {
51 1.1 mrg register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
52 1.3 mrg register int lws_old asm("r25") = oldval;
53 1.3 mrg register int lws_new asm("r24") = newval;
54 1.1 mrg register long lws_ret asm("r28");
55 1.1 mrg register long lws_errno asm("r21");
56 1.1 mrg asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t"
57 1.3 mrg "ldi %2, %%r20 \n\t"
58 1.3 mrg : "=r" (lws_ret), "=r" (lws_errno)
59 1.3 mrg : "i" (LWS_CAS), "r" (lws_mem), "r" (lws_old), "r" (lws_new)
60 1.1 mrg : "r1", "r20", "r22", "r23", "r29", "r31", "memory"
61 1.1 mrg );
62 1.1 mrg if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
63 1.3 mrg __builtin_trap ();
64 1.1 mrg
65 1.1 mrg /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains
66 1.1 mrg the old value from memory. If this value is equal to OLDVAL, the
67 1.1 mrg new value was written to memory. If not, return -EBUSY. */
68 1.1 mrg if (!lws_errno && lws_ret != oldval)
69 1.1 mrg lws_errno = -EBUSY;
70 1.1 mrg
71 1.1 mrg return lws_errno;
72 1.1 mrg }
73 1.1 mrg
74 1.3 mrg static inline long
75 1.3 mrg __kernel_cmpxchg2 (void *mem, const void *oldval, const void *newval,
76 1.3 mrg int val_size)
77 1.3 mrg {
78 1.3 mrg register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
79 1.3 mrg register unsigned long lws_old asm("r25") = (unsigned long) oldval;
80 1.3 mrg register unsigned long lws_new asm("r24") = (unsigned long) newval;
81 1.3 mrg register int lws_size asm("r23") = val_size;
82 1.3 mrg register long lws_ret asm("r28");
83 1.3 mrg register long lws_errno asm("r21");
84 1.3 mrg asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t"
85 1.3 mrg "ldi %6, %%r20 \n\t"
86 1.3 mrg : "=r" (lws_ret), "=r" (lws_errno), "+r" (lws_mem),
87 1.3 mrg "+r" (lws_old), "+r" (lws_new), "+r" (lws_size)
88 1.3 mrg : "i" (2)
89 1.3 mrg : "r1", "r20", "r22", "r29", "r31", "fr4", "memory"
90 1.3 mrg );
91 1.3 mrg
92 1.3 mrg /* If the kernel LWS call is successful, lws_ret contains 0. */
93 1.3 mrg if (__builtin_expect (lws_ret == 0, 1))
94 1.3 mrg return 0;
95 1.3 mrg
96 1.3 mrg if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
97 1.3 mrg __builtin_trap ();
98 1.3 mrg
99 1.3 mrg /* If the kernel LWS call fails with no error, return -EBUSY */
100 1.3 mrg if (__builtin_expect (!lws_errno, 0))
101 1.3 mrg return -EBUSY;
102 1.3 mrg
103 1.3 mrg return lws_errno;
104 1.3 mrg }
105 1.1 mrg #define HIDDEN __attribute__ ((visibility ("hidden")))
106 1.1 mrg
107 1.1 mrg /* Big endian masks */
108 1.1 mrg #define INVERT_MASK_1 24
109 1.1 mrg #define INVERT_MASK_2 16
110 1.1 mrg
111 1.1 mrg #define MASK_1 0xffu
112 1.1 mrg #define MASK_2 0xffffu
113 1.1 mrg
114 1.3 mrg #define FETCH_AND_OP_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX) \
115 1.3 mrg TYPE HIDDEN \
116 1.3 mrg __sync_fetch_and_##OP##_##WIDTH (TYPE *ptr, TYPE val) \
117 1.1 mrg { \
118 1.3 mrg TYPE tmp, newval; \
119 1.3 mrg long failure; \
120 1.1 mrg \
121 1.1 mrg do { \
122 1.3 mrg tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
123 1.3 mrg newval = PFX_OP (tmp INF_OP val); \
124 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX); \
125 1.1 mrg } while (failure != 0); \
126 1.1 mrg \
127 1.1 mrg return tmp; \
128 1.1 mrg }
129 1.1 mrg
130 1.3 mrg FETCH_AND_OP_2 (add, , +, long long, 8, 3)
131 1.3 mrg FETCH_AND_OP_2 (sub, , -, long long, 8, 3)
132 1.3 mrg FETCH_AND_OP_2 (or, , |, long long, 8, 3)
133 1.3 mrg FETCH_AND_OP_2 (and, , &, long long, 8, 3)
134 1.3 mrg FETCH_AND_OP_2 (xor, , ^, long long, 8, 3)
135 1.3 mrg FETCH_AND_OP_2 (nand, ~, &, long long, 8, 3)
136 1.3 mrg
137 1.3 mrg FETCH_AND_OP_2 (add, , +, short, 2, 1)
138 1.3 mrg FETCH_AND_OP_2 (sub, , -, short, 2, 1)
139 1.3 mrg FETCH_AND_OP_2 (or, , |, short, 2, 1)
140 1.3 mrg FETCH_AND_OP_2 (and, , &, short, 2, 1)
141 1.3 mrg FETCH_AND_OP_2 (xor, , ^, short, 2, 1)
142 1.3 mrg FETCH_AND_OP_2 (nand, ~, &, short, 2, 1)
143 1.3 mrg
144 1.3 mrg FETCH_AND_OP_2 (add, , +, signed char, 1, 0)
145 1.3 mrg FETCH_AND_OP_2 (sub, , -, signed char, 1, 0)
146 1.3 mrg FETCH_AND_OP_2 (or, , |, signed char, 1, 0)
147 1.3 mrg FETCH_AND_OP_2 (and, , &, signed char, 1, 0)
148 1.3 mrg FETCH_AND_OP_2 (xor, , ^, signed char, 1, 0)
149 1.3 mrg FETCH_AND_OP_2 (nand, ~, &, signed char, 1, 0)
150 1.1 mrg
151 1.3 mrg #define OP_AND_FETCH_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX) \
152 1.1 mrg TYPE HIDDEN \
153 1.3 mrg __sync_##OP##_and_fetch_##WIDTH (TYPE *ptr, TYPE val) \
154 1.1 mrg { \
155 1.3 mrg TYPE tmp, newval; \
156 1.3 mrg long failure; \
157 1.1 mrg \
158 1.3 mrg do { \
159 1.3 mrg tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
160 1.3 mrg newval = PFX_OP (tmp INF_OP val); \
161 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX); \
162 1.3 mrg } while (failure != 0); \
163 1.3 mrg \
164 1.3 mrg return PFX_OP (tmp INF_OP val); \
165 1.3 mrg }
166 1.3 mrg
167 1.3 mrg OP_AND_FETCH_2 (add, , +, long long, 8, 3)
168 1.3 mrg OP_AND_FETCH_2 (sub, , -, long long, 8, 3)
169 1.3 mrg OP_AND_FETCH_2 (or, , |, long long, 8, 3)
170 1.3 mrg OP_AND_FETCH_2 (and, , &, long long, 8, 3)
171 1.3 mrg OP_AND_FETCH_2 (xor, , ^, long long, 8, 3)
172 1.3 mrg OP_AND_FETCH_2 (nand, ~, &, long long, 8, 3)
173 1.3 mrg
174 1.3 mrg OP_AND_FETCH_2 (add, , +, short, 2, 1)
175 1.3 mrg OP_AND_FETCH_2 (sub, , -, short, 2, 1)
176 1.3 mrg OP_AND_FETCH_2 (or, , |, short, 2, 1)
177 1.3 mrg OP_AND_FETCH_2 (and, , &, short, 2, 1)
178 1.3 mrg OP_AND_FETCH_2 (xor, , ^, short, 2, 1)
179 1.3 mrg OP_AND_FETCH_2 (nand, ~, &, short, 2, 1)
180 1.3 mrg
181 1.3 mrg OP_AND_FETCH_2 (add, , +, signed char, 1, 0)
182 1.3 mrg OP_AND_FETCH_2 (sub, , -, signed char, 1, 0)
183 1.3 mrg OP_AND_FETCH_2 (or, , |, signed char, 1, 0)
184 1.3 mrg OP_AND_FETCH_2 (and, , &, signed char, 1, 0)
185 1.3 mrg OP_AND_FETCH_2 (xor, , ^, signed char, 1, 0)
186 1.3 mrg OP_AND_FETCH_2 (nand, ~, &, signed char, 1, 0)
187 1.3 mrg
188 1.3 mrg #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP) \
189 1.3 mrg int HIDDEN \
190 1.3 mrg __sync_fetch_and_##OP##_4 (int *ptr, int val) \
191 1.3 mrg { \
192 1.3 mrg int tmp; \
193 1.3 mrg long failure; \
194 1.1 mrg \
195 1.1 mrg do { \
196 1.3 mrg tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
197 1.3 mrg failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val)); \
198 1.1 mrg } while (failure != 0); \
199 1.1 mrg \
200 1.3 mrg return tmp; \
201 1.1 mrg }
202 1.1 mrg
203 1.3 mrg FETCH_AND_OP_WORD (add, , +)
204 1.3 mrg FETCH_AND_OP_WORD (sub, , -)
205 1.3 mrg FETCH_AND_OP_WORD (or, , |)
206 1.3 mrg FETCH_AND_OP_WORD (and, , &)
207 1.3 mrg FETCH_AND_OP_WORD (xor, , ^)
208 1.3 mrg FETCH_AND_OP_WORD (nand, ~, &)
209 1.1 mrg
210 1.1 mrg #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP) \
211 1.1 mrg int HIDDEN \
212 1.1 mrg __sync_##OP##_and_fetch_4 (int *ptr, int val) \
213 1.1 mrg { \
214 1.3 mrg int tmp; \
215 1.3 mrg long failure; \
216 1.1 mrg \
217 1.1 mrg do { \
218 1.3 mrg tmp = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
219 1.3 mrg failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val)); \
220 1.1 mrg } while (failure != 0); \
221 1.1 mrg \
222 1.1 mrg return PFX_OP (tmp INF_OP val); \
223 1.1 mrg }
224 1.1 mrg
225 1.1 mrg OP_AND_FETCH_WORD (add, , +)
226 1.1 mrg OP_AND_FETCH_WORD (sub, , -)
227 1.1 mrg OP_AND_FETCH_WORD (or, , |)
228 1.1 mrg OP_AND_FETCH_WORD (and, , &)
229 1.1 mrg OP_AND_FETCH_WORD (xor, , ^)
230 1.1 mrg OP_AND_FETCH_WORD (nand, ~, &)
231 1.1 mrg
232 1.3 mrg typedef unsigned char bool;
233 1.3 mrg
234 1.3 mrg #define COMPARE_AND_SWAP_2(TYPE, WIDTH, INDEX) \
235 1.3 mrg TYPE HIDDEN \
236 1.3 mrg __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
237 1.3 mrg TYPE newval) \
238 1.3 mrg { \
239 1.3 mrg TYPE actual_oldval; \
240 1.3 mrg long fail; \
241 1.3 mrg \
242 1.3 mrg while (1) \
243 1.3 mrg { \
244 1.3 mrg actual_oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
245 1.3 mrg \
246 1.3 mrg if (__builtin_expect (oldval != actual_oldval, 0)) \
247 1.3 mrg return actual_oldval; \
248 1.3 mrg \
249 1.3 mrg fail = __kernel_cmpxchg2 (ptr, &actual_oldval, &newval, INDEX); \
250 1.3 mrg \
251 1.3 mrg if (__builtin_expect (!fail, 1)) \
252 1.3 mrg return actual_oldval; \
253 1.3 mrg } \
254 1.3 mrg } \
255 1.3 mrg \
256 1.3 mrg bool HIDDEN \
257 1.3 mrg __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
258 1.3 mrg TYPE newval) \
259 1.3 mrg { \
260 1.3 mrg long failure = __kernel_cmpxchg2 (ptr, &oldval, &newval, INDEX); \
261 1.3 mrg return (failure == 0); \
262 1.3 mrg }
263 1.3 mrg
264 1.3 mrg COMPARE_AND_SWAP_2 (long long, 8, 3)
265 1.3 mrg COMPARE_AND_SWAP_2 (short, 2, 1)
266 1.3 mrg COMPARE_AND_SWAP_2 (char, 1, 0)
267 1.1 mrg
268 1.1 mrg int HIDDEN
269 1.1 mrg __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
270 1.1 mrg {
271 1.3 mrg long fail;
272 1.3 mrg int actual_oldval;
273 1.1 mrg
274 1.1 mrg while (1)
275 1.1 mrg {
276 1.3 mrg actual_oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
277 1.1 mrg
278 1.1 mrg if (__builtin_expect (oldval != actual_oldval, 0))
279 1.1 mrg return actual_oldval;
280 1.1 mrg
281 1.3 mrg fail = __kernel_cmpxchg (ptr, actual_oldval, newval);
282 1.1 mrg
283 1.1 mrg if (__builtin_expect (!fail, 1))
284 1.1 mrg return actual_oldval;
285 1.1 mrg }
286 1.1 mrg }
287 1.1 mrg
288 1.1 mrg bool HIDDEN
289 1.1 mrg __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
290 1.1 mrg {
291 1.3 mrg long failure = __kernel_cmpxchg (ptr, oldval, newval);
292 1.1 mrg return (failure == 0);
293 1.1 mrg }
294 1.1 mrg
295 1.3 mrg #define SYNC_LOCK_TEST_AND_SET_2(TYPE, WIDTH, INDEX) \
296 1.3 mrg TYPE HIDDEN \
297 1.3 mrg __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val) \
298 1.1 mrg { \
299 1.3 mrg TYPE oldval; \
300 1.3 mrg long failure; \
301 1.3 mrg \
302 1.3 mrg do { \
303 1.3 mrg oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
304 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX); \
305 1.3 mrg } while (failure != 0); \
306 1.3 mrg \
307 1.3 mrg return oldval; \
308 1.1 mrg }
309 1.1 mrg
310 1.3 mrg SYNC_LOCK_TEST_AND_SET_2 (long long, 8, 3)
311 1.3 mrg SYNC_LOCK_TEST_AND_SET_2 (short, 2, 1)
312 1.3 mrg SYNC_LOCK_TEST_AND_SET_2 (signed char, 1, 0)
313 1.1 mrg
314 1.1 mrg int HIDDEN
315 1.1 mrg __sync_lock_test_and_set_4 (int *ptr, int val)
316 1.1 mrg {
317 1.3 mrg long failure;
318 1.3 mrg int oldval;
319 1.1 mrg
320 1.1 mrg do {
321 1.3 mrg oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
322 1.3 mrg failure = __kernel_cmpxchg (ptr, oldval, val);
323 1.1 mrg } while (failure != 0);
324 1.1 mrg
325 1.1 mrg return oldval;
326 1.1 mrg }
327 1.1 mrg
328 1.3 mrg #define SYNC_LOCK_RELEASE_2(TYPE, WIDTH, INDEX) \
329 1.3 mrg void HIDDEN \
330 1.3 mrg __sync_lock_release_##WIDTH (TYPE *ptr) \
331 1.3 mrg { \
332 1.3 mrg TYPE oldval, zero = 0; \
333 1.3 mrg long failure; \
334 1.3 mrg \
335 1.3 mrg do { \
336 1.3 mrg oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST); \
337 1.3 mrg failure = __kernel_cmpxchg2 (ptr, &oldval, &zero, INDEX); \
338 1.3 mrg } while (failure != 0); \
339 1.1 mrg }
340 1.1 mrg
341 1.3 mrg SYNC_LOCK_RELEASE_2 (long long, 8, 3)
342 1.3 mrg SYNC_LOCK_RELEASE_2 (short, 2, 1)
343 1.3 mrg SYNC_LOCK_RELEASE_2 (signed char, 1, 0)
344 1.1 mrg
345 1.3 mrg void HIDDEN
346 1.3 mrg __sync_lock_release_4 (int *ptr)
347 1.3 mrg {
348 1.3 mrg long failure;
349 1.3 mrg int oldval;
350 1.1 mrg
351 1.3 mrg do {
352 1.3 mrg oldval = __atomic_load_n (ptr, __ATOMIC_SEQ_CST);
353 1.3 mrg failure = __kernel_cmpxchg (ptr, oldval, 0);
354 1.3 mrg } while (failure != 0);
355 1.3 mrg }
356