2 * aot.c: mono Ahead of Time compiler
5 * Dietmar Maurer (dietmar@ximian.com)
7 * (C) 2002 Ximian, Inc.
11 #include <sys/types.h>
15 #ifndef PLATFORM_WIN32
21 #include <limits.h> /* for PAGESIZE */
26 #include <mono/metadata/tabledefs.h>
27 #include <mono/metadata/class.h>
28 #include <mono/metadata/object.h>
29 #include <mono/metadata/tokentype.h>
30 #include <mono/metadata/appdomain.h>
31 #include <mono/metadata/debug-helpers.h>
32 #include <mono/metadata/assembly.h>
33 #include <mono/metadata/metadata-internals.h>
34 #include <mono/metadata/marshal.h>
35 #include <mono/utils/mono-logger.h>
36 #include <mono/os/gc_wrapper.h>
41 #define SHARED_EXT ".dll"
42 #elif defined(__ppc__) && defined(__MACH__)
43 #define SHARED_EXT ".dylib"
45 #define SHARED_EXT ".so"
48 #if defined(sparc) || defined(__ppc__)
49 #define AS_STRING_DIRECTIVE ".asciz"
52 #define AS_STRING_DIRECTIVE ".string"
55 #define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1)))
57 typedef struct MonoAotMethod {
59 MonoJumpInfo *patch_info;
63 typedef struct MonoAotModule {
64 /* Optimization flags used to compile the module */
66 /* Maps MonoMethods to MonoAotMethodInfos */
67 MonoGHashTable *methods;
69 MonoImage **image_table;
70 guint32* methods_present_table;
73 typedef struct MonoAotCompile {
76 GHashTable *icall_hash;
77 GPtrArray *icall_table;
78 GHashTable *image_hash;
79 GPtrArray *image_table;
82 static MonoGHashTable *aot_modules;
84 static CRITICAL_SECTION aot_mutex;
87 * Disabling this will make a copy of the loaded code and use the copy instead
88 * of the original. This will place the caller and the callee close to each
89 * other in memory, possibly improving cache behavior. Since the original
90 * code is in copy-on-write memory, this will not increase the memory usage
93 static gboolean use_loaded_code = FALSE;
96 static gint32 mono_last_aot_method = -1;
99 mono_aot_load_method (MonoDomain *domain, MonoAotModule *aot_module, MonoMethod *method, guint8 *code, guint8 *info);
102 decode_class_info (MonoAotModule *module, guint32 *data)
107 image = module->image_table [data [1]];
111 return mono_class_get (image, data [0]);
113 /* the pointer is dword aligned */
114 klass = decode_class_info (module, *(guint32**)(ALIGN_PTR_TO (&data[3], sizeof (gpointer))));
115 return mono_array_class_get (klass, data [2]);
122 load_aot_module (MonoAssembly *assembly, gpointer user_data)
126 gboolean usable = TRUE;
127 char *saved_guid = NULL;
128 char *aot_version = NULL;
129 char *opt_flags = NULL;
131 aot_name = g_strdup_printf ("%s%s", assembly->image->name, SHARED_EXT);
133 assembly->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY);
135 if (!assembly->aot_module) {
136 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Failed to load AOT module %s: %s\n", aot_name, g_module_error ());
141 g_module_symbol (assembly->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid);
142 g_module_symbol (assembly->aot_module, "mono_aot_version", (gpointer *) &aot_version);
143 g_module_symbol (assembly->aot_module, "mono_aot_opt_flags", (gpointer *)&opt_flags);
145 if (!aot_version || strcmp (aot_version, MONO_AOT_FILE_VERSION)) {
146 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);
150 if (!saved_guid || strcmp (assembly->image->guid, saved_guid)) {
151 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Module %s is out of date.\n", aot_name);
157 g_module_close (assembly->aot_module);
158 assembly->aot_module = NULL;
163 * It seems that MonoGHashTables are in the GC heap, so structures
164 * containing them must be in the GC heap as well :(
167 info = GC_MALLOC (sizeof (MonoAotModule));
169 info = g_new0 (MonoAotModule, 1);
171 info->methods = mono_g_hash_table_new (NULL, NULL);
172 sscanf (opt_flags, "%d", &info->opts);
174 /* Read image table */
176 guint32 table_len, i;
179 g_module_symbol (assembly->aot_module, "mono_image_table", (gpointer *)&table);
182 table_len = *(guint32*)table;
183 table += sizeof (guint32);
184 info->image_table = g_new0 (MonoImage*, table_len);
185 for (i = 0; i < table_len; ++i) {
186 info->image_table [i] = mono_image_loaded_by_guid (table);
187 if (!info->image_table [i]) {
188 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module %s is out of date.\n", aot_name);
189 mono_g_hash_table_destroy (info->methods);
190 g_free (info->image_table);
191 #ifndef HAVE_BOEHM_GC
195 g_module_close (assembly->aot_module);
196 assembly->aot_module = NULL;
199 table += strlen (table) + 1;
203 /* Read icall table */
205 guint32 table_len, i;
208 g_module_symbol (assembly->aot_module, "mono_icall_table", (gpointer *)&table);
211 table_len = *(guint32*)table;
212 table += sizeof (guint32);
213 info->icall_table = g_new0 (char*, table_len);
214 for (i = 0; i < table_len; ++i) {
215 info->icall_table [i] = table;
216 table += strlen (table) + 1;
220 /* Read methods present table */
221 g_module_symbol (assembly->aot_module, "mono_methods_present_table", (gpointer *)&info->methods_present_table);
222 g_assert (info->methods_present_table);
224 EnterCriticalSection (&aot_mutex);
225 mono_g_hash_table_insert (aot_modules, assembly, info);
226 LeaveCriticalSection (&aot_mutex);
228 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT Loaded AOT Module for %s.\n", assembly->image->name);
234 InitializeCriticalSection (&aot_mutex);
236 aot_modules = mono_g_hash_table_new (NULL, NULL);
238 mono_install_assembly_load_hook (load_aot_module, NULL);
240 if (getenv ("MONO_LASTAOT"))
241 mono_last_aot_method = atoi (getenv ("MONO_LASTAOT"));
245 mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
247 MonoClass *klass = method->klass;
248 MonoAssembly *ass = klass->image->assembly;
249 GModule *module = ass->aot_module;
250 char method_label [256];
251 char info_label [256];
254 MonoAotModule *aot_module;
255 MonoAotMethod *minfo;
257 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
266 if (mono_profiler_get_events () & MONO_PROFILE_ENTER_LEAVE)
269 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
270 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
271 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
272 (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
275 aot_module = (MonoAotModule*)mono_g_hash_table_lookup (aot_modules, ass);
277 g_assert (klass->inited);
279 minfo = mono_g_hash_table_lookup (aot_module->methods, method);
280 /* Can't use code from non-root domains since they can be unloaded */
281 if (minfo && (minfo->domain == mono_get_root_domain ())) {
282 /* This method was already loaded in another appdomain */
284 /* Duplicate jinfo */
285 jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
286 memcpy (jinfo, minfo->info, sizeof (MonoJitInfo));
287 if (jinfo->clauses) {
289 mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
290 memcpy (jinfo->clauses, minfo->info->clauses, sizeof (MonoJitExceptionInfo) * header->num_clauses);
293 if (aot_module->opts & MONO_OPT_SHARED)
294 /* Use the same method in the new appdomain */
296 else if (!minfo->patch_info)
297 /* Use the same method in the new appdomain */
300 /* Create a copy of the original method and apply relocations */
302 code = mono_code_manager_reserve (domain->code_mp, minfo->info->code_size);
303 memcpy (code, minfo->info->code_start, minfo->info->code_size);
305 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);
307 /* Do this outside the lock to avoid deadlocks */
308 LeaveCriticalSection (&aot_mutex);
309 mono_arch_patch_code (method, domain, code, minfo->patch_info, TRUE);
310 EnterCriticalSection (&aot_mutex);
311 mono_arch_flush_icache (code, minfo->info->code_size);
314 jinfo->code_start = code;
315 if (jinfo->clauses) {
316 for (i = 0; i < header->num_clauses; ++i) {
317 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
318 gint32 offset = code - (guint8*)minfo->info->code_start;
320 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
321 ei->data.filter = (guint8*)ei->data.filter + offset;
322 ei->try_start = (guint8*)ei->try_start + offset;
323 ei->try_end = (guint8*)ei->try_end + offset;
324 ei->handler_start = (guint8*)ei->handler_start + offset;
332 /* Do a fast check to see whenever the method exists */
334 guint32 index = mono_metadata_token_index (method->token) - 1;
336 w = aot_module->methods_present_table [index / 32];
337 if (! (w & (1 << (index % 32)))) {
338 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT NOT FOUND: %s.\n", mono_method_full_name (method, TRUE));
343 sprintf (method_label, "m_%x", mono_metadata_token_index (method->token));
345 if (!g_module_symbol (module, method_label, (gpointer *)&code))
348 sprintf (info_label, "%s_p", method_label);
350 if (!g_module_symbol (module, info_label, (gpointer *)&info))
353 if (mono_last_aot_method != -1) {
354 if (mono_jit_stats.methods_aot > mono_last_aot_method)
357 if (mono_jit_stats.methods_aot == mono_last_aot_method)
358 printf ("LAST AOT METHOD: %s.%s.%s.\n", klass->name_space, klass->name, method->name);
361 return mono_aot_load_method (domain, aot_module, method, code, info);
365 mono_aot_load_method (MonoDomain *domain, MonoAotModule *aot_module, MonoMethod *method, guint8 *code, guint8 *info)
367 MonoClass *klass = method->klass;
368 MonoJumpInfo *patch_info = NULL;
369 guint code_len, used_int_regs, used_strings;
370 MonoAotMethod *minfo;
372 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
377 minfo = GC_MALLOC (sizeof (MonoAotMethod));
379 minfo = g_new0 (MonoAotMethod, 1);
382 minfo->domain = domain;
383 jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
385 code_len = *(guint32*)info;
387 used_int_regs = *(guint32*)info;
390 if (!use_loaded_code) {
392 code2 = mono_code_manager_reserve (domain->code_mp, code_len);
393 memcpy (code2, code, code_len);
394 mono_arch_flush_icache (code2, code_len);
398 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);
400 /* Exception table */
401 if (header->num_clauses) {
403 mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
404 jinfo->num_clauses = header->num_clauses;
406 jinfo->exvar_offset = *(guint32*)info;
409 for (i = 0; i < header->num_clauses; ++i) {
410 MonoExceptionClause *ec = &header->clauses [i];
411 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
413 ei->flags = ec->flags;
414 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
415 ei->data.filter = code + *(guint32*)info;
417 ei->data.token = *(guint32*)info;
419 ei->try_start = code + *(guint32*)info;
421 ei->try_end = code + *(guint32*)info;
423 ei->handler_start = code + *(guint32*)info;
428 if (aot_module->opts & MONO_OPT_SHARED) {
429 used_strings = *(guint32*)info;
435 for (i = 0; i < used_strings; i++) {
436 guint token = *(guint32*)info;
438 mono_ldstr (mono_get_root_domain (), klass->image, mono_metadata_token_index (token));
448 guint32 last_offset, buf_len;
451 if (aot_module->opts & MONO_OPT_SHARED)
452 mp = mono_mempool_new ();
456 /* First load the type + offset table */
458 patches = g_ptr_array_new ();
460 MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
465 b2 = *((guint8*)info + 1);
471 if (((b1 & (1 + 2)) == 3) && (b2 == 255)) {
472 info = ALIGN_PTR_TO (info, 4);
473 ji->ip.i = *(guint32*)info;
477 ji->ip.i = (((guint32)(b1 & (1 + 2))) << 8) + b2;
479 ji->ip.i += last_offset;
480 last_offset = ji->ip.i;
481 //printf ("T: %d O: %d.\n", ji->type, ji->ip.i);
483 ji->next = patch_info;
486 g_ptr_array_add (patches, ji);
490 info = ALIGN_PTR_TO (info, sizeof (gpointer));
492 /* Then load the other data */
493 for (pindex = 0; pindex < patches->len; ++pindex) {
494 MonoJumpInfo *ji = g_ptr_array_index (patches, pindex);
496 data = *((guint32 **)info);
497 info += sizeof (gpointer);
500 case MONO_PATCH_INFO_CLASS:
501 case MONO_PATCH_INFO_IID:
502 ji->data.klass = decode_class_info (aot_module, data);
503 g_assert (ji->data.klass);
504 mono_class_init (ji->data.klass);
506 case MONO_PATCH_INFO_VTABLE:
507 case MONO_PATCH_INFO_CLASS_INIT:
508 ji->data.klass = decode_class_info (aot_module, data);
509 g_assert (ji->data.klass);
510 mono_class_init (ji->data.klass);
512 case MONO_PATCH_INFO_IMAGE:
513 ji->data.image = aot_module->image_table [(guint32)data];
514 g_assert (ji->data.image);
516 case MONO_PATCH_INFO_METHOD:
517 case MONO_PATCH_INFO_METHODCONST:
518 case MONO_PATCH_INFO_METHOD_JUMP: {
519 guint32 image_index, token;
521 image_index = (guint32)data >> 24;
522 token = MONO_TOKEN_METHOD_DEF | ((guint32)data & 0xffffff);
524 image = aot_module->image_table [image_index];
525 ji->data.method = mono_get_method (image, token, NULL);
526 g_assert (ji->data.method);
527 mono_class_init (ji->data.method->klass);
531 case MONO_PATCH_INFO_WRAPPER: {
532 guint32 image_index, token;
533 guint32 wrapper_type;
535 wrapper_type = (guint32)data[0];
536 image_index = (guint32)data[1] >> 24;
537 token = MONO_TOKEN_METHOD_DEF | ((guint32)data[1] & 0xffffff);
539 image = aot_module->image_table [image_index];
540 ji->data.method = mono_get_method (image, token, NULL);
541 g_assert (ji->data.method);
542 mono_class_init (ji->data.method->klass);
544 g_assert (wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK);
545 ji->type = MONO_PATCH_INFO_METHOD;
546 ji->data.method = mono_marshal_get_remoting_invoke_with_check (ji->data.method);
549 case MONO_PATCH_INFO_FIELD:
550 case MONO_PATCH_INFO_SFLDA: {
551 /* The pointer is dword aligned */
552 gpointer class_ptr = *(gpointer*)(ALIGN_PTR_TO (&data[1], sizeof (gpointer)));
553 MonoClass *klass = decode_class_info (aot_module, class_ptr);
554 mono_class_init (klass);
555 ji->data.field = mono_class_get_field (klass, data [0]);
558 case MONO_PATCH_INFO_INTERNAL_METHOD:
559 ji->data.name = aot_module->icall_table [(guint32)data];
560 g_assert (ji->data.name);
561 //printf ("A: %s.\n", ji->data.name);
563 case MONO_PATCH_INFO_SWITCH:
564 ji->table_size = data [0];
565 table = g_new (gpointer, ji->table_size);
566 ji->data.target = table;
567 for (i = 0; i < ji->table_size; i++) {
568 table [i] = data [i + 1];
571 case MONO_PATCH_INFO_R4:
572 case MONO_PATCH_INFO_R8:
573 ji->data.target = data;
575 case MONO_PATCH_INFO_LDSTR:
576 case MONO_PATCH_INFO_LDTOKEN:
577 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
578 image = aot_module->image_table [data [0]];
579 ji->data.token = mono_jump_info_token_new (mp, image, data [1]);
581 case MONO_PATCH_INFO_EXC_NAME:
582 ji->data.klass = decode_class_info (aot_module, data);
583 g_assert (ji->data.klass);
584 mono_class_init (ji->data.klass);
585 ji->data.name = ji->data.klass->name;
587 case MONO_PATCH_INFO_METHOD_REL:
588 ji->data.offset = data [0];
591 g_warning ("unhandled type %d", ji->type);
592 g_assert_not_reached ();
596 g_ptr_array_free (patches, TRUE);
599 buf_len = *(guint32*)info;
601 mono_debug_add_aot_method (domain, method, code, info, buf_len);
603 if (use_loaded_code) {
604 /* disable write protection */
605 #ifndef PLATFORM_WIN32
606 page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
607 pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
608 err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
613 g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
618 /* Do this outside the lock to avoid deadlocks */
619 LeaveCriticalSection (&aot_mutex);
620 mono_arch_patch_code (method, domain, code, patch_info, TRUE);
621 EnterCriticalSection (&aot_mutex);
623 if (aot_module->opts & MONO_OPT_SHARED)
624 /* No need to cache patches */
625 mono_mempool_destroy (mp);
627 minfo->patch_info = patch_info;
630 mono_jit_stats.methods_aot++;
633 jinfo->code_size = code_len;
634 jinfo->used_regs = used_int_regs;
635 jinfo->method = method;
636 jinfo->code_start = code;
637 jinfo->domain_neutral = (aot_module->opts & MONO_OPT_SHARED) != 0;
640 mono_g_hash_table_insert (aot_module->methods, method, minfo);
647 mono_aot_get_method (MonoDomain *domain, MonoMethod *method)
651 EnterCriticalSection (&aot_mutex);
652 info = mono_aot_get_method_inner (domain, method);
653 LeaveCriticalSection (&aot_mutex);
655 /* Do this outside the lock */
657 mono_jit_info_table_add (domain, info);
665 emit_section_change (FILE *fp, const char *section_name, int subsection_index)
668 /* For solaris as, GNU as should accept the same */
669 fprintf (fp, ".section \"%s\"\n", section_name);
670 #elif defined(__ppc__) && defined(__MACH__)
671 /* This needs to be made more precise on mach. */
672 fprintf (fp, "%s\n", subsection_index == 0 ? ".text" : ".data");
674 fprintf (fp, "%s %d\n", section_name, subsection_index);
679 emit_global (FILE *fp, const char *name)
681 #if defined(__ppc__) && defined(__MACH__)
682 // mach-o always uses a '_' prefix.
683 fprintf (fp, ".globl _%s\n", name);
685 fprintf (fp, ".globl %s\n", name);
690 emit_label (FILE *fp, const char *name)
692 #if defined(__ppc__) && defined(__MACH__)
693 // mach-o always uses a '_' prefix.
694 fprintf (fp, "_%s:\n", name);
696 fprintf (fp, "%s:\n", name);
702 write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align)
706 emit_section_change (fp, ".text", 1);
708 fprintf (fp, ".globl %s\n", name);
709 fprintf (fp, "\t.align %d\n", align);
710 fprintf (fp, "\t.type %s,#object\n", name);
711 fprintf (fp, "\t.size %s,%d\n", name, size);
712 fprintf (fp, "%s:\n", name);
713 for (i = 0; i < size; i++) {
714 fprintf (fp, ".byte %d\n", buf [i]);
721 write_string_symbol (FILE *fp, const char *name, const char *value)
723 emit_section_change (fp, ".text", 1);
724 emit_global(fp, name);
725 emit_label(fp, name);
726 fprintf (fp, "\t%s \"%s\"\n", AS_STRING_DIRECTIVE, value);
730 mono_get_field_token (MonoClassField *field)
732 MonoClass *klass = field->parent;
735 for (i = 0; i < klass->field.count; ++i) {
736 if (field == &klass->fields [i])
737 return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i);
740 g_assert_not_reached ();
745 get_image_index (MonoAotCompile *cfg, MonoImage *image)
749 index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image));
753 index = g_hash_table_size (cfg->image_hash);
754 g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1));
755 g_ptr_array_add (cfg->image_table, image);
761 emit_image_index (MonoAotCompile *cfg, MonoImage *image)
765 image_index = get_image_index (cfg, image);
767 fprintf (cfg->fp, "\t.long %d\n", image_index);
770 #if defined(__ppc__) && defined(__MACH__)
772 ilog2(register int value)
775 while (value & ~0xf) count += 4, value >>= 4;
776 while (value) count++, value >>= 1;
781 static void emit_alignment(FILE *fp, int size)
783 #if defined(__ppc__) && defined(__MACH__)
784 // the mach-o assembler specifies alignments as powers of 2.
785 fprintf (fp, "\t.align %d\t; ilog2\n", ilog2(size));
786 #elif defined(__powerpc__)
787 /* ignore on linux/ppc */
789 fprintf (fp, "\t.align %d\n", size);
794 emit_pointer (FILE *fp, const char *target)
796 emit_alignment (fp, sizeof (gpointer));
797 #if defined(sparc) && SIZEOF_VOID_P == 8
798 fprintf (fp, "\t.xword %s\n", target);
800 fprintf (fp, "\t.long %s\n", target);
805 cond_emit_klass_label (MonoAotCompile *cfg, MonoClass *klass)
807 char *l1, *el = NULL;
809 if ((l1 = g_hash_table_lookup (cfg->ref_hash, klass)))
812 if (!klass->type_token) {
813 g_assert (klass->rank > 0);
814 el = cond_emit_klass_label (cfg, klass->element_class);
817 emit_alignment(cfg->fp, sizeof (gpointer));
819 l1 = g_strdup_printf ("klass_p_%08x_%p", klass->type_token, klass);
820 fprintf (cfg->fp, "%s:\n", l1);
821 fprintf (cfg->fp, "\t.long 0x%08x\n", klass->type_token);
822 emit_image_index (cfg, klass->image);
825 fprintf (cfg->fp, "\t.long %d\n", klass->rank);
826 emit_pointer (cfg->fp, el);
829 g_hash_table_insert (cfg->ref_hash, klass, l1);
835 cond_emit_field_label (MonoAotCompile *cfg, MonoJumpInfo *patch_info)
837 MonoClassField *field = patch_info->data.field;
841 if ((l1 = g_hash_table_lookup (cfg->ref_hash, field)))
844 l2 = cond_emit_klass_label (cfg, field->parent);
845 emit_alignment(cfg->fp, sizeof (gpointer));
846 token = mono_get_field_token (field);
848 l1 = g_strdup_printf ("klass_p_%08x_%p", token, field);
849 fprintf (cfg->fp, "%s:\n", l1);
850 fprintf (cfg->fp, "\t.long 0x%08x\n", token);
851 emit_pointer (cfg->fp, l2);
853 g_hash_table_insert (cfg->ref_hash, field, l1);
859 compare_patches (gconstpointer a, gconstpointer b)
863 i = (*(MonoJumpInfo**)a)->ip.i;
864 j = (*(MonoJumpInfo**)b)->ip.i;
876 emit_method (MonoAotCompile *acfg, MonoCompile *cfg)
882 guint8 *code, *mname, *mname_p;
883 int func_alignment = 16;
885 MonoJumpInfo *patch_info;
886 MonoMethodHeader *header;
889 method = cfg->method;
890 code = cfg->native_code;
891 header = ((MonoMethodNormal*)method)->header;
893 emit_section_change (tmpfp, ".text", 0);
894 mname = g_strdup_printf ("m_%x", mono_metadata_token_index (method->token));
895 mname_p = g_strdup_printf ("%s_p", mname);
896 emit_alignment(tmpfp, func_alignment);
897 emit_global(tmpfp, mname);
899 fprintf (tmpfp, "\t.type %s,#function\n", mname);
900 #elif !(defined(__ppc__) && defined(__MACH__))
901 fprintf (tmpfp, "\t.type %s,@function\n", mname);
903 emit_label(tmpfp, mname);
905 for (i = 0; i < cfg->code_len; i++)
906 fprintf (tmpfp, ".byte %d\n", (unsigned int) code [i]);
908 emit_section_change (tmpfp, ".text", 1);
910 /* Sort relocations */
911 patches = g_ptr_array_new ();
912 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next)
913 g_ptr_array_add (patches, patch_info);
914 g_ptr_array_sort (patches, compare_patches);
916 /* Emit out of line info for relocations */
918 for (pindex = 0; pindex < patches->len; ++pindex) {
919 patch_info = g_ptr_array_index (patches, pindex);
920 switch (patch_info->type) {
921 case MONO_PATCH_INFO_LABEL:
922 case MONO_PATCH_INFO_BB:
923 /* relative jumps are no problem, there is no need to handle then here */
925 case MONO_PATCH_INFO_SWITCH: {
926 gpointer *table = (gpointer *)patch_info->data.target;
929 emit_alignment(tmpfp, sizeof (gpointer));
930 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
931 fprintf (tmpfp, "\t.long %d\n", patch_info->table_size);
933 for (k = 0; k < patch_info->table_size; k++) {
934 fprintf (tmpfp, "\t.long %d\n", (int)table [k]);
939 case MONO_PATCH_INFO_INTERNAL_METHOD: {
942 icall_index = (guint32)g_hash_table_lookup (acfg->icall_hash, patch_info->data.name);
944 icall_index = g_hash_table_size (acfg->icall_hash) + 1;
945 g_hash_table_insert (acfg->icall_hash, (gpointer)patch_info->data.name,
946 GUINT_TO_POINTER (icall_index));
947 g_ptr_array_add (acfg->icall_table, (gpointer)patch_info->data.name);
949 patch_info->data.name = g_strdup_printf ("%d", icall_index - 1);
953 case MONO_PATCH_INFO_METHODCONST:
954 case MONO_PATCH_INFO_METHOD:
955 case MONO_PATCH_INFO_METHOD_JUMP: {
957 * The majority of patches are for methods, so we emit
958 * them inline instead of defining a label for them to
959 * decrease the number of relocations.
961 guint32 image_index = get_image_index (acfg, patch_info->data.method->klass->image);
962 guint32 token = patch_info->data.method->token;
963 g_assert (image_index < 256);
964 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
966 patch_info->data.name =
967 g_strdup_printf ("%d", (image_index << 24) + (mono_metadata_token_index (token)));
971 case MONO_PATCH_INFO_WRAPPER: {
976 m = mono_marshal_method_from_wrapper (patch_info->data.method);
977 image_index = get_image_index (acfg, m->klass->image);
979 g_assert (image_index < 256);
980 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
982 emit_alignment(tmpfp, sizeof (gpointer));
983 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
984 fprintf (tmpfp, "\t.long %d\n", patch_info->data.method->wrapper_type);
985 fprintf (tmpfp, "\t.long %d\n", (image_index << 24) + (mono_metadata_token_index (token)));
989 case MONO_PATCH_INFO_FIELD:
990 patch_info->data.name = cond_emit_field_label (acfg, patch_info);
993 case MONO_PATCH_INFO_CLASS:
994 case MONO_PATCH_INFO_IID:
995 patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
998 case MONO_PATCH_INFO_IMAGE:
999 patch_info->data.name = g_strdup_printf ("%d", get_image_index (acfg, patch_info->data.image));
1002 case MONO_PATCH_INFO_EXC_NAME: {
1003 MonoClass *ex_class;
1006 mono_class_from_name (mono_defaults.exception_class->image,
1007 "System", patch_info->data.target);
1008 g_assert (ex_class);
1009 patch_info->data.name = cond_emit_klass_label (acfg, ex_class);
1013 case MONO_PATCH_INFO_R4:
1014 emit_alignment(tmpfp, 8);
1015 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1016 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
1019 case MONO_PATCH_INFO_R8:
1020 emit_alignment(tmpfp, 8);
1021 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1022 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
1023 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1));
1026 case MONO_PATCH_INFO_METHOD_REL:
1027 emit_alignment(tmpfp, sizeof (gpointer));
1028 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1029 fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.offset);
1032 case MONO_PATCH_INFO_VTABLE:
1033 case MONO_PATCH_INFO_CLASS_INIT:
1034 patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
1037 case MONO_PATCH_INFO_SFLDA:
1038 patch_info->data.name = cond_emit_field_label (acfg, patch_info);
1041 case MONO_PATCH_INFO_LDSTR:
1042 case MONO_PATCH_INFO_LDTOKEN:
1043 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
1044 emit_alignment(tmpfp, 8);
1045 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1046 fprintf (tmpfp, "\t.long 0x%08x\n", get_image_index (acfg, patch_info->data.token->image));
1047 fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.token->token);
1051 g_warning ("unable to handle jump info %d", patch_info->type);
1052 g_assert_not_reached ();
1056 emit_global (tmpfp, mname_p);
1057 emit_alignment (tmpfp, sizeof (gpointer));
1058 emit_label (tmpfp, mname_p);
1060 fprintf (tmpfp, "\t.long %d\n", cfg->code_len);
1061 fprintf (tmpfp, "\t.long %ld\n", (long)cfg->used_int_regs);
1063 /* Exception table */
1064 if (header->num_clauses) {
1065 MonoJitInfo *jinfo = cfg->jit_info;
1067 fprintf (tmpfp, "\t.long %d\n", jinfo->exvar_offset);
1069 for (k = 0; k < header->num_clauses; ++k) {
1070 MonoJitExceptionInfo *ei = &jinfo->clauses [k];
1072 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
1073 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->data.filter - code);
1075 /* fixme: tokens are not global */
1076 fprintf (tmpfp, "\t.long %d\n", ei->data.token);
1078 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_start - code);
1079 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_end - code);
1080 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->handler_start - code);
1085 if (cfg->opt & MONO_OPT_SHARED) {
1086 fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list));
1087 for (l = cfg->ldstr_list; l; l = l->next) {
1088 fprintf (tmpfp, "\t.long 0x%08lx\n", (long)l->data);
1092 /* Used only in shared mode */
1093 g_assert (!cfg->ldstr_list);
1095 //printf ("M: %s (%s).\n", mono_method_full_name (method, TRUE), mname);
1098 guint32 last_offset;
1103 /* First emit the type+position table */
1104 for (pindex = 0; pindex < patches->len; ++pindex) {
1106 patch_info = g_ptr_array_index (patches, pindex);
1108 if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
1109 (patch_info->type == MONO_PATCH_INFO_BB))
1113 //printf ("T: %d O: %d.\n", patch_info->type, patch_info->ip.i);
1114 offset = patch_info->ip.i - last_offset;
1115 last_offset = patch_info->ip.i;
1117 /* Encode type+position compactly */
1118 g_assert (patch_info->type < 64);
1119 if (offset < 1024 - 1) {
1120 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + (offset >> 8));
1121 fprintf (tmpfp, "\t.byte %d\n", offset & ((1 << 8) - 1));
1124 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + 3);
1125 fprintf (tmpfp, "\t.byte %d\n", 255);
1126 emit_alignment(tmpfp, 4);
1127 fprintf (tmpfp, "\t.long %d\n", offset);
1132 * 0 is PATCH_INFO_BB, which can't be in the file.
1134 /* NULL terminated array */
1135 fprintf (tmpfp, "\t.byte 0\n");
1137 emit_alignment (tmpfp, sizeof (gpointer));
1139 /* Then emit the other info */
1140 for (pindex = 0; pindex < patches->len; ++pindex) {
1141 patch_info = g_ptr_array_index (patches, pindex);
1143 if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
1144 (patch_info->type == MONO_PATCH_INFO_BB))
1148 switch (patch_info->type) {
1149 case MONO_PATCH_INFO_METHODCONST:
1150 case MONO_PATCH_INFO_METHOD:
1151 case MONO_PATCH_INFO_METHOD_JUMP:
1152 case MONO_PATCH_INFO_CLASS:
1153 case MONO_PATCH_INFO_IID:
1154 case MONO_PATCH_INFO_FIELD:
1155 case MONO_PATCH_INFO_INTERNAL_METHOD:
1156 case MONO_PATCH_INFO_IMAGE:
1157 case MONO_PATCH_INFO_VTABLE:
1158 case MONO_PATCH_INFO_CLASS_INIT:
1159 case MONO_PATCH_INFO_SFLDA:
1160 case MONO_PATCH_INFO_EXC_NAME:
1161 emit_pointer (tmpfp, patch_info->data.name);
1164 case MONO_PATCH_INFO_SWITCH:
1165 case MONO_PATCH_INFO_R4:
1166 case MONO_PATCH_INFO_R8:
1167 case MONO_PATCH_INFO_METHOD_REL:
1168 case MONO_PATCH_INFO_LDSTR:
1169 case MONO_PATCH_INFO_LDTOKEN:
1170 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
1171 case MONO_PATCH_INFO_WRAPPER: {
1173 sprintf (buf, "%s_p_%d", mname, j);
1174 emit_pointer (tmpfp, buf);
1178 case MONO_PATCH_INFO_LABEL:
1179 case MONO_PATCH_INFO_BB:
1182 g_warning ("unable to handle jump info %d", patch_info->type);
1183 g_assert_not_reached ();
1193 mono_debug_serialize_debug_info (cfg, &buf, &buf_len);
1195 fprintf (tmpfp, "\t.long %d\n", buf_len);
1197 for (i = 0; i < buf_len; ++i)
1198 fprintf (tmpfp, ".byte %d\n", (unsigned int) buf [i]);
1204 /* fixme: save the rest of the required infos */
1211 mono_compile_assembly (MonoAssembly *ass, guint32 opts)
1214 MonoImage *image = ass->image;
1216 char *com, *tmpfname, *opts_str;
1220 int ccount = 0, mcount = 0, lmfcount = 0, abscount = 0, wrappercount = 0, ocount = 0;
1221 GHashTable *ref_hash;
1222 MonoAotCompile *acfg;
1225 printf ("Mono Ahead of Time compiler - compiling assembly %s\n", image->name);
1227 i = g_file_open_tmp ("mono_aot_XXXXXX", &tmpfname, NULL);
1228 tmpfp = fdopen (i, "w+");
1231 ref_hash = g_hash_table_new (NULL, NULL);
1233 acfg = g_new0 (MonoAotCompile, 1);
1235 acfg->ref_hash = ref_hash;
1236 acfg->icall_hash = g_hash_table_new (NULL, NULL);
1237 acfg->icall_table = g_ptr_array_new ();
1238 acfg->image_hash = g_hash_table_new (NULL, NULL);
1239 acfg->image_table = g_ptr_array_new ();
1241 write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid);
1243 write_string_symbol (tmpfp, "mono_aot_version", MONO_AOT_FILE_VERSION);
1245 opts_str = g_strdup_printf ("%d", opts);
1246 write_string_symbol (tmpfp, "mono_aot_opt_flags", opts_str);
1249 emitted = g_new0 (gboolean, image->tables [MONO_TABLE_METHOD].rows);
1251 for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
1252 MonoJumpInfo *patch_info;
1254 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
1255 method = mono_get_method (image, token, NULL);
1257 /* fixme: maybe we can also precompile wrapper methods */
1258 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
1259 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
1260 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
1261 (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
1262 //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
1268 /* fixme: we need to patch the IP for the LMF in that case */
1269 if (method->save_lmf) {
1270 //printf ("Skip (needs lmf): %s\n", mono_method_full_name (method, TRUE));
1275 //printf ("START: %s\n", mono_method_full_name (method, TRUE));
1276 //mono_compile_method (method);
1278 cfg = mini_method_compile (method, opts, mono_get_root_domain (), FALSE, 0);
1281 if (cfg->disable_aot) {
1282 printf ("Skip (other): %s\n", mono_method_full_name (method, TRUE));
1288 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1289 if (patch_info->type == MONO_PATCH_INFO_ABS) {
1290 /* unable to handle this */
1291 //printf ("Skip (abs addr): %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
1302 /* remoting-invoke-with-check wrappers are very common */
1303 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1304 if ((patch_info->type == MONO_PATCH_INFO_METHOD) &&
1305 ((patch_info->data.method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)))
1306 patch_info->type = MONO_PATCH_INFO_WRAPPER;
1310 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1311 if ((patch_info->type == MONO_PATCH_INFO_METHOD ||
1312 patch_info->type == MONO_PATCH_INFO_METHODCONST) &&
1313 patch_info->data.method->wrapper_type) {
1314 /* unable to handle this */
1315 //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));
1326 //printf ("Compile: %s\n", mono_method_full_name (method, TRUE));
1329 emit_method (acfg, cfg);
1331 mono_destroy_compile (cfg);
1337 * The icall and image tables are small but referenced in a lot of places.
1338 * So we emit them at once, and reference their elements by an index
1339 * instead of an assembly label to cut back on the number of relocations.
1342 /* Emit icall table */
1344 symbol = g_strdup_printf ("mono_icall_table");
1345 emit_section_change (tmpfp, ".text", 1);
1346 emit_global(tmpfp, symbol);
1347 emit_alignment(tmpfp, 8);
1348 emit_label(tmpfp, symbol);
1349 fprintf (tmpfp, ".long %d\n", acfg->icall_table->len);
1350 for (i = 0; i < acfg->icall_table->len; i++)
1351 fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, (char*)g_ptr_array_index (acfg->icall_table, i));
1353 /* Emit image table */
1355 symbol = g_strdup_printf ("mono_image_table");
1356 emit_section_change (tmpfp, ".text", 1);
1357 emit_global(tmpfp, symbol);
1358 emit_alignment(tmpfp, 8);
1359 emit_label(tmpfp, symbol);
1360 fprintf (tmpfp, ".long %d\n", acfg->image_table->len);
1361 for (i = 0; i < acfg->image_table->len; i++)
1362 fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, ((MonoImage*)g_ptr_array_index (acfg->image_table, i))->guid);
1365 * g_module_symbol takes a lot of time for failed lookups, so we emit
1366 * a table which contains one bit for each method. This bit specifies
1367 * whenever the method is emitted or not.
1370 symbol = g_strdup_printf ("mono_methods_present_table");
1371 emit_section_change (tmpfp, ".text", 1);
1372 emit_global(tmpfp, symbol);
1373 emit_alignment(tmpfp, 8);
1374 emit_label(tmpfp, symbol);
1379 nrows = image->tables [MONO_TABLE_METHOD].rows;
1380 for (i = 0; i < nrows / 32 + 1; ++i) {
1382 for (k = 0; k < 32; ++k) {
1383 if (emitted [(i * 32) + k])
1386 //printf ("EMITTED [%d] = %d.\n", i, b);
1387 fprintf (tmpfp, "\t.long %d\n", w);
1393 #if defined(sparc) && SIZEOF_VOID_P == 8
1394 com = g_strdup_printf ("as -xarch=v9 %s -o %s.o", tmpfname, tmpfname);
1396 com = g_strdup_printf ("as %s -o %s.o", tmpfname, tmpfname);
1398 printf ("Executing the native assembler: %s\n", com);
1399 if (system (com) != 0) {
1406 com = g_strdup_printf ("ld -shared -G -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
1407 #elif defined(__ppc__) && defined(__MACH__)
1408 com = g_strdup_printf ("gcc -dynamiclib -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
1410 com = g_strdup_printf ("ld -shared -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
1412 printf ("Executing the native linker: %s\n", com);
1413 if (system (com) != 0) {
1419 com = g_strdup_printf ("%s.o", tmpfname);
1422 /*com = g_strdup_printf ("strip --strip-unneeded %s%s", image->name, SHARED_EXT);
1423 printf ("Stripping the binary: %s\n", com);
1427 printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, mcount ? (ccount*100)/mcount : 100);
1428 printf ("%d methods contain absolute addresses (%d%%)\n", abscount, mcount ? (abscount*100)/mcount : 100);
1429 printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, mcount ? (wrappercount*100)/mcount : 100);
1430 printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, mcount ? (lmfcount*100)/mcount : 100);
1431 printf ("%d methods have other problems (%d%%)\n", ocount, mcount ? (ocount*100)/mcount : 100);
1432 //printf ("Retained input file.\n");