Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] Second try at using C11 atomics #91808

Merged
merged 6 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/mono/mono/metadata/icall-signatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
// mono_icall_sig_void_int32
// mono_icall_sig_void_object
// mono_icall_sig_void_ptr
// mono_icall_sig_bool_ptr_ptrref
// mono_icall_sig_boolean_ptr_ptrref
// mono_icall_sig_double_double_double
// mono_icall_sig_float_float_float
// mono_icall_sig_int_obj_ptr
Expand Down Expand Up @@ -94,7 +94,7 @@
// mono_icall_sig_void_ptr_ptr
// mono_icall_sig_void_ptr_ptrref
// mono_icall_sig_void_uint32_ptrref
// mono_icall_sig_bool_ptr_int32_ptrref
// mono_icall_sig_boolean_ptr_int32_ptrref
// mono_icall_sig_int32_int32_ptr_ptrref
// mono_icall_sig_int32_ptr_int32_ptr
// mono_icall_sig_int32_ptr_int32_ptrref
Expand Down Expand Up @@ -182,7 +182,7 @@ ICALL_SIG (2, (void, int)) \
ICALL_SIG (2, (void, int32)) \
ICALL_SIG (2, (void, object)) \
ICALL_SIG (2, (void, ptr)) \
ICALL_SIG (3, (bool, ptr, ptrref)) \
ICALL_SIG (3, (boolean, ptr, ptrref)) \
ICALL_SIG (3, (double, double, double)) \
ICALL_SIG (3, (float, float, float)) \
ICALL_SIG (3, (int, obj, ptr)) \
Expand Down Expand Up @@ -222,7 +222,7 @@ ICALL_SIG (3, (void, ptr, object)) \
ICALL_SIG (3, (void, ptr, ptr)) \
ICALL_SIG (3, (void, ptr, ptrref)) \
ICALL_SIG (3, (void, uint32, ptrref)) \
ICALL_SIG (4, (bool, ptr, int32, ptrref)) \
ICALL_SIG (4, (boolean, ptr, int32, ptrref)) \
ICALL_SIG (4, (int32, int32, ptr, ptrref)) \
ICALL_SIG (4, (int32, ptr, int32, ptr)) \
ICALL_SIG (4, (int32, ptr, int32, ptrref)) \
Expand Down
5 changes: 2 additions & 3 deletions src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -7186,8 +7186,7 @@ mono_lookup_icall_symbol (MonoMethod *m)
//
// mono_create_icall_signatures depends on this order. Handle with care.
typedef enum ICallSigType {
ICALL_SIG_TYPE_bool = 0x00,
ICALL_SIG_TYPE_boolean = ICALL_SIG_TYPE_bool,
ICALL_SIG_TYPE_boolean = 0x00,
ICALL_SIG_TYPE_double = 0x01,
ICALL_SIG_TYPE_float = 0x02,
ICALL_SIG_TYPE_int = 0x03,
Expand Down Expand Up @@ -7265,7 +7264,7 @@ mono_create_icall_signatures (void)
typedef gsize G_MAY_ALIAS gsize_a;

MonoType * const lookup [ ] = {
m_class_get_byval_arg (mono_defaults.boolean_class), // ICALL_SIG_TYPE_bool
m_class_get_byval_arg (mono_defaults.boolean_class), // ICALL_SIG_TYPE_boolean
m_class_get_byval_arg (mono_defaults.double_class), // ICALL_SIG_TYPE_double
m_class_get_byval_arg (mono_defaults.single_class), // ICALL_SIG_TYPE_float
m_class_get_byval_arg (mono_defaults.int32_class), // ICALL_SIG_TYPE_int
Expand Down
4 changes: 2 additions & 2 deletions src/mono/mono/mini/aot-compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -14612,8 +14612,8 @@ add_preinit_got_slots (MonoAotCompile *acfg)

#ifndef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
static MonoMethodSignature * const * const interp_in_static_sigs [] = {
&mono_icall_sig_bool_ptr_int32_ptrref,
&mono_icall_sig_bool_ptr_ptrref,
&mono_icall_sig_boolean_ptr_int32_ptrref,
&mono_icall_sig_boolean_ptr_ptrref,
&mono_icall_sig_int32_int32_ptrref,
&mono_icall_sig_int32_int32_ptr_ptrref,
&mono_icall_sig_int32_ptr_int32_ptr,
Expand Down
4 changes: 3 additions & 1 deletion src/mono/mono/tools/offsets-tool/offsets-tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
284 changes: 280 additions & 4 deletions src/mono/mono/utils/atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,281 @@ 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)
/* For each platform, decide what atomic implementation to use.
*
* 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.
*
*/
#undef MONO_USE_C11_ATOMIC
#undef MONO_USE_WIN32_ATOMIC
#undef MONO_USE_GCC_ATOMIC
#undef MONO_USE_EMULATED_ATOMIC

#if defined(MONO_GENERATING_OFFSETS)
/*
* Hack: for the offsets tool, define MONO_USE_EMULATED_ATOMIC since it doesn't actually need to see
* the impementation, and the stdatomic ones cause problems on some Linux configurations where
* libclang sees the platform header, not the clang one.
*/
# define MONO_USE_EMULATED_ATOMIC 1
#elif defined(_MSC_VER) || defined(HOST_WIN32)
/*
* we need two things to switch to C11 atomics on Windows:
*
* 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)
*
*/
# define MONO_USE_WIN32_ATOMIC 1
#elif defined(HOST_IOS) || defined(HOST_OSX) || defined(HOST_WATCHOS) || defined(HOST_TVOS)
# define MONO_USE_C11_ATOMIC 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_C11_ATOMIC 1
# elif defined(USE_GCC_ATOMIC_OPS)
# define MONO_USE_GCC_ATOMIC 1
# else
# define MONO_USE_EMULATED_ATOMIC 1
# endif
#elif defined(HOST_LINUX)
/* FIXME: probably need arch checks */
# define MONO_USE_C11_ATOMIC 1
#elif defined(HOST_WASI) || defined(HOST_BROWSER)
# define MONO_USE_C11_ATOMIC 1
#elif defined(USE_GCC_ATOMIC_OPS)
/* Prefer GCC atomic ops if the target supports it (see configure.ac). */
# define MONO_USE_GCC_ATOMIC 1
#else
# define MONO_USE_EMULATED_ATOMIC 1
#endif

#if defined(MONO_USE_C11_ATOMIC)

#include<stdatomic.h>

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(MONO_USE_WIN32_ATOMIC)

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
Expand Down Expand Up @@ -204,8 +478,8 @@ mono_atomic_store_ptr (volatile gpointer *dst, gpointer val)
InterlockedExchangePointer ((PVOID volatile *)dst, (PVOID)val);
}

/* Prefer GCC atomic ops if the target supports it (see configure.ac). */
#elif defined(USE_GCC_ATOMIC_OPS)

#elif defined(MONO_USE_GCC_ATOMIC)

/*
* As of this comment (August 2016), all current Clang versions get atomic
Expand Down Expand Up @@ -453,7 +727,7 @@ static inline void mono_atomic_store_i64(volatile gint64 *dst, gint64 val)
mono_atomic_xchg_i64 (dst, val);
}

#else
#elif defined(MONO_USE_EMULATED_ATOMIC)

#define WAPI_NO_ATOMIC_ASM

Expand Down Expand Up @@ -482,6 +756,8 @@ extern void mono_atomic_store_i32(volatile gint32 *dst, gint32 val);
extern void mono_atomic_store_i64(volatile gint64 *dst, gint64 val);
extern void mono_atomic_store_ptr(volatile gpointer *dst, gpointer val);

#else
#error one of MONO_USE_C11_ATOMIC, MONO_USE_WIN32_ATOMIC, MONO_USE_GCC_ATOMIC or MONO_USE_EMULATED_ATOMIC must be defined
#endif

#if SIZEOF_VOID_P == 4
Expand Down
Loading