2003-09-15 Zoltan Varga <vargaz@freemail.hu>
authorZoltan Varga <vargaz@gmail.com>
Mon, 15 Sep 2003 09:12:45 +0000 (09:12 -0000)
committerZoltan Varga <vargaz@gmail.com>
Mon, 15 Sep 2003 09:12:45 +0000 (09:12 -0000)
* tramp-x86.c (x86_class_init_trampoline): New trampoline function
which calls mono_runtime_class_init and patches the call site to
avoid further calls.
(mono_arch_create_class_init_trampoline): New arch specific function
to create a class init trampoline.
(create_trampoline_code): Generalized so it can create
class init trampolines as well.

* mini.c (helper_sig_class_init_trampoline): New helper variable.
(mono_create_class_init_trampoline): New function to create and cache
class init trampolines.
(mono_find_class_init_trampoline_by_addr): New function to lookup the
vtable given the address of a class init trampoline. Used by the
patching process.
(mono_codegen): Generate a call to a trampoline instead of
mono_runtime_class_init in LDSFLD[A].
(mono_codegen): Add relocations for the new trampoline.

* mini.h mini-x86.c aot.c: Added a new relocation type:
MONO_PATCH_INFO_CLASS_INIT.

* mini.h: Bump AOT version number.

* aot.c: Create a copy of the loaded code instead of using the original
so methods which call each other will be close in memory, improving
cache behaviour.

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

mono/mini/ChangeLog
mono/mini/aot.c
mono/mini/mini-x86.c
mono/mini/mini.c
mono/mini/mini.h
mono/mini/tramp-x86.c

index ffa51eeef9b075e51751adc694c843b568bf0d0c..d5bf41904823fb2e85b831b1655469228eca94a1 100644 (file)
@@ -1,5 +1,32 @@
 2003-09-15  Zoltan Varga  <vargaz@freemail.hu>
 
+       * tramp-x86.c (x86_class_init_trampoline): New trampoline function
+       which calls mono_runtime_class_init and patches the call site to
+       avoid further calls.
+       (mono_arch_create_class_init_trampoline): New arch specific function 
+       to create a class init trampoline.
+       (create_trampoline_code): Generalized so it can create
+       class init trampolines as well.
+
+       * mini.c (helper_sig_class_init_trampoline): New helper variable.
+       (mono_create_class_init_trampoline): New function to create and cache
+       class init trampolines.
+       (mono_find_class_init_trampoline_by_addr): New function to lookup the
+       vtable given the address of a class init trampoline. Used by the
+       patching process.
+       (mono_codegen): Generate a call to a trampoline instead of
+       mono_runtime_class_init in LDSFLD[A].
+       (mono_codegen): Add relocations for the new trampoline.
+       
+       * mini.h mini-x86.c aot.c: Added a new relocation type: 
+       MONO_PATCH_INFO_CLASS_INIT.
+
+       * mini.h: Bump AOT version number.
+
+       * aot.c: Create a copy of the loaded code instead of using the original
+       so methods which call each other will be close in memory, improving
+       cache behaviour.
+       
        * exceptions-x86.c (mono_arch_has_unwind_info): Back out the previous
        patch since it breaks the regression tests.
        
index 9bf96eaad2b8209089c02a6c841c785438143049..d51db251038b2982bd17dd4297e9e892a5a9af8b 100644 (file)
@@ -68,7 +68,16 @@ static MonoGHashTable *aot_modules;
 
 static CRITICAL_SECTION aot_mutex;
 
-static guint32 mono_aot_verbose = 0;
+static guint32 mono_aot_verbose = 1;
+
+/*
+ * Disabling this will make a copy of the loaded code and use the copy instead 
+ * of the original. This will place the caller and the callee close to each 
+ * other in memory, possibly improving cache behavior. Since the original
+ * code is in copy-on-write memory, this will not increase the memory usage
+ * of the runtime.
+ */
+static gboolean use_loaded_code = FALSE;
 
 /* For debugging */
 static gint32 mono_last_aot_method = -1;
