2010-06-28 Zoltan Varga <vargaz@gmail.com>
authorZoltan Varga <vargaz@gmail.com>
Mon, 28 Jun 2010 21:04:50 +0000 (21:04 -0000)
committerZoltan Varga <vargaz@gmail.com>
Mon, 28 Jun 2010 21:04:50 +0000 (21:04 -0000)
* mini-trampolines.c mini.c: Rework the virtual call handling code by using a separate
vtable trampoline for each vtable slot for both the LLVM and non-LLVM cases. Move
most of the vcall specific code to mono_vcall_trampoline () from
common_call_trampoline ().

svn path=/trunk/mono/; revision=159620

mono/mini/ChangeLog
mono/mini/mini-trampolines.c
mono/mini/mini.c
mono/mini/mini.h

index 7a3851e22c1f74f612e468caf4ba3222fbb8dace..7257401a00ce365cca5834c498954dd9b4041381 100755 (executable)
@@ -1,3 +1,10 @@
+2010-06-28  Zoltan Varga  <vargaz@gmail.com>
+
+       * mini-trampolines.c mini.c: Rework the virtual call handling code by using a separate
+       vtable trampoline for each vtable slot for both the LLVM and non-LLVM cases. Move
+       most of the vcall specific code to mono_vcall_trampoline () from
+       common_call_trampoline ().
+
 2010-06-27  Zoltan Varga  <vargaz@gmail.com>
 
        * mini-trampolines.c (common_call_trampoline): Remove the code which created static
index 7d5bcec2e22aba329f9f99cbcc3cd7b6a1e4af24..7cfabd9fa93454706f55ee6f11e7fcaad743df77 100644 (file)
@@ -271,86 +271,24 @@ is_generic_method_definition (MonoMethod *m)
  * from JITted and LLVM compiled code.
  */
 static gpointer
-common_call_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp, MonoVTable *vt, gpointer *vtable_slot, gboolean need_rgctx_tramp)
+common_call_trampoline (mgreg_t *regs, guint8 *code, MonoMethod *m, guint8* tramp, MonoVTable *vt, gpointer *vtable_slot, gboolean need_rgctx_tramp)
 {
        gpointer addr, compiled_method;
        gboolean generic_shared = FALSE;
-       MonoMethod *m;
        MonoMethod *declaring = NULL;
        MonoMethod *generic_virtual = NULL, *variant_iface = NULL;
        int context_used;
        gboolean virtual, proxy = FALSE, variance_used = FALSE;
-       gpointer *orig_vtable_slot;
+       gpointer *orig_vtable_slot, *imt_vcall_slot = NULL;
        MonoJitInfo *ji = NULL;
 
-       /* The first case is for !llvm, the second is for llvm */
-       virtual = (arg == MONO_FAKE_VTABLE_METHOD) || ((gpointer)vtable_slot > (gpointer)vt);
-
-       m = arg;
+       virtual = (gpointer)vtable_slot > (gpointer)vt;
 
        orig_vtable_slot = vtable_slot;
 
-       if (m == MONO_FAKE_VTABLE_METHOD) {
-               int displacement;
-               if (!vt) {
-                       int i;
-                       MonoJitInfo *ji;
-
-                       ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
-                       if (ji)
-                               printf ("Caller: %s\n", mono_method_full_name (ji->method, TRUE));
-                       /* Print some debug info */
-                       for (i = 0; i < 32; ++i)
-                               printf ("0x%x ", code [-32 + i]);
-                       printf ("\n");
-                       g_assert (vt);
-               }
-               displacement = (guint8*)vtable_slot - (guint8*)vt;
-               if (displacement > 0) {
-                       int slot = (displacement - G_STRUCT_OFFSET (MonoVTable, vtable)) / sizeof (gpointer);
-                       g_assert (slot >= 0);
-
-                       /* Avoid loading metadata or creating a generic vtable if possible */
-                       addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, slot);
-                       if (addr)
-                               addr = mono_create_ftnptr (mono_domain_get (), addr);
-                       if (addr && !vt->klass->valuetype) {
-                               vtable_slot = mono_get_vcall_slot_addr (code, regs);
-                               if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot)) {
-                                       *vtable_slot = mono_get_addr_from_ftnptr (addr);
-                               }
-
-                               return addr;
-                       }
-
-                       /*
-                        * Bug #616463 (see
-                        * is_generic_method_definition() above) also
-                        * goes away if we do a
-                        * mono_class_setup_vtable (vt->klass) here,
-                        * because we then inflate the method
-                        * correctly, put it in the cache, and the
-                        * "wrong" inflation invocation still looks up
-                        * the correctly inflated method.
-                        *
-                        * The hack above seems more stable and
-                        * trustworthy.
-                        */
-                       m = mono_class_get_vtable_entry (vt->klass, slot);
-                       if (mono_method_needs_static_rgctx_invoke (m, FALSE))
-                               need_rgctx_tramp = TRUE;
-
-                       /*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
-               } else {
-                       /* We got here from an interface method: redirect to IMT handling */
-                       m = MONO_FAKE_IMT_METHOD;
-                       /*g_print ("vtable with disp %d at %p\n", displacement, code);*/
-               }
-       }
-
-       /* this is the IMT trampoline */
 #ifdef MONO_ARCH_HAVE_IMT
