With this patch, we see a 3x execution speed improvement in the following micro-benchmark :
using System;
class Foo {
public int Test () { return 10; }
}
class Program {
public static int Main (String[] args) {
long n =
50000000;
if (args.Length > 0)
n = Math.Max (n, Int64.Parse (args [0]));
var a = new Foo ();
for (long i = 0; i < n; i++) {
var f = new Func<int> (a.Test);
if (f () != 10)
return 1;
}
return 0;
}
}
DECL_OFFSET(MonoLMFTramp, lmf_addr)
#endif
+
+DECL_OFFSET(MonoDelegateTrampInfo, invoke_impl)
+DECL_OFFSET(MonoDelegateTrampInfo, method_ptr)
#endif
{
MonoInst *ptr;
int dreg;
- gpointer *trampoline;
+ MonoDelegateTrampInfo *trampoline;
MonoInst *obj, *method_ins, *tramp_ins;
MonoDomain *domain;
guint8 **code_slot;
del_tramp->method = context_used ? NULL : method;
EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
} else {
- trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method)->invoke_impl;
+ trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
}
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
+
+ dreg = alloc_preg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
+
+ dreg = alloc_preg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
/* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
delegate->method_ptr = mono_create_static_rgctx_trampoline (method, delegate->method_ptr);
}
+ /* Necessary for !code condition to fallback to slow path */
+ code = NULL;
+
multicast = ((MonoMulticastDelegate*)delegate)->prev != NULL;
if (!multicast && !callvirt) {
if (method && (method->flags & METHOD_ATTRIBUTE_STATIC) && mono_method_signature (method)->param_count == mono_method_signature (invoke)->param_count + 1)
code = impl_this;
else
code = delegate->target ? impl_this : impl_nothis;
+ }
- if (code) {
- delegate->invoke_impl = mono_get_addr_from_ftnptr (code);
- return code;
- }
+ if (!code) {
+ /* The general, unoptimized case */
+ m = mono_marshal_get_delegate_invoke (invoke, delegate);
+ code = mono_compile_method (m);
+ code = mini_add_method_trampoline (NULL, m, code, mono_method_needs_static_rgctx_invoke (m, FALSE), FALSE);
}
- /* The general, unoptimized case */
- m = mono_marshal_get_delegate_invoke (invoke, delegate);
- code = mono_compile_method (m);
- code = mini_add_method_trampoline (NULL, m, code, mono_method_needs_static_rgctx_invoke (m, FALSE), FALSE);
delegate->invoke_impl = mono_get_addr_from_ftnptr (code);
+ if (enable_caching && !callvirt && tramp_info->method) {
+ tramp_info->method_ptr = delegate->method_ptr;
+ tramp_info->invoke_impl = delegate->invoke_impl;
+ }
return code;
}
case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: {
MonoClassMethodPair *del_tramp = patch_info->data.del_tramp;
- target = mono_create_delegate_trampoline_info (domain, del_tramp->klass, del_tramp->method)->invoke_impl;
+ target = mono_create_delegate_trampoline_info (domain, del_tramp->klass, del_tramp->method);
break;
}
case MONO_PATCH_INFO_SFLDA: {