@@ -321,18 +330,12 @@ mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
        if (!g_module_symbol (module, info_label, (gpointer *)&info))
                return NULL;
 
-       {
-               static int count = 0;
-
-               count ++;
-
-               if (mono_last_aot_method != -1) {
-                       if (count > mono_last_aot_method)
+       if (mono_last_aot_method != -1) {
+               if (mono_jit_stats.methods_aot > mono_last_aot_method)
                                return NULL;
-                       else
-                               if (count == mono_last_aot_method)
-                                       printf ("LAST AOT METHOD: %s.%s.%s.\n", klass->name_space, klass->name, method->name);
-               }
+               else
+                       if (mono_jit_stats.methods_aot == mono_last_aot_method)
+                               printf ("LAST AOT METHOD: %s.%s.%s.\n", klass->name_space, klass->name, method->name);
        }
 
 #ifdef HAVE_BOEHM_GC
@@ -348,18 +351,12 @@ mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
        used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info));
        info++;
 
-       /* 
-        * Enabling this will place the caller and the callee close to each other
-        * in memory, possibly improving cache behavior.
-        */
-/*
-       {
+       if (!use_loaded_code) {
                guint8 *code2;
                code2 = mono_mempool_alloc (domain->code_mp, code_len);
                memcpy (code2, code, code_len);
                code = code2;
        }
-*/
 
        if (mono_aot_verbose > 1)
                printf ("FOUND AOT compiled code for %s %p - %p %p\n", mono_method_full_name (method, TRUE), code, code + code_len, info);
@@ -454,6 +451,7 @@ mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
                                mono_class_init (ji->data.klass);
                                break;
                        case MONO_PATCH_INFO_VTABLE:
+                       case MONO_PATCH_INFO_CLASS_INIT:
                                ji->data.klass = decode_class_info (aot_module, data);
                                g_assert (ji->data.klass);
                                mono_class_init (ji->data.klass);
@@ -524,18 +522,20 @@ mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
                        patch_info = ji;
                }
 
-#ifndef PLATFORM_WIN32
+               if (use_loaded_code) {
                /* disable write protection */
-               page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
-               pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
-               err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
-               g_assert (err == 0);
+#ifndef PLATFORM_WIN32
+                       page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
+                       pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
+                       err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
+                       g_assert (err == 0);
 #else
-               {
-                       DWORD oldp;
-                       g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
-               }
+                       {
+                               DWORD oldp;
+                               g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
+                       }
 #endif
+               }
 
                /* Do this outside the lock to avoid deadlocks */
                LeaveCriticalSection (&aot_mutex);
@@ -854,6 +854,7 @@ emit_method (MonoAotCompile *acfg, MonoCompile *cfg)
                        j++;
                        break;
                case MONO_PATCH_INFO_VTABLE:
+               case MONO_PATCH_INFO_CLASS_INIT:
                        patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
                        j++;
                        break;
@@ -955,6 +956,7 @@ emit_method (MonoAotCompile *acfg, MonoCompile *cfg)
                        case MONO_PATCH_INFO_INTERNAL_METHOD:
                        case MONO_PATCH_INFO_IMAGE:
                        case MONO_PATCH_INFO_VTABLE:
+                       case MONO_PATCH_INFO_CLASS_INIT:
                        case MONO_PATCH_INFO_SFLDA:
                        case MONO_PATCH_INFO_EXC_NAME:
                                fprintf (tmpfp, "\t.long %s\n", patch_info->data.name);
index 3e51829e5d3846ef6c4e4bcf6534654b48459988..0044c0f0621e9114115c625e49091b72aa5f5151 100644 (file)
@@ -3062,6 +3062,13 @@ mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, Mono
                case MONO_PATCH_INFO_VTABLE:
                        *((gconstpointer *)(ip + 1)) = mono_class_vtable (domain, patch_info->data.klass);
                        continue;
