OSDN Git Service

* pa/linux-atomic.c (EBUSY): Define if not _LP64.
[pf3gnuchains/gcc-fork.git] / gcc / config / pa / linux-atomic.c
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>
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 2, or (at your option) any later
11 version.
12
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
20 executable.)
21
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
25 for more details.
26
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
30 02110-1301, USA.  */
31
32 /* FIXME: work around build failure for hppa64-linux-gnu target. */
33 #ifndef _LP64
34 #include <errno.h>
35 #else 
36 #define EFAULT  14 
37 #define EBUSY   16
38 #define ENOSYS 251 
39 #endif 
40
41 /* All PA-RISC implementations supported by linux have strongly
42    ordered loads and stores.  Only cache flushes and purges can be
43    delayed.  The data cache implementations are all globally
44    coherent.  Thus, there is no need to synchonize memory accesses.
45
46    GCC automatically issues a asm memory barrier when it encounters
47    a __sync_synchronize builtin.  Thus, we do not need to define this
48    builtin.
49
50    We implement byte, short and int versions of each atomic operation
51    using the kernel helper defined below.  There is no support for
52    64-bit operations yet.  */
53
54 /* A privileged instruction to crash a userspace program with SIGILL.  */
55 #define ABORT_INSTRUCTION asm ("iitlbp %r0,(%sr0, %r0)")
56
57 /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace).  */
58 #define LWS_CAS (sizeof(unsigned long) == 4 ? 0 : 1)
59
60 /* Kernel helper for compare-and-exchange a 32-bit value.  */
61 static inline long
62 __kernel_cmpxchg (int oldval, int newval, int *mem)
63 {
64   register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
65   register long lws_ret   asm("r28");
66   register long lws_errno asm("r21");
67   register int lws_old asm("r25") = oldval;
68   register int lws_new asm("r24") = newval;
69   asm volatile (        "ble    0xb0(%%sr2, %%r0)       \n\t"
70                         "ldi    %5, %%r20               \n\t"
71         : "=r" (lws_ret), "=r" (lws_errno), "=r" (lws_mem),
72           "=r" (lws_old), "=r" (lws_new)
73         : "i" (LWS_CAS), "2" (lws_mem), "3" (lws_old), "4" (lws_new)
74         : "r1", "r20", "r22", "r23", "r29", "r31", "memory"
75   );
76   if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
77     ABORT_INSTRUCTION;
78
79   /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains
80      the old value from memory.  If this value is equal to OLDVAL, the
81      new value was written to memory.  If not, return -EBUSY.  */
82   if (!lws_errno && lws_ret != oldval)
83     lws_errno = -EBUSY;
84
85   return lws_errno;
86 }
87
88 #define HIDDEN __attribute__ ((visibility ("hidden")))
89
90 /* Big endian masks  */
91 #define INVERT_MASK_1 24
92 #define INVERT_MASK_2 16
93
94 #define MASK_1 0xffu
95 #define MASK_2 0xffffu
96
97 #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)                           \
98   int HIDDEN                                                            \
99   __sync_fetch_and_##OP##_4 (int *ptr, int val)                         \
100   {                                                                     \
101     int failure, tmp;                                                   \
102                                                                         \
103     do {                                                                \
104       tmp = *ptr;                                                       \
105       failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);     \
106     } while (failure != 0);                                             \
107                                                                         \
108     return tmp;                                                         \
109   }
110
111 FETCH_AND_OP_WORD (add,   , +)
112 FETCH_AND_OP_WORD (sub,   , -)
113 FETCH_AND_OP_WORD (or,    , |)
114 FETCH_AND_OP_WORD (and,   , &)
115 FETCH_AND_OP_WORD (xor,   , ^)
116 FETCH_AND_OP_WORD (nand, ~, &)
117
118 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
119 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
120
121 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
122    subword-sized quantities.  */
123
124 #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)        \
125   TYPE HIDDEN                                                           \
126   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)                     \
127   {                                                                     \
128     int *wordptr = (int *) ((unsigned long) ptr & ~3);                  \
129     unsigned int mask, shift, oldval, newval;                           \
130     int failure;                                                        \
131                                                                         \
132     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;     \
133     mask = MASK_##WIDTH << shift;                                       \
134                                                                         \
135     do {                                                                \
136       oldval = *wordptr;                                                \
137       newval = ((PFX_OP ((oldval & mask) >> shift)                      \
138                  INF_OP (unsigned int) val) << shift) & mask;           \
139       newval |= oldval & ~mask;                                         \
140       failure = __kernel_cmpxchg (oldval, newval, wordptr);             \
141     } while (failure != 0);                                             \
142                                                                         \
143     return (RETURN & mask) >> shift;                                    \
144   }
145
146 SUBWORD_SYNC_OP (add,   , +, short, 2, oldval)
147 SUBWORD_SYNC_OP (sub,   , -, short, 2, oldval)
148 SUBWORD_SYNC_OP (or,    , |, short, 2, oldval)
149 SUBWORD_SYNC_OP (and,   , &, short, 2, oldval)
150 SUBWORD_SYNC_OP (xor,   , ^, short, 2, oldval)
151 SUBWORD_SYNC_OP (nand, ~, &, short, 2, oldval)
152
153 SUBWORD_SYNC_OP (add,   , +, char, 1, oldval)
154 SUBWORD_SYNC_OP (sub,   , -, char, 1, oldval)
155 SUBWORD_SYNC_OP (or,    , |, char, 1, oldval)
156 SUBWORD_SYNC_OP (and,   , &, char, 1, oldval)
157 SUBWORD_SYNC_OP (xor,   , ^, char, 1, oldval)
158 SUBWORD_SYNC_OP (nand, ~, &, char, 1, oldval)
159
160 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)                           \
161   int HIDDEN                                                            \
162   __sync_##OP##_and_fetch_4 (int *ptr, int val)                         \
163   {                                                                     \
164     int tmp, failure;                                                   \
165                                                                         \
166     do {                                                                \
167       tmp = *ptr;                                                       \
168       failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);     \
169     } while (failure != 0);                                             \
170                                                                         \
171     return PFX_OP tmp INF_OP val;                                       \
172   }
173
174 OP_AND_FETCH_WORD (add,   , +)
175 OP_AND_FETCH_WORD (sub,   , -)
176 OP_AND_FETCH_WORD (or,    , |)
177 OP_AND_FETCH_WORD (and,   , &)
178 OP_AND_FETCH_WORD (xor,   , ^)
179 OP_AND_FETCH_WORD (nand, ~, &)
180
181 SUBWORD_SYNC_OP (add,   , +, short, 2, newval)
182 SUBWORD_SYNC_OP (sub,   , -, short, 2, newval)
183 SUBWORD_SYNC_OP (or,    , |, short, 2, newval)
184 SUBWORD_SYNC_OP (and,   , &, short, 2, newval)
185 SUBWORD_SYNC_OP (xor,   , ^, short, 2, newval)
186 SUBWORD_SYNC_OP (nand, ~, &, short, 2, newval)
187
188 SUBWORD_SYNC_OP (add,   , +, char, 1, newval)
189 SUBWORD_SYNC_OP (sub,   , -, char, 1, newval)
190 SUBWORD_SYNC_OP (or,    , |, char, 1, newval)
191 SUBWORD_SYNC_OP (and,   , &, char, 1, newval)
192 SUBWORD_SYNC_OP (xor,   , ^, char, 1, newval)
193 SUBWORD_SYNC_OP (nand, ~, &, char, 1, newval)
194
195 int HIDDEN
196 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
197 {
198   int actual_oldval, fail;
199     
200   while (1)
201     {
202       actual_oldval = *ptr;
203
204       if (oldval != actual_oldval)
205         return actual_oldval;
206
207       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
208   
209       if (!fail)
210         return oldval;
211     }
212 }
213
214 #define SUBWORD_VAL_CAS(TYPE, WIDTH)                                    \
215   TYPE HIDDEN                                                           \
216   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,          \
217                                        TYPE newval)                     \
218   {                                                                     \
219     int *wordptr = (int *)((unsigned long) ptr & ~3), fail;             \
220     unsigned int mask, shift, actual_oldval, actual_newval;             \
221                                                                         \
222     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;     \
223     mask = MASK_##WIDTH << shift;                                       \
224                                                                         \
225     while (1)                                                           \
226       {                                                                 \
227         actual_oldval = *wordptr;                                       \
228                                                                         \
229         if (((actual_oldval & mask) >> shift) != (unsigned int) oldval) \
230           return (actual_oldval & mask) >> shift;                       \
231                                                                         \
232         actual_newval = (actual_oldval & ~mask)                         \
233                         | (((unsigned int) newval << shift) & mask);    \
234                                                                         \
235         fail = __kernel_cmpxchg (actual_oldval, actual_newval,          \
236                                  wordptr);                              \
237                                                                         \
238         if (!fail)                                                      \
239           return oldval;                                                \
240       }                                                                 \
241   }
242
243 SUBWORD_VAL_CAS (short, 2)
244 SUBWORD_VAL_CAS (char,  1)
245
246 typedef unsigned char bool;
247
248 bool HIDDEN
249 __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
250 {
251   int failure = __kernel_cmpxchg (oldval, newval, ptr);
252   return (failure == 0);
253 }
254
255 #define SUBWORD_BOOL_CAS(TYPE, WIDTH)                                   \
256   bool HIDDEN                                                           \
257   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,         \
258                                         TYPE newval)                    \
259   {                                                                     \
260     TYPE actual_oldval                                                  \
261       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);      \
262     return (oldval == actual_oldval);                                   \
263   }
264
265 SUBWORD_BOOL_CAS (short, 2)
266 SUBWORD_BOOL_CAS (char,  1)
267
268 int HIDDEN
269 __sync_lock_test_and_set_4 (int *ptr, int val)
270 {
271   int failure, oldval;
272
273   do {
274     oldval = *ptr;
275     failure = __kernel_cmpxchg (oldval, val, ptr);
276   } while (failure != 0);
277
278   return oldval;
279 }
280
281 #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)                               \
282   TYPE HIDDEN                                                           \
283   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)                \
284   {                                                                     \
285     int failure;                                                        \
286     unsigned int oldval, newval, shift, mask;                           \
287     int *wordptr = (int *) ((unsigned long) ptr & ~3);                  \
288                                                                         \
289     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;     \
290     mask = MASK_##WIDTH << shift;                                       \
291                                                                         \
292     do {                                                                \
293       oldval = *wordptr;                                                \
294       newval = (oldval & ~mask)                                         \
295                | (((unsigned int) val << shift) & mask);                \
296       failure = __kernel_cmpxchg (oldval, newval, wordptr);             \
297     } while (failure != 0);                                             \
298                                                                         \
299     return (oldval & mask) >> shift;                                    \
300   }
301
302 SUBWORD_TEST_AND_SET (short, 2)
303 SUBWORD_TEST_AND_SET (char,  1)
304
305 #define SYNC_LOCK_RELEASE(TYPE, WIDTH)                                  \
306   void HIDDEN                                                           \
307   __sync_lock_release_##WIDTH (TYPE *ptr)                               \
308   {                                                                     \
309     *ptr = 0;                                                           \
310   }
311
312 SYNC_LOCK_RELEASE (int,   4)
313 SYNC_LOCK_RELEASE (short, 2)
314 SYNC_LOCK_RELEASE (char,  1)