xref: /netbsd-src/external/gpl3/gcc.old/dist/libgcc/config/nios2/linux-atomic.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
136ac495dSmrg /* Linux-specific atomic operations for Nios II Linux.
2*8feb0f0bSmrg    Copyright (C) 2008-2020 Free Software Foundation, Inc.
336ac495dSmrg 
436ac495dSmrg This file is free software; you can redistribute it and/or modify it
536ac495dSmrg under the terms of the GNU General Public License as published by the
636ac495dSmrg Free Software Foundation; either version 3, or (at your option) any
736ac495dSmrg later version.
836ac495dSmrg 
936ac495dSmrg This file is distributed in the hope that it will be useful, but
1036ac495dSmrg WITHOUT ANY WARRANTY; without even the implied warranty of
1136ac495dSmrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1236ac495dSmrg General Public License for more details.
1336ac495dSmrg 
1436ac495dSmrg Under Section 7 of GPL version 3, you are granted additional
1536ac495dSmrg permissions described in the GCC Runtime Library Exception, version
1636ac495dSmrg 3.1, as published by the Free Software Foundation.
1736ac495dSmrg 
1836ac495dSmrg You should have received a copy of the GNU General Public License and
1936ac495dSmrg a copy of the GCC Runtime Library Exception along with this program;
2036ac495dSmrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
2136ac495dSmrg <http://www.gnu.org/licenses/>.  */
2236ac495dSmrg 
2336ac495dSmrg /* We implement byte, short and int versions of each atomic operation
2436ac495dSmrg    using the kernel helper defined below.  There is no support for
2536ac495dSmrg    64-bit operations yet.  */
2636ac495dSmrg 
2736ac495dSmrg /* Crash a userspace program with SIGSEV.  */
2836ac495dSmrg #define ABORT_INSTRUCTION asm ("stw zero, 0(zero)")
2936ac495dSmrg 
3036ac495dSmrg /* Kernel helper for compare-and-exchange a 32-bit value.  */
3136ac495dSmrg static inline long
__kernel_cmpxchg(int oldval,int newval,int * mem)3236ac495dSmrg __kernel_cmpxchg (int oldval, int newval, int *mem)
3336ac495dSmrg {
3436ac495dSmrg   register int r2 asm ("r2");
3536ac495dSmrg   register int *r4 asm ("r4") = mem;
3636ac495dSmrg   register int r5 asm ("r5") = oldval;
3736ac495dSmrg   register int r6 asm ("r6") = newval;
3836ac495dSmrg 
3936ac495dSmrg   /* Call the kernel provided fixed address cmpxchg helper routine.  */
4036ac495dSmrg   asm volatile ("movi %0, %4\n\t"
4136ac495dSmrg 		"callr %0\n"
4236ac495dSmrg 		: "=r" (r2)
4336ac495dSmrg 		: "r" (r4), "r" (r5), "r" (r6), "I" (0x00001004)
4436ac495dSmrg 		: "ra", "memory");
4536ac495dSmrg   return r2;
4636ac495dSmrg }
4736ac495dSmrg 
4836ac495dSmrg #define HIDDEN __attribute__ ((visibility ("hidden")))
4936ac495dSmrg 
5036ac495dSmrg #ifdef __nios2_little_endian__
5136ac495dSmrg #define INVERT_MASK_1 0
5236ac495dSmrg #define INVERT_MASK_2 0
5336ac495dSmrg #else
5436ac495dSmrg #define INVERT_MASK_1 24
5536ac495dSmrg #define INVERT_MASK_2 16
5636ac495dSmrg #endif
5736ac495dSmrg 
5836ac495dSmrg #define MASK_1 0xffu
5936ac495dSmrg #define MASK_2 0xffffu
6036ac495dSmrg 
6136ac495dSmrg #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
6236ac495dSmrg   int HIDDEN								\
6336ac495dSmrg   __sync_fetch_and_##OP##_4 (int *ptr, int val)				\
6436ac495dSmrg   {									\
6536ac495dSmrg     int failure, tmp;							\
6636ac495dSmrg 									\
6736ac495dSmrg     do {								\
6836ac495dSmrg       tmp = *ptr;							\
6936ac495dSmrg       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
7036ac495dSmrg     } while (failure != 0);						\
7136ac495dSmrg 									\
7236ac495dSmrg     return tmp;								\
7336ac495dSmrg   }
7436ac495dSmrg 
7536ac495dSmrg FETCH_AND_OP_WORD (add,   , +)
7636ac495dSmrg FETCH_AND_OP_WORD (sub,   , -)
7736ac495dSmrg FETCH_AND_OP_WORD (or,    , |)
7836ac495dSmrg FETCH_AND_OP_WORD (and,   , &)
7936ac495dSmrg FETCH_AND_OP_WORD (xor,   , ^)
8036ac495dSmrg FETCH_AND_OP_WORD (nand, ~, &)
8136ac495dSmrg 
8236ac495dSmrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
8336ac495dSmrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
8436ac495dSmrg 
8536ac495dSmrg /* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for
8636ac495dSmrg    subword-sized quantities.  */
8736ac495dSmrg 
8836ac495dSmrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN)	\
8936ac495dSmrg   TYPE HIDDEN								\
9036ac495dSmrg   NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val)			\
9136ac495dSmrg   {									\
9236ac495dSmrg     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
9336ac495dSmrg     unsigned int mask, shift, oldval, newval;				\
9436ac495dSmrg     int failure;							\
9536ac495dSmrg 									\
9636ac495dSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
9736ac495dSmrg     mask = MASK_##WIDTH << shift;					\
9836ac495dSmrg 									\
9936ac495dSmrg     do {								\
10036ac495dSmrg       oldval = *wordptr;						\
10136ac495dSmrg       newval = ((PFX_OP (((oldval & mask) >> shift)			\
10236ac495dSmrg 			 INF_OP (unsigned int) val)) << shift) & mask;	\
10336ac495dSmrg       newval |= oldval & ~mask;						\
10436ac495dSmrg       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
10536ac495dSmrg     } while (failure != 0);						\
10636ac495dSmrg 									\
10736ac495dSmrg     return (RETURN & mask) >> shift;					\
10836ac495dSmrg   }
10936ac495dSmrg 
11036ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, oldval)
11136ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, oldval)
11236ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, oldval)
11336ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, oldval)
11436ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, oldval)
11536ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
11636ac495dSmrg 
11736ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, oldval)
11836ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, oldval)
11936ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, oldval)
12036ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, oldval)
12136ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, oldval)
12236ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
12336ac495dSmrg 
12436ac495dSmrg #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
12536ac495dSmrg   int HIDDEN								\
12636ac495dSmrg   __sync_##OP##_and_fetch_4 (int *ptr, int val)				\
12736ac495dSmrg   {									\
12836ac495dSmrg     int tmp, failure;							\
12936ac495dSmrg 									\
13036ac495dSmrg     do {								\
13136ac495dSmrg       tmp = *ptr;							\
13236ac495dSmrg       failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr);	\
13336ac495dSmrg     } while (failure != 0);						\
13436ac495dSmrg 									\
13536ac495dSmrg     return PFX_OP (tmp INF_OP val);					\
13636ac495dSmrg   }
13736ac495dSmrg 
13836ac495dSmrg OP_AND_FETCH_WORD (add,   , +)
13936ac495dSmrg OP_AND_FETCH_WORD (sub,   , -)
14036ac495dSmrg OP_AND_FETCH_WORD (or,    , |)
14136ac495dSmrg OP_AND_FETCH_WORD (and,   , &)
14236ac495dSmrg OP_AND_FETCH_WORD (xor,   , ^)
14336ac495dSmrg OP_AND_FETCH_WORD (nand, ~, &)
14436ac495dSmrg 
14536ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned short, 2, newval)
14636ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned short, 2, newval)
14736ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned short, 2, newval)
14836ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned short, 2, newval)
14936ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned short, 2, newval)
15036ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
15136ac495dSmrg 
15236ac495dSmrg SUBWORD_SYNC_OP (add,   , +, unsigned char, 1, newval)
15336ac495dSmrg SUBWORD_SYNC_OP (sub,   , -, unsigned char, 1, newval)
15436ac495dSmrg SUBWORD_SYNC_OP (or,    , |, unsigned char, 1, newval)
15536ac495dSmrg SUBWORD_SYNC_OP (and,   , &, unsigned char, 1, newval)
15636ac495dSmrg SUBWORD_SYNC_OP (xor,   , ^, unsigned char, 1, newval)
15736ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
15836ac495dSmrg 
15936ac495dSmrg int HIDDEN
__sync_val_compare_and_swap_4(int * ptr,int oldval,int newval)16036ac495dSmrg __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval)
16136ac495dSmrg {
16236ac495dSmrg   int actual_oldval, fail;
16336ac495dSmrg 
16436ac495dSmrg   while (1)
16536ac495dSmrg     {
16636ac495dSmrg       actual_oldval = *ptr;
16736ac495dSmrg 
16836ac495dSmrg       if (oldval != actual_oldval)
16936ac495dSmrg 	return actual_oldval;
17036ac495dSmrg 
17136ac495dSmrg       fail = __kernel_cmpxchg (actual_oldval, newval, ptr);
17236ac495dSmrg 
17336ac495dSmrg       if (!fail)
17436ac495dSmrg 	return oldval;
17536ac495dSmrg     }
17636ac495dSmrg }
17736ac495dSmrg 
17836ac495dSmrg #define SUBWORD_VAL_CAS(TYPE, WIDTH)					\
17936ac495dSmrg   TYPE HIDDEN								\
18036ac495dSmrg   __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
18136ac495dSmrg 				       TYPE newval)			\
18236ac495dSmrg   {									\
18336ac495dSmrg     int *wordptr = (int *)((unsigned long) ptr & ~3), fail;		\
18436ac495dSmrg     unsigned int mask, shift, actual_oldval, actual_newval;		\
18536ac495dSmrg 									\
18636ac495dSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
18736ac495dSmrg     mask = MASK_##WIDTH << shift;					\
18836ac495dSmrg 									\
18936ac495dSmrg     while (1)								\
19036ac495dSmrg       {									\
19136ac495dSmrg 	actual_oldval = *wordptr;					\
19236ac495dSmrg 									\
19336ac495dSmrg 	if (((actual_oldval & mask) >> shift) != (unsigned int) oldval)	\
19436ac495dSmrg           return (actual_oldval & mask) >> shift;			\
19536ac495dSmrg 									\
19636ac495dSmrg 	actual_newval = (actual_oldval & ~mask)				\
19736ac495dSmrg 			| (((unsigned int) newval << shift) & mask);	\
19836ac495dSmrg 									\
19936ac495dSmrg 	fail = __kernel_cmpxchg (actual_oldval, actual_newval,		\
20036ac495dSmrg 				 wordptr);				\
20136ac495dSmrg 									\
20236ac495dSmrg 	if (!fail)							\
20336ac495dSmrg 	  return oldval;						\
20436ac495dSmrg       }									\
20536ac495dSmrg   }
20636ac495dSmrg 
20736ac495dSmrg SUBWORD_VAL_CAS (unsigned short, 2)
20836ac495dSmrg SUBWORD_VAL_CAS (unsigned char,  1)
20936ac495dSmrg 
21036ac495dSmrg typedef unsigned char bool;
21136ac495dSmrg 
21236ac495dSmrg bool HIDDEN
__sync_bool_compare_and_swap_4(int * ptr,int oldval,int newval)21336ac495dSmrg __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval)
21436ac495dSmrg {
21536ac495dSmrg   int failure = __kernel_cmpxchg (oldval, newval, ptr);
21636ac495dSmrg   return (failure == 0);
21736ac495dSmrg }
21836ac495dSmrg 
21936ac495dSmrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH)					\
22036ac495dSmrg   bool HIDDEN								\
22136ac495dSmrg   __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval,		\
22236ac495dSmrg 					TYPE newval)			\
22336ac495dSmrg   {									\
22436ac495dSmrg     TYPE actual_oldval							\
22536ac495dSmrg       = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval);	\
22636ac495dSmrg     return (oldval == actual_oldval);					\
22736ac495dSmrg   }
22836ac495dSmrg 
22936ac495dSmrg SUBWORD_BOOL_CAS (unsigned short, 2)
23036ac495dSmrg SUBWORD_BOOL_CAS (unsigned char,  1)
23136ac495dSmrg 
23236ac495dSmrg int HIDDEN
__sync_lock_test_and_set_4(int * ptr,int val)23336ac495dSmrg __sync_lock_test_and_set_4 (int *ptr, int val)
23436ac495dSmrg {
23536ac495dSmrg   int failure, oldval;
23636ac495dSmrg 
23736ac495dSmrg   do {
23836ac495dSmrg     oldval = *ptr;
23936ac495dSmrg     failure = __kernel_cmpxchg (oldval, val, ptr);
24036ac495dSmrg   } while (failure != 0);
24136ac495dSmrg 
24236ac495dSmrg   return oldval;
24336ac495dSmrg }
24436ac495dSmrg 
24536ac495dSmrg #define SUBWORD_TEST_AND_SET(TYPE, WIDTH)				\
24636ac495dSmrg   TYPE HIDDEN								\
24736ac495dSmrg   __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val)		\
24836ac495dSmrg   {									\
24936ac495dSmrg     int failure;							\
25036ac495dSmrg     unsigned int oldval, newval, shift, mask;				\
25136ac495dSmrg     int *wordptr = (int *) ((unsigned long) ptr & ~3);			\
25236ac495dSmrg 									\
25336ac495dSmrg     shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH;	\
25436ac495dSmrg     mask = MASK_##WIDTH << shift;					\
25536ac495dSmrg 									\
25636ac495dSmrg     do {								\
25736ac495dSmrg       oldval = *wordptr;						\
25836ac495dSmrg       newval = (oldval & ~mask)						\
25936ac495dSmrg 	       | (((unsigned int) val << shift) & mask);		\
26036ac495dSmrg       failure = __kernel_cmpxchg (oldval, newval, wordptr);		\
26136ac495dSmrg     } while (failure != 0);						\
26236ac495dSmrg 									\
26336ac495dSmrg     return (oldval & mask) >> shift;					\
26436ac495dSmrg   }
26536ac495dSmrg 
26636ac495dSmrg SUBWORD_TEST_AND_SET (unsigned short, 2)
26736ac495dSmrg SUBWORD_TEST_AND_SET (unsigned char,  1)
26836ac495dSmrg 
26936ac495dSmrg #define SYNC_LOCK_RELEASE(TYPE, WIDTH)					\
27036ac495dSmrg   void HIDDEN								\
27136ac495dSmrg   __sync_lock_release_##WIDTH (TYPE *ptr)				\
27236ac495dSmrg   {									\
27336ac495dSmrg     /* All writes before this point must be seen before we release	\
27436ac495dSmrg        the lock itself.  */						\
27536ac495dSmrg     __builtin_sync ();							\
27636ac495dSmrg     *ptr = 0;								\
27736ac495dSmrg   }
27836ac495dSmrg 
27936ac495dSmrg SYNC_LOCK_RELEASE (int,   4)
28036ac495dSmrg SYNC_LOCK_RELEASE (short, 2)
28136ac495dSmrg SYNC_LOCK_RELEASE (char,  1)
282