Skip to content

Commit

Permalink
Mono: support CallConvSuppressGCTransition (#47006)
Browse files Browse the repository at this point in the history
* [metadata] Parse modopt encoded calling conventions

Add suppress_gc_transition bit to MonoMethodSignature to add support for
the SuppressGCTransition unmanaged calling convention modifier.

* [mini] 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).

* [marshal] 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.

* [tests] Remove SuppressGCTransitionTest from exclude list

Fixes #46451
  • Loading branch information
lambdageek authored Jan 15, 2021
1 parent 9ba916a commit a3b37a2
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 7 deletions.
8 changes: 7 additions & 1 deletion src/mono/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 src/mono/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 src/mono/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 src/mono/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
3 changes: 0 additions & 3 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2459,9 +2459,6 @@
<ExcludeList Include = "$(XunitTestBinBase)JIT/IL_Conformance/Old/Base/ckfinite/**">
<Issue>needs triage</Issue>
</ExcludeList>
<ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Attributes/SuppressGCTransition/SuppressGCTransitionTest/**">
<Issue>https://github.com/dotnet/runtime/issues/46451</Issue>
</ExcludeList>
<!-- End interpreter issues -->
</ItemGroup>

Expand Down

0 comments on commit a3b37a2

Please sign in to comment.