[delegate-virtual-trampoline] Add virtual functions delegate trampoline support
authorLudovic Henry <ludovic.henry@xamarin.com>
Mon, 14 Jul 2014 15:04:24 +0000 (11:04 -0400)
committerLudovic Henry <ludovic.henry@xamarin.com>
Tue, 12 Aug 2014 17:30:45 +0000 (13:30 -0400)
This optimization is activated in the ldvirtftn + delegate case, and is deactivated in the case where the architecture does not support it (mono_arch_get_delegate_virtual_invoke_impl return NULL)

mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-arm.c
mono/mini/mini-ia64.c
mono/mini/mini-mips.c
mono/mini/mini-ppc.c
mono/mini/mini-s390x.c
mono/mini/mini-trampolines.c
mono/mini/mini-x86.c
mono/mini/mini.c
mono/mini/mini.h

index 52eeaaf2fbfb32e0017b5862f246992577b5d5f6..48ffd7bb913f629b3faa6930450b636bc9202802 100644 (file)
@@ -4495,10 +4495,18 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono
 {
        MonoInst *ptr;
        int dreg;
-       MonoDelegateTrampInfo *trampoline;
+       gpointer trampoline;
        MonoInst *obj, *method_ins, *tramp_ins;
        MonoDomain *domain;
        guint8 **code_slot;
+       
+       if (virtual) {
+               MonoMethod *invoke = mono_get_delegate_invoke (klass);
+               g_assert (invoke);
+
+               if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
+                       return NULL;
+       }
 
        obj = handle_alloc (cfg, klass, FALSE, 0);
        if (!obj)
@@ -4520,6 +4528,7 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono
        /* Set method field */
        method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
        MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
+
        /* 
         * To avoid looking up the compiled code belonging to the target method
         * in mono_delegate_trampoline (), we allocate a per-domain memory slot to
@@ -4550,7 +4559,6 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono
                MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);                
        }
 
-       /* Set invoke_impl field */
        if (cfg->compile_aot) {
                MonoDelegateClassMethodPair *del_tramp;
 
@@ -4560,17 +4568,25 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono
                del_tramp->virtual = virtual;
                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);
+               if (virtual)
+                       trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
+               else
+                       trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
                EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
        }
 
-       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);
+       /* Set invoke_impl field */
+       if (virtual) {
+               MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
+       } else {
+               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);
+               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 */
 
@@ -11807,6 +11823,47 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
                                }
 
+                               /*
+                                * Optimize the common case of ldvirtftn+delegate creation
+                                */
+                               if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ip > header->code && ip [-1] == CEE_DUP)) {
+                                       MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
+                                       if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
+                                               MonoInst *target_ins, *handle_ins;
+                                               MonoMethod *invoke;
+                                               int invoke_context_used;
+
+                                               invoke = mono_get_delegate_invoke (ctor_method->klass);
+                                               if (!invoke || !mono_method_signature (invoke))
+                                                       LOAD_ERROR;
+
+                                               invoke_context_used = mini_method_check_context_used (cfg, invoke);
+
+                                               target_ins = sp [-1];
+
+                                               if (mono_security_core_clr_enabled ())
+                                                       ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
+
+#if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE)
+                                               /* FIXME: SGEN support */
+                                               if (invoke_context_used == 0) {
+                                                       ip += 6;
+                                                       if (cfg->verbose_level > 3)
+                                                               g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
+                                                       if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, TRUE))) {
+                                                               sp -= 2;
+                                                               *sp = handle_ins;
+                                                               CHECK_CFG_EXCEPTION;
+                                                               ip += 5;
+                                                               sp ++;
+                                                               break;
+                                                       }
+                                                       ip -= 6;
+                                               }
+#endif
+                                       }
+                               }
+
                                --sp;
                                args [0] = *sp;
 
