diff --git a/src/mono/mono/metadata/cominterop.c b/src/mono/mono/metadata/cominterop.c index 4c46107221a44..e80cca9f292d7 100644 --- a/src/mono/mono/metadata/cominterop.c +++ b/src/mono/mono/metadata/cominterop.c @@ -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); diff --git a/src/mono/mono/metadata/image.c b/src/mono/mono/metadata/image.c index 79d3921b03439..fe5ec7d9eb103 100644 --- a/src/mono/mono/metadata/image.c +++ b/src/mono/mono/metadata/image.c @@ -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); diff --git a/src/mono/mono/metadata/marshal-ilgen.c b/src/mono/mono/metadata/marshal-ilgen.c index ea580d33e0450..17337b71bbb3d 100644 --- a/src/mono/mono/metadata/marshal-ilgen.c +++ b/src/mono/mono/metadata/marshal-ilgen.c @@ -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; + gboolean skip_gc_trans = (flags & EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS) != 0; EmitMarshalContext m; MonoMethodSignature *csig; MonoClass *klass; @@ -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); } diff --git a/src/mono/mono/metadata/marshal-noilgen.c b/src/mono/mono/metadata/marshal-noilgen.c index 6555604810baf..cb79e2f504a73 100644 --- a/src/mono/mono/metadata/marshal-noilgen.c +++ b/src/mono/mono/metadata/marshal-noilgen.c @@ -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) { } diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 17d0430d740c8..44cf4ad27ee7a 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -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) { @@ -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; @@ -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; @@ -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; @@ -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 + + /* 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: * @@ -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; diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index 13b3ec94da513..2ec6326acdb87 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -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, @@ -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; @@ -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); @@ -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); @@ -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); diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index 3dbf9b85f3f3a..329f839f7fc33 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -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; diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index d7a792bdf57fe..73a6978c988e9 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -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; } diff --git a/src/mono/mono/metadata/metadata.h b/src/mono/mono/metadata/metadata.h index 965b06a86c195..c44931d7328d8 100644 --- a/src/mono/mono/metadata/metadata.h +++ b/src/mono/mono/metadata/metadata.h @@ -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... */ diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 3e383722bcf72..530320f0d07ee 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -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); + cfg->real_offset += 5; + inline_costs += costs; + ins = sp[0]; + } else { + ins = mono_emit_method_call (cfg, wrapper, /*args*/sp, NULL); + } + goto calli_end; + } + } + n = fsig->param_count + fsig->hasthis; CHECK_STACK (n);