1/* Linux-specific atomic operations for m68k Linux. 2 Copyright (C) 2011-2015 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 <asm/unistd.h> 37#include <stdbool.h> 38 39#ifndef __NR_atomic_cmpxchg_32 40#define __NR_atomic_cmpxchg_32 335 41#endif 42 43/* Kernel helper for compare-and-exchange a 32-bit value. */ 44static inline unsigned 45__kernel_cmpxchg (unsigned *mem, unsigned oldval, unsigned newval) 46{ 47 register unsigned *a0 asm("a0") = mem; 48 register unsigned d2 asm("d2") = oldval; 49 register unsigned d1 asm("d1") = newval; 50 register unsigned d0 asm("d0") = __NR_atomic_cmpxchg_32; 51 52 asm volatile ("trap #0" 53 : "=r"(d0), "=r"(d1), "=r"(a0) 54 : "r"(d0), "r"(d1), "r"(d2), "r"(a0) 55 : "memory", "a1"); 56 57 return d0; 58} 59 60#define HIDDEN __attribute__ ((visibility ("hidden"))) 61 62/* Big endian masks */ 63#define INVERT_MASK_1 24 64#define INVERT_MASK_2 16 65 66#define MASK_1 0xffu 67#define MASK_2 0xffffu 68 69#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH 70#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH 71 72#define WORD_SYNC_OP(OP, PFX_OP, INF_OP, RETURN) \ 73 unsigned HIDDEN \ 74 NAME##_##RETURN (OP, 4) (unsigned *ptr, unsigned val) \ 75 { \ 76 unsigned oldval, newval, cmpval = *ptr; \ 77 \ 78 do { \ 79 oldval = cmpval; \ 80 newval = PFX_OP (oldval INF_OP val); \ 81 cmpval = __kernel_cmpxchg (ptr, oldval, newval); \ 82 } while (__builtin_expect (oldval != cmpval, 0)); \ 83 \ 84 return RETURN; \ 85 } 86 87#define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \ 88 TYPE HIDDEN \ 89 NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE sval) \ 90 { \ 91 unsigned *wordptr = (unsigned *) ((unsigned long) ptr & ~3); \ 92 unsigned int mask, shift, oldval, newval, cmpval, wval; \ 93 \ 94 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 95 mask = MASK_##WIDTH << shift; \ 96 wval = (sval & MASK_##WIDTH) << shift; \ 97 \ 98 cmpval = *wordptr; \ 99 do { \ 100 oldval = cmpval; \ 101 newval = PFX_OP (oldval INF_OP wval); \ 102 newval = (newval & mask) | (oldval & ~mask); \ 103 cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \ 104 } while (__builtin_expect (oldval != cmpval, 0)); \ 105 \ 106 return (RETURN >> shift) & MASK_##WIDTH; \ 107 } 108 109WORD_SYNC_OP (add, , +, oldval) 110WORD_SYNC_OP (sub, , -, oldval) 111WORD_SYNC_OP (or, , |, oldval) 112WORD_SYNC_OP (and, , &, oldval) 113WORD_SYNC_OP (xor, , ^, oldval) 114WORD_SYNC_OP (nand, ~, &, oldval) 115 116SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval) 117SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval) 118SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval) 119SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval) 120SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval) 121SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval) 122 123SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval) 124SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval) 125SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval) 126SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval) 127SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval) 128SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval) 129 130WORD_SYNC_OP (add, , +, newval) 131WORD_SYNC_OP (sub, , -, newval) 132WORD_SYNC_OP (or, , |, newval) 133WORD_SYNC_OP (and, , &, newval) 134WORD_SYNC_OP (xor, , ^, newval) 135WORD_SYNC_OP (nand, ~, &, newval) 136 137SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval) 138SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval) 139SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval) 140SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval) 141SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval) 142SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval) 143 144SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval) 145SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval) 146SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval) 147SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval) 148SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval) 149SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval) 150 151unsigned HIDDEN 152__sync_val_compare_and_swap_4 (unsigned *ptr, unsigned oldval, unsigned newval) 153{ 154 return __kernel_cmpxchg (ptr, oldval, newval); 155} 156 157bool HIDDEN 158__sync_bool_compare_and_swap_4 (unsigned *ptr, unsigned oldval, 159 unsigned newval) 160{ 161 return __kernel_cmpxchg (ptr, oldval, newval) == oldval; 162} 163 164#define SUBWORD_VAL_CAS(TYPE, WIDTH) \ 165 TYPE HIDDEN \ 166 __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE soldval, \ 167 TYPE snewval) \ 168 { \ 169 unsigned *wordptr = (unsigned *)((unsigned long) ptr & ~3); \ 170 unsigned int mask, shift, woldval, wnewval; \ 171 unsigned oldval, newval, cmpval; \ 172 \ 173 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 174 mask = MASK_##WIDTH << shift; \ 175 woldval = (soldval & MASK_##WIDTH) << shift; \ 176 wnewval = (snewval & MASK_##WIDTH) << shift; \ 177 cmpval = *wordptr; \ 178 \ 179 do { \ 180 oldval = cmpval; \ 181 if ((oldval & mask) != woldval) \ 182 break; \ 183 newval = (oldval & ~mask) | wnewval; \ 184 cmpval = __kernel_cmpxchg (wordptr, oldval, newval); \ 185 } while (__builtin_expect (oldval != cmpval, 0)); \ 186 \ 187 return (oldval >> shift) & MASK_##WIDTH; \ 188 } 189 190SUBWORD_VAL_CAS (unsigned short, 2) 191SUBWORD_VAL_CAS (unsigned char, 1) 192 193#define SUBWORD_BOOL_CAS(TYPE, WIDTH) \ 194 bool HIDDEN \ 195 __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \ 196 TYPE newval) \ 197 { \ 198 return (__sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval) \ 199 == oldval); \ 200 } 201 202SUBWORD_BOOL_CAS (unsigned short, 2) 203SUBWORD_BOOL_CAS (unsigned char, 1) 204 205#undef NAME_oldval 206#define NAME_oldval(OP, WIDTH) __sync_lock_##OP##_##WIDTH 207#define COMMA , 208 209WORD_SYNC_OP (test_and_set, , COMMA, oldval) 210SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned char, 1, oldval) 211SUBWORD_SYNC_OP (test_and_set, , COMMA, unsigned short, 2, oldval) 212