1 #ifndef HARDWARE_ATOMIC_H
    2 #define HARDWARE_ATOMIC_H
    3 
    4 /*
    5 	useful inlines for atomic operations
    6 
    7 	Copyright © 2007-2018 The MorphOS Development Team, All Rights Reserved.
    8 */
    9 
   10 #ifndef EXEC_TYPES_H
   11 # include <exec/types.h>
   12 #endif
   13 
   14 #ifndef DOS_DOS_H
   15 # include <dos/dos.h>
   16 #endif
   17 
   18 #if defined(__GNUC__)
   19 
   20 #if !defined(__PPC__)
   21 # warning "Unsupported CPU family"
   22 #endif
   23 
   24 /*
   25  * NOTES
   26  *
   27  * - The pointer to access must be aligned by 4. Unaligned access will
   28  *   result in exception.
   29  * - To work correctly, all concurrent write accesses to the pointer must
   30  *   be done using ATOMIC-routines.
   31  * - ATOMIC_SPINLOCK lock should not be held for a long time. Also,
   32  *   code keeping the lock most of the time will be equally bad. In fact,
   33  *   for such cases you should think twice whether SignalSemaphore would
   34  *   do a better job.
   35  * - ATOMIC_SPINLOCK does not nest.
   36  */
   37 
   38 static inline LONG _ATOMIC_FETCH(LONG *ptr)
   39 {
   40 	return *ptr;
   41 }
   42 #define ATOMIC_FETCH(p) _ATOMIC_FETCH(p)
   43 
   44 static inline LONG _ATOMIC_STORE(LONG *ptr, LONG newval)
   45 {
   46 	register LONG ret;
   47 
   48 	__asm__ __volatile__ (
   49 	"\n"
   50 	".atomic_store_loop_%=:\n"
   51 	"	lwarx	%0,0,%1\n"
   52 	"	stwcx.	%2,0,%1\n"
   53 	"	bne-	.atomic_store_loop_%=\n"
   54 	: "=&r" (ret)
   55 	: "b" (ptr), "r" (newval)
   56 	: "memory", "cr0");
   57 
   58 	return ret;
   59 }
   60 #define ATOMIC_STORE(p,n) _ATOMIC_STORE(p,n)
   61 
   62 static inline LONG _ATOMIC_ADD(LONG *ptr, LONG val)
   63 {
   64 	register LONG ret, tmp;
   65 
   66 	__asm__ __volatile__ (
   67 	"\n"
   68 	".atomic_add_loop_%=:\n"
   69 	"	lwarx	%0,0,%2\n"
   70 	"	add	%1,%3,%0\n"
   71 	"	stwcx.	%1,0,%2\n"
   72 	"	bne-	.atomic_add_loop_%=\n"
   73 	: "=&b" (ret), "=&r" (tmp)
   74 	: "b" (ptr), "r" (val)
   75 	: "memory", "cr0");
   76 
   77 	return ret;
   78 }
   79 #define ATOMIC_ADD(p,v) _ATOMIC_ADD(p,v)
   80 #define ATOMIC_SUB(p,v) _ATOMIC_ADD(p,-(v))
   81 
   82 static inline ULONG _ATOMIC_OR(ULONG *ptr, ULONG val)
   83 {
   84 	register ULONG ret, tmp;
   85 
   86 	__asm__ __volatile__ (
   87 	"\n"
   88 	".atomic_or_loop_%=:\n"
   89 	"	lwarx	%0,0,%2\n"
   90 	"	or	%1,%3,%0\n"
   91 	"	stwcx.	%1,0,%2\n"
   92 	"	bne-	.atomic_or_loop_%=\n"
   93 	: "=&r" (ret), "=&r" (tmp)
   94 	: "b" (ptr), "r" (val)
   95 	: "memory", "cr0");
   96 
   97 	return ret;
   98 }
   99 #define ATOMIC_OR(p,v) _ATOMIC_OR(p,v)
  100 
  101 static inline ULONG _ATOMIC_AND(ULONG *ptr, ULONG val)
  102 {
  103 	register ULONG ret, tmp;
  104 
  105 	__asm__ __volatile__ (
  106 	"\n"
  107 	".atomic_and_loop_%=:\n"
  108 	"	lwarx	%0,0,%2\n"
  109 	"	and	%1,%3,%0\n"
  110 	"	stwcx.	%1,0,%2\n"
  111 	"	bne-	.atomic_and_loop_%=\n"
  112 	: "=&r" (ret), "=&r" (tmp)
  113 	: "b" (ptr), "r" (val)
  114 	: "memory", "cr0");
  115 
  116 	return ret;
  117 }
  118 #define ATOMIC_AND(p,v) _ATOMIC_AND(p,v)
  119 
  120 static inline ULONG _ATOMIC_XOR(ULONG *ptr, ULONG val)
  121 {
  122 	register ULONG ret, tmp;
  123 
  124 	__asm__ __volatile__ (
  125 	"\n"
  126 	".atomic_xor_loop_%=:\n"
  127 	"	lwarx	%0,0,%2\n"
  128 	"	xor	%1,%3,%0\n"
  129 	"	stwcx.	%1,0,%2\n"
  130 	"	bne-	.atomic_xor_loop_%=\n"
  131 	: "=&r" (ret), "=&r" (tmp)
  132 	: "b" (ptr), "r" (val)
  133 	: "memory", "cr0");
  134 
  135 	return ret;
  136 }
  137 #define ATOMIC_XOR(p,v) _ATOMIC_XOR(p,v)
  138 
  139 static inline LONG _ATOMIC_CMPXCHG(LONG *ptr, LONG old, LONG newval)
  140 {
  141 	register LONG ret;
  142 
  143 	__asm__ __volatile__ (
  144 	"\n"
  145 	".atomic_cmpxchg_loop_%=:\n"
  146 	"	lwarx	%0,0,%2\n"
  147 	"	cmpw	0,%0,%3\n"
  148 	"	bne-	.atomic_cmpxchg_out_%=\n"
  149 	"	stwcx.	%4,0,%2\n"
  150 	"	bne-	.atomic_cmpxchg_loop_%=\n"
  151 	".atomic_cmpxchg_out_%=:\n"
  152 	: "=&r" (ret)
  153 #if __GNUC__ == 3
  154 	: "+m" (*ptr),
  155 #else
  156 	: "m" (*ptr),
  157 #endif
  158 	"b" (ptr), "r" (old), "r" (newval)
  159 	: "memory", "cr0");
  160 
  161 	return ret;
  162 }
  163 #define ATOMIC_CMPXCHG(p,o,n) _ATOMIC_CMPXCHG(p,o,n)
  164 
  165 
  166 static inline ULONG _ATOMIC_BTST(ULONG *ptr, LONG bit)
  167 {
  168 	const ULONG mask = 1 << bit;
  169 	return _ATOMIC_FETCH((LONG *)ptr) & mask;
  170 }
  171 #define ATOMIC_BTST(p,b) _ATOMIC_BTST(p,b)
  172 
  173 static inline ULONG _ATOMIC_BSET(ULONG *ptr, LONG bit)
  174 {
  175 	const ULONG mask = 1 << bit;
  176 	return _ATOMIC_OR(ptr, mask) & mask;
  177 }
  178 #define ATOMIC_BSET(p,b) _ATOMIC_BSET(p,b)
  179 
  180 static inline ULONG _ATOMIC_BCLR(ULONG *ptr, LONG bit)
  181 {
  182 	const ULONG mask = 1 << bit;
  183 	return _ATOMIC_AND(ptr, ~mask) & mask;
  184 }
  185 #define ATOMIC_BCLR(p,b) _ATOMIC_BCLR(p,b)
  186 
  187 static inline ULONG _ATOMIC_BCHG(ULONG *ptr, LONG bit)
  188 {
  189 	const ULONG mask = 1 << bit;
  190 	return _ATOMIC_XOR(ptr, mask) & mask;
  191 }
  192 #define ATOMIC_BCHG(p,b) _ATOMIC_BCHG(p,b)
  193 
  194 /*
  195  * Note: The succ pointer must be the first entry in node.
  196  */
  197 static inline void _ATOMIC_SADDHEAD(APTR *head, APTR node)
  198 {
  199 	register APTR tmp;
  200 
  201 	__asm__ __volatile__ (
  202 	"\n"
  203 	".atomic_saddhead_loop_%=:\n"
  204 	"	lwarx	%0,0,%1\n"
  205 	"	stw	%0,0(%2)\n"
  206 	"	sync\n"
  207 	"	stwcx.	%2,0,%1\n"
  208 	"	bne-	.atomic_saddhead_loop_%=\n"
  209 	: "=&r" (tmp)
  210 	: "b" (head), "b" (node)
  211 	: "memory", "cr0");
  212 }
  213 #define ATOMIC_SADDHEAD(h,n) _ATOMIC_SADDHEAD(h,n)
  214 
  215 static inline APTR _ATOMIC_SREMHEAD(APTR *head)
  216 {
  217 	register APTR node, tmp;
  218 
  219 	__asm__ __volatile__ (
  220 	"\n"
  221 	".atomic_sremhead_loop_%=:\n"
  222 	"	lwarx	%0,0,%2\n"
  223 	"	mr.	%1,%0\n"
  224 	"	beq-	.atomic_sremhead_skip_%=\n"
  225 	"	lwz	%1,0(%0)\n"
  226 	".atomic_sremhead_skip_%=:\n"
  227 	"	stwcx.	%1,0,%2\n"
  228 	"	bne-	.atomic_sremhead_loop_%=\n"
  229 	"	isync\n"
  230 	: "=&b" (node), "=&r" (tmp)
  231 	: "b" (head)
  232 	: "memory", "cr0");
  233 
  234 	return node;
  235 }
  236 #define ATOMIC_SREMHEAD(h) _ATOMIC_SREMHEAD(h)
  237 
  238 /*
  239  * Note: The succ bptr pointer must be the first entry in node.
  240  */
  241 static inline void _ATOMIC_BADDHEAD(BPTR *head, APTR node)
  242 {
  243 	register BPTR tmp;
  244 
  245 	__asm__ __volatile__ (
  246 	"\n"
  247 	".atomic_baddhead_loop_%=:\n"
  248 	"	lwarx	%0,0,%1\n"
  249 	"	stw	%0,0(%2)\n"
  250 	"	sync\n"
  251 	"	stwcx.	%3,0,%1\n"
  252 	"	bne-	.atomic_baddhead_loop_%=\n"
  253 	: "=&r" (tmp)
  254 	: "b" (head), "b" (node), "r" ((BPTR) node >> 2)
  255 	: "memory", "cr0");
  256 }
  257 #define ATOMIC_BADDHEAD(h,n) _ATOMIC_BADDHEAD(h,n)
  258 
  259 static inline APTR _ATOMIC_BREMHEAD(BPTR *head)
  260 {
  261 	register BPTR tmp;
  262 	register APTR node;
  263 
  264 	__asm__ __volatile__ (
  265 	"\n"
  266 	".atomic_bremhead_loop_%=:\n"
  267 	"	lwarx	%0,0,%2\n"
  268 	"	rlwinm.	%1,%0,2,0,29\n"
  269 	"	beq-	.atomic_bremhead_skip_%=\n"
  270 	"	lwz	%0,0(%1)\n"
  271 	".atomic_bremhead_skip_%=:\n"
  272 	"	stwcx.	%0,0,%2\n"
  273 	"	bne-	.atomic_bremhead_loop_%=\n"
  274 	"	isync\n"
  275 	: "=&r" (tmp), "=&b" (node)
  276 	: "b" (head)
  277 	: "memory", "cr0");
  278 
  279 	return node;
  280 }
  281 #define ATOMIC_BREMHEAD(h) _ATOMIC_BREMHEAD(h)
  282 
  283 typedef struct
  284 {
  285 	volatile ULONG as_lock;
  286 } atomic_spinlock;
  287 
  288 static inline void _ATOMIC_SPINLOCK(atomic_spinlock *as)
  289 {
  290 	register LONG tmp;
  291 
  292 	__asm__ __volatile__ (
  293 	"\n"
  294 	".atomic_spinlock_loop_%=:\n"
  295 	"	lwarx	%0,0,%1\n"
  296 	"	cmpwi	%0,0\n"
  297 	"	bne-	.atomic_spinlock_loop_%=\n"
  298 	"	stwcx.	%2,0,%1\n"
  299 	"	bne-	.atomic_spinlock_loop_%=\n"
  300 	"	isync\n"
  301 	: "=&r" (tmp)
  302 	: "b" (&as->as_lock), "r" (1)
  303 	: "memory", "cr0");
  304 }
  305 #define ATOMIC_SPINLOCK(p) _ATOMIC_SPINLOCK(p)
  306 
  307 static inline void _ATOMIC_SPINUNLOCK(atomic_spinlock *as)
  308 {
  309 	__asm__ __volatile__ (
  310 	"\n"
  311 	"	eieio\n"  /* was: sync */
  312 	"	stw	%1,0(%0)\n"
  313 	: /* no result */
  314 	: "b" (&as->as_lock), "r" (0)
  315 	: "memory");
  316 }
  317 #define ATOMIC_SPINUNLOCK(p) _ATOMIC_SPINUNLOCK(p)
  318 
  319 static inline LONG _ATOMIC_SPINTRYLOCK(atomic_spinlock *as)
  320 {
  321 	return _ATOMIC_STORE((LONG *) &as->as_lock, 1) == 0;
  322 }
  323 #define ATOMIC_SPINTRYLOCK(p) _ATOMIC_SPINTRYLOCK(p)
  324 
  325 #else
  326 # warning "Unsupported compiler"
  327 #endif
  328 
  329 #endif /* HARDWARE_ATOMIC_H */