Skip to content

Commit

Permalink
[mono][aot] Enabling AOT wrappers for virtual delegates (#85643)
Browse files Browse the repository at this point in the history
* Adding AOT virtual delegates wrappers

Signed-off-by: Vlad - Alexandru Ionescu <[email protected]>

---------

Signed-off-by: Vlad - Alexandru Ionescu <[email protected]>
Co-authored-by: Vlad - Alexandru Ionescu <[email protected]>
  • Loading branch information
LeVladIonescu and Vlad - Alexandru Ionescu authored May 22, 2023
1 parent 1f15489 commit e91db04
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 41 deletions.
3 changes: 2 additions & 1 deletion src/mono/mono/metadata/loader-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ struct _MonoDllMap {
#endif

typedef struct {
GHashTable *delegate_invoke_cache;
GHashTable *delegate_invoke_virtual_cache;
/*
* indexed by MonoMethodSignature
* Protected by the marshal lock
*/
GHashTable *delegate_invoke_cache;
GHashTable *delegate_begin_invoke_cache;
GHashTable *delegate_end_invoke_cache;
GHashTable *runtime_invoke_signature_cache;
Expand Down
14 changes: 11 additions & 3 deletions src/mono/mono/metadata/marshal-lightweight.c
Original file line number Diff line number Diff line change
Expand Up @@ -1989,8 +1989,10 @@ emit_delegate_end_invoke_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
mono_mb_emit_restore_result (mb, sig->ret);
}

#define MONO_TYPE_IS_PRIMITIVE(t) ((!m_type_is_byref ((t)) && ((((t)->type >= MONO_TYPE_BOOLEAN && (t)->type <= MONO_TYPE_R8) || ((t)->type >= MONO_TYPE_I && (t)->type <= MONO_TYPE_U)))))

static void
emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container)
emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoGenericContext *ctx, MonoGenericContainer *container)
{
int local_i, local_len, local_delegates, local_d, local_target, local_res = 0;
int pos0, pos1, pos2;
Expand Down Expand Up @@ -2088,9 +2090,15 @@ emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature

if (callvirt) {
if (!closed_over_null) {
for (i = 1; i <= sig->param_count; ++i)
for (i = 1; i <= sig->param_count; ++i) {
mono_mb_emit_ldarg (mb, i);
mono_mb_emit_ldarg (mb, 1);
if (i == 1 && (MONO_TYPE_ISSTRUCT (sig->params [0]) || MONO_TYPE_IS_PRIMITIVE (sig->params [0])))
mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (sig->params [0]));
}
if (MONO_TYPE_ISSTRUCT (sig->params [0]) || MONO_TYPE_IS_PRIMITIVE (sig->params [0]))
mono_mb_emit_ldarg_addr (mb, 1);
else
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_icall (mb, mono_get_addr_compiled_method);
mono_mb_emit_op (mb, CEE_CALLI, target_method_sig);
Expand Down
42 changes: 17 additions & 25 deletions src/mono/mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2092,7 +2092,6 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
GHashTable *cache;
gpointer cache_key = NULL;
char *name;
MonoClass *target_class = NULL;
gboolean closed_over_null = FALSE;
MonoGenericContext *ctx = NULL;
MonoGenericContainer *container = NULL;
Expand All @@ -2111,28 +2110,8 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
* call is made to that method with the first delegate argument as this. This is
* a non-documented .NET feature.
*/
if (callvirt) {
if (callvirt)
subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL;
if (target_method->is_inflated) {
ERROR_DECL (error);
MonoType *target_type;

g_assert (method->signature->hasthis);
target_type = mono_class_inflate_generic_type_checked (method->signature->params [0],
mono_method_get_context (method), error);
mono_error_assert_ok (error); /* FIXME don't swallow the error */
target_class = mono_class_from_mono_type_internal (target_type);
} else {
target_class = target_method->klass;
}

closed_over_null = sig->param_count == mono_method_signature_internal (target_method)->param_count;

/*
* We don't want to use target_method's signature because it can be freed early
*/
target_method_sig = mono_method_signature_internal (target_method);
}

if (static_method_with_first_arg_bound) {
subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND;
Expand All @@ -2150,7 +2129,7 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
/*
* For generic delegates, create a generic wrapper, and return an instance to help AOT.
*/
if (method->is_inflated && subtype == WRAPPER_SUBTYPE_NONE) {
if (method->is_inflated && (subtype == WRAPPER_SUBTYPE_NONE || subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL)) {
ctx = &((MonoMethodInflated*)method)->context;
method = ((MonoMethodInflated*)method)->declaring;

Expand All @@ -2162,11 +2141,23 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
invoke_sig = sig = mono_signature_no_pinvoke (method);
}

if (subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL) {
/*
* We don't want to use target_method's signature because it can be freed early
*/
target_method_sig = mono_metadata_signature_dup_delegate_invoke_to_target (invoke_sig);

closed_over_null = sig->param_count == target_method_sig->param_count;
}

/*
* Check cache
*/
if (ctx) {
cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_cache, mono_aligned_addr_hash, NULL);
if (callvirt)
cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_virtual_cache, mono_aligned_addr_hash, NULL);
else
cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_cache, mono_aligned_addr_hash, NULL);
res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
if (res)
return res;
Expand Down Expand Up @@ -2241,7 +2232,7 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
/* FIXME: Other subtypes */
mb->mem_manager = m_method_get_mem_manager (method);

get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, target_method_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, target_class, ctx, container);
get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, target_method_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, ctx, container);

