Skip to content

Commit cc3de40

Browse files
authored
[llvmonly] Optimize delegate invocation. (dotnet#60593)
* [llvmonly] Optimize delegate invocation. * Unify the initialization of delegates in llvmonly mode in mini_llvmonly_init_delegate (). * Pass a MonoDelegateTrampInfo structure to init_delegate (), use it to cache various data. * Invoke delegates similarly to the non-llvmonly case, by making an indirect call through del->invoke_impl, which is initialized in init_delegate (). This also fixes some special cases like bound delegates, since init_delegate () has access to the delegate instance, so it can use the appropriate wrapper. * Compute a cached MonoDelegateTrampInfo for delegates created from the interpreter as well. * Fix interp execution of delegate invokes, the invoke wrapper depends on the delegate instance, so it needs to be created in interp_entry () not in transform_method ().
1 parent 3af0a68 commit cc3de40

File tree

14 files changed

+181
-54
lines changed

14 files changed

+181
-54
lines changed

src/mono/mono/metadata/icall.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6137,7 +6137,7 @@ ves_icall_System_Delegate_AllocDelegateLike_internal (MonoDelegateHandle delegat
61376137
MonoMulticastDelegateHandle ret = MONO_HANDLE_CAST (MonoMulticastDelegate, mono_object_new_handle (klass, error));
61386138
return_val_if_nok (error, MONO_HANDLE_CAST (MonoMulticastDelegate, NULL_HANDLE));
61396139

6140-
MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoDelegate, ret), invoke_impl, gpointer, mono_runtime_create_delegate_trampoline (klass));
6140+
mono_get_runtime_callbacks ()->init_delegate (MONO_HANDLE_CAST (MonoDelegate, ret), NULL_HANDLE, NULL, NULL, error);
61416141

61426142
return ret;
61436143
}

src/mono/mono/metadata/marshal.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2198,6 +2198,29 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
21982198
return res;
21992199
}
22002200

