xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/config/m68k/linux-atomic.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
148fb7bfaSmrg /* Linux-specific atomic operations for m68k Linux.
2*b1e83836Smrg    Copyright (C) 2011-2022 Free Software Foundation, Inc.
348fb7bfaSmrg    Based on code contributed by CodeSourcery for ARM EABI Linux.
448fb7bfaSmrg 
548fb7bfaSmrg This file is part of GCC.
648fb7bfaSmrg 
748fb7bfaSmrg GCC is free software; you can redistribute it and/or modify it under
848fb7bfaSmrg the terms of the GNU General Public License as published by the Free
948fb7bfaSmrg Software Foundation; either version 3, or (at your option) any later
1048fb7bfaSmrg version.
1148fb7bfaSmrg 
1248fb7bfaSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1348fb7bfaSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
1448fb7bfaSmrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1548fb7bfaSmrg for more details.
1648fb7bfaSmrg 
1748fb7bfaSmrg Under Section 7 of GPL version 3, you are granted additional
1848fb7bfaSmrg permissions described in the GCC Runtime Library Exception, version
1948fb7bfaSmrg 3.1, as published by the Free Software Foundation.
2048fb7bfaSmrg 
2148fb7bfaSmrg You should have received a copy of the GNU General Public License and
2248fb7bfaSmrg a copy of the GCC Runtime Library Exception along with this program;
2348fb7bfaSmrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2448fb7bfaSmrg <http://www.gnu.org/licenses/>.  */
2548fb7bfaSmrg 
2648fb7bfaSmrg /* Coldfire dropped the CAS instruction from the base M68K ISA.
2748fb7bfaSmrg 
2848fb7bfaSmrg    GCC automatically issues a asm memory barrier when it encounters
2948fb7bfaSmrg    a __sync_synchronize builtin.  Thus, we do not need to define this
3048fb7bfaSmrg    builtin.
3148fb7bfaSmrg 
3248fb7bfaSmrg    We implement byte, short and int versions of each atomic operation
3348fb7bfaSmrg    using the kernel helper defined below.  There is no support for
3448fb7bfaSmrg    64-bit operations yet.  */
3548fb7bfaSmrg 
3648fb7bfaSmrg #include <stdbool.h>
3748fb7bfaSmrg 
3848fb7bfaSmrg #ifndef __NR_atomic_cmpxchg_32
3948fb7bfaSmrg #define __NR_atomic_cmpxchg_32  335
4048fb7bfaSmrg #endif
4148fb7bfaSmrg 
4248fb7bfaSmrg /* Kernel helper for compare-and-exchange a 32-bit value.  */
4348fb7bfaSmrg static inline unsigned
__kernel_cmpxchg(unsigned * mem,unsigned oldval,unsigned newval)4448fb7bfaSmrg __kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval)
4548fb7bfaSmrg {
4648fb7bfaSmrg   register unsigned *a0 asm("a0") = mem;
4748fb7bfaSmrg   register unsigned d2 asm("d2") = oldval;
4848fb7bfaSmrg   register unsigned d1 asm("d1") = newval;
4948fb7bfaSmrg   register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32;
5048fb7bfaSmrg 
5148fb7bfaSmrg   asm volatile ("trap #0"
5248fb7bfaSmrg 		: "=r"(d0), "=r"(d1), "=r"(a0)
5348fb7bfaSmrg 		: "r"(d0), "r"(d1), "r"(d2), "r"(a0)
5448fb7bfaSmrg 		: "memory", "a1");
5548fb7bfaSmrg 
5648fb7bfaSmrg   return d0;
5748fb7bfaSmrg }
5848fb7bfaSmrg 
5948fb7bfaSmrg #define HIDDEN __attribute__ ((visibility ("hidden")))
6048fb7bfaSmrg 
6148fb7bfaSmrg /* Big endian masks  */
6248fb7bfaSmrg #define INVERT_MASK_1 24
6348fb7bfaSmrg #define INVERT_MASK_2 16
6448fb7bfaSmrg 
6548fb7bfaSmrg #define MASK_1 0xffu
6648fb7bfaSmrg #define MASK_2 0xffffu
6748fb7bfaSmrg 
6848fb7bfaSmrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
6948fb7bfaSmrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
7048fb7bfaSmrg 
7148fb7bfaSmrg #define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN)			\
7248fb7bfaSmrg   unsigned HIDDEN							\
7348fb7bfaSmrg   NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val)			\
7448fb7bfaSmrg   {									\
7548fb7bfaSmrg     unsigned oldval, newval, cmpval = *ptr;				\
7648fb7bfaSmrg 									\
7748fb7bfaSmrg     do {								\
7848fb7bfaSmrg       oldval = cmpval;							\
7948fb7bfaSmrg       newval = PFX_OP (oldval INF_OP val);				\
8048fb7bfaSmrg       cmpval = __kernel_cmpxchg (ptr, oldval, newval);			\
8148fb7bfaSmrg     } while (__builtin_expect (oldval != cmpval, 0));			\
8248fb7bfaSmrg 									\
8348fb7bfaSmrg     return RETURN;							\
8448fb7bfaSmrg   }
8548fb7bfaSmrg 
8648fb7bfaSmrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
8748fb7bfaSmrg   TYPE HIDDEN								\
8848fb7bfaSmrg   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval)			\
8948fb7bfaSmrg   {									\
9048fb7bfaSmrg     unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3);	\
9148fb7bfaSmrg     unsigned int mask, shift, oldval, newval, cmpval, wval;		\
9248fb7bfaSmrg 									\
9348fb7bfaSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
9448fb7bfaSmrg     mask = MASK_##WIDTH << shift;					\
9548fb7bfaSmrg     wval = (sval & MASK_##WIDTH) << shift;				\
9648fb7bfaSmrg 									\
9748fb7bfaSmrg     cmpval = *wordptr;							\
9848fb7bfaSmrg     do {								\
9948fb7bfaSmrg       oldval = cmpval;							\
10048fb7bfaSmrg       newval = PFX_OP (oldval INF_OP wval);				\
10148fb7bfaSmrg       newval = (newval & mask) | (oldval & ~mask);			\
10248fb7bfaSmrg       cmpval = __kernel_cmpxchg (wordptr, oldval, newval);		\
10348fb7bfaSmrg     } while (__builtin_expect (oldval != cmpval, 0));			\
10448fb7bfaSmrg 									\
10548fb7bfaSmrg     return (RETURN >> shift) & MASK_##WIDTH;				\
10648fb7bfaSmrg   }
10748fb7bfaSmrg 
10848fb7bfaSmrg WORD_SYNC_OP (add,   , +, oldval)
10948fb7bfaSmrg WORD_SYNC_OP (sub,   , -, oldval)
11048fb7bfaSmrg WORD_SYNC_OP (or,    , |, oldval)
11148fb7bfaSmrg WORD_SYNC_OP (and,   , &, oldval)
11248fb7bfaSmrg WORD_SYNC_OP (xor,   , ^, oldval)
11348fb7bfaSmrg WORD_SYNC_OP (nand, ~, &, oldval)
11448fb7bfaSmrg 
11548fb7bfaSmrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
11648fb7bfaSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
11748fb7bfaSmrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
11848fb7bfaSmrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
11948fb7bfaSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
12048fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
12148fb7bfaSmrg 
12248fb7bfaSmrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
12348fb7bfaSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
12448fb7bfaSmrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
12548fb7bfaSmrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
12648fb7bfaSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
12748fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
12848fb7bfaSmrg 
12948fb7bfaSmrg WORD_SYNC_OP (add,   , +, newval)
13048fb7bfaSmrg WORD_SYNC_OP (sub,   , -, newval)
13148fb7bfaSmrg WORD_SYNC_OP (or,    , |, newval)
13248fb7bfaSmrg WORD_SYNC_OP (and,   , &, newval)
13348fb7bfaSmrg WORD_SYNC_OP (xor,   , ^, newval)
13448fb7bfaSmrg WORD_SYNC_OP (nand, ~, &, newval)
13548fb7bfaSmrg 
13648fb7bfaSmrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
13748fb7bfaSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
13848fb7bfaSmrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
13948fb7bfaSmrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
14048fb7bfaSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
14148fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
14248fb7bfaSmrg 
14348fb7bfaSmrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
14448fb7bfaSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
14548fb7bfaSmrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
14648fb7bfaSmrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
14748fb7bfaSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
14848fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
14948fb7bfaSmrg 
15048fb7bfaSmrg unsigned HIDDEN
__sync_val_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15148fb7bfaSmrg __sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval)
15248fb7bfaSmrg {
15348fb7bfaSmrg   return __kernel_cmpxchg (ptr, oldval, newval);
15448fb7bfaSmrg }
15548fb7bfaSmrg 
15648fb7bfaSmrg bool HIDDEN
__sync_bool_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15748fb7bfaSmrg __sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval,
15848fb7bfaSmrg 				unsigned newval)
15948fb7bfaSmrg {
16048fb7bfaSmrg   return __kernel_cmpxchg (ptr, oldval, newval) == oldval;
16148fb7bfaSmrg }
16248fb7bfaSmrg 
16348fb7bfaSmrg #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
16448fb7bfaSmrg   TYPE HIDDEN								\
16548fb7bfaSmrg   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval,		\
16648fb7bfaSmrg 				       TYPE snewval)			\
16748fb7bfaSmrg   {									\
16848fb7bfaSmrg     unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3);		\
16948fb7bfaSmrg     unsigned int mask, shift, woldval, wnewval;				\
17048fb7bfaSmrg     unsigned oldval, newval, cmpval;					\
17148fb7bfaSmrg 									\
17248fb7bfaSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
17348fb7bfaSmrg     mask = MASK_##WIDTH << shift;					\
17448fb7bfaSmrg     woldval = (soldval & MASK_##WIDTH) << shift;			\
17548fb7bfaSmrg     wnewval = (snewval & MASK_##WIDTH) << shift;			\
17648fb7bfaSmrg     cmpval = *wordptr;							\
17748fb7bfaSmrg 									\
17848fb7bfaSmrg     do {								\
17948fb7bfaSmrg       oldval = cmpval;							\
18048fb7bfaSmrg       if ((oldval & mask) != woldval)					\
18148fb7bfaSmrg 	break;								\
18248fb7bfaSmrg       newval = (oldval & ~mask) | wnewval;				\
18348fb7bfaSmrg       cmpval = __kernel_cmpxchg (wordptr, oldval, newval);		\
18448fb7bfaSmrg     } while (__builtin_expect (oldval != cmpval, 0));			\
18548fb7bfaSmrg 									\
18648fb7bfaSmrg     return (oldval >> shift) & MASK_##WIDTH;				\
18748fb7bfaSmrg   }
18848fb7bfaSmrg 
18948fb7bfaSmrg SUBWORD_VAL_CAS (unsigned short, 2)
19048fb7bfaSmrg SUBWORD_VAL_CAS (unsigned char,  1)
19148fb7bfaSmrg 
19248fb7bfaSmrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
19348fb7bfaSmrg   bool HIDDEN								\
19448fb7bfaSmrg   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
19548fb7bfaSmrg 					TYPE newval)			\
19648fb7bfaSmrg   {									\
19748fb7bfaSmrg     return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval)	\
19848fb7bfaSmrg 	    == oldval);							\
19948fb7bfaSmrg   }
20048fb7bfaSmrg 
20148fb7bfaSmrg SUBWORD_BOOL_CAS (unsigned short, 2)
20248fb7bfaSmrg SUBWORD_BOOL_CAS (unsigned char,  1)
20348fb7bfaSmrg 
20448fb7bfaSmrg #undef NAME_oldval
20548fb7bfaSmrg #define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH
20648fb7bfaSmrg #define COMMA ,
20748fb7bfaSmrg 
20848fb7bfaSmrg WORD_SYNC_OP (test_and_set, , COMMA, oldval)
20948fb7bfaSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval)
21048fb7bfaSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval)
211