1 /* Atomic operations used inside libc.  Linux/SH version.
2    Copyright (C) 2003-2021 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    <https://www.gnu.org/licenses/>.  */
18 
19 #define __HAVE_64B_ATOMICS 0
20 #define USE_ATOMIC_COMPILER_BUILTINS 0
21 
22 /* XXX Is this actually correct?  */
23 #define ATOMIC_EXCHANGE_USES_CAS 1
24 
25 /* SH kernel has implemented a gUSA ("g" User Space Atomicity) support
26    for the user space atomicity. The atomicity macros use this scheme.
27 
28   Reference:
29     Niibe Yutaka, "gUSA: Simple and Efficient User Space Atomicity
30     Emulation with Little Kernel Modification", Linux Conference 2002,
31     Japan. http://lc.linux.or.jp/lc2002/papers/niibe0919h.pdf (in
32     Japanese).
33 
34     B.N. Bershad, D. Redell, and J. Ellis, "Fast Mutual Exclusion for
35     Uniprocessors",  Proceedings of the Fifth Architectural Support for
36     Programming Languages and Operating Systems (ASPLOS), pp. 223-233,
37     October 1992. http://www.cs.washington.edu/homes/bershad/Papers/Rcs.ps
38 
39   SuperH ABI:
40       r15:    -(size of atomic instruction sequence) < 0
41       r0:     end point
42       r1:     saved stack pointer
43 */
44 
45 #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
46   ({ __typeof (*(mem)) __result; \
47      __asm __volatile ("\
48 	mova 1f,r0\n\
49 	.align 2\n\
50 	mov r15,r1\n\
51 	mov #(0f-1f),r15\n\
52      0: mov.b @%1,%0\n\
53 	cmp/eq %0,%3\n\
54 	bf 1f\n\
55 	mov.b %2,@%1\n\
56      1: mov r1,r15"\
57 	: "=&r" (__result) : "u" (mem), "u" (newval), "u" (oldval) \
58 	: "r0", "r1", "t", "memory"); \
59      __result; })
60 
61 #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
62   ({ __typeof (*(mem)) __result; \
63      __asm __volatile ("\
64 	mova 1f,r0\n\
65 	mov r15,r1\n\
66 	.align 2\n\
67 	mov #(0f-1f),r15\n\
68 	mov #-8,r15\n\
69      0: mov.w @%1,%0\n\
70 	cmp/eq %0,%3\n\
71 	bf 1f\n\
72 	mov.w %2,@%1\n\
73      1: mov r1,r15"\
74 	: "=&r" (__result) : "u" (mem), "u" (newval), "u" (oldval) \
75 	: "r0", "r1", "t", "memory"); \
76      __result; })
77 
78 #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
79   ({ __typeof (*(mem)) __result; \
80      __asm __volatile ("\
81 	mova 1f,r0\n\
82 	.align 2\n\
83 	mov r15,r1\n\
84 	mov #(0f-1f),r15\n\
85      0: mov.l @%1,%0\n\
86 	cmp/eq %0,%3\n\
87 	bf 1f\n\
88 	mov.l %2,@%1\n\
89      1: mov r1,r15"\
90 	: "=&r" (__result) : "u" (mem), "u" (newval), "u" (oldval) \
91 	: "r0", "r1", "t", "memory"); \
92      __result; })
93 
94 /* XXX We do not really need 64-bit compare-and-exchange.  At least
95    not in the moment.  Using it would mean causing portability
96    problems since not many other 32-bit architectures have support for
97    such an operation.  So don't define any code for now.  */
98 
99 # define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
100   (abort (), (__typeof (*mem)) 0)
101 
102 #define atomic_exchange_and_add(mem, value) \
103   ({ __typeof (*(mem)) __result, __tmp, __value = (value); \
104      if (sizeof (*(mem)) == 1) \
105        __asm __volatile ("\
106 	  mova 1f,r0\n\
107 	  .align 2\n\
108 	  mov r15,r1\n\
109 	  mov #(0f-1f),r15\n\
110        0: mov.b @%2,%0\n\
111 	  mov %1,r2\n\
112 	  add %0,r2\n\
113 	  mov.b r2,@%2\n\
114        1: mov r1,r15"\
115 	: "=&r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
116 	: "r0", "r1", "r2", "memory");		       \
117      else if (sizeof (*(mem)) == 2) \
118        __asm __volatile ("\
119 	  mova 1f,r0\n\
120 	  .align 2\n\
121 	  mov r15,r1\n\
122 	  mov #(0f-1f),r15\n\
123        0: mov.w @%2,%0\n\
124 	  mov %1,r2\n\
125 	  add %0,r2\n\
126 	  mov.w r2,@%2\n\
127        1: mov r1,r15"\
128 	: "=&r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
129 	: "r0", "r1", "r2", "memory"); \
130      else if (sizeof (*(mem)) == 4) \
131        __asm __volatile ("\
132 	  mova 1f,r0\n\
133 	  .align 2\n\
134 	  mov r15,r1\n\
135 	  mov #(0f-1f),r15\n\
136        0: mov.l @%2,%0\n\
137 	  mov %1,r2\n\
138 	  add %0,r2\n\
139 	  mov.l r2,@%2\n\
140        1: mov r1,r15"\
141 	: "=&r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
142 	: "r0", "r1", "r2", "memory"); \
143      else \
144        { \
145 	 __typeof (mem) memp = (mem); \
146 	 do \
147 	   __result = *memp; \
148 	 while (__arch_compare_and_exchange_val_64_acq \
149 		 (memp,	__result + __value, __result) == __result); \
150 	 (void) __value; \
151        } \
152      __result; })
153 
154 #define atomic_add(mem, value) \
155   (void) ({ __typeof (*(mem)) __tmp, __value = (value); \
156 	    if (sizeof (*(mem)) == 1) \
157 	      __asm __volatile ("\
158 		mova 1f,r0\n\
159 		mov r15,r1\n\
160 		.align 2\n\
161 		mov #(0f-1f),r15\n\
162 	     0: mov.b @%1,r2\n\
163 		add %0,r2\n\
164 		mov.b r2,@%1\n\
165 	     1: mov r1,r15"\
166 		: "=&r" (__tmp) : "u" (mem), "0" (__value) \
167 		: "r0", "r1", "r2", "memory"); \
168 	    else if (sizeof (*(mem)) == 2) \
169 	      __asm __volatile ("\
170 		mova 1f,r0\n\
171 		mov r15,r1\n\
172 		.align 2\n\
173 		mov #(0f-1f),r15\n\
174 	     0: mov.w @%1,r2\n\
175 		add %0,r2\n\
176 		mov.w r2,@%1\n\
177 	     1: mov r1,r15"\
178 		: "=&r" (__tmp) : "u" (mem), "0" (__value) \
179 		: "r0", "r1", "r2", "memory"); \
180 	    else if (sizeof (*(mem)) == 4) \
181 	      __asm __volatile ("\
182 		mova 1f,r0\n\
183 		mov r15,r1\n\
184 		.align 2\n\
185 		mov #(0f-1f),r15\n\
186 	     0: mov.l @%1,r2\n\
187 		add %0,r2\n\
188 		mov.l r2,@%1\n\
189 	     1: mov r1,r15"\
190 		: "=&r" (__tmp) : "u" (mem), "0" (__value) \
191 		: "r0", "r1", "r2", "memory"); \
192 	    else \
193 	      { \
194 		__typeof (*(mem)) oldval; \
195 		__typeof (mem) memp = (mem); \
196 		do \
197 		  oldval = *memp; \
198 		while (__arch_compare_and_exchange_val_64_acq \
199 			(memp, oldval + __value, oldval) == oldval); \
200 		(void) __value; \
201 	      } \
202 	    })
203 
204 #define atomic_add_negative(mem, value) \
205   ({ unsigned char __result; \
206      __typeof (*(mem)) __tmp, __value = (value); \
207      if (sizeof (*(mem)) == 1) \
208        __asm __volatile ("\
209 	  mova 1f,r0\n\
210 	  mov r15,r1\n\
211 	  .align 2\n\
212 	  mov #(0f-1f),r15\n\
213        0: mov.b @%2,r2\n\
214 	  add %1,r2\n\
215 	  mov.b r2,@%2\n\
216        1: mov r1,r15\n\
217 	  shal r2\n\
218 	  movt %0"\
219 	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
220 	: "r0", "r1", "r2", "t", "memory"); \
221      else if (sizeof (*(mem)) == 2) \
222        __asm __volatile ("\
223 	  mova 1f,r0\n\
224 	  mov r15,r1\n\
225 	  .align 2\n\
226 	  mov #(0f-1f),r15\n\
227        0: mov.w @%2,r2\n\
228 	  add %1,r2\n\
229 	  mov.w r2,@%2\n\
230        1: mov r1,r15\n\
231 	  shal r2\n\
232 	  movt %0"\
233 	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
234 	: "r0", "r1", "r2", "t", "memory"); \
235      else if (sizeof (*(mem)) == 4) \
236        __asm __volatile ("\
237 	  mova 1f,r0\n\
238 	  mov r15,r1\n\
239 	  .align 2\n\
240 	  mov #(0f-1f),r15\n\
241        0: mov.l @%2,r2\n\
242 	  add %1,r2\n\
243 	  mov.l r2,@%2\n\
244        1: mov r1,r15\n\
245 	  shal r2\n\
246 	  movt %0"\
247 	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
248 	: "r0", "r1", "r2", "t", "memory"); \
249      else \
250        abort (); \
251      __result; })
252 
253 #define atomic_add_zero(mem, value) \
254   ({ unsigned char __result; \
255      __typeof (*(mem)) __tmp, __value = (value); \
256      if (sizeof (*(mem)) == 1) \
257        __asm __volatile ("\
258 	  mova 1f,r0\n\
259 	  mov r15,r1\n\
260 	  .align 2\n\
261 	  mov #(0f-1f),r15\n\
262        0: mov.b @%2,r2\n\
263 	  add %1,r2\n\
264 	  mov.b r2,@%2\n\
265        1: mov r1,r15\n\
266 	  tst r2,r2\n\
267 	  movt %0"\
268 	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
269 	: "r0", "r1", "r2", "t", "memory"); \
270      else if (sizeof (*(mem)) == 2) \
271        __asm __volatile ("\
272 	  mova 1f,r0\n\
273 	  mov r15,r1\n\
274 	  .align 2\n\
275 	  mov #(0f-1f),r15\n\
276        0: mov.w @%2,r2\n\
277 	  add %1,r2\n\
278 	  mov.w r2,@%2\n\
279        1: mov r1,r15\n\
280 	  tst r2,r2\n\
281 	  movt %0"\
282 	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
283 	: "r0", "r1", "r2", "t", "memory"); \
284      else if (sizeof (*(mem)) == 4) \
285        __asm __volatile ("\
286 	  mova 1f,r0\n\
287 	  mov r15,r1\n\
288 	  .align 2\n\
289 	  mov #(0f-1f),r15\n\
290        0: mov.l @%2,r2\n\
291 	  add %1,r2\n\
292 	  mov.l r2,@%2\n\
293        1: mov r1,r15\n\
294 	  tst r2,r2\n\
295 	  movt %0"\
296 	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
297 	: "r0", "r1", "r2", "t", "memory"); \
298      else \
299        abort (); \
300      __result; })
301 
302 #define atomic_increment_and_test(mem) atomic_add_zero((mem), 1)
303 #define atomic_decrement_and_test(mem) atomic_add_zero((mem), -1)
304 
305 #define atomic_bit_set(mem, bit) \
306   (void) ({ unsigned int __mask = 1 << (bit); \
307 	    if (sizeof (*(mem)) == 1) \
308 	      __asm __volatile ("\
309 		mova 1f,r0\n\
310 		mov r15,r1\n\
311 		.align 2\n\
312 		mov #(0f-1f),r15\n\
313 	     0: mov.b @%0,r2\n\
314 		or %1,r2\n\
315 		mov.b r2,@%0\n\
316 	     1: mov r1,r15"\
317 		: : "u" (mem), "u" (__mask) \
318 		: "r0", "r1", "r2", "memory"); \
319 	    else if (sizeof (*(mem)) == 2) \
320 	      __asm __volatile ("\
321 		mova 1f,r0\n\
322 		mov r15,r1\n\
323 		.align 2\n\
324 		mov #(0f-1f),r15\n\
325 	     0: mov.w @%0,r2\n\
326 		or %1,r2\n\
327 		mov.w r2,@%0\n\
328 	     1: mov r1,r15"\
329 		: : "u" (mem), "u" (__mask) \
330 		: "r0", "r1", "r2", "memory"); \
331 	    else if (sizeof (*(mem)) == 4) \
332 	      __asm __volatile ("\
333 		mova 1f,r0\n\
334 		mov r15,r1\n\
335 		.align 2\n\
336 		mov #(0f-1f),r15\n\
337 	     0: mov.l @%0,r2\n\
338 		or %1,r2\n\
339 		mov.l r2,@%0\n\
340 	     1: mov r1,r15"\
341 		: : "u" (mem), "u" (__mask) \
342 		: "r0", "r1", "r2", "memory"); \
343 	    else \
344 	      abort (); \
345 	    })
346 
347 #define atomic_bit_test_set(mem, bit) \
348   ({ unsigned int __mask = 1 << (bit); \
349      unsigned int __result = __mask; \
350      if (sizeof (*(mem)) == 1) \
351        __asm __volatile ("\
352 	  mova 1f,r0\n\
353 	  .align 2\n\
354 	  mov r15,r1\n\
355 	  mov #(0f-1f),r15\n\
356        0: mov.b @%2,r2\n\
357 	  mov r2,r3\n\
358 	  or %1,r2\n\
359 	  mov.b r2,@%2\n\
360        1: mov r1,r15\n\
361 	  and r3,%0"\
362 	: "=&r" (__result), "=&r" (__mask) \
363 	: "u" (mem), "0" (__result), "1" (__mask) \
364 	: "r0", "r1", "r2", "r3", "memory");	\
365      else if (sizeof (*(mem)) == 2) \
366        __asm __volatile ("\
367 	  mova 1f,r0\n\
368 	  .align 2\n\
369 	  mov r15,r1\n\
370 	  mov #(0f-1f),r15\n\
371        0: mov.w @%2,r2\n\
372 	  mov r2,r3\n\
373 	  or %1,r2\n\
374 	  mov.w %1,@%2\n\
375        1: mov r1,r15\n\
376 	  and r3,%0"\
377 	: "=&r" (__result), "=&r" (__mask) \
378 	: "u" (mem), "0" (__result), "1" (__mask) \
379 	: "r0", "r1", "r2", "r3", "memory"); \
380      else if (sizeof (*(mem)) == 4) \
381        __asm __volatile ("\
382 	  mova 1f,r0\n\
383 	  .align 2\n\
384 	  mov r15,r1\n\
385 	  mov #(0f-1f),r15\n\
386        0: mov.l @%2,r2\n\
387 	  mov r2,r3\n\
388 	  or r2,%1\n\
389 	  mov.l %1,@%2\n\
390        1: mov r1,r15\n\
391 	  and r3,%0"\
392 	: "=&r" (__result), "=&r" (__mask) \
393 	: "u" (mem), "0" (__result), "1" (__mask) \
394 	: "r0", "r1", "r2", "r3", "memory"); \
395      else \
396        abort (); \
397      __result; })
398