1  /* Atomic operations used inside libc.  Linux/SH version.
       2     Copyright (C) 2003-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #define __HAVE_64B_ATOMICS 0
      20  #define USE_ATOMIC_COMPILER_BUILTINS 0
      21  
      22  /* XXX Is this actually correct?  */
      23  #define ATOMIC_EXCHANGE_USES_CAS 1
      24  
      25  /* SH kernel has implemented a gUSA ("g" User Space Atomicity) support
      26     for the user space atomicity. The atomicity macros use this scheme.
      27  
      28    Reference:
      29      Niibe Yutaka, "gUSA: Simple and Efficient User Space Atomicity
      30      Emulation with Little Kernel Modification", Linux Conference 2002,
      31      Japan. http://lc.linux.or.jp/lc2002/papers/niibe0919h.pdf (in
      32      Japanese).
      33  
      34      B.N. Bershad, D. Redell, and J. Ellis, "Fast Mutual Exclusion for
      35      Uniprocessors",  Proceedings of the Fifth Architectural Support for
      36      Programming Languages and Operating Systems (ASPLOS), pp. 223-233,
      37      October 1992. http://www.cs.washington.edu/homes/bershad/Papers/Rcs.ps
      38  
      39    SuperH ABI:
      40        r15:    -(size of atomic instruction sequence) < 0
      41        r0:     end point
      42        r1:     saved stack pointer
      43  */
      44  
      45  #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
      46    ({ __typeof (*(mem)) __result; \
      47       __asm __volatile ("\
      48  	mova 1f,r0\n\
      49  	.align 2\n\
      50  	mov r15,r1\n\
      51  	mov #(0f-1f),r15\n\
      52       0: mov.b @%1,%0\n\
      53  	cmp/eq %0,%3\n\
      54  	bf 1f\n\
      55  	mov.b %2,@%1\n\
      56       1: mov r1,r15"\
      57  	: "=&r" (__result) : "u" (mem), "u" (newval), "u" (oldval) \
      58  	: "r0", "r1", "t", "memory"); \
      59       __result; })
      60  
      61  #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
      62    ({ __typeof (*(mem)) __result; \
      63       __asm __volatile ("\
      64  	mova 1f,r0\n\
      65  	mov r15,r1\n\
      66  	.align 2\n\
      67  	mov #(0f-1f),r15\n\
      68  	mov #-8,r15\n\
      69       0: mov.w @%1,%0\n\
      70  	cmp/eq %0,%3\n\
      71  	bf 1f\n\
      72  	mov.w %2,@%1\n\
      73       1: mov r1,r15"\
      74  	: "=&r" (__result) : "u" (mem), "u" (newval), "u" (oldval) \
      75  	: "r0", "r1", "t", "memory"); \
      76       __result; })
      77  
      78  #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
      79    ({ __typeof (*(mem)) __result; \
      80       __asm __volatile ("\
      81  	mova 1f,r0\n\
      82  	.align 2\n\
      83  	mov r15,r1\n\
      84  	mov #(0f-1f),r15\n\
      85       0: mov.l @%1,%0\n\
      86  	cmp/eq %0,%3\n\
      87  	bf 1f\n\
      88  	mov.l %2,@%1\n\
      89       1: mov r1,r15"\
      90  	: "=&r" (__result) : "u" (mem), "u" (newval), "u" (oldval) \
      91  	: "r0", "r1", "t", "memory"); \
      92       __result; })
      93  
      94  /* XXX We do not really need 64-bit compare-and-exchange.  At least
      95     not in the moment.  Using it would mean causing portability
      96     problems since not many other 32-bit architectures have support for
      97     such an operation.  So don't define any code for now.  */
      98  
      99  # define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
     100    (abort (), (__typeof (*mem)) 0)
     101  
     102  #define atomic_exchange_and_add(mem, value) \
     103    ({ __typeof (*(mem)) __result, __tmp, __value = (value); \
     104       if (sizeof (*(mem)) == 1) \
     105         __asm __volatile ("\
     106  	  mova 1f,r0\n\
     107  	  .align 2\n\
     108  	  mov r15,r1\n\
     109  	  mov #(0f-1f),r15\n\
     110         0: mov.b @%2,%0\n\
     111  	  mov %1,r2\n\
     112  	  add %0,r2\n\
     113  	  mov.b r2,@%2\n\
     114         1: mov r1,r15"\
     115  	: "=&r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     116  	: "r0", "r1", "r2", "memory");		       \
     117       else if (sizeof (*(mem)) == 2) \
     118         __asm __volatile ("\
     119  	  mova 1f,r0\n\
     120  	  .align 2\n\
     121  	  mov r15,r1\n\
     122  	  mov #(0f-1f),r15\n\
     123         0: mov.w @%2,%0\n\
     124  	  mov %1,r2\n\
     125  	  add %0,r2\n\
     126  	  mov.w r2,@%2\n\
     127         1: mov r1,r15"\
     128  	: "=&r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     129  	: "r0", "r1", "r2", "memory"); \
     130       else if (sizeof (*(mem)) == 4) \
     131         __asm __volatile ("\
     132  	  mova 1f,r0\n\
     133  	  .align 2\n\
     134  	  mov r15,r1\n\
     135  	  mov #(0f-1f),r15\n\
     136         0: mov.l @%2,%0\n\
     137  	  mov %1,r2\n\
     138  	  add %0,r2\n\
     139  	  mov.l r2,@%2\n\
     140         1: mov r1,r15"\
     141  	: "=&r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     142  	: "r0", "r1", "r2", "memory"); \
     143       else \
     144         { \
     145  	 __typeof (mem) memp = (mem); \
     146  	 do \
     147  	   __result = *memp; \
     148  	 while (__arch_compare_and_exchange_val_64_acq \
     149  		 (memp,	__result + __value, __result) == __result); \
     150  	 (void) __value; \
     151         } \
     152       __result; })
     153  
     154  #define atomic_add(mem, value) \
     155    (void) ({ __typeof (*(mem)) __tmp, __value = (value); \
     156  	    if (sizeof (*(mem)) == 1) \
     157  	      __asm __volatile ("\
     158  		mova 1f,r0\n\
     159  		mov r15,r1\n\
     160  		.align 2\n\
     161  		mov #(0f-1f),r15\n\
     162  	     0: mov.b @%1,r2\n\
     163  		add %0,r2\n\
     164  		mov.b r2,@%1\n\
     165  	     1: mov r1,r15"\
     166  		: "=&r" (__tmp) : "u" (mem), "0" (__value) \
     167  		: "r0", "r1", "r2", "memory"); \
     168  	    else if (sizeof (*(mem)) == 2) \
     169  	      __asm __volatile ("\
     170  		mova 1f,r0\n\
     171  		mov r15,r1\n\
     172  		.align 2\n\
     173  		mov #(0f-1f),r15\n\
     174  	     0: mov.w @%1,r2\n\
     175  		add %0,r2\n\
     176  		mov.w r2,@%1\n\
     177  	     1: mov r1,r15"\
     178  		: "=&r" (__tmp) : "u" (mem), "0" (__value) \
     179  		: "r0", "r1", "r2", "memory"); \
     180  	    else if (sizeof (*(mem)) == 4) \
     181  	      __asm __volatile ("\
     182  		mova 1f,r0\n\
     183  		mov r15,r1\n\
     184  		.align 2\n\
     185  		mov #(0f-1f),r15\n\
     186  	     0: mov.l @%1,r2\n\
     187  		add %0,r2\n\
     188  		mov.l r2,@%1\n\
     189  	     1: mov r1,r15"\
     190  		: "=&r" (__tmp) : "u" (mem), "0" (__value) \
     191  		: "r0", "r1", "r2", "memory"); \
     192  	    else \
     193  	      { \
     194  		__typeof (*(mem)) oldval; \
     195  		__typeof (mem) memp = (mem); \
     196  		do \
     197  		  oldval = *memp; \
     198  		while (__arch_compare_and_exchange_val_64_acq \
     199  			(memp, oldval + __value, oldval) == oldval); \
     200  		(void) __value; \
     201  	      } \
     202  	    })
     203  
     204  #define atomic_add_negative(mem, value) \
     205    ({ unsigned char __result; \
     206       __typeof (*(mem)) __tmp, __value = (value); \
     207       if (sizeof (*(mem)) == 1) \
     208         __asm __volatile ("\
     209  	  mova 1f,r0\n\
     210  	  mov r15,r1\n\
     211  	  .align 2\n\
     212  	  mov #(0f-1f),r15\n\
     213         0: mov.b @%2,r2\n\
     214  	  add %1,r2\n\
     215  	  mov.b r2,@%2\n\
     216         1: mov r1,r15\n\
     217  	  shal r2\n\
     218  	  movt %0"\
     219  	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     220  	: "r0", "r1", "r2", "t", "memory"); \
     221       else if (sizeof (*(mem)) == 2) \
     222         __asm __volatile ("\
     223  	  mova 1f,r0\n\
     224  	  mov r15,r1\n\
     225  	  .align 2\n\
     226  	  mov #(0f-1f),r15\n\
     227         0: mov.w @%2,r2\n\
     228  	  add %1,r2\n\
     229  	  mov.w r2,@%2\n\
     230         1: mov r1,r15\n\
     231  	  shal r2\n\
     232  	  movt %0"\
     233  	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     234  	: "r0", "r1", "r2", "t", "memory"); \
     235       else if (sizeof (*(mem)) == 4) \
     236         __asm __volatile ("\
     237  	  mova 1f,r0\n\
     238  	  mov r15,r1\n\
     239  	  .align 2\n\
     240  	  mov #(0f-1f),r15\n\
     241         0: mov.l @%2,r2\n\
     242  	  add %1,r2\n\
     243  	  mov.l r2,@%2\n\
     244         1: mov r1,r15\n\
     245  	  shal r2\n\
     246  	  movt %0"\
     247  	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     248  	: "r0", "r1", "r2", "t", "memory"); \
     249       else \
     250         abort (); \
     251       __result; })
     252  
     253  #define atomic_add_zero(mem, value) \
     254    ({ unsigned char __result; \
     255       __typeof (*(mem)) __tmp, __value = (value); \
     256       if (sizeof (*(mem)) == 1) \
     257         __asm __volatile ("\
     258  	  mova 1f,r0\n\
     259  	  mov r15,r1\n\
     260  	  .align 2\n\
     261  	  mov #(0f-1f),r15\n\
     262         0: mov.b @%2,r2\n\
     263  	  add %1,r2\n\
     264  	  mov.b r2,@%2\n\
     265         1: mov r1,r15\n\
     266  	  tst r2,r2\n\
     267  	  movt %0"\
     268  	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     269  	: "r0", "r1", "r2", "t", "memory"); \
     270       else if (sizeof (*(mem)) == 2) \
     271         __asm __volatile ("\
     272  	  mova 1f,r0\n\
     273  	  mov r15,r1\n\
     274  	  .align 2\n\
     275  	  mov #(0f-1f),r15\n\
     276         0: mov.w @%2,r2\n\
     277  	  add %1,r2\n\
     278  	  mov.w r2,@%2\n\
     279         1: mov r1,r15\n\
     280  	  tst r2,r2\n\
     281  	  movt %0"\
     282  	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     283  	: "r0", "r1", "r2", "t", "memory"); \
     284       else if (sizeof (*(mem)) == 4) \
     285         __asm __volatile ("\
     286  	  mova 1f,r0\n\
     287  	  mov r15,r1\n\
     288  	  .align 2\n\
     289  	  mov #(0f-1f),r15\n\
     290         0: mov.l @%2,r2\n\
     291  	  add %1,r2\n\
     292  	  mov.l r2,@%2\n\
     293         1: mov r1,r15\n\
     294  	  tst r2,r2\n\
     295  	  movt %0"\
     296  	: "=r" (__result), "=&r" (__tmp) : "u" (mem), "1" (__value) \
     297  	: "r0", "r1", "r2", "t", "memory"); \
     298       else \
     299         abort (); \
     300       __result; })
     301  
     302  #define atomic_increment_and_test(mem) atomic_add_zero((mem), 1)
     303  #define atomic_decrement_and_test(mem) atomic_add_zero((mem), -1)
     304  
     305  #define atomic_bit_set(mem, bit) \
     306    (void) ({ unsigned int __mask = 1 << (bit); \
     307  	    if (sizeof (*(mem)) == 1) \
     308  	      __asm __volatile ("\
     309  		mova 1f,r0\n\
     310  		mov r15,r1\n\
     311  		.align 2\n\
     312  		mov #(0f-1f),r15\n\
     313  	     0: mov.b @%0,r2\n\
     314  		or %1,r2\n\
     315  		mov.b r2,@%0\n\
     316  	     1: mov r1,r15"\
     317  		: : "u" (mem), "u" (__mask) \
     318  		: "r0", "r1", "r2", "memory"); \
     319  	    else if (sizeof (*(mem)) == 2) \
     320  	      __asm __volatile ("\
     321  		mova 1f,r0\n\
     322  		mov r15,r1\n\
     323  		.align 2\n\
     324  		mov #(0f-1f),r15\n\
     325  	     0: mov.w @%0,r2\n\
     326  		or %1,r2\n\
     327  		mov.w r2,@%0\n\
     328  	     1: mov r1,r15"\
     329  		: : "u" (mem), "u" (__mask) \
     330  		: "r0", "r1", "r2", "memory"); \
     331  	    else if (sizeof (*(mem)) == 4) \
     332  	      __asm __volatile ("\
     333  		mova 1f,r0\n\
     334  		mov r15,r1\n\
     335  		.align 2\n\
     336  		mov #(0f-1f),r15\n\
     337  	     0: mov.l @%0,r2\n\
     338  		or %1,r2\n\
     339  		mov.l r2,@%0\n\
     340  	     1: mov r1,r15"\
     341  		: : "u" (mem), "u" (__mask) \
     342  		: "r0", "r1", "r2", "memory"); \
     343  	    else \
     344  	      abort (); \
     345  	    })
     346  
     347  #define atomic_bit_test_set(mem, bit) \
     348    ({ unsigned int __mask = 1 << (bit); \
     349       unsigned int __result = __mask; \
     350       if (sizeof (*(mem)) == 1) \
     351         __asm __volatile ("\
     352  	  mova 1f,r0\n\
     353  	  .align 2\n\
     354  	  mov r15,r1\n\
     355  	  mov #(0f-1f),r15\n\
     356         0: mov.b @%2,r2\n\
     357  	  mov r2,r3\n\
     358  	  or %1,r2\n\
     359  	  mov.b r2,@%2\n\
     360         1: mov r1,r15\n\
     361  	  and r3,%0"\
     362  	: "=&r" (__result), "=&r" (__mask) \
     363  	: "u" (mem), "0" (__result), "1" (__mask) \
     364  	: "r0", "r1", "r2", "r3", "memory");	\
     365       else if (sizeof (*(mem)) == 2) \
     366         __asm __volatile ("\
     367  	  mova 1f,r0\n\
     368  	  .align 2\n\
     369  	  mov r15,r1\n\
     370  	  mov #(0f-1f),r15\n\
     371         0: mov.w @%2,r2\n\
     372  	  mov r2,r3\n\
     373  	  or %1,r2\n\
     374  	  mov.w %1,@%2\n\
     375         1: mov r1,r15\n\
     376  	  and r3,%0"\
     377  	: "=&r" (__result), "=&r" (__mask) \
     378  	: "u" (mem), "0" (__result), "1" (__mask) \
     379  	: "r0", "r1", "r2", "r3", "memory"); \
     380       else if (sizeof (*(mem)) == 4) \
     381         __asm __volatile ("\
     382  	  mova 1f,r0\n\
     383  	  .align 2\n\
     384  	  mov r15,r1\n\
     385  	  mov #(0f-1f),r15\n\
     386         0: mov.l @%2,r2\n\
     387  	  mov r2,r3\n\
     388  	  or r2,%1\n\
     389  	  mov.l %1,@%2\n\
     390         1: mov r1,r15\n\
     391  	  and r3,%0"\
     392  	: "=&r" (__result), "=&r" (__mask) \
     393  	: "u" (mem), "0" (__result), "1" (__mask) \
     394  	: "r0", "r1", "r2", "r3", "memory"); \
     395       else \
     396         abort (); \
     397       __result; })