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][jit] Emit GC transitions for "calli unmanaged" instructions #46491

Merged
merged 11 commits into from
Jan 6, 2021
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/cominterop.c
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ cominterop_get_native_wrapper_adjusted (MonoMethod *method)
}
}

mono_marshal_emit_native_wrapper (m_class_get_image (method->klass), mb_native, sig_native, piinfo, mspecs, piinfo->addr, FALSE, TRUE, FALSE, FALSE);
mono_marshal_emit_native_wrapper (m_class_get_image (method->klass), mb_native, sig_native, piinfo, mspecs, piinfo->addr, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS);

res = mono_mb_create_method (mb_native, sig_native, sig_native->param_count + 16);

Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -2493,6 +2493,7 @@ mono_wrapper_caches_free (MonoWrapperCaches *cache)
free_hash (cache->native_wrapper_aot_check_cache);

free_hash (cache->native_func_wrapper_aot_cache);
free_hash (cache->native_func_wrapper_indirect_cache);
free_hash (cache->remoting_invoke_cache);
free_hash (cache->synchronized_cache);
free_hash (cache->unbox_wrapper_cache);
Expand Down
16 changes: 12 additions & 4 deletions src/mono/mono/metadata/marshal-ilgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1995,13 +1995,19 @@ gc_safe_transition_builder_cleanup (GCSafeTransitionBuilder *builder)
* \param method if non-NULL, the pinvoke method to call
* \param check_exceptions Whenever to check for pending exceptions after the native call
* \param func_param the function to call is passed as a boxed IntPtr as the first parameter
* \param func_param_unboxed combined with \p func_param, expect the function to call as an unboxed IntPtr as the first parameter
* \param skip_gc_trans Whenever to skip GC transitions
*
* generates IL code for the pinvoke wrapper, the generated code calls \p func .
*/
static void
emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param, gboolean skip_gc_trans)
emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags)
{
gboolean aot = (flags & EMIT_NATIVE_WRAPPER_AOT) != 0;
gboolean check_exceptions = (flags & EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS) != 0;
gboolean func_param = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM) != 0;
gboolean func_param_unboxed = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED) != 0;
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
gboolean skip_gc_trans = (flags & EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS) != 0;
EmitMarshalContext m;
MonoMethodSignature *csig;
MonoClass *klass;
Expand Down Expand Up @@ -2139,9 +2145,11 @@ emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSi
/* call the native method */
if (func_param) {
mono_mb_emit_byte (mb, CEE_LDARG_0);
mono_mb_emit_op (mb, CEE_UNBOX, mono_defaults.int_class);
mono_mb_emit_byte (mb, CEE_LDIND_I);
if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) {
if (!func_param_unboxed) {
mono_mb_emit_op (mb, CEE_UNBOX, mono_defaults.int_class);
mono_mb_emit_byte (mb, CEE_LDIND_I);
}
if (piinfo && (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) != 0) {
mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
}
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/marshal-noilgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ emit_thunk_invoke_wrapper_noilgen (MonoMethodBuilder *mb, MonoMethod *method, Mo
}

static void
emit_native_wrapper_noilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param, gboolean skip_gc_trans)
emit_native_wrapper_noilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags)
{
}

Expand Down
88 changes: 83 additions & 5 deletions src/mono/mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ static GENERATE_TRY_GET_CLASS_WITH_CACHE (suppress_gc_transition_attribute, "Sys
static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callers_only_attribute, "System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute")
#endif

static gboolean type_is_blittable (MonoType *type);

static MonoImage*
get_method_image (MonoMethod *method)
{
Expand Down Expand Up @@ -3518,7 +3520,11 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
}
#endif