index e56fbb9034a1f347e03ff6568405a4a234839511..3cefc22c87dae6fb6f254bd3ecf05eaaf0359cf2 100644 (file)
@@ -7899,6 +7899,31 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
 
        return start;
 }
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       guint8 *code, *start;
+       int size = 20;
+
+       start = code = mono_global_codeman_reserve (size);
+
+       /* Replace the this argument with the target */
+       amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
+       amd64_mov_reg_membase (code, AMD64_ARG_REG1, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, target), 8);
+
+       if (load_imt_reg) {
+               /* Load the IMT reg */
+               amd64_mov_reg_membase (code, MONO_ARCH_IMT_REG, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, method), 8);
+       }
+
+       /* Load the vtable */
+       amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoObject, vtable), 8);
+       amd64_jump_membase (code, AMD64_RAX, offset);
+
+       return start;
+}
+
 void
 mono_arch_finish_init (void)
 {
index d93cd923e814946730bcf703fbe750241bb87bbc..2b37acc3aae7c358867f535c18695c0c5fe18b43 100644 (file)
@@ -795,6 +795,12 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
        return NULL;
 }
 
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       return NULL;
+}
+
 gpointer
 mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
 {
index 7d74ad8f45f34aead1f502d2d46f48a2d6006a8d..2daf4fca598bad5064aa8f83e23c7167671159cc 100644 (file)
@@ -4649,6 +4649,12 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
        return NULL;
 }
 
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       return NULL;
+}
+
 MonoInst*
 mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
 {
index a16c228edaf626fc0ffd4d92dbe52bf7401685dd..2aa6bd1d268e846a0a80db10d815b7e34718de00 100644 (file)
@@ -669,6 +669,12 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
        return NULL;
 }
 
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       return NULL;
+}
+
 gpointer
 mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
 {
index d27f4ad6028ee7141aa2e8f06c4519510df368b2..a7c83988da6bcf9b664fd42f7329e01923d9c913 100644 (file)
@@ -511,6 +511,12 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
        return start;
 }
 
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       return NULL;
+}
+
 gpointer
 mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
 {
index 0318e12c53683725440bb1ff4e34c7d9be09e551..435ef2904a495ce910dd6a9aa36e55ea1d9db6f7 100644 (file)
@@ -5726,6 +5726,22 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
 
 /*========================= End of Function ========================*/
 
+/*------------------------------------------------------------------*/
+/*                                                                  */
+/* Name                - mono_arch_get_delegate_virtual_invoke_impl.       */
+/*                                                                  */
+/* Function    -                                                   */
+/*                                                                 */
+/*------------------------------------------------------------------*/
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       return NULL;
+}
+
+/*========================= End of Function ========================*/
+
 /*------------------------------------------------------------------*/
 /*                                                                  */
 /* Name                - mono_arch_build_imt_thunk.                        */
index 95798a2350c19156b310fc8dce0a2c47c49cb196..acb623366fde2d9b67d54882947e6aa3b95be750 100644 (file)
@@ -1571,6 +1571,19 @@ mono_create_delegate_trampoline (MonoDomain *domain, MonoClass *klass)
 #endif
 }
 