get_marshal_cb ()->mb_skip_visibility (mb);

Expand Down Expand Up @@ -6384,6 +6375,7 @@ void
mono_wrapper_caches_free (MonoWrapperCaches *cache)
{
free_hash (cache->delegate_invoke_cache);
free_hash (cache->delegate_invoke_virtual_cache);
free_hash (cache->delegate_begin_invoke_cache);
free_hash (cache->delegate_end_invoke_cache);
free_hash (cache->delegate_bound_static_invoke_cache);
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/marshal.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ typedef struct {
void (*emit_runtime_invoke_dynamic) (MonoMethodBuilder *mb);
void (*emit_delegate_begin_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig);
void (*emit_delegate_end_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig);
void (*emit_delegate_invoke_internal) (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container);
void (*emit_delegate_invoke_internal) (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoGenericContext *ctx, MonoGenericContainer *container);
void (*emit_synchronized_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoGenericContext *ctx, MonoGenericContainer *container, MonoMethod *enter_method, MonoMethod *exit_method, MonoMethod *gettypefromhandle_method);
void (*emit_unbox_wrapper) (MonoMethodBuilder *mb, MonoMethod *method);
void (*emit_array_accessor_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *sig, MonoGenericContext *ctx);
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 @@ -991,6 +991,7 @@ MonoMethodSignature *mono_metadata_signature_dup_full (MonoImage *image,MonoMet
MonoMethodSignature *mono_metadata_signature_dup_mempool (MonoMemPool *mp, MonoMethodSignature *sig);
MonoMethodSignature *mono_metadata_signature_dup_mem_manager (MonoMemoryManager *mem_manager, MonoMethodSignature *sig);
MonoMethodSignature *mono_metadata_signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass);
MonoMethodSignature *mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig);

MonoGenericInst *
mono_get_shared_generic_inst (MonoGenericContainer *container);
Expand Down
23 changes: 23 additions & 0 deletions src/mono/mono/metadata/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -2512,6 +2512,29 @@ mono_metadata_signature_dup (MonoMethodSignature *sig)
return mono_metadata_signature_dup_full (NULL, sig);
}

/**
* mono_metadata_signature_dup_delegate_invoke_to_target:
* \param sig method signature
*
* Duplicate an existing \c MonoMethodSignature but removes first param from it so it can
* be used as signature for a delegate target method.
* This is a Mono runtime internal function.
*
* \returns the new \c MonoMethodSignature structure.
*/
MonoMethodSignature*
mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig)
{
MonoMethodSignature *res = mono_metadata_signature_dup_full (NULL, sig);

for (int i = 0 ; i < sig->param_count - 1; i ++) {
res->params [i] = sig->params [i + 1];
}
res->param_count --;

return res;
}