mono_marshal_emit_native_wrapper (get_method_image (mb->method), mb, csig, piinfo, mspecs, piinfo->addr, aot, check_exceptions, FALSE, skip_gc_trans);
MonoNativeWrapperFlags flags = aot ? EMIT_NATIVE_WRAPPER_AOT : (MonoNativeWrapperFlags)0;
flags |= check_exceptions ? EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS : (MonoNativeWrapperFlags)0;
flags |= skip_gc_trans ? EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS : (MonoNativeWrapperFlags)0;

mono_marshal_emit_native_wrapper (get_method_image (mb->method), mb, csig, piinfo, mspecs, piinfo->addr, flags);
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_PINVOKE);
info->d.managed_to_native.method = method;

Expand Down Expand Up @@ -3573,7 +3579,7 @@ mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig
mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE);
mb->method->save_lmf = 1;

mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, FALSE, TRUE, FALSE, FALSE);
mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS);

csig = mono_metadata_signature_dup_full (image, sig);
csig->pinvoke = 0;
Expand Down Expand Up @@ -3636,7 +3642,7 @@ mono_marshal_get_native_func_wrapper_aot (MonoClass *klass)
mb = mono_mb_new (invoke->klass, name, MONO_WRAPPER_MANAGED_TO_NATIVE);
mb->method->save_lmf = 1;

mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, NULL, FALSE, TRUE, TRUE, FALSE);
mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, NULL, EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS | EMIT_NATIVE_WRAPPER_FUNC_PARAM);

info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NATIVE_FUNC_AOT);
info->d.managed_to_native.method = invoke;
Expand All @@ -3658,6 +3664,78 @@ mono_marshal_get_native_func_wrapper_aot (MonoClass *klass)
return res;
}

/*
* Gets a wrapper for an indirect call to a function with the given signature.
* The actual function is passed as the first argument to the wrapper.
*
* The wrapper is
*
* retType wrapper (fnPtr, arg1... argN) {
* enter_gc_safe;
* ret = fnPtr (arg1, ... argN);
* exit_gc_safe;
* return ret;
* }
*
*/
MonoMethod*
mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMethodSignature *sig,
gboolean aot)
{
caller_class = mono_class_get_generic_type_definition (caller_class);
MonoImage *image = m_class_get_image (caller_class);
g_assert (sig->pinvoke);
g_assert (!sig->hasthis && ! sig->explicit_this);
g_assert (!sig->is_inflated && !sig->has_type_parameters);

g_assertf (type_is_blittable (sig->ret), "sig return type %s is not blittable\n", mono_type_full_name (sig->ret));

for (int i = 0; i < sig->param_count; ++i) {
MonoType *ty = sig->params [i];
g_assertf (type_is_blittable (ty), "sig param %d (type %s) is not blittable\n", i, mono_type_full_name (ty));
}
/* g_assert (every param and return type is blittable) */

GHashTable *cache = get_cache (&image->wrapper_caches.native_func_wrapper_indirect_cache,
(GHashFunc)mono_signature_hash,
(GCompareFunc)mono_metadata_signature_equal);

MonoMethod *res;
if ((res = mono_marshal_find_in_cache (cache, sig)))
return res;

#if 0
fprintf (stderr, "generating wrapper for signature %s\n", mono_signature_full_name (sig));
#endif

lambdageek marked this conversation as resolved.
Show resolved Hide resolved
/* FIXME: better wrapper name */
char * name = g_strdup_printf ("wrapper_native_indirect_%p", sig);
MonoMethodBuilder *mb = mono_mb_new (caller_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE);
mb->method->save_lmf = 1;

WrapperInfo *info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NATIVE_FUNC_INDIRECT);
info->d.managed_to_native.method = NULL;

MonoMethodPInvoke *piinfo = NULL;
MonoMarshalSpec **mspecs = g_new0 (MonoMarshalSpec *, 1 + sig->param_count);
MonoNativeWrapperFlags flags = aot ? EMIT_NATIVE_WRAPPER_AOT : (MonoNativeWrapperFlags)0;
flags |= EMIT_NATIVE_WRAPPER_FUNC_PARAM | EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED;
mono_marshal_emit_native_wrapper (image, mb, sig, piinfo, mspecs, /*func*/NULL, flags);
g_free (mspecs);

