1 /* FreeBSD specific atomic operations for ARM EABI. 2 Copyright (C) 2015-2016 Free Software Foundation, Inc. 3 4 This file is part of GCC. 5 6 GCC is free software; you can redistribute it and/or modify it under 7 the terms of the GNU General Public License as published by the Free 8 Software Foundation; either version 3, or (at your option) any later 9 version. 10 11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 for more details. 15 16 Under Section 7 of GPL version 3, you are granted additional 17 permissions described in the GCC Runtime Library Exception, version 18 3.1, as published by the Free Software Foundation. 19 20 You should have received a copy of the GNU General Public License and 21 a copy of the GCC Runtime Library Exception along with this program; 22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 <http://www.gnu.org/licenses/>. */ 24 25 #include <sys/types.h> 26 27 #define HIDDEN __attribute__ ((visibility ("hidden"))) 28 29 #define ARM_VECTORS_HIGH 0xffff0000U 30 #define ARM_TP_ADDRESS (ARM_VECTORS_HIGH + 0x1000) 31 #define ARM_RAS_START (ARM_TP_ADDRESS + 4) 32 33 void HIDDEN 34 __sync_synchronize (void) 35 { 36 #if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__) \ 37 || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6T2__) \ 38 || defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__) \ 39 || defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__) 40 #if defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__) 41 __asm __volatile ("dmb" : : : "memory"); 42 #else 43 __asm __volatile ("mcr p15, 0, r0, c7, c10, 5" : : : "memory"); 44 #endif 45 #else 46 __asm __volatile ("nop" : : : "memory"); 47 #endif 48 } 49 50 #if defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__) \ 51 || defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6T2__) \ 52 || defined (__ARM_ARCH_6Z__) || defined (__ARM_ARCH_6ZK__) \ 53 || defined (__ARM_ARCH_7__) || defined (__ARM_ARCH_7A__) 54 55 /* These systems should be supported by the compiler. */ 56 57 #else /* __ARM_ARCH_5__ */ 58 59 #define SYNC_LOCK_TEST_AND_SET_N(N, TYPE, LDR, STR) \ 60 TYPE HIDDEN \ 61 __sync_lock_test_and_set_##N (TYPE *mem, TYPE val) \ 62 { \ 63 unsigned int old, temp, ras_start; \ 64 \ 65 ras_start = ARM_RAS_START; \ 66 __asm volatile ( \ 67 /* Set up Restartable Atomic Sequence. */ \ 68 "1:" \ 69 "\tadr %2, 1b\n" \ 70 "\tstr %2, [%5]\n" \ 71 "\tadr %2, 2f\n" \ 72 "\tstr %2, [%5, #4]\n" \ 73 \ 74 "\t"LDR" %0, %4\n" /* Load old value. */ \ 75 "\t"STR" %3, %1\n" /* Store new value. */ \ 76 \ 77 /* Tear down Restartable Atomic Sequence. */ \ 78 "2:" \ 79 "\tmov %2, #0x00000000\n" \ 80 "\tstr %2, [%5]\n" \ 81 "\tmov %2, #0xffffffff\n" \ 82 "\tstr %2, [%5, #4]\n" \ 83 : "=&r" (old), "=m" (*mem), "=&r" (temp) \ 84 : "r" (val), "m" (*mem), "r" (ras_start)); \ 85 return (old); \ 86 } 87 88 #define SYNC_LOCK_RELEASE_N(N, TYPE) \ 89 void HIDDEN \ 90 __sync_lock_release_##N (TYPE *ptr) \ 91 { \ 92 /* All writes before this point must be seen before we release \ 93 the lock itself. */ \ 94 __sync_synchronize (); \ 95 *ptr = 0; \ 96 } 97 98 #define SYNC_VAL_CAS_N(N, TYPE, LDR, STREQ) \ 99 TYPE HIDDEN \ 100 __sync_val_compare_and_swap_##N (TYPE *mem, TYPE expected, \ 101 TYPE desired) \ 102 { \ 103 unsigned int old, temp, ras_start; \ 104 \ 105 ras_start = ARM_RAS_START; \ 106 __asm volatile ( \ 107 /* Set up Restartable Atomic Sequence. */ \ 108 "1:" \ 109 "\tadr %2, 1b\n" \ 110 "\tstr %2, [%6]\n" \ 111 "\tadr %2, 2f\n" \ 112 "\tstr %2, [%6, #4]\n" \ 113 \ 114 "\t"LDR" %0, %5\n" /* Load old value. */ \ 115 "\tcmp %0, %3\n" /* Compare to expected value. */\ 116 "\t"STREQ" %4, %1\n" /* Store new value. */ \ 117 \ 118 /* Tear down Restartable Atomic Sequence. */ \ 119 "2:" \ 120 "\tmov %2, #0x00000000\n" \ 121 "\tstr %2, [%6]\n" \ 122 "\tmov %2, #0xffffffff\n" \ 123 "\tstr %2, [%6, #4]\n" \ 124 : "=&r" (old), "=m" (*mem), "=&r" (temp) \ 125 : "r" (expected), "r" (desired), "m" (*mem), \ 126 "r" (ras_start)); \ 127 return (old); \ 128 } 129 130 typedef unsigned char bool; 131 132 #define SYNC_BOOL_CAS_N(N, TYPE) \ 133 bool HIDDEN \ 134 __sync_bool_compare_and_swap_##N (TYPE *ptr, TYPE oldval, \ 135 TYPE newval) \ 136 { \ 137 TYPE actual_oldval \ 138 = __sync_val_compare_and_swap_##N (ptr, oldval, newval); \ 139 return (oldval == actual_oldval); \ 140 } 141 142 #define SYNC_FETCH_AND_OP_N(N, TYPE, LDR, STR, NAME, OP) \ 143 TYPE HIDDEN \ 144 __sync_fetch_and_##NAME##_##N (TYPE *mem, TYPE val) \ 145 { \ 146 unsigned int old, temp, ras_start; \ 147 \ 148 ras_start = ARM_RAS_START; \ 149 __asm volatile ( \ 150 /* Set up Restartable Atomic Sequence. */ \ 151 "1:" \ 152 "\tadr %2, 1b\n" \ 153 "\tstr %2, [%5]\n" \ 154 "\tadr %2, 2f\n" \ 155 "\tstr %2, [%5, #4]\n" \ 156 \ 157 "\t"LDR" %0, %4\n" /* Load old value. */ \ 158 "\t"OP" %2, %0, %3\n" /* Calculate new value. */ \ 159 "\t"STR" %2, %1\n" /* Store new value. */ \ 160 \ 161 /* Tear down Restartable Atomic Sequence. */ \ 162 "2:" \ 163 "\tmov %2, #0x00000000\n" \ 164 "\tstr %2, [%5]\n" \ 165 "\tmov %2, #0xffffffff\n" \ 166 "\tstr %2, [%5, #4]\n" \ 167 : "=&r" (old), "=m" (*mem), "=&r" (temp) \ 168 : "r" (val), "m" (*mem), "r" (ras_start)); \ 169 return (old); \ 170 } 171 172 #define SYNC_OP_AND_FETCH_N(N, TYPE, LDR, STR, NAME, OP) \ 173 TYPE HIDDEN \ 174 __sync_##NAME##_and_fetch_##N (TYPE *mem, TYPE val) \ 175 { \ 176 unsigned int old, temp, ras_start; \ 177 \ 178 ras_start = ARM_RAS_START; \ 179 __asm volatile ( \ 180 /* Set up Restartable Atomic Sequence. */ \ 181 "1:" \ 182 "\tadr %2, 1b\n" \ 183 "\tstr %2, [%5]\n" \ 184 "\tadr %2, 2f\n" \ 185 "\tstr %2, [%5, #4]\n" \ 186 \ 187 "\t"LDR" %0, %4\n" /* Load old value. */ \ 188 "\t"OP" %2, %0, %3\n" /* Calculate new value. */ \ 189 "\t"STR" %2, %1\n" /* Store new value. */ \ 190 \ 191 /* Tear down Restartable Atomic Sequence. */ \ 192 "2:" \ 193 "\tmov %2, #0x00000000\n" \ 194 "\tstr %2, [%5]\n" \ 195 "\tmov %2, #0xffffffff\n" \ 196 "\tstr %2, [%5, #4]\n" \ 197 : "=&r" (old), "=m" (*mem), "=&r" (temp) \ 198 : "r" (val), "m" (*mem), "r" (ras_start)); \ 199 return (old); \ 200 } 201 202 #define EMIT_ALL_OPS_N(N, TYPE, LDR, STR, STREQ) \ 203 SYNC_LOCK_TEST_AND_SET_N (N, TYPE, LDR, STR) \ 204 SYNC_LOCK_RELEASE_N (N, TYPE) \ 205 SYNC_VAL_CAS_N (N, TYPE, LDR, STREQ) \ 206 SYNC_BOOL_CAS_N (N, TYPE) \ 207 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, add, "add") \ 208 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, and, "and") \ 209 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, or, "orr") \ 210 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, sub, "sub") \ 211 SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, xor, "eor") \ 212 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, add, "add") \ 213 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, and, "and") \ 214 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, or, "orr") \ 215 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, sub, "sub") \ 216 SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, xor, "eor") 217 218 219 220 EMIT_ALL_OPS_N (1, unsigned char, "ldrb", "strb", "streqb") 221 EMIT_ALL_OPS_N (2, unsigned short, "ldrh", "strh", "streqh") 222 EMIT_ALL_OPS_N (4, unsigned int, "ldr", "str", "streq") 223 224 #endif 225