1/* Linux-specific atomic operations for m68k Linux. 2 Copyright (C) 2011-2020 Free Software Foundation, Inc. 3 Based on code contributed by CodeSourcery for ARM EABI Linux. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17Under Section 7 of GPL version 3, you are granted additional 18permissions described in the GCC Runtime Library Exception, version 193.1, as published by the Free Software Foundation. 20 21You should have received a copy of the GNU General Public License and 22a copy of the GCC Runtime Library Exception along with this program; 23see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 24<http://www.gnu.org/licenses/>. */ 25 26/* Coldfire dropped the CAS instruction from the base M68K ISA. 27 28 GCC automatically issues a asm memory barrier when it encounters 29 a __sync_synchronize builtin. Thus, we do not need to define this 30 builtin. 31 32 We implement byte, short and int versions of each atomic operation 33 using the kernel helper defined below. There is no support for 34 64-bit operations yet. */ 35 36#include <stdbool.h> 37 38#ifndef __NR_atomic_cmpxchg_32 39#define __NR_atomic_cmpxchg_32 335 40#endif 41 42/* Kernel helper for compare-and-exchange a 32-bit value. */ 43static inline unsigned 44__kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval) 45{ 46 register unsigned *a0 asm("a0") = mem; 47 register unsigned d2 asm("d2") = oldval; 48 register unsigned d1 asm("d1") = newval; 49 register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32; 50 51 asm volatile ("trap #0" 52 : "=r"(d0), "=r"(d1), "=r"(a0) 53 : "r"(d0), "r"(d1), "r"(d2), "r"(a0) 54 : "memory", "a1"); 55 56 return d0; 57} 58 59#define HIDDEN __attribute__ ((visibility ("hidden"))) 60 61/* Big endian masks */ 62#define INVERT_MASK_1 24 63#define INVERT_MASK_2 16 64 65#define MASK_1 0xffu 66#define MASK_2 0xffffu 67 68#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH 69#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH 70 71#define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN) \ 72 unsigned HIDDEN \ 73 NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val) \ 74 { \ 75 unsigned oldval, newval, cmpval = *ptr; \ 76 \ 77 do { \ 78 oldval = cmpval; \ 79 newval = PFX_OP (oldval INF_OP val); \ 80 cmpval = __kernel_cmpxchg (ptr, oldval, newval); \ 81 } while (__builtin_expect (oldval != cmpval, 0)); \ 82 \ 83 return RETURN; \ 84 } 85 86#define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \ 87 TYPE HIDDEN \ 88 NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval) \ 89 { \ 90 unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3); \ 91 unsigned int mask, shift, oldval, newval, cmpval, wval; \ 92 \ 93 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 94 mask = MASK_##WIDTH << shift; \ 95 wval = (sval & MASK_##WIDTH) << shift; \ 96 \ 97 cmpval = *wordptr; \ 98 do { \ 99 oldval = cmpval; \ 100 newval = PFX_OP (oldval INF_OP wval); \ 101 newval = (newval & mask) | (oldval & ~mask); \ 102 cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \ 103 } while (__builtin_expect (oldval != cmpval, 0)); \ 104 \ 105 return (RETURN >> shift) & MASK_##WIDTH; \ 106 } 107 108WORD_SYNC_OP (add, , +, oldval) 109WORD_SYNC_OP (sub, , -, oldval) 110WORD_SYNC_OP (or, , |, oldval) 111WORD_SYNC_OP (and, , &, oldval) 112WORD_SYNC_OP (xor, , ^, oldval) 113WORD_SYNC_OP (nand, ~, &, oldval) 114 115SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval) 116SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval) 117SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval) 118SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval) 119SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval) 120SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval) 121 122SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval) 123SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval) 124SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval) 125SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval) 126SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval) 127SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval) 128 129WORD_SYNC_OP (add, , +, newval) 130WORD_SYNC_OP (sub, , -, newval) 131WORD_SYNC_OP (or, , |, newval) 132WORD_SYNC_OP (and, , &, newval) 133WORD_SYNC_OP (xor, , ^, newval) 134WORD_SYNC_OP (nand, ~, &, newval) 135 136SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval) 137SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval) 138SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval) 139SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval) 140SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval) 141SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval) 142 143SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval) 144SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval) 145SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval) 146SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval) 147SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval) 148SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval) 149 150unsigned HIDDEN 151__sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval) 152{ 153 return __kernel_cmpxchg (ptr, oldval, newval); 154} 155 156bool HIDDEN 157__sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval, 158 unsigned newval) 159{ 160 return __kernel_cmpxchg (ptr, oldval, newval) == oldval; 161} 162 163#define SUBWORD_VAL_CAS(TYPE, WIDTH) \ 164 TYPE HIDDEN \ 165 __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval, \ 166 TYPE snewval) \ 167 { \ 168 unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3); \ 169 unsigned int mask, shift, woldval, wnewval; \ 170 unsigned oldval, newval, cmpval; \ 171 \ 172 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 173 mask = MASK_##WIDTH << shift; \ 174 woldval = (soldval & MASK_##WIDTH) << shift; \ 175 wnewval = (snewval & MASK_##WIDTH) << shift; \ 176 cmpval = *wordptr; \ 177 \ 178 do { \ 179 oldval = cmpval; \ 180 if ((oldval & mask) != woldval) \ 181 break; \ 182 newval = (oldval & ~mask) | wnewval; \ 183 cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \ 184 } while (__builtin_expect (oldval != cmpval, 0)); \ 185 \ 186 return (oldval >> shift) & MASK_##WIDTH; \ 187 } 188 189SUBWORD_VAL_CAS (unsigned short, 2) 190SUBWORD_VAL_CAS (unsigned char, 1) 191 192#define SUBWORD_BOOL_CAS(TYPE, WIDTH) \ 193 bool HIDDEN \ 194 __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \ 195 TYPE newval) \ 196 { \ 197 return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval) \ 198 == oldval); \ 199 } 200 201SUBWORD_BOOL_CAS (unsigned short, 2) 202SUBWORD_BOOL_CAS (unsigned char, 1) 203 204#undef NAME_oldval 205#define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH 206#define COMMA , 207 208WORD_SYNC_OP (test_and_set, , COMMA, oldval) 209SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval) 210SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval) 211