1/* FreeBSD specific atomic operations for ARM EABI. 2 Copyright (C) 2015 Free Software Foundation, Inc. 3 4This file is part of GCC. 5 6GCC is free software; you can redistribute it and/or modify it under 7the terms of the GNU General Public License as published by the Free 8Software Foundation; either version 3, or (at your option) any later 9version. 10 11GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12WARRANTY; without even the implied warranty of MERCHANTABILITY or 13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14for more details. 15 16Under Section 7 of GPL version 3, you are granted additional 17permissions described in the GCC Runtime Library Exception, version 183.1, as published by the Free Software Foundation. 19 20You should have received a copy of the GNU General Public License and 21a copy of the GCC Runtime Library Exception along with this program; 22see 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 33void 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) \ 60TYPE 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) \ 89void 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) \ 99TYPE 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 130typedef unsigned char bool; 131 132#define SYNC_BOOL_CAS_N(N, TYPE) \ 133bool 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) \ 143TYPE 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) \ 173TYPE 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) \ 203SYNC_LOCK_TEST_AND_SET_N (N, TYPE, LDR, STR) \ 204SYNC_LOCK_RELEASE_N (N, TYPE) \ 205SYNC_VAL_CAS_N (N, TYPE, LDR, STREQ) \ 206SYNC_BOOL_CAS_N (N, TYPE) \ 207SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, add, "add") \ 208SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, and, "and") \ 209SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, or, "orr") \ 210SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, sub, "sub") \ 211SYNC_FETCH_AND_OP_N (N, TYPE, LDR, STR, xor, "eor") \ 212SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, add, "add") \ 213SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, and, "and") \ 214SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, or, "orr") \ 215SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, sub, "sub") \ 216SYNC_OP_AND_FETCH_N (N, TYPE, LDR, STR, xor, "eor") 217 218 219 220EMIT_ALL_OPS_N (1, unsigned char, "ldrb", "strb", "streqb") 221EMIT_ALL_OPS_N (2, unsigned short, "ldrh", "strh", "streqh") 222EMIT_ALL_OPS_N (4, unsigned int, "ldr", "str", "streq") 223 224#endif 225