-       if (m == MONO_FAKE_IMT_METHOD) {
+       /* IMT call */
+       if (vt && (gpointer)vtable_slot < (gpointer)vt) {
                MonoMethod *impl_method;
                MonoObject *this_arg;
 
@@ -368,7 +306,8 @@ common_call_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp
                        proxy = TRUE;
                    m = mono_object_get_virtual_method (this_arg, m);
                } else {
-                       vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, regs, code, m, &impl_method, &need_rgctx_tramp, &variance_used);
+                       imt_vcall_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, regs, code, m, &impl_method, &need_rgctx_tramp, &variance_used);
+                       vtable_slot = imt_vcall_slot;
                        /* mono_convert_imt_slot_to_vtable_slot () also gives us the method that is supposed
                         * to be called, so we compile it and go ahead as usual.
                         */
@@ -574,15 +513,14 @@ common_call_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp
        vtable_slot = orig_vtable_slot;
 
        if (vtable_slot) {
-               gboolean variance_used = FALSE;
                if (m->klass->valuetype)
                        addr = get_unbox_trampoline (mono_get_generic_context_from_code (code), m, addr, need_rgctx_tramp);
                g_assert (*vtable_slot);
 
                if (!proxy && (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))) {
-#ifdef MONO_ARCH_HAVE_IMT
-                       vtable_slot = mono_convert_imt_slot_to_vtable_slot (vtable_slot, regs, code, m, NULL, &need_rgctx_tramp, &variance_used);
-#endif
+                       if (imt_vcall_slot)
+                               /* This is the vcall slot which gets called through the IMT thunk */
+                               vtable_slot = imt_vcall_slot;
                        *vtable_slot = mono_get_addr_from_ftnptr (addr);
                }
        }
@@ -618,43 +556,31 @@ common_call_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp
 /**
  * mono_magic_trampoline:
  *
- *   This trampoline handles calls from JITted code.
+ *   This trampoline handles normal calls from JITted code.
  */
 gpointer
 mono_magic_trampoline (mgreg_t *regs, guint8 *code, gpointer arg, guint8* tramp)
 {
-       gpointer *vtable_slot;
-       int displacement;
-       MonoVTable *vt;
-
        trampoline_calls ++;
 
-       if (code && !mono_use_llvm)
-               vt = mono_arch_get_vcall_slot (code, regs, &displacement);
-       else
-               vt = NULL;
-       if (vt)
-               vtable_slot = (gpointer*)((char*)vt + displacement);
-       else
-               vtable_slot = NULL;
-
-       return common_call_trampoline (regs, code, arg, tramp, vt, vtable_slot, FALSE);
+       return common_call_trampoline (regs, code, arg, tramp, NULL, NULL, FALSE);
 }
 
-#ifdef MONO_ARCH_LLVM_SUPPORTED
 /*
- * mono_llvm_vcall_trampoline:
+ * mono_vcall_trampoline:
  *
- *   This trampoline handles virtual calls when using LLVM.
+ *   This trampoline handles virtual calls.
  */
 static gpointer
