(root)/
gcc-13.2.0/
libgcc/
config/
pa/
linux-atomic.c
       1  /* Linux-specific atomic operations for PA Linux.
       2     Copyright (C) 2008-2023 Free Software Foundation, Inc.
       3     Based on code contributed by CodeSourcery for ARM EABI Linux.
       4     Modifications for PA Linux by Helge Deller <deller@gmx.de>
       5  
       6  This file is part of GCC.
       7  
       8  GCC is free software; you can redistribute it and/or modify it under
       9  the terms of the GNU General Public License as published by the Free
      10  Software Foundation; either version 3, or (at your option) any later
      11  version.
      12  
      13  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      14  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      15  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      16  for more details.
      17  
      18  Under Section 7 of GPL version 3, you are granted additional
      19  permissions described in the GCC Runtime Library Exception, version
      20  3.1, as published by the Free Software Foundation.
      21  
      22  You should have received a copy of the GNU General Public License and
      23  a copy of the GCC Runtime Library Exception along with this program;
      24  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      25  <http://www.gnu.org/licenses/>.  */
      26  
      27  #define EFAULT  14 
      28  #define EBUSY   16
      29  #define ENOSYS 251 
      30  
      31  #define _ASM_EFAULT "-14"
      32  
      33  typedef unsigned char u8;
      34  typedef short unsigned int u16;
      35  typedef unsigned int u32;
      36  #ifdef __LP64__
      37  typedef long unsigned int u64;
      38  #else
      39  typedef long long unsigned int u64;
      40  #endif
      41  
      42  /* PA-RISC 2.0 supports out-of-order execution for loads and stores.
      43     Thus, we need to synchonize memory accesses.  For more info, see:
      44     "Advanced Performance Features of the 64-bit PA-8000" by Doug Hunt.
      45  
      46     We implement byte, short and int versions of each atomic operation
      47     using the kernel helper defined below.  There is no support for
      48     64-bit operations yet.  */
      49  
      50  /* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace).  */
      51  #define LWS_CAS (sizeof(long) == 4 ? 0 : 1)
      52  
      53  /* Kernel helper for compare-and-exchange a 32-bit value.  */
      54  static inline long
      55  __kernel_cmpxchg (volatile void *mem, int oldval, int newval)
      56  {
      57    register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
      58    register int lws_old asm("r25") = oldval;
      59    register int lws_new asm("r24") = newval;
      60    register long lws_ret   asm("r28");
      61    register long lws_errno asm("r21");
      62    asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
      63  			"ldi	%2, %%r20		\n\t"
      64  			"cmpiclr,<> " _ASM_EFAULT ", %%r21, %%r0\n\t"
      65  			"iitlbp %%r0,(%%sr0, %%r0)	\n\t"
      66  	: "=r" (lws_ret), "=r" (lws_errno)
      67  	: "i" (LWS_CAS), "r" (lws_mem), "r" (lws_old), "r" (lws_new)
      68  	: "r1", "r20", "r22", "r23", "r29", "r31", "memory"
      69    );
      70  
      71    /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains
      72       the old value from memory.  If this value is equal to OLDVAL, the
      73       new value was written to memory.  If not, return -EBUSY.  */
      74    if (!lws_errno && lws_ret != oldval)
      75      return -EBUSY;
      76  
      77    return lws_errno;
      78  }
      79  
      80  static inline long
      81  __kernel_cmpxchg2 (volatile void *mem, const void *oldval, const void *newval,
      82  		   int val_size)
      83  {
      84    register unsigned long lws_mem asm("r26") = (unsigned long) (mem);
      85    register unsigned long lws_old asm("r25") = (unsigned long) oldval;
      86    register unsigned long lws_new asm("r24") = (unsigned long) newval;
      87    register int lws_size asm("r23") = val_size;
      88    register long lws_ret   asm("r28");
      89    register long lws_errno asm("r21");
      90    asm volatile (	"ble	0xb0(%%sr2, %%r0)	\n\t"
      91  			"ldi	%6, %%r20		\n\t"
      92  			"cmpiclr,<> " _ASM_EFAULT ", %%r21, %%r0\n\t"
      93  			"iitlbp %%r0,(%%sr0, %%r0)	\n\t"
      94  	: "=r" (lws_ret), "=r" (lws_errno), "+r" (lws_mem),
      95  	  "+r" (lws_old), "+r" (lws_new), "+r" (lws_size)
      96  	: "i" (2)
      97  	: "r1", "r20", "r22", "r29", "r31", "fr4", "memory"
      98    );
      99  
     100    /* If the kernel LWS call is successful, lws_ret contains 0.  */
     101    if (__builtin_expect (lws_ret == 0, 1))
     102      return 0;
     103  
     104    /* If the kernel LWS call fails with no error, return -EBUSY */
     105    if (__builtin_expect (!lws_errno, 0))
     106      return -EBUSY;
     107  
     108    return lws_errno;
     109  }
     110  #define HIDDEN __attribute__ ((visibility ("hidden")))
     111  
     112  /* Big endian masks  */
     113  #define INVERT_MASK_1 24
     114  #define INVERT_MASK_2 16
     115  
     116  #define MASK_1 0xffu
     117  #define MASK_2 0xffffu
     118  
     119  /* Load value with an atomic processor load if possible.  */
     120  #define ATOMIC_LOAD(TYPE, WIDTH)					\
     121    static inline TYPE							\
     122    atomic_load_##WIDTH (volatile void *ptr)				\
     123    {									\
     124      return *(volatile TYPE *)ptr;					\
     125    }
     126  
     127  #if defined(__LP64__) || defined(__SOFTFP__)
     128  ATOMIC_LOAD (u64, 8)
     129  #else
     130  static inline u64
     131  atomic_load_8 (volatile void *ptr)
     132  {
     133    u64 result;
     134    double tmp;
     135  
     136    asm volatile ("{fldds|fldd} 0(%2),%1\n\t"
     137  		"{fstds|fstd} %1,-16(%%sp)\n\t"
     138  		"{ldws|ldw} -16(%%sp),%0\n\t"
     139  		"{ldws|ldw} -12(%%sp),%R0"
     140  		: "=r" (result), "=f" (tmp) : "r" (ptr): "memory");
     141    return result;
     142  }
     143  #endif
     144  
     145  ATOMIC_LOAD (u32, 4)
     146  ATOMIC_LOAD (u16, 2)
     147  ATOMIC_LOAD (u8, 1)
     148  
     149  #define FETCH_AND_OP_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX)		\
     150    TYPE HIDDEN								\
     151    __sync_fetch_and_##OP##_##WIDTH (volatile void *ptr, TYPE val)	\
     152    {									\
     153      TYPE tmp, newval;							\
     154      long failure;							\
     155  									\
     156      do {								\
     157        tmp = atomic_load_##WIDTH ((volatile TYPE *)ptr);			\
     158        newval = PFX_OP (tmp INF_OP val);					\
     159        failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX);		\
     160      } while (failure != 0);						\
     161  									\
     162      return tmp;								\
     163    }
     164  
     165  FETCH_AND_OP_2 (add,   , +, u64, 8, 3)
     166  FETCH_AND_OP_2 (sub,   , -, u64, 8, 3)
     167  FETCH_AND_OP_2 (or,    , |, u64, 8, 3)
     168  FETCH_AND_OP_2 (and,   , &, u64, 8, 3)
     169  FETCH_AND_OP_2 (xor,   , ^, u64, 8, 3)
     170  FETCH_AND_OP_2 (nand, ~, &, u64, 8, 3)
     171  
     172  FETCH_AND_OP_2 (add,   , +, u16, 2, 1)
     173  FETCH_AND_OP_2 (sub,   , -, u16, 2, 1)
     174  FETCH_AND_OP_2 (or,    , |, u16, 2, 1)
     175  FETCH_AND_OP_2 (and,   , &, u16, 2, 1)
     176  FETCH_AND_OP_2 (xor,   , ^, u16, 2, 1)
     177  FETCH_AND_OP_2 (nand, ~, &, u16, 2, 1)
     178  
     179  FETCH_AND_OP_2 (add,   , +, u8, 1, 0)
     180  FETCH_AND_OP_2 (sub,   , -, u8, 1, 0)
     181  FETCH_AND_OP_2 (or,    , |, u8, 1, 0)
     182  FETCH_AND_OP_2 (and,   , &, u8, 1, 0)
     183  FETCH_AND_OP_2 (xor,   , ^, u8, 1, 0)
     184  FETCH_AND_OP_2 (nand, ~, &, u8, 1, 0)
     185  
     186  #define OP_AND_FETCH_2(OP, PFX_OP, INF_OP, TYPE, WIDTH, INDEX)		\
     187    TYPE HIDDEN								\
     188    __sync_##OP##_and_fetch_##WIDTH (volatile void *ptr, TYPE val)	\
     189    {									\
     190      TYPE tmp, newval;							\
     191      long failure;							\
     192  									\
     193      do {								\
     194        tmp = atomic_load_##WIDTH ((volatile TYPE *)ptr);			\
     195        newval = PFX_OP (tmp INF_OP val);					\
     196        failure = __kernel_cmpxchg2 (ptr, &tmp, &newval, INDEX);		\
     197      } while (failure != 0);						\
     198  									\
     199      return PFX_OP (tmp INF_OP val);					\
     200    }
     201  
     202  OP_AND_FETCH_2 (add,   , +, u64, 8, 3)
     203  OP_AND_FETCH_2 (sub,   , -, u64, 8, 3)
     204  OP_AND_FETCH_2 (or,    , |, u64, 8, 3)
     205  OP_AND_FETCH_2 (and,   , &, u64, 8, 3)
     206  OP_AND_FETCH_2 (xor,   , ^, u64, 8, 3)
     207  OP_AND_FETCH_2 (nand, ~, &, u64, 8, 3)
     208  
     209  OP_AND_FETCH_2 (add,   , +, u16, 2, 1)
     210  OP_AND_FETCH_2 (sub,   , -, u16, 2, 1)
     211  OP_AND_FETCH_2 (or,    , |, u16, 2, 1)
     212  OP_AND_FETCH_2 (and,   , &, u16, 2, 1)
     213  OP_AND_FETCH_2 (xor,   , ^, u16, 2, 1)
     214  OP_AND_FETCH_2 (nand, ~, &, u16, 2, 1)
     215  
     216  OP_AND_FETCH_2 (add,   , +, u8, 1, 0)
     217  OP_AND_FETCH_2 (sub,   , -, u8, 1, 0)
     218  OP_AND_FETCH_2 (or,    , |, u8, 1, 0)
     219  OP_AND_FETCH_2 (and,   , &, u8, 1, 0)
     220  OP_AND_FETCH_2 (xor,   , ^, u8, 1, 0)
     221  OP_AND_FETCH_2 (nand, ~, &, u8, 1, 0)
     222  
     223  #define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP)				\
     224    unsigned int HIDDEN							\
     225    __sync_fetch_and_##OP##_4 (volatile void *ptr, unsigned int val)	\
     226    {									\
     227      unsigned int tmp;							\
     228      long failure;							\
     229  									\
     230      do {								\
     231        tmp = atomic_load_4 ((volatile unsigned int *)ptr);		\
     232        failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val));	\
     233      } while (failure != 0);						\
     234  									\
     235      return tmp;								\
     236    }
     237  
     238  FETCH_AND_OP_WORD (add,   , +)
     239  FETCH_AND_OP_WORD (sub,   , -)
     240  FETCH_AND_OP_WORD (or,    , |)
     241  FETCH_AND_OP_WORD (and,   , &)
     242  FETCH_AND_OP_WORD (xor,   , ^)
     243  FETCH_AND_OP_WORD (nand, ~, &)
     244  
     245  #define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP)				\
     246    unsigned int HIDDEN							\
     247    __sync_##OP##_and_fetch_4 (volatile void *ptr, unsigned int val)	\
     248    {									\
     249      unsigned int tmp;							\
     250      long failure;							\
     251  									\
     252      do {								\
     253        tmp = atomic_load_4 ((volatile unsigned int *)ptr);		\
     254        failure = __kernel_cmpxchg (ptr, tmp, PFX_OP (tmp INF_OP val));	\
     255      } while (failure != 0);						\
     256  									\
     257      return PFX_OP (tmp INF_OP val);					\
     258    }
     259  
     260  OP_AND_FETCH_WORD (add,   , +)
     261  OP_AND_FETCH_WORD (sub,   , -)
     262  OP_AND_FETCH_WORD (or,    , |)
     263  OP_AND_FETCH_WORD (and,   , &)
     264  OP_AND_FETCH_WORD (xor,   , ^)
     265  OP_AND_FETCH_WORD (nand, ~, &)
     266  
     267  typedef unsigned char bool;
     268  
     269  #define COMPARE_AND_SWAP_2(TYPE, WIDTH, INDEX)				\
     270    TYPE HIDDEN								\
     271    __sync_val_compare_and_swap_##WIDTH (volatile void *ptr, TYPE oldval,	\
     272  				       TYPE newval)			\
     273    {									\
     274      TYPE actual_oldval;							\
     275      long fail;								\
     276  									\
     277      while (1)								\
     278        {									\
     279  	actual_oldval = atomic_load_##WIDTH ((volatile TYPE *)ptr);	\
     280  									\
     281  	if (__builtin_expect (oldval != actual_oldval, 0))		\
     282  	  return actual_oldval;						\
     283  									\
     284  	fail = __kernel_cmpxchg2 (ptr, &actual_oldval, &newval, INDEX);	\
     285  									\
     286  	if (__builtin_expect (!fail, 1))				\
     287  	  return actual_oldval;						\
     288        }									\
     289    }									\
     290  									\
     291    _Bool HIDDEN								\
     292    __sync_bool_compare_and_swap_##WIDTH (volatile void *ptr,		\
     293  					TYPE oldval, TYPE newval)	\
     294    {									\
     295      long failure = __kernel_cmpxchg2 (ptr, &oldval, &newval, INDEX);	\
     296      return (failure == 0);						\
     297    }
     298  
     299  COMPARE_AND_SWAP_2 (u64, 8, 3)
     300  COMPARE_AND_SWAP_2 (u16, 2, 1)
     301  COMPARE_AND_SWAP_2 (u8, 1, 0)
     302  
     303  unsigned int HIDDEN
     304  __sync_val_compare_and_swap_4 (volatile void *ptr, unsigned int oldval,
     305  			       unsigned int newval)
     306  {
     307    long fail;
     308    unsigned int actual_oldval;
     309      
     310    while (1)
     311      {
     312        actual_oldval = atomic_load_4 ((volatile unsigned int *)ptr);
     313  
     314        if (__builtin_expect (oldval != actual_oldval, 0))
     315  	return actual_oldval;
     316  
     317        fail = __kernel_cmpxchg (ptr, actual_oldval, newval);
     318    
     319        if (__builtin_expect (!fail, 1))
     320  	return actual_oldval;
     321      }
     322  }
     323  
     324  _Bool HIDDEN
     325  __sync_bool_compare_and_swap_4 (volatile void *ptr, unsigned int oldval,
     326  				unsigned int newval)
     327  {
     328    long failure = __kernel_cmpxchg (ptr, oldval, newval);
     329    return (failure == 0);
     330  }
     331  
     332  #define SYNC_LOCK_TEST_AND_SET_2(TYPE, WIDTH, INDEX)			\
     333  TYPE HIDDEN								\
     334    __sync_lock_test_and_set_##WIDTH (volatile void *ptr, TYPE val)	\
     335    {									\
     336      TYPE oldval;							\
     337      long failure;							\
     338  									\
     339      do {								\
     340        oldval = atomic_load_##WIDTH ((volatile TYPE *)ptr);		\
     341        failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX);		\
     342      } while (failure != 0);						\
     343  									\
     344      return oldval;							\
     345    }
     346  
     347  SYNC_LOCK_TEST_AND_SET_2 (u64, 8, 3)
     348  SYNC_LOCK_TEST_AND_SET_2 (u16, 2, 1)
     349  SYNC_LOCK_TEST_AND_SET_2 (u8, 1, 0)
     350  
     351  u32 HIDDEN
     352  __sync_lock_test_and_set_4 (volatile void *ptr, unsigned int val)
     353  {
     354    long failure;
     355    unsigned int oldval;
     356  
     357    do {
     358      oldval = atomic_load_4 ((volatile unsigned int *)ptr);
     359      failure = __kernel_cmpxchg (ptr, oldval, val);
     360    } while (failure != 0);
     361  
     362    return oldval;
     363  }
     364  
     365  #define SYNC_LOCK_RELEASE_1(TYPE, WIDTH, INDEX)			\
     366    void HIDDEN							\
     367    __sync_lock_release_##WIDTH (volatile void *ptr)		\
     368    {								\
     369      TYPE oldval, val = 0;					\
     370      long failure;						\
     371  								\
     372      do {							\
     373        oldval = atomic_load_##WIDTH ((volatile TYPE *)ptr);	\
     374        failure = __kernel_cmpxchg2 (ptr, &oldval, &val, INDEX);	\
     375      } while (failure != 0);					\
     376    }
     377  
     378  SYNC_LOCK_RELEASE_1 (u64, 8, 3)
     379  SYNC_LOCK_RELEASE_1 (u16, 2, 1)
     380  SYNC_LOCK_RELEASE_1 (u8, 1, 0)
     381  
     382  void HIDDEN
     383  __sync_lock_release_4 (volatile void *ptr)
     384  {
     385    long failure;
     386    unsigned int oldval;
     387  
     388    do {
     389      oldval = atomic_load_4 ((volatile unsigned int *)ptr);
     390      failure = __kernel_cmpxchg (ptr, oldval, 0);
     391    } while (failure != 0);
     392  }
     393  
     394  #ifndef __LP64__
     395  #define SYNC_LOCK_LOAD_2(TYPE, WIDTH, INDEX)				\
     396    TYPE __sync_lock_load_##WIDTH (volatile void *) HIDDEN;		\
     397    TYPE									\
     398    __sync_lock_load_##WIDTH (volatile void *ptr)				\
     399    {									\
     400      TYPE oldval;							\
     401      long failure;							\
     402  									\
     403      do {								\
     404        oldval = atomic_load_##WIDTH ((volatile TYPE *)ptr);		\
     405        failure = __kernel_cmpxchg2 (ptr, &oldval, &oldval, INDEX);	\
     406      } while (failure != 0);						\
     407  									\
     408      return oldval;							\
     409    }
     410  
     411  SYNC_LOCK_LOAD_2 (u64, 8, 3)
     412  #endif