未验证 提交 4110bcea 编写于 作者: A Aleksey Kliger (λgeek) 提交者: GitHub

Bump mono to -std=gnu11 (#91421)

Change the mono build to require C11 (with gnu extensions on gcc/clang platforms).
* Change `g_static_assert` to be `_Static_assert` or `static_assert` as apropriate.
* Change `_DN_STATIC_ASSERT` to be `static_assert`
* Add static asserts in `jiterp.c` when it casts between `T*` and `atomic_T*`
* Add C11 guidance to the mono coding guide doc

Contributes to #90404 

---

* Bump mono to -std=gnu99; use static_assert

* don't fall back to runtime checks for g_static_assert

* fix static assert that wasn't a constant expression

* use static_assert in shared containers

* bump C standard in offsets-tool.py

* use _Static_assert before C23

   Dont' include assert.h in glib.h because some of our older 3P code includes assert.h on its own and there are conflicts

* use CMAKE_C_STANDARD and related properties

* jiterp: static_assert that atomic ops are (less likely) to go wrong

   Not every C implementation guarantees that atomic operations on arbitrary types are lock free.  So for example, casting between atomic_ushort* and uint16_t* might not actually be ok to do.  We can't assert that they're inter-castable, but at least assert that they're the same size and that atomic_ushort is always lock-free. There might still be restrictions (for example atomic_ushort might have to be aligned differently) but this should at least catch obvious data corruption.

* Add C11 guidance to the Mono coding guide

* jiterp: long is 32-bits on wasm; use llong
上级 528a5cfa
......@@ -61,6 +61,49 @@ There are actually three boolean types to keep in mind:
* `MonoBoolean` used as an interop type with C# bool in internal calls
* `mono_bool` used by the public C API - generally new code shouldn't use it except when adding a new public API function.
## Newer features of C11 and later
Mono is currently (2023) written in C11.
### Static asserts
Use `static_assert` liberally. Include `<assert.h>`. If you cannot include `<assert.h>` (for
example because it introduces conflicting symbols or macros), use `g_static_assert`. Do not use
`_Static_assert` directly (see how `g_static_assert` is defined). Rationale: C23 deprecates
`_Static_assert`.
### Threads, locks, `call_once`
Due to mono's threading model and cooperative GC, using C threading and locking primitives directly
is not ok. Prefer `MonoCoopMutex`, `MonoCoopCond` that have GC-aware locking/waiting operations.
If you need them, use `mono_mutex_t`, `mono_cond_t`, etc for cases where GC transitions are
prohibitively costly and you can guarantee that the lock will never be taken by a GC initiator or by
a mix of threads in GC cooperative and GC preemptive mode.
Using standard C threads and `call_once` in native library PInvokes outside the runtime is okay.
Using standard C mutexes and condition variables in native library PInvokes outside the runtime is
okay if the locks are not shared with the runtime internals.
### Thread locals
FIXME: no guidance yet
### Atomics
The C standard atomics are not guaranteed to be lock-free. Use the mono `mono_atomic_` functions
(some of which may be implemented in terms of standard C atomics on some platforms). We do not in
general want locking because it would not be GC aware and may deadlock the cooperative GC.
Uses of `_Atomic` are a code smell in Mono.
Using standard C atomics in native library PInvokes outside the runtime is okay provided the atomics
aren't also accessed inside the runtime internals.
### Generic operations
FIXME: no guidance for `_Generic` yet.
## Utility and platform abstraction functions
Mono generally tries to fill in POSIX-like abstractions on platforms that lack them (for example, Windows).
......@@ -251,3 +294,4 @@ calling Mono internals.
In general new code should not do this. When modifying existing code, mysterious WASM failures may
be attributed to symbol signature mismatches between WASM and the Mono runtime.
......@@ -8,6 +8,9 @@ include(../../eng/native/configurepaths.cmake)
include(${CLR_ENG_NATIVE_DIR}/functions.cmake)
include(${CLR_ENG_NATIVE_DIR}/configuretools.cmake)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
if (MSVC)
# Also set by configurecompiler.cmake, which isn't used by mono yet
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/source-charset:utf-8>) # Force MSVC to compile source as UTF-8.
......@@ -523,13 +526,8 @@ include(configure)
######################################
if(GCC)
if(HOST_WASI)
# WASI SDK only includes some required definitions if the C version is at least this new
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")
else()
# We require C99 with some GNU extensions, e.g. `linux` macro
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
endif()
# We require C11 with some GNU extensions, e.g. `linux` macro
set(CMAKE_C_EXTENSIONS ON)
# Turn off floating point expression contraction because it is considered a value changing
# optimization in the IEEE 754 specification and is therefore considered unsafe.
......
......@@ -827,10 +827,13 @@ gpointer g_convert_error_quark(void);
#define g_assert(x) (G_LIKELY((x)) ? 1 : (mono_assertion_message (__FILE__, __LINE__, #x), 0))
#endif
#ifdef __cplusplus
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
#define g_static_assert(x) static_assert (x, "")
#elif (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
#define g_static_assert(x) _Static_assert (x, "")
#else
#define g_static_assert(x) g_assert (x)
#error Mono requires static_assert (C11 or newer)
/* #define g_static_assert(x) g_assert (x)*/
#endif
#define g_assert_not_reached() G_STMT_START { mono_assertion_message_unreachable (__FILE__, __LINE__); eg_unreachable(); } G_STMT_END
......
......@@ -294,7 +294,7 @@ mono_lookup_icall_symbol_internal (gpointer func)
#if ENABLE_ICALL_SYMBOL_MAP || TEST_ICALL_SYMBOL_MAP
typedef guint16 T;
const gsize N = STRING_LENGTH (icall_functions); // skip terminal null element
g_static_assert (N <= 0xFFFF); // If this fails, change T to guint32
g_static_assert (STRING_LENGTH (icall_functions) <= 0xFFFF); // If this fails, change T to guint32
static T *static_functions_sorted;
if (!func)
......
......@@ -21,6 +21,7 @@ void jiterp_preserve_module (void);
#define jiterp_assert(b)
#endif
#include <assert.h>
#include <stdatomic.h>
#include <emscripten.h>
......@@ -724,6 +725,7 @@ trace_info_allocate_segment (gint32 index) {
return segment;
#else
TraceInfo *expected = NULL;
static_assert (sizeof(atomic_uintptr_t) == sizeof(trace_segments[index]) && ATOMIC_POINTER_LOCK_FREE == 2, "");
if (!atomic_compare_exchange_strong ((atomic_uintptr_t *)&trace_segments[index], (uintptr_t *)&expected, (uintptr_t)segment)) {
g_free (segment);
return expected;
......@@ -751,11 +753,12 @@ trace_info_get (gint32 index) {
static gint32
trace_info_alloc () {
#ifdef DISABLE_THREADS
gint32 index = trace_count++,
gint32 index = trace_count++;
#else
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1),
static_assert (sizeof(atomic_int) == sizeof(trace_count) && ATOMIC_INT_LOCK_FREE == 2, "");
gint32 index = atomic_fetch_add ((atomic_int *)&trace_count, 1);
#endif
limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
gint32 limit = (MAX_TRACE_SEGMENTS * TRACE_SEGMENT_SIZE);
// Make sure we're not out of space in the trace info table.
if (index == limit)
g_print ("MONO_WASM: Reached maximum number of jiterpreter trace entry points (%d).\n", limit);
......@@ -925,7 +928,8 @@ mono_interp_tier_prepare_jiterpreter_fast (
#ifdef DISABLE_THREADS
gint64 count = trace_info->hit_count++;
#else
gint64 count = atomic_fetch_add ((atomic_long *)&trace_info->hit_count, 1);
static_assert (sizeof(atomic_llong) == sizeof(trace_info->hit_count) && ATOMIC_LLONG_LOCK_FREE == 2, "");
gint64 count = atomic_fetch_add ((atomic_llong *)&trace_info->hit_count, 1);
#endif
if (count == mono_opt_jiterpreter_minimum_trace_hit_count) {
......@@ -1282,6 +1286,7 @@ mono_jiterp_modify_counter (int counter, double delta) {
long actual_result = *counter_address;
*counter_address = actual_result + actual_delta;
#else
static_assert (sizeof(counter_address) == sizeof(atomic_long) && ATOMIC_LONG_LOCK_FREE == 2, "");
long actual_result = atomic_fetch_add ((atomic_long *)counter_address, actual_delta);
#endif
......@@ -1356,6 +1361,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
#ifdef DISABLE_THREADS
info->penalty_total += penalty;
#else
static_assert (sizeof(info->penalty_total) == sizeof(atomic_int) && ATOMIC_INT_LOCK_FREE == 2, "");
atomic_fetch_add ((atomic_int *)&info->penalty_total, penalty);
#endif
......@@ -1367,7 +1373,8 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
gint64 hit_count = info->hit_count - mono_opt_jiterpreter_minimum_trace_hit_count;
info->hit_count += 1;
#else
gint64 hit_count = atomic_fetch_add ((atomic_long *)&info->hit_count, 1) - mono_opt_jiterpreter_minimum_trace_hit_count;
static_assert (sizeof(atomic_llong) == sizeof(info->hit_count) && ATOMIC_LLONG_LOCK_FREE == 2, "");
gint64 hit_count = atomic_fetch_add ((atomic_llong *)&info->hit_count, 1) - mono_opt_jiterpreter_minimum_trace_hit_count;
#endif
if (hit_count == mono_opt_jiterpreter_trace_monitoring_period) {
......@@ -1389,6 +1396,7 @@ mono_jiterp_monitor_trace (const guint16 *ip, void *_frame, void *locals)
#else
guint16 zero = 0;
// atomically patch the relative fn ptr inside the opcode.
static_assert (sizeof(atomic_ushort) == sizeof(opcode->relative_fn_ptr) && ATOMIC_SHORT_LOCK_FREE == 2, "");
g_assert (atomic_compare_exchange_strong ((atomic_ushort *)&opcode->relative_fn_ptr, &zero, new_relative_fn_ptr));
#endif
g_assert (mono_jiterp_patch_opcode (opcode, MINT_TIER_NOP_JITERPRETER, MINT_TIER_ENTER_JITERPRETER));
......@@ -1465,6 +1473,7 @@ atomically_set_value_once (gint32 *address, gint32 value) {
*address = value;
#else
gint32 expected = 0;
static_assert (sizeof(atomic_int) == sizeof(address) && ATOMIC_INT_LOCK_FREE == 2, "");
if (atomic_compare_exchange_strong ((atomic_int *)address, &expected, value))
return;
if (expected == value)
......@@ -1489,6 +1498,7 @@ mono_jiterp_initialize_table (int type, int first_index, int last_index) {
table->next_index = first_index;
#else
gint32 expected = 0;
static_assert (sizeof (atomic_int) == sizeof(table->next_index) && ATOMIC_INT_LOCK_FREE == 2, "");
atomic_compare_exchange_strong ((atomic_int *)&table->next_index, &expected, first_index);
#endif
}
......@@ -1506,6 +1516,7 @@ mono_jiterp_allocate_table_entry (int type) {
int index = table->next_index++;
#else
gint32 expected = 0;
static_assert (sizeof (atomic_int) == sizeof(table->next_index) && ATOMIC_INT_LOCK_FREE == 2, "");
atomic_compare_exchange_strong ((atomic_int *)&table->next_index, &expected, table->first_index);
int index = atomic_fetch_add ((atomic_int *)&table->next_index, 1);
#endif
......@@ -1525,6 +1536,7 @@ mono_jiterp_increment_counter (volatile int *counter) {
*counter = result + 1;
return result;
#else
static_assert (sizeof (atomic_int) == sizeof(counter) && ATOMIC_INT_LOCK_FREE == 2, "");
return atomic_fetch_add ((atomic_int *)counter, 1);
#endif
}
......@@ -1539,6 +1551,7 @@ mono_jiterp_patch_opcode (volatile JiterpreterOpcode *ip, guint16 old_opcode, gu
return FALSE;
#else
// guint16 actual_old_opcode = old_opcode;
static_assert (sizeof (atomic_ushort) == sizeof(ip->opcode) && ATOMIC_SHORT_LOCK_FREE == 2, "");
gboolean result = atomic_compare_exchange_strong ((atomic_ushort *)&ip->opcode, &old_opcode, new_opcode);
/*
if (!result)
......
......@@ -281,7 +281,7 @@ class OffsetsTool:
clang_args = []
clang_args += self.target_args
clang_args += ['-std=gnu99', '-DMONO_GENERATING_OFFSETS']
clang_args += ['-std=gnu11', '-DMONO_GENERATING_OFFSETS']
for include in self.sys_includes:
clang_args.append ("-isystem")
clang_args.append (include)
......
......@@ -16,8 +16,10 @@
#include <stdint.h>
#include <string.h>
#if defined(_DEBUG)
// included unconditionally for static_assert macro on C11
#include <assert.h>
#if defined(_DEBUG)
#define DN_ASSERT(x) assert(x)
#else
#define DN_ASSERT(x)
......@@ -47,10 +49,7 @@
#define DN_UNREFERENCED_PARAMETER(expr) (void)(expr)
// Until C11 support, use typedef expression for static assertion.
#define _DN_STATIC_ASSERT_UNQIUE_TYPEDEF0(line) __dn_static_assert_ ## line ## _t
#define _DN_STATIC_ASSERT_UNQIUE_TYPEDEF(line) _DN_STATIC_ASSERT_UNQIUE_TYPEDEF0(line)
#define _DN_STATIC_ASSERT(expr) typedef char _DN_STATIC_ASSERT_UNQIUE_TYPEDEF(__LINE__)[(expr) != 0]
#define _DN_STATIC_ASSERT(expr) static_assert(expr, "")
static inline bool
dn_safe_size_t_multiply (size_t lhs, size_t rhs, size_t *result)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册