MonoMethodSignature *csig = mono_metadata_signature_dup_add_this (image, sig, mono_defaults.int_class);
csig->pinvoke = 0;

MonoMethodSignature *key_sig = mono_metadata_signature_dup_full (image, sig);

gboolean found;
res = mono_mb_create_and_cache_full (cache, key_sig, mb, csig, csig->param_count + 16, info, &found);

mono_mb_free (mb);

return res;
}

/*
* mono_marshal_emit_managed_wrapper:
*
Expand Down Expand Up @@ -6383,9 +6461,9 @@ mono_marshal_lookup_pinvoke (MonoMethod *method)
}

void
mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param, gboolean skip_gc_trans)
mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags)
{
get_marshal_cb ()->emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, aot, check_exceptions, func_param, skip_gc_trans);
get_marshal_cb ()->emit_native_wrapper (image, mb, sig, piinfo, mspecs, func, flags);
}

static MonoMarshalCallbacks marshal_cb;
Expand Down
21 changes: 18 additions & 3 deletions src/mono/mono/metadata/marshal.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ typedef enum {
/* Subtypes of MONO_WRAPPER_MANAGED_TO_NATIVE */
WRAPPER_SUBTYPE_ICALL_WRAPPER, // specifically JIT icalls
WRAPPER_SUBTYPE_NATIVE_FUNC_AOT,
WRAPPER_SUBTYPE_NATIVE_FUNC_INDIRECT,
WRAPPER_SUBTYPE_PINVOKE,
/* Subtypes of MONO_WRAPPER_OTHER */
WRAPPER_SUBTYPE_SYNCHRONIZED_INNER,
Expand Down Expand Up @@ -300,7 +301,17 @@ typedef enum {
} MonoStelemrefKind;


#define MONO_MARSHAL_CALLBACKS_VERSION 4
typedef enum {
EMIT_NATIVE_WRAPPER_AOT = 0x01, /* FIXME: what does "aot" mean here */
EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS = 0x02,
EMIT_NATIVE_WRAPPER_FUNC_PARAM = 0x04,
EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED = 0x08,
EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS=0x10,
} MonoNativeWrapperFlags;

G_ENUM_FUNCTIONS(MonoNativeWrapperFlags);

#define MONO_MARSHAL_CALLBACKS_VERSION 5

typedef struct {
int version;
Expand All @@ -325,7 +336,7 @@ typedef struct {
void (*emit_virtual_stelemref) (MonoMethodBuilder *mb, const char **param_names, MonoStelemrefKind kind);
void (*emit_stelemref) (MonoMethodBuilder *mb);
void (*emit_array_address) (MonoMethodBuilder *mb, int rank, int elem_size);
void (*emit_native_wrapper) (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param, gboolean skip_gc_trans);
void (*emit_native_wrapper) (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags);
void (*emit_managed_wrapper) (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle);
void (*emit_runtime_invoke_body) (MonoMethodBuilder *mb, const char **param_names, MonoImage *image, MonoMethod *method, MonoMethodSignature *sig, MonoMethodSignature *callsig, gboolean virtual_, gboolean need_direct_wrapper);
void (*emit_runtime_invoke_dynamic) (MonoMethodBuilder *mb);
Expand Down Expand Up @@ -473,6 +484,10 @@ mono_marshal_get_native_func_wrapper (MonoImage *image, MonoMethodSignature *sig
MonoMethod*
mono_marshal_get_native_func_wrapper_aot (MonoClass *klass);

MonoMethod*
mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMethodSignature *sig,
gboolean aot);

MonoMethod *
mono_marshal_get_struct_to_ptr (MonoClass *klass);

Expand Down Expand Up @@ -668,7 +683,7 @@ mono_signature_no_pinvoke (MonoMethod *method);
/* Called from cominterop.c/remoting.c */

void
mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, gboolean aot, gboolean check_exceptions, gboolean func_param, gboolean skip_gc_trans);
mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags);

