"Initial commit to Gerrit"
[profile/ivi/libatomic_ops.git] / src / atomic_ops / sysdeps / gcc / powerpc.h
1 /* 
2  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
3  * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
4  * Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
5  *
6  *
7  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
9  *
10  * Permission is hereby granted to use or copy this program
11  * for any purpose,  provided the above notices are retained on all copies.
12  * Permission to modify the code and to distribute modified code is granted,
13  * provided the above notices are retained, and a notice that the code was
14  * modified is included with the above copyright notice.
15  *
16  */
17
18 /* FIXME.  Incomplete.  No support for 64 bits.                         */
19 /* Memory model documented at http://www-106.ibm.com/developerworks/    */
20 /* eserver/articles/archguide.html and (clearer)                        */
21 /* http://www-106.ibm.com/developerworks/eserver/articles/powerpc.html. */
22 /* There appears to be no implicit ordering between any kind of         */
23 /* independent memory references.                                       */
24 /* Architecture enforces some ordering based on control dependence.     */
25 /* I don't know if that could help.                                     */
26 /* Data-dependent loads are always ordered.                             */
27 /* Based on the above references, eieio is intended for use on          */
28 /* uncached memory, which we don't support.  It does not order loads    */
29 /* from cached memory.                                                  */
30 /* Thanks to Maged Michael, Doug Lea, and Roger Hoover for helping to   */
31 /* track some of this down and correcting my misunderstandings. -HB     */
32
33 #include "../all_aligned_atomic_load_store.h"
34
35 #include "../test_and_set_t_is_ao_t.h"
36         /* There seems to be no byte equivalent of lwarx, so this       */
37         /* may really be what we want, at least in the 32-bit case.     */
38
39 AO_INLINE void
40 AO_nop_full()
41 {
42   __asm__ __volatile__("sync" : : : "memory");
43 }
44
45 #define AO_HAVE_nop_full
46
47 /* lwsync apparently works for everything but a StoreLoad barrier.      */
48 AO_INLINE void
49 AO_lwsync()
50 {
51   __asm__ __volatile__("lwsync" : : : "memory");
52 }
53
54 #define AO_nop_write() AO_lwsync()
55 #define AO_HAVE_nop_write
56
57 #define AO_nop_read() AO_lwsync()
58 #define AO_HAVE_nop_read
59
60 /* We explicitly specify load_acquire, since it is important, and can   */
61 /* be implemented relatively cheaply.  It could be implemented          */
62 /* with an ordinary load followed by a lwsync.  But the general wisdom  */
63 /* seems to be that a data dependent branch followed by an isync is     */
64 /* cheaper.  And the documentation is fairly explicit that this also    */
65 /* has acquire semantics.                                               */
66 AO_INLINE AO_t
67 AO_load_acquire(volatile AO_t *addr)
68 {
69   AO_t result;
70
71   /* FIXME: We should get gcc to allocate one of the condition  */
72   /* registers.  I always got "impossible constraint" when I    */
73   /* tried the "y" constraint.                                  */
74   __asm__ __volatile__ (
75     "lwz %0,%1\n"
76     "cmpw cr7,%0,%0\n"
77     "bne- cr7,1f\n"
78     "1: isync\n"
79     : "=r" (result)
80     : "m"(*addr) : "memory", "cc");
81   return result;
82 }
83
84 #define AO_HAVE_load_acquire
85
86 /* We explicitly specify store_release, since it relies         */
87 /* on the fact that lwsync is also a LoadStore barrier.         */
88 AO_INLINE void
89 AO_store_release(volatile AO_t *addr, AO_t value)
90 {
91   AO_lwsync();
92   *addr = value;
93 }
94
95 #define AO_HAVE_load_acquire
96
97 /* This is similar to the code in the garbage collector.  Deleting      */
98 /* this and having it synthesized from compare_and_swap would probably  */
99 /* only cost us a load immediate instruction.                           */
100 #if defined(__powerpc64__) || defined(__ppc64__) || defined(__64BIT__)
101 /* Completely untested.  And we should be using smaller objects anyway. */
102 AO_INLINE AO_TS_VAL_t
103 AO_test_and_set(volatile AO_TS_t *addr) {
104   unsigned long oldval;
105   unsigned long temp = 1; /* locked value */
106
107   __asm__ __volatile__(
108                "1:ldarx %0,0,%1\n"   /* load and reserve               */
109                "cmpdi %0, 0\n"       /* if load is                     */
110                "bne 2f\n"            /*   non-zero, return already set */
111                "stdcx. %2,0,%1\n"    /* else store conditional         */
112                "bne- 1b\n"           /* retry if lost reservation      */
113                "2:\n"                /* oldval is zero if we set       */
114               : "=&r"(oldval)
115               : "r"(addr), "r"(temp)
116               : "memory", "cc");
117
118   return oldval;
119 }
120
121 #else
122
123 AO_INLINE AO_TS_VAL_t
124 AO_test_and_set(volatile AO_TS_t *addr) {
125   int oldval;
126   int temp = 1; /* locked value */
127
128   __asm__ __volatile__(
129                "1:lwarx %0,0,%1\n"   /* load and reserve               */
130                "cmpwi %0, 0\n"       /* if load is                     */
131                "bne 2f\n"            /*   non-zero, return already set */
132                "stwcx. %2,0,%1\n"    /* else store conditional         */
133                "bne- 1b\n"           /* retry if lost reservation      */
134                "2:\n"                /* oldval is zero if we set       */
135               : "=&r"(oldval)
136               : "r"(addr), "r"(temp)
137               : "memory", "cc");
138
139   return oldval;
140 }
141
142 #endif
143
144 #define AO_have_test_and_set
145
146 AO_INLINE AO_TS_VAL_t
147 AO_test_and_set_acquire(volatile AO_TS_t *addr) {
148   AO_TS_VAL_t result = AO_test_and_set(addr);
149   AO_lwsync();
150   return result;
151 }
152
153 #define AO_HAVE_test_and_set_acquire
154
155 AO_INLINE AO_TS_VAL_t
156 AO_test_and_set_release(volatile AO_TS_t *addr) {
157   AO_lwsync();
158   return AO_test_and_set(addr);
159 }
160
161 #define AO_HAVE_test_and_set_release
162
163 AO_INLINE AO_TS_VAL_t
164 AO_test_and_set_full(volatile AO_TS_t *addr) {
165   AO_TS_VAL_t result;
166   AO_lwsync();
167   result = AO_test_and_set(addr);
168   AO_lwsync();
169   return result;
170 }
171
172 #define AO_HAVE_test_and_set_full
173
174 #if defined(__powerpc64__) || defined(__ppc64__) || defined(__64BIT__)
175 /* FIXME: Completely untested.  */
176 AO_INLINE int
177 AO_compare_and_swap(volatile AO_t *addr, AO_t old, AO_t new_val) {
178   AO_t oldval;
179   int result = 0;
180
181   __asm__ __volatile__(
182                "1:ldarx %0,0,%2\n"   /* load and reserve              */
183                "cmpd %0, %4\n"      /* if load is not equal to  */
184                "bne 2f\n"            /*   old, fail                     */
185                "stdcx. %3,0,%2\n"    /* else store conditional         */
186                "bne- 1b\n"           /* retry if lost reservation      */
187                "li %1,1\n"           /* result = 1;                     */
188                "2:\n"
189               : "=&r"(oldval), "=&r"(result)
190               : "r"(addr), "r"(new_val), "r"(old), "1"(result)
191               : "memory", "cc");
192
193   return result;
194 }
195
196 #else
197
198 AO_INLINE int
199 AO_compare_and_swap(volatile AO_t *addr, AO_t old, AO_t new_val) {
200   AO_t oldval;
201   int result = 0;
202
203   __asm__ __volatile__(
204                "1:lwarx %0,0,%2\n"   /* load and reserve              */
205                "cmpw %0, %4\n"      /* if load is not equal to  */
206                "bne 2f\n"            /*   old, fail                     */
207                "stwcx. %3,0,%2\n"    /* else store conditional         */
208                "bne- 1b\n"           /* retry if lost reservation      */
209                "li %1,1\n"           /* result = 1;                     */
210                "2:\n"
211               : "=&r"(oldval), "=&r"(result)
212               : "r"(addr), "r"(new_val), "r"(old), "1"(result)
213               : "memory", "cc");
214
215   return result;
216 }
217 #endif
218
219 #define AO_HAVE_compare_and_swap
220
221 AO_INLINE int
222 AO_compare_and_swap_acquire(volatile AO_t *addr, AO_t old, AO_t new_val) {
223   int result = AO_compare_and_swap(addr, old, new_val);
224   AO_lwsync();
225   return result;
226 }
227
228 #define AO_HAVE_compare_and_swap_acquire
229
230 AO_INLINE int
231 AO_compare_and_swap_release(volatile AO_t *addr, AO_t old, AO_t new_val) {
232   AO_lwsync();
233   return AO_compare_and_swap(addr, old, new_val);
234 }
235
236 #define AO_HAVE_compare_and_swap_release
237
238 AO_INLINE int
239 AO_compare_and_swap_full(volatile AO_t *addr, AO_t old, AO_t new_val) {
240   AO_t result;
241   AO_lwsync();
242   result = AO_compare_and_swap(addr, old, new_val);
243   AO_lwsync();
244   return result;
245 }
246
247 #define AO_HAVE_compare_and_swap_full
248
249 /* FIXME: We should also implement fetch_and_add and or primitives      */
250 /* directly.                                                            */