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