#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
+#include <string.h>
#ifndef PLATFORM_WIN32
#include <sys/mman.h>
#else
#include <mono/metadata/tokentype.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/metadata-internals.h>
+#include <mono/metadata/marshal.h>
+#include <mono/utils/mono-logger.h>
+#include <mono/os/gc_wrapper.h>
#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)
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++) {
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
{
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++) {
#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
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+");
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;
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;
}
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;
}
//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);
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;
}
+
+