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.
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;
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
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);
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);
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);
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;
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);
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))
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;
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 {
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;
info->name = name;
info->func = func;
info->sig = sig;
-
+
if (is_save
#ifdef MONO_USE_EXC_TABLES
|| mono_arch_has_unwind_info (func)
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 *
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: {
#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)))
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,
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);
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);
#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)
*((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;
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);
/* 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);
/* 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);
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 */
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
#endif
}
- tramp = create_trampoline_code (TRUE);
+ tramp = create_trampoline_code (MONO_TRAMPOLINE_JUMP);
code = buf = g_malloc (TRAMPOLINE_SIZE);
x86_push_imm (buf, method);
gpointer
mono_arch_create_jit_trampoline (MonoMethod *method)
{
- guint8 *code, *buf;
+ guint8 *code, *buf, *tramp;
/* previously created trampoline code */
if (method->info)
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 */
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.
*/