2201+
WrapperSubtype
2202+
mono_marshal_get_delegate_invoke_subtype (MonoMethod *method, MonoDelegate *del)
2203+
{
2204+
MonoMethodSignature *sig;
2205+
2206+
sig = mono_method_signature_internal (method);
2207+
2208+
if (!del || !del->method)
2209+
return WRAPPER_SUBTYPE_NONE;
2210+
2211+
if (!del->target && mono_method_signature_internal (del->method)->hasthis) {
2212+
if (!(del->method->flags & METHOD_ATTRIBUTE_VIRTUAL) && !m_class_is_valuetype (del->method->klass) && sig->param_count == mono_method_signature_internal (del->method)->param_count + 1)
2213+
return WRAPPER_SUBTYPE_NONE;
2214+
else
2215+
return WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL;
2216+
}
2217+
2218+
if (mono_method_signature_internal (del->method)->param_count == sig->param_count + 1 && (del->method->flags & METHOD_ATTRIBUTE_STATIC))
2219+
return WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND;
2220+
2221+
return WRAPPER_SUBTYPE_NONE;
2222+
}
2223+
22012224
/**
22022225
* mono_marshal_get_delegate_invoke:
22032226
* The returned method invokes all methods in a multicast delegate.

src/mono/mono/metadata/marshal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,9 @@ mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del);
428428
MonoMethod *
429429
mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method);
430430

431+
WrapperSubtype
432+
mono_marshal_get_delegate_invoke_subtype (MonoMethod *method, MonoDelegate *del);
433+
431434
MonoMethod *
432435
mono_marshal_get_runtime_invoke_full (MonoMethod *method, gboolean virtual_, gboolean need_direct_wrapper);
433436

src/mono/mono/mini/calls.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,21 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth
703703
helper_sig_llvmonly_imt_trampoline = tmp;
704704
}
705705

706+
if (!cfg->gsharedvt && (m_class_get_parent (cmethod->klass) == mono_defaults.multicastdelegate_class) && !strcmp (cmethod->name, "Invoke")) {
707+
/* Delegate invokes */
708+
MONO_EMIT_NULL_CHECK (cfg, this_reg, FALSE);
709+
710+
/* Make a call to delegate->invoke_impl */
711+
int invoke_reg = alloc_preg (cfg);
712+
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, invoke_reg, this_reg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl));
713+
714+
int addr_reg = alloc_preg (cfg);
715+
int arg_reg = alloc_preg (cfg);
716+
EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, invoke_reg, 0);
717+
MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, invoke_reg, TARGET_SIZEOF_VOID_P);
718+
return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
719+
}
720+
706721
if (!fsig->generic_param_count && !is_iface) {
707722
/*
708723
* The simplest case, a normal virtual call.

src/mono/mono/mini/ee.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#ifndef __MONO_EE_H__
1515
#define __MONO_EE_H__
1616

17-
#define MONO_EE_API_VERSION 0x12
17+
#define MONO_EE_API_VERSION 0x13
1818

1919
typedef struct _MonoInterpStackIter MonoInterpStackIter;
2020

@@ -32,7 +32,7 @@ typedef gpointer MonoInterpFrameHandle;
3232
MONO_EE_CALLBACK (MonoFtnDesc*, create_method_pointer_llvmonly, (MonoMethod *method, gboolean unbox, MonoError *error)) \
3333
MONO_EE_CALLBACK (void, free_method, (MonoMethod *method)) \
3434
MONO_EE_CALLBACK (MonoObject*, runtime_invoke, (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error)) \
35-
MONO_EE_CALLBACK (void, init_delegate, (MonoDelegate *del, MonoError *error)) \
35+
MONO_EE_CALLBACK (void, init_delegate, (MonoDelegate *del, MonoDelegateTrampInfo **out_info, MonoError *error)) \
3636
MONO_EE_CALLBACK (void, delegate_ctor, (MonoObjectHandle this_obj, MonoObjectHandle target, gpointer addr, MonoError *error)) \
3737
MONO_EE_CALLBACK (void, set_resume_state, (MonoJitTlsData *jit_tls, MonoObject *ex, MonoJitExceptionInfo *ei, MonoInterpFrameHandle interp_frame, gpointer handler_ip)) \
3838
MONO_EE_CALLBACK (void, get_resume_state, (const MonoJitTlsData *jit_tls, gboolean *has_resume_state, MonoInterpFrameHandle *interp_frame, gpointer *handler_ip)) \

src/mono/mono/mini/interp-stubs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ stub_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **
155155
}
156156

157157
static void
158-
stub_init_delegate (MonoDelegate *del, MonoError *error)
158+
stub_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo **info, MonoError *error)
159159
{
160160
g_assert_not_reached ();
161161
}

src/mono/mono/mini/interp/interp-internals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ struct InterpMethod {
130130
MonoJitInfo *jinfo;
131131
MonoFtnDesc *ftndesc;
132132
MonoFtnDesc *ftndesc_unbox;
133+
MonoDelegateTrampInfo *del_info;
133134

134135
guint32 locals_size;
135136
guint32 alloca_size;

src/mono/mono/mini/interp/interp.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1619,7 +1619,7 @@ ves_pinvoke_method (
16191619
* Initialize del->interp_method.
16201620
*/
16211621
static void
1622-
interp_init_delegate (MonoDelegate *del, MonoError *error)
1622+
interp_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo **out_info, MonoError *error)
16231623
{
16241624
MonoMethod *method;
16251625

@@ -1669,6 +1669,25 @@ interp_init_delegate (MonoDelegate *del, MonoError *error)
16691669
mono_interp_transform_method ((InterpMethod *) del->interp_method, get_context (), error);
16701670
return_if_nok (error);
16711671
}
1672+
1673+
/*
1674+
* Compute a MonoDelegateTrampInfo for this delegate if possible and pass it back to
1675+
* the caller.
1676+
* Keep a 1 element cache in imethod->del_info. This should be good enough since most methods
1677+
* are only associated with one delegate type.
1678+
*/
1679+
if (out_info)
1680+
*out_info = NULL;
1681+
if (mono_llvm_only) {
1682+
InterpMethod *imethod = del->interp_method;
1683+
method = imethod->method;
1684+
if (imethod->del_info && imethod->del_info->klass == del->object.vtable->klass) {
1685+
*out_info = imethod->del_info;
1686+
} else if (!imethod->del_info) {
1687+
imethod->del_info = mono_create_delegate_trampoline_info (del->object.vtable->klass, method);
1688+
*out_info = imethod->del_info;
1689+
}
1690+
}
16721691
}
16731692

