Path: blob/main/contrib/llvm-project/compiler-rt/lib/builtins/atomic.c
35260 views
//===-- atomic.c - Implement support functions for atomic operations.------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// atomic.c defines a set of functions for performing atomic accesses on9// arbitrary-sized memory locations. This design uses locks that should10// be fast in the uncontended case, for two reasons:11//12// 1) This code must work with C programs that do not link to anything13// (including pthreads) and so it should not depend on any pthread14// functions. If the user wishes to opt into using pthreads, they may do so.15// 2) Atomic operations, rather than explicit mutexes, are most commonly used16// on code where contended operations are rate.17//18// To avoid needing a per-object lock, this code allocates an array of19// locks and hashes the object pointers to find the one that it should use.20// For operations that must be atomic on two locations, the lower lock is21// always acquired first, to avoid deadlock.22//23//===----------------------------------------------------------------------===//2425#include <stdbool.h>26#include <stddef.h>27#include <stdint.h>2829#include "assembly.h"3031// We use __builtin_mem* here to avoid dependencies on libc-provided headers.32#define memcpy __builtin_memcpy33#define memcmp __builtin_memcmp3435// Clang objects if you redefine a builtin. This little hack allows us to36// define a function with the same name as an intrinsic.37#pragma redefine_extname __atomic_load_c SYMBOL_NAME(__atomic_load)38#pragma redefine_extname __atomic_store_c SYMBOL_NAME(__atomic_store)39#pragma redefine_extname __atomic_exchange_c SYMBOL_NAME(__atomic_exchange)40#pragma redefine_extname __atomic_compare_exchange_c SYMBOL_NAME( \41__atomic_compare_exchange)42#pragma redefine_extname __atomic_is_lock_free_c SYMBOL_NAME( \43__atomic_is_lock_free)4445/// Number of locks. This allocates one page on 32-bit platforms, two on46/// 64-bit. This can be specified externally if a different trade between47/// memory usage and contention probability is required for a given platform.48#ifndef SPINLOCK_COUNT49#define SPINLOCK_COUNT (1 << 10)50#endif51static const long SPINLOCK_MASK = SPINLOCK_COUNT - 1;5253////////////////////////////////////////////////////////////////////////////////54// Platform-specific lock implementation. Falls back to spinlocks if none is55// defined. Each platform should define the Lock type, and corresponding56// lock() and unlock() functions.57////////////////////////////////////////////////////////////////////////////////58#if defined(_LIBATOMIC_USE_PTHREAD)59#include <pthread.h>60typedef pthread_mutex_t Lock;61/// Unlock a lock. This is a release operation.62__inline static void unlock(Lock *l) { pthread_mutex_unlock(l); }63/// Locks a lock.64__inline static void lock(Lock *l) { pthread_mutex_lock(l); }65/// locks for atomic operations66static Lock locks[SPINLOCK_COUNT];6768#elif defined(__FreeBSD__) || defined(__DragonFly__)69#include <errno.h>70// clang-format off71#include <sys/types.h>72#include <machine/atomic.h>73#include <sys/umtx.h>74// clang-format on75typedef struct _usem Lock;76__inline static void unlock(Lock *l) {77__c11_atomic_store((_Atomic(uint32_t) *)&l->_count, 1, __ATOMIC_RELEASE);78__c11_atomic_thread_fence(__ATOMIC_SEQ_CST);79if (l->_has_waiters)80_umtx_op(l, UMTX_OP_SEM_WAKE, 1, 0, 0);81}82__inline static void lock(Lock *l) {83uint32_t old = 1;84while (!__c11_atomic_compare_exchange_weak((_Atomic(uint32_t) *)&l->_count,85&old, 0, __ATOMIC_ACQUIRE,86__ATOMIC_RELAXED)) {87_umtx_op(l, UMTX_OP_SEM_WAIT, 0, 0, 0);88old = 1;89}90}91/// locks for atomic operations92static Lock locks[SPINLOCK_COUNT] = {[0 ... SPINLOCK_COUNT - 1] = {0, 1, 0}};9394#elif defined(__APPLE__)95#include <libkern/OSAtomic.h>96typedef OSSpinLock Lock;97__inline static void unlock(Lock *l) { OSSpinLockUnlock(l); }98/// Locks a lock. In the current implementation, this is potentially99/// unbounded in the contended case.100__inline static void lock(Lock *l) { OSSpinLockLock(l); }101static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0102103#else104_Static_assert(__atomic_always_lock_free(sizeof(uintptr_t), 0),105"Implementation assumes lock-free pointer-size cmpxchg");106typedef _Atomic(uintptr_t) Lock;107/// Unlock a lock. This is a release operation.108__inline static void unlock(Lock *l) {109__c11_atomic_store(l, 0, __ATOMIC_RELEASE);110}111/// Locks a lock. In the current implementation, this is potentially112/// unbounded in the contended case.113__inline static void lock(Lock *l) {114uintptr_t old = 0;115while (!__c11_atomic_compare_exchange_weak(l, &old, 1, __ATOMIC_ACQUIRE,116__ATOMIC_RELAXED))117old = 0;118}119/// locks for atomic operations120static Lock locks[SPINLOCK_COUNT];121#endif122123/// Returns a lock to use for a given pointer.124static __inline Lock *lock_for_pointer(void *ptr) {125intptr_t hash = (intptr_t)ptr;126// Disregard the lowest 4 bits. We want all values that may be part of the127// same memory operation to hash to the same value and therefore use the same128// lock.129hash >>= 4;130// Use the next bits as the basis for the hash131intptr_t low = hash & SPINLOCK_MASK;132// Now use the high(er) set of bits to perturb the hash, so that we don't133// get collisions from atomic fields in a single object134hash >>= 16;135hash ^= low;136// Return a pointer to the word to use137return locks + (hash & SPINLOCK_MASK);138}139140/// Macros for determining whether a size is lock free.141#define ATOMIC_ALWAYS_LOCK_FREE_OR_ALIGNED_LOCK_FREE(size, p) \142(__atomic_always_lock_free(size, p) || \143(__atomic_always_lock_free(size, 0) && ((uintptr_t)p % size) == 0))144#define IS_LOCK_FREE_1(p) ATOMIC_ALWAYS_LOCK_FREE_OR_ALIGNED_LOCK_FREE(1, p)145#define IS_LOCK_FREE_2(p) ATOMIC_ALWAYS_LOCK_FREE_OR_ALIGNED_LOCK_FREE(2, p)146#define IS_LOCK_FREE_4(p) ATOMIC_ALWAYS_LOCK_FREE_OR_ALIGNED_LOCK_FREE(4, p)147#define IS_LOCK_FREE_8(p) ATOMIC_ALWAYS_LOCK_FREE_OR_ALIGNED_LOCK_FREE(8, p)148#define IS_LOCK_FREE_16(p) ATOMIC_ALWAYS_LOCK_FREE_OR_ALIGNED_LOCK_FREE(16, p)149150/// Macro that calls the compiler-generated lock-free versions of functions151/// when they exist.152#define TRY_LOCK_FREE_CASE(n, type, ptr) \153case n: \154if (IS_LOCK_FREE_##n(ptr)) { \155LOCK_FREE_ACTION(type); \156} \157break;158#ifdef __SIZEOF_INT128__159#define TRY_LOCK_FREE_CASE_16(p) TRY_LOCK_FREE_CASE(16, __uint128_t, p)160#else161#define TRY_LOCK_FREE_CASE_16(p) /* __uint128_t not available */162#endif163164#define LOCK_FREE_CASES(ptr) \165do { \166switch (size) { \167TRY_LOCK_FREE_CASE(1, uint8_t, ptr) \168TRY_LOCK_FREE_CASE(2, uint16_t, ptr) \169TRY_LOCK_FREE_CASE(4, uint32_t, ptr) \170TRY_LOCK_FREE_CASE(8, uint64_t, ptr) \171TRY_LOCK_FREE_CASE_16(ptr) /* __uint128_t may not be supported */ \172default: \173break; \174} \175} while (0)176177/// Whether atomic operations for the given size (and alignment) are lock-free.178bool __atomic_is_lock_free_c(size_t size, void *ptr) {179#define LOCK_FREE_ACTION(type) return true;180LOCK_FREE_CASES(ptr);181#undef LOCK_FREE_ACTION182return false;183}184185/// An atomic load operation. This is atomic with respect to the source186/// pointer only.187void __atomic_load_c(int size, void *src, void *dest, int model) {188#define LOCK_FREE_ACTION(type) \189*((type *)dest) = __c11_atomic_load((_Atomic(type) *)src, model); \190return;191LOCK_FREE_CASES(src);192#undef LOCK_FREE_ACTION193Lock *l = lock_for_pointer(src);194lock(l);195memcpy(dest, src, size);196unlock(l);197}198199/// An atomic store operation. This is atomic with respect to the destination200/// pointer only.201void __atomic_store_c(int size, void *dest, void *src, int model) {202#define LOCK_FREE_ACTION(type) \203__c11_atomic_store((_Atomic(type) *)dest, *(type *)src, model); \204return;205LOCK_FREE_CASES(dest);206#undef LOCK_FREE_ACTION207Lock *l = lock_for_pointer(dest);208lock(l);209memcpy(dest, src, size);210unlock(l);211}212213/// Atomic compare and exchange operation. If the value at *ptr is identical214/// to the value at *expected, then this copies value at *desired to *ptr. If215/// they are not, then this stores the current value from *ptr in *expected.216///217/// This function returns 1 if the exchange takes place or 0 if it fails.218int __atomic_compare_exchange_c(int size, void *ptr, void *expected,219void *desired, int success, int failure) {220#define LOCK_FREE_ACTION(type) \221return __c11_atomic_compare_exchange_strong( \222(_Atomic(type) *)ptr, (type *)expected, *(type *)desired, success, \223failure)224LOCK_FREE_CASES(ptr);225#undef LOCK_FREE_ACTION226Lock *l = lock_for_pointer(ptr);227lock(l);228if (memcmp(ptr, expected, size) == 0) {229memcpy(ptr, desired, size);230unlock(l);231return 1;232}233memcpy(expected, ptr, size);234unlock(l);235return 0;236}237238/// Performs an atomic exchange operation between two pointers. This is atomic239/// with respect to the target address.240void __atomic_exchange_c(int size, void *ptr, void *val, void *old, int model) {241#define LOCK_FREE_ACTION(type) \242*(type *)old = \243__c11_atomic_exchange((_Atomic(type) *)ptr, *(type *)val, model); \244return;245LOCK_FREE_CASES(ptr);246#undef LOCK_FREE_ACTION247Lock *l = lock_for_pointer(ptr);248lock(l);249memcpy(old, ptr, size);250memcpy(ptr, val, size);251unlock(l);252}253254////////////////////////////////////////////////////////////////////////////////255// Where the size is known at compile time, the compiler may emit calls to256// specialised versions of the above functions.257////////////////////////////////////////////////////////////////////////////////258#ifdef __SIZEOF_INT128__259#define OPTIMISED_CASES \260OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t) \261OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t) \262OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t) \263OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t) \264OPTIMISED_CASE(16, IS_LOCK_FREE_16, __uint128_t)265#else266#define OPTIMISED_CASES \267OPTIMISED_CASE(1, IS_LOCK_FREE_1, uint8_t) \268OPTIMISED_CASE(2, IS_LOCK_FREE_2, uint16_t) \269OPTIMISED_CASE(4, IS_LOCK_FREE_4, uint32_t) \270OPTIMISED_CASE(8, IS_LOCK_FREE_8, uint64_t)271#endif272273#define OPTIMISED_CASE(n, lockfree, type) \274type __atomic_load_##n(type *src, int model) { \275if (lockfree(src)) \276return __c11_atomic_load((_Atomic(type) *)src, model); \277Lock *l = lock_for_pointer(src); \278lock(l); \279type val = *src; \280unlock(l); \281return val; \282}283OPTIMISED_CASES284#undef OPTIMISED_CASE285286#define OPTIMISED_CASE(n, lockfree, type) \287void __atomic_store_##n(type *dest, type val, int model) { \288if (lockfree(dest)) { \289__c11_atomic_store((_Atomic(type) *)dest, val, model); \290return; \291} \292Lock *l = lock_for_pointer(dest); \293lock(l); \294*dest = val; \295unlock(l); \296return; \297}298OPTIMISED_CASES299#undef OPTIMISED_CASE300301#define OPTIMISED_CASE(n, lockfree, type) \302type __atomic_exchange_##n(type *dest, type val, int model) { \303if (lockfree(dest)) \304return __c11_atomic_exchange((_Atomic(type) *)dest, val, model); \305Lock *l = lock_for_pointer(dest); \306lock(l); \307type tmp = *dest; \308*dest = val; \309unlock(l); \310return tmp; \311}312OPTIMISED_CASES313#undef OPTIMISED_CASE314315#define OPTIMISED_CASE(n, lockfree, type) \316bool __atomic_compare_exchange_##n(type *ptr, type *expected, type desired, \317int success, int failure) { \318if (lockfree(ptr)) \319return __c11_atomic_compare_exchange_strong( \320(_Atomic(type) *)ptr, expected, desired, success, failure); \321Lock *l = lock_for_pointer(ptr); \322lock(l); \323if (*ptr == *expected) { \324*ptr = desired; \325unlock(l); \326return true; \327} \328*expected = *ptr; \329unlock(l); \330return false; \331}332OPTIMISED_CASES333#undef OPTIMISED_CASE334335////////////////////////////////////////////////////////////////////////////////336// Atomic read-modify-write operations for integers of various sizes.337////////////////////////////////////////////////////////////////////////////////338#define ATOMIC_RMW(n, lockfree, type, opname, op) \339type __atomic_fetch_##opname##_##n(type *ptr, type val, int model) { \340if (lockfree(ptr)) \341return __c11_atomic_fetch_##opname((_Atomic(type) *)ptr, val, model); \342Lock *l = lock_for_pointer(ptr); \343lock(l); \344type tmp = *ptr; \345*ptr = tmp op val; \346unlock(l); \347return tmp; \348}349350#define ATOMIC_RMW_NAND(n, lockfree, type) \351type __atomic_fetch_nand_##n(type *ptr, type val, int model) { \352if (lockfree(ptr)) \353return __c11_atomic_fetch_nand((_Atomic(type) *)ptr, val, model); \354Lock *l = lock_for_pointer(ptr); \355lock(l); \356type tmp = *ptr; \357*ptr = ~(tmp & val); \358unlock(l); \359return tmp; \360}361362#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, add, +)363OPTIMISED_CASES364#undef OPTIMISED_CASE365#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, sub, -)366OPTIMISED_CASES367#undef OPTIMISED_CASE368#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, and, &)369OPTIMISED_CASES370#undef OPTIMISED_CASE371#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, or, |)372OPTIMISED_CASES373#undef OPTIMISED_CASE374#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW(n, lockfree, type, xor, ^)375OPTIMISED_CASES376#undef OPTIMISED_CASE377// Allow build with clang without __c11_atomic_fetch_nand builtin (pre-14)378#if __has_builtin(__c11_atomic_fetch_nand)379#define OPTIMISED_CASE(n, lockfree, type) ATOMIC_RMW_NAND(n, lockfree, type)380OPTIMISED_CASES381#undef OPTIMISED_CASE382#endif383384385