/*
* mono_metadata_signature_size:
*
Expand Down
72 changes: 68 additions & 4 deletions src/mono/mono/mini/aot-compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -3922,12 +3922,15 @@ encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8
encode_klass_ref (acfg, method->klass, p, &p);
} else {
MonoMethodSignature *sig = mono_method_signature_internal (method);
WrapperInfo *wrapper_info = mono_marshal_get_wrapper_info (method);

encode_value (0, p, &p);
if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE)
encode_value (wrapper_info ? wrapper_info->subtype : 0, p, &p);
encode_signature (acfg, sig, p, &p);
encode_value (info ? info->subtype : 0, p, &p);

if (info && info->subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL)
encode_klass_ref (acfg, info->d.delegate_invoke.method->klass, p, &p);
else
encode_signature (acfg, sig, p, &p);
}
break;
}
Expand Down Expand Up @@ -4546,6 +4549,41 @@ can_marshal_struct (MonoClass *klass)
return can_marshal;
}

/* Create a ref shared instantiation */
static void
create_ref_shared_inst (MonoAotCompile *acfg, MonoMethod *method, MonoGenericContext *ctx)
{
MonoGenericContext shared_context;
MonoType **args;
MonoGenericInst *inst;
MonoGenericContainer *container;

memset (ctx, 0, sizeof (MonoGenericContext));

if (mono_class_is_gtd (method->klass)) {
shared_context = mono_class_get_generic_container (method->klass)->context;
inst = shared_context.class_inst;

args = g_new0 (MonoType*, inst->type_argc);
for (guint i = 0; i < inst->type_argc; ++i)
args [i] = mono_get_object_type ();
ctx->class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
g_free (args);
}
if (method->is_generic) {
container = mono_method_get_generic_container (method);
g_assert (!container->is_anonymous && container->is_method);
shared_context = container->context;
inst = shared_context.method_inst;

args = g_new0 (MonoType*, inst->type_argc);
for (int i = 0; i < container->type_argc; ++i)
args [i] = mono_get_object_type ();
ctx->method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
g_free (args);
}
}

static void
create_gsharedvt_inst (MonoAotCompile *acfg, MonoMethod *method, MonoGenericContext *ctx)
{
Expand Down Expand Up @@ -4962,13 +5000,39 @@ add_full_aot_wrappers (MonoAotCompile *acfg)
if (!m_class_is_delegate (klass) || klass == mono_defaults.delegate_class || klass == mono_defaults.multicastdelegate_class)
continue;

method = mono_get_delegate_invoke_internal (klass);
if (mono_class_is_gtd (klass)) {
MonoGenericContext ctx;
MonoMethod *inst, *gshared;

create_ref_shared_inst (acfg, method, &ctx);

inst = mono_class_inflate_generic_method_checked (method, &ctx, error);
g_assert (is_ok (error)); /* FIXME don't swallow the error */

sig = mono_method_signature_internal (method);
if (sig->param_count && !m_class_is_byreflike (mono_class_from_mono_type_internal (sig->params [0])) && !m_type_is_byref (sig->params [0])) {
m = mono_marshal_get_delegate_invoke_internal (inst, TRUE, FALSE, NULL);

gshared = mini_get_shared_method_full (m, SHARE_MODE_NONE, error);
mono_error_assert_ok (error);

add_extra_method (acfg, gshared);
}
}

if (!mono_class_is_gtd (klass)) {
method = mono_get_delegate_invoke_internal (klass);

m = mono_marshal_get_delegate_invoke (method, NULL);

add_method (acfg, m);

sig = mono_method_signature_internal (method);
if (sig->param_count && !m_class_is_byreflike (mono_class_from_mono_type_internal (sig->params [0])) && !m_type_is_byref (sig->params [0])) {
m = mono_marshal_get_delegate_invoke_internal (method, TRUE, FALSE, NULL);
add_method (acfg, m);
}

method = try_get_method_nofail (klass, "BeginInvoke", -1, 0);
if (method)
add_method (acfg, mono_marshal_get_delegate_begin_invoke (method));
Expand Down
24 changes: 17 additions & 7 deletions src/mono/mono/mini/aot-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1269,15 +1269,25 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
ref->method = wrapper;
}
} else {
/*
* These wrappers are associated with a signature, not with a method.
* Since we can't decode them into methods, they need a target method.
*/
if (!target)
return FALSE;
MonoClass *klass;
MonoMethod *invoke;


if (wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) {
subtype = (WrapperSubtype)decode_value (p, &p);
if (subtype == WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL) {
klass = decode_klass_ref (module, p, &p, error);
invoke = mono_get_delegate_invoke_internal (klass);
ref->method = mono_marshal_get_delegate_invoke_internal(invoke, TRUE, FALSE, NULL);
break;
}

/*
* These wrappers are associated with a signature, not with a method.
* Since we can't decode them into methods, they need a target method.
*/
if (!target)
return FALSE;
info = mono_marshal_get_wrapper_info (target);
if (info) {
if (info->subtype != subtype)
Expand All @@ -1287,7 +1297,7 @@ decode_method_ref_with_target (MonoAotModule *module, MethodRef *ref, MonoMethod
return FALSE;
}
}
if (sig_matches_target (module, target, p, &p))
if (target && sig_matches_target (module, target, p, &p))
ref->method = target;
else
return FALSE;
Expand Down

0 comments on commit e91db04

Please sign in to comment.