OSDN Git Service

2008-10-26 John David Anglin <dave.anglin@nrc-cnrc.gc.ca>
[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 ENOSYS 251 
37 #define EFAULT 14 
38 #endif 
39
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.
44
45    GCC automatically issues a asm memory barrier when it encounters
46    a __sync_synchronize builtin.  Thus, we do not need to define this
47    builtin.
48
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.  */
52
53 /* A privileged instruction to crash a userspace program with SIGILL.  */
54 #define ABORT_INSTRUCTION asm ("iitlbp %r0,(%sr0, %r0)")
55
56 /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace).  */
57 #define LWS_CAS (sizeof(unsigned long) == 4 ? 0 : 1)
58
59 /* Kernel helper for compare-and-exchange a 32-bit value.  */
60 static inline long
61 __kernel_cmpxchg (int oldval, int newval, int *mem)
62 {
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"
69                         "ldi    %5, %%r20               \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"
74   );
75   if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0))
76     ABORT_INSTRUCTION;
77   return lws_errno;
78 }
79
80 #define HIDDEN __attribute__ ((visibility ("hidden")))
81
82 /* Big endian masks  */
83 #define INVERT_MASK_1 24
84 #define INVERT_MASK_2 16
85
86 #define MASK_1 0xffu
87 #define MASK_2 0xffffu
88
89 #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)                           \
90   int HIDDEN                                                            \
91   __sync_fetch_and_##OP##_4 (int *ptr, int val)                         \
92   {                                                                     \
93     int failure, tmp;                                                   \
94                                                                         \
95     do {                                                                \
96       tmp = *ptr;                                                       \
97       failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);     \
98     } while (failure != 0);                                             \
99                                                                         \
100     return tmp;                                                         \
101   }
102
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, ~, &)
109
110 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
111 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
112
113 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
114    subword-sized quantities.  */
115
116 #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)        \
117   TYPE HIDDEN                                                           \
118   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)                     \
119   {                                                                     \
120     int *wordptr = (int *) ((unsigned long) ptr & ~3);                  \
121     unsigned int mask, shift, oldval, newval;                           \
122     int failure;                                                        \
123                                                                         \
124     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;     \
125     mask = MASK_##WIDTH << shift;                                       \
126                                                                         \
127     do {                                                                \
128       oldval = *wordptr;                                                \
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);                                             \
134                                                                         \
135     return (RETURN & mask) >> shift;                                    \
136   }
137
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)
144
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)
151
152 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)                           \
153   int HIDDEN                                                            \
154   __sync_##OP##_and_fetch_4 (int *ptr, int val)                         \
155   {                                                                     \
156     int tmp, failure;                                                   \
157                                                                         \
158     do {                                                                \
159       tmp = *ptr;                                                       \
160       failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);     \
161     } while (failure != 0);                                             \
162                                                                         \
163     return PFX_OP tmp INF_OP val;                                       \
164   }
165
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, ~, &)
172
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)
179
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)
186
187 int HIDDEN
188 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
189 {
190   int actual_oldval, fail;
191     
192   while (1)
193     {
194       actual_oldval = *ptr;
195
196       if (oldval != actual_oldval)
197         return actual_oldval;
198
199       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
200   
201       if (!fail)
202         return oldval;
203     }
204 }
205
206 #define SUBWORD_VAL_CAS(TYPE, WIDTH)                                    \
207   TYPE HIDDEN                                                           \
208   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,          \
209                                        TYPE newval)                     \
210   {                                                                     \
211     int *wordptr = (int *)((unsigned long) ptr & ~3), fail;             \
212     unsigned int mask, shift, actual_oldval, actual_newval;             \
213                                                                         \
214     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;     \
215     mask = MASK_##WIDTH << shift;                                       \
216                                                                         \
217     while (1)                                                           \
218       {                                                                 \
219         actual_oldval = *wordptr;                                       \
220                                                                         \
221         if (((actual_oldval & mask) >> shift) != (unsigned int) oldval) \
222           return (actual_oldval & mask) >> shift;                       \
223                                                                         \
224         actual_newval = (actual_oldval & ~mask)                         \
225                         | (((unsigned int) newval << shift) & mask);    \
226                                                                         \
227         fail = __kernel_cmpxchg (actual_oldval, actual_newval,          \
228                                  wordptr);                              \
229                                                                         \
230         if (!fail)                                                      \
231           return oldval;                                                \
232       }                                                                 \
233   }
234
235 SUBWORD_VAL_CAS (short, 2)
236 SUBWORD_VAL_CAS (char,  1)
237
238 typedef unsigned char bool;
239
240 bool HIDDEN
241 __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
242 {
243   int failure = __kernel_cmpxchg (oldval, newval, ptr);
244   return (failure == 0);
245 }
246
247 #define SUBWORD_BOOL_CAS(TYPE, WIDTH)                                   \
248   bool HIDDEN                                                           \
249   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,         \
250                                         TYPE newval)                    \
251   {                                                                     \
252     TYPE actual_oldval                                                  \
253       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);      \
254     return (oldval == actual_oldval);                                   \
255   }
256
257 SUBWORD_BOOL_CAS (short, 2)
258 SUBWORD_BOOL_CAS (char,  1)
259
260 int HIDDEN
261 __sync_lock_test_and_set_4 (int *ptr, int val)
262 {
263   int failure, oldval;
264
265   do {
266     oldval = *ptr;
267     failure = __kernel_cmpxchg (oldval, val, ptr);
268   } while (failure != 0);
269
270   return oldval;
271 }
272
273 #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)                               \
274   TYPE HIDDEN                                                           \
275   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)                \
276   {                                                                     \
277     int failure;                                                        \
278     unsigned int oldval, newval, shift, mask;                           \
279     int *wordptr = (int *) ((unsigned long) ptr & ~3);                  \
280                                                                         \
281     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;     \
282     mask = MASK_##WIDTH << shift;                                       \
283                                                                         \
284     do {                                                                \
285       oldval = *wordptr;                                                \
286       newval = (oldval & ~mask)                                         \
287                | (((unsigned int) val << shift) & mask);                \
288       failure = __kernel_cmpxchg (oldval, newval, wordptr);             \
289     } while (failure != 0);                                             \
290                                                                         \
291     return (oldval & mask) >> shift;                                    \
292   }
293
294 SUBWORD_TEST_AND_SET (short, 2)
295 SUBWORD_TEST_AND_SET (char,  1)
296
297 #define SYNC_LOCK_RELEASE(TYPE, WIDTH)                                  \
298   void HIDDEN                                                           \
299   __sync_lock_release_##WIDTH (TYPE *ptr)                               \
300   {                                                                     \
301     *ptr = 0;                                                           \
302   }
303
304 SYNC_LOCK_RELEASE (int,   4)
305 SYNC_LOCK_RELEASE (short, 2)
306 SYNC_LOCK_RELEASE (char,  1)