16741693
/* Convert a function pointer for a managed method to an InterpMethod* */
@@ -2007,6 +2026,20 @@ interp_entry (InterpEntryData *data)
20072026
sp_args = sp = (stackval*)context->stack_pointer;
20082027

20092028
method = rmethod->method;
2029+
2030+
if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class && !strcmp (method->name, "Invoke")) {
2031+
/*
2032+
* This happens when AOT code for the invoke wrapper is not found.
2033+
* Have to replace the method with the wrapper here, since the wrapper depends on the delegate.
2034+
*/
2035+
ERROR_DECL (error);
2036+
MonoDelegate *del = (MonoDelegate*)data->this_arg;
2037+
// FIXME: This is slow
2038+
method = mono_marshal_get_delegate_invoke (method, del);
2039+
data->rmethod = mono_interp_get_imethod (method, error);
2040+
mono_error_assert_ok (error);
2041+
}
2042+
20102043
sig = mono_method_signature_internal (method);
20112044

20122045
// FIXME: Optimize this

src/mono/mono/mini/llvmonly-runtime.c

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -651,24 +651,39 @@ mini_llvmonly_init_vtable_slot (MonoVTable *vtable, int slot)
651651
* Similar to mono_delegate_ctor ().
652652
*/
653653
void
654-
mini_llvmonly_init_delegate (MonoDelegate *del)
654+
mini_llvmonly_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo *info)
655655
{
656656
ERROR_DECL (error);
657-
MonoFtnDesc *ftndesc = *(MonoFtnDesc**)del->method_code;
657+
MonoFtnDesc *ftndesc;
658+
659+
if (!info && !del->method) {
660+
// Multicast delegate init
661+
// Have to set the invoke_impl field
662+
// FIXME: Cache
663+
MonoMethod *invoke_impl = mono_marshal_get_delegate_invoke (mono_get_delegate_invoke_internal (del->object.vtable->klass), NULL);
664+
gpointer arg = NULL;
665+
gpointer addr = mini_llvmonly_load_method_delegate (invoke_impl, FALSE, FALSE, &arg, error);
666+
mono_error_assert_ok (error);
667+
del->invoke_impl = mini_llvmonly_create_ftndesc (invoke_impl, addr, arg);
668+
return;
669+
}
670+
671+
if (G_UNLIKELY (!info)) {
672+
g_assert (del->method);
673+
info = mono_create_delegate_trampoline_info (del->object.vtable->klass, del->method);
674+
}
675+
676+
/* Cache the target method address in MonoDelegateTrampInfo */
677+
ftndesc = (MonoFtnDesc*)info->method_ptr;
658678

659-
/*
660-
* We store a MonoFtnDesc in del->method_code.
661-
* It would be better to store an ftndesc in del->method_ptr too,
662-
* but we don't have a a structure which could own its memory.
663-
*/
664679
if (G_UNLIKELY (!ftndesc)) {
665680
MonoMethod *m = del->method;
666681
gboolean need_unbox = FALSE;
667682

668683
if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
669684
m = mono_marshal_get_synchronized_wrapper (m);
670685

671-
if (m_class_is_valuetype (m->klass) && mono_method_signature_internal (m)->hasthis)
686+
if (m_class_is_valuetype (m->klass) && mono_method_signature_internal (m)->hasthis && !(info->invoke_sig->param_count > info->sig->param_count))
672687
need_unbox = TRUE;
673688

674689
gpointer arg = NULL;
@@ -677,33 +692,40 @@ mini_llvmonly_init_delegate (MonoDelegate *del)
677692
return;
678693
ftndesc = mini_llvmonly_create_ftndesc (m, addr, arg);
679694
mono_memory_barrier ();
680-
*del->method_code = (guint8*)ftndesc;
695+
//*del->method_code = (guint8*)ftndesc;
696+
info->method_ptr = ftndesc;
681697
}
682698
del->method_ptr = ftndesc->addr;
683699
del->extra_arg = ftndesc->arg;
700+
701+
WrapperSubtype subtype = mono_marshal_get_delegate_invoke_subtype (info->invoke, del);
702+
703+
ftndesc = info->invoke_impl;
704+
if (G_UNLIKELY (!ftndesc) || subtype != WRAPPER_SUBTYPE_NONE) {
705+
ERROR_DECL (error);
706+
MonoMethod *invoke_impl = mono_marshal_get_delegate_invoke (info->invoke, del);
707+
gpointer arg = NULL;
708+
gpointer addr = mini_llvmonly_load_method_delegate (invoke_impl, FALSE, FALSE, &arg, error);
709+
mono_error_assert_ok (error);
710+
ftndesc = mini_llvmonly_create_ftndesc (invoke_impl, addr, arg);
711+
if (subtype == WRAPPER_SUBTYPE_NONE) {
712+
mono_memory_barrier ();
713+
info->invoke_impl = ftndesc;
714+
} else {
715+
// FIXME: Cache
716+
}
717+
}
718+
del->invoke_impl = ftndesc;
684719
}
685720

