xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/config/m68k/linux-atomic.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
136ac495dSmrg /* Linux-specific atomic operations for m68k Linux.
2*8feb0f0bSmrg    Copyright (C) 2011-2020 Free Software Foundation, Inc.
336ac495dSmrg    Based on code contributed by CodeSourcery for ARM EABI Linux.
436ac495dSmrg 
536ac495dSmrg This file is part of GCC.
636ac495dSmrg 
736ac495dSmrg GCC is free software; you can redistribute it and/or modify it under
836ac495dSmrg the terms of the GNU General Public License as published by the Free
936ac495dSmrg Software Foundation; either version 3, or (at your option) any later
1036ac495dSmrg version.
1136ac495dSmrg 
1236ac495dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1336ac495dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
1436ac495dSmrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1536ac495dSmrg for more details.
1636ac495dSmrg 
1736ac495dSmrg Under Section 7 of GPL version 3, you are granted additional
1836ac495dSmrg permissions described in the GCC Runtime Library Exception, version
1936ac495dSmrg 3.1, as published by the Free Software Foundation.
2036ac495dSmrg 
2136ac495dSmrg You should have received a copy of the GNU General Public License and
2236ac495dSmrg a copy of the GCC Runtime Library Exception along with this program;
2336ac495dSmrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2436ac495dSmrg <http://www.gnu.org/licenses/>.  */
2536ac495dSmrg 
2636ac495dSmrg /* Coldfire dropped the CAS instruction from the base M68K ISA.
2736ac495dSmrg 
2836ac495dSmrg    GCC automatically issues a asm memory barrier when it encounters
2936ac495dSmrg    a __sync_synchronize builtin.  Thus, we do not need to define this
3036ac495dSmrg    builtin.
3136ac495dSmrg 
3236ac495dSmrg    We implement byte, short and int versions of each atomic operation
3336ac495dSmrg    using the kernel helper defined below.  There is no support for
3436ac495dSmrg    64-bit operations yet.  */
3536ac495dSmrg 
3636ac495dSmrg #include <stdbool.h>
3736ac495dSmrg 
3836ac495dSmrg #ifndef __NR_atomic_cmpxchg_32
3936ac495dSmrg #define __NR_atomic_cmpxchg_32  335
4036ac495dSmrg #endif
4136ac495dSmrg 
4236ac495dSmrg /* Kernel helper for compare-and-exchange a 32-bit value.  */
4336ac495dSmrg static inline unsigned
__kernel_cmpxchg(unsigned * mem,unsigned oldval,unsigned newval)4436ac495dSmrg __kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval)
4536ac495dSmrg {
4636ac495dSmrg   register unsigned *a0 asm("a0") = mem;
4736ac495dSmrg   register unsigned d2 asm("d2") = oldval;
4836ac495dSmrg   register unsigned d1 asm("d1") = newval;
4936ac495dSmrg   register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32;
5036ac495dSmrg 
5136ac495dSmrg   asm volatile ("trap #0"
5236ac495dSmrg 		: "=r"(d0), "=r"(d1), "=r"(a0)
5336ac495dSmrg 		: "r"(d0), "r"(d1), "r"(d2), "r"(a0)
5436ac495dSmrg 		: "memory", "a1");
5536ac495dSmrg 
5636ac495dSmrg   return d0;
5736ac495dSmrg }
5836ac495dSmrg 
5936ac495dSmrg #define HIDDEN __attribute__ ((visibility ("hidden")))
6036ac495dSmrg 
6136ac495dSmrg /* Big endian masks  */
6236ac495dSmrg #define INVERT_MASK_1 24
6336ac495dSmrg #define INVERT_MASK_2 16
6436ac495dSmrg 
6536ac495dSmrg #define MASK_1 0xffu
6636ac495dSmrg #define MASK_2 0xffffu
6736ac495dSmrg 
6836ac495dSmrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
6936ac495dSmrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
7036ac495dSmrg 
7136ac495dSmrg #define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN)			\
7236ac495dSmrg   unsigned HIDDEN							\
7336ac495dSmrg   NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val)			\
7436ac495dSmrg   {									\
7536ac495dSmrg     unsigned oldval, newval, cmpval = *ptr;				\
7636ac495dSmrg 									\
7736ac495dSmrg     do {								\
7836ac495dSmrg       oldval = cmpval;							\
7936ac495dSmrg       newval = PFX_OP (oldval INF_OP val);				\
8036ac495dSmrg       cmpval = __kernel_cmpxchg (ptr, oldval, newval);			\
8136ac495dSmrg     } while (__builtin_expect (oldval != cmpval, 0));			\
8236ac495dSmrg 									\
8336ac495dSmrg     return RETURN;							\
8436ac495dSmrg   }
8536ac495dSmrg 
8636ac495dSmrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
8736ac495dSmrg   TYPE HIDDEN								\
8836ac495dSmrg   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval)			\
8936ac495dSmrg   {									\
9036ac495dSmrg     unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3);	\
9136ac495dSmrg     unsigned int mask, shift, oldval, newval, cmpval, wval;		\
9236ac495dSmrg 									\
9336ac495dSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
9436ac495dSmrg     mask = MASK_##WIDTH << shift;					\
9536ac495dSmrg     wval = (sval & MASK_##WIDTH) << shift;				\
9636ac495dSmrg 									\
9736ac495dSmrg     cmpval = *wordptr;							\
9836ac495dSmrg     do {								\
9936ac495dSmrg       oldval = cmpval;							\
10036ac495dSmrg       newval = PFX_OP (oldval INF_OP wval);				\
10136ac495dSmrg       newval = (newval & mask) | (oldval & ~mask);			\
10236ac495dSmrg       cmpval = __kernel_cmpxchg (wordptr, oldval, newval);		\
10336ac495dSmrg     } while (__builtin_expect (oldval != cmpval, 0));			\
10436ac495dSmrg 									\
10536ac495dSmrg     return (RETURN >> shift) & MASK_##WIDTH;				\
10636ac495dSmrg   }
10736ac495dSmrg 
10836ac495dSmrg WORD_SYNC_OP (add,   , +, oldval)
10936ac495dSmrg WORD_SYNC_OP (sub,   , -, oldval)
11036ac495dSmrg WORD_SYNC_OP (or,    , |, oldval)
11136ac495dSmrg WORD_SYNC_OP (and,   , &, oldval)
11236ac495dSmrg WORD_SYNC_OP (xor,   , ^, oldval)
11336ac495dSmrg WORD_SYNC_OP (nand, ~, &, oldval)
11436ac495dSmrg 
11536ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
11636ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
11736ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
11836ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
11936ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
12036ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
12136ac495dSmrg 
12236ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
12336ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
12436ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
12536ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
12636ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
12736ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
12836ac495dSmrg 
12936ac495dSmrg WORD_SYNC_OP (add,   , +, newval)
13036ac495dSmrg WORD_SYNC_OP (sub,   , -, newval)
13136ac495dSmrg WORD_SYNC_OP (or,    , |, newval)
13236ac495dSmrg WORD_SYNC_OP (and,   , &, newval)
13336ac495dSmrg WORD_SYNC_OP (xor,   , ^, newval)
13436ac495dSmrg WORD_SYNC_OP (nand, ~, &, newval)
13536ac495dSmrg 
13636ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
13736ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
13836ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
13936ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
14036ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
14136ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
14236ac495dSmrg 
14336ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
14436ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
14536ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
14636ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
14736ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
14836ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
14936ac495dSmrg 
15036ac495dSmrg unsigned HIDDEN
__sync_val_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15136ac495dSmrg __sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval)
15236ac495dSmrg {
15336ac495dSmrg   return __kernel_cmpxchg (ptr, oldval, newval);
15436ac495dSmrg }
15536ac495dSmrg 
15636ac495dSmrg bool HIDDEN
__sync_bool_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15736ac495dSmrg __sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval,
15836ac495dSmrg 				unsigned newval)
15936ac495dSmrg {
16036ac495dSmrg   return __kernel_cmpxchg (ptr, oldval, newval) == oldval;
16136ac495dSmrg }
16236ac495dSmrg 
16336ac495dSmrg #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
16436ac495dSmrg   TYPE HIDDEN								\
16536ac495dSmrg   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval,		\
16636ac495dSmrg 				       TYPE snewval)			\
16736ac495dSmrg   {									\
16836ac495dSmrg     unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3);		\
16936ac495dSmrg     unsigned int mask, shift, woldval, wnewval;				\
17036ac495dSmrg     unsigned oldval, newval, cmpval;					\
17136ac495dSmrg 									\
17236ac495dSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
17336ac495dSmrg     mask = MASK_##WIDTH << shift;					\
17436ac495dSmrg     woldval = (soldval & MASK_##WIDTH) << shift;			\
17536ac495dSmrg     wnewval = (snewval & MASK_##WIDTH) << shift;			\
17636ac495dSmrg     cmpval = *wordptr;							\
17736ac495dSmrg 									\
17836ac495dSmrg     do {								\
17936ac495dSmrg       oldval = cmpval;							\
18036ac495dSmrg       if ((oldval & mask) != woldval)					\
18136ac495dSmrg 	break;								\
18236ac495dSmrg       newval = (oldval & ~mask) | wnewval;				\
18336ac495dSmrg       cmpval = __kernel_cmpxchg (wordptr, oldval, newval);		\
18436ac495dSmrg     } while (__builtin_expect (oldval != cmpval, 0));			\
18536ac495dSmrg 									\
18636ac495dSmrg     return (oldval >> shift) & MASK_##WIDTH;				\
18736ac495dSmrg   }
18836ac495dSmrg 
18936ac495dSmrg SUBWORD_VAL_CAS (unsigned short, 2)
19036ac495dSmrg SUBWORD_VAL_CAS (unsigned char,  1)
19136ac495dSmrg 
19236ac495dSmrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
19336ac495dSmrg   bool HIDDEN								\
19436ac495dSmrg   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
19536ac495dSmrg 					TYPE newval)			\
19636ac495dSmrg   {									\
19736ac495dSmrg     return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval)	\
19836ac495dSmrg 	    == oldval);							\
19936ac495dSmrg   }
20036ac495dSmrg 
20136ac495dSmrg SUBWORD_BOOL_CAS (unsigned short, 2)
20236ac495dSmrg SUBWORD_BOOL_CAS (unsigned char,  1)
20336ac495dSmrg 
20436ac495dSmrg #undef NAME_oldval
20536ac495dSmrg #define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH
20636ac495dSmrg #define COMMA ,
20736ac495dSmrg 
20836ac495dSmrg WORD_SYNC_OP (test_and_set, , COMMA, oldval)
20936ac495dSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval)
21036ac495dSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval)
211