+gpointer
+mono_create_delegate_virtual_trampoline (MonoDomain *domain, MonoClass *klass, MonoMethod *method)
+{
+#ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
+       MonoMethod *invoke = mono_get_delegate_invoke (klass);
+       g_assert (invoke);
+
+       return mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), method);
+#else
+       return NULL;
+#endif
+}
+
 gpointer
 mono_create_rgctx_lazy_fetch_trampoline (guint32 offset)
 {
index 301c3856fe47667927deb15722321c23307af1a7..c7931e328ff6b032e4e3e5beb1862cdc2ac4f68d 100644 (file)
@@ -6363,6 +6363,36 @@ mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_targe
        return start;
 }
 
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+       guint8 *code, *start;
+       int size = 24;
+
+       /*
+        * The stack contains:
+        * <delegate>
+        * <return addr>
+        */
+       start = code = mono_global_codeman_reserve (size);
+
+       /* Replace the this argument with the target */
+       x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
+       x86_mov_reg_membase (code, X86_ECX, X86_EAX, MONO_STRUCT_OFFSET (MonoDelegate, target), 4);
+       x86_mov_membase_reg (code, X86_ESP, 4, X86_ECX, 4);
+
+       if (load_imt_reg) {
+               /* Load the IMT reg */
+               x86_mov_reg_membase (code, MONO_ARCH_IMT_REG, X86_EAX, MONO_STRUCT_OFFSET (MonoDelegate, method), 4);
+       }
+
+       /* Load the vtable */
+       x86_mov_reg_membase (code, X86_EAX, X86_ECX, MONO_STRUCT_OFFSET (MonoObject, vtable), 4);
+       x86_jump_membase (code, X86_EAX, offset);
+
+       return start;
+}
+
 mgreg_t
 mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
 {
index dbc300a542374229e6cd26a5b0d8eb302fb5f4d7..e7a479a9398ee00bb5aea77e27d2bd83b1868d98 100644 (file)
@@ -3478,7 +3478,10 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code,
        case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: {
                MonoDelegateClassMethodPair *del_tramp = patch_info->data.del_tramp;
 
-               target = mono_create_delegate_trampoline_info (domain, del_tramp->klass, del_tramp->method);
+               if (del_tramp->virtual)
+                       target = mono_create_delegate_virtual_trampoline (domain, del_tramp->klass, del_tramp->method);
+               else
+                       target = mono_create_delegate_trampoline_info (domain, del_tramp->klass, del_tramp->method);
                break;
        }
        case MONO_PATCH_INFO_SFLDA: {
@@ -6946,6 +6949,62 @@ mini_get_vtable_trampoline (int slot_index)
        return vtable_trampolines [index];
 }
 
+gpointer
+mono_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method)
+{
+       gboolean is_virtual_generic, is_interface, load_imt_reg;
+       int offset, idx;
+
+       static guint8 **cache = NULL;
+       static int cache_size = 0;
+
+       if (!method)
+               return NULL;
+
+       /* FIXME Support more cases */
+       if (mono_aot_only)
+               return NULL;
+
+       if (MONO_TYPE_ISSTRUCT (sig->ret))
+               return NULL;
+
+       is_virtual_generic = method->is_inflated && mono_method_get_declaring_generic_method (method)->is_generic;
+       is_interface = method->klass->flags & TYPE_ATTRIBUTE_INTERFACE ? TRUE : FALSE;
+       load_imt_reg = is_virtual_generic || is_interface;
+
+       if (is_interface && !is_virtual_generic)
+               offset = ((gint32)mono_method_get_imt_slot (method) - MONO_IMT_SIZE) * SIZEOF_VOID_P;
+       else
+               offset = G_STRUCT_OFFSET (MonoVTable, vtable) + ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
+
+       idx = (offset / SIZEOF_VOID_P + MONO_IMT_SIZE) * 2 + (load_imt_reg ? 1 : 0);
+       g_assert (idx >= 0);
+
+       /* Resize the cache to idx + 1 */
+       if (cache_size < idx + 1) {
+               mono_jit_lock ();
+               if (cache_size < idx + 1) {
+                       guint8 **new_cache;
+                       int new_cache_size = idx + 1;
+
+                       new_cache = g_new0 (guint8*, new_cache_size);
+                       if (cache)
+                               memcpy (new_cache, cache, cache_size * sizeof (guint8*));
+                       g_free (cache);
+
+                       mono_memory_barrier ();
+                       cache = new_cache;
+                       cache_size = new_cache_size;
+               }
+               mono_jit_unlock ();
+       }
+
+       if (cache [idx])
+               return cache [idx];
+
+       return cache [idx] = mono_arch_get_delegate_virtual_invoke_impl (sig, method, offset, load_imt_reg);
+}
+
 static gpointer
 mini_get_imt_trampoline (int slot_index)
 {
index 7e0d9dddc9bd567526047811eaac542027d05a99..cccdc09561637a012045b0ec0c9b0e06cd766168 100644 (file)
@@ -2229,6 +2229,7 @@ gpointer          mono_create_jit_trampoline_from_token (MonoImage *image, guint
 gpointer          mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method) MONO_LLVM_INTERNAL;
 gpointer          mono_create_delegate_trampoline (MonoDomain *domain, MonoClass *klass) MONO_INTERNAL;
 MonoDelegateTrampInfo* mono_create_delegate_trampoline_info (MonoDomain *domain, MonoClass *klass, MonoMethod *method) MONO_INTERNAL;
+gpointer          mono_create_delegate_virtual_trampoline (MonoDomain *domain, MonoClass *klass, MonoMethod *method) MONO_INTERNAL;
 gpointer          mono_create_rgctx_lazy_fetch_trampoline (guint32 offset) MONO_INTERNAL;
 gpointer          mono_create_monitor_enter_trampoline (void) MONO_INTERNAL;
 gpointer          mono_create_monitor_exit_trampoline (void) MONO_INTERNAL;
@@ -2302,6 +2303,9 @@ void              mono_handle_global_vregs (MonoCompile *cfg) MONO_INTERNAL;
 void              mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) MONO_INTERNAL;
 void              mono_if_conversion (MonoCompile *cfg) MONO_INTERNAL;
 
+/* virtual function delegate */
+gpointer          mono_get_delegate_virtual_invoke_impl  (MonoMethodSignature *sig, MonoMethod *method) MONO_INTERNAL;
+
 /* methods that must be provided by the arch-specific port */
 void      mono_arch_init                        (void) MONO_INTERNAL;
 void      mono_arch_finish_init                 (void) MONO_INTERNAL;
@@ -2452,6 +2456,7 @@ void     mono_arch_nullify_class_init_trampoline(guint8 *code, mgreg_t *regs) MO
 int      mono_arch_get_this_arg_reg             (guint8 *code) MONO_INTERNAL;
 gpointer mono_arch_get_this_arg_from_call       (mgreg_t *regs, guint8 *code) MONO_INTERNAL;
 gpointer mono_arch_get_delegate_invoke_impl     (MonoMethodSignature *sig, gboolean has_target) MONO_INTERNAL;
+gpointer mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg) MONO_INTERNAL;
 gpointer mono_arch_create_specific_trampoline   (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) MONO_INTERNAL;
 void        mono_arch_emit_imt_argument         (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg) MONO_INTERNAL;
 MonoMethod* mono_arch_find_imt_method           (mgreg_t *regs, guint8 *code) MONO_INTERNAL;