| | 1 | /* Haiku-specific atomic operations for libgcc. |
| | 2 | Copyright (C) 2008-2013 Free Software Foundation, Inc. |
| | 3 | Contributed by CodeSourcery. |
| | 4 | Modified for Haiku project by Shantanu Gupta <shans95g@gmail.com> (2014) |
| | 5 | |
| | 6 | This file is part of GCC. |
| | 7 | |
| | 8 | GCC is free software; you can redistribute it and/or modify it under |
| | 9 | the terms of the GNU General Public License as published by the Free |
| | 10 | Software Foundation; either version 3, or (at your option) any later |
| | 11 | version. |
| | 12 | |
| | 13 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| | 14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| | 15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| | 16 | for more details. |
| | 17 | |
| | 18 | Under Section 7 of GPL version 3, you are granted additional |
| | 19 | permissions described in the GCC Runtime Library Exception, version |
| | 20 | 3.1, as published by the Free Software Foundation. |
| | 21 | |
| | 22 | You should have received a copy of the GNU General Public License and |
| | 23 | a copy of the GCC Runtime Library Exception along with this program; |
| | 24 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| | 25 | <http://www.gnu.org/licenses/>. */ |
| | 26 | |
| | 27 | extern int _kern_atomic_test_and_set(int *value, int newValue, int testAgainst); |
| | 28 | extern int _kern_atomic_add(int *value, int addValue); |
| | 29 | extern int _kern_atomic_and(int *value, int andValue); |
| | 30 | extern int _kern_atomic_or(int *value, int orValue); |
| | 31 | |
| | 32 | static inline int _kern_atomic_sub(int *value, int subValue) |
| | 33 | { |
| | 34 | return _kern_atomic_add(value, -subValue); |
| | 35 | } |
| | 36 | |
| | 37 | #define HIDDEN __attribute__ ((visibility ("hidden"))) |
| | 38 | |
| | 39 | #ifdef __ARMEL__ |
| | 40 | #define INVERT_MASK_1 0 |
| | 41 | #define INVERT_MASK_2 0 |
| | 42 | #else |
| | 43 | #define INVERT_MASK_1 24 |
| | 44 | #define INVERT_MASK_2 16 |
| | 45 | #endif |
| | 46 | |
| | 47 | #define MASK_1 0xffu |
| | 48 | #define MASK_2 0xffffu |
| | 49 | |
| | 50 | #define FETCH_AND_OP_WORD(OP) \ |
| | 51 | int HIDDEN \ |
| | 52 | __sync_fetch_and_##OP##_4(int *ptr, int val) \ |
| | 53 | { \ |
| | 54 | return _kern_atomic_##OP (ptr, val); \ |
| | 55 | } |
| | 56 | |
| | 57 | // FETCH_AND_OP_WORD (add) TODO: Cleanup duplication in libroot.so |
| | 58 | FETCH_AND_OP_WORD (sub) |
| | 59 | FETCH_AND_OP_WORD (or) |
| | 60 | FETCH_AND_OP_WORD (and) |
| | 61 | |
| | 62 | #define FETCH_AND_OP_WORD_EX(OP, PFX_OP, INF_OP) \ |
| | 63 | int HIDDEN \ |
| | 64 | __sync_fetch_and_##OP##_4(int *ptr, int val) \ |
| | 65 | { \ |
| | 66 | int tmp; \ |
| | 67 | \ |
| | 68 | do { \ |
| | 69 | tmp = *ptr; \ |
| | 70 | } while(_kern_atomic_test_and_set(ptr, PFX_OP(tmp INF_OP val), tmp) != tmp); \ |
| | 71 | \ |
| | 72 | return tmp; \ |
| | 73 | } |
| | 74 | |
| | 75 | FETCH_AND_OP_WORD_EX (xor, , ^) // TODO: Not supported by haiku ? |
| | 76 | FETCH_AND_OP_WORD_EX (nand, ~, &) // TODO: Not supported by haiku ? |
| | 77 | |
| | 78 | #define OP_AND_FETCH_WORD(OP, INF_OP) \ |
| | 79 | int HIDDEN \ |
| | 80 | __sync_##OP##_and_fetch_4(int *ptr, int val) \ |
| | 81 | { \ |
| | 82 | return (_kern_atomic_##OP (ptr, val) INF_OP val); \ |
| | 83 | } |
| | 84 | |
| | 85 | OP_AND_FETCH_WORD (add, +) |
| | 86 | OP_AND_FETCH_WORD (sub, -) |
| | 87 | OP_AND_FETCH_WORD (or, |) |
| | 88 | OP_AND_FETCH_WORD (and, &) |
| | 89 | |
| | 90 | #define OP_AND_FETCH_WORD_EX(OP, PFX_OP, INF_OP) \ |
| | 91 | int HIDDEN \ |
| | 92 | __sync_##OP##_and_fetch_4(int *ptr, int val) \ |
| | 93 | { \ |
| | 94 | int tmp; \ |
| | 95 | \ |
| | 96 | do { \ |
| | 97 | tmp = *ptr; \ |
| | 98 | } while(_kern_atomic_test_and_set(ptr, PFX_OP(tmp INF_OP val), tmp) != tmp); \ |
| | 99 | \ |
| | 100 | return PFX_OP (tmp INF_OP val); \ |
| | 101 | } |
| | 102 | |
| | 103 | OP_AND_FETCH_WORD_EX (xor, , ^) |
| | 104 | OP_AND_FETCH_WORD_EX (nand, ~, &) |
| | 105 | |
| | 106 | #define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH |
| | 107 | #define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH |
| | 108 | |
| | 109 | #define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \ |
| | 110 | TYPE HIDDEN \ |
| | 111 | NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val) \ |
| | 112 | { \ |
| | 113 | int *wordptr = (int *) ((unsigned int) ptr & ~3); \ |
| | 114 | unsigned int mask, shift, oldval, newval; \ |
| | 115 | \ |
| | 116 | shift = (((unsigned int) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ |
| | 117 | mask = MASK_##WIDTH << shift; \ |
| | 118 | \ |
| | 119 | do { \ |
| | 120 | oldval = *wordptr; \ |
| | 121 | newval = ((PFX_OP (((oldval & mask) >> shift) \ |
| | 122 | INF_OP (unsigned int) val)) << shift) & mask; \ |
| | 123 | newval |= oldval & ~mask; \ |
| | 124 | } while ((unsigned int) _kern_atomic_test_and_set(wordptr, newval, oldval) \ |
| | 125 | != oldval); \ |
| | 126 | \ |
| | 127 | return (RETURN & mask) >> shift; \ |
| | 128 | } |
| | 129 | |
| | 130 | SUBWORD_SYNC_OP (add, , +, short, 2, oldval) |
| | 131 | SUBWORD_SYNC_OP (sub, , -, short, 2, oldval) |
| | 132 | SUBWORD_SYNC_OP (or, , |, short, 2, oldval) |
| | 133 | SUBWORD_SYNC_OP (and, , &, short, 2, oldval) |
| | 134 | SUBWORD_SYNC_OP (xor, , ^, short, 2, oldval) |
| | 135 | SUBWORD_SYNC_OP (nand, ~, &, short, 2, oldval) |
| | 136 | |
| | 137 | SUBWORD_SYNC_OP (add, , +, signed char, 1, oldval) |
| | 138 | SUBWORD_SYNC_OP (sub, , -, signed char, 1, oldval) |
| | 139 | SUBWORD_SYNC_OP (or, , |, signed char, 1, oldval) |
| | 140 | SUBWORD_SYNC_OP (and, , &, signed char, 1, oldval) |
| | 141 | SUBWORD_SYNC_OP (xor, , ^, signed char, 1, oldval) |
| | 142 | SUBWORD_SYNC_OP (nand, ~, &, signed char, 1, oldval) |
| | 143 | |
| | 144 | SUBWORD_SYNC_OP (add, , +, short, 2, newval) |
| | 145 | SUBWORD_SYNC_OP (sub, , -, short, 2, newval) |
| | 146 | SUBWORD_SYNC_OP (or, , |, short, 2, newval) |
| | 147 | SUBWORD_SYNC_OP (and, , &, short, 2, newval) |
| | 148 | SUBWORD_SYNC_OP (xor, , ^, short, 2, newval) |
| | 149 | SUBWORD_SYNC_OP (nand, ~, &, short, 2, newval) |
| | 150 | |
| | 151 | SUBWORD_SYNC_OP (add, , +, signed char, 1, newval) |
| | 152 | SUBWORD_SYNC_OP (sub, , -, signed char, 1, newval) |
| | 153 | SUBWORD_SYNC_OP (or, , |, signed char, 1, newval) |
| | 154 | SUBWORD_SYNC_OP (and, , &, signed char, 1, newval) |
| | 155 | SUBWORD_SYNC_OP (xor, , ^, signed char, 1, newval) |
| | 156 | SUBWORD_SYNC_OP (nand, ~, &, signed char, 1, newval) |
| | 157 | |
| | 158 | int HIDDEN |
| | 159 | __sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval) |
| | 160 | { |
| | 161 | int actual_oldval; |
| | 162 | |
| | 163 | for (;;) |
| | 164 | { |
| | 165 | actual_oldval = *ptr; |
| | 166 | |
| | 167 | if (__builtin_expect (oldval != actual_oldval, 0)) |
| | 168 | return actual_oldval; |
| | 169 | |
| | 170 | if (_kern_atomic_test_and_set(ptr, newval, oldval) == oldval) |
| | 171 | return oldval; |
| | 172 | } |
| | 173 | } |
| | 174 | |
| | 175 | #define SUBWORD_VAL_CAS(TYPE, WIDTH) \ |
| | 176 | TYPE HIDDEN \ |
| | 177 | __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, TYPE newval) \ |
| | 178 | { \ |
| | 179 | int *wordptr = (int *)((unsigned int) ptr & ~3); \ |
| | 180 | unsigned int mask, shift, actual_oldval, actual_newval; \ |
| | 181 | \ |
| | 182 | shift = (((unsigned int) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ |
| | 183 | mask = MASK_##WIDTH << shift; \ |
| | 184 | \ |
| | 185 | for (;;) \ |
| | 186 | { \ |
| | 187 | actual_oldval = *wordptr; \ |
| | 188 | \ |
| | 189 | if (__builtin_expect (((actual_oldval & mask) >> shift) != \ |
| | 190 | ((unsigned int) oldval & MASK_##WIDTH), 0)) \ |
| | 191 | return (actual_oldval & mask) >> shift; \ |
| | 192 | \ |
| | 193 | actual_newval = (actual_oldval & ~mask) \ |
| | 194 | | (((unsigned int) newval << shift) & mask); \ |
| | 195 | \ |
| | 196 | if ((unsigned int) _kern_atomic_test_and_set(wordptr, actual_newval, \ |
| | 197 | actual_oldval) == actual_oldval) \ |
| | 198 | return oldval; \ |
| | 199 | } \ |
| | 200 | } |
| | 201 | |
| | 202 | SUBWORD_VAL_CAS (short, 2) |
| | 203 | SUBWORD_VAL_CAS (signed char, 1) |
| | 204 | |
| | 205 | typedef unsigned char bool; |
| | 206 | |
| | 207 | bool HIDDEN |
| | 208 | __sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval) |
| | 209 | { |
| | 210 | return (_kern_atomic_test_and_set(ptr, newval, oldval) == oldval); |
| | 211 | } |
| | 212 | |
| | 213 | #define SUBWORD_BOOL_CAS(TYPE, WIDTH) \ |
| | 214 | bool HIDDEN \ |
| | 215 | __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, TYPE newval) \ |
| | 216 | { \ |
| | 217 | TYPE actual_oldval = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval); \ |
| | 218 | return (oldval == actual_oldval); \ |
| | 219 | } |
| | 220 | |
| | 221 | SUBWORD_BOOL_CAS (short, 2) |
| | 222 | SUBWORD_BOOL_CAS (signed char, 1) |
| | 223 | |
| | 224 | int HIDDEN |
| | 225 | __sync_lock_test_and_set_4 (int *ptr, int val) |
| | 226 | { |
| | 227 | int oldval; |
| | 228 | |
| | 229 | do { |
| | 230 | oldval = *ptr; |
| | 231 | } while(_kern_atomic_test_and_set(ptr, val, oldval) != oldval); |
| | 232 | |
| | 233 | return oldval; |
| | 234 | } |
| | 235 | |
| | 236 | #define SUBWORD_TEST_AND_SET(TYPE, WIDTH) \ |
| | 237 | TYPE HIDDEN \ |
| | 238 | __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val) \ |
| | 239 | { \ |
| | 240 | unsigned int oldval, newval, shift, mask; \ |
| | 241 | int *wordptr = (int *) ((unsigned int) ptr & ~3); \ |
| | 242 | \ |
| | 243 | shift = (((unsigned int) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ |
| | 244 | mask = MASK_##WIDTH << shift; \ |
| | 245 | \ |
| | 246 | do { \ |
| | 247 | oldval = *wordptr; \ |
| | 248 | newval = (oldval & ~mask) \ |
| | 249 | | (((unsigned int) val << shift) & mask); \ |
| | 250 | } while ((unsigned int) _kern_atomic_test_and_set(wordptr, newval, oldval) \ |
| | 251 | != oldval); \ |
| | 252 | \ |
| | 253 | return (oldval & mask) >> shift; \ |
| | 254 | } |
| | 255 | |
| | 256 | SUBWORD_TEST_AND_SET (short, 2) |
| | 257 | SUBWORD_TEST_AND_SET (signed char, 1) |
| | 258 | |
| | 259 | #define SYNC_LOCK_RELEASE(TYPE, WIDTH) \ |
| | 260 | void HIDDEN \ |
| | 261 | __sync_lock_release_##WIDTH (TYPE *ptr) \ |
| | 262 | { \ |
| | 263 | *ptr = 0; \ |
| | 264 | } |
| | 265 | |
| | 266 | SYNC_LOCK_RELEASE (long long, 8) |
| | 267 | SYNC_LOCK_RELEASE (int, 4) |
| | 268 | SYNC_LOCK_RELEASE (short, 2) |
| | 269 | SYNC_LOCK_RELEASE (char, 1) |
| | 270 | |