1 /* Linux-specific atomic operations for C-SKY.
2    Copyright (C) 2018-2021 Free Software Foundation, Inc.
3    Contributed by C-SKY Microsystems and Mentor Graphics.
4 
5    This file is part of GCC.
6 
7    GCC is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 3, or (at your option) any later
10    version.
11 
12    GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15    for more details.
16 
17    Under Section 7 of GPL version 3, you are granted additional
18    permissions described in the GCC Runtime Library Exception, version
19    3.1, as published by the Free Software Foundation.
20 
21    You should have received a copy of the GNU General Public License and
22    a copy of the GCC Runtime Library Exception along with this program;
23    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24    <http://www.gnu.org/licenses/>.	 */
25 
26 /* Kernel helper for compare-and-exchange.  */
27 inline int
__kernel_cmpxchg(int oldval,int newval,volatile int * ptr)28 __kernel_cmpxchg (int oldval, int newval, volatile int *ptr)
29 {
30   register int _a0 asm ("a0") = oldval;
31   register int _a1 asm ("a1") = newval;
32   register volatile int *_a2 asm ("a2") = ptr;
33   __asm__ __volatile__ ("trap	  2\n"	    \
34 			:"+r" (_a0) :"r" (_a1) , "r" (_a2)	\
35 			: "a3", "memory");			\
36   return _a0;
37 }
38 
39 
40 /* Kernel helper for memory barrier.  */
__kernel_dmb(void)41 inline void __kernel_dmb (void)
42 {
43   asm ("sync":::"memory");
44 }
45 
46 /* Note: we implement byte, short and int versions of atomic operations using
47    the above kernel helpers, but there is no support for "long long" (64-bit)
48    operations as yet.  */
49 
50 #define HIDDEN __attribute__ ((visibility ("hidden")))
51 
52 #ifdef __CSKYLE__
53 #define INVERT_MASK_1 0
54 #define INVERT_MASK_2 0
55 #else
56 #define INVERT_MASK_1 24
57 #define INVERT_MASK_2 16
58 #endif
59 
60 #define MASK_1 0xffu
61 #define MASK_2 0xffffu
62 
63 #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
64   int HIDDEN								\
65   __sync_fetch_and_##OP##_4 (int *ptr, int val)				\
66   {									\
67     int failure, tmp;							\
68 									\
69     do									\
70       {									\
71 	tmp = *ptr;							\
72 	failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
73       }									\
74     while (failure != 0);						\
75 									\
76     return tmp;								\
77   }
78 
79 FETCH_AND_OP_WORD (add,	  , +)
80 FETCH_AND_OP_WORD (sub,	  , -)
81 FETCH_AND_OP_WORD (or,	  , |)
82 FETCH_AND_OP_WORD (and,	  , &)
83 FETCH_AND_OP_WORD (xor,	  , ^)
84 FETCH_AND_OP_WORD (nand, ~, &)
85 
86 #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
87 #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
88 
89 /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
90    subword-sized quantities.  */
91 
92 #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
93   TYPE HIDDEN								\
94   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)			\
95   {									\
96     int *wordptr = (int *) ((unsigned int) ptr & ~3);			\
97     unsigned int mask, shift, oldval, newval;				\
98     int failure;							\
99 									\
100     shift = (((unsigned int) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
101     mask = MASK_##WIDTH << shift;					\
102 									\
103     do									\
104       {									\
105 	oldval = *wordptr;						\
106 	newval = ((PFX_OP (((oldval & mask) >> shift)			\
107 		   INF_OP (unsigned int) val)) << shift) & mask;	\
108 	newval |= oldval & ~mask;					\
109 	failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
110       }									\
111     while (failure != 0);						\
112 									\
113     return (RETURN & mask) >> shift;					\
114   }
115 
116 SUBWORD_SYNC_OP (add,	, +, unsigned short, 2, oldval)
117 SUBWORD_SYNC_OP (sub,	, -, unsigned short, 2, oldval)
118 SUBWORD_SYNC_OP (or,	, |, unsigned short, 2, oldval)
119 SUBWORD_SYNC_OP (and,	, &, unsigned short, 2, oldval)
120 SUBWORD_SYNC_OP (xor,	, ^, unsigned short, 2, oldval)
121 SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
122 
123 SUBWORD_SYNC_OP (add,	, +, unsigned char, 1, oldval)
124 SUBWORD_SYNC_OP (sub,	, -, unsigned char, 1, oldval)
125 SUBWORD_SYNC_OP (or,	, |, unsigned char, 1, oldval)
126 SUBWORD_SYNC_OP (and,	, &, unsigned char, 1, oldval)
127 SUBWORD_SYNC_OP (xor,	, ^, unsigned char, 1, oldval)
128 SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
129 
130 #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
131   int HIDDEN								\
132   __sync_##OP##_and_fetch_4 (int *ptr, int val)				\
133   {									\
134     int tmp, failure;							\
135 									\
136     do									\
137       {									\
138 	tmp = *ptr;							\
139 	failure = __kernel_cmpxchg (tmp, PFX_OP tmp INF_OP val, ptr);	\
140       }									\
141     while (failure != 0);						\
142 									\
143     return PFX_OP tmp INF_OP val;					\
144   }
145 
146 OP_AND_FETCH_WORD (add,	  , +)
147 OP_AND_FETCH_WORD (sub,	  , -)
148 OP_AND_FETCH_WORD (or,	  , |)
149 OP_AND_FETCH_WORD (and,	  , &)
150 OP_AND_FETCH_WORD (xor,	  , ^)
151 OP_AND_FETCH_WORD (nand, ~, &)
152 
153 SUBWORD_SYNC_OP (add,	, +, unsigned short, 2, newval)
154 SUBWORD_SYNC_OP (sub,	, -, unsigned short, 2, newval)
155 SUBWORD_SYNC_OP (or,	, |, unsigned short, 2, newval)
156 SUBWORD_SYNC_OP (and,	, &, unsigned short, 2, newval)
157 SUBWORD_SYNC_OP (xor,	, ^, unsigned short, 2, newval)
158 SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
159 
160 SUBWORD_SYNC_OP (add,	, +, unsigned char, 1, newval)
161 SUBWORD_SYNC_OP (sub,	, -, unsigned char, 1, newval)
162 SUBWORD_SYNC_OP (or,	, |, unsigned char, 1, newval)
163 SUBWORD_SYNC_OP (and,	, &, unsigned char, 1, newval)
164 SUBWORD_SYNC_OP (xor,	, ^, unsigned char, 1, newval)
165 SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
166 
167 int HIDDEN
__sync_val_compare_and_swap_4(int * ptr,int oldval,int newval)168 __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
169 {
170   int actual_oldval, fail;
171 
172   while (1)
173     {
174       actual_oldval = *ptr;
175 
176       if (oldval != actual_oldval)
177 	return actual_oldval;
178 
179       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
180 
181       if (!fail)
182 	return oldval;
183     }
184 }
185 
186 #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
187   TYPE HIDDEN								\
188   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
189 				       TYPE newval)			\
190   {									\
191     int *wordptr = (int *)((unsigned int) ptr & ~3), fail;		\
192     unsigned int mask, shift, actual_oldval, actual_newval;		\
193 									\
194     shift = (((unsigned int) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
195     mask = MASK_##WIDTH << shift;					\
196 									\
197     while (1)								\
198       {									\
199 	actual_oldval = *wordptr;					\
200 									\
201 	if (((actual_oldval & mask) >> shift) != (unsigned int) oldval)	\
202 	  return (actual_oldval & mask) >> shift;			\
203 									\
204 	actual_newval = (actual_oldval & ~mask)				\
205 			| (((unsigned int) newval << shift) & mask);	\
206 									\
207 	fail = __kernel_cmpxchg (actual_oldval, actual_newval,		\
208 				 wordptr);				\
209 									\
210 	if (!fail)							\
211 	  return oldval;						\
212       }									\
213   }
214 
215 SUBWORD_VAL_CAS (unsigned short, 2)
216 SUBWORD_VAL_CAS (unsigned char,	1)
217 
218 typedef unsigned char bool;
219 
220 bool HIDDEN
__sync_bool_compare_and_swap_4(int * ptr,int oldval,int newval)221 __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
222 {
223   int failure = __kernel_cmpxchg (oldval, newval, ptr);
224   return (failure == 0);
225 }
226 
227 #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
228   bool HIDDEN								\
229   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
230 					TYPE newval)			\
231   {									\
232     TYPE actual_oldval							\
233       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);	\
234     return (oldval == actual_oldval);					\
235   }
236 
237 SUBWORD_BOOL_CAS (unsigned short, 2)
238 SUBWORD_BOOL_CAS (unsigned char, 1)
239 
240 void HIDDEN
__sync_synchronize(void)241 __sync_synchronize (void)
242 {
243   __kernel_dmb ();
244 }
245 
246 int HIDDEN
__sync_lock_test_and_set_4(int * ptr,int val)247 __sync_lock_test_and_set_4 (int *ptr, int val)
248 {
249   int failure, oldval;
250 
251   do
252     {
253       oldval = *ptr;
254       failure = __kernel_cmpxchg (oldval, val, ptr);
255     }
256   while (failure != 0);
257 
258   return oldval;
259 }
260 
261 #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)				\
262   TYPE HIDDEN								\
263   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)		\
264   {									\
265     int failure;							\
266     unsigned int oldval, newval, shift, mask;				\
267     int *wordptr = (int *) ((unsigned int) ptr & ~3);			\
268 									\
269     shift = (((unsigned int) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
270     mask = MASK_##WIDTH << shift;					\
271 									\
272     do									\
273       {									\
274 	oldval = *wordptr;						\
275 	newval = ((oldval & ~mask)					\
276 		  | (((unsigned int) val << shift) & mask));		\
277 	failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
278       }									\
279     while (failure != 0);						\
280 									\
281     return (oldval & mask) >> shift;					\
282   }
283 
284 SUBWORD_TEST_AND_SET (unsigned short, 2)
285 SUBWORD_TEST_AND_SET (unsigned char,  1)
286 
287 #define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
288   void HIDDEN								\
289   __sync_lock_release_##WIDTH (TYPE *ptr)				\
290   {									\
291     /* All writes before this point must be seen before we release	\
292        the lock itself.	 */						\
293     __kernel_dmb ();							\
294     *ptr = 0;								\
295   }
296 
297 SYNC_LOCK_RELEASE (int,	  4)
298 SYNC_LOCK_RELEASE (short, 2)
299 SYNC_LOCK_RELEASE (char,  1)
300