1/* Linux-specific atomic operations for m68k Linux.
2   Copyright (C) 2011-2015 Free Software Foundation, Inc.
3   Based on code contributed by CodeSourcery for ARM EABI Linux.
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24<http://www.gnu.org/licenses/>.  */
25
26/* Coldfire dropped the CAS instruction from the base M68K ISA.
27
28   GCC automatically issues a asm memory barrier when it encounters
29   a __sync_synchronize builtin.  Thus, we do not need to define this
30   builtin.
31
32   We implement byte, short and int versions of each atomic operation
33   using the kernel helper defined below.  There is no support for
34   64-bit operations yet.  */
35
36#include <asm/unistd.h>
37#include <stdbool.h>
38
39#ifndef __NR_atomic_cmpxchg_32
40#define __NR_atomic_cmpxchg_32  335
41#endif
42
43/* Kernel helper for compare-and-exchange a 32-bit value.  */
44static inline unsigned
45__kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval)
46{
47  register unsigned *a0 asm("a0") = mem;
48  register unsigned d2 asm("d2") = oldval;
49  register unsigned d1 asm("d1") = newval;
50  register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32;
51
52  asm volatile ("trap #0"
53		: "=r"(d0), "=r"(d1), "=r"(a0)
54		: "r"(d0), "r"(d1), "r"(d2), "r"(a0)
55		: "memory", "a1");
56
57  return d0;
58}
59
60#define HIDDEN __attribute__ ((visibility ("hidden")))
61
62/* Big endian masks  */
63#define INVERT_MASK_1 24
64#define INVERT_MASK_2 16
65
66#define MASK_1 0xffu
67#define MASK_2 0xffffu
68
69#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
70#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
71
72#define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN)			\
73  unsigned HIDDEN							\
74  NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val)			\
75  {									\
76    unsigned oldval, newval, cmpval = *ptr;				\
77									\
78    do {								\
79      oldval = cmpval;							\
80      newval = PFX_OP (oldval INF_OP val);				\
81      cmpval = __kernel_cmpxchg (ptr, oldval, newval);			\
82    } while (__builtin_expect (oldval != cmpval, 0));			\
83									\
84    return RETURN;							\
85  }
86
87#define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
88  TYPE HIDDEN								\
89  NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval)			\
90  {									\
91    unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3);	\
92    unsigned int mask, shift, oldval, newval, cmpval, wval;		\
93									\
94    shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
95    mask = MASK_##WIDTH << shift;					\
96    wval = (sval & MASK_##WIDTH) << shift;				\
97									\
98    cmpval = *wordptr;							\
99    do {								\
100      oldval = cmpval;							\
101      newval = PFX_OP (oldval INF_OP wval);				\
102      newval = (newval & mask) | (oldval & ~mask);			\
103      cmpval = __kernel_cmpxchg (wordptr, oldval, newval);		\
104    } while (__builtin_expect (oldval != cmpval, 0));			\
105									\
106    return (RETURN >> shift) & MASK_##WIDTH;				\
107  }
108
109WORD_SYNC_OP (add,   , +, oldval)
110WORD_SYNC_OP (sub,   , -, oldval)
111WORD_SYNC_OP (or,    , |, oldval)
112WORD_SYNC_OP (and,   , &, oldval)
113WORD_SYNC_OP (xor,   , ^, oldval)
114WORD_SYNC_OP (nand, ~, &, oldval)
115
116SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
117SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
118SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
119SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
120SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
121SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
122
123SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
124SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
125SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
126SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
127SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
128SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
129
130WORD_SYNC_OP (add,   , +, newval)
131WORD_SYNC_OP (sub,   , -, newval)
132WORD_SYNC_OP (or,    , |, newval)
133WORD_SYNC_OP (and,   , &, newval)
134WORD_SYNC_OP (xor,   , ^, newval)
135WORD_SYNC_OP (nand, ~, &, newval)
136
137SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
138SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
139SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
140SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
141SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
142SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
143
144SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
145SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
146SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
147SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
148SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
149SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
150
151unsigned HIDDEN
152__sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval)
153{
154  return __kernel_cmpxchg (ptr, oldval, newval);
155}
156
157bool HIDDEN
158__sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval,
159				unsigned newval)
160{
161  return __kernel_cmpxchg (ptr, oldval, newval) == oldval;
162}
163
164#define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
165  TYPE HIDDEN								\
166  __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval,		\
167				       TYPE snewval)			\
168  {									\
169    unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3);		\
170    unsigned int mask, shift, woldval, wnewval;				\
171    unsigned oldval, newval, cmpval;					\
172									\
173    shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
174    mask = MASK_##WIDTH << shift;					\
175    woldval = (soldval & MASK_##WIDTH) << shift;			\
176    wnewval = (snewval & MASK_##WIDTH) << shift;			\
177    cmpval = *wordptr;							\
178									\
179    do {								\
180      oldval = cmpval;							\
181      if ((oldval & mask) != woldval)					\
182	break;								\
183      newval = (oldval & ~mask) | wnewval;				\
184      cmpval = __kernel_cmpxchg (wordptr, oldval, newval);		\
185    } while (__builtin_expect (oldval != cmpval, 0));			\
186									\
187    return (oldval >> shift) & MASK_##WIDTH;				\
188  }
189
190SUBWORD_VAL_CAS (unsigned short, 2)
191SUBWORD_VAL_CAS (unsigned char,  1)
192
193#define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
194  bool HIDDEN								\
195  __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
196					TYPE newval)			\
197  {									\
198    return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval)	\
199	    == oldval);							\
200  }
201
202SUBWORD_BOOL_CAS (unsigned short, 2)
203SUBWORD_BOOL_CAS (unsigned char,  1)
204
205#undef NAME_oldval
206#define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH
207#define COMMA ,
208
209WORD_SYNC_OP (test_and_set, , COMMA, oldval)
210SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval)
211SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval)
212