136ac495dSmrg /* Linux-specific atomic operations for m68k Linux.
2*8feb0f0bSmrg Copyright (C) 2011-2020 Free Software Foundation, Inc.
336ac495dSmrg Based on code contributed by CodeSourcery for ARM EABI Linux.
436ac495dSmrg
536ac495dSmrg This file is part of GCC.
636ac495dSmrg
736ac495dSmrg GCC is free software; you can redistribute it and/or modify it under
836ac495dSmrg the terms of the GNU General Public License as published by the Free
936ac495dSmrg Software Foundation; either version 3, or (at your option) any later
1036ac495dSmrg version.
1136ac495dSmrg
1236ac495dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
1336ac495dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
1436ac495dSmrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
1536ac495dSmrg for more details.
1636ac495dSmrg
1736ac495dSmrg Under Section 7 of GPL version 3, you are granted additional
1836ac495dSmrg permissions described in the GCC Runtime Library Exception, version
1936ac495dSmrg 3.1, as published by the Free Software Foundation.
2036ac495dSmrg
2136ac495dSmrg You should have received a copy of the GNU General Public License and
2236ac495dSmrg a copy of the GCC Runtime Library Exception along with this program;
2336ac495dSmrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
2436ac495dSmrg <http://www.gnu.org/licenses/>. */
2536ac495dSmrg
2636ac495dSmrg /* Coldfire dropped the CAS instruction from the base M68K ISA.
2736ac495dSmrg
2836ac495dSmrg GCC automatically issues a asm memory barrier when it encounters
2936ac495dSmrg a __sync_synchronize builtin. Thus, we do not need to define this
3036ac495dSmrg builtin.
3136ac495dSmrg
3236ac495dSmrg We implement byte, short and int versions of each atomic operation
3336ac495dSmrg using the kernel helper defined below. There is no support for
3436ac495dSmrg 64-bit operations yet. */
3536ac495dSmrg
3636ac495dSmrg #include <stdbool.h>
3736ac495dSmrg
3836ac495dSmrg #ifndef __NR_atomic_cmpxchg_32
3936ac495dSmrg #define __NR_atomic_cmpxchg_32 335
4036ac495dSmrg #endif
4136ac495dSmrg
4236ac495dSmrg /* Kernel helper for compare-and-exchange a 32-bit value. */
4336ac495dSmrg static inline unsigned
__kernel_cmpxchg(unsigned * mem,unsigned oldval,unsigned newval)4436ac495dSmrg __kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval)
4536ac495dSmrg {
4636ac495dSmrg register unsigned *a0 asm("a0") = mem;
4736ac495dSmrg register unsigned d2 asm("d2") = oldval;
4836ac495dSmrg register unsigned d1 asm("d1") = newval;
4936ac495dSmrg register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32;
5036ac495dSmrg
5136ac495dSmrg asm volatile ("trap #0"
5236ac495dSmrg : "=r"(d0), "=r"(d1), "=r"(a0)
5336ac495dSmrg : "r"(d0), "r"(d1), "r"(d2), "r"(a0)
5436ac495dSmrg : "memory", "a1");
5536ac495dSmrg
5636ac495dSmrg return d0;
5736ac495dSmrg }
5836ac495dSmrg
5936ac495dSmrg #define HIDDEN __attribute__ ((visibility ("hidden")))
6036ac495dSmrg
6136ac495dSmrg /* Big endian masks */
6236ac495dSmrg #define INVERT_MASK_1 24
6336ac495dSmrg #define INVERT_MASK_2 16
6436ac495dSmrg
6536ac495dSmrg #define MASK_1 0xffu
6636ac495dSmrg #define MASK_2 0xffffu
6736ac495dSmrg
6836ac495dSmrg #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH
6936ac495dSmrg #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH
7036ac495dSmrg
7136ac495dSmrg #define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN) \
7236ac495dSmrg unsigned HIDDEN \
7336ac495dSmrg NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val) \
7436ac495dSmrg { \
7536ac495dSmrg unsigned oldval, newval, cmpval = *ptr; \
7636ac495dSmrg \
7736ac495dSmrg do { \
7836ac495dSmrg oldval = cmpval; \
7936ac495dSmrg newval = PFX_OP (oldval INF_OP val); \
8036ac495dSmrg cmpval = __kernel_cmpxchg (ptr, oldval, newval); \
8136ac495dSmrg } while (__builtin_expect (oldval != cmpval, 0)); \
8236ac495dSmrg \
8336ac495dSmrg return RETURN; \
8436ac495dSmrg }
8536ac495dSmrg
8636ac495dSmrg #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \
8736ac495dSmrg TYPE HIDDEN \
8836ac495dSmrg NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval) \
8936ac495dSmrg { \
9036ac495dSmrg unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3); \
9136ac495dSmrg unsigned int mask, shift, oldval, newval, cmpval, wval; \
9236ac495dSmrg \
9336ac495dSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
9436ac495dSmrg mask = MASK_##WIDTH << shift; \
9536ac495dSmrg wval = (sval & MASK_##WIDTH) << shift; \
9636ac495dSmrg \
9736ac495dSmrg cmpval = *wordptr; \
9836ac495dSmrg do { \
9936ac495dSmrg oldval = cmpval; \
10036ac495dSmrg newval = PFX_OP (oldval INF_OP wval); \
10136ac495dSmrg newval = (newval & mask) | (oldval & ~mask); \
10236ac495dSmrg cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \
10336ac495dSmrg } while (__builtin_expect (oldval != cmpval, 0)); \
10436ac495dSmrg \
10536ac495dSmrg return (RETURN >> shift) & MASK_##WIDTH; \
10636ac495dSmrg }
10736ac495dSmrg
10836ac495dSmrg WORD_SYNC_OP (add, , +, oldval)
10936ac495dSmrg WORD_SYNC_OP (sub, , -, oldval)
11036ac495dSmrg WORD_SYNC_OP (or, , |, oldval)
11136ac495dSmrg WORD_SYNC_OP (and, , &, oldval)
11236ac495dSmrg WORD_SYNC_OP (xor, , ^, oldval)
11336ac495dSmrg WORD_SYNC_OP (nand, ~, &, oldval)
11436ac495dSmrg
11536ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval)
11636ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval)
11736ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval)
11836ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval)
11936ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval)
12036ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval)
12136ac495dSmrg
12236ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval)
12336ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval)
12436ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval)
12536ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval)
12636ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval)
12736ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval)
12836ac495dSmrg
12936ac495dSmrg WORD_SYNC_OP (add, , +, newval)
13036ac495dSmrg WORD_SYNC_OP (sub, , -, newval)
13136ac495dSmrg WORD_SYNC_OP (or, , |, newval)
13236ac495dSmrg WORD_SYNC_OP (and, , &, newval)
13336ac495dSmrg WORD_SYNC_OP (xor, , ^, newval)
13436ac495dSmrg WORD_SYNC_OP (nand, ~, &, newval)
13536ac495dSmrg
13636ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval)
13736ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval)
13836ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval)
13936ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval)
14036ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval)
14136ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval)
14236ac495dSmrg
14336ac495dSmrg SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval)
14436ac495dSmrg SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval)
14536ac495dSmrg SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval)
14636ac495dSmrg SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval)
14736ac495dSmrg SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval)
14836ac495dSmrg SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval)
14936ac495dSmrg
15036ac495dSmrg unsigned HIDDEN
__sync_val_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15136ac495dSmrg __sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval)
15236ac495dSmrg {
15336ac495dSmrg return __kernel_cmpxchg (ptr, oldval, newval);
15436ac495dSmrg }
15536ac495dSmrg
15636ac495dSmrg bool HIDDEN
__sync_bool_compare_and_swap_4(unsigned * ptr,unsigned oldval,unsigned newval)15736ac495dSmrg __sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval,
15836ac495dSmrg unsigned newval)
15936ac495dSmrg {
16036ac495dSmrg return __kernel_cmpxchg (ptr, oldval, newval) == oldval;
16136ac495dSmrg }
16236ac495dSmrg
16336ac495dSmrg #define SUBWORD_VAL_CAS(TYPE, WIDTH) \
16436ac495dSmrg TYPE HIDDEN \
16536ac495dSmrg __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval, \
16636ac495dSmrg TYPE snewval) \
16736ac495dSmrg { \
16836ac495dSmrg unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3); \
16936ac495dSmrg unsigned int mask, shift, woldval, wnewval; \
17036ac495dSmrg unsigned oldval, newval, cmpval; \
17136ac495dSmrg \
17236ac495dSmrg shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \
17336ac495dSmrg mask = MASK_##WIDTH << shift; \
17436ac495dSmrg woldval = (soldval & MASK_##WIDTH) << shift; \
17536ac495dSmrg wnewval = (snewval & MASK_##WIDTH) << shift; \
17636ac495dSmrg cmpval = *wordptr; \
17736ac495dSmrg \
17836ac495dSmrg do { \
17936ac495dSmrg oldval = cmpval; \
18036ac495dSmrg if ((oldval & mask) != woldval) \
18136ac495dSmrg break; \
18236ac495dSmrg newval = (oldval & ~mask) | wnewval; \
18336ac495dSmrg cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \
18436ac495dSmrg } while (__builtin_expect (oldval != cmpval, 0)); \
18536ac495dSmrg \
18636ac495dSmrg return (oldval >> shift) & MASK_##WIDTH; \
18736ac495dSmrg }
18836ac495dSmrg
18936ac495dSmrg SUBWORD_VAL_CAS (unsigned short, 2)
19036ac495dSmrg SUBWORD_VAL_CAS (unsigned char, 1)
19136ac495dSmrg
19236ac495dSmrg #define SUBWORD_BOOL_CAS(TYPE, WIDTH) \
19336ac495dSmrg bool HIDDEN \
19436ac495dSmrg __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \
19536ac495dSmrg TYPE newval) \
19636ac495dSmrg { \
19736ac495dSmrg return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval) \
19836ac495dSmrg == oldval); \
19936ac495dSmrg }
20036ac495dSmrg
20136ac495dSmrg SUBWORD_BOOL_CAS (unsigned short, 2)
20236ac495dSmrg SUBWORD_BOOL_CAS (unsigned char, 1)
20336ac495dSmrg
20436ac495dSmrg #undef NAME_oldval
20536ac495dSmrg #define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH
20636ac495dSmrg #define COMMA ,
20736ac495dSmrg
20836ac495dSmrg WORD_SYNC_OP (test_and_set, , COMMA, oldval)
20936ac495dSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval)
21036ac495dSmrg SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval)
211