148fb7bfaSmrg /* Linux-specific atomic operations for m68k Linux.
2*b1e83836Smrg Copyright (C) 2011-2022 Free Software Foundation, Inc.
348fb7bfaSmrg Based on code contributed by CodeSourcery for ARM EABI Linux.
448fb7bfaSmrg
548fb7bfaSmrg This file is part of GCC.
648fb7bfaSmrg
748fb7bfaSmrg GCC is free software; you can redistribute it and/or modify it under
848fb7bfaSmrg the terms of the GNU General Public License as published by the Free
948fb7bfaSmrg Software Foundation; either version 3, or (at your option) any later
1048fb7bfaSmrg version.
1148fb7bfaSmrg
1248fb7bfaSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1348fb7bfaSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
1448fb7bfaSmrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1548fb7bfaSmrg for more details.
1648fb7bfaSmrg
1748fb7bfaSmrg Under Section 7 of GPL version 3, you are granted additional
1848fb7bfaSmrg permissions described in the GCC Runtime Library Exception, version
1948fb7bfaSmrg 3.1, as published by the Free Software Foundation.
2048fb7bfaSmrg
2148fb7bfaSmrg You should have received a copy of the GNU General Public License and
2248fb7bfaSmrg a copy of the GCC Runtime Library Exception along with this program;
2348fb7bfaSmrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
2448fb7bfaSmrg <http://www.gnu.org/licenses/>. */
2548fb7bfaSmrg
2648fb7bfaSmrg /* Coldfire dropped the CAS instruction from the base M68K ISA.
2748fb7bfaSmrg
2848fb7bfaSmrg GCC automatically issues a asm memory barrier when it encounters
2948fb7bfaSmrg a __sync_synchronize builtin. Thus, we do not need to define this
3048fb7bfaSmrg builtin.
3148fb7bfaSmrg
3248fb7bfaSmrg We implement byte, short and int versions of each atomic operation
3348fb7bfaSmrg using the kernel helper defined below. There is no support for
3448fb7bfaSmrg 64-bit operations yet. */
3548fb7bfaSmrg
3648fb7bfaSmrg #include <stdbool.h>
3748fb7bfaSmrg
3848fb7bfaSmrg #ifndef __NR_atomic_cmpxchg_32
3948fb7bfaSmrg #define __NR_atomic_cmpxchg_32 335
4048fb7bfaSmrg #endif
4148fb7bfaSmrg
4248fb7bfaSmrg /* Kernel helper for compare-and-exchange a 32-bit value. */
4348fb7bfaSmrg static inline unsigned
__kernel_cmpxchg(unsigned * mem,unsigned oldval,unsigned newval)4448fb7bfaSmrg __kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval)
4548fb7bfaSmrg {
4648fb7bfaSmrg register unsigned *a0 asm("a0") = mem;
4748fb7bfaSmrg register unsigned d2 asm("d2") = oldval;
4848fb7bfaSmrg register unsigned d1 asm("d1") = newval;
4948fb7bfaSmrg register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32;
5048fb7bfaSmrg
5148fb7bfaSmrg asm volatile ("trap #0"
5248fb7bfaSmrg : "=r"(d0), "=r"(d1), "=r"(a0)
5348fb7bfaSmrg : "r"(d0), "r"(d1), "r"(d2), "r"(a0)
5448fb7bfaSmrg : "memory", "a1");
5548fb7bfaSmrg
5648fb7bfaSmrg return d0;
5748fb7bfaSmrg }
5848fb7bfaSmrg
5948fb7bfaSmrg #define HIDDEN __attribute__ ((visibility ("hidden")))
6048fb7bfaSmrg
6148fb7bfaSmrg /* Big endian masks */
6248fb7bfaSmrg #define INVERT_MASK_1 24
6348fb7bfaSmrg #define INVERT_MASK_2 16
6448fb7bfaSmrg
6548fb7bfaSmrg #define MASK_1 0xffu
6648fb7bfaSmrg #define MASK_2 0xffffu
6748fb7bfaSmrg
6848fb7bfaSmrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
6948fb7bfaSmrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
7048fb7bfaSmrg
7148fb7bfaSmrg #define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN) \
7248fb7bfaSmrg unsigned HIDDEN \
7348fb7bfaSmrg NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val) \
7448fb7bfaSmrg { \
7548fb7bfaSmrg unsigned oldval, newval, cmpval = *ptr; \
7648fb7bfaSmrg \
7748fb7bfaSmrg do { \
7848fb7bfaSmrg oldval = cmpval; \
7948fb7bfaSmrg newval = PFX_OP (oldval INF_OP val); \
8048fb7bfaSmrg cmpval = __kernel_cmpxchg (ptr, oldval, newval); \
8148fb7bfaSmrg } while (__builtin_expect (oldval != cmpval, 0)); \
8248fb7bfaSmrg \
8348fb7bfaSmrg return RETURN; \
8448fb7bfaSmrg }
8548fb7bfaSmrg
8648fb7bfaSmrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \
8748fb7bfaSmrg TYPE HIDDEN \
8848fb7bfaSmrg NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval) \
8948fb7bfaSmrg { \
9048fb7bfaSmrg unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3); \
9148fb7bfaSmrg unsigned int mask, shift, oldval, newval, cmpval, wval; \
9248fb7bfaSmrg \
9348fb7bfaSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
9448fb7bfaSmrg mask = MASK_##WIDTH << shift; \
9548fb7bfaSmrg wval = (sval & MASK_##WIDTH) << shift; \
9648fb7bfaSmrg \
9748fb7bfaSmrg cmpval = *wordptr; \
9848fb7bfaSmrg do { \
9948fb7bfaSmrg oldval = cmpval; \
10048fb7bfaSmrg newval = PFX_OP (oldval INF_OP wval); \
10148fb7bfaSmrg newval = (newval & mask) | (oldval & ~mask); \
10248fb7bfaSmrg cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \
10348fb7bfaSmrg } while (__builtin_expect (oldval != cmpval, 0)); \
10448fb7bfaSmrg \
10548fb7bfaSmrg return (RETURN >> shift) & MASK_##WIDTH; \
10648fb7bfaSmrg }
10748fb7bfaSmrg
10848fb7bfaSmrg WORD_SYNC_OP (add, , +, oldval)
10948fb7bfaSmrg WORD_SYNC_OP (sub, , -, oldval)
11048fb7bfaSmrg WORD_SYNC_OP (or, , |, oldval)
11148fb7bfaSmrg WORD_SYNC_OP (and, , &, oldval)
11248fb7bfaSmrg WORD_SYNC_OP (xor, , ^, oldval)
11348fb7bfaSmrg WORD_SYNC_OP (nand, ~, &, oldval)
11448fb7bfaSmrg
11548fb7bfaSmrg SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval)
11648fb7bfaSmrg SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval)
11748fb7bfaSmrg SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval)
11848fb7bfaSmrg SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval)
11948fb7bfaSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval)
12048fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
12148fb7bfaSmrg
12248fb7bfaSmrg SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval)
12348fb7bfaSmrg SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval)
12448fb7bfaSmrg SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval)
12548fb7bfaSmrg SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval)
12648fb7bfaSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval)
12748fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
12848fb7bfaSmrg
12948fb7bfaSmrg WORD_SYNC_OP (add, , +, newval)
13048fb7bfaSmrg WORD_SYNC_OP (sub, , -, newval)
13148fb7bfaSmrg WORD_SYNC_OP (or, , |, newval)
13248fb7bfaSmrg WORD_SYNC_OP (and, , &, newval)
13348fb7bfaSmrg WORD_SYNC_OP (xor, , ^, newval)
13448fb7bfaSmrg WORD_SYNC_OP (nand, ~, &, newval)
13548fb7bfaSmrg
13648fb7bfaSmrg SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval)
13748fb7bfaSmrg SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval)
13848fb7bfaSmrg SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval)
13948fb7bfaSmrg SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval)
14048fb7bfaSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval)
14148fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
14248fb7bfaSmrg
14348fb7bfaSmrg SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval)
14448fb7bfaSmrg SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval)
14548fb7bfaSmrg SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval)
14648fb7bfaSmrg SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval)
14748fb7bfaSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval)
14848fb7bfaSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
14948fb7bfaSmrg
15048fb7bfaSmrg unsigned HIDDEN
__sync_val_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15148fb7bfaSmrg __sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval)
15248fb7bfaSmrg {
15348fb7bfaSmrg return __kernel_cmpxchg (ptr, oldval, newval);
15448fb7bfaSmrg }
15548fb7bfaSmrg
15648fb7bfaSmrg bool HIDDEN
__sync_bool_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15748fb7bfaSmrg __sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval,
15848fb7bfaSmrg unsigned newval)
15948fb7bfaSmrg {
16048fb7bfaSmrg return __kernel_cmpxchg (ptr, oldval, newval) == oldval;
16148fb7bfaSmrg }
16248fb7bfaSmrg
16348fb7bfaSmrg #define SUBWORD_VAL_CAS(TYPE, WIDTH) \
16448fb7bfaSmrg TYPE HIDDEN \
16548fb7bfaSmrg __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval, \
16648fb7bfaSmrg TYPE snewval) \
16748fb7bfaSmrg { \
16848fb7bfaSmrg unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3); \
16948fb7bfaSmrg unsigned int mask, shift, woldval, wnewval; \
17048fb7bfaSmrg unsigned oldval, newval, cmpval; \
17148fb7bfaSmrg \
17248fb7bfaSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
17348fb7bfaSmrg mask = MASK_##WIDTH << shift; \
17448fb7bfaSmrg woldval = (soldval & MASK_##WIDTH) << shift; \
17548fb7bfaSmrg wnewval = (snewval & MASK_##WIDTH) << shift; \
17648fb7bfaSmrg cmpval = *wordptr; \
17748fb7bfaSmrg \
17848fb7bfaSmrg do { \
17948fb7bfaSmrg oldval = cmpval; \
18048fb7bfaSmrg if ((oldval & mask) != woldval) \
18148fb7bfaSmrg break; \
18248fb7bfaSmrg newval = (oldval & ~mask) | wnewval; \
18348fb7bfaSmrg cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \
18448fb7bfaSmrg } while (__builtin_expect (oldval != cmpval, 0)); \
18548fb7bfaSmrg \
18648fb7bfaSmrg return (oldval >> shift) & MASK_##WIDTH; \
18748fb7bfaSmrg }
18848fb7bfaSmrg
18948fb7bfaSmrg SUBWORD_VAL_CAS (unsigned short, 2)
19048fb7bfaSmrg SUBWORD_VAL_CAS (unsigned char, 1)
19148fb7bfaSmrg
19248fb7bfaSmrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH) \
19348fb7bfaSmrg bool HIDDEN \
19448fb7bfaSmrg __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
19548fb7bfaSmrg TYPE newval) \
19648fb7bfaSmrg { \
19748fb7bfaSmrg return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval) \
19848fb7bfaSmrg == oldval); \
19948fb7bfaSmrg }
20048fb7bfaSmrg
20148fb7bfaSmrg SUBWORD_BOOL_CAS (unsigned short, 2)
20248fb7bfaSmrg SUBWORD_BOOL_CAS (unsigned char, 1)
20348fb7bfaSmrg
20448fb7bfaSmrg #undef NAME_oldval
20548fb7bfaSmrg #define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH
20648fb7bfaSmrg #define COMMA ,
20748fb7bfaSmrg
20848fb7bfaSmrg WORD_SYNC_OP (test_and_set, , COMMA, oldval)
20948fb7bfaSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval)
21048fb7bfaSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval)
211