xref: /netbsd-src/external/gpl3/gcc/dist/libgcc/config/nios2/linux-atomic.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
14d5abbe8Smrg /* Linux-specific atomic operations for Nios II Linux.
2*b1e83836Smrg    Copyright (C) 2008-2022 Free Software Foundation, Inc.
34d5abbe8Smrg 
44d5abbe8Smrg This file is free software; you can redistribute it and/or modify it
54d5abbe8Smrg under the terms of the GNU General Public License as published by the
64d5abbe8Smrg Free Software Foundation; either version 3, or (at your option) any
74d5abbe8Smrg later version.
84d5abbe8Smrg 
94d5abbe8Smrg This file is distributed in the hope that it will be useful, but
104d5abbe8Smrg WITHOUT ANY WARRANTY; without even the implied warranty of
114d5abbe8Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
124d5abbe8Smrg General Public License for more details.
134d5abbe8Smrg 
144d5abbe8Smrg Under Section 7 of GPL version 3, you are granted additional
154d5abbe8Smrg permissions described in the GCC Runtime Library Exception, version
164d5abbe8Smrg 3.1, as published by the Free Software Foundation.
174d5abbe8Smrg 
184d5abbe8Smrg You should have received a copy of the GNU General Public License and
194d5abbe8Smrg a copy of the GCC Runtime Library Exception along with this program;
204d5abbe8Smrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
214d5abbe8Smrg <http://www.gnu.org/licenses/>.  */
224d5abbe8Smrg 
234d5abbe8Smrg /* We implement byte, short and int versions of each atomic operation
244d5abbe8Smrg    using the kernel helper defined below.  There is no support for
254d5abbe8Smrg    64-bit operations yet.  */
264d5abbe8Smrg 
274d5abbe8Smrg /* Crash a userspace program with SIGSEV.  */
284d5abbe8Smrg #define ABORT_INSTRUCTION asm ("stw zero, 0(zero)")
294d5abbe8Smrg 
304d5abbe8Smrg /* Kernel helper for compare-and-exchange a 32-bit value.  */
314d5abbe8Smrg static inline long
__kernel_cmpxchg(int oldval,int newval,int * mem)324d5abbe8Smrg __kernel_cmpxchg (int oldval, int newval, int *mem)
334d5abbe8Smrg {
344d5abbe8Smrg   register int r2 asm ("r2");
354d5abbe8Smrg   register int *r4 asm ("r4") = mem;
364d5abbe8Smrg   register int r5 asm ("r5") = oldval;
374d5abbe8Smrg   register int r6 asm ("r6") = newval;
384d5abbe8Smrg 
394d5abbe8Smrg   /* Call the kernel provided fixed address cmpxchg helper routine.  */
404d5abbe8Smrg   asm volatile ("movi %0, %4\n\t"
414d5abbe8Smrg 		"callr %0\n"
424d5abbe8Smrg 		: "=r" (r2)
434d5abbe8Smrg 		: "r" (r4), "r" (r5), "r" (r6), "I" (0x00001004)
444d5abbe8Smrg 		: "ra", "memory");
454d5abbe8Smrg   return r2;
464d5abbe8Smrg }
474d5abbe8Smrg 
484d5abbe8Smrg #define HIDDEN __attribute__ ((visibility ("hidden")))
494d5abbe8Smrg 
504d5abbe8Smrg #ifdef __nios2_little_endian__
514d5abbe8Smrg #define INVERT_MASK_1 0
524d5abbe8Smrg #define INVERT_MASK_2 0
534d5abbe8Smrg #else
544d5abbe8Smrg #define INVERT_MASK_1 24
554d5abbe8Smrg #define INVERT_MASK_2 16
564d5abbe8Smrg #endif
574d5abbe8Smrg 
584d5abbe8Smrg #define MASK_1 0xffu
594d5abbe8Smrg #define MASK_2 0xffffu
604d5abbe8Smrg 
614d5abbe8Smrg #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
624d5abbe8Smrg   int HIDDEN								\
634d5abbe8Smrg   __sync_fetch_and_##OP##_4 (int *ptr, int val)				\
644d5abbe8Smrg   {									\
654d5abbe8Smrg     int failure, tmp;							\
664d5abbe8Smrg 									\
674d5abbe8Smrg     do {								\
684d5abbe8Smrg       tmp = *ptr;							\
694d5abbe8Smrg       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
704d5abbe8Smrg     } while (failure != 0);						\
714d5abbe8Smrg 									\
724d5abbe8Smrg     return tmp;								\
734d5abbe8Smrg   }
744d5abbe8Smrg 
754d5abbe8Smrg FETCH_AND_OP_WORD (add,   , +)
764d5abbe8Smrg FETCH_AND_OP_WORD (sub,   , -)
774d5abbe8Smrg FETCH_AND_OP_WORD (or,    , |)
784d5abbe8Smrg FETCH_AND_OP_WORD (and,   , &)
794d5abbe8Smrg FETCH_AND_OP_WORD (xor,   , ^)
804d5abbe8Smrg FETCH_AND_OP_WORD (nand, ~, &)
814d5abbe8Smrg 
824d5abbe8Smrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
834d5abbe8Smrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
844d5abbe8Smrg 
854d5abbe8Smrg /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
864d5abbe8Smrg    subword-sized quantities.  */
874d5abbe8Smrg 
884d5abbe8Smrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
894d5abbe8Smrg   TYPE HIDDEN								\
904d5abbe8Smrg   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)			\
914d5abbe8Smrg   {									\
924d5abbe8Smrg     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
934d5abbe8Smrg     unsigned int mask, shift, oldval, newval;				\
944d5abbe8Smrg     int failure;							\
954d5abbe8Smrg 									\
964d5abbe8Smrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
974d5abbe8Smrg     mask = MASK_##WIDTH << shift;					\
984d5abbe8Smrg 									\
994d5abbe8Smrg     do {								\
1004d5abbe8Smrg       oldval = *wordptr;						\
1014d5abbe8Smrg       newval = ((PFX_OP (((oldval & mask) >> shift)			\
1024d5abbe8Smrg 			 INF_OP (unsigned int) val)) << shift) & mask;	\
1034d5abbe8Smrg       newval |= oldval & ~mask;						\
1044d5abbe8Smrg       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
1054d5abbe8Smrg     } while (failure != 0);						\
1064d5abbe8Smrg 									\
1074d5abbe8Smrg     return (RETURN & mask) >> shift;					\
1084d5abbe8Smrg   }
1094d5abbe8Smrg 
1104d5abbe8Smrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
1114d5abbe8Smrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
1124d5abbe8Smrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
1134d5abbe8Smrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
1144d5abbe8Smrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
1154d5abbe8Smrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
1164d5abbe8Smrg 
1174d5abbe8Smrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
1184d5abbe8Smrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
1194d5abbe8Smrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
1204d5abbe8Smrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
1214d5abbe8Smrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
1224d5abbe8Smrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
1234d5abbe8Smrg 
1244d5abbe8Smrg #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
1254d5abbe8Smrg   int HIDDEN								\
1264d5abbe8Smrg   __sync_##OP##_and_fetch_4 (int *ptr, int val)				\
1274d5abbe8Smrg   {									\
1284d5abbe8Smrg     int tmp, failure;							\
1294d5abbe8Smrg 									\
1304d5abbe8Smrg     do {								\
1314d5abbe8Smrg       tmp = *ptr;							\
1324d5abbe8Smrg       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
1334d5abbe8Smrg     } while (failure != 0);						\
1344d5abbe8Smrg 									\
1354d5abbe8Smrg     return PFX_OP (tmp INF_OP val);					\
1364d5abbe8Smrg   }
1374d5abbe8Smrg 
1384d5abbe8Smrg OP_AND_FETCH_WORD (add,   , +)
1394d5abbe8Smrg OP_AND_FETCH_WORD (sub,   , -)
1404d5abbe8Smrg OP_AND_FETCH_WORD (or,    , |)
1414d5abbe8Smrg OP_AND_FETCH_WORD (and,   , &)
1424d5abbe8Smrg OP_AND_FETCH_WORD (xor,   , ^)
1434d5abbe8Smrg OP_AND_FETCH_WORD (nand, ~, &)
1444d5abbe8Smrg 
1454d5abbe8Smrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
1464d5abbe8Smrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
1474d5abbe8Smrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
1484d5abbe8Smrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
1494d5abbe8Smrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
1504d5abbe8Smrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
1514d5abbe8Smrg 
1524d5abbe8Smrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
1534d5abbe8Smrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
1544d5abbe8Smrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
1554d5abbe8Smrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
1564d5abbe8Smrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
1574d5abbe8Smrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
1584d5abbe8Smrg 
1594d5abbe8Smrg int HIDDEN
__sync_val_compare_and_swap_4(int * ptr,int oldval,int newval)1604d5abbe8Smrg __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
1614d5abbe8Smrg {
1624d5abbe8Smrg   int actual_oldval, fail;
1634d5abbe8Smrg 
1644d5abbe8Smrg   while (1)
1654d5abbe8Smrg     {
1664d5abbe8Smrg       actual_oldval = *ptr;
1674d5abbe8Smrg 
1684d5abbe8Smrg       if (oldval != actual_oldval)
1694d5abbe8Smrg 	return actual_oldval;
1704d5abbe8Smrg 
1714d5abbe8Smrg       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
1724d5abbe8Smrg 
1734d5abbe8Smrg       if (!fail)
1744d5abbe8Smrg 	return oldval;
1754d5abbe8Smrg     }
1764d5abbe8Smrg }
1774d5abbe8Smrg 
1784d5abbe8Smrg #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
1794d5abbe8Smrg   TYPE HIDDEN								\
1804d5abbe8Smrg   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
1814d5abbe8Smrg 				       TYPE newval)			\
1824d5abbe8Smrg   {									\
1834d5abbe8Smrg     int *wordptr = (int *)((unsigned long) ptr & ~3), fail;		\
1844d5abbe8Smrg     unsigned int mask, shift, actual_oldval, actual_newval;		\
1854d5abbe8Smrg 									\
1864d5abbe8Smrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
1874d5abbe8Smrg     mask = MASK_##WIDTH << shift;					\
1884d5abbe8Smrg 									\
1894d5abbe8Smrg     while (1)								\
1904d5abbe8Smrg       {									\
1914d5abbe8Smrg 	actual_oldval = *wordptr;					\
1924d5abbe8Smrg 									\
1934d5abbe8Smrg 	if (((actual_oldval & mask) >> shift) != (unsigned int) oldval)	\
1944d5abbe8Smrg           return (actual_oldval & mask) >> shift;			\
1954d5abbe8Smrg 									\
1964d5abbe8Smrg 	actual_newval = (actual_oldval & ~mask)				\
1974d5abbe8Smrg 			| (((unsigned int) newval << shift) & mask);	\
1984d5abbe8Smrg 									\
1994d5abbe8Smrg 	fail = __kernel_cmpxchg (actual_oldval, actual_newval,		\
2004d5abbe8Smrg 				 wordptr);				\
2014d5abbe8Smrg 									\
2024d5abbe8Smrg 	if (!fail)							\
2034d5abbe8Smrg 	  return oldval;						\
2044d5abbe8Smrg       }									\
2054d5abbe8Smrg   }
2064d5abbe8Smrg 
2074d5abbe8Smrg SUBWORD_VAL_CAS (unsigned short, 2)
2084d5abbe8Smrg SUBWORD_VAL_CAS (unsigned char,  1)
2094d5abbe8Smrg 
2104d5abbe8Smrg typedef unsigned char bool;
2114d5abbe8Smrg 
2124d5abbe8Smrg bool HIDDEN
__sync_bool_compare_and_swap_4(int * ptr,int oldval,int newval)2134d5abbe8Smrg __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
2144d5abbe8Smrg {
2154d5abbe8Smrg   int failure = __kernel_cmpxchg (oldval, newval, ptr);
2164d5abbe8Smrg   return (failure == 0);
2174d5abbe8Smrg }
2184d5abbe8Smrg 
2194d5abbe8Smrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
2204d5abbe8Smrg   bool HIDDEN								\
2214d5abbe8Smrg   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
2224d5abbe8Smrg 					TYPE newval)			\
2234d5abbe8Smrg   {									\
2244d5abbe8Smrg     TYPE actual_oldval							\
2254d5abbe8Smrg       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);	\
2264d5abbe8Smrg     return (oldval == actual_oldval);					\
2274d5abbe8Smrg   }
2284d5abbe8Smrg 
2294d5abbe8Smrg SUBWORD_BOOL_CAS (unsigned short, 2)
2304d5abbe8Smrg SUBWORD_BOOL_CAS (unsigned char,  1)
2314d5abbe8Smrg 
2324d5abbe8Smrg int HIDDEN
__sync_lock_test_and_set_4(int * ptr,int val)2334d5abbe8Smrg __sync_lock_test_and_set_4 (int *ptr, int val)
2344d5abbe8Smrg {
2354d5abbe8Smrg   int failure, oldval;
2364d5abbe8Smrg 
2374d5abbe8Smrg   do {
2384d5abbe8Smrg     oldval = *ptr;
2394d5abbe8Smrg     failure = __kernel_cmpxchg (oldval, val, ptr);
2404d5abbe8Smrg   } while (failure != 0);
2414d5abbe8Smrg 
2424d5abbe8Smrg   return oldval;
2434d5abbe8Smrg }
2444d5abbe8Smrg 
2454d5abbe8Smrg #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)				\
2464d5abbe8Smrg   TYPE HIDDEN								\
2474d5abbe8Smrg   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)		\
2484d5abbe8Smrg   {									\
2494d5abbe8Smrg     int failure;							\
2504d5abbe8Smrg     unsigned int oldval, newval, shift, mask;				\
2514d5abbe8Smrg     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
2524d5abbe8Smrg 									\
2534d5abbe8Smrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
2544d5abbe8Smrg     mask = MASK_##WIDTH << shift;					\
2554d5abbe8Smrg 									\
2564d5abbe8Smrg     do {								\
2574d5abbe8Smrg       oldval = *wordptr;						\
2584d5abbe8Smrg       newval = (oldval & ~mask)						\
2594d5abbe8Smrg 	       | (((unsigned int) val << shift) & mask);		\
2604d5abbe8Smrg       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
2614d5abbe8Smrg     } while (failure != 0);						\
2624d5abbe8Smrg 									\
2634d5abbe8Smrg     return (oldval & mask) >> shift;					\
2644d5abbe8Smrg   }
2654d5abbe8Smrg 
2664d5abbe8Smrg SUBWORD_TEST_AND_SET (unsigned short, 2)
2674d5abbe8Smrg SUBWORD_TEST_AND_SET (unsigned char,  1)
2684d5abbe8Smrg 
2694d5abbe8Smrg #define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
2704d5abbe8Smrg   void HIDDEN								\
2714d5abbe8Smrg   __sync_lock_release_##WIDTH (TYPE *ptr)				\
2724d5abbe8Smrg   {									\
2734d5abbe8Smrg     /* All writes before this point must be seen before we release	\
2744d5abbe8Smrg        the lock itself.  */						\
2754d5abbe8Smrg     __builtin_sync ();							\
2764d5abbe8Smrg     *ptr = 0;								\
2774d5abbe8Smrg   }
2784d5abbe8Smrg 
2794d5abbe8Smrg SYNC_LOCK_RELEASE (int,   4)
2804d5abbe8Smrg SYNC_LOCK_RELEASE (short, 2)
2814d5abbe8Smrg SYNC_LOCK_RELEASE (char,  1)
282