686721
void
687722
mini_llvmonly_init_delegate_virtual (MonoDelegate *del, MonoObject *target, MonoMethod *method)
688723
{
689-
ERROR_DECL (error);
690-
gpointer addr, arg;
691-
gboolean need_unbox;
692-
693724
g_assert (target);
694725

695-
method = mono_object_get_virtual_method_internal (target, method);
696-
697-
if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
698-
method = mono_marshal_get_synchronized_wrapper (method);
699-
need_unbox = m_class_is_valuetype (method->klass);
726+
del->method = mono_object_get_virtual_method_internal (target, method);
700727

701-
del->method = method;
702-
addr = mini_llvmonly_load_method_delegate (method, FALSE, need_unbox, &arg, error);
703-
if (mono_error_set_pending_exception (error))
704-
return;
705-
del->method_ptr = addr;
706-
del->extra_arg = arg;
728+
mini_llvmonly_init_delegate (del, NULL);
707729
}
708730

709731
/*

src/mono/mono/mini/llvmonly-runtime.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ G_EXTERN_C gpointer mini_llvmonly_resolve_vcall_gsharedvt (MonoObject *this_obj,
2525
G_EXTERN_C gpointer mini_llvmonly_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg);
2626
G_EXTERN_C MonoFtnDesc* mini_llvmonly_resolve_generic_virtual_call (MonoVTable *vt, int slot, MonoMethod *imt_method);
2727
G_EXTERN_C MonoFtnDesc* mini_llvmonly_resolve_generic_virtual_iface_call (MonoVTable *vt, int imt_slot, MonoMethod *imt_method);
28-
G_EXTERN_C void mini_llvmonly_init_delegate (MonoDelegate *del);
28+
G_EXTERN_C void mini_llvmonly_init_delegate (MonoDelegate *del, MonoDelegateTrampInfo *info);
2929
G_EXTERN_C void mini_llvmonly_init_delegate_virtual (MonoDelegate *del, MonoObject *target, MonoMethod *method);
3030

3131
/* Used for regular llvm as well */

0 commit comments

Comments
 (0)