stdatomic.c revision 251539
1/*- 2 * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Copyright (c) 1998 Doug Rabson 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sys/mips/mips/stdatomic.c 251539 2013-06-08 16:24:49Z ed $"); 32 33#include <sys/types.h> 34 35#ifdef _KERNEL 36#include "opt_global.h" 37#endif 38 39/* 40 * Memory barriers. 41 * 42 * It turns out __sync_synchronize() does not emit any code when used 43 * with GCC 4.2. Implement our own version that does work reliably. 44 * 45 * Although __sync_lock_test_and_set() should only perform an acquire 46 * barrier, make it do a full barrier like the other functions. This 47 * should make <stdatomic.h>'s atomic_exchange_explicit() work reliably. 48 */ 49 50static inline void 51mips_sync(void) 52{ 53 54 __asm volatile ( 55#if !defined(_KERNEL) || defined(SMP) 56 ".set noreorder\n" 57 "\tsync\n" 58 "\tnop\n" 59 "\tnop\n" 60 "\tnop\n" 61 "\tnop\n" 62 "\tnop\n" 63 "\tnop\n" 64 "\tnop\n" 65 "\tnop\n" 66 ".set reorder\n" 67#else /* _KERNEL && !SMP */ 68 "" 69#endif /* !KERNEL || SMP */ 70 : : : "memory"); 71} 72 73typedef union { 74 uint8_t v8[4]; 75 uint32_t v32; 76} reg_t; 77 78static inline uint32_t * 79round_to_word(void *ptr) 80{ 81 82 return ((uint32_t *)((intptr_t)ptr & ~3)); 83} 84 85/* 86 * 8-bit routines. 87 */ 88 89static inline void 90put_1(reg_t *r, uint8_t *offset_ptr, uint8_t val) 91{ 92 size_t offset; 93 94 offset = (intptr_t)offset_ptr & 3; 95 r->v8[offset] = val; 96} 97 98static inline uint8_t 99get_1(const reg_t *r, uint8_t *offset_ptr) 100{ 101 size_t offset; 102 103 offset = (intptr_t)offset_ptr & 3; 104 return (r->v8[offset]); 105} 106 107uint8_t 108__sync_lock_test_and_set_1(uint8_t *mem8, uint8_t val8) 109{ 110 uint32_t *mem32; 111 reg_t val32, negmask32, old; 112 uint32_t temp; 113 114 mem32 = round_to_word(mem8); 115 val32.v32 = 0x00000000; 116 put_1(&val32, mem8, val8); 117 negmask32.v32 = 0xffffffff; 118 put_1(&negmask32, mem8, val8); 119 120 mips_sync(); 121 __asm volatile ( 122 "1:" 123 "\tll %0, %5\n" /* Load old value. */ 124 "\tand %2, %4, %0\n" /* Trim out affected part. */ 125 "\tor %2, %3\n" /* Put in the new value. */ 126 "\tsc %2, %1\n" /* Attempt to store. */ 127 "\tbeqz %2, 1b\n" /* Spin if failed. */ 128 : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp) 129 : "r" (val32.v32), "r" (negmask32.v32), "m" (*mem32)); 130 return (get_1(&old, mem8)); 131} 132 133uint8_t 134__sync_val_compare_and_swap_1(uint8_t *mem8, uint8_t expected, uint8_t desired) 135{ 136 uint32_t *mem32; 137 reg_t expected32, desired32, posmask32, negmask32, old; 138 uint32_t temp; 139 140 mem32 = round_to_word(mem8); 141 expected32.v32 = 0x00000000; 142 put_1(&expected32, mem8, expected); 143 desired32.v32 = 0x00000000; 144 put_1(&desired32, mem8, desired); 145 posmask32.v32 = 0x00000000; 146 put_1(&posmask32, mem8, 0xff); 147 negmask32.v32 = ~posmask32.v32; 148 149 mips_sync(); 150 __asm volatile ( 151 "1:" 152 "\tll %0, %7\n" /* Load old value. */ 153 "\tand %2, %5, %0\n" /* Isolate affected part. */ 154 "\tbne %2, %3, 2f\n" /* Compare to expected value. */ 155 "\tand %2, %6, %0\n" /* Trim out affected part. */ 156 "\tor %2, %4\n" /* Put in the new value. */ 157 "\tsc %2, %1\n" /* Attempt to store. */ 158 "\tbeqz %2, 1b\n" /* Spin if failed. */ 159 "2:" 160 : "=&r" (old), "=m" (*mem32), "=&r" (temp) 161 : "r" (expected32.v32), "r" (desired32.v32), 162 "r" (posmask32.v32), "r" (negmask32.v32), "m" (*mem32)); 163 return (get_1(&old, mem8)); 164} 165 166#define EMIT_ARITHMETIC_FETCH_AND_OP_1(name, op) \ 167uint8_t \ 168__sync_##name##_1(uint8_t *mem8, uint8_t val8) \ 169{ \ 170 uint32_t *mem32; \ 171 reg_t val32, posmask32, negmask32, old; \ 172 uint32_t temp1, temp2; \ 173 \ 174 mem32 = round_to_word(mem8); \ 175 val32.v32 = 0x00000000; \ 176 put_1(&val32, mem8, val8); \ 177 posmask32.v32 = 0x00000000; \ 178 put_1(&posmask32, mem8, 0xff); \ 179 negmask32.v32 = ~posmask32.v32; \ 180 \ 181 mips_sync(); \ 182 __asm volatile ( \ 183 "1:" \ 184 "\tll %0, %7\n" /* Load old value. */ \ 185 "\t"op" %2, %0, %4\n" /* Calculate new value. */ \ 186 "\tand %2, %5\n" /* Isolate affected part. */ \ 187 "\tand %3, %6, %0\n" /* Trim out affected part. */ \ 188 "\tor %2, %3\n" /* Put in the new value. */ \ 189 "\tsc %2, %1\n" /* Attempt to store. */ \ 190 "\tbeqz %2, 1b\n" /* Spin if failed. */ \ 191 : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1), \ 192 "=&r" (temp2) \ 193 : "r" (val32.v32), "r" (posmask32.v32), \ 194 "r" (negmask32.v32), "m" (*mem32)); \ 195 return (get_1(&old, mem8)); \ 196} 197 198EMIT_ARITHMETIC_FETCH_AND_OP_1(fetch_and_add, "addu") 199EMIT_ARITHMETIC_FETCH_AND_OP_1(fetch_and_sub, "subu") 200 201#define EMIT_BITWISE_FETCH_AND_OP_1(name, op, idempotence) \ 202uint8_t \ 203__sync_##name##_1(uint8_t *mem8, uint8_t val8) \ 204{ \ 205 uint32_t *mem32; \ 206 reg_t val32, old; \ 207 uint32_t temp; \ 208 \ 209 mem32 = round_to_word(mem8); \ 210 val32.v32 = idempotence ? 0xffffffff : 0x00000000; \ 211 put_1(&val32, mem8, val8); \ 212 \ 213 mips_sync(); \ 214 __asm volatile ( \ 215 "1:" \ 216 "\tll %0, %4\n" /* Load old value. */ \ 217 "\t"op" %2, %3, %0\n" /* Calculate new value. */ \ 218 "\tsc %2, %1\n" /* Attempt to store. */ \ 219 "\tbeqz %2, 1b\n" /* Spin if failed. */ \ 220 : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp) \ 221 : "r" (val32.v32), "m" (*mem32)); \ 222 return (get_1(&old, mem8)); \ 223} 224 225EMIT_BITWISE_FETCH_AND_OP_1(fetch_and_and, "and", 1) 226EMIT_BITWISE_FETCH_AND_OP_1(fetch_and_or, "or", 0) 227EMIT_BITWISE_FETCH_AND_OP_1(fetch_and_xor, "xor", 0) 228 229/* 230 * 16-bit routines. 231 */ 232 233static inline void 234put_2(reg_t *r, uint16_t *offset_ptr, uint16_t val) 235{ 236 size_t offset; 237 union { 238 uint16_t in; 239 uint8_t out[2]; 240 } bytes; 241 242 offset = (intptr_t)offset_ptr & 3; 243 bytes.in = val; 244 r->v8[offset] = bytes.out[0]; 245 r->v8[offset + 1] = bytes.out[1]; 246} 247 248static inline uint16_t 249get_2(const reg_t *r, uint16_t *offset_ptr) 250{ 251 size_t offset; 252 union { 253 uint8_t in[2]; 254 uint16_t out; 255 } bytes; 256 257 offset = (intptr_t)offset_ptr & 3; 258 bytes.in[0] = r->v8[offset]; 259 bytes.in[1] = r->v8[offset + 1]; 260 return (bytes.out); 261} 262 263uint16_t 264__sync_lock_test_and_set_2(uint16_t *mem16, uint16_t val16) 265{ 266 uint32_t *mem32; 267 reg_t val32, negmask32, old; 268 uint32_t temp; 269 270 mem32 = round_to_word(mem16); 271 val32.v32 = 0x00000000; 272 put_2(&val32, mem16, val16); 273 negmask32.v32 = 0xffffffff; 274 put_2(&negmask32, mem16, 0x0000); 275 276 mips_sync(); 277 __asm volatile ( 278 "1:" 279 "\tll %0, %5\n" /* Load old value. */ 280 "\tand %2, %4, %0\n" /* Trim out affected part. */ 281 "\tor %2, %3\n" /* Combine to new value. */ 282 "\tsc %2, %1\n" /* Attempt to store. */ 283 "\tbeqz %2, 1b\n" /* Spin if failed. */ 284 : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp) 285 : "r" (val32.v32), "r" (negmask32.v32), "m" (*mem32)); 286 return (get_2(&old, mem16)); 287} 288 289uint16_t 290__sync_val_compare_and_swap_2(uint16_t *mem16, uint16_t expected, 291 uint16_t desired) 292{ 293 uint32_t *mem32; 294 reg_t expected32, desired32, posmask32, negmask32, old; 295 uint32_t temp; 296 297 mem32 = round_to_word(mem16); 298 expected32.v32 = 0x00000000; 299 put_2(&expected32, mem16, expected); 300 desired32.v32 = 0x00000000; 301 put_2(&desired32, mem16, desired); 302 posmask32.v32 = 0x00000000; 303 put_2(&posmask32, mem16, 0xffff); 304 negmask32.v32 = ~posmask32.v32; 305 306 mips_sync(); 307 __asm volatile ( 308 "1:" 309 "\tll %0, %7\n" /* Load old value. */ 310 "\tand %2, %5, %0\n" /* Isolate affected part. */ 311 "\tbne %2, %3, 2f\n" /* Compare to expected value. */ 312 "\tand %2, %6, %0\n" /* Trim out affected part. */ 313 "\tor %2, %4\n" /* Put in the new value. */ 314 "\tsc %2, %1\n" /* Attempt to store. */ 315 "\tbeqz %2, 1b\n" /* Spin if failed. */ 316 "2:" 317 : "=&r" (old), "=m" (*mem32), "=&r" (temp) 318 : "r" (expected32.v32), "r" (desired32.v32), 319 "r" (posmask32.v32), "r" (negmask32.v32), "m" (*mem32)); 320 return (get_2(&old, mem16)); 321} 322 323#define EMIT_ARITHMETIC_FETCH_AND_OP_2(name, op) \ 324uint16_t \ 325__sync_##name##_2(uint16_t *mem16, uint16_t val16) \ 326{ \ 327 uint32_t *mem32; \ 328 reg_t val32, posmask32, negmask32, old; \ 329 uint32_t temp1, temp2; \ 330 \ 331 mem32 = round_to_word(mem16); \ 332 val32.v32 = 0x00000000; \ 333 put_2(&val32, mem16, val16); \ 334 posmask32.v32 = 0x00000000; \ 335 put_2(&posmask32, mem16, 0xffff); \ 336 negmask32.v32 = ~posmask32.v32; \ 337 \ 338 mips_sync(); \ 339 __asm volatile ( \ 340 "1:" \ 341 "\tll %0, %7\n" /* Load old value. */ \ 342 "\t"op" %2, %0, %4\n" /* Calculate new value. */ \ 343 "\tand %2, %5\n" /* Isolate affected part. */ \ 344 "\tand %3, %6, %0\n" /* Trim out affected part. */ \ 345 "\tor %2, %3\n" /* Combine to new value. */ \ 346 "\tsc %2, %1\n" /* Attempt to store. */ \ 347 "\tbeqz %2, 1b\n" /* Spin if failed. */ \ 348 : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp1), \ 349 "=&r" (temp2) \ 350 : "r" (val32.v32), "r" (posmask32.v32), \ 351 "r" (negmask32.v32), "m" (*mem32)); \ 352 return (get_2(&old, mem16)); \ 353} 354 355EMIT_ARITHMETIC_FETCH_AND_OP_2(fetch_and_add, "addu") 356EMIT_ARITHMETIC_FETCH_AND_OP_2(fetch_and_sub, "subu") 357 358#define EMIT_BITWISE_FETCH_AND_OP_2(name, op, idempotence) \ 359uint16_t \ 360__sync_##name##_2(uint16_t *mem16, uint16_t val16) \ 361{ \ 362 uint32_t *mem32; \ 363 reg_t val32, old; \ 364 uint32_t temp; \ 365 \ 366 mem32 = round_to_word(mem16); \ 367 val32.v32 = idempotence ? 0xffffffff : 0x00000000; \ 368 put_2(&val32, mem16, val16); \ 369 \ 370 mips_sync(); \ 371 __asm volatile ( \ 372 "1:" \ 373 "\tll %0, %4\n" /* Load old value. */ \ 374 "\t"op" %2, %3, %0\n" /* Calculate new value. */ \ 375 "\tsc %2, %1\n" /* Attempt to store. */ \ 376 "\tbeqz %2, 1b\n" /* Spin if failed. */ \ 377 : "=&r" (old.v32), "=m" (*mem32), "=&r" (temp) \ 378 : "r" (val32.v32), "m" (*mem32)); \ 379 return (get_2(&old, mem16)); \ 380} 381 382EMIT_BITWISE_FETCH_AND_OP_2(fetch_and_and, "and", 1) 383EMIT_BITWISE_FETCH_AND_OP_2(fetch_and_or, "or", 0) 384EMIT_BITWISE_FETCH_AND_OP_2(fetch_and_xor, "xor", 0) 385 386/* 387 * 32-bit routines. 388 */ 389 390uint32_t 391__sync_val_compare_and_swap_4(uint32_t *mem, uint32_t expected, 392 uint32_t desired) 393{ 394 uint32_t old, temp; 395 396 mips_sync(); 397 __asm volatile ( 398 "1:" 399 "\tll %0, %5\n" /* Load old value. */ 400 "\tbne %0, %3, 2f\n" /* Compare to expected value. */ 401 "\tmove %2, %4\n" /* Value to store. */ 402 "\tsc %2, %1\n" /* Attempt to store. */ 403 "\tbeqz %2, 1b\n" /* Spin if failed. */ 404 "2:" 405 : "=&r" (old), "=m" (*mem), "=&r" (temp) 406 : "r" (expected), "r" (desired), "m" (*mem)); 407 return (old); 408} 409 410#define EMIT_FETCH_AND_OP_4(name, op) \ 411uint32_t \ 412__sync_##name##_4(uint32_t *mem, uint32_t val) \ 413{ \ 414 uint32_t old, temp; \ 415 \ 416 mips_sync(); \ 417 __asm volatile ( \ 418 "1:" \ 419 "\tll %0, %4\n" /* Load old value. */ \ 420 "\t"op"\n" /* Calculate new value. */ \ 421 "\tsc %2, %1\n" /* Attempt to store. */ \ 422 "\tbeqz %2, 1b\n" /* Spin if failed. */ \ 423 : "=&r" (old), "=m" (*mem), "=&r" (temp) \ 424 : "r" (val), "m" (*mem)); \ 425 return (old); \ 426} 427 428EMIT_FETCH_AND_OP_4(lock_test_and_set, "move %2, %3") 429EMIT_FETCH_AND_OP_4(fetch_and_add, "addu %2, %0, %3") 430EMIT_FETCH_AND_OP_4(fetch_and_and, "and %2, %0, %3") 431EMIT_FETCH_AND_OP_4(fetch_and_or, "or %2, %0, %3") 432EMIT_FETCH_AND_OP_4(fetch_and_sub, "subu %2, %0, %3") 433EMIT_FETCH_AND_OP_4(fetch_and_xor, "xor %2, %0, %3") 434 435/* 436 * 64-bit routines. 437 * 438 * Note: All the 64-bit atomic operations are only atomic when running 439 * in 64-bit mode. It is assumed that code compiled for n32 and n64 fits 440 * into this definition and no further safeties are needed. 441 */ 442 443#if defined(__mips_n32) || defined(__mips_n64) 444 445uint64_t 446__sync_val_compare_and_swap_8(uint64_t *mem, uint64_t expected, 447 uint64_t desired) 448{ 449 uint64_t old, temp; 450 451 mips_sync(); 452 __asm volatile ( 453 "1:" 454 "\tlld %0, %5\n" /* Load old value. */ 455 "\tbne %0, %3, 2f\n" /* Compare to expected value. */ 456 "\tmove %2, %4\n" /* Value to store. */ 457 "\tscd %2, %1\n" /* Attempt to store. */ 458 "\tbeqz %2, 1b\n" /* Spin if failed. */ 459 "2:" 460 : "=&r" (old), "=m" (*mem), "=&r" (temp) 461 : "r" (expected), "r" (desired), "m" (*mem)); 462 return (old); 463} 464 465#define EMIT_FETCH_AND_OP_8(name, op) \ 466uint64_t \ 467__sync_##name##_8(uint64_t *mem, uint64_t val) \ 468{ \ 469 uint64_t old, temp; \ 470 \ 471 mips_sync(); \ 472 __asm volatile ( \ 473 "1:" \ 474 "\tlld %0, %4\n" /* Load old value. */ \ 475 "\t"op"\n" /* Calculate new value. */ \ 476 "\tscd %2, %1\n" /* Attempt to store. */ \ 477 "\tbeqz %2, 1b\n" /* Spin if failed. */ \ 478 : "=&r" (old), "=m" (*mem), "=&r" (temp) \ 479 : "r" (val), "m" (*mem)); \ 480 return (old); \ 481} 482 483EMIT_FETCH_AND_OP_8(lock_test_and_set, "move %2, %3") 484EMIT_FETCH_AND_OP_8(fetch_and_add, "daddu %2, %0, %3") 485EMIT_FETCH_AND_OP_8(fetch_and_and, "and %2, %0, %3") 486EMIT_FETCH_AND_OP_8(fetch_and_or, "or %2, %0, %3") 487EMIT_FETCH_AND_OP_8(fetch_and_sub, "dsubu %2, %0, %3") 488EMIT_FETCH_AND_OP_8(fetch_and_xor, "xor %2, %0, %3") 489 490#endif /* __mips_n32 || __mips_n64 */ 491