From e8b037642104527bd9b9ba70d502210b9c12d2b8 Mon Sep 17 00:00:00 2001 From: Jay Krell Date: Wed, 24 Oct 2018 09:38:19 -0700 Subject: [PATCH] Stack-based icall parameter handles and only one managed/native transition per icall. (#11294) Handles for icall parameters allocated in managed (but not native), can just be pointers to locals or parameters. Currently the managed stack is scanned conservatively. When this changes, there will be metadata provided to find these, so this approach will just keep working and should still be ideal (aside from nohandles or staying in managed). For ObjIn, use pointer to parameter. For ObjOut/ObjInOut, the parameter might, though it is far fetched, point to a native frame, which isn't convincing. Therefore for those, use a pointer to a managed volatile local, closer to how handles looked like. For ValueTypeRef, make the parameter volatile so it is sure to survive. We use volatile instead of merely address-taken because 1. a hypothetical aggressive whole program compiler/optimizer could see through address-taken (runtime and wrappers and corlib compiled together). 2. ValuetypeRef is even easier to optimize/break because the address-taken is dead. FIXME: Tests that exercise every icall, coop and otherwise. FIXME: Gcstress. Collateral damage: The one rarely run assert that handles are not out of scope cannot be so easily implemented so is removed. It might come back though, if we type tag the handles. Furthermore, the handle stack setup/cleanup can be done in native instead of managed. Furthermore, the error check can either be done in native instead of managed, or the fast path inlined in the JIT. Here we just move it to native (and inline the fast path in native). Therefore coop icalls go from 3 + handle-count managed/native transitions, to just always one, *and* at least some of their handle allocation is exceedingly fast and free of TLS. The typed handle wrappers change from returning a handle to returning a raw pointer. Because previously the handle would be allocated in the handle frame setup by the JIT. Now the JIT doesn't setup a handle frame. There is a weakreference test that is seemingly very sensitive to stack layout. For this reason at least, the MonoError is still allocated on the managed side. Moving it to native broke the test. This might also be preferable for the public interface. Coop icalls retain some costs: - The volatile might cost a little -- lack of enregistration. - Native code allocating handles. - Still TLS access. - JIT icalls (vs. metadata-driven) still native-only handle allocation, and still fully manual conversion. - Coop icalls that return an object always allocate a handle -- could be more efficient out or inout handle instead, though inout is currently very rare. Some of this can be further mitigated. Mainly, the metadata icall signatures could move the "local" handles to be out parameters, in order for managed to allocate them. The JIT icalls could have richer signatures than just "ptr" and gain these wrappers, or previous (ref parameters, retyped as handles). --- mcs/class/corlib/LinkerDescriptor/mscorlib.xml | 4 +- mcs/class/corlib/Mono/RuntimeStructs.cs | 6 - mono/eglib/glib.h | 2 +- mono/metadata/exception.c | 5 +- mono/metadata/handle-decl.h | 4 +- mono/metadata/handle.c | 128 ++---------- mono/metadata/handle.h | 19 +- mono/metadata/icall-table.h | 92 +++++---- mono/metadata/marshal-ilgen.c | 262 +++++++++---------------- mono/metadata/marshal.c | 38 ---- mono/metadata/marshal.h | 12 -- mono/metadata/metadata-internals.h | 2 + mono/metadata/method-builder-ilgen-internals.h | 8 +- mono/metadata/method-builder-ilgen.c | 4 + mono/metadata/object-internals.h | 8 +- mono/mini/mini.c | 23 ++- mono/utils/monobitset.c | 33 +++- mono/utils/monobitset.h | 6 + 18 files changed, 262 insertions(+), 394 deletions(-) diff --git a/mcs/class/corlib/LinkerDescriptor/mscorlib.xml b/mcs/class/corlib/LinkerDescriptor/mscorlib.xml index 6cdd34adde2..27e39de66b3 100644 --- a/mcs/class/corlib/LinkerDescriptor/mscorlib.xml +++ b/mcs/class/corlib/LinkerDescriptor/mscorlib.xml @@ -808,9 +808,7 @@ --> - - - + diff --git a/mcs/class/corlib/Mono/RuntimeStructs.cs b/mcs/class/corlib/Mono/RuntimeStructs.cs index 92884a2591d..48d1514ab98 100644 --- a/mcs/class/corlib/Mono/RuntimeStructs.cs +++ b/mcs/class/corlib/Mono/RuntimeStructs.cs @@ -49,12 +49,6 @@ namespace Mono { internal int len; } - // handle.h HandleStackMark - struct HandleStackMark { - int size, interior_size; - IntPtr chunk; - } - // mono-error.h MonoError struct MonoError { ushort error_code; diff --git a/mono/eglib/glib.h b/mono/eglib/glib.h index 015962e7699..edb579bb85d 100644 --- a/mono/eglib/glib.h +++ b/mono/eglib/glib.h @@ -80,7 +80,7 @@ struct g_cast private: void * const x; public: - explicit g_cast (void *y) : x(y) { } + explicit g_cast (void volatile *y) : x((void*)y) { } // Lack of rvalue constructor inhibits ternary operator. // Either don't use ternary, or cast each side. // sa = (salen <= 128) ? g_alloca (salen) : g_malloc (salen); diff --git a/mono/metadata/exception.c b/mono/metadata/exception.c index f6ee3e6582d..ebb15488b71 100644 --- a/mono/metadata/exception.c +++ b/mono/metadata/exception.c @@ -1209,7 +1209,7 @@ mono_error_raise_exception_deprecated (MonoError *target_error) } /** - * mono_error_set_pending_exception: + * mono_error_set_pending_exception_slow: * \param error The error * If \p error is set, convert it to an exception and set the pending exception for the current icall. * \returns TRUE if \p error was set, or FALSE otherwise, so that you can write: @@ -1218,8 +1218,9 @@ mono_error_raise_exception_deprecated (MonoError *target_error) * return; * } */ +// For efficiency, call mono_error_set_pending_exception instead of mono_error_set_pending_exception_slow. gboolean -mono_error_set_pending_exception (MonoError *error) +mono_error_set_pending_exception_slow (MonoError *error) { if (is_ok (error)) return FALSE; diff --git a/mono/metadata/handle-decl.h b/mono/metadata/handle-decl.h index e0ed0fb6f09..d5bd2c29965 100644 --- a/mono/metadata/handle-decl.h +++ b/mono/metadata/handle-decl.h @@ -68,14 +68,14 @@ Handle macros/functions MONO_ALWAYS_INLINE \ TYPE * GetRaw () { return __raw ? *__raw : NULL; } \ ) \ - TYPE **__raw; \ + TYPE * volatile *__raw; \ } TYPED_HANDLE_NAME (TYPE), \ TYPED_OUT_HANDLE_NAME (TYPE), \ TYPED_IN_OUT_HANDLE_NAME (TYPE); \ /* Do not call these functions directly. Use MONO_HANDLE_NEW and MONO_HANDLE_CAST. */ \ /* Another way to do this involved casting mono_handle_new function to a different type. */ \ static inline MONO_ALWAYS_INLINE TYPED_HANDLE_NAME (TYPE) \ -MONO_HANDLE_CAST_FOR (TYPE) (gpointer a) \ +MONO_HANDLE_CAST_FOR (TYPE) (MonoRawHandle a) \ { \ TYPED_HANDLE_NAME (TYPE) b = { (TYPE**)a }; \ return b; \ diff --git a/mono/metadata/handle.c b/mono/metadata/handle.c index ca2a9af1e64..4bea2cbae27 100644 --- a/mono/metadata/handle.c +++ b/mono/metadata/handle.c @@ -104,36 +104,6 @@ chunk_element (HandleChunk *chunk, int idx) return &chunk->elems[idx]; } -static HandleChunkElem* -handle_to_chunk_element (MonoObjectHandle o) -{ - return (HandleChunkElem*)o.__raw; -} - -/* Given a HandleChunkElem* search through the current handle stack to find its chunk and offset. */ -static HandleChunk* -chunk_element_to_chunk_idx (HandleStack *stack, HandleChunkElem *elem, int *out_idx) -{ - HandleChunk *top = stack->top; - HandleChunk *cur = stack->bottom; - - *out_idx = 0; - - while (cur != NULL) { - HandleChunkElem *front = &cur->elems [0]; - HandleChunkElem *back = &cur->elems [cur->size]; - - if (front <= elem && elem < back) { - *out_idx = (int)(elem - front); - return cur; - } - - if (cur == top) - break; /* didn't find it. */ - cur = cur->next; - } - return NULL; -} #ifdef MONO_HANDLE_TRACK_OWNER #ifdef HAVE_BACKTRACE_SYMBOLS @@ -240,54 +210,16 @@ retry: goto retry; } -gpointer -#ifndef MONO_HANDLE_TRACK_OWNER -mono_handle_new_interior (gpointer rawptr) -#else -mono_handle_new_interior (gpointer rawptr, const char *owner) -#endif -{ - MonoThreadInfo *info = mono_thread_info_current (); - HandleStack *handles = info->handle_stack; - HandleChunk *top = handles->interior; -#ifdef MONO_HANDLE_TRACK_SP - mono_handle_chunk_leak_check (handles); -#endif - - g_assert (top); - - /* - * Don't extend the chunk now, interior handles are - * only used for icall arguments, they shouldn't - * overflow. - */ - g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK); - int idx = top->size; - gpointer *objslot = &top->elems [idx].o; - *objslot = NULL; - mono_memory_write_barrier (); - top->size++; - mono_memory_write_barrier (); - *objslot = rawptr; - SET_OWNER (top,idx); - SET_SP (handles, top, idx); - return objslot; -} - HandleStack* mono_handle_stack_alloc (void) { HandleStack *stack = new_handle_stack (); HandleChunk *chunk = new_handle_chunk (); - HandleChunk *interior = new_handle_chunk (); chunk->prev = chunk->next = NULL; chunk->size = 0; - interior->prev = interior->next = NULL; - interior->size = 0; mono_memory_write_barrier (); stack->top = stack->bottom = chunk; - stack->interior = interior; #ifdef MONO_HANDLE_TRACK_SP stack->stackmark_sp = NULL; #endif @@ -308,7 +240,6 @@ mono_handle_stack_free (HandleStack *stack) c = next; } free_handle_chunk (c); - free_handle_chunk (stack->interior); free_handle_stack (stack); } @@ -340,10 +271,6 @@ mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain) break; cur = cur->next; } - /* We don't examine the interior pointers here because the GC treats - * them conservatively and anyway we don't have enough information here to - * find the object's vtable. - */ } static void @@ -385,39 +312,29 @@ mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, g check_handle_stack_monotonic (stack); /* - We're called twice - on the imprecise pass we call func to pin the - objects where the handle points to its interior. On the precise - pass, we scan all the objects where the handles point to the start of + We're called twice - on the imprecise pass we do nothing. + Interior pointers are retained in managed frames. + On the precise pass, we scan all the objects where the handles point to the start of the object. Note that if we're running, we know the world is stopped. */ - if (precise) { - HandleChunk *cur = stack->bottom; - HandleChunk *last = stack->top; - - while (cur) { - for (int i = 0; i < cur->size; ++i) { - HandleChunkElem* elem = chunk_element (cur, i); - gpointer* obj_slot = &elem->o; - if (*obj_slot != NULL) - func (obj_slot, gc_data); - } - if (cur == last) - break; - cur = cur->next; - } - } else { - HandleChunk *cur = stack->interior; + if (!precise) + return; - if (!cur) - return; + HandleChunk *cur = stack->bottom; + HandleChunk *last = stack->top; + + while (cur) { for (int i = 0; i < cur->size; ++i) { HandleChunkElem* elem = chunk_element (cur, i); - gpointer* ptr_slot = &elem->o; - if (*ptr_slot != NULL) - func (ptr_slot, gc_data); + gpointer* obj_slot = &elem->o; + if (*obj_slot != NULL) + func (obj_slot, gc_data); } + if (cur == last) + break; + cur = cur->next; } } @@ -486,15 +403,12 @@ mono_array_handle_length (MonoArrayHandle arr) uint32_t mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned) { - /* FIXME: chunk_element_to_chunk_idx does a linear search through the - * chunks and we only need it for the assert */ - MonoThreadInfo *info = mono_thread_info_current (); - HandleStack *stack = info->handle_stack; - HandleChunkElem* elem = handle_to_chunk_element (handle); - int elem_idx = 0; - HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx); - /* gchandles cannot deal with interior pointers */ - g_assert (chunk != NULL); + // FIXME This used to check for out of scope handles. + // Stack-based handles coming from icall wrappers do not + // work with that. This functionality can be largely restored + // by introducing a tag bit in handles -- 0 for managed stack-based, + // 1 for native TLS-based, having MONO_HANDLE_RAW clear it, and only + // doing the former checking for native TLS-based handles. return mono_gchandle_new_internal (MONO_HANDLE_RAW (handle), pinned); } diff --git a/mono/metadata/handle.h b/mono/metadata/handle.h index 4fc7eed051f..dc0d7667406 100644 --- a/mono/metadata/handle.h +++ b/mono/metadata/handle.h @@ -63,13 +63,11 @@ typedef struct _HandleChunk HandleChunk; * to ensure that when a new handle is allocated the previous newest handle is not lower in the stack. * This is useful to catch missing HANDLE_FUNCTION_ENTER / HANDLE_FUNCTION_RETURN pairs which could cause * handle leaks. - * - * If defined, keep HandleStackMark in sync in RuntimeStructs.cs */ /*#define MONO_HANDLE_TRACK_SP*/ typedef struct { - gpointer o; /* MonoObject ptr or interior ptr */ + gpointer o; /* MonoObject ptr */ #ifdef MONO_HANDLE_TRACK_OWNER const char *owner; gpointer backtrace_ips[7]; /* result of backtrace () at time of allocation */ @@ -91,20 +89,23 @@ typedef struct MonoHandleStack { #ifdef MONO_HANDLE_TRACK_SP gpointer stackmark_sp; // C stack pointer top when from most recent mono_stack_mark_init #endif - /* Chunk for storing interior pointers. Not extended right now */ - HandleChunk *interior; } HandleStack; // Keep this in sync with RuntimeStructs.cs typedef struct { - int size, interior_size; + int size; HandleChunk *chunk; #ifdef MONO_HANDLE_TRACK_SP gpointer prev_sp; // C stack pointer from prior mono_stack_mark_init #endif } HandleStackMark; -typedef void *MonoRawHandle; +// There are two types of handles. +// Pointers to volatile pointers in managed frames. +// These are allocated by icall wrappers in marshal-ilgen.c. +// Pointers to non-volatile pointers in TLS. +// These are allocated by MONO_HANDLE_NEW. +typedef void volatile * MonoRawHandle; typedef void (*GcScanFunc) (gpointer*, gpointer); @@ -126,10 +127,8 @@ typedef void (*GcScanFunc) (gpointer*, gpointer); #ifndef MONO_HANDLE_TRACK_OWNER MonoRawHandle mono_handle_new (MonoObject *object); -gpointer mono_handle_new_interior (gpointer rawptr); #else MonoRawHandle mono_handle_new (MonoObject *object, const char* owner); -gpointer mono_handle_new_interior (gpointer rawptr, const char *owner); #endif void mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise, gboolean check); @@ -153,7 +152,6 @@ mono_stack_mark_init (MonoThreadInfo *info, HandleStackMark *stackmark) HandleStack *handles = info->handle_stack; stackmark->size = handles->top->size; stackmark->chunk = handles->top; - stackmark->interior_size = handles->interior->size; #ifdef MONO_HANDLE_TRACK_SP stackmark->prev_sp = handles->stackmark_sp; handles->stackmark_sp = sptop; @@ -168,7 +166,6 @@ mono_stack_mark_pop (MonoThreadInfo *info, HandleStackMark *stackmark) old_top->size = stackmark->size; mono_memory_write_barrier (); handles->top = old_top; - handles->interior->size = stackmark->interior_size; #ifdef MONO_HANDLE_TRACK_SP mono_memory_write_barrier (); /* write to top before prev_sp */ handles->stackmark_sp = stackmark->prev_sp; diff --git a/mono/metadata/icall-table.h b/mono/metadata/icall-table.h index af2ea9084cb..5c1e29b8dfd 100644 --- a/mono/metadata/icall-table.h +++ b/mono/metadata/icall-table.h @@ -229,15 +229,15 @@ typedef MonoReflectionModuleHandle MonoReflectionModuleOutHandle; #define MONO_HANDLE_DO2(macro_prefix, type) MONO_HANDLE_DO3 (macro_prefix, type) #define MONO_HANDLE_DO(macro_prefix, type) MONO_HANDLE_DO2 (macro_prefix, MONO_HANDLE_TYPE_WRAP_ ## type) -#define MONO_HANDLE_RETURN_BEGIN(type) MONO_HANDLE_DO (MONO_HANDLE_RETURN_BEGIN_, type) -#define MONO_HANDLE_RETURN_BEGIN_Void /* nothing */ -#define MONO_HANDLE_RETURN_BEGIN_ICALL_HANDLES_WRAP_NONE return -#define MONO_HANDLE_RETURN_BEGIN_ICALL_HANDLES_WRAP_OBJ return +#define MONO_HANDLE_RETURN_BEGIN(type) MONO_HANDLE_DO (MONO_HANDLE_RETURN_BEGIN_, type) (type) +#define MONO_HANDLE_RETURN_BEGIN_Void(type) /* nothing */ +#define MONO_HANDLE_RETURN_BEGIN_ICALL_HANDLES_WRAP_NONE(type) type icall_result = +#define MONO_HANDLE_RETURN_BEGIN_ICALL_HANDLES_WRAP_OBJ(type) type ## Handle icall_result = #define MONO_HANDLE_RETURN_END(type) MONO_HANDLE_DO (MONO_HANDLE_RETURN_END_, type); -#define MONO_HANDLE_RETURN_END_Void /* nothing */ -#define MONO_HANDLE_RETURN_END_ICALL_HANDLES_WRAP_NONE /* nothing */ -#define MONO_HANDLE_RETURN_END_ICALL_HANDLES_WRAP_OBJ .__raw +#define MONO_HANDLE_RETURN_END_Void HANDLE_FUNCTION_RETURN () +#define MONO_HANDLE_RETURN_END_ICALL_HANDLES_WRAP_NONE HANDLE_FUNCTION_RETURN_VAL (icall_result) +#define MONO_HANDLE_RETURN_END_ICALL_HANDLES_WRAP_OBJ HANDLE_FUNCTION_RETURN_OBJ (icall_result) #define MONO_HANDLE_MARSHAL(type, n) MONO_HANDLE_DO (MONO_HANDLE_MARSHAL_, type) (type, n) #define MONO_HANDLE_MARSHAL_ICALL_HANDLES_WRAP_NONE(type, n) a ## n @@ -254,21 +254,30 @@ typedef MonoReflectionModuleHandle MonoReflectionModuleOutHandle; #define MONO_HANDLE_TYPE_TYPED_ICALL_HANDLES_WRAP_OBJ_INOUT(type) type ## Handle #define MONO_HANDLE_TYPE_TYPED_ICALL_HANDLES_WRAP_VALUETYPE_REF(type) type -#define MONO_HANDLE_TYPE_RAW(type) MONO_HANDLE_DO (MONO_HANDLE_TYPE_RAW_, type) (type) -#define MONO_HANDLE_TYPE_RAW_Void(type) type -#define MONO_HANDLE_TYPE_RAW_ICALL_HANDLES_WRAP_NONE(type) type -#define MONO_HANDLE_TYPE_RAW_ICALL_HANDLES_WRAP_OBJ(type) MonoRawHandle -#define MONO_HANDLE_TYPE_RAW_ICALL_HANDLES_WRAP_OBJ_OUT(type) MonoRawHandle -#define MONO_HANDLE_TYPE_RAW_ICALL_HANDLES_WRAP_OBJ_INOUT(type) MonoRawHandle -#define MONO_HANDLE_TYPE_RAW_ICALL_HANDLES_WRAP_VALUETYPE_REF(type) type +#define MONO_HANDLE_TYPE_RAWHANDLE(type) MONO_HANDLE_DO (MONO_HANDLE_TYPE_RAWHANDLE_, type) (type) +#define MONO_HANDLE_TYPE_RAWHANDLE_Void(type) type +#define MONO_HANDLE_TYPE_RAWHANDLE_ICALL_HANDLES_WRAP_NONE(type) type +#define MONO_HANDLE_TYPE_RAWHANDLE_ICALL_HANDLES_WRAP_OBJ(type) MonoRawHandle +#define MONO_HANDLE_TYPE_RAWHANDLE_ICALL_HANDLES_WRAP_OBJ_OUT(type) MonoRawHandle +#define MONO_HANDLE_TYPE_RAWHANDLE_ICALL_HANDLES_WRAP_OBJ_INOUT(type) MonoRawHandle +#define MONO_HANDLE_TYPE_RAWHANDLE_ICALL_HANDLES_WRAP_VALUETYPE_REF(type) type + +#define MONO_HANDLE_TYPE_RAWPOINTER(type) MONO_HANDLE_DO (MONO_HANDLE_TYPE_RAWPOINTER_, type) (type) +#define MONO_HANDLE_TYPE_RAWPOINTER_Void(type) type +#define MONO_HANDLE_TYPE_RAWPOINTER_ICALL_HANDLES_WRAP_NONE(type) type +#define MONO_HANDLE_TYPE_RAWPOINTER_ICALL_HANDLES_WRAP_OBJ(type) type* +// Only used for return types. +//#define MONO_HANDLE_TYPE_RAWPOINTER_ICALL_HANDLES_WRAP_OBJ_OUT(type) type* +//#define MONO_HANDLE_TYPE_RAWPOINTER_ICALL_HANDLES_WRAP_OBJ_INOUT(type) type* +#define MONO_HANDLE_TYPE_RAWPOINTER_ICALL_HANDLES_WRAP_VALUETYPE_REF(type) type // Type/name in raw handle prototype and implementation. -#define MONO_HANDLE_ARG_RAW(type, n) MONO_HANDLE_DO (MONO_HANDLE_ARG_RAW_, type) (type, n) -#define MONO_HANDLE_ARG_RAW_ICALL_HANDLES_WRAP_NONE(type, n) MONO_HANDLE_TYPE_RAW (type) a ## n -#define MONO_HANDLE_ARG_RAW_ICALL_HANDLES_WRAP_OBJ(type, n) MONO_HANDLE_TYPE_RAW (type) a ## n -#define MONO_HANDLE_ARG_RAW_ICALL_HANDLES_WRAP_OBJ_OUT(type, n) MONO_HANDLE_TYPE_RAW (type) a ## n -#define MONO_HANDLE_ARG_RAW_ICALL_HANDLES_WRAP_OBJ_INOUT(type, n) MONO_HANDLE_TYPE_RAW (type) a ## n -#define MONO_HANDLE_ARG_RAW_ICALL_HANDLES_WRAP_VALUETYPE_REF(type, n) MONO_HANDLE_TYPE_RAW (type) a ## n +#define MONO_HANDLE_ARG_RAWHANDLE(type, n) MONO_HANDLE_DO (MONO_HANDLE_ARG_RAWHANDLE_, type) (type, n) +#define MONO_HANDLE_ARG_RAWHANDLE_ICALL_HANDLES_WRAP_NONE(type, n) MONO_HANDLE_TYPE_RAWHANDLE (type) a ## n +#define MONO_HANDLE_ARG_RAWHANDLE_ICALL_HANDLES_WRAP_OBJ(type, n) MONO_HANDLE_TYPE_RAWHANDLE (type) a ## n +#define MONO_HANDLE_ARG_RAWHANDLE_ICALL_HANDLES_WRAP_OBJ_OUT(type, n) MONO_HANDLE_TYPE_RAWHANDLE (type) a ## n +#define MONO_HANDLE_ARG_RAWHANDLE_ICALL_HANDLES_WRAP_OBJ_INOUT(type, n) MONO_HANDLE_TYPE_RAWHANDLE (type) a ## n +#define MONO_HANDLE_ARG_RAWHANDLE_ICALL_HANDLES_WRAP_VALUETYPE_REF(type, n) MONO_HANDLE_TYPE_RAWHANDLE (type) a ## n // Generate a parameter list, types only, for a function accepting/returning typed handles. #define MONO_HANDLE_FOREACH_TYPE_TYPED_0() /* nothing */ @@ -282,17 +291,18 @@ typedef MonoReflectionModuleHandle MonoReflectionModuleOutHandle; #define MONO_HANDLE_FOREACH_TYPE_TYPED_8(t0, t1, t2, t3, t4, t5, t6, t7) MONO_HANDLE_FOREACH_TYPE_TYPED_7 (t0, t1, t2, t3, t4, t5, t6) ,MONO_HANDLE_TYPE_TYPED (t7) #define MONO_HANDLE_FOREACH_TYPE_TYPED_9(t0, t1, t2, t3, t4, t5, t6, t7, t8) MONO_HANDLE_FOREACH_TYPE_TYPED_8 (t0, t1, t2, t3, t4, t5, t6, t7) ,MONO_HANDLE_TYPE_TYPED (t8) -// Generate a parameter list, types and names, for a function accepting/returning raw handles. +// Generate a parameter list, types and names, for a function accepting raw handles and a MonoError, +// and returning a raw pointer. #define MONO_HANDLE_FOREACH_ARG_RAW_0() /* nothing */ -#define MONO_HANDLE_FOREACH_ARG_RAW_1(t0) MONO_HANDLE_ARG_RAW (t0, 0) -#define MONO_HANDLE_FOREACH_ARG_RAW_2(t0, t1) MONO_HANDLE_FOREACH_ARG_RAW_1 (t0), MONO_HANDLE_ARG_RAW (t1, 1) -#define MONO_HANDLE_FOREACH_ARG_RAW_3(t0, t1, t2) MONO_HANDLE_FOREACH_ARG_RAW_2 (t0, t1), MONO_HANDLE_ARG_RAW (t2, 2) -#define MONO_HANDLE_FOREACH_ARG_RAW_4(t0, t1, t2, t3) MONO_HANDLE_FOREACH_ARG_RAW_3 (t0, t1, t2), MONO_HANDLE_ARG_RAW (t3, 3) -#define MONO_HANDLE_FOREACH_ARG_RAW_5(t0, t1, t2, t3, t4) MONO_HANDLE_FOREACH_ARG_RAW_4 (t0, t1, t2, t3), MONO_HANDLE_ARG_RAW (t4, 4) -#define MONO_HANDLE_FOREACH_ARG_RAW_6(t0, t1, t2, t3, t4, t5) MONO_HANDLE_FOREACH_ARG_RAW_5 (t0, t1, t2, t3, t4), MONO_HANDLE_ARG_RAW (t5, 5) -#define MONO_HANDLE_FOREACH_ARG_RAW_7(t0, t1, t2, t3, t4, t5, t6) MONO_HANDLE_FOREACH_ARG_RAW_6 (t0, t1, t2, t3, t4, t5), MONO_HANDLE_ARG_RAW (t6, 6) -#define MONO_HANDLE_FOREACH_ARG_RAW_8(t0, t1, t2, t3, t4, t5, t6, t7) MONO_HANDLE_FOREACH_ARG_RAW_7 (t0, t1, t2, t3, t4, t5, t6), MONO_HANDLE_ARG_RAW (t7, 7) -#define MONO_HANDLE_FOREACH_ARG_RAW_9(t0, t1, t2, t3, t4, t5, t6, t7, t8) MONO_HANDLE_FOREACH_ARG_RAW_8 (t0, t1, t2, t3, t4, t5, t6, t7), MONO_HANDLE_ARG_RAW (t8, 8) +#define MONO_HANDLE_FOREACH_ARG_RAW_1(t0) MONO_HANDLE_ARG_RAWHANDLE (t0, 0) +#define MONO_HANDLE_FOREACH_ARG_RAW_2(t0, t1) MONO_HANDLE_FOREACH_ARG_RAW_1 (t0), MONO_HANDLE_ARG_RAWHANDLE (t1, 1) +#define MONO_HANDLE_FOREACH_ARG_RAW_3(t0, t1, t2) MONO_HANDLE_FOREACH_ARG_RAW_2 (t0, t1), MONO_HANDLE_ARG_RAWHANDLE (t2, 2) +#define MONO_HANDLE_FOREACH_ARG_RAW_4(t0, t1, t2, t3) MONO_HANDLE_FOREACH_ARG_RAW_3 (t0, t1, t2), MONO_HANDLE_ARG_RAWHANDLE (t3, 3) +#define MONO_HANDLE_FOREACH_ARG_RAW_5(t0, t1, t2, t3, t4) MONO_HANDLE_FOREACH_ARG_RAW_4 (t0, t1, t2, t3), MONO_HANDLE_ARG_RAWHANDLE (t4, 4) +#define MONO_HANDLE_FOREACH_ARG_RAW_6(t0, t1, t2, t3, t4, t5) MONO_HANDLE_FOREACH_ARG_RAW_5 (t0, t1, t2, t3, t4), MONO_HANDLE_ARG_RAWHANDLE (t5, 5) +#define MONO_HANDLE_FOREACH_ARG_RAW_7(t0, t1, t2, t3, t4, t5, t6) MONO_HANDLE_FOREACH_ARG_RAW_6 (t0, t1, t2, t3, t4, t5), MONO_HANDLE_ARG_RAWHANDLE (t6, 6) +#define MONO_HANDLE_FOREACH_ARG_RAW_8(t0, t1, t2, t3, t4, t5, t6, t7) MONO_HANDLE_FOREACH_ARG_RAW_7 (t0, t1, t2, t3, t4, t5, t6), MONO_HANDLE_ARG_RAWHANDLE (t7, 7) +#define MONO_HANDLE_FOREACH_ARG_RAW_9(t0, t1, t2, t3, t4, t5, t6, t7, t8) MONO_HANDLE_FOREACH_ARG_RAW_8 (t0, t1, t2, t3, t4, t5, t6, t7), MONO_HANDLE_ARG_RAWHANDLE (t8, 8) // Call from the wrapper to the actual icall, passing on the // WRAP_NONE parameters directly, casting handles from raw to typed. @@ -322,12 +332,17 @@ typedef MonoReflectionModuleHandle MonoReflectionModuleOutHandle; // Declare the function that takes/returns typed handles. #define MONO_HANDLE_DECLARE(id, name, func, rettype, n, argtypes) \ MONO_HANDLE_TYPE_TYPED (rettype) \ -func (MONO_HANDLE_FOREACH_TYPE_TYPED_ ## n argtypes MONO_HANDLE_COMMA_ ## n MonoError *error) \ +func (MONO_HANDLE_FOREACH_TYPE_TYPED_ ## n argtypes MONO_HANDLE_COMMA_ ## n MonoError *error) -// Declare the function wrapper that takes/returns raw handles. +// Declare the function wrapper that takes raw handles and a MonoError and returns a raw pointer. +// +// FIXME The error variable is on the managed side instead of native +// only to satisfy fragile test external/coreclr/tests/src/CoreMangLib/cti/system/weakreference/weakreferenceisaliveb.exe. +// I.e. We should have ERROR_DECL instead of error_init and MonoError parameter +// should be a local instead of a parameter. The different is minor. #define MONO_HANDLE_DECLARE_RAW(id, name, func, rettype, n, argtypes) \ -ICALL_EXPORT MONO_HANDLE_TYPE_RAW (rettype) \ -func ## _raw ( MONO_HANDLE_FOREACH_ARG_RAW_ ## n argtypes MONO_HANDLE_COMMA_ ## n MonoError *error) \ +ICALL_EXPORT MONO_HANDLE_TYPE_RAWPOINTER (rettype) \ +func ## _raw ( MONO_HANDLE_FOREACH_ARG_RAW_ ## n argtypes MONO_HANDLE_COMMA_ ## n MonoError *error) // Implement ves_icall_foo_raw over ves_icall_foo. // Raw handles are converted to/from typed handles and the rest is passed through. @@ -338,9 +353,16 @@ MONO_HANDLE_DECLARE_RAW (id, name, func, rettype, n, argtypes) \ { \ g_assert (cond); \ \ + HANDLE_FUNCTION_ENTER (); \ + \ + /* FIXME Should be ERROR_DECL but for fragile test. */ \ + error_init (error); \ + \ MONO_HANDLE_RETURN_BEGIN (rettype) \ \ - func (MONO_HANDLE_CALL_ ## n argtypes MONO_HANDLE_COMMA_ ## n error) \ + func (MONO_HANDLE_CALL_ ## n argtypes MONO_HANDLE_COMMA_ ## n error); \ + \ + mono_error_set_pending_exception (error); \ \ MONO_HANDLE_RETURN_END (rettype) \ } \ diff --git a/mono/metadata/marshal-ilgen.c b/mono/metadata/marshal-ilgen.c index 4952b302173..31e8cc37b22 100644 --- a/mono/metadata/marshal-ilgen.c +++ b/mono/metadata/marshal-ilgen.c @@ -6240,9 +6240,9 @@ typedef enum { typedef struct { IcallHandlesWrap wrap; - /* if wrap is NONE or OBJ or VALUETYPE_REF, this is not meaningful. - if wrap is OBJ_INOUT it's the local var that holds the MonoObjectHandle. - */ + // If wrap is OBJ_OUT or OBJ_INOUT this holds the referenced managed object, + // in case the actual parameter refers to a native frame. + // Otherwise it is not meaningful. int handle; } IcallHandlesLocal; @@ -6274,7 +6274,7 @@ signature_param_uses_handles (MonoMethodSignature *sig, MonoMethodSignature *gen * for both valuetypes and reference types. */ if (generic_sig && mono_type_is_byref (generic_sig->params [param]) && - (generic_sig->params [param]->type == MONO_TYPE_VAR || generic_sig->params [param]->type == MONO_TYPE_MVAR)) + (generic_sig->params [param]->type == MONO_TYPE_VAR || generic_sig->params [param]->type == MONO_TYPE_MVAR)) return ICALL_HANDLES_WRAP_VALUETYPE_REF; if (MONO_TYPE_IS_REFERENCE (sig->params [param])) { @@ -6291,29 +6291,12 @@ signature_param_uses_handles (MonoMethodSignature *sig, MonoMethodSignature *gen } static void -mono_emit_handle_raw (MonoMethodBuilder *mb) -{ - // This is similar to MONO_HANDLE_RAW() but not quite the same. - // MONO_HANDLE_RAW accepts a struct by value, that contains - // a pointer to a pointer, checks the pointer for null, - // and dereferences it if it is not null. - // - // This code receives the pointer instead of the struct - // and assumes it is not null. - mono_mb_emit_byte (mb, CEE_LDIND_REF); -} - -static void emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig, gboolean check_exceptions, gboolean aot, MonoMethodPInvoke *piinfo) { // FIXME: - MonoClass *handle_stack_mark_class; - MonoClass *error_class; - int thread_info_var = -1, stack_mark_var = -1, error_var = -1; MonoMethodSignature *call_sig = csig; gboolean uses_handles = FALSE; gboolean foreign_icall = FALSE; - gboolean save_handles_to_locals = FALSE; IcallHandlesLocal *handles_locals = NULL; MonoMethodSignature *sig = mono_method_signature_internal (method); gboolean need_gc_safe = FALSE; @@ -6327,10 +6310,23 @@ emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, Mono if (G_UNLIKELY (foreign_icall)) { /* FIXME: we only want the transitions for hybrid suspend. Q: What to do about AOT? */ need_gc_safe = gc_safe_transition_builder_init (&gc_safe_transition_builder, mb, FALSE); + + if (G_UNLIKELY (need_gc_safe)) + gc_safe_transition_builder_add_locals (&gc_safe_transition_builder); + } + + if (sig->hasthis) { + /* + * Add a null check since public icalls can be called with 'call' which + * does no such check. + */ + mono_mb_emit_byte (mb, CEE_LDARG_0); + const int pos = mono_mb_emit_branch (mb, CEE_BRTRUE); + mono_mb_emit_exception (mb, "NullReferenceException", NULL); + mono_mb_patch_branch (mb, pos); } if (uses_handles) { - MonoMethodSignature *ret; MonoMethodSignature *generic_sig = NULL; if (method->is_inflated) { @@ -6340,137 +6336,94 @@ emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, Mono mono_error_assert_ok (error); } - /* Add a MonoError argument and figure out which args need to be wrapped in handles */ + // Add a MonoError argument (due to a fragile test external/coreclr/tests/src/CoreMangLib/cti/system/weakreference/weakreferenceisaliveb.exe), + // vs. on the native side. // FIXME: The stuff from mono_metadata_signature_dup_internal_with_padding () - ret = mono_metadata_signature_alloc (get_method_image (method), csig->param_count + 1); + call_sig = mono_metadata_signature_alloc (get_method_image (method), csig->param_count + 1); + call_sig->param_count = csig->param_count + 1; + call_sig->ret = csig->ret; + call_sig->pinvoke = csig->pinvoke; - ret->param_count = csig->param_count + 1; - ret->ret = csig->ret; + /* TODO support adding wrappers to non-static struct methods */ + g_assert (!sig->hasthis || !m_class_is_valuetype (mono_method_get_class (method))); + + /* Add MonoError* param */ + MonoClass * const error_class = mono_class_load_from_name (mono_get_corlib (), "Mono", "RuntimeStructs/MonoError"); + int const error_var = mono_mb_add_local (mb, m_class_get_byval_arg (error_class)); + call_sig->params [csig->param_count] = mono_class_get_byref_type (error_class); handles_locals = g_new0 (IcallHandlesLocal, csig->param_count); + for (int i = 0; i < csig->param_count; ++i) { - IcallHandlesWrap w = signature_param_uses_handles (csig, generic_sig, i); + // Determine which args need to be wrapped in handles and adjust icall signature. + // Here, a handle is a pointer to a volatile local in a managed frame -- which is sufficient and efficient. + const IcallHandlesWrap w = signature_param_uses_handles (csig, generic_sig, i); handles_locals [i].wrap = w; + int local = -1; + switch (w) { case ICALL_HANDLES_WRAP_OBJ: case ICALL_HANDLES_WRAP_OBJ_INOUT: case ICALL_HANDLES_WRAP_OBJ_OUT: - ret->params [i] = mono_class_get_byref_type (mono_class_from_mono_type_internal (csig->params[i])); - if (w == ICALL_HANDLES_WRAP_OBJ_OUT || w == ICALL_HANDLES_WRAP_OBJ_INOUT) - save_handles_to_locals = TRUE; + call_sig->params [i] = mono_class_get_byref_type (mono_class_from_mono_type_internal (csig->params[i])); break; case ICALL_HANDLES_WRAP_NONE: case ICALL_HANDLES_WRAP_VALUETYPE_REF: - ret->params [i] = csig->params [i]; + call_sig->params [i] = csig->params [i]; break; default: g_assert_not_reached (); } - } - /* Add MonoError* param */ - ret->params [csig->param_count] = m_class_get_byval_arg (mono_get_intptr_class ()); - ret->pinvoke = csig->pinvoke; - - call_sig = ret; - } - - if (G_UNLIKELY (need_gc_safe)) { - gc_safe_transition_builder_add_locals (&gc_safe_transition_builder); - } - if (uses_handles) { - handle_stack_mark_class = mono_class_load_from_name (mono_get_corlib (), "Mono", "RuntimeStructs/HandleStackMark"); - error_class = mono_class_load_from_name (mono_get_corlib (), "Mono", "RuntimeStructs/MonoError"); - - thread_info_var = mono_mb_add_local (mb, m_class_get_byval_arg (mono_get_intptr_class ())); - stack_mark_var = mono_mb_add_local (mb, m_class_get_byval_arg (handle_stack_mark_class)); - error_var = mono_mb_add_local (mb, m_class_get_byval_arg (error_class)); - - if (save_handles_to_locals) { - /* add a local var to hold the handles for each out arg */ - for (int i = 0; i < sig->param_count; ++i) { - int j = i + sig->hasthis; - switch (handles_locals[j].wrap) { - case ICALL_HANDLES_WRAP_NONE: - case ICALL_HANDLES_WRAP_OBJ: - case ICALL_HANDLES_WRAP_VALUETYPE_REF: - handles_locals [j].handle = -1; - break; - case ICALL_HANDLES_WRAP_OBJ_INOUT: - case ICALL_HANDLES_WRAP_OBJ_OUT: - handles_locals [j].handle = mono_mb_add_local (mb, sig->params [i]); - break; - default: - g_assert_not_reached (); - } + // Add a local var to hold the references for each out arg. + switch (w) { + case ICALL_HANDLES_WRAP_OBJ_INOUT: + case ICALL_HANDLES_WRAP_OBJ_OUT: + // FIXME better type + local = mono_mb_add_local (mb, mono_get_object_type ()); + mono_bitset_set_safe (&mb->volatile_locals, local); + break; + case ICALL_HANDLES_WRAP_VALUETYPE_REF: + case ICALL_HANDLES_WRAP_OBJ: + mono_bitset_set_safe (&mb->volatile_args, i); + break; + case ICALL_HANDLES_WRAP_NONE: + break; + default: + g_assert_not_reached (); } - } - } - - if (sig->hasthis) { - int pos; + handles_locals [i].handle = local; - /* - * Add a null check since public icalls can be called with 'call' which - * does no such check. - */ - mono_mb_emit_byte (mb, CEE_LDARG_0); - pos = mono_mb_emit_branch (mb, CEE_BRTRUE); - mono_mb_emit_exception (mb, "NullReferenceException", NULL); - mono_mb_patch_branch (mb, pos); - } - - if (uses_handles) { - mono_mb_emit_ldloc_addr (mb, stack_mark_var); - mono_mb_emit_ldloc_addr (mb, error_var); - mono_mb_emit_icall (mb, mono_icall_start); - mono_mb_emit_stloc (mb, thread_info_var); - - if (sig->hasthis) { - mono_mb_emit_byte (mb, CEE_LDARG_0); - /* TODO support adding wrappers to non-static struct methods */ - g_assert (!m_class_is_valuetype (mono_method_get_class (method))); - mono_mb_emit_icall (mb, mono_icall_handle_new); - } - for (int i = 0; i < sig->param_count; i++) { - /* load each argument. references into the managed heap get wrapped in handles */ - int j = i + sig->hasthis; - switch (handles_locals[j].wrap) { + // Load each argument. References into the managed heap get wrapped in handles. + // Again, handles here are just pointers to managed volatile locals. + switch (w) { case ICALL_HANDLES_WRAP_NONE: - mono_mb_emit_ldarg (mb, j); + case ICALL_HANDLES_WRAP_VALUETYPE_REF: + // argI = argI + mono_mb_emit_ldarg (mb, i); break; case ICALL_HANDLES_WRAP_OBJ: - /* argI = mono_handle_new (argI_raw) */ - mono_mb_emit_ldarg (mb, j); - mono_mb_emit_icall (mb, mono_icall_handle_new); + // argI = &argI_raw + mono_mb_emit_ldarg_addr (mb, i); break; case ICALL_HANDLES_WRAP_OBJ_INOUT: case ICALL_HANDLES_WRAP_OBJ_OUT: - /* if inout: - * handleI = argI = mono_handle_new (*argI_raw) - * otherwise: - * handleI = argI = mono_handle_new (NULL) - */ - if (handles_locals[j].wrap == ICALL_HANDLES_WRAP_OBJ_INOUT) { - mono_mb_emit_ldarg (mb, j); - mono_mb_emit_byte (mb, CEE_LDIND_REF); - } else + // If parameter guaranteeably referred to a managed frame, + // then could just be passthrough and volatile. Since + // that cannot be guaranteed, use a managed volatile local intermediate. + // ObjOut: + // localI = NULL + // ObjInOut: + // localI = *argI_raw + // &localI + if (w == ICALL_HANDLES_WRAP_OBJ_OUT) { mono_mb_emit_byte (mb, CEE_LDNULL); - mono_mb_emit_icall (mb, mono_icall_handle_new); - /* tmp = argI */ - mono_mb_emit_byte (mb, CEE_DUP); - /* handleI = tmp */ - mono_mb_emit_stloc (mb, handles_locals[j].handle); - break; - case ICALL_HANDLES_WRAP_VALUETYPE_REF: - /* (void) mono_handle_new (argI); argI */ - mono_mb_emit_ldarg (mb, j); - mono_mb_emit_byte (mb, CEE_DUP); - mono_mb_emit_icall (mb, mono_icall_handle_new_interior); - mono_mb_emit_byte (mb, CEE_POP); -#if 0 - fprintf (stderr, " Method %s.%s.%s has byref valuetype argument %d\n", method->klass->name_space, method->klass->name, method->name, i); -#endif + } else { + mono_mb_emit_ldarg (mb, i); + mono_mb_emit_byte (mb, CEE_LDIND_REF); + } + mono_mb_emit_stloc (mb, local); + mono_mb_emit_ldloc_addr (mb, local); break; default: g_assert_not_reached (); @@ -6478,10 +6431,8 @@ emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, Mono } mono_mb_emit_ldloc_addr (mb, error_var); } else { - if (sig->hasthis) - mono_mb_emit_byte (mb, CEE_LDARG_0); - for (int i = 0; i < sig->param_count; i++) - mono_mb_emit_ldarg (mb, i + sig->hasthis); + for (int i = 0; i < csig->param_count; i++) + mono_mb_emit_ldarg (mb, i); } if (G_UNLIKELY (need_gc_safe)) @@ -6499,49 +6450,20 @@ emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, Mono if (G_UNLIKELY (need_gc_safe)) gc_safe_transition_builder_emit_exit (&gc_safe_transition_builder); - if (uses_handles) { - if (MONO_TYPE_IS_REFERENCE (sig->ret)) { - // if (ret != NULL_HANDLE) { - // ret = MONO_HANDLE_RAW(ret) - // } - mono_mb_emit_byte (mb, CEE_DUP); - int pos = mono_mb_emit_branch (mb, CEE_BRFALSE); - mono_emit_handle_raw (mb); - mono_mb_patch_branch (mb, pos); - } - if (save_handles_to_locals) { - for (int i = 0; i < sig->param_count; i++) { - int j = i + sig->hasthis; - switch (handles_locals [j].wrap) { - case ICALL_HANDLES_WRAP_NONE: - case ICALL_HANDLES_WRAP_OBJ: - case ICALL_HANDLES_WRAP_VALUETYPE_REF: - break; - case ICALL_HANDLES_WRAP_OBJ_INOUT: - case ICALL_HANDLES_WRAP_OBJ_OUT: - /* *argI_raw = MONO_HANDLE_RAW (handleI) */ - - /* argI_raw */ - mono_mb_emit_ldarg (mb, j); - /* handleI */ - mono_mb_emit_ldloc (mb, handles_locals [j].handle); - /* MONO_HANDLE_RAW(handleI) */ - mono_emit_handle_raw (mb); - /* *argI_raw = MONO_HANDLE_RAW(handleI) */ - mono_mb_emit_byte (mb, CEE_STIND_REF); - break; - default: - g_assert_not_reached (); - } + // Copy back ObjIn and ObjInOut from locals through parameters. + if (mb->volatile_locals) { + g_assert (handles_locals); + for (int i = 0; i < csig->param_count; i++) { + const int local = handles_locals [i].handle; + if (local >= 0) { + // *argI_raw = localI + mono_mb_emit_ldarg (mb, i); + mono_mb_emit_ldloc (mb, local); + mono_mb_emit_byte (mb, CEE_STIND_REF); } } - g_free (handles_locals); - - mono_mb_emit_ldloc (mb, thread_info_var); - mono_mb_emit_ldloc_addr (mb, stack_mark_var); - mono_mb_emit_ldloc_addr (mb, error_var); - mono_mb_emit_icall (mb, mono_icall_end); } + g_free (handles_locals); if (G_UNLIKELY (need_gc_safe)) gc_safe_transition_builder_cleanup (&gc_safe_transition_builder); diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index 7132ed02272..424d91ed9a3 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -286,10 +286,6 @@ mono_marshal_init (void) register_icall (mono_threads_exit_gc_unsafe_region_unbalanced, "mono_threads_exit_gc_unsafe_region_unbalanced", "void ptr ptr", TRUE); register_icall (mono_threads_attach_coop, "mono_threads_attach_coop", "ptr ptr ptr", TRUE); register_icall (mono_threads_detach_coop, "mono_threads_detach_coop", "void ptr ptr", TRUE); - register_icall (mono_icall_start, "mono_icall_start", "ptr ptr ptr", TRUE); - register_icall (mono_icall_end, "mono_icall_end", "void ptr ptr ptr", TRUE); - register_icall (mono_icall_handle_new, "mono_icall_handle_new", "ptr ptr", TRUE); - register_icall (mono_icall_handle_new_interior, "mono_icall_handle_new_interior", "ptr ptr", TRUE); register_icall (mono_marshal_get_type_object, "mono_marshal_get_type_object", "object ptr", TRUE); mono_cominterop_init (); @@ -6199,40 +6195,6 @@ mono_marshal_free_dynamic_wrappers (MonoMethod *method) mono_marshal_unlock (); } -MonoThreadInfo* -mono_icall_start (HandleStackMark *stackmark, MonoError *error) -{ - MonoThreadInfo *info = mono_thread_info_current (); - - mono_stack_mark_init (info, stackmark); - error_init (error); - return info; -} - -void -mono_icall_end (MonoThreadInfo *info, HandleStackMark *stackmark, MonoError *error) -{ - mono_stack_mark_pop (info, stackmark); - if (G_UNLIKELY (!is_ok (error))) - mono_error_set_pending_exception (error); -} - -MonoRawHandle -mono_icall_handle_new (gpointer rawobj) -{ - return mono_handle_new ((MonoObject*)rawobj); -} - -gpointer -mono_icall_handle_new_interior (gpointer rawobj) -{ -#ifdef MONO_HANDLE_TRACK_OWNER - return mono_handle_new_interior ((MonoObject*)rawobj, ""); -#else - return mono_handle_new_interior ((MonoObject*)rawobj); -#endif -} - MonoObject* mono_marshal_get_type_object (MonoClass *klass) { diff --git a/mono/metadata/marshal.h b/mono/metadata/marshal.h index 7d147d1e595..673b6d92c67 100644 --- a/mono/metadata/marshal.h +++ b/mono/metadata/marshal.h @@ -640,18 +640,6 @@ mono_pinvoke_is_unicode (MonoMethodPInvoke *piinfo); gboolean mono_marshal_need_free (MonoType *t, MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec); -MonoThreadInfo* -mono_icall_start (HandleStackMark *stackmark, MonoError *error); - -void -mono_icall_end (MonoThreadInfo *info, HandleStackMark *stackmark, MonoError *error); - -MonoRawHandle -mono_icall_handle_new (gpointer rawobj); - -gpointer -mono_icall_handle_new_interior (gpointer rawobj); - MonoObject* mono_marshal_get_type_object (MonoClass *klass); ICALL_EXPORT diff --git a/mono/metadata/metadata-internals.h b/mono/metadata/metadata-internals.h index 3bdb6f6dc36..67bef81712b 100644 --- a/mono/metadata/metadata-internals.h +++ b/mono/metadata/metadata-internals.h @@ -611,6 +611,8 @@ struct _MonoMethodHeader { unsigned int init_locals : 1; guint16 num_locals; MonoExceptionClause *clauses; + MonoBitSet *volatile_args; + MonoBitSet *volatile_locals; MonoType *locals [MONO_ZERO_LEN_ARRAY]; }; diff --git a/mono/metadata/method-builder-ilgen-internals.h b/mono/metadata/method-builder-ilgen-internals.h index 5c15a089382..642c49030c7 100644 --- a/mono/metadata/method-builder-ilgen-internals.h +++ b/mono/metadata/method-builder-ilgen-internals.h @@ -22,12 +22,16 @@ struct _MonoMethodBuilder { GList *locals_list; gint locals; gboolean dynamic; - gboolean skip_visibility, init_locals; - guint32 code_size, pos; + gboolean skip_visibility; + gboolean init_locals; + guint32 code_size; + guint32 pos; guchar *code; gint num_clauses; MonoExceptionClause *clauses; const gchar **param_names; + MonoBitSet *volatile_args; + MonoBitSet *volatile_locals; }; #endif diff --git a/mono/metadata/method-builder-ilgen.c b/mono/metadata/method-builder-ilgen.c index 832c88e81a0..0dabe5358f0 100644 --- a/mono/metadata/method-builder-ilgen.c +++ b/mono/metadata/method-builder-ilgen.c @@ -141,6 +141,10 @@ create_method_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *signature, int header->code_size = mb->pos; header->num_locals = mb->locals; header->init_locals = mb->init_locals; + header->volatile_args = mb->volatile_args; + header->volatile_locals = mb->volatile_locals; + mb->volatile_args = NULL; + mb->volatile_locals = NULL; header->num_clauses = mb->num_clauses; header->clauses = mb->clauses; diff --git a/mono/metadata/object-internals.h b/mono/metadata/object-internals.h index 5c828edf471..04c85fa16d1 100644 --- a/mono/metadata/object-internals.h +++ b/mono/metadata/object-internals.h @@ -1936,7 +1936,13 @@ void mono_error_raise_exception_deprecated (MonoError *target_error); gboolean -mono_error_set_pending_exception (MonoError *error); +mono_error_set_pending_exception_slow (MonoError *error); + +static inline gboolean +mono_error_set_pending_exception (MonoError *error) +{ + return is_ok (error) ? FALSE : mono_error_set_pending_exception_slow (error); +} MonoArray * mono_glist_to_array (GList *list, MonoClass *eclass, MonoError *error); diff --git a/mono/mini/mini.c b/mono/mini/mini.c index 42ece7e91b8..1ce6b312a44 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -2010,6 +2010,12 @@ mono_add_var_location (MonoCompile *cfg, MonoInst *var, gboolean is_reg, int reg } static void +mono_apply_volatile (MonoInst *inst, MonoBitSet *set, gsize index) +{ + inst->flags |= mono_bitset_test_safe (set, index) ? MONO_INST_VOLATILE : 0; +} + +static void mono_compile_create_vars (MonoCompile *cfg) { MonoMethodSignature *sig; @@ -2031,12 +2037,16 @@ mono_compile_create_vars (MonoCompile *cfg) cfg->args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, (sig->param_count + sig->hasthis) * sizeof (MonoInst*)); if (sig->hasthis) { - cfg->args [0] = mono_compile_create_var (cfg, m_class_get_this_arg (cfg->method->klass), OP_ARG); - cfg->this_arg = cfg->args [0]; + MonoInst* arg = mono_compile_create_var (cfg, m_class_get_this_arg (cfg->method->klass), OP_ARG); + mono_apply_volatile (arg, header->volatile_args, 0); + cfg->args [0] = arg; + cfg->this_arg = arg; } for (i = 0; i < sig->param_count; ++i) { - cfg->args [i + sig->hasthis] = mono_compile_create_var (cfg, sig->params [i], OP_ARG); + MonoInst* arg = mono_compile_create_var (cfg, sig->params [i], OP_ARG); + mono_apply_volatile (arg, header->volatile_args, i + sig->hasthis); + cfg->args [i + sig->hasthis] = arg; } if (cfg->verbose_level > 2) { @@ -2066,8 +2076,15 @@ mono_compile_create_vars (MonoCompile *cfg) if (cfg->verbose_level > 2) g_print ("\tlocal [%d]: ", i); cfg->locals [i] = mono_compile_create_var (cfg, header->locals [i], OP_LOCAL); + mono_apply_volatile (cfg->locals [i], header->volatile_locals, i); } + mono_bitset_free (header->volatile_locals); + header->volatile_locals = NULL; + + mono_bitset_free (header->volatile_args); + header->volatile_args = NULL; + if (cfg->verbose_level > 2) g_print ("locals done\n"); diff --git a/mono/utils/monobitset.c b/mono/utils/monobitset.c index c3615d14bbe..43ab9e3bcd0 100644 --- a/mono/utils/monobitset.c +++ b/mono/utils/monobitset.c @@ -73,7 +73,7 @@ mono_bitset_mem_new (gpointer mem, guint32 max_size, guint32 flags) { */ void mono_bitset_free (MonoBitSet *set) { - if (!(set->flags & MONO_BITSET_DONT_FREE)) + if (set && !(set->flags & MONO_BITSET_DONT_FREE)) g_free (set); } @@ -644,6 +644,37 @@ mono_bitset_foreach (MonoBitSet *set, MonoBitSetFunc func, gpointer data) } } +static void +mono_bitset_resize (MonoBitSet **set, guint32 max_size) +{ + if (max_size == 0) + return; + if (!*set) { + *set = mono_bitset_new (max_size, 0); + return; + } + if ((*set)->size >= max_size) + return; + MonoBitSet *new_set = mono_bitset_new (max_size, (*set)->flags); + // mono_bitset_copyto uses the wrong size. + memcpy (&new_set->data, &(*set)->data, (*set)->size / 8); + mono_bitset_free (*set); + *set = new_set; +} + +void +mono_bitset_set_safe (MonoBitSet **set, guint32 pos) +{ + mono_bitset_resize (set, pos + 1); + mono_bitset_set (*set, pos); +} + +gboolean +mono_bitset_test_safe (const MonoBitSet *set, guint32 pos) +{ + return set && set->size > pos && mono_bitset_test (set, pos); +} + #ifdef TEST_BITSET /* diff --git a/mono/utils/monobitset.h b/mono/utils/monobitset.h index fbeedeefbe1..52aa1876013 100644 --- a/mono/utils/monobitset.h +++ b/mono/utils/monobitset.h @@ -121,4 +121,10 @@ MONO_API void mono_bitset_foreach (MonoBitSet *set, MonoBitSetFunc f MONO_API void mono_bitset_intersection_2 (MonoBitSet *dest, const MonoBitSet *src1, const MonoBitSet *src2); +void +mono_bitset_set_safe (MonoBitSet **set, guint32 pos); + +gboolean +mono_bitset_test_safe (const MonoBitSet *set, guint32 pos); + #endif /* __MONO_BITSET_H__ */ -- 2.11.4.GIT