void
mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle);
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/metadata-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ typedef struct {
GHashTable *native_wrapper_aot_check_cache;

GHashTable *native_func_wrapper_aot_cache;
GHashTable *native_func_wrapper_indirect_cache; /* Indexed by MonoMethodSignature. Protected by the marshal lock */
GHashTable *remoting_invoke_cache;
GHashTable *synchronized_cache;
GHashTable *unbox_wrapper_cache;
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -2355,6 +2355,7 @@ mono_metadata_parse_method_signature_full (MonoImage *m, MonoGenericContainer *c
case MONO_CALL_STDCALL:
case MONO_CALL_THISCALL:
case MONO_CALL_FASTCALL:
case MONO_CALL_UNMANAGED_MD:
method->pinvoke = 1;
break;
}
Expand Down
6 changes: 5 additions & 1 deletion src/mono/mono/metadata/metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ typedef enum {
MONO_CALL_STDCALL,
MONO_CALL_THISCALL,
MONO_CALL_FASTCALL,
MONO_CALL_VARARG
MONO_CALL_VARARG = 0x05,
/* unused, */
/* unused, */
/* unused, */
MONO_CALL_UNMANAGED_MD = 0x09, /* default unmanaged calling convention, with additional attributed encoded in modopts */
} MonoCallConvention;

/* ECMA lamespec: the old spec had more info... */
Expand Down
47 changes: 47 additions & 0 deletions src/mono/mono/mini/method-to-ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -7065,6 +7065,53 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
addr = mono_emit_jit_icall (cfg, mono_get_native_calli_wrapper, args);
}

if (!method->dynamic && fsig->pinvoke &&
!method->wrapper_type) {
/* MONO_WRAPPER_DYNAMIC_METHOD dynamic method handled above in the
method->dynamic case; for other wrapper types assume the code knows
what its doing and added its own GC transitions */

/* TODO: unmanaged[SuppressGCTransition] call conv will set
* skip_gc_trans to TRUE*/
gboolean skip_gc_trans = FALSE;
if (!skip_gc_trans) {
#if 0
fprintf (stderr, "generating wrapper for calli in method %s with wrapper type %s\n", method->name, mono_wrapper_type_to_str (method->wrapper_type));
#endif
/* Call the wrapper that will do the GC transition instead */
MonoMethod *wrapper = mono_marshal_get_native_func_wrapper_indirect (method->klass, fsig, cfg->compile_aot);

fsig = mono_method_signature_internal (wrapper);

n = fsig->param_count - 1; /* wrapper has extra fnptr param */

CHECK_STACK (n);

/* move the args to allow room for 'this' in the first position */
while (n--) {
--sp;
sp [1] = sp [0];
}

sp[0] = addr; /* n+1 args, first arg is the address of the indirect method to call */

g_assert (!fsig->hasthis && !fsig->pinvoke);

gboolean inline_wrapper = cfg->opt & MONO_OPT_INLINE || cfg->compile_aot;
if (inline_wrapper) {
int costs = inline_method (cfg, wrapper, fsig, sp, ip, cfg->real_offset, TRUE);
CHECK_CFG_EXCEPTION;
g_assert (costs > 0);
vargaz marked this conversation as resolved.
Show resolved Hide resolved
cfg->real_offset += 5;
inline_costs += costs;
ins = sp[0];
} else {
vargaz marked this conversation as resolved.
Show resolved Hide resolved
ins = mono_emit_method_call (cfg, wrapper, /*args*/sp, NULL);
}
goto calli_end;
}
}

n = fsig->param_count + fsig->hasthis;

CHECK_STACK (n);
Expand Down