Skip to content

Commit

Permalink
Mono: support CallConvSuppressGCTransition
Browse files Browse the repository at this point in the history
* Parse modopt encoded calling conventions
   Add `suppress_gc_transition` bit to `MonoMethodSignature` to add support for the `SuppressGCTransition` unmanaged calling convention modifier.

* Don't emit a wrapper on `calli` if `suppress_gc_transition` is set

   This adds support for `delegate* unmanaged[Cdecl, SuppressGCTransition] <TRet, TArgs...>` function pointers. (And other base calling conventions other than Cdecl).

* Allow blittable types in `mono_marshal_get_native_func_wrapper_indirect`

   This was already used by C++/CLI, so the C# function pointers spec allows blittable types in unmanaged function pointer types.

* Remove SuppressGCTransitionTest from exclude list

Fixes dotnet/runtime#46451
  • Loading branch information
lambdageek committed Jan 14, 2021
1 parent bda9bc5 commit 6aeac6a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 4 deletions.
8 changes: 7 additions & 1 deletion mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3688,13 +3688,19 @@ mono_marshal_get_native_func_wrapper_indirect (MonoClass *caller_class, MonoMeth
g_assert (!sig->hasthis && ! sig->explicit_this);
g_assert (!sig->is_inflated && !sig->has_type_parameters);

#if 0
/*
* Since calli sigs are already part of ECMA-335, they were already used by C++/CLI, which
* allowed non-blittable types. So the C# function pointers spec doesn't restrict this to
* blittable tyhpes only.
*/
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) */
#endif

GHashTable *cache = get_cache (&image->wrapper_caches.native_func_wrapper_indirect_cache,
(GHashFunc)mono_signature_hash,
Expand Down
1 change: 1 addition & 0 deletions mono/metadata/metadata-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ struct _MonoMethodSignature {
unsigned int pinvoke : 1;
unsigned int is_inflated : 1;
unsigned int has_type_parameters : 1;
unsigned int suppress_gc_transition : 1;
MonoType *params [MONO_ZERO_LEN_ARRAY];
};

Expand Down
73 changes: 73 additions & 0 deletions mono/metadata/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -2467,6 +2467,71 @@ mono_metadata_signature_size (MonoMethodSignature *sig)
return MONO_SIZEOF_METHOD_SIGNATURE + sig->param_count * sizeof (MonoType *);
}

/**
* metadata_signature_set_modopt_call_conv:
*
* Reads the custom attributes from \p cmod_type and adds them to the signature \p sig.
*
* This follows the C# unmanaged function pointer encoding.
* The modopts are from the System.Runtime.CompilerServices namespace and all have a name of the form CallConvXXX.
*
* The calling convention will be one of:
* Cdecl, Thiscall, Stdcall, Fastcall
* plus an optional SuppressGCTransition
*/
static void
metadata_signature_set_modopt_call_conv (MonoMethodSignature *sig, MonoType *cmod_type, MonoError *error)
{
uint8_t count = mono_type_custom_modifier_count (cmod_type);
if (count == 0)
return;
int base_callconv = sig->call_convention;
gboolean suppress_gc_transition = sig->suppress_gc_transition;
for (uint8_t i = 0; i < count; ++i) {
gboolean req = FALSE;
MonoType *cmod = mono_type_get_custom_modifier (cmod_type, i, &req, error);
return_if_nok (error);
/* callconv is a modopt, not a modreq */
if (req)
continue;
/* shouldn't be a valuetype, array, gparam, gtd, ginst etc */
if (cmod->type != MONO_TYPE_CLASS)
continue;
MonoClass *cmod_klass = mono_class_from_mono_type_internal (cmod);
if (m_class_get_image (cmod_klass) != mono_defaults.corlib)
continue;
if (strcmp (m_class_get_name_space (cmod_klass), "System.Runtime.CompilerServices"))
continue;
const char *name = m_class_get_name (cmod_klass);
if (strstr (name, "CallConv") != name)
continue;
name += strlen ("CallConv"); /* skip the prefix */

/* Check for the known base unmanaged calling conventions */
if (!strcmp (name, "Cdecl")) {
base_callconv = MONO_CALL_C;
continue;
} else if (!strcmp (name, "Stdcall")) {
base_callconv = MONO_CALL_STDCALL;
continue;
} else if (!strcmp (name, "Thiscall")) {
base_callconv = MONO_CALL_THISCALL;
continue;
} else if (!strcmp (name, "Fastcall")) {
base_callconv = MONO_CALL_FASTCALL;
continue;
}

/* Check for known calling convention modifiers */
if (!strcmp (name, "SuppressGCTransition")) {
suppress_gc_transition = TRUE;
continue;
}
}
sig->call_convention = base_callconv;
sig->suppress_gc_transition = suppress_gc_transition;
}

/**
* mono_metadata_parse_method_signature_full:
* \param m metadata context
Expand Down Expand Up @@ -2540,6 +2605,14 @@ mono_metadata_parse_method_signature_full (MonoImage *m, MonoGenericContainer *c
return NULL;
}
is_open = mono_class_is_open_constructed_type (method->ret);
if (G_UNLIKELY (method->ret->has_cmods && method->call_convention == MONO_CALL_UNMANAGED_MD)) {
/* calling convention encoded in modopts */
metadata_signature_set_modopt_call_conv (method, method->ret, error);
if (!is_ok (error)) {
g_free (pattrs);
return NULL;
}
}
}

for (i = 0; i < method->param_count; ++i) {
Expand Down
4 changes: 1 addition & 3 deletions mono/mini/method-to-ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -7086,9 +7086,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
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;
gboolean skip_gc_trans = fsig->suppress_gc_transition;
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));
Expand Down

0 comments on commit 6aeac6a

Please sign in to comment.