From bb2cfd5fd5e4caa65c31885d93ad01cfdc4776f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Thu, 7 Sep 2023 13:03:19 -0400 Subject: [PATCH] [c11] define mono atomics in terms of standard atomics (#91489) * [c11] define mono atomics in terms of standard atomics * include clang headers in offset_tool.py for wasm * fix clang include path on windows libclang.dll is in bin, not lib * disable stdatomic on android-x86 ATOMIC_LONG_LONG_LOCK_FREE is 1, not 2 * android-x64 also doesn't always have lockfree atomics * fix typo - disable stdatomic atomics when building with MSVC * disable stdatomic on armv7 * switch to opt-in * Apply suggestions from code review - Fix typos. - Use `_Atomic(T)` (macro) instead of `_Atomic T` (qualifier). * add volatile qualifier to casts; fixup whitespace --------- Co-authored-by: Aaron Robinson Co-authored-by: Johan Lorensson --- .../mono/tools/offsets-tool/offsets-tool.py | 4 +- src/mono/mono/utils/atomic.h | 257 +++++++++++++++++- 2 files changed, 259 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/tools/offsets-tool/offsets-tool.py b/src/mono/mono/tools/offsets-tool/offsets-tool.py index 1c0a4e99166a6..3cef0de576c77 100644 --- a/src/mono/mono/tools/offsets-tool/offsets-tool.py +++ b/src/mono/mono/tools/offsets-tool/offsets-tool.py @@ -94,7 +94,9 @@ def require_emscipten_path (args): self.target_args += ["-target", args.abi] else: require_emscipten_path (args) - self.sys_includes = [args.emscripten_path + "/system/include", args.emscripten_path + "/system/include/libc", args.emscripten_path + "/system/lib/libc/musl/arch/emscripten", args.emscripten_path + "/system/lib/libc/musl/include", args.emscripten_path + "/system/lib/libc/musl/arch/generic"] + clang_path = os.path.dirname(args.libclang) + self.sys_includes = [args.emscripten_path + "/system/include", args.emscripten_path + "/system/include/libc", args.emscripten_path + "/system/lib/libc/musl/arch/emscripten", args.emscripten_path + "/system/lib/libc/musl/include", args.emscripten_path + "/system/lib/libc/musl/arch/generic", + clang_path + "/../lib/clang/16/include"] self.target = Target ("TARGET_WASM", None, []) self.target_args += ["-target", args.abi] diff --git a/src/mono/mono/utils/atomic.h b/src/mono/mono/utils/atomic.h index 7d00cbcd7403e..f1f43966272b1 100644 --- a/src/mono/mono/utils/atomic.h +++ b/src/mono/mono/utils/atomic.h @@ -25,7 +25,262 @@ F/MonoDroid( 1568): shared runtime initialization error: Cannot load library: re Apple targets have historically being problematic, xcode 4.6 would miscompile the intrinsic. */ -#if defined(HOST_WIN32) +/* Decide if we will use stdatomic.h */ +/* + * Generally, we can enable C11 atomics if the header is available and if all the primitive types we + * care about (int, long, void*, long long) are lock-free. + * + * Note that we generally don't want the compiler's locking implementation because it may take a + * global lock, in which case if the atomic is used by both the GC implementation and runtime + * internals we may have deadlocks during GC suspend. + * + * It might be possible to use some Mono specific implementation for specific types (e.g. long long) + * on some platforms if the standard atomics for some type are not lock-free (for example: long + * long). We might be able to use a GC-aware lock, for example. + * + */ +#if defined(_MSC_VER) + /* + * we need two things: + * + * 1. MSVC atomics support is not experimental, or we pass /experimental:c11atomics + * + * 2. We build our C++ code with C++23 or later (otherwise MSVC will complain about including + * stdatomic.h) + * + */ +# undef MONO_USE_STDATOMIC +#elif defined(HOST_IOS) || defined(HOST_OSX) || defined(HOST_WATCHOS) || defined(HOST_TVOS) +# define MONO_USE_STDATOMIC 1 +#elif defined(HOST_ANDROID) + /* on Android-x86 ATOMIC_LONG_LONG_LOCK_FREE == 1, not 2 like we want. */ + /* on Andriod-x64 ATOMIC_LONG_LOCK_FREE == 1, not 2 */ + /* on Android-armv7 ATOMIC_INT_LOCK_FREE == 1, not 2 */ +# if defined(HOST_ARM64) +# define MONO_USE_STDATOMIC 1 +# endif +#elif defined(HOST_LINUX) + /* FIXME: probably need arch checks */ +# define MONO_USE_STDATOMIC 1 +#elif defined(HOST_WASI) || defined(HOST_BROWSER) +# define MONO_USE_STDATOMIC 1 +#else +# undef MONO_USE_STDATOMIC +#endif + +#ifdef MONO_USE_STDATOMIC + +#include + +static inline gint32 +mono_atomic_cas_i32 (volatile gint32 *dest, gint32 exch, gint32 comp) +{ + g_static_assert (sizeof (atomic_int) == sizeof (*dest) && ATOMIC_INT_LOCK_FREE == 2); + (void)atomic_compare_exchange_strong ((volatile atomic_int *)dest, &comp, exch); + return comp; +} + +static inline gint64 +mono_atomic_cas_i64 (volatile gint64 *dest, gint64 exch, gint64 comp) +{ +#if SIZEOF_LONG == 8 + g_static_assert (sizeof (atomic_long) == sizeof (*dest) && ATOMIC_LONG_LOCK_FREE == 2); + (void)atomic_compare_exchange_strong ((volatile atomic_long *)dest, (long*)&comp, exch); + return comp; +#elif SIZEOF_LONG_LONG == 8 + g_static_assert (sizeof (atomic_llong) == sizeof (*dest) && ATOMIC_LLONG_LOCK_FREE == 2); + (void)atomic_compare_exchange_strong ((volatile atomic_llong *)dest, (long long*)&comp, exch); + return comp; +#else +#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC +#endif +} + +static inline gpointer +mono_atomic_cas_ptr (volatile gpointer *dest, gpointer exch, gpointer comp) +{ + g_static_assert(ATOMIC_POINTER_LOCK_FREE == 2); + (void)atomic_compare_exchange_strong ((volatile _Atomic(gpointer) *)dest, &comp, exch); + return comp; +} + +static inline gint32 +mono_atomic_fetch_add_i32 (volatile gint32 *dest, gint32 add); +static inline gint64 +mono_atomic_fetch_add_i64 (volatile gint64 *dest, gint64 add); + +static inline gint32 +mono_atomic_add_i32 (volatile gint32 *dest, gint32 add) +{ + // mono_atomic_add_ is supposed to return the value that is stored. + // the atomic_add intrinsic returns the previous value instead. + // so we return prev+add which should be the new value + return mono_atomic_fetch_add_i32 (dest, add) + add; +} + +static inline gint64 +mono_atomic_add_i64 (volatile gint64 *dest, gint64 add) +{ + return mono_atomic_fetch_add_i64 (dest, add) + add; +} + +static inline gint32 +mono_atomic_inc_i32 (volatile gint32 *dest) +{ + return mono_atomic_add_i32 (dest, 1); +} + +static inline gint64 +mono_atomic_inc_i64 (volatile gint64 *dest) +{ + return mono_atomic_add_i64 (dest, 1); +} + +static inline gint32 +mono_atomic_dec_i32 (volatile gint32 *dest) +{ + return mono_atomic_add_i32 (dest, -1); +} + +static inline gint64 +mono_atomic_dec_i64 (volatile gint64 *dest) +{ + return mono_atomic_add_i64 (dest, -1); +} + +static inline gint32 +mono_atomic_xchg_i32 (volatile gint32 *dest, gint32 exch) +{ + g_static_assert (sizeof (atomic_int) == sizeof (*dest) && ATOMIC_INT_LOCK_FREE == 2); + return atomic_exchange ((volatile atomic_int *)dest, exch); +} + +static inline gint64 +mono_atomic_xchg_i64 (volatile gint64 *dest, gint64 exch) +{ +#if SIZEOF_LONG == 8 + g_static_assert (sizeof (atomic_long) == sizeof (*dest) && ATOMIC_LONG_LOCK_FREE == 2); + return atomic_exchange ((volatile atomic_long *)dest, exch); +#elif SIZEOF_LONG_LONG == 8 + g_static_assert (sizeof (atomic_llong) == sizeof (*dest) && ATOMIC_LLONG_LOCK_FREE == 2); + return atomic_exchange ((volatile atomic_llong *)dest, exch); +#else +#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC +#endif +} + +static inline gpointer +mono_atomic_xchg_ptr (volatile gpointer *dest, gpointer exch) +{ + g_static_assert (ATOMIC_POINTER_LOCK_FREE == 2); + return atomic_exchange ((volatile _Atomic(gpointer) *)dest, exch); +} + +static inline gint32 +mono_atomic_fetch_add_i32 (volatile gint32 *dest, gint32 add) +{ + g_static_assert (sizeof (atomic_int) == sizeof (*dest) && ATOMIC_INT_LOCK_FREE == 2); + return atomic_fetch_add ((volatile atomic_int *)dest, add); +} + +static inline gint64 +mono_atomic_fetch_add_i64 (volatile gint64 *dest, gint64 add) +{ +#if SIZEOF_LONG == 8 + g_static_assert (sizeof (atomic_long) == sizeof (*dest) && ATOMIC_LONG_LOCK_FREE == 2); + return atomic_fetch_add ((volatile atomic_long *)dest, add); +#elif SIZEOF_LONG_LONG == 8 + g_static_assert (sizeof (atomic_llong) == sizeof (*dest) && ATOMIC_LLONG_LOCK_FREE == 2); + return atomic_fetch_add ((volatile atomic_llong *)dest, add); +#else +#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC +#endif +} + +static inline gint8 +mono_atomic_load_i8 (volatile gint8 *src) +{ + g_static_assert (sizeof (atomic_char) == sizeof (*src) && ATOMIC_CHAR_LOCK_FREE == 2); + return atomic_load ((volatile atomic_char *)src); +} + +static inline gint16 +mono_atomic_load_i16 (volatile gint16 *src) +{ + g_static_assert (sizeof (atomic_short) == sizeof (*src) && ATOMIC_SHORT_LOCK_FREE == 2); + return atomic_load ((volatile atomic_short *)src); +} + +static inline gint32 mono_atomic_load_i32 (volatile gint32 *src) +{ + g_static_assert (sizeof (atomic_int) == sizeof (*src) && ATOMIC_INT_LOCK_FREE == 2); + return atomic_load ((volatile atomic_int *)src); +} + +static inline gint64 +mono_atomic_load_i64 (volatile gint64 *src) +{ +#if SIZEOF_LONG == 8 + g_static_assert (sizeof (atomic_long) == sizeof (*src) && ATOMIC_LONG_LOCK_FREE == 2); + return atomic_load ((volatile atomic_long *)src); +#elif SIZEOF_LONG_LONG == 8 + g_static_assert (sizeof (atomic_llong) == sizeof (*src) && ATOMIC_LLONG_LOCK_FREE == 2); + return atomic_load ((volatile atomic_llong *)src); +#else +#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC +#endif +} + +static inline gpointer +mono_atomic_load_ptr (volatile gpointer *src) +{ + g_static_assert (ATOMIC_POINTER_LOCK_FREE == 2); + return atomic_load ((volatile _Atomic(gpointer) *)src); +} + +static inline void +mono_atomic_store_i8 (volatile gint8 *dst, gint8 val) +{ + g_static_assert (sizeof (atomic_char) == sizeof (*dst) && ATOMIC_CHAR_LOCK_FREE == 2); + atomic_store ((volatile atomic_char *)dst, val); +} + +static inline void +mono_atomic_store_i16 (volatile gint16 *dst, gint16 val) +{ + g_static_assert (sizeof (atomic_short) == sizeof (*dst) && ATOMIC_SHORT_LOCK_FREE == 2); + atomic_store ((volatile atomic_short *)dst, val); +} + +static inline void +mono_atomic_store_i32 (volatile gint32 *dst, gint32 val) +{ + g_static_assert (sizeof (atomic_int) == sizeof (*dst) && ATOMIC_INT_LOCK_FREE == 2); + atomic_store ((atomic_int *)dst, val); +} + +static inline void +mono_atomic_store_i64 (volatile gint64 *dst, gint64 val) +{ +#if SIZEOF_LONG == 8 + g_static_assert (sizeof (atomic_long) == sizeof (*dst) && ATOMIC_LONG_LOCK_FREE == 2); + atomic_store ((volatile atomic_long *)dst, val); +#elif SIZEOF_LONG_LONG == 8 + g_static_assert (sizeof (atomic_llong) == sizeof (*dst) && ATOMIC_LLONG_LOCK_FREE == 2); + atomic_store ((volatile atomic_llong *)dst, val); +#else +#error gint64 not same size atomic_llong or atomic_long, don't define MONO_USE_STDATOMIC +#endif +} + +static inline void +mono_atomic_store_ptr (volatile gpointer *dst, gpointer val) +{ + g_static_assert (ATOMIC_POINTER_LOCK_FREE == 2); + atomic_store ((volatile _Atomic(gpointer) *)dst, val); +} + +#elif defined(HOST_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN