X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Faot.c;h=d367904f81e5396341f120338ee8f72dc24053de;hb=311dcbd59f854031a0c719bbc12056431e7108e0;hp=8316f9c1ebe4ee66a632c8169e6e9baad58dcf02;hpb=3eb19d5c1d4fe5d7c69b36b8740ef691ce9e47e6;p=mono.git diff --git a/mono/mini/aot.c b/mono/mini/aot.c index 8316f9c1ebe..d367904f81e 100644 --- a/mono/mini/aot.c +++ b/mono/mini/aot.c @@ -11,6 +11,7 @@ #include #include #include +#include #ifndef PLATFORM_WIN32 #include #else @@ -28,49 +29,232 @@ #include #include #include +#include +#include +#include +#include +#include #include "mini.h" -#define ENCODE_TYPE_POS(t,l) (((t) << 24) | (l)) -#define DECODE_TYPE(v) ((v) >> 24) -#define DECODE_POS(v) ((v) & 0xffffff) - #ifdef PLATFORM_WIN32 #define SHARED_EXT ".dll" +#elif defined(__ppc__) && defined(__MACH__) +#define SHARED_EXT ".dylib" #else #define SHARED_EXT ".so" #endif +#if defined(sparc) || defined(__ppc__) +#define AS_STRING_DIRECTIVE ".asciz" +#else +/* GNU as */ +#define AS_STRING_DIRECTIVE ".string" +#endif + +#define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) + +typedef struct MonoAotMethod { + MonoJitInfo *info; + MonoJumpInfo *patch_info; + MonoDomain *domain; +} MonoAotMethod; + +typedef struct MonoAotModule { + /* Optimization flags used to compile the module */ + guint32 opts; + /* Maps MonoMethods to MonoAotMethodInfos */ + MonoGHashTable *methods; + char **icall_table; + MonoImage **image_table; + guint32* methods_present_table; +} MonoAotModule; + +typedef struct MonoAotCompile { + FILE *fp; + GHashTable *ref_hash; + GHashTable *icall_hash; + GPtrArray *icall_table; + GHashTable *image_hash; + GPtrArray *image_table; +} MonoAotCompile; + +static MonoGHashTable *aot_modules; + +static CRITICAL_SECTION aot_mutex; + +/* + * 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; + +static MonoJitInfo* +mono_aot_load_method (MonoDomain *domain, MonoAotModule *aot_module, MonoMethod *method, guint8 *code, guint8 *info); + static MonoClass * -decode_class_info (gpointer *data) +decode_class_info (MonoAotModule *module, guint32 *data) { MonoImage *image; MonoClass *klass; - image = mono_image_loaded_by_guid ((char *)data [1]); + image = module->image_table [data [1]]; g_assert (image); if (data [0]) { - return mono_class_get (image, (guint32)data [0]); + return mono_class_get (image, data [0]); } else { - klass = decode_class_info (data [3]); - return mono_array_class_get (klass, (guint32)data [2]); + /* the pointer is dword aligned */ + klass = decode_class_info (module, *(guint32**)(ALIGN_PTR_TO (&data[3], sizeof (gpointer)))); + return mono_array_class_get (klass, data [2]); } return NULL; } + +static void +load_aot_module (MonoAssembly *assembly, gpointer user_data) +{ + char *aot_name; + MonoAotModule *info; + gboolean usable = TRUE; + char *saved_guid = NULL; + char *aot_version = NULL; + char *opt_flags = NULL; + + aot_name = g_strdup_printf ("%s%s", assembly->image->name, SHARED_EXT); + + assembly->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY); + + if (!assembly->aot_module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Failed to load AOT module %s: %s\n", aot_name, g_module_error ()); + g_free (aot_name); + return; + } + + g_module_symbol (assembly->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid); + g_module_symbol (assembly->aot_module, "mono_aot_version", (gpointer *) &aot_version); + g_module_symbol (assembly->aot_module, "mono_aot_opt_flags", (gpointer *)&opt_flags); + + if (!aot_version || strcmp (aot_version, MONO_AOT_FILE_VERSION)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Module %s has wrong file format version (expected %s got %s)\n", aot_name, MONO_AOT_FILE_VERSION, aot_version); + usable = FALSE; + } + else + if (!saved_guid || strcmp (assembly->image->guid, saved_guid)) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Module %s is out of date.\n", aot_name); + usable = FALSE; + } + + if (!usable) { + g_free (aot_name); + g_module_close (assembly->aot_module); + assembly->aot_module = NULL; + return; + } + + /* + * It seems that MonoGHashTables are in the GC heap, so structures + * containing them must be in the GC heap as well :( + */ +#ifdef HAVE_BOEHM_GC + info = GC_MALLOC (sizeof (MonoAotModule)); +#else + info = g_new0 (MonoAotModule, 1); +#endif + info->methods = mono_g_hash_table_new (NULL, NULL); + sscanf (opt_flags, "%d", &info->opts); + + /* Read image table */ + { + guint32 table_len, i; + char *table = NULL; + + g_module_symbol (assembly->aot_module, "mono_image_table", (gpointer *)&table); + g_assert (table); + + table_len = *(guint32*)table; + table += sizeof (guint32); + info->image_table = g_new0 (MonoImage*, table_len); + for (i = 0; i < table_len; ++i) { + info->image_table [i] = mono_image_loaded_by_guid (table); + if (!info->image_table [i]) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module %s is out of date.\n", aot_name); + mono_g_hash_table_destroy (info->methods); + g_free (info->image_table); +#ifndef HAVE_BOEHM_GC + g_free (info); +#endif + g_free (aot_name); + g_module_close (assembly->aot_module); + assembly->aot_module = NULL; + return; + } + table += strlen (table) + 1; + } + } + + /* Read icall table */ + { + guint32 table_len, i; + char *table = NULL; + + g_module_symbol (assembly->aot_module, "mono_icall_table", (gpointer *)&table); + g_assert (table); + + table_len = *(guint32*)table; + table += sizeof (guint32); + info->icall_table = g_new0 (char*, table_len); + for (i = 0; i < table_len; ++i) { + info->icall_table [i] = table; + table += strlen (table) + 1; + } + } + + /* Read methods present table */ + g_module_symbol (assembly->aot_module, "mono_methods_present_table", (gpointer *)&info->methods_present_table); + g_assert (info->methods_present_table); + + EnterCriticalSection (&aot_mutex); + mono_g_hash_table_insert (aot_modules, assembly, info); + LeaveCriticalSection (&aot_mutex); + + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Loaded AOT Module for %s.\n", assembly->image->name); +} + +void +mono_aot_init (void) +{ + InitializeCriticalSection (&aot_mutex); + + aot_modules = mono_g_hash_table_new (NULL, NULL); + + mono_install_assembly_load_hook (load_aot_module, NULL); + + if (getenv ("MONO_LASTAOT")) + mono_last_aot_method = atoi (getenv ("MONO_LASTAOT")); +} -gpointer -mono_aot_get_method (MonoMethod *method) +static MonoJitInfo * +mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method) { MonoClass *klass = method->klass; MonoAssembly *ass = klass->image->assembly; - MonoJumpInfo *patch_info = NULL; GModule *module = ass->aot_module; - char *method_label, *info_label; + char method_label [256]; + char info_label [256]; guint8 *code = NULL; - gpointer *info; - guint code_len, used_int_regs, used_strings; + guint8 *info; + MonoAotModule *aot_module; + MonoAotMethod *minfo; + MonoJitInfo *jinfo; + MonoMethodHeader *header = ((MonoMethodNormal*)method)->header; int i; if (!module) @@ -79,84 +263,305 @@ mono_aot_get_method (MonoMethod *method) if (!method->token) return NULL; + if (mono_profiler_get_events () & MONO_PROFILE_ENTER_LEAVE) + return NULL; + + if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || + (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) + return NULL; + + aot_module = (MonoAotModule*)mono_g_hash_table_lookup (aot_modules, ass); + g_assert (klass->inited); - method_label = g_strdup_printf ("method_%08X", method->token); + minfo = mono_g_hash_table_lookup (aot_module->methods, method); + /* Can't use code from non-root domains since they can be unloaded */ + if (minfo && (minfo->domain == mono_get_root_domain ())) { + /* This method was already loaded in another appdomain */ + + /* Duplicate jinfo */ + jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo)); + memcpy (jinfo, minfo->info, sizeof (MonoJitInfo)); + if (jinfo->clauses) { + jinfo->clauses = + mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses); + memcpy (jinfo->clauses, minfo->info->clauses, sizeof (MonoJitExceptionInfo) * header->num_clauses); + } - if (!g_module_symbol (module, method_label, (gpointer *)&code)) { - g_free (method_label); - return NULL; + if (aot_module->opts & MONO_OPT_SHARED) + /* Use the same method in the new appdomain */ + ; + else if (!minfo->patch_info) + /* Use the same method in the new appdomain */ + ; + else { + /* Create a copy of the original method and apply relocations */ + + code = mono_code_manager_reserve (domain->code_mp, minfo->info->code_size); + memcpy (code, minfo->info->code_start, minfo->info->code_size); + + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT REUSE METHOD: %s %p - %p.\n", mono_method_full_name (method, TRUE), code, (char*)code + minfo->info->code_size); + + /* Do this outside the lock to avoid deadlocks */ + LeaveCriticalSection (&aot_mutex); + mono_arch_patch_code (method, domain, code, minfo->patch_info, TRUE); + EnterCriticalSection (&aot_mutex); + mono_arch_flush_icache (code, minfo->info->code_size); + + /* Relocate jinfo */ + jinfo->code_start = code; + if (jinfo->clauses) { + for (i = 0; i < header->num_clauses; ++i) { + MonoJitExceptionInfo *ei = &jinfo->clauses [i]; + gint32 offset = code - (guint8*)minfo->info->code_start; + + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) + ei->data.filter = (guint8*)ei->data.filter + offset; + ei->try_start = (guint8*)ei->try_start + offset; + ei->try_end = (guint8*)ei->try_end + offset; + ei->handler_start = (guint8*)ei->handler_start + offset; + } + } + } + + return jinfo; } - info_label = g_strdup_printf ("%s_patch_info", method_label); - if (!g_module_symbol (module, info_label, (gpointer *)&info)) { - g_free (method_label); - g_free (info_label); + /* Do a fast check to see whenever the method exists */ + { + guint32 index = mono_metadata_token_index (method->token) - 1; + guint32 w; + w = aot_module->methods_present_table [index / 32]; + if (! (w & (1 << (index % 32)))) { + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT NOT FOUND: %s.\n", mono_method_full_name (method, TRUE)); + return NULL; + } + } + + sprintf (method_label, "m_%x", mono_metadata_token_index (method->token)); + + if (!g_module_symbol (module, method_label, (gpointer *)&code)) + return NULL; + + sprintf (info_label, "%s_p", method_label); + + if (!g_module_symbol (module, info_label, (gpointer *)&info)) return NULL; + + if (mono_last_aot_method != -1) { + if (mono_jit_stats.methods_aot > mono_last_aot_method) + return NULL; + 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); } - //printf ("FOUND AOT compiled code for %s %p %p\n", mono_method_full_name (method, TRUE), code, info); + return mono_aot_load_method (domain, aot_module, method, code, info); +} + +static MonoJitInfo* +mono_aot_load_method (MonoDomain *domain, MonoAotModule *aot_module, MonoMethod *method, guint8 *code, guint8 *info) +{ + MonoClass *klass = method->klass; + MonoJumpInfo *patch_info = NULL; + guint code_len, used_int_regs, used_strings; + MonoAotMethod *minfo; + MonoJitInfo *jinfo; + MonoMethodHeader *header = ((MonoMethodNormal*)method)->header; + GPtrArray *patches; + int i, pindex; + +#ifdef HAVE_BOEHM_GC + minfo = GC_MALLOC (sizeof (MonoAotMethod)); +#else + minfo = g_new0 (MonoAotMethod, 1); +#endif + + minfo->domain = domain; + jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo)); - code_len = GPOINTER_TO_UINT (*((gpointer **)info)); - info++; - used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info)); - info++; - used_strings = GPOINTER_TO_UINT (*((gpointer **)info)); - info++; + code_len = *(guint32*)info; + info += 4; + used_int_regs = *(guint32*)info; + info += 4; - for (i = 0; i < used_strings; i++) { - guint token = GPOINTER_TO_UINT (*((gpointer **)info)); - info++; - mono_ldstr (mono_root_domain, klass->image, mono_metadata_token_index (token)); + if (!use_loaded_code) { + guint8 *code2; + code2 = mono_code_manager_reserve (domain->code_mp, code_len); + memcpy (code2, code, code_len); + mono_arch_flush_icache (code2, code_len); + code = code2; } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT FOUND AOT compiled code for %s %p - %p %p\n", mono_method_full_name (method, TRUE), code, code + code_len, info); + + /* Exception table */ + if (header->num_clauses) { + jinfo->clauses = + mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses); + jinfo->num_clauses = header->num_clauses; + + jinfo->exvar_offset = *(guint32*)info; + info += 4; + + for (i = 0; i < header->num_clauses; ++i) { + MonoExceptionClause *ec = &header->clauses [i]; + MonoJitExceptionInfo *ei = &jinfo->clauses [i]; + + ei->flags = ec->flags; + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) + ei->data.filter = code + *(guint32*)info; + else + ei->data.token = *(guint32*)info; + info += 4; + ei->try_start = code + *(guint32*)info; + info += 4; + ei->try_end = code + *(guint32*)info; + info += 4; + ei->handler_start = code + *(guint32*)info; + info += 4; + } + } - if (info) { - MonoMemPool *mp = mono_mempool_new (); + if (aot_module->opts & MONO_OPT_SHARED) { + used_strings = *(guint32*)info; + info += 4; + } + else + used_strings = 0; + + for (i = 0; i < used_strings; i++) { + guint token = *(guint32*)info; + info += 4; + mono_ldstr (mono_get_root_domain (), klass->image, mono_metadata_token_index (token)); + } + + if (*info) { + MonoMemPool *mp; MonoImage *image; guint8 *page_start; gpointer *table; int pages; int i, err; + guint32 last_offset, buf_len; + guint32 *data; + + if (aot_module->opts & MONO_OPT_SHARED) + mp = mono_mempool_new (); + else + mp = domain->mp; + /* First load the type + offset table */ + last_offset = 0; + patches = g_ptr_array_new (); while (*info) { MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo)); - gpointer *data = *((gpointer **)info); - info++; - ji->type = DECODE_TYPE (GPOINTER_TO_UINT (*info)); - ji->ip.i = DECODE_POS (GPOINTER_TO_UINT (*info)); + + guint8 b1, b2; + + b1 = *(guint8*)info; + b2 = *((guint8*)info + 1); + + info += 2; + + ji->type = b1 >> 2; + + if (((b1 & (1 + 2)) == 3) && (b2 == 255)) { + info = ALIGN_PTR_TO (info, 4); + ji->ip.i = *(guint32*)info; + info += 4; + } + else + ji->ip.i = (((guint32)(b1 & (1 + 2))) << 8) + b2; + + ji->ip.i += last_offset; + last_offset = ji->ip.i; + //printf ("T: %d O: %d.\n", ji->type, ji->ip.i); + + ji->next = patch_info; + patch_info = ji; + + g_ptr_array_add (patches, ji); + } + info ++; + + info = ALIGN_PTR_TO (info, sizeof (gpointer)); + + /* Then load the other data */ + for (pindex = 0; pindex < patches->len; ++pindex) { + MonoJumpInfo *ji = g_ptr_array_index (patches, pindex); + + data = *((guint32 **)info); + info += sizeof (gpointer); switch (ji->type) { case MONO_PATCH_INFO_CLASS: - ji->data.klass = decode_class_info (data); + case MONO_PATCH_INFO_IID: + ji->data.klass = decode_class_info (aot_module, data); + g_assert (ji->data.klass); + 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); break; case MONO_PATCH_INFO_IMAGE: - ji->data.image = mono_image_loaded_by_guid ((char *)data); + ji->data.image = aot_module->image_table [(guint32)data]; g_assert (ji->data.image); break; case MONO_PATCH_INFO_METHOD: case MONO_PATCH_INFO_METHODCONST: - image = mono_image_loaded_by_guid ((char *)data [1]); - g_assert (image); - ji->data.method = mono_get_method (image, (guint32)data [0], NULL); + case MONO_PATCH_INFO_METHOD_JUMP: { + guint32 image_index, token; + + image_index = (guint32)data >> 24; + token = MONO_TOKEN_METHOD_DEF | ((guint32)data & 0xffffff); + + image = aot_module->image_table [image_index]; + ji->data.method = mono_get_method (image, token, NULL); + g_assert (ji->data.method); + mono_class_init (ji->data.method->klass); + + break; + } + case MONO_PATCH_INFO_WRAPPER: { + guint32 image_index, token; + guint32 wrapper_type; + + wrapper_type = (guint32)data[0]; + image_index = (guint32)data[1] >> 24; + token = MONO_TOKEN_METHOD_DEF | ((guint32)data[1] & 0xffffff); + + image = aot_module->image_table [image_index]; + ji->data.method = mono_get_method (image, token, NULL); g_assert (ji->data.method); mono_class_init (ji->data.method->klass); + + g_assert (wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK); + ji->type = MONO_PATCH_INFO_METHOD; + ji->data.method = mono_marshal_get_remoting_invoke_with_check (ji->data.method); break; + } case MONO_PATCH_INFO_FIELD: - image = mono_image_loaded_by_guid ((char *)data [1]); - g_assert (image); - ji->data.field = mono_field_from_token (image, (guint32)data [0], NULL); - mono_class_init (ji->data.field->parent); - g_assert (ji->data.field); + case MONO_PATCH_INFO_SFLDA: { + /* The pointer is dword aligned */ + gpointer class_ptr = *(gpointer*)(ALIGN_PTR_TO (&data[1], sizeof (gpointer))); + MonoClass *klass = decode_class_info (aot_module, class_ptr); + mono_class_init (klass); + ji->data.field = mono_class_get_field (klass, data [0]); break; + } case MONO_PATCH_INFO_INTERNAL_METHOD: - ji->data.name = (char *)data; + ji->data.name = aot_module->icall_table [(guint32)data]; g_assert (ji->data.name); + //printf ("A: %s.\n", ji->data.name); break; case MONO_PATCH_INFO_SWITCH: - ji->table_size = (int)data [0]; + ji->table_size = data [0]; table = g_new (gpointer, ji->table_size); ji->data.target = table; for (i = 0; i < ji->table_size; i++) { @@ -167,48 +572,129 @@ mono_aot_get_method (MonoMethod *method) case MONO_PATCH_INFO_R8: ji->data.target = data; break; + case MONO_PATCH_INFO_LDSTR: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + image = aot_module->image_table [data [0]]; + ji->data.token = mono_jump_info_token_new (mp, image, data [1]); + break; + case MONO_PATCH_INFO_EXC_NAME: + ji->data.klass = decode_class_info (aot_module, data); + g_assert (ji->data.klass); + mono_class_init (ji->data.klass); + ji->data.name = ji->data.klass->name; + break; + case MONO_PATCH_INFO_METHOD_REL: + ji->data.offset = data [0]; + break; default: g_warning ("unhandled type %d", ji->type); g_assert_not_reached (); } - - info++; - ji->next = patch_info; - patch_info = ji; } -#ifndef PLATFORM_WIN32 + g_ptr_array_free (patches, TRUE); + + info += 4; + buf_len = *(guint32*)info; + info += 4; + mono_debug_add_aot_method (domain, method, code, info, buf_len); + + 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); + mono_arch_patch_code (method, domain, code, patch_info, TRUE); + EnterCriticalSection (&aot_mutex); - mono_arch_patch_code (method, mono_root_domain, code, patch_info); - mono_mempool_destroy (mp); + if (aot_module->opts & MONO_OPT_SHARED) + /* No need to cache patches */ + mono_mempool_destroy (mp); + else + minfo->patch_info = patch_info; } - g_free (info_label); - g_free (method_label); + mono_jit_stats.methods_aot++; { - MonoJitInfo *info; - info = mono_mempool_alloc0 (mono_root_domain->mp, sizeof (MonoJitInfo)); - info->code_size = code_len; - info->used_regs = used_int_regs; - info->method = method; - info->code_start = code; - mono_jit_info_table_add (mono_root_domain, info); + jinfo->code_size = code_len; + jinfo->used_regs = used_int_regs; + jinfo->method = method; + jinfo->code_start = code; + jinfo->domain_neutral = (aot_module->opts & MONO_OPT_SHARED) != 0; + + minfo->info = jinfo; + mono_g_hash_table_insert (aot_module->methods, method, minfo); + + return jinfo; } - mono_jit_stats.methods_aot++; +} - return code; +MonoJitInfo* +mono_aot_get_method (MonoDomain *domain, MonoMethod *method) +{ + MonoJitInfo *info; + + EnterCriticalSection (&aot_mutex); + info = mono_aot_get_method_inner (domain, method); + LeaveCriticalSection (&aot_mutex); + + /* Do this outside the lock */ + if (info) { + mono_jit_info_table_add (domain, info); + return info; + } + else + return NULL; +} + +static void +emit_section_change (FILE *fp, const char *section_name, int subsection_index) +{ +#if defined(sparc) + /* For solaris as, GNU as should accept the same */ + fprintf (fp, ".section \"%s\"\n", section_name); +#elif defined(__ppc__) && defined(__MACH__) + /* This needs to be made more precise on mach. */ + fprintf (fp, "%s\n", subsection_index == 0 ? ".text" : ".data"); +#else + fprintf (fp, "%s %d\n", section_name, subsection_index); +#endif +} + +static void +emit_global (FILE *fp, const char *name) +{ +#if defined(__ppc__) && defined(__MACH__) + // mach-o always uses a '_' prefix. + fprintf (fp, ".globl _%s\n", name); +#else + fprintf (fp, ".globl %s\n", name); +#endif +} + +static void +emit_label (FILE *fp, const char *name) +{ +#if defined(__ppc__) && defined(__MACH__) + // mach-o always uses a '_' prefix. + fprintf (fp, "_%s:\n", name); +#else + fprintf (fp, "%s:\n", name); +#endif } #if 0 @@ -217,9 +703,11 @@ write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align) { int i; + emit_section_change (fp, ".text", 1); + fprintf (fp, ".globl %s\n", name); - fprintf (fp, ".data\n\t.align %d\n", align); - fprintf (fp, "\t.type %s,@object\n", name); + fprintf (fp, "\t.align %d\n", align); + fprintf (fp, "\t.type %s,#object\n", name); fprintf (fp, "\t.size %s,%d\n", name, size); fprintf (fp, "%s:\n", name); for (i = 0; i < size; i++) { @@ -230,12 +718,12 @@ write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align) #endif static void -write_string_symbol (FILE *fp, const char *name, char *value) +write_string_symbol (FILE *fp, const char *name, const char *value) { - fprintf (fp, ".globl %s\n", name); - fprintf (fp, ".data\n"); - fprintf (fp, "%s:\n", name); - fprintf (fp, "\t.string \"%s\"\n", value); + emit_section_change (fp, ".text", 1); + emit_global(fp, name); + emit_label(fp, name); + fprintf (fp, "\t%s \"%s\"\n", AS_STRING_DIRECTIVE, value); } static guint32 @@ -253,132 +741,488 @@ mono_get_field_token (MonoClassField *field) return 0; } -static char * -cond_emit_image_label (FILE *tmpfp, GHashTable *image_hash, MonoImage *image) +static guint32 +get_image_index (MonoAotCompile *cfg, MonoImage *image) { - char *label; - - if ((label = g_hash_table_lookup (image_hash, image))) - return label; - - label = g_strdup_printf ("image_patch_info_%p", image); - fprintf (tmpfp, "%s:\n", label); - fprintf (tmpfp, "\t.string \"%s\"\n", image->guid); - - g_hash_table_insert (image_hash, image, label); - - return label; + guint32 index; + + index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image)); + if (index) + return index - 1; + else { + index = g_hash_table_size (cfg->image_hash); + g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1)); + g_ptr_array_add (cfg->image_table, image); + return index; + } } -static char * -cond_emit_icall_label (FILE *tmpfp, GHashTable *hash, const char *icall_name) +static void +emit_image_index (MonoAotCompile *cfg, MonoImage *image) { - char *label; + guint32 image_index; - if ((label = g_hash_table_lookup (hash, icall_name))) - return label; + image_index = get_image_index (cfg, image); - label = g_strdup_printf ("icall_patch_info_%s", icall_name); - fprintf (tmpfp, "%s:\n", label); - fprintf (tmpfp, "\t.string \"%s\"\n", icall_name); - - g_hash_table_insert (hash, (gpointer)icall_name, label); - - return label; + fprintf (cfg->fp, "\t.long %d\n", image_index); } -static char * -cond_emit_method_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info) +#if defined(__ppc__) && defined(__MACH__) +static int +ilog2(register int value) { - MonoMethod *method = patch_info->data.method; - char *l1, *l2; + int count = -1; + while (value & ~0xf) count += 4, value >>= 4; + while (value) count++, value >>= 1; + return count; +} +#endif - if ((l1 = g_hash_table_lookup (hash, method))) - return l1; - - l2 = cond_emit_image_label (tmpfp, hash, method->klass->image); - fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); - l1 = g_strdup_printf ("method_patch_info_%08x_%p", method->token, method); - fprintf (tmpfp, "%s:\n", l1); - fprintf (tmpfp, "\t.long 0x%08x\n", method->token); - g_assert (method->token); - fprintf (tmpfp, "\t.long %s\n", l2); - - g_hash_table_insert (hash, method, l1); +static void emit_alignment(FILE *fp, int size) +{ +#if defined(__ppc__) && defined(__MACH__) + // the mach-o assembler specifies alignments as powers of 2. + fprintf (fp, "\t.align %d\t; ilog2\n", ilog2(size)); +#elif defined(__powerpc__) + /* ignore on linux/ppc */ +#else + fprintf (fp, "\t.align %d\n", size); +#endif +} - return l1; +static void +emit_pointer (FILE *fp, const char *target) +{ + emit_alignment (fp, sizeof (gpointer)); +#if defined(sparc) && SIZEOF_VOID_P == 8 + fprintf (fp, "\t.xword %s\n", target); +#else + fprintf (fp, "\t.long %s\n", target); +#endif } static char * -cond_emit_klass_label (FILE *tmpfp, GHashTable *hash, MonoClass *klass) +cond_emit_klass_label (MonoAotCompile *cfg, MonoClass *klass) { - char *l1, *l2, *el = NULL; + char *l1, *el = NULL; - if ((l1 = g_hash_table_lookup (hash, klass))) + if ((l1 = g_hash_table_lookup (cfg->ref_hash, klass))) return l1; if (!klass->type_token) { g_assert (klass->rank > 0); - el = cond_emit_klass_label (tmpfp, hash, klass->element_class); + el = cond_emit_klass_label (cfg, klass->element_class); } - l2 = cond_emit_image_label (tmpfp, hash, klass->image); - fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); - l1 = g_strdup_printf ("klass_patch_info_%08x_%p", klass->type_token, klass); - fprintf (tmpfp, "%s:\n", l1); - fprintf (tmpfp, "\t.long 0x%08x\n", klass->type_token); - fprintf (tmpfp, "\t.long %s\n", l2); + emit_alignment(cfg->fp, sizeof (gpointer)); + + l1 = g_strdup_printf ("klass_p_%08x_%p", klass->type_token, klass); + fprintf (cfg->fp, "%s:\n", l1); + fprintf (cfg->fp, "\t.long 0x%08x\n", klass->type_token); + emit_image_index (cfg, klass->image); if (el) { - fprintf (tmpfp, "\t.long %d\n", klass->rank); - fprintf (tmpfp, "\t.long %s\n", el); + fprintf (cfg->fp, "\t.long %d\n", klass->rank); + emit_pointer (cfg->fp, el); } - g_hash_table_insert (hash, klass, l1); + g_hash_table_insert (cfg->ref_hash, klass, l1); return l1; } static char * -cond_emit_field_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info) +cond_emit_field_label (MonoAotCompile *cfg, MonoJumpInfo *patch_info) { MonoClassField *field = patch_info->data.field; char *l1, *l2; guint token; - if ((l1 = g_hash_table_lookup (hash, field))) + if ((l1 = g_hash_table_lookup (cfg->ref_hash, field))) return l1; - - l2 = cond_emit_image_label (tmpfp, hash, field->parent->image); - fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); + + l2 = cond_emit_klass_label (cfg, field->parent); + emit_alignment(cfg->fp, sizeof (gpointer)); token = mono_get_field_token (field); - l1 = g_strdup_printf ("klass_patch_info_%08x_%p", token, field); - fprintf (tmpfp, "%s:\n", l1); - fprintf (tmpfp, "\t.long 0x%08x\n", token); g_assert (token); - fprintf (tmpfp, "\t.long %s\n", l2); + l1 = g_strdup_printf ("klass_p_%08x_%p", token, field); + fprintf (cfg->fp, "%s:\n", l1); + fprintf (cfg->fp, "\t.long 0x%08x\n", token); + emit_pointer (cfg->fp, l2); - g_hash_table_insert (hash, field, l1); + g_hash_table_insert (cfg->ref_hash, field, l1); return l1; } +static gint +compare_patches (gconstpointer a, gconstpointer b) +{ + int i, j; + + i = (*(MonoJumpInfo**)a)->ip.i; + j = (*(MonoJumpInfo**)b)->ip.i; + + if (i < j) + return -1; + else + if (i > j) + return 1; + else + return 0; +} + +static void +emit_method (MonoAotCompile *acfg, MonoCompile *cfg) +{ + MonoMethod *method; + GList *l; + FILE *tmpfp; + int i, j, k, pindex; + guint8 *code, *mname, *mname_p; + int func_alignment = 16; + GPtrArray *patches; + MonoJumpInfo *patch_info; + MonoMethodHeader *header; + + tmpfp = acfg->fp; + method = cfg->method; + code = cfg->native_code; + header = ((MonoMethodNormal*)method)->header; + + emit_section_change (tmpfp, ".text", 0); + mname = g_strdup_printf ("m_%x", mono_metadata_token_index (method->token)); + mname_p = g_strdup_printf ("%s_p", mname); + emit_alignment(tmpfp, func_alignment); + emit_global(tmpfp, mname); +#if defined(sparc) + fprintf (tmpfp, "\t.type %s,#function\n", mname); +#elif !(defined(__ppc__) && defined(__MACH__)) + fprintf (tmpfp, "\t.type %s,@function\n", mname); +#endif + emit_label(tmpfp, mname); + + for (i = 0; i < cfg->code_len; i++) + fprintf (tmpfp, ".byte %d\n", (unsigned int) code [i]); + + emit_section_change (tmpfp, ".text", 1); + + /* Sort relocations */ + patches = g_ptr_array_new (); + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) + g_ptr_array_add (patches, patch_info); + g_ptr_array_sort (patches, compare_patches); + + /* Emit out of line info for relocations */ + j = 0; + for (pindex = 0; pindex < patches->len; ++pindex) { + patch_info = g_ptr_array_index (patches, pindex); + switch (patch_info->type) { + case MONO_PATCH_INFO_LABEL: + case MONO_PATCH_INFO_BB: + /* relative jumps are no problem, there is no need to handle then here */ + break; + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = (gpointer *)patch_info->data.target; + int k; + + emit_alignment(tmpfp, sizeof (gpointer)); + fprintf (tmpfp, "%s_p_%d:\n", mname, j); + fprintf (tmpfp, "\t.long %d\n", patch_info->table_size); + + for (k = 0; k < patch_info->table_size; k++) { + fprintf (tmpfp, "\t.long %d\n", (int)table [k]); + } + j++; + break; + } + case MONO_PATCH_INFO_INTERNAL_METHOD: { + guint32 icall_index; + + icall_index = (guint32)g_hash_table_lookup (acfg->icall_hash, patch_info->data.name); + if (!icall_index) { + icall_index = g_hash_table_size (acfg->icall_hash) + 1; + g_hash_table_insert (acfg->icall_hash, (gpointer)patch_info->data.name, + GUINT_TO_POINTER (icall_index)); + g_ptr_array_add (acfg->icall_table, (gpointer)patch_info->data.name); + } + patch_info->data.name = g_strdup_printf ("%d", icall_index - 1); + j++; + break; + } + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHOD_JUMP: { + /* + * The majority of patches are for methods, so we emit + * them inline instead of defining a label for them to + * decrease the number of relocations. + */ + guint32 image_index = get_image_index (acfg, patch_info->data.method->klass->image); + guint32 token = patch_info->data.method->token; + g_assert (image_index < 256); + g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); + + patch_info->data.name = + g_strdup_printf ("%d", (image_index << 24) + (mono_metadata_token_index (token))); + j++; + break; + } + case MONO_PATCH_INFO_WRAPPER: { + MonoMethod *m; + guint32 image_index; + guint32 token; + + m = mono_marshal_method_from_wrapper (patch_info->data.method); + image_index = get_image_index (acfg, m->klass->image); + token = m->token; + g_assert (image_index < 256); + g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); + + emit_alignment(tmpfp, sizeof (gpointer)); + fprintf (tmpfp, "%s_p_%d:\n", mname, j); + fprintf (tmpfp, "\t.long %d\n", patch_info->data.method->wrapper_type); + fprintf (tmpfp, "\t.long %d\n", (image_index << 24) + (mono_metadata_token_index (token))); + j++; + break; + } + case MONO_PATCH_INFO_FIELD: + patch_info->data.name = cond_emit_field_label (acfg, patch_info); + j++; + break; + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass); + j++; + break; + case MONO_PATCH_INFO_IMAGE: + patch_info->data.name = g_strdup_printf ("%d", get_image_index (acfg, patch_info->data.image)); + j++; + break; + case MONO_PATCH_INFO_EXC_NAME: { + MonoClass *ex_class; + + ex_class = + mono_class_from_name (mono_defaults.exception_class->image, + "System", patch_info->data.target); + g_assert (ex_class); + patch_info->data.name = cond_emit_klass_label (acfg, ex_class); + j++; + break; + } + case MONO_PATCH_INFO_R4: + emit_alignment(tmpfp, 8); + fprintf (tmpfp, "%s_p_%d:\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target)); + j++; + break; + case MONO_PATCH_INFO_R8: + emit_alignment(tmpfp, 8); + fprintf (tmpfp, "%s_p_%d:\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target)); + fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1)); + j++; + break; + case MONO_PATCH_INFO_METHOD_REL: + emit_alignment(tmpfp, sizeof (gpointer)); + fprintf (tmpfp, "%s_p_%d:\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.offset); + 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_SFLDA: + patch_info->data.name = cond_emit_field_label (acfg, patch_info); + j++; + break; + case MONO_PATCH_INFO_LDSTR: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + emit_alignment(tmpfp, 8); + fprintf (tmpfp, "%s_p_%d:\n", mname, j); + fprintf (tmpfp, "\t.long 0x%08x\n", get_image_index (acfg, patch_info->data.token->image)); + fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.token->token); + j++; + break; + default: + g_warning ("unable to handle jump info %d", patch_info->type); + g_assert_not_reached (); + } + } + + emit_global (tmpfp, mname_p); + emit_alignment (tmpfp, sizeof (gpointer)); + emit_label (tmpfp, mname_p); + + fprintf (tmpfp, "\t.long %d\n", cfg->code_len); + fprintf (tmpfp, "\t.long %ld\n", (long)cfg->used_int_regs); + + /* Exception table */ + if (header->num_clauses) { + MonoJitInfo *jinfo = cfg->jit_info; + + fprintf (tmpfp, "\t.long %d\n", jinfo->exvar_offset); + + for (k = 0; k < header->num_clauses; ++k) { + MonoJitExceptionInfo *ei = &jinfo->clauses [k]; + + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) + fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->data.filter - code); + else + /* fixme: tokens are not global */ + fprintf (tmpfp, "\t.long %d\n", ei->data.token); + + fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_start - code); + fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_end - code); + fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->handler_start - code); + } + } + + /* String table */ + if (cfg->opt & MONO_OPT_SHARED) { + fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list)); + for (l = cfg->ldstr_list; l; l = l->next) { + fprintf (tmpfp, "\t.long 0x%08lx\n", (long)l->data); + } + } + else + /* Used only in shared mode */ + g_assert (!cfg->ldstr_list); + + //printf ("M: %s (%s).\n", mono_method_full_name (method, TRUE), mname); + + if (j) { + guint32 last_offset; + last_offset = 0; + + j = 0; + + /* First emit the type+position table */ + for (pindex = 0; pindex < patches->len; ++pindex) { + guint32 offset; + patch_info = g_ptr_array_index (patches, pindex); + + if ((patch_info->type == MONO_PATCH_INFO_LABEL) || + (patch_info->type == MONO_PATCH_INFO_BB)) + /* Nothing to do */ + continue; + + //printf ("T: %d O: %d.\n", patch_info->type, patch_info->ip.i); + offset = patch_info->ip.i - last_offset; + last_offset = patch_info->ip.i; + + /* Encode type+position compactly */ + g_assert (patch_info->type < 64); + if (offset < 1024 - 1) { + fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + (offset >> 8)); + fprintf (tmpfp, "\t.byte %d\n", offset & ((1 << 8) - 1)); + } + else { + fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + 3); + fprintf (tmpfp, "\t.byte %d\n", 255); + emit_alignment(tmpfp, 4); + fprintf (tmpfp, "\t.long %d\n", offset); + } + } + + /* + * 0 is PATCH_INFO_BB, which can't be in the file. + */ + /* NULL terminated array */ + fprintf (tmpfp, "\t.byte 0\n"); + + emit_alignment (tmpfp, sizeof (gpointer)); + + /* Then emit the other info */ + for (pindex = 0; pindex < patches->len; ++pindex) { + patch_info = g_ptr_array_index (patches, pindex); + + if ((patch_info->type == MONO_PATCH_INFO_LABEL) || + (patch_info->type == MONO_PATCH_INFO_BB)) + /* Nothing to do */ + continue; + + switch (patch_info->type) { + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHOD_JUMP: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_FIELD: + 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: + emit_pointer (tmpfp, patch_info->data.name); + j++; + break; + case MONO_PATCH_INFO_SWITCH: + case MONO_PATCH_INFO_R4: + case MONO_PATCH_INFO_R8: + case MONO_PATCH_INFO_METHOD_REL: + case MONO_PATCH_INFO_LDSTR: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + case MONO_PATCH_INFO_WRAPPER: { + char buf [256]; + sprintf (buf, "%s_p_%d", mname, j); + emit_pointer (tmpfp, buf); + j++; + break; + } + case MONO_PATCH_INFO_LABEL: + case MONO_PATCH_INFO_BB: + break; + default: + g_warning ("unable to handle jump info %d", patch_info->type); + g_assert_not_reached (); + } + + } + } + + { + guint8 *buf; + guint32 buf_len; + + mono_debug_serialize_debug_info (cfg, &buf, &buf_len); + + fprintf (tmpfp, "\t.long %d\n", buf_len); + + for (i = 0; i < buf_len; ++i) + fprintf (tmpfp, ".byte %d\n", (unsigned int) buf [i]); + + if (buf_len > 0) + g_free (buf); + } + + /* fixme: save the rest of the required infos */ + + g_free (mname); + g_free (mname_p); +} + int mono_compile_assembly (MonoAssembly *ass, guint32 opts) { MonoCompile *cfg; MonoImage *image = ass->image; MonoMethod *method; - GList *l; - char *com, *tmpfname; + char *com, *tmpfname, *opts_str; FILE *tmpfp; - int i, j; - guint8 *code, *mname; - int ccount = 0, mcount = 0, lmfcount = 0, ecount = 0, abscount = 0, wrappercount = 0, ocount = 0; + int i; + guint8 *symbol; + int ccount = 0, mcount = 0, lmfcount = 0, abscount = 0, wrappercount = 0, ocount = 0; GHashTable *ref_hash; - int func_alignment = 16; + MonoAotCompile *acfg; + gboolean *emitted; - printf ("Mono AOT compiler - compiling assembly %s\n", image->name); + printf ("Mono Ahead of Time compiler - compiling assembly %s\n", image->name); i = g_file_open_tmp ("mono_aot_XXXXXX", &tmpfname, NULL); tmpfp = fdopen (i, "w+"); @@ -386,8 +1230,24 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts) ref_hash = g_hash_table_new (NULL, NULL); + acfg = g_new0 (MonoAotCompile, 1); + acfg->fp = tmpfp; + acfg->ref_hash = ref_hash; + acfg->icall_hash = g_hash_table_new (NULL, NULL); + acfg->icall_table = g_ptr_array_new (); + acfg->image_hash = g_hash_table_new (NULL, NULL); + acfg->image_table = g_ptr_array_new (); + write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid); - + + write_string_symbol (tmpfp, "mono_aot_version", MONO_AOT_FILE_VERSION); + + opts_str = g_strdup_printf ("%d", opts); + write_string_symbol (tmpfp, "mono_aot_opt_flags", opts_str); + g_free (opts_str); + + emitted = g_new0 (gboolean, image->tables [MONO_TABLE_METHOD].rows); + for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { MonoJumpInfo *patch_info; gboolean skip; @@ -412,20 +1272,14 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts) continue; } - /* fixme: add methods with exception tables */ - if (((MonoMethodNormal *)method)->header->num_clauses) { - //printf ("Skip (exceptions): %s\n", mono_method_full_name (method, TRUE)); - ecount++; - continue; - } - //printf ("START: %s\n", mono_method_full_name (method, TRUE)); //mono_compile_method (method); - cfg = mini_method_compile (method, opts, mono_root_domain, 0); + cfg = mini_method_compile (method, opts, mono_get_root_domain (), FALSE, 0); g_assert (cfg); if (cfg->disable_aot) { + printf ("Skip (other): %s\n", mono_method_full_name (method, TRUE)); ocount++; continue; } @@ -445,13 +1299,20 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts) continue; } + /* remoting-invoke-with-check wrappers are very common */ + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { + if ((patch_info->type == MONO_PATCH_INFO_METHOD) && + ((patch_info->data.method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK))) + patch_info->type = MONO_PATCH_INFO_WRAPPER; + } + skip = FALSE; for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { if ((patch_info->type == MONO_PATCH_INFO_METHOD || patch_info->type == MONO_PATCH_INFO_METHODCONST) && patch_info->data.method->wrapper_type) { /* unable to handle this */ - //printf ("Skip (wrapper call): %s %d\n", mono_method_full_name (method, TRUE), patch_info->type); + //printf ("Skip (wrapper call): %s %d -> %s\n", mono_method_full_name (method, TRUE), patch_info->type, mono_method_full_name (patch_info->data.method, TRUE)); skip = TRUE; break; } @@ -464,146 +1325,96 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts) //printf ("Compile: %s\n", mono_method_full_name (method, TRUE)); - code = cfg->native_code; - - fprintf (tmpfp, ".text\n"); - mname = g_strdup_printf ("method_%08X", token); - fprintf (tmpfp, "\t.align %d\n", func_alignment); - fprintf (tmpfp, ".globl %s\n", mname); - fprintf (tmpfp, "\t.type %s,@function\n", mname); - fprintf (tmpfp, "%s:\n", mname); - - for (j = 0; j < cfg->code_len; j++) - fprintf (tmpfp, ".byte %d\n", (unsigned int) code [j]); + emitted [i] = TRUE; + emit_method (acfg, cfg); - fprintf (tmpfp, ".data\n"); - - j = 0; - for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { - switch (patch_info->type) { - case MONO_PATCH_INFO_LABEL: - case MONO_PATCH_INFO_BB: - /* relative jumps are no problem, there is no need to handle then here */ - break; - case MONO_PATCH_INFO_SWITCH: { - gpointer *table = (gpointer *)patch_info->data.target; - int k; - - fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); - fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j); - fprintf (tmpfp, "\t.long %d\n", patch_info->table_size); - - for (k = 0; k < patch_info->table_size; k++) { - fprintf (tmpfp, "\t.long %d\n", (guint8 *)table [k] - code); - } - j++; - break; - } - case MONO_PATCH_INFO_INTERNAL_METHOD: - patch_info->data.name = cond_emit_icall_label (tmpfp, ref_hash, patch_info->data.name); - j++; - break; - case MONO_PATCH_INFO_METHODCONST: - case MONO_PATCH_INFO_METHOD: - patch_info->data.name = cond_emit_method_label (tmpfp, ref_hash, patch_info); - j++; - break; - case MONO_PATCH_INFO_FIELD: - patch_info->data.name = cond_emit_field_label (tmpfp, ref_hash, patch_info); - j++; - break; - case MONO_PATCH_INFO_CLASS: - patch_info->data.name = cond_emit_klass_label (tmpfp, ref_hash, patch_info->data.klass); - j++; - break; - case MONO_PATCH_INFO_IMAGE: - patch_info->data.name = cond_emit_image_label (tmpfp, ref_hash, patch_info->data.image); - j++; - break; - case MONO_PATCH_INFO_R4: - fprintf (tmpfp, "\t.align 8\n"); - fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j); - fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target)); - - j++; - break; - case MONO_PATCH_INFO_R8: - fprintf (tmpfp, "\t.align 8\n"); - fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j); - fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target)); - fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1)); - - j++; - break; - default: - g_warning ("unable to handle jump info %d", patch_info->type); - g_assert_not_reached (); - } - } - - fprintf (tmpfp, ".globl %s_patch_info\n", mname); - fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer)); - fprintf (tmpfp, "%s_patch_info:\n", mname); - - fprintf (tmpfp, "\t.long %d\n", cfg->code_len); - fprintf (tmpfp, "\t.long %d\n", cfg->used_int_regs); - - fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list)); - for (l = cfg->ldstr_list; l; l = l->next) { - fprintf (tmpfp, "\t.long 0x%08x\n", l->data); - } + mono_destroy_compile (cfg); - if (j) { - j = 0; - for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) { - switch (patch_info->type) { - case MONO_PATCH_INFO_METHODCONST: - case MONO_PATCH_INFO_METHOD: - case MONO_PATCH_INFO_CLASS: - case MONO_PATCH_INFO_FIELD: - case MONO_PATCH_INFO_INTERNAL_METHOD: - case MONO_PATCH_INFO_IMAGE: - fprintf (tmpfp, "\t.long %s\n", patch_info->data.name); - fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i)); - j++; - break; - case MONO_PATCH_INFO_SWITCH: - case MONO_PATCH_INFO_R4: - case MONO_PATCH_INFO_R8: - fprintf (tmpfp, "\t.long %s_patch_info_%d\n", mname, j); - fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i)); - j++; - break; - case MONO_PATCH_INFO_LABEL: - case MONO_PATCH_INFO_BB: - break; - default: - g_warning ("unable to handle jump info %d", patch_info->type); - g_assert_not_reached (); - } + ccount++; + } + /* + * The icall and image tables are small but referenced in a lot of places. + * So we emit them at once, and reference their elements by an index + * instead of an assembly label to cut back on the number of relocations. + */ + + /* Emit icall table */ + + symbol = g_strdup_printf ("mono_icall_table"); + emit_section_change (tmpfp, ".text", 1); + emit_global(tmpfp, symbol); + emit_alignment(tmpfp, 8); + emit_label(tmpfp, symbol); + fprintf (tmpfp, ".long %d\n", acfg->icall_table->len); + for (i = 0; i < acfg->icall_table->len; i++) + fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, (char*)g_ptr_array_index (acfg->icall_table, i)); + + /* Emit image table */ + + symbol = g_strdup_printf ("mono_image_table"); + emit_section_change (tmpfp, ".text", 1); + emit_global(tmpfp, symbol); + emit_alignment(tmpfp, 8); + emit_label(tmpfp, symbol); + fprintf (tmpfp, ".long %d\n", acfg->image_table->len); + for (i = 0; i < acfg->image_table->len; i++) + fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, ((MonoImage*)g_ptr_array_index (acfg->image_table, i))->guid); + + /* + * g_module_symbol takes a lot of time for failed lookups, so we emit + * a table which contains one bit for each method. This bit specifies + * whenever the method is emitted or not. + */ + + symbol = g_strdup_printf ("mono_methods_present_table"); + emit_section_change (tmpfp, ".text", 1); + emit_global(tmpfp, symbol); + emit_alignment(tmpfp, 8); + emit_label(tmpfp, symbol); + { + guint32 k, nrows; + guint32 w; + + nrows = image->tables [MONO_TABLE_METHOD].rows; + for (i = 0; i < nrows / 32 + 1; ++i) { + w = 0; + for (k = 0; k < 32; ++k) { + if (emitted [(i * 32) + k]) + w += (1 << k); } + //printf ("EMITTED [%d] = %d.\n", i, b); + fprintf (tmpfp, "\t.long %d\n", w); } - /* NULL terminated array */ - fprintf (tmpfp, "\t.long 0\n"); - - /* fixme: save the rest of the required infos */ - - g_free (mname); - mono_destroy_compile (cfg); - - ccount++; } fclose (tmpfp); +#if defined(sparc) && SIZEOF_VOID_P == 8 + com = g_strdup_printf ("as -xarch=v9 %s -o %s.o", tmpfname, tmpfname); +#else com = g_strdup_printf ("as %s -o %s.o", tmpfname, tmpfname); +#endif printf ("Executing the native assembler: %s\n", com); - system (com); + if (system (com) != 0) { + g_free (com); + return 1; + } + g_free (com); +#if defined(sparc) + com = g_strdup_printf ("ld -shared -G -o %s%s %s.o", image->name, SHARED_EXT, tmpfname); +#elif defined(__ppc__) && defined(__MACH__) + com = g_strdup_printf ("gcc -dynamiclib -o %s%s %s.o", image->name, SHARED_EXT, tmpfname); +#else com = g_strdup_printf ("ld -shared -o %s%s %s.o", image->name, SHARED_EXT, tmpfname); +#endif printf ("Executing the native linker: %s\n", com); - system (com); + if (system (com) != 0) { + g_free (com); + return 1; + } + g_free (com); com = g_strdup_printf ("%s.o", tmpfname); unlink (com); @@ -613,13 +1424,15 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts) system (com); g_free (com);*/ - printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, (ccount*100)/mcount); - printf ("%d methods contain exception tables (%d%%)\n", ecount, (ecount*100)/mcount); - printf ("%d methods contain absolute addresses (%d%%)\n", abscount, (abscount*100)/mcount); - printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, (wrappercount*100)/mcount); - printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, (lmfcount*100)/mcount); - printf ("%d methods have other problems (%d%%)\n", ocount, (ocount*100)/mcount); + printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, mcount ? (ccount*100)/mcount : 100); + printf ("%d methods contain absolute addresses (%d%%)\n", abscount, mcount ? (abscount*100)/mcount : 100); + printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, mcount ? (wrappercount*100)/mcount : 100); + printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, mcount ? (lmfcount*100)/mcount : 100); + printf ("%d methods have other problems (%d%%)\n", ocount, mcount ? (ocount*100)/mcount : 100); + //printf ("Retained input file.\n"); unlink (tmpfname); return 0; } + +