+               case MONO_PATCH_INFO_CLASS_INIT: {
+                       guint8 *code = ip;
+                       /* Might already been changed to a nop */
+                       x86_call_imm (code, 0);
+                       target = mono_create_class_init_trampoline (mono_class_vtable (domain, patch_info->data.klass));
+                       break;
+               }
                case MONO_PATCH_INFO_SFLDA: {
                        MonoVTable *vtable = mono_class_vtable (domain, patch_info->data.field->parent);
                        if (!vtable->initialized && !(vtable->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && mono_class_needs_cctor_run (vtable->klass, method))
index 40b67080c2ea38e7fcf2c51757dbee2992e88261..2dcee7ca468cd308db77e2e27e161f8ba40ccc9b 100644 (file)
@@ -86,6 +86,7 @@ static MonoMethodSignature *helper_sig_long_double = NULL;
 static MonoMethodSignature *helper_sig_uint_double = NULL;
 static MonoMethodSignature *helper_sig_int_double = NULL;
 static MonoMethodSignature *helper_sig_stelem_ref = NULL;
+static MonoMethodSignature *helper_sig_class_init_trampoline = NULL;
 
 static guint32 default_opt = MONO_OPT_PEEPHOLE;
 
@@ -4035,9 +4036,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                vtable = mono_class_vtable (cfg->domain, klass);
                                if (!addr) {
                                        if ((!vtable->initialized || mono_compile_aot) && !(klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT) && mono_class_needs_cctor_run (klass, method)) {
-                                               MonoInst *iargs [1];
-                                               NEW_VTABLECONST (cfg, iargs [0], vtable);
-                                               mono_emit_jit_icall (cfg, bblock, mono_runtime_class_init, iargs, ip);
+                                               guint8 *tramp = mono_create_class_init_trampoline (vtable);
+                                               mono_emit_native_call (cfg, bblock, tramp, 
+                                                                                          helper_sig_class_init_trampoline,
+                                                                                          NULL, ip, FALSE);
                                                if (cfg->verbose_level > 2)
                                                        g_print ("class %s.%s needs init call for %s\n", klass->name_space, klass->name, field->name);
                                        } else {
@@ -5527,6 +5529,10 @@ create_helper_signature (void)
        helper_sig_memset->params [2] = &mono_defaults.int32_class->byval_arg;
        helper_sig_memset->ret = &mono_defaults.void_class->byval_arg;
        helper_sig_memset->pinvoke = 1;
+
+       helper_sig_class_init_trampoline = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
+       helper_sig_class_init_trampoline->ret = &mono_defaults.void_class->byval_arg;
+       helper_sig_class_init_trampoline->pinvoke = 1;  
 }
 
 static GHashTable *jit_icall_hash_name = NULL;
@@ -5587,7 +5593,7 @@ mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignatu
        info->name = name;
        info->func = func;
        info->sig = sig;
-               
+
        if (is_save
 #ifdef MONO_USE_EXC_TABLES
            || mono_arch_has_unwind_info (func)
@@ -5607,6 +5613,47 @@ mono_register_jit_icall (gconstpointer func, const char *name, MonoMethodSignatu
        return info;
 }
 
+static GHashTable *class_init_hash_addr = NULL;
+
+gpointer
+mono_create_class_init_trampoline (MonoVTable *vtable)
+{
+       gpointer code;
+
+       /* previously created trampoline code */
+       mono_domain_lock (vtable->domain);
+       code = 
+               mono_g_hash_table_lookup (vtable->domain->class_init_trampoline_hash,
+                                                                 vtable);
+       mono_domain_unlock (vtable->domain);
+       if (code)
+               return code;
+
+       code = mono_arch_create_class_init_trampoline (vtable);
+
+       /* store trampoline address */
+       mono_domain_lock (vtable->domain);
+       mono_g_hash_table_insert (vtable->domain->class_init_trampoline_hash,
+                                                         vtable, code);
+       mono_domain_unlock (vtable->domain);
+
+       /* FIXME: locking */
+       if (!class_init_hash_addr)
+               class_init_hash_addr = g_hash_table_new (NULL, NULL);
+       g_hash_table_insert (class_init_hash_addr, code, vtable);
+
+       return code;
+}
+
+MonoVTable*
+mono_find_class_init_trampoline_by_addr (gconstpointer addr)
+{
+       if (class_init_hash_addr)
+               return g_hash_table_lookup (class_init_hash_addr, addr);
+       else
+               return NULL;
+}
+
 static GHashTable *emul_opcode_hash = NULL;
 
 static MonoJitICallInfo *
@@ -6548,6 +6595,13 @@ mono_codegen (MonoCompile *cfg)
                                patch_info->type = MONO_PATCH_INFO_INTERNAL_METHOD;
                                patch_info->data.name = info->name;
                        }
+                       else {
+                               MonoVTable *vtable = mono_find_class_init_trampoline_by_addr (patch_info->data.target);
+                               if (vtable) {
+                                       patch_info->type = MONO_PATCH_INFO_CLASS_INIT;
+                                       patch_info->data.klass = vtable->klass;
+                               }
+                       }
                        break;
                }
                case MONO_PATCH_INFO_SWITCH: {
index 9b98f3f7be7f20e518a1a6667d85976f26faddf5..0968ef262892808cfa51d54ca4b372725ce23ff7 100644 (file)
@@ -18,7 +18,7 @@
 #define MONO_USE_AOT_COMPILER
 
 /* Version number of the AOT file format */
-#define MONO_AOT_FILE_VERSION "4"
+#define MONO_AOT_FILE_VERSION "5"
 
 #if 1
 #define mono_bitset_test_fast(set,n) (((guint32*)set)[2+(n)/32] & (1 << ((n) % 32)))
@@ -347,6 +347,7 @@ typedef enum {
        MONO_PATCH_INFO_IMAGE,
        MONO_PATCH_INFO_FIELD,
        MONO_PATCH_INFO_VTABLE,
+       MONO_PATCH_INFO_CLASS_INIT,
        MONO_PATCH_INFO_SFLDA,
        MONO_PATCH_INFO_LDSTR,
        MONO_PATCH_INFO_LDTOKEN,
@@ -639,6 +640,9 @@ MonoJitICallInfo *mono_find_jit_icall_by_addr  (gconstpointer addr);
 MonoJitICallInfo *mono_register_jit_icall      (gconstpointer func, const char *name, MonoMethodSignature *sig, gboolean is_save);
 gconstpointer     mono_icall_get_wrapper       (MonoJitICallInfo* callinfo);
 
+gpointer          mono_create_class_init_trampoline (MonoVTable *vtable);
+MonoVTable*       mono_find_class_init_trampoline_by_addr (gconstpointer addr);
+
 /* methods that must be provided by the arch-specific port */
 void      mono_arch_cpu_init                    (void);
 guint32   mono_arch_cpu_optimizazions           (guint32 *exclude_mask);
@@ -652,6 +656,7 @@ gpointer  mono_arch_get_throw_exception         (void);
 gpointer  mono_arch_get_throw_exception_by_name (void);
 gpointer  mono_arch_create_jit_trampoline       (MonoMethod *method);
 gpointer  mono_arch_create_jump_trampoline      (MonoMethod *method);
+gpointer  mono_arch_create_class_init_trampoline(MonoVTable *vtable);
 GList    *mono_arch_get_allocatable_int_vars    (MonoCompile *cfg);
 GList    *mono_arch_get_global_int_regs         (MonoCompile *cfg);
 void      mono_arch_patch_code                  (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji);
index 3309827c7eedc4661e80badddcadfb9249edd565..3a1ea64405ac3255befb0bd99acafe5ced67ca44 100644 (file)
 #include "mini.h"
 #include "mini-x86.h"
 
+typedef enum {
+       MONO_TRAMPOLINE_GENERIC,
+       MONO_TRAMPOLINE_JUMP,
+       MONO_TRAMPOLINE_CLASS_INIT
+} MonoTrampolineType;
+
 /* adapt to mini later... */
 #define mono_jit_share_code (1)
 
@@ -124,7 +130,7 @@ x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi,
                                *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5;
 #ifdef HAVE_VALGRIND_MEMCHECK_H
                                /* Tell valgrind to recompile the patched code */
-                               VALGRIND_DISCARD_TRANSLATIONS (code, code + 16);
+                               VALGRIND_DISCARD_TRANSLATIONS (code + 2, code + 6);
 #endif
                        }
                        return addr;
@@ -176,20 +182,65 @@ x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi,
        return addr;
 }
 
+/**
+ * x86_class_init_trampoline:
+ * @eax: saved x86 register 
+ * @ecx: saved x86 register 
+ * @edx: saved x86 register 
+ * @esi: saved x86 register 
+ * @edi: saved x86 register 
+ * @ebx: saved x86 register
+ * @code: pointer into caller code
+ * @vtable: the type to initialize
+ *
+ * This method calls mono_runtime_class_init () to run the static constructor
+ * for the type, then patches the caller code so it is not called again.
+ */
+static void
+x86_class_init_trampoline (int eax, int ecx, int edx, int esi, int edi, 
+                                                  int ebx, guint8 *code, MonoVTable *vtable)
+{
+       int i;
+
+       mono_runtime_class_init (vtable);
+
+       code -= 5;
+       if (code [0] == 0xe8) {
+               for (i = 0; i < 5; ++i)
+                       x86_nop (code);
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+               /* FIXME: the calltree skin trips on the self modifying code above */
+
+               /* Tell valgrind to recompile the patched code */
+               VALGRIND_DISCARD_TRANSLATIONS (code, code + 8);
+#endif
+       }
+       else
+               g_assert_not_reached ();
+}
+
 static guchar*
-create_trampoline_code (int is_jump)
+create_trampoline_code (MonoTrampolineType tramp_type)
 {
        guint8 *buf, *code;
        static guint8* generic_jump_trampoline = NULL;
-       
-       if (is_jump) {
-               if (generic_jump_trampoline)
-                       return generic_jump_trampoline;
-       } else {
+       static guint8 *generic_class_init_trampoline = NULL;
+
+       switch (tramp_type) {
+       case MONO_TRAMPOLINE_GENERIC:
                if (mono_generic_trampoline_code)
                        return mono_generic_trampoline_code;
+               break;
+       case MONO_TRAMPOLINE_JUMP:
+               if (generic_jump_trampoline)
+                       return generic_jump_trampoline;
+               break;
+       case MONO_TRAMPOLINE_CLASS_INIT:
+               if (generic_class_init_trampoline)
+                       return generic_class_init_trampoline;
+               break;
        }
-       
+
        code = buf = g_malloc (256);
        /* save caller save regs because we need to do a call */ 
        x86_push_reg (buf, X86_EDX);
@@ -199,7 +250,7 @@ create_trampoline_code (int is_jump)
        /* save LMF begin */
 
        /* save the IP (caller ip) */
-       if (is_jump)
+       if (tramp_type == MONO_TRAMPOLINE_JUMP)
                x86_push_imm (buf, 0);
        else
                x86_push_membase (buf, X86_ESP, 16);
@@ -224,7 +275,7 @@ create_trampoline_code (int is_jump)
        /* push the method info */
        x86_push_membase (buf, X86_ESP, 44);
        /* push the return address onto the stack */
-       if (is_jump)
+       if (tramp_type == MONO_TRAMPOLINE_JUMP)
                x86_push_imm (buf, 0);
        else
                x86_push_membase (buf, X86_ESP, 52);
@@ -237,7 +288,10 @@ create_trampoline_code (int is_jump)
        x86_push_membase (buf, X86_ESP, 64); /* ECX */
        x86_push_membase (buf, X86_ESP, 64); /* EAX */
 
-       x86_call_code (buf, x86_magic_trampoline);
+       if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
+               x86_call_code (buf, x86_class_init_trampoline);
+       else
+               x86_call_code (buf, x86_magic_trampoline);
        x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4);
 
        /* restore LMF start */
@@ -260,16 +314,27 @@ create_trampoline_code (int is_jump)
 
        x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16);
 
-       /* call the compiled method */
-       x86_jump_reg (buf, X86_EAX);
+       if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
+               x86_ret (buf);
+       else
+               /* call the compiled method */
+               x86_jump_reg (buf, X86_EAX);
 
        g_assert ((buf - code) <= 256);
 
-       if (is_jump) {
-               return generic_jump_trampoline = code;
-       } else {
-               return mono_generic_trampoline_code = code;
+       switch (tramp_type) {
+       case MONO_TRAMPOLINE_GENERIC:
+               mono_generic_trampoline_code = code;
+               break;
+       case MONO_TRAMPOLINE_JUMP:
+               generic_jump_trampoline = code;
+               break;
+       case MONO_TRAMPOLINE_CLASS_INIT:
+               generic_class_init_trampoline = code;
+               break;
        }
+
+       return code;
 }
 
 #define TRAMPOLINE_SIZE 10
@@ -302,7 +367,7 @@ mono_arch_create_jump_trampoline (MonoMethod *method)
 #endif
        }
        
-       tramp = create_trampoline_code (TRUE);
+       tramp = create_trampoline_code (MONO_TRAMPOLINE_JUMP);
 
        code = buf = g_malloc (TRAMPOLINE_SIZE);
        x86_push_imm (buf, method);
@@ -329,7 +394,7 @@ mono_arch_create_jump_trampoline (MonoMethod *method)
 gpointer
 mono_arch_create_jit_trampoline (MonoMethod *method)
 {
-       guint8 *code, *buf;
+       guint8 *code, *buf, *tramp;
 
        /* previously created trampoline code */
        if (method->info)
@@ -365,11 +430,11 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
        if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
                return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
 
-       create_trampoline_code (FALSE);
+       tramp = create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
 
        code = buf = g_malloc (TRAMPOLINE_SIZE);
        x86_push_imm (buf, method);
-       x86_jump_code (buf, mono_generic_trampoline_code);
+       x86_jump_code (buf, tramp);
        g_assert ((buf - code) <= TRAMPOLINE_SIZE);
 
        /* store trampoline address */
@@ -380,6 +445,34 @@ mono_arch_create_jit_trampoline (MonoMethod *method)
        return code;
 }
 
+/**
+ * mono_arch_create_class_init_trampoline:
+ *  @vtable: the type to initialize
+ *
+ * Creates a trampoline function to run a type initializer. 
+ * If the trampoline is called, it calls mono_runtime_class_init with the
+ * given vtable, then patches the caller code so it does not get called any
+ * more.
+ * 
+ * Returns: a pointer to the newly created code 
+ */
+gpointer
+mono_arch_create_class_init_trampoline (MonoVTable *vtable)
+{
+       guint8 *code, *buf, *tramp;
+
+       tramp = create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
+
+       code = buf = g_malloc (TRAMPOLINE_SIZE);
+       x86_push_imm (buf, vtable);
+       x86_jump_code (buf, tramp);
+       g_assert ((buf - code) <= TRAMPOLINE_SIZE);
+
+       mono_jit_stats.method_trampolines++;
+
+       return code;
+}
+
 /*
  * This method is only called when running in the Mono Debugger.
  */