-mono_llvm_vcall_trampoline (mgreg_t *regs, guint8 *code, int slot, guint8 *tramp)
+mono_vcall_trampoline (mgreg_t *regs, guint8 *code, int slot, guint8 *tramp)
 {
        MonoObject *this;
        MonoVTable *vt;
        gpointer *vtable_slot;
        MonoMethod *m;
-       gboolean need_rgctx_tramp;
+       gboolean need_rgctx_tramp = FALSE;
+       int displacement;
+       gpointer addr;
 
        trampoline_calls ++;
 
@@ -662,36 +588,85 @@ mono_llvm_vcall_trampoline (mgreg_t *regs, guint8 *code, int slot, guint8 *tramp
         * We need to obtain the following pieces of information:
         * - the method which needs to be compiled.
         * - the vtable slot.
-        * Since disassembly is impossible with LLVM, we use one vtable trampoline per
-        * vtable slot index, and obtain the method using this index + the this argument.
-        * This requires us to always pass this as the first argument, regardless of the
-        * signature.
+        * We use one vtable trampoline per vtable slot index, so we need only the vtable,
+        * the other two can be computed from the vtable + the slot index.
         */
+       if (mono_use_llvm) {
+               /*
+                * Since disassembly is impossible with LLVM, we obtain the vtable using
+                * the this argument. This requires us to always pass this as the first argument,
+                * regardless of the signature.
+                */
 #ifndef MONO_ARCH_THIS_AS_FIRST_ARG
-       NOT_IMPLEMENTED;
+               NOT_IMPLEMENTED;
 #endif
+               this = mono_arch_get_this_arg_from_call (NULL, NULL, regs, code);
+               g_assert (this);
 
-       this = mono_arch_get_this_arg_from_call (NULL, NULL, regs, code);
-       g_assert (this);
+               vt = this->vtable;
+       } else {
+               /*
+                * Obtain the vtable pointer in an arch specific manner.
+                */
+               vt = mono_arch_get_vcall_slot (code, regs, &displacement);
 
-       vt = this->vtable;
+               if (!vt) {
+                       int i;
+                       MonoJitInfo *ji;
 
-       if (slot < 0) {
-               m = MONO_FAKE_IMT_METHOD;
-               need_rgctx_tramp = FALSE;
-               vtable_slot = &(((gpointer*)vt) [slot]);
-       } else {
+                       ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
+                       if (ji)
+                               printf ("Caller: %s\n", mono_method_full_name (ji->method, TRUE));
+                       /* Print some debug info */
+                       for (i = 0; i < 32; ++i)
+                               printf ("0x%x ", code [-32 + i]);
+                       printf ("\n");
+                       g_assert (vt);
+               }
+       }
+
+       if (slot >= 0) {
+               /* Normal virtual call */
+               vtable_slot = &(vt->vtable [slot]);
+
+               /* Avoid loading metadata or creating a generic vtable if possible */
+               addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, slot);
+               if (addr && !vt->klass->valuetype) {
+                       if (mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
+                               *vtable_slot = addr;
 
-               m = mono_class_get_vtable_entry (this->vtable->klass, slot);
+                       return mono_create_ftnptr (mono_domain_get (), addr);
+               }
+
+               /*
+                * Bug #616463 (see
+                * is_generic_method_definition() above) also
+                * goes away if we do a
+                * mono_class_setup_vtable (vt->klass) here,
+                * because we then inflate the method
+                * correctly, put it in the cache, and the
+                * "wrong" inflation invocation still looks up
+                * the correctly inflated method.
+                *
+                * The hack above seems more stable and
+                * trustworthy.
+                */
+               m = mono_class_get_vtable_entry (vt->klass, slot);
 
                need_rgctx_tramp = mono_method_needs_static_rgctx_invoke (m, 0);
+       } else {
+               /* IMT call */
+               vtable_slot = &(((gpointer*)vt) [slot]);
 
-               vtable_slot = &(vt->vtable [slot]);
+               m = NULL;
+               need_rgctx_tramp = FALSE;
        }
 
+       // FIXME:
+       // - get rid of the non-vcall cases in mono_arch_get_vcall_slot
+
        return common_call_trampoline (regs, code, m, tramp, vt, vtable_slot, need_rgctx_tramp);
 }
-#endif
 
 gpointer
 mono_generic_virtual_remoting_trampoline (mgreg_t *regs, guint8 *code, MonoMethod *m, guint8 *tramp)
@@ -745,7 +720,6 @@ mono_aot_trampoline (mgreg_t *regs, guint8 *code, guint8 *token_info,
        guint32 token;
        MonoMethod *method = NULL;
        gpointer addr;
-       gpointer *vtable_slot;
        guint8 *plt_entry;
 
        trampoline_calls ++;
@@ -765,9 +739,6 @@ mono_aot_trampoline (mgreg_t *regs, guint8 *code, guint8 *token_info,
 
        addr = mono_create_ftnptr (mono_domain_get (), addr);
 
-       vtable_slot = mono_get_vcall_slot_addr (code, regs);
-       g_assert (!vtable_slot);
-
        /* This is a normal call through a PLT entry */
        plt_entry = mono_aot_get_plt_entry (code);
        g_assert (plt_entry);
@@ -1099,10 +1070,8 @@ mono_get_trampoline_func (MonoTrampolineType tramp_type)
                return mono_monitor_enter_trampoline;
        case MONO_TRAMPOLINE_MONITOR_EXIT:
                return mono_monitor_exit_trampoline;
-#ifdef MONO_ARCH_LLVM_SUPPORTED
-       case MONO_TRAMPOLINE_LLVM_VCALL:
-               return mono_llvm_vcall_trampoline;
-#endif
+       case MONO_TRAMPOLINE_VCALL:
+               return mono_vcall_trampoline;
 #ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
        case MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD:
                return mono_handler_block_guard_trampoline;
@@ -1152,9 +1121,7 @@ mono_trampolines_init (void)
        mono_trampoline_code [MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING] = create_trampoline_code (MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING);
        mono_trampoline_code [MONO_TRAMPOLINE_MONITOR_ENTER] = create_trampoline_code (MONO_TRAMPOLINE_MONITOR_ENTER);
        mono_trampoline_code [MONO_TRAMPOLINE_MONITOR_EXIT] = create_trampoline_code (MONO_TRAMPOLINE_MONITOR_EXIT);
-#ifdef MONO_ARCH_LLVM_SUPPORTED
-       mono_trampoline_code [MONO_TRAMPOLINE_LLVM_VCALL] = create_trampoline_code (MONO_TRAMPOLINE_LLVM_VCALL);
-#endif
+       mono_trampoline_code [MONO_TRAMPOLINE_VCALL] = create_trampoline_code (MONO_TRAMPOLINE_VCALL);
 #ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
        mono_trampoline_code [MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD] = create_trampoline_code (MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD);
        mono_create_handler_block_trampoline ();
@@ -1485,41 +1452,6 @@ mono_create_monitor_exit_trampoline (void)
 }
  
 #ifdef MONO_ARCH_LLVM_SUPPORTED
-/*
- * mono_create_llvm_vcall_trampoline:
- *
- *  LLVM emits code for virtual calls which mono_get_vcall_slot is unable to
- * decode, i.e. only the final branch address is available:
- * mov <offset>(%rax), %rax
- * <random code inserted by instruction scheduling>
- * call *%rax
- *
- * To work around this problem, we don't use the common vtable trampoline when
- * llvm is enabled. Instead, we use one trampoline per method.
- */
-gpointer
-mono_create_llvm_vcall_trampoline (MonoMethod *method)
-{
-       MonoDomain *domain;
-       gpointer res;
-
-       domain = mono_domain_get ();
-
-       mono_domain_lock (domain);
-       res = g_hash_table_lookup (domain_jit_info (domain)->llvm_vcall_trampoline_hash, method);
-       mono_domain_unlock (domain);
-       if (res)
-               return res;
-
-       res = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_LLVM_VCALL, domain, NULL);
-
-       mono_domain_lock (domain);
-       g_hash_table_insert (domain_jit_info (domain)->llvm_vcall_trampoline_hash, method, res);
-       mono_domain_unlock (domain);
-
-       return res;
-}
-
 /*
  * mono_create_llvm_imt_trampoline:
  *
index 50b5af2f27bb0354f078ccae69fcaed2e31d9df3..cf60ab304b265352a713dcc03af8c6eb8f471089 100644 (file)
@@ -5443,54 +5443,32 @@ static int vtable_trampolines_size;
 gpointer
 mini_get_vtable_trampoline (int slot_index)
 {
-       if (!mono_use_llvm) {
-               /* Use only one trampoline */
-               if (slot_index < 0) {
-                       static gpointer tramp = NULL;
-
-                       if (!tramp)
-                               tramp = mono_create_specific_trampoline (MONO_FAKE_IMT_METHOD, MONO_TRAMPOLINE_JIT, mono_get_root_domain (), NULL);
-                       return tramp;
-               } else {
-                       static gpointer tramp = NULL;
+       int index = slot_index + MONO_IMT_SIZE;
 
-                       if (!tramp)
-                               tramp = mono_create_specific_trampoline (MONO_FAKE_VTABLE_METHOD, MONO_TRAMPOLINE_JIT, mono_get_root_domain (), NULL);
-                       return tramp;
-               }
-       } else {
-               int index = slot_index + MONO_IMT_SIZE;
-
-               /* For LLVM, use one trampoline per vtable slot index */
-               g_assert (slot_index >= - MONO_IMT_SIZE);
-               if (!vtable_trampolines || slot_index + MONO_IMT_SIZE >= vtable_trampolines_size) {
-                       mono_jit_lock ();
-                       if (!vtable_trampolines || index >= vtable_trampolines_size) {
-                               int new_size;
-                               gpointer new_table;
-
-                               new_size = vtable_trampolines_size ? vtable_trampolines_size * 2 : 128;
-                               while (new_size <= index)
-                                       new_size *= 2;
-                               new_table = g_new0 (gpointer, new_size);
-
-                               if (vtable_trampolines)
-                                       memcpy (new_table, vtable_trampolines, vtable_trampolines_size * sizeof (gpointer));
-                               mono_memory_barrier ();
-                               vtable_trampolines = new_table;
-                               vtable_trampolines_size = new_size;
-                       }
-                       mono_jit_unlock ();
+       g_assert (slot_index >= - MONO_IMT_SIZE);
+       if (!vtable_trampolines || slot_index + MONO_IMT_SIZE >= vtable_trampolines_size) {
+               mono_jit_lock ();
+               if (!vtable_trampolines || index >= vtable_trampolines_size) {
+                       int new_size;
+                       gpointer new_table;
+
+                       new_size = vtable_trampolines_size ? vtable_trampolines_size * 2 : 128;
+                       while (new_size <= index)
+                               new_size *= 2;
+                       new_table = g_new0 (gpointer, new_size);
+
+                       if (vtable_trampolines)
+                               memcpy (new_table, vtable_trampolines, vtable_trampolines_size * sizeof (gpointer));
+                       mono_memory_barrier ();
+                       vtable_trampolines = new_table;
+                       vtable_trampolines_size = new_size;
                }
-
-#ifdef MONO_ARCH_LLVM_SUPPORTED 
-               if (!vtable_trampolines [index])
-                       vtable_trampolines [index] = mono_create_specific_trampoline (GUINT_TO_POINTER (slot_index), MONO_TRAMPOLINE_LLVM_VCALL, mono_get_root_domain (), NULL);
-               return vtable_trampolines [index];
-#else
-               g_assert_not_reached ();
-#endif
+               mono_jit_unlock ();
        }
+
+       if (!vtable_trampolines [index])
+               vtable_trampolines [index] = mono_create_specific_trampoline (GUINT_TO_POINTER (slot_index), MONO_TRAMPOLINE_VCALL, mono_get_root_domain (), NULL);
+       return vtable_trampolines [index];
 }
 
 static gpointer
index 0d60e8a6f66f5b1255350dd3f0930e2b783048b8..74b9a96bd494a3e0de849030b608aa3aa61a82b0 100644 (file)
@@ -106,9 +106,6 @@ typedef gint64 mgreg_t;
 #define inst_ls_word data.op[MINI_LS_WORD_IDX].const_val
 #define inst_ms_word data.op[MINI_MS_WORD_IDX].const_val
 
-#define MONO_FAKE_IMT_METHOD ((MonoMethod*)GINT_TO_POINTER(-1))
-#define MONO_FAKE_VTABLE_METHOD ((MonoMethod*)GINT_TO_POINTER(-2))
-
 #ifndef DISABLE_AOT
 #define MONO_USE_AOT_COMPILER
 #endif
@@ -954,9 +951,7 @@ typedef enum {
        MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING,
        MONO_TRAMPOLINE_MONITOR_ENTER,
        MONO_TRAMPOLINE_MONITOR_EXIT,
-#ifdef MONO_ARCH_LLVM_SUPPORTED
-       MONO_TRAMPOLINE_LLVM_VCALL,
-#endif
+       MONO_TRAMPOLINE_VCALL,
 #ifdef MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD
        MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD,
 #endif
@@ -1676,7 +1671,6 @@ gpointer          mono_create_rgctx_lazy_fetch_trampoline (guint32 offset) MONO_
 gpointer          mono_create_monitor_enter_trampoline (void) MONO_INTERNAL;
 gpointer          mono_create_monitor_exit_trampoline (void) MONO_INTERNAL;
 gpointer          mono_create_static_rgctx_trampoline (MonoMethod *m, gpointer addr) MONO_INTERNAL;
-gpointer          mono_create_llvm_vcall_trampoline (MonoMethod *method) MONO_INTERNAL;
 gpointer          mono_create_llvm_imt_trampoline (MonoDomain *domain, MonoMethod *m, int vt_offset) MONO_INTERNAL;
 MonoVTable*       mono_find_class_init_trampoline_by_addr (gconstpointer addr) MONO_INTERNAL;
 guint32           mono_find_rgctx_lazy_fetch_trampoline_by_addr (gconstpointer addr) MONO_INTERNAL;