-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[mono] Add runtime support for Swift calling convention on x64 and arm64 #94764
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
Changes from all commits
5136aaf
674a156
9fc9402
d279a03
de27030
476a605
b1b69d3
a95aca8
6c8e477
b06c840
7810e56
6d9d67f
87adeed
0716fda
ab53067
675619f
24a8a01
11d60cc
e8169f0
8584918
279a594
acc15a0
5ea8755
7a69077
ebe4116
0c40694
71d33f4
7bcf61f
7d9f73d
5973ff5
ffc173a
907c69a
42680b1
2cce70f
a702f0d
5bc2693
78b974a
a4ab558
ece53c2
a862485
6a5dda2
e2b6fe9
b3cb4cb
68ad76d
1efac9d
74dc8c9
fd1b036
458c1e4
2939996
8298058
5447ccd
7e04140
64157c5
6b3a52f
6077b22
af416eb
cc9c619
89f46b8
20eb7a6
ae08445
5a225b5
ac7f565
b945d4d
18fcbae
4c6c0c1
0587a55
56bcb1b
a5afdbe
2faea55
05a8c1c
b67c3c6
4dad320
c088a57
91e6955
9327d1e
5df0523
fc9178e
738d14d
dccf89e
9a8cc67
8c5cf81
6ee1c1e
5c5daad
f412721
8c53ad6
9482a79
883d645
4f788fc
1e19be8
7b689a2
152fa6e
c732e25
40f6e35
ab3e840
c29a35f
8663c3c
3ed6733
cb20690
da5279b
a32421a
3b1b4dd
37230be
c25d435
a57307f
1fe9611
e256556
e9ce7d9
91a3263
5e9f6fc
97a74cc
5384dce
7103cfc
ed2a7dc
8e18dc3
7961e29
5e1afb5
d9da03d
061934d
eb3103e
672ac92
dfc5de6
0984ffc
eaf9aa2
fe19a09
f93e231
2d5b9f5
60749f4
fa085d9
bc19659
5309a58
735b84e
f444eae
fe6e270
39b8bd0
eccc6a3
a6f3369
0b3920e
0e4a48c
2b3d6ff
f7528f8
2a1b7c2
cdaff6c
1af3d21
d243462
6f36c21
a60f392
bd11b7b
2b2b75e
6e54b5b
426802e
4e09785
89d211c
d450fb1
1a39a88
c2e354f
1405a98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,9 @@ 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") | ||
static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callconv_attribute, "System.Runtime.InteropServices", "UnmanagedCallConvAttribute") | ||
|
||
GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_error, "System.Runtime.InteropServices.Swift", "SwiftError") | ||
GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_self, "System.Runtime.InteropServices.Swift", "SwiftSelf") | ||
|
||
static gboolean type_is_blittable (MonoType *type); | ||
|
||
static IlgenCallbacksToMono ilgenCallbacksToMono = { | ||
|
@@ -3232,6 +3235,8 @@ mono_marshal_set_callconv_for_type(MonoType *type, MonoMethodSignature *csig, gb | |
csig->call_convention = MONO_CALL_FASTCALL; | ||
else if (!strcmp (class_name, "CallConvThiscall")) | ||
csig->call_convention = MONO_CALL_THISCALL; | ||
else if (!strcmp (class_name, "CallConvSwift")) | ||
csig->ext_callconv |= MONO_EXT_CALLCONV_SWIFTCALL; | ||
else if (!strcmp (class_name, "CallConvSuppressGCTransition") && skip_gc_trans != NULL) | ||
*skip_gc_trans = TRUE; | ||
} | ||
|
@@ -3358,8 +3363,10 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon | |
sig->call_convention = MONO_CALL_THISCALL; | ||
else if (!strcmp (name, "Fastcall")) | ||
sig->call_convention = MONO_CALL_FASTCALL; | ||
else if (!strcmp (name, "Swift")) | ||
sig->ext_callconv |= MONO_EXT_CALLCONV_SWIFTCALL; | ||
else if (!strcmp (name, "SuppressGCTransition")) | ||
sig->suppress_gc_transition = 1; | ||
sig->ext_callconv |= MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION; | ||
// TODO: Support CallConvMemberFunction? | ||
// TODO: Support multiple calling convetions? | ||
// - Switch MonoCallConvention enum values to powers of 2 | ||
|
@@ -3427,23 +3434,31 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, | |
MonoMethodSignature *sig, *csig; | ||
MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method; | ||
MonoMethodBuilder *mb; | ||
MonoMarshalSpec **mspecs; | ||
MonoMarshalSpec **mspecs = NULL; | ||
MonoMethod *res; | ||
GHashTable *cache; | ||
gboolean pinvoke = FALSE; | ||
gboolean skip_gc_trans = FALSE; | ||
gboolean pinvoke_not_found = FALSE; | ||
gpointer iter; | ||
ERROR_DECL (emitted_error); | ||
WrapperInfo *info; | ||
MonoType *string_type; | ||
GHashTable **cache_ptr; | ||
ERROR_DECL (emitted_error); | ||
|
||
g_assert (method != NULL); | ||
g_assertf (mono_method_signature_internal (method)->pinvoke, "%s flags:%X iflags:%X param_count:%X", | ||
method->name, method->flags, method->iflags, mono_method_signature_internal (method)->param_count); | ||
|
||
GHashTable **cache_ptr; | ||
|
||
MonoType *string_type = m_class_get_byval_arg (mono_defaults.string_class); | ||
if (MONO_CLASS_IS_IMPORT (method->klass)) { | ||
/* The COM code is not AOT compatible, it calls mono_custom_attrs_get_attr_checked () */ | ||
if (aot) | ||
return method; | ||
#ifndef DISABLE_COM | ||
return mono_cominterop_get_native_wrapper (method); | ||
#else | ||
g_assert_not_reached (); | ||
#endif | ||
} | ||
|
||
if (aot) { | ||
if (check_exceptions) | ||
|
@@ -3470,17 +3485,6 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, | |
} | ||
} | ||
|
||
if (MONO_CLASS_IS_IMPORT (method->klass)) { | ||
/* The COM code is not AOT compatible, it calls mono_custom_attrs_get_attr_checked () */ | ||
if (aot) | ||
return method; | ||
#ifndef DISABLE_COM | ||
return mono_cominterop_get_native_wrapper (method); | ||
#else | ||
g_assert_not_reached (); | ||
#endif | ||
} | ||
|
||
sig = mono_method_signature_internal (method); | ||
|
||
if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && | ||
|
@@ -3493,12 +3497,30 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, | |
mono_error_set_generic_error (emitted_error, "System", "MissingMethodException", "Method contains unsupported native code"); | ||
else if (!aot) | ||
mono_lookup_pinvoke_call_internal (method, emitted_error); | ||
} else { | ||
if (!aot || (method->klass == mono_defaults.string_class)) | ||
piinfo->addr = mono_lookup_internal_call (method); | ||
} else if (!aot || (method->klass == mono_defaults.string_class)) { | ||
piinfo->addr = mono_lookup_internal_call (method); | ||
} | ||
} | ||
|
||
mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE); | ||
mb->method->save_lmf = 1; | ||
|
||
if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) { | ||
/* | ||
* In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation. | ||
* Emit a wrapper that throws a NotSupportedException. | ||
*/ | ||
get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute"); | ||
goto emit_exception_for_error; | ||
} else if (!pinvoke && !piinfo->addr && !aot) { | ||
/* if there's no code but the error isn't set, just use a fairly generic exception. */ | ||
if (is_ok (emitted_error)) | ||
mono_error_set_generic_error (emitted_error, "System", "MissingMethodException", ""); | ||
get_marshal_cb ()->mb_emit_exception_for_error (mb, emitted_error); | ||
goto emit_exception_for_error; | ||
} | ||
|
||
string_type = m_class_get_byval_arg (mono_defaults.string_class); | ||
/* hack - redirect certain string constructors to CreateString */ | ||
if (piinfo->addr == ves_icall_System_String_ctor_RedirectToCreateString) { | ||
MonoMethod *m; | ||
|
@@ -3555,133 +3577,130 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, | |
return res; | ||
} | ||
|
||
mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE); | ||
|
||
mb->method->save_lmf = 1; | ||
|
||
if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) { | ||
/* emit a wrapper that throws a NotSupportedException */ | ||
get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute"); | ||
|
||
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); | ||
info->d.managed_to_native.method = method; | ||
|
||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
csig->pinvoke = 0; | ||
res = mono_mb_create_and_cache_full (cache, method, mb, csig, | ||
csig->param_count + 16, info, NULL); | ||
mono_mb_free (mb); | ||
|
||
return res; | ||
} | ||
|
||
/* | ||
* In AOT mode and embedding scenarios, it is possible that the icall is not | ||
* registered in the runtime doing the AOT compilation. | ||
*/ | ||
/* Handled at runtime */ | ||
pinvoke_not_found = !pinvoke && !piinfo->addr && !aot; | ||
if (pinvoke_not_found) { | ||
/* if there's no code but the error isn't set, just use a fairly generic exception. */ | ||
if (is_ok (emitted_error)) | ||
mono_error_set_generic_error (emitted_error, "System", "MissingMethodException", ""); | ||
get_marshal_cb ()->mb_emit_exception_for_error (mb, emitted_error); | ||
mono_error_cleanup (emitted_error); | ||
|
||
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); | ||
info->d.managed_to_native.method = method; | ||
|
||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
csig->pinvoke = 0; | ||
res = mono_mb_create_and_cache_full (cache, method, mb, csig, | ||
csig->param_count + 16, info, NULL); | ||
mono_mb_free (mb); | ||
|
||
return res; | ||
} | ||
|
||
/* internal calls: we simply push all arguments and call the method (no conversions) */ | ||
if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) { | ||
if (sig->hasthis) | ||
csig = mono_metadata_signature_dup_add_this (get_method_image (method), sig, method->klass); | ||
else | ||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
|
||
//printf ("%s\n", mono_method_full_name (method, 1)); | ||
|
||
/* hack - string constructors returns a value */ | ||
if (method->string_ctor) | ||
csig->ret = string_type; | ||
|
||
get_marshal_cb ()->emit_native_icall_wrapper (mb, method, csig, check_exceptions, aot, piinfo); | ||
} else { | ||
g_assert(pinvoke); | ||
|
||
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); | ||
info->d.managed_to_native.method = method; | ||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
mono_marshal_set_callconv_from_modopt (method, csig, FALSE); | ||
|
||
csig = mono_metadata_signature_dup_full (get_method_image (method), csig); | ||
csig->pinvoke = 0; | ||
res = mono_mb_create_and_cache_full (cache, method, mb, csig, csig->param_count + 16, | ||
info, NULL); | ||
mspecs = g_new0 (MonoMarshalSpec*, sig->param_count + 1); | ||
mono_method_get_marshal_info (method, mspecs); | ||
|
||
mono_mb_free (mb); | ||
return res; | ||
} | ||
if (mono_class_try_get_suppress_gc_transition_attribute_class ()) { | ||
MonoCustomAttrInfo *cinfo; | ||
ERROR_DECL (error); | ||
|
||
g_assert (pinvoke); | ||
cinfo = mono_custom_attrs_from_method_checked (method, error); | ||
mono_error_assert_ok (error); | ||
gboolean found = FALSE; | ||
if (cinfo) { | ||
for (int i = 0; i < cinfo->num_attrs; ++i) { | ||
MonoClass *ctor_class = cinfo->attrs [i].ctor->klass; | ||
if (ctor_class == mono_class_try_get_suppress_gc_transition_attribute_class ()) { | ||
found = TRUE; | ||
break; | ||
} | ||
} | ||
} | ||
if (found) | ||
skip_gc_trans = TRUE; | ||
if (cinfo && !cinfo->cached) | ||
mono_custom_attrs_free (cinfo); | ||
} | ||
|
||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
mono_marshal_set_callconv_from_modopt (method, csig, FALSE); | ||
if (csig->call_convention == MONO_CALL_DEFAULT) { | ||
/* If the calling convention has not been set, check the UnmanagedCallConv attribute */ | ||
mono_marshal_set_callconv_from_unmanaged_callconv_attribute (method, csig, &skip_gc_trans); | ||
} | ||
|
||
mspecs = g_new0 (MonoMarshalSpec*, sig->param_count + 1); | ||
mono_method_get_marshal_info (method, mspecs); | ||
if (mono_method_signature_has_ext_callconv (csig, MONO_EXT_CALLCONV_SWIFTCALL)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kotlarmilos @vargaz Do we need to prevent these m2n swiftcall wrappers from being inlined? I think there's logic in the mini-arm64 and mini-amd64 backends that depends on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the inlining performed before or after the lowering process? Do the method prolog and epilog get inlined as well? ArgSwiftError is handled differently in the method prolog, which could be affected when it is inlined. Also, there is a potential problem related to context registers clobbering when wrappers are inlined. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These wrappers are not inlined right now. |
||
MonoClass *swift_self = mono_class_try_get_swift_self_class (); | ||
MonoClass *swift_error = mono_class_try_get_swift_error_class (); | ||
MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error)); | ||
int swift_error_args = 0, swift_self_args = 0; | ||
for (int i = 0; i < method->signature->param_count; ++i) { | ||
MonoClass *param_klass = mono_class_from_mono_type_internal (method->signature->params [i]); | ||
if (param_klass) { | ||
if (param_klass == swift_error && !m_type_is_byref (method->signature->params [i])) { | ||
swift_error_args = swift_self_args = 0; | ||
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "SwiftError argument must be passed by reference."); | ||
break; | ||
} else if (param_klass == swift_error || param_klass == swift_error_ptr) { | ||
swift_error_args++; | ||
} else if (param_klass == swift_self) { | ||
swift_self_args++; | ||
} else if (!m_class_is_blittable (param_klass) && m_class_get_this_arg (param_klass)->type != MONO_TYPE_FNPTR) { | ||
swift_error_args = swift_self_args = 0; | ||
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-primitive value types to a P/Invoke with the Swift calling convention is unsupported."); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
if (mono_class_try_get_suppress_gc_transition_attribute_class ()) { | ||
MonoCustomAttrInfo *cinfo; | ||
ERROR_DECL (error); | ||
if (swift_self_args > 1 || swift_error_args > 1) { | ||
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Method signature contains multiple SwiftSelf or SwiftError arguments."); | ||
} | ||
|
||
cinfo = mono_custom_attrs_from_method_checked (method, error); | ||
mono_error_assert_ok (error); | ||
gboolean found = FALSE; | ||
if (cinfo) { | ||
for (int i = 0; i < cinfo->num_attrs; ++i) { | ||
MonoClass *ctor_class = cinfo->attrs [i].ctor->klass; | ||
if (ctor_class == mono_class_try_get_suppress_gc_transition_attribute_class ()) { | ||
found = TRUE; | ||
break; | ||
} | ||
if (!is_ok (emitted_error)) { | ||
get_marshal_cb ()->mb_emit_exception_for_error (mb, emitted_error); | ||
goto emit_exception_for_error; | ||
} | ||
} | ||
if (found) | ||
skip_gc_trans = TRUE; | ||
if (cinfo && !cinfo->cached) | ||
mono_custom_attrs_free (cinfo); | ||
} | ||
|
||
if (csig->call_convention == MONO_CALL_DEFAULT) { | ||
/* If the calling convention has not been set, check the UnmanagedCallConv attribute */ | ||
mono_marshal_set_callconv_from_unmanaged_callconv_attribute (method, csig, &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; | ||
flags |= runtime_marshalling_enabled (get_method_image (method)) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; | ||
|
||
mono_marshal_emit_native_wrapper (get_method_image (mb->method), mb, csig, piinfo, mspecs, piinfo->addr, flags); | ||
} | ||
|
||
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; | ||
flags |= runtime_marshalling_enabled (get_method_image (method)) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; | ||
info = mono_wrapper_info_create (mb, method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME) | ||
? WRAPPER_SUBTYPE_NONE | ||
: WRAPPER_SUBTYPE_PINVOKE); | ||
|
||
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; | ||
|
||
if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) | ||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
csig->pinvoke = 0; | ||
res = mono_mb_create_and_cache_full (cache, method, mb, csig, csig->param_count + 16, | ||
info, NULL); | ||
mono_mb_free (mb); | ||
|
||
for (int i = sig->param_count; i >= 0; i--) | ||
if (mspecs [i]) | ||
mono_metadata_free_marshal_spec (mspecs [i]); | ||
g_free (mspecs); | ||
if (mspecs) { | ||
for (int i = sig->param_count; i >= 0; i--) | ||
if (mspecs [i]) | ||
mono_metadata_free_marshal_spec (mspecs [i]); | ||
g_free (mspecs); | ||
} | ||
|
||
return res; | ||
goto leave; | ||
|
||
emit_exception_for_error: | ||
mono_error_cleanup (emitted_error); | ||
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); | ||
info->d.managed_to_native.method = method; | ||
|
||
csig = mono_metadata_signature_dup_full (get_method_image (method), sig); | ||
csig->pinvoke = 0; | ||
res = mono_mb_create_and_cache_full (cache, method, mb, csig, | ||
csig->param_count + 16, info, NULL); | ||
|
||
leave: | ||
mono_mb_free (mb); | ||
return res; | ||
} | ||
|
||
/** | ||
|
Uh oh!
There was an error while loading. Please reload this page.