1 /* Atomic operations used inside libc.  Linux/SH version.
2    Copyright (C) 2003 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <http://www.gnu.org/licenses/>.  */
18 
19 #include <stdint.h>
20 
21 
22 typedef int8_t atomic8_t;
23 typedef uint8_t uatomic8_t;
24 typedef int_fast8_t atomic_fast8_t;
25 typedef uint_fast8_t uatomic_fast8_t;
26 
27 typedef int16_t atomic16_t;
28 typedef uint16_t uatomic16_t;
29 typedef int_fast16_t atomic_fast16_t;
30 typedef uint_fast16_t uatomic_fast16_t;
31 
32 typedef int32_t atomic32_t;
33 typedef uint32_t uatomic32_t;
34 typedef int_fast32_t atomic_fast32_t;
35 typedef uint_fast32_t uatomic_fast32_t;
36 
37 typedef int64_t atomic64_t;
38 typedef uint64_t uatomic64_t;
39 typedef int_fast64_t atomic_fast64_t;
40 typedef uint_fast64_t uatomic_fast64_t;
41 
42 typedef intptr_t atomicptr_t;
43 typedef uintptr_t uatomicptr_t;
44 typedef intmax_t atomic_max_t;
45 typedef uintmax_t uatomic_max_t;
46 
47 /* SH kernel has implemented a gUSA ("g" User Space Atomicity) support
48    for the user space atomicity. The atomicity macros use this scheme.
49 
50   Reference:
51     Niibe Yutaka, "gUSA: Simple and Efficient User Space Atomicity
52     Emulation with Little Kernel Modification", Linux Conference 2002,
53     Japan. http://lc.linux.or.jp/lc2002/papers/niibe0919h.pdf (in
54     Japanese).
55 
56     Niibe Yutaka, "gUSA: User Space Atomicity with Little Kernel
57     Modification", LinuxTag 2003, Rome.
58     http://www.semmel.ch/Linuxtag-DVD/talks/170/paper.html (in English).
59 
60     B.N. Bershad, D. Redell, and J. Ellis, "Fast Mutual Exclusion for
61     Uniprocessors",  Proceedings of the Fifth Architectural Support for
62     Programming Languages and Operating Systems (ASPLOS), pp. 223-233,
63     October 1992. http://www.cs.washington.edu/homes/bershad/Papers/Rcs.ps
64 
65   SuperH ABI:
66       r15:    -(size of atomic instruction sequence) < 0
67       r0:     end point
68       r1:     saved stack pointer
69 */
70 
71 #if __GNUC_PREREQ (4, 7)
72 # define rNOSP "u"
73 #else
74 # define rNOSP "r"
75 #endif
76 
77 /* Avoid having lots of different versions of compare and exchange,
78    by having this one complicated version. Parameters:
79       bwl:     b, w or l for 8, 16 and 32 bit versions.
80       version: val or bool, depending on whether the result is the
81                previous value or a bool indicating whether the transfer
82                did happen (note this needs inverting before being
83                returned in atomic_compare_and_exchange_bool).
84 */
85 
86 #define __arch_compare_and_exchange_n(mem, newval, oldval, bwl, version) \
87   ({ signed long __arch_result; \
88      __asm__ __volatile__ ("\
89 	.align 2\n\
90 	mova 1f,r0\n\
91 	nop\n\
92 	mov r15,r1\n\
93 	mov #-8,r15\n\
94      0: mov." #bwl " @%1,%0\n\
95 	cmp/eq %0,%3\n\
96 	bf 1f\n\
97 	mov." #bwl " %2,@%1\n\
98      1: mov r1,r15\n\
99      .ifeqs \"bool\",\"" #version "\"\n\
100         movt %0\n\
101      .endif\n"					\
102 	: "=&r" (__arch_result)			\
103 	: rNOSP (mem), rNOSP (newval), rNOSP (oldval)	\
104 	: "r0", "r1", "t", "memory");		\
105      __arch_result; })
106 
107 #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
108   __arch_compare_and_exchange_n(mem, newval, (int8_t)(oldval), b, val)
109 
110 #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
111   __arch_compare_and_exchange_n(mem, newval, (int16_t)(oldval), w, val)
112 
113 #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
114   __arch_compare_and_exchange_n(mem, newval, (int32_t)(oldval), l, val)
115 
116 /* XXX We do not really need 64-bit compare-and-exchange.  At least
117    not in the moment.  Using it would mean causing portability
118    problems since not many other 32-bit architectures have support for
119    such an operation.  So don't define any code for now.  */
120 
121 # define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
122   (abort (), 0)
123 
124 /* For "bool" routines, return if the exchange did NOT occur */
125 
126 #define __arch_compare_and_exchange_bool_8_acq(mem, newval, oldval) \
127   (! __arch_compare_and_exchange_n(mem, newval, (int8_t)(oldval), b, bool))
128 
129 #define __arch_compare_and_exchange_bool_16_acq(mem, newval, oldval) \
130   (! __arch_compare_and_exchange_n(mem, newval, (int16_t)(oldval), w, bool))
131 
132 #define __arch_compare_and_exchange_bool_32_acq(mem, newval, oldval) \
133   (! __arch_compare_and_exchange_n(mem, newval, (int32_t)(oldval), l, bool))
134 
135 # define __arch_compare_and_exchange_bool_64_acq(mem, newval, oldval) \
136   (abort (), 0)
137 
138 /* Similar to the above, have one template which can be used in a
139    number of places. This version returns both the old and the new
140    values of the location. Parameters:
141       bwl:     b, w or l for 8, 16 and 32 bit versions.
142       oper:    The instruction to perform on the old value.
143    Note old is not sign extended, so should be an unsigned long.
144 */
145 
146 #define __arch_operate_old_new_n(mem, value, old, new, bwl, oper)	\
147   (void) ({ __asm__ __volatile__ ("\
148 	.align 2\n\
149 	mova 1f,r0\n\
150 	mov r15,r1\n\
151 	nop\n\
152 	mov #-8,r15\n\
153      0: mov." #bwl " @%2,%0\n\
154 	mov %0,%1\n\
155 	" #oper " %3,%1\n\
156 	mov." #bwl " %1,@%2\n\
157      1: mov r1,r15"			\
158 	: "=&r" (old), "=&r"(new)	\
159 	: rNOSP (mem), rNOSP (value)	\
160 	: "r0", "r1", "memory");	\
161     })
162 
163 #define __arch_exchange_and_add_8_int(mem, value)			\
164   ({ int32_t __value = (value), __new, __old;				\
165     __arch_operate_old_new_n((mem), __value, __old, __new, b, add);	\
166     __old; })
167 
168 #define __arch_exchange_and_add_16_int(mem, value)			\
169   ({ int32_t __value = (value), __new, __old;				\
170     __arch_operate_old_new_n((mem), __value, __old, __new, w, add);	\
171     __old; })
172 
173 #define __arch_exchange_and_add_32_int(mem, value)			\
174   ({ int32_t __value = (value), __new, __old;				\
175     __arch_operate_old_new_n((mem), __value, __old, __new, l, add);	\
176     __old; })
177 
178 #define __arch_exchange_and_add_64_int(mem, value)			\
179   (abort (), 0)
180 
181 #define atomic_exchange_and_add(mem, value) \
182   __atomic_val_bysize (__arch_exchange_and_add, int, mem, value)
183 
184 
185 /* Again, another template. We get a slight optimisation when the old value
186    does not need to be returned. Parameters:
187       bwl:     b, w or l for 8, 16 and 32 bit versions.
188       oper:    The instruction to perform on the old value.
189 */
190 
191 #define __arch_operate_new_n(mem, value, bwl, oper)	 \
192   ({ int32_t __value = (value), __new; \
193      __asm__ __volatile__ ("\
194 	.align 2\n\
195 	mova 1f,r0\n\
196 	mov r15,r1\n\
197 	mov #-6,r15\n\
198      0: mov." #bwl " @%1,%0\n\
199 	" #oper " %2,%0\n\
200 	mov." #bwl " %0,@%1\n\
201      1: mov r1,r15"			\
202 	: "=&r" (__new)			\
203 	: rNOSP (mem), rNOSP (__value)	\
204 	: "r0", "r1", "memory");	\
205      __new;				\
206   })
207 
208 #define __arch_add_8_int(mem, value)		\
209   __arch_operate_new_n(mem, value, b, add)
210 
211 #define __arch_add_16_int(mem, value)		\
212   __arch_operate_new_n(mem, value, w, add)
213 
214 #define __arch_add_32_int(mem, value)		\
215   __arch_operate_new_n(mem, value, l, add)
216 
217 #define __arch_add_64_int(mem, value)		\
218   (abort (), 0)
219 
220 #define atomic_add(mem, value) \
221   ((void) __atomic_val_bysize (__arch_add, int, mem, value))
222 
223 
224 #define __arch_add_negative_8_int(mem, value)		\
225   (__arch_operate_new_n(mem, value, b, add) < 0)
226 
227 #define __arch_add_negative_16_int(mem, value)		\
228   (__arch_operate_new_n(mem, value, w, add) < 0)
229 
230 #define __arch_add_negative_32_int(mem, value)		\
231   (__arch_operate_new_n(mem, value, l, add) < 0)
232 
233 #define __arch_add_negative_64_int(mem, value)		\
234   (abort (), 0)
235 
236 #define atomic_add_negative(mem, value) \
237   __atomic_bool_bysize (__arch_add_negative, int, mem, value)
238 
239 
240 #define __arch_add_zero_8_int(mem, value)		\
241   (__arch_operate_new_n(mem, value, b, add) == 0)
242 
243 #define __arch_add_zero_16_int(mem, value)		\
244   (__arch_operate_new_n(mem, value, w, add) == 0)
245 
246 #define __arch_add_zero_32_int(mem, value)		\
247   (__arch_operate_new_n(mem, value, l, add) == 0)
248 
249 #define __arch_add_zero_64_int(mem, value)		\
250   (abort (), 0)
251 
252 #define atomic_add_zero(mem, value) \
253   __atomic_bool_bysize (__arch_add_zero, int, mem, value)
254 
255 
256 #define atomic_increment_and_test(mem) atomic_add_zero((mem), 1)
257 #define atomic_decrement_and_test(mem) atomic_add_zero((mem), -1)
258 
259 
260 #define __arch_bit_set_8_int(mem, value)		\
261   __arch_operate_new_n(mem, 1<<(value), b, or)
262 
263 #define __arch_bit_set_16_int(mem, value)		\
264   __arch_operate_new_n(mem, 1<<(value), w, or)
265 
266 #define __arch_bit_set_32_int(mem, value)		\
267   __arch_operate_new_n(mem, 1<<(value), l, or)
268 
269 #define __arch_bit_set_64_int(mem, value)		\
270   (abort (), 0)
271 
272 #define __arch_add_64_int(mem, value)			\
273   (abort (), 0)
274 
275 #define atomic_bit_set(mem, value) \
276   ((void) __atomic_val_bysize (__arch_bit_set, int, mem, value))
277 
278 
279 #define __arch_bit_test_set_8_int(mem, value)				\
280   ({ int32_t __value = 1<<(value), __new, __old;			\
281     __arch_operate_old_new_n((mem), __value, __old, __new, b, or);	\
282     __old & __value; })
283 
284 #define __arch_bit_test_set_16_int(mem, value)				\
285   ({ int32_t __value = 1<<(value), __new, __old;			\
286     __arch_operate_old_new_n((mem), __value, __old, __new, w, or);	\
287     __old & __value; })
288 
289 #define __arch_bit_test_set_32_int(mem, value)				\
290   ({ int32_t __value = 1<<(value), __new, __old;			\
291     __arch_operate_old_new_n((mem), __value, __old, __new, l, or);	\
292     __old & __value; })
293 
294 #define __arch_bit_test_set_64_int(mem, value)	\
295   (abort (), 0)
296 
297 #define atomic_bit_test_set(mem, value) \
298   __atomic_val_bysize (__arch_bit_test_set, int, mem, value)
299