xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/config/arm/freebsd-atomic.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /* FreeBSD specific atomic operations for ARM EABI.
2    Copyright (C) 2015-2016 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10 
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
19 
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 <http://www.gnu.org/licenses/>.  */
24 
25 #include <sys/types.h>
26 
27 #define HIDDEN __attribute__ ((visibility ("hidden")))
28 
29 #define ARM_VECTORS_HIGH 0xffff0000U
30 #define ARM_TP_ADDRESS   (ARM_VECTORS_HIGH + 0x1000)
31 #define ARM_RAS_START    (ARM_TP_ADDRESS + 4)
32 
33 void HIDDEN
34 __sync_synchronize (void)
35 {
36 #if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__)       \
37     || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6T2__)  \
38     || defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__)  \
39     || defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
40 #if defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
41     __asm __volatile ("dmb" : : : "memory");
42 #else
43     __asm __volatile ("mcr p15, 0, r0, c7, c10, 5" : : : "memory");
44 #endif
45 #else
46     __asm __volatile ("nop" : : : "memory");
47 #endif
48 }
49 
50 #if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__)        \
51     || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6T2__)   \
52     || defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__)   \
53     || defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__)
54 
55 /* These systems should be supported by the compiler.  */
56 
57 #else /* __ARM_ARCH_5__  */
58 
59 #define	SYNC_LOCK_TEST_AND_SET_N(N, TYPE, LDR, STR)			\
60 TYPE HIDDEN    								\
61 __sync_lock_test_and_set_##N (TYPE *mem, TYPE val)			\
62 {									\
63         unsigned int old, temp, ras_start;                              \
64                                                                         \
65         ras_start = ARM_RAS_START;					\
66         __asm volatile (						\
67                 /* Set up Restartable Atomic Sequence.  */		\
68                 "1:"							\
69                 "\tadr   %2, 1b\n"					\
70                 "\tstr   %2, [%5]\n"					\
71                 "\tadr   %2, 2f\n"					\
72                 "\tstr   %2, [%5, #4]\n"				\
73                                                                         \
74                 "\t"LDR" %0, %4\n"	/* Load old value.  */		\
75                 "\t"STR" %3, %1\n"	/* Store new value.  */		\
76                                                                         \
77                 /* Tear down Restartable Atomic Sequence.  */		\
78                 "2:"							\
79                 "\tmov   %2, #0x00000000\n"				\
80                 "\tstr   %2, [%5]\n"					\
81                 "\tmov   %2, #0xffffffff\n"				\
82                 "\tstr   %2, [%5, #4]\n"				\
83                 : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
84                 : "r" (val), "m" (*mem), "r" (ras_start));		\
85         return (old);							\
86 }
87 
88 #define SYNC_LOCK_RELEASE_N(N, TYPE)					\
89 void HIDDEN								\
90 __sync_lock_release_##N (TYPE *ptr)     				\
91 {					       				\
92     /* All writes before this point must be seen before we release	\
93        the lock itself.  */						\
94     __sync_synchronize ();     						\
95     *ptr = 0;								\
96 }
97 
98 #define	SYNC_VAL_CAS_N(N, TYPE, LDR, STREQ)             		\
99 TYPE HIDDEN    								\
100 __sync_val_compare_and_swap_##N (TYPE *mem, TYPE expected,		\
101     TYPE desired)							\
102 {									\
103         unsigned int old, temp, ras_start;                              \
104                                                                         \
105         ras_start = ARM_RAS_START;					\
106         __asm volatile (						\
107                 /* Set up Restartable Atomic Sequence.  */		\
108                 "1:"							\
109                 "\tadr   %2, 1b\n"					\
110                 "\tstr   %2, [%6]\n"					\
111                 "\tadr   %2, 2f\n"					\
112                 "\tstr   %2, [%6, #4]\n"				\
113                                                                         \
114                 "\t"LDR" %0, %5\n"     /* Load old value.  */		\
115                 "\tcmp   %0, %3\n"     /* Compare to expected value.  */\
116                 "\t"STREQ" %4, %1\n"   /* Store new value.  */		\
117                                                                         \
118                 /* Tear down Restartable Atomic Sequence.  */		\
119                 "2:"							\
120                 "\tmov   %2, #0x00000000\n"				\
121                 "\tstr   %2, [%6]\n"					\
122                 "\tmov   %2, #0xffffffff\n"				\
123                 "\tstr   %2, [%6, #4]\n"				\
124                 : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
125                 : "r" (expected), "r" (desired), "m" (*mem),		\
126                   "r" (ras_start));					\
127         return (old);							\
128 }
129 
130 typedef unsigned char bool;
131 
132 #define SYNC_BOOL_CAS_N(N, TYPE)                                        \
133 bool HIDDEN								\
134 __sync_bool_compare_and_swap_##N (TYPE *ptr, TYPE oldval,		\
135                                   TYPE newval)                          \
136 {									\
137     TYPE actual_oldval							\
138       = __sync_val_compare_and_swap_##N (ptr, oldval, newval);          \
139     return (oldval == actual_oldval);					\
140 }
141 
142 #define	SYNC_FETCH_AND_OP_N(N, TYPE, LDR, STR, NAME, OP)		\
143 TYPE HIDDEN								\
144 __sync_fetch_and_##NAME##_##N (TYPE *mem, TYPE val)	       		\
145 {									\
146         unsigned int old, temp, ras_start;                              \
147                                                                         \
148         ras_start = ARM_RAS_START;					\
149         __asm volatile (						\
150                 /* Set up Restartable Atomic Sequence.  */		\
151                 "1:"							\
152                 "\tadr   %2, 1b\n"					\
153                 "\tstr   %2, [%5]\n"					\
154                 "\tadr   %2, 2f\n"					\
155                 "\tstr   %2, [%5, #4]\n"				\
156                                                                         \
157                 "\t"LDR" %0, %4\n"	/* Load old value.  */		\
158                 "\t"OP"  %2, %0, %3\n"	/* Calculate new value.  */	\
159                 "\t"STR" %2, %1\n"	/* Store new value.  */		\
160                                                                         \
161                 /* Tear down Restartable Atomic Sequence.  */		\
162                 "2:"							\
163                 "\tmov   %2, #0x00000000\n"				\
164                 "\tstr   %2, [%5]\n"					\
165                 "\tmov   %2, #0xffffffff\n"				\
166                 "\tstr   %2, [%5, #4]\n"				\
167                 : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
168                 : "r" (val), "m" (*mem), "r" (ras_start));		\
169         return (old);							\
170 }
171 
172 #define	SYNC_OP_AND_FETCH_N(N, TYPE, LDR, STR, NAME, OP)		\
173 TYPE HIDDEN   								\
174 __sync_##NAME##_and_fetch_##N (TYPE *mem, TYPE val)	       		\
175 {									\
176         unsigned int old, temp, ras_start;                              \
177                                                                         \
178         ras_start = ARM_RAS_START;					\
179         __asm volatile (						\
180                 /* Set up Restartable Atomic Sequence.  */		\
181                 "1:"							\
182                 "\tadr   %2, 1b\n"					\
183                 "\tstr   %2, [%5]\n"					\
184                 "\tadr   %2, 2f\n"					\
185                 "\tstr   %2, [%5, #4]\n"				\
186                                                                         \
187                 "\t"LDR" %0, %4\n"	/* Load old value.  */		\
188                 "\t"OP"  %2, %0, %3\n"	/* Calculate new value.  */	\
189                 "\t"STR" %2, %1\n"	/* Store new value.  */		\
190                                                                         \
191                 /* Tear down Restartable Atomic Sequence.  */		\
192                 "2:"							\
193                 "\tmov   %2, #0x00000000\n"				\
194                 "\tstr   %2, [%5]\n"					\
195                 "\tmov   %2, #0xffffffff\n"				\
196                 "\tstr   %2, [%5, #4]\n"				\
197                 : "=&r" (old), "=m" (*mem), "=&r" (temp)		\
198                 : "r" (val), "m" (*mem), "r" (ras_start));		\
199         return (old);							\
200 }
201 
202 #define	EMIT_ALL_OPS_N(N, TYPE, LDR, STR, STREQ)			\
203 SYNC_LOCK_TEST_AND_SET_N (N, TYPE, LDR, STR)				\
204 SYNC_LOCK_RELEASE_N (N, TYPE)                                           \
205 SYNC_VAL_CAS_N (N, TYPE, LDR, STREQ)	                		\
206 SYNC_BOOL_CAS_N (N, TYPE)                                               \
207 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, add, "add")		        \
208 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, and, "and")		        \
209 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, or, "orr")		        \
210 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, sub, "sub")		        \
211 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, xor, "eor")                     \
212 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, add, "add")		        \
213 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, and, "and")		        \
214 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, or, "orr")		        \
215 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, sub, "sub")		        \
216 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, xor, "eor")
217 
218 
219 
220 EMIT_ALL_OPS_N (1, unsigned char, "ldrb", "strb", "streqb")
221 EMIT_ALL_OPS_N (2, unsigned short, "ldrh", "strh", "streqh")
222 EMIT_ALL_OPS_N (4, unsigned int, "ldr", "str", "streq")
223 
224 #endif
225