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/marshal.h>
34 #include <mono/os/gc_wrapper.h>
39 #define SHARED_EXT ".dll"
41 #define SHARED_EXT ".so"
45 #define AS_STRING_DIRECTIVE ".asciz"
48 #define AS_STRING_DIRECTIVE ".string"
51 typedef struct MonoAotMethod {
53 MonoJumpInfo *patch_info;
57 typedef struct MonoAotModule {
58 /* Optimization flags used to compile the module */
60 /* Maps MonoMethods to MonoAotMethodInfos */
61 MonoGHashTable *methods;
63 MonoImage **image_table;
64 guint32* methods_present_table;
67 typedef struct MonoAotCompile {
70 GHashTable *icall_hash;
71 GPtrArray *icall_table;
72 GHashTable *image_hash;
73 GPtrArray *image_table;
76 static MonoGHashTable *aot_modules;
78 static CRITICAL_SECTION aot_mutex;
80 static guint32 mono_aot_verbose = 0;
83 * Disabling this will make a copy of the loaded code and use the copy instead
84 * of the original. This will place the caller and the callee close to each
85 * other in memory, possibly improving cache behavior. Since the original
86 * code is in copy-on-write memory, this will not increase the memory usage
89 static gboolean use_loaded_code = FALSE;
92 static gint32 mono_last_aot_method = -1;
95 decode_class_info (MonoAotModule *module, gpointer *data)
100 image = module->image_table [(guint32)data [1]];
104 return mono_class_get (image, (guint32)data [0]);
106 klass = decode_class_info (module, data [3]);
107 return mono_array_class_get (klass, (guint32)data [2]);
114 load_aot_module (MonoAssembly *assembly, gpointer user_data)
118 gboolean usable = TRUE;
119 char *saved_guid = NULL;
120 char *aot_version = NULL;
121 char *opt_flags = NULL;
123 aot_name = g_strdup_printf ("%s.so", assembly->image->name);
125 assembly->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY);
127 if (!assembly->aot_module)
130 g_module_symbol (assembly->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid);
131 g_module_symbol (assembly->aot_module, "mono_aot_version", (gpointer *) &aot_version);
132 g_module_symbol (assembly->aot_module, "mono_aot_opt_flags", (gpointer *)&opt_flags);
134 if (!aot_version || strcmp (aot_version, MONO_AOT_FILE_VERSION)) {
135 if (mono_aot_verbose > 0)
136 printf ("AOT module %s has wrong file format version (expected %s got %s)\n", aot_name, MONO_AOT_FILE_VERSION, aot_version);
140 if (!saved_guid || strcmp (assembly->image->guid, saved_guid)) {
141 if (mono_aot_verbose > 0)
142 printf ("AOT module %s is out of date.\n", aot_name);
148 g_module_close (assembly->aot_module);
149 assembly->aot_module = NULL;
154 * It seems that MonoGHashTables are in the GC heap, so structures
155 * containing them must be in the GC heap as well :(
158 info = GC_MALLOC (sizeof (MonoAotModule));
160 info = g_new0 (MonoAotModule, 1);
162 info->methods = mono_g_hash_table_new (NULL, NULL);
163 sscanf (opt_flags, "%d", &info->opts);
165 /* Read image table */
167 guint32 table_len, i;
170 g_module_symbol (assembly->aot_module, "mono_image_table", (gpointer *)&table);
173 table_len = *(guint32*)table;
174 table += sizeof (guint32);
175 info->image_table = g_new0 (MonoImage*, table_len);
176 for (i = 0; i < table_len; ++i) {
177 info->image_table [i] = mono_image_loaded_by_guid (table);
178 if (!info->image_table [i]) {
179 if (mono_aot_verbose > 0)
180 printf ("AOT module %s is out of date.\n", aot_name);
181 mono_g_hash_table_destroy (info->methods);
182 g_free (info->image_table);
183 #ifndef HAVE_BOEHM_GC
187 g_module_close (assembly->aot_module);
188 assembly->aot_module = NULL;
191 table += strlen (table) + 1;
195 /* Read icall table */
197 guint32 table_len, i;
200 g_module_symbol (assembly->aot_module, "mono_icall_table", (gpointer *)&table);
203 table_len = *(guint32*)table;
204 table += sizeof (guint32);
205 info->icall_table = g_new0 (char*, table_len);
206 for (i = 0; i < table_len; ++i) {
207 info->icall_table [i] = table;
208 table += strlen (table) + 1;
212 /* Read methods present table */
213 g_module_symbol (assembly->aot_module, "mono_methods_present_table", (gpointer *)&info->methods_present_table);
214 g_assert (info->methods_present_table);
216 EnterCriticalSection (&aot_mutex);
217 mono_g_hash_table_insert (aot_modules, assembly, info);
218 LeaveCriticalSection (&aot_mutex);
220 if (mono_aot_verbose > 0)
221 printf ("Loaded AOT Module for %s.\n", assembly->image->name);
227 InitializeCriticalSection (&aot_mutex);
229 aot_modules = mono_g_hash_table_new (NULL, NULL);
231 mono_install_assembly_load_hook (load_aot_module, NULL);
233 if (getenv ("MONO_LASTAOT"))
234 mono_last_aot_method = atoi (getenv ("MONO_LASTAOT"));
238 mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
240 MonoClass *klass = method->klass;
241 MonoAssembly *ass = klass->image->assembly;
242 MonoJumpInfo *patch_info = NULL;
243 GModule *module = ass->aot_module;
244 char method_label [256];
245 char info_label [256];
248 guint code_len, used_int_regs, used_strings;
249 MonoAotModule *aot_module;
250 MonoAotMethod *minfo;
252 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
261 if (mono_profiler_get_events () & MONO_PROFILE_ENTER_LEAVE)
264 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
265 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
266 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
267 (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
270 aot_module = (MonoAotModule*)mono_g_hash_table_lookup (aot_modules, ass);
272 g_assert (klass->inited);
274 minfo = mono_g_hash_table_lookup (aot_module->methods, method);
275 /* Can't use code from non-root domains since they can be unloaded */
276 if (minfo && (minfo->domain == mono_root_domain)) {
277 /* This method was already loaded in another appdomain */
279 /* Duplicate jinfo */
280 jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
281 memcpy (jinfo, minfo->info, sizeof (MonoJitInfo));
282 if (jinfo->clauses) {
284 mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
285 memcpy (jinfo->clauses, minfo->info->clauses, sizeof (MonoJitExceptionInfo) * header->num_clauses);
288 if (aot_module->opts & MONO_OPT_SHARED)
289 /* Use the same method in the new appdomain */
291 else if (!minfo->patch_info)
292 /* Use the same method in the new appdomain */
295 /* Create a copy of the original method and apply relocations */
297 code = mono_code_manager_reserve (domain->code_mp, minfo->info->code_size);
298 memcpy (code, minfo->info->code_start, minfo->info->code_size);
300 if (mono_aot_verbose > 1)
301 printf ("REUSE METHOD: %s %p - %p.\n", mono_method_full_name (method, TRUE), code, (char*)code + minfo->info->code_size);
303 /* Do this outside the lock to avoid deadlocks */
304 LeaveCriticalSection (&aot_mutex);
305 mono_arch_patch_code (method, domain, code, minfo->patch_info, TRUE);
306 EnterCriticalSection (&aot_mutex);
307 mono_arch_flush_icache (code, minfo->info->code_size);
310 jinfo->code_start = code;
311 if (jinfo->clauses) {
312 for (i = 0; i < header->num_clauses; ++i) {
313 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
314 gint32 offset = code - (guint8*)minfo->info->code_start;
316 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
317 ei->data.filter = (guint8*)ei->data.filter + offset;
318 ei->try_start = (guint8*)ei->try_start + offset;
319 ei->try_end = (guint8*)ei->try_end + offset;
320 ei->handler_start = (guint8*)ei->handler_start + offset;
328 /* Do a fast check to see whenever the method exists */
330 guint32 index = mono_metadata_token_index (method->token) - 1;
332 w = aot_module->methods_present_table [index / 32];
333 if (! (w & (1 << (index % 32)))) {
334 if (mono_aot_verbose > 1)
335 printf ("NOT FOUND: %s.\n", mono_method_full_name (method, TRUE));
340 sprintf (method_label, "m_%x", mono_metadata_token_index (method->token));
342 if (!g_module_symbol (module, method_label, (gpointer *)&code))
345 sprintf (info_label, "%s_p", method_label);
347 if (!g_module_symbol (module, info_label, (gpointer *)&info))
350 if (mono_last_aot_method != -1) {
351 if (mono_jit_stats.methods_aot > mono_last_aot_method)
354 if (mono_jit_stats.methods_aot == mono_last_aot_method)
355 printf ("LAST AOT METHOD: %s.%s.%s.\n", klass->name_space, klass->name, method->name);
359 minfo = GC_MALLOC (sizeof (MonoAotMethod));
361 minfo = g_new0 (MonoAotMethod, 1);
364 minfo->domain = domain;
365 jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
367 code_len = GPOINTER_TO_UINT (*((gpointer **)info));
369 used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info));
372 if (!use_loaded_code) {
374 code2 = mono_code_manager_reserve (domain->code_mp, code_len);
375 memcpy (code2, code, code_len);
376 mono_arch_flush_icache (code2, code_len);
380 if (mono_aot_verbose > 1)
381 printf ("FOUND AOT compiled code for %s %p - %p %p\n", mono_method_full_name (method, TRUE), code, code + code_len, info);
383 /* Exception table */
384 if (header->num_clauses) {
386 mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
387 jinfo->num_clauses = header->num_clauses;
389 jinfo->exvar_offset = GPOINTER_TO_UINT (*((gpointer**)info));
392 for (i = 0; i < header->num_clauses; ++i) {
393 MonoExceptionClause *ec = &header->clauses [i];
394 MonoJitExceptionInfo *ei = &jinfo->clauses [i];
396 ei->flags = ec->flags;
397 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
398 ei->data.filter = code + GPOINTER_TO_UINT (*((gpointer**)info));
400 ei->data.token = GPOINTER_TO_UINT (*((gpointer**)info));
402 ei->try_start = code + GPOINTER_TO_UINT (*((gpointer**)info));
404 ei->try_end = code + GPOINTER_TO_UINT (*((gpointer**)info));
406 ei->handler_start = code + GPOINTER_TO_UINT (*((gpointer**)info));
411 if (aot_module->opts & MONO_OPT_SHARED) {
412 used_strings = GPOINTER_TO_UINT (*((gpointer **)info));
418 for (i = 0; i < used_strings; i++) {
419 guint token = GPOINTER_TO_UINT (*((gpointer **)info));
421 mono_ldstr (mono_root_domain, klass->image, mono_metadata_token_index (token));
431 guint32 last_offset, buf_len;
433 if (aot_module->opts & MONO_OPT_SHARED)
434 mp = mono_mempool_new ();
440 MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
446 b2 = *((guint8*)info + 1);
448 info = (gpointer*)((guint8*)info + 2);
452 guint32 ptr = (guint32)info;
453 info = (gpointer)((ptr + 3) & ~3);
459 if (((b1 & (1 + 2)) == 3) && (b2 == 255)) {
460 ji->ip.i = GPOINTER_TO_UINT (*info);
464 ji->ip.i = (((guint32)(b1 & (1 + 2))) << 8) + b2;
466 ji->ip.i += last_offset;
467 last_offset = ji->ip.i;
468 //printf ("T: %d O: %d.\n", ji->type, ji->ip.i);
470 data = *((gpointer **)info);
473 case MONO_PATCH_INFO_CLASS:
474 case MONO_PATCH_INFO_IID:
475 ji->data.klass = decode_class_info (aot_module, data);
476 g_assert (ji->data.klass);
477 mono_class_init (ji->data.klass);
479 case MONO_PATCH_INFO_VTABLE:
480 case MONO_PATCH_INFO_CLASS_INIT:
481 ji->data.klass = decode_class_info (aot_module, data);
482 g_assert (ji->data.klass);
483 mono_class_init (ji->data.klass);
485 case MONO_PATCH_INFO_IMAGE:
486 ji->data.image = aot_module->image_table [(guint32)data];
487 g_assert (ji->data.image);
489 case MONO_PATCH_INFO_METHOD:
490 case MONO_PATCH_INFO_METHODCONST:
491 case MONO_PATCH_INFO_METHOD_JUMP: {
492 guint32 image_index, token;
494 image_index = (guint32)data >> 24;
495 token = MONO_TOKEN_METHOD_DEF | ((guint32)data & 0xffffff);
497 image = aot_module->image_table [image_index];
498 ji->data.method = mono_get_method (image, token, NULL);
499 g_assert (ji->data.method);
500 mono_class_init (ji->data.method->klass);
504 case MONO_PATCH_INFO_WRAPPER: {
505 guint32 image_index, token;
506 guint32 wrapper_type;
508 wrapper_type = (guint32)data[0];
509 image_index = (guint32)data[1] >> 24;
510 token = MONO_TOKEN_METHOD_DEF | ((guint32)data[1] & 0xffffff);
512 image = aot_module->image_table [image_index];
513 ji->data.method = mono_get_method (image, token, NULL);
514 g_assert (ji->data.method);
515 mono_class_init (ji->data.method->klass);
517 g_assert (wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK);
518 ji->type = MONO_PATCH_INFO_METHOD;
519 ji->data.method = mono_marshal_get_remoting_invoke_with_check (ji->data.method);
522 case MONO_PATCH_INFO_FIELD:
523 case MONO_PATCH_INFO_SFLDA: {
524 MonoClass *klass = decode_class_info (aot_module, data [1]);
525 mono_class_init (klass);
526 ji->data.field = mono_class_get_field (klass, (guint32)data [0]);
529 case MONO_PATCH_INFO_INTERNAL_METHOD:
530 ji->data.name = aot_module->icall_table [(guint32)data];
531 g_assert (ji->data.name);
532 //printf ("A: %s.\n", ji->data.name);
534 case MONO_PATCH_INFO_SWITCH:
535 ji->table_size = (int)data [0];
536 table = g_new (gpointer, ji->table_size);
537 ji->data.target = table;
538 for (i = 0; i < ji->table_size; i++) {
539 table [i] = data [i + 1];
542 case MONO_PATCH_INFO_R4:
543 case MONO_PATCH_INFO_R8:
544 ji->data.target = data;
546 case MONO_PATCH_INFO_LDSTR:
547 case MONO_PATCH_INFO_LDTOKEN:
548 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
549 image = aot_module->image_table [(int)data [0]];
550 ji->data.token = mono_jump_info_token_new (mp, image, (int)data [1]);
552 case MONO_PATCH_INFO_EXC_NAME:
553 ji->data.klass = decode_class_info (aot_module, data);
554 g_assert (ji->data.klass);
555 mono_class_init (ji->data.klass);
556 ji->data.name = ji->data.klass->name;
558 case MONO_PATCH_INFO_METHOD_REL:
559 ji->data.offset = (int)data [0];
562 g_warning ("unhandled type %d", ji->type);
563 g_assert_not_reached ();
567 ji->next = patch_info;
571 info = (gpointer)((guint8*)info + 4);
572 buf_len = *(guint32*)info;
573 info = (gpointer)((guint8*)info + 4);
574 mono_debug_add_aot_method (domain, method, code, (guint8*)info, buf_len);
576 if (use_loaded_code) {
577 /* disable write protection */
578 #ifndef PLATFORM_WIN32
579 page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
580 pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
581 err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
586 g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
591 /* Do this outside the lock to avoid deadlocks */
592 LeaveCriticalSection (&aot_mutex);
593 mono_arch_patch_code (method, domain, code, patch_info, TRUE);
594 EnterCriticalSection (&aot_mutex);
596 if (aot_module->opts & MONO_OPT_SHARED)
597 /* No need to cache patches */
598 mono_mempool_destroy (mp);
600 minfo->patch_info = patch_info;
603 mono_jit_stats.methods_aot++;
606 jinfo->code_size = code_len;
607 jinfo->used_regs = used_int_regs;
608 jinfo->method = method;
609 jinfo->code_start = code;
610 jinfo->domain_neutral = (aot_module->opts & MONO_OPT_SHARED) != 0;
613 mono_g_hash_table_insert (aot_module->methods, method, minfo);
620 mono_aot_get_method (MonoDomain *domain, MonoMethod *method)
624 EnterCriticalSection (&aot_mutex);
625 info = mono_aot_get_method_inner (domain, method);
626 LeaveCriticalSection (&aot_mutex);
628 /* Do this outside the lock */
630 mono_jit_info_table_add (domain, info);
638 emit_section_change (FILE *fp, const char *section_name, int subsection_index)
641 /* For solaris as, GNU as should accept the same */
642 fprintf (fp, ".section \"%s\"\n", section_name);
644 fprintf (fp, "%s %d\n", section_name, subsection_index);
650 write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align)
654 emit_section_change (fp, ".text", 1);
656 fprintf (fp, ".globl %s\n", name);
657 fprintf (fp, "\t.align %d\n", align);
658 fprintf (fp, "\t.type %s,#object\n", name);
659 fprintf (fp, "\t.size %s,%d\n", name, size);
660 fprintf (fp, "%s:\n", name);
661 for (i = 0; i < size; i++) {
662 fprintf (fp, ".byte %d\n", buf [i]);
669 write_string_symbol (FILE *fp, const char *name, const char *value)
671 emit_section_change (fp, ".text", 1);
672 fprintf (fp, ".globl %s\n", name);
673 fprintf (fp, "%s:\n", name);
674 fprintf (fp, "\t%s \"%s\"\n", AS_STRING_DIRECTIVE, value);
678 mono_get_field_token (MonoClassField *field)
680 MonoClass *klass = field->parent;
683 for (i = 0; i < klass->field.count; ++i) {
684 if (field == &klass->fields [i])
685 return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i);
688 g_assert_not_reached ();
693 get_image_index (MonoAotCompile *cfg, MonoImage *image)
697 index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image));
701 index = g_hash_table_size (cfg->image_hash);
702 g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1));
703 g_ptr_array_add (cfg->image_table, image);
709 emit_image_index (MonoAotCompile *cfg, MonoImage *image)
713 image_index = get_image_index (cfg, image);
715 fprintf (cfg->fp, "\t.long %d\n", image_index);
719 cond_emit_klass_label (MonoAotCompile *cfg, MonoClass *klass)
721 char *l1, *el = NULL;
723 if ((l1 = g_hash_table_lookup (cfg->ref_hash, klass)))
726 if (!klass->type_token) {
727 g_assert (klass->rank > 0);
728 el = cond_emit_klass_label (cfg, klass->element_class);
731 fprintf (cfg->fp, "\t.align %d\n", sizeof (gpointer));
732 l1 = g_strdup_printf ("klass_p_%08x_%p", klass->type_token, klass);
733 fprintf (cfg->fp, "%s:\n", l1);
734 fprintf (cfg->fp, "\t.long 0x%08x\n", klass->type_token);
735 emit_image_index (cfg, klass->image);
738 fprintf (cfg->fp, "\t.long %d\n", klass->rank);
739 fprintf (cfg->fp, "\t.long %s\n", el);
742 g_hash_table_insert (cfg->ref_hash, klass, l1);
748 cond_emit_field_label (MonoAotCompile *cfg, MonoJumpInfo *patch_info)
750 MonoClassField *field = patch_info->data.field;
754 if ((l1 = g_hash_table_lookup (cfg->ref_hash, field)))
757 l2 = cond_emit_klass_label (cfg, field->parent);
758 fprintf (cfg->fp, "\t.align %d\n", sizeof (gpointer));
759 token = mono_get_field_token (field);
761 l1 = g_strdup_printf ("klass_p_%08x_%p", token, field);
762 fprintf (cfg->fp, "%s:\n", l1);
763 fprintf (cfg->fp, "\t.long 0x%08x\n", token);
764 fprintf (cfg->fp, "\t.long %s\n", l2);
766 g_hash_table_insert (cfg->ref_hash, field, l1);
772 compare_patches (gconstpointer a, gconstpointer b)
776 i = (*(MonoJumpInfo**)a)->ip.i;
777 j = (*(MonoJumpInfo**)b)->ip.i;
789 emit_method (MonoAotCompile *acfg, MonoCompile *cfg)
795 guint8 *code, *mname;
796 int func_alignment = 16;
798 MonoJumpInfo *patch_info;
799 MonoMethodHeader *header;
802 method = cfg->method;
803 code = cfg->native_code;
804 header = ((MonoMethodNormal*)method)->header;
806 emit_section_change (tmpfp, ".text", 0);
807 mname = g_strdup_printf ("m_%x", mono_metadata_token_index (method->token));
808 fprintf (tmpfp, "\t.align %d\n", func_alignment);
809 fprintf (tmpfp, ".globl %s\n", mname);
811 fprintf (tmpfp, "\t.type %s,#function\n", mname);
813 fprintf (tmpfp, "\t.type %s,@function\n", mname);
815 fprintf (tmpfp, "%s:\n", mname);
817 for (i = 0; i < cfg->code_len; i++)
818 fprintf (tmpfp, ".byte %d\n", (unsigned int) code [i]);
820 emit_section_change (tmpfp, ".text", 1);
822 /* Sort relocations */
823 patches = g_ptr_array_new ();
824 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next)
825 g_ptr_array_add (patches, patch_info);
826 g_ptr_array_sort (patches, compare_patches);
829 for (pindex = 0; pindex < patches->len; ++pindex) {
830 patch_info = g_ptr_array_index (patches, pindex);
831 switch (patch_info->type) {
832 case MONO_PATCH_INFO_LABEL:
833 case MONO_PATCH_INFO_BB:
834 /* relative jumps are no problem, there is no need to handle then here */
836 case MONO_PATCH_INFO_SWITCH: {
837 gpointer *table = (gpointer *)patch_info->data.target;
840 fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
841 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
842 fprintf (tmpfp, "\t.long %d\n", patch_info->table_size);
844 for (k = 0; k < patch_info->table_size; k++) {
845 fprintf (tmpfp, "\t.long %d\n", (int)table [k]);
850 case MONO_PATCH_INFO_INTERNAL_METHOD: {
853 icall_index = (guint32)g_hash_table_lookup (acfg->icall_hash, patch_info->data.name);
855 icall_index = g_hash_table_size (acfg->icall_hash) + 1;
856 g_hash_table_insert (acfg->icall_hash, (gpointer)patch_info->data.name,
857 GUINT_TO_POINTER (icall_index));
858 g_ptr_array_add (acfg->icall_table, (gpointer)patch_info->data.name);
860 patch_info->data.name = g_strdup_printf ("%d", icall_index - 1);
864 case MONO_PATCH_INFO_METHODCONST:
865 case MONO_PATCH_INFO_METHOD:
866 case MONO_PATCH_INFO_METHOD_JUMP: {
868 * The majority of patches are for methods, so we emit
869 * them inline instead of defining a label for them to
870 * decrease the number of relocations.
872 guint32 image_index = get_image_index (acfg, patch_info->data.method->klass->image);
873 guint32 token = patch_info->data.method->token;
874 g_assert (image_index < 256);
875 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
877 patch_info->data.name =
878 g_strdup_printf ("%d", (image_index << 24) + (mono_metadata_token_index (token)));
882 case MONO_PATCH_INFO_WRAPPER: {
887 m = mono_marshal_method_from_wrapper (patch_info->data.method);
888 image_index = get_image_index (acfg, m->klass->image);
890 g_assert (image_index < 256);
891 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
893 fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
894 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
895 fprintf (tmpfp, "\t.long %d\n", patch_info->data.method->wrapper_type);
896 fprintf (tmpfp, "\t.long %d\n", (image_index << 24) + (mono_metadata_token_index (token)));
900 case MONO_PATCH_INFO_FIELD:
901 patch_info->data.name = cond_emit_field_label (acfg, patch_info);
904 case MONO_PATCH_INFO_CLASS:
905 case MONO_PATCH_INFO_IID:
906 patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
909 case MONO_PATCH_INFO_IMAGE:
910 patch_info->data.name = g_strdup_printf ("%d", get_image_index (acfg, patch_info->data.image));
913 case MONO_PATCH_INFO_EXC_NAME: {
917 mono_class_from_name (mono_defaults.exception_class->image,
918 "System", patch_info->data.target);
920 patch_info->data.name = cond_emit_klass_label (acfg, ex_class);
924 case MONO_PATCH_INFO_R4:
925 fprintf (tmpfp, "\t.align 8\n");
926 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
927 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
930 case MONO_PATCH_INFO_R8:
931 fprintf (tmpfp, "\t.align 8\n");
932 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
933 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
934 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1));
937 case MONO_PATCH_INFO_METHOD_REL:
938 fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
939 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
940 fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.offset);
943 case MONO_PATCH_INFO_VTABLE:
944 case MONO_PATCH_INFO_CLASS_INIT:
945 patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
948 case MONO_PATCH_INFO_SFLDA:
949 patch_info->data.name = cond_emit_field_label (acfg, patch_info);
952 case MONO_PATCH_INFO_LDSTR:
953 case MONO_PATCH_INFO_LDTOKEN:
954 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
955 fprintf (tmpfp, "\t.align 8\n");
956 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
957 fprintf (tmpfp, "\t.long 0x%08x\n", get_image_index (acfg, patch_info->data.token->image));
958 fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.token->token);
962 g_warning ("unable to handle jump info %d", patch_info->type);
963 g_assert_not_reached ();
967 fprintf (tmpfp, ".globl %s_p\n", mname);
968 fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
969 fprintf (tmpfp, "%s_p:\n", mname);
971 fprintf (tmpfp, "\t.long %d\n", cfg->code_len);
972 fprintf (tmpfp, "\t.long %d\n", cfg->used_int_regs);
974 /* Exception table */
975 if (header->num_clauses) {
976 MonoJitInfo *jinfo = cfg->jit_info;
978 fprintf (tmpfp, "\t.long %d\n", jinfo->exvar_offset);
980 for (k = 0; k < header->num_clauses; ++k) {
981 MonoJitExceptionInfo *ei = &jinfo->clauses [k];
983 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
984 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->data.filter - code);
986 /* fixme: tokens are not global */
987 fprintf (tmpfp, "\t.long %d\n", ei->data.token);
989 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_start - code);
990 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_end - code);
991 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->handler_start - code);
996 if (cfg->opt & MONO_OPT_SHARED) {
997 fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list));
998 for (l = cfg->ldstr_list; l; l = l->next) {
999 fprintf (tmpfp, "\t.long 0x%08lx\n", (long)l->data);
1003 /* Used only in shared mode */
1004 g_assert (!cfg->ldstr_list);
1006 //printf ("M: %s (%s).\n", mono_method_full_name (method, TRUE), mname);
1009 guint32 last_offset;
1013 for (pindex = 0; pindex < patches->len; ++pindex) {
1015 patch_info = g_ptr_array_index (patches, pindex);
1017 if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
1018 (patch_info->type == MONO_PATCH_INFO_BB))
1022 //printf ("T: %d O: %d.\n", patch_info->type, patch_info->ip.i);
1023 offset = patch_info->ip.i - last_offset;
1024 last_offset = patch_info->ip.i;
1026 /* Encode type+position compactly */
1027 g_assert (patch_info->type < 64);
1028 if (offset < 1024 - 1) {
1029 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + (offset >> 8));
1030 fprintf (tmpfp, "\t.byte %d\n", offset & ((1 << 8) - 1));
1032 fprintf (tmpfp, "\t.align 4\n");
1036 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + 3);
1037 fprintf (tmpfp, "\t.byte %d\n", 255);
1039 fprintf (tmpfp, "\t.align 4\n");
1041 fprintf (tmpfp, "\t.long %d\n", offset);
1044 switch (patch_info->type) {
1045 case MONO_PATCH_INFO_METHODCONST:
1046 case MONO_PATCH_INFO_METHOD:
1047 case MONO_PATCH_INFO_METHOD_JUMP:
1048 case MONO_PATCH_INFO_CLASS:
1049 case MONO_PATCH_INFO_IID:
1050 case MONO_PATCH_INFO_FIELD:
1051 case MONO_PATCH_INFO_INTERNAL_METHOD:
1052 case MONO_PATCH_INFO_IMAGE:
1053 case MONO_PATCH_INFO_VTABLE:
1054 case MONO_PATCH_INFO_CLASS_INIT:
1055 case MONO_PATCH_INFO_SFLDA:
1056 case MONO_PATCH_INFO_EXC_NAME:
1057 fprintf (tmpfp, "\t.long %s\n", patch_info->data.name);
1060 case MONO_PATCH_INFO_SWITCH:
1061 case MONO_PATCH_INFO_R4:
1062 case MONO_PATCH_INFO_R8:
1063 case MONO_PATCH_INFO_METHOD_REL:
1064 case MONO_PATCH_INFO_LDSTR:
1065 case MONO_PATCH_INFO_LDTOKEN:
1066 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
1067 case MONO_PATCH_INFO_WRAPPER:
1068 fprintf (tmpfp, "\t.long %s_p_%d\n", mname, j);
1071 case MONO_PATCH_INFO_LABEL:
1072 case MONO_PATCH_INFO_BB:
1075 g_warning ("unable to handle jump info %d", patch_info->type);
1076 g_assert_not_reached ();
1083 * 0 is PATCH_INFO_BB, which can't be in the file.
1085 /* NULL terminated array */
1086 fprintf (tmpfp, "\t.long 0\n");
1092 mono_debug_serialize_debug_info (cfg, &buf, &buf_len);
1094 fprintf (tmpfp, "\t.long %d\n", buf_len);
1096 for (i = 0; i < buf_len; ++i)
1097 fprintf (tmpfp, ".byte %d\n", (unsigned int) buf [i]);
1103 /* fixme: save the rest of the required infos */
1109 mono_compile_assembly (MonoAssembly *ass, guint32 opts)
1112 MonoImage *image = ass->image;
1114 char *com, *tmpfname, *opts_str;
1118 int ccount = 0, mcount = 0, lmfcount = 0, abscount = 0, wrappercount = 0, ocount = 0;
1119 GHashTable *ref_hash;
1120 MonoAotCompile *acfg;
1123 printf ("Mono Ahead of Time compiler - compiling assembly %s\n", image->name);
1125 i = g_file_open_tmp ("mono_aot_XXXXXX", &tmpfname, NULL);
1126 tmpfp = fdopen (i, "w+");
1129 ref_hash = g_hash_table_new (NULL, NULL);
1131 acfg = g_new0 (MonoAotCompile, 1);
1133 acfg->ref_hash = ref_hash;
1134 acfg->icall_hash = g_hash_table_new (NULL, NULL);
1135 acfg->icall_table = g_ptr_array_new ();
1136 acfg->image_hash = g_hash_table_new (NULL, NULL);
1137 acfg->image_table = g_ptr_array_new ();
1139 write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid);
1141 write_string_symbol (tmpfp, "mono_aot_version", MONO_AOT_FILE_VERSION);
1143 opts_str = g_strdup_printf ("%d", opts);
1144 write_string_symbol (tmpfp, "mono_aot_opt_flags", opts_str);
1147 emitted = g_new0 (gboolean, image->tables [MONO_TABLE_METHOD].rows);
1149 for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
1150 MonoJumpInfo *patch_info;
1152 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
1153 method = mono_get_method (image, token, NULL);
1155 /* fixme: maybe we can also precompile wrapper methods */
1156 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
1157 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
1158 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
1159 (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
1160 //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
1166 /* fixme: we need to patch the IP for the LMF in that case */
1167 if (method->save_lmf) {
1168 //printf ("Skip (needs lmf): %s\n", mono_method_full_name (method, TRUE));
1173 //printf ("START: %s\n", mono_method_full_name (method, TRUE));
1174 //mono_compile_method (method);
1176 cfg = mini_method_compile (method, opts, mono_root_domain, FALSE, 0);
1179 if (cfg->disable_aot) {
1180 printf ("Skip (other): %s\n", mono_method_full_name (method, TRUE));
1186 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1187 if (patch_info->type == MONO_PATCH_INFO_ABS) {
1188 /* unable to handle this */
1189 //printf ("Skip (abs addr): %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
1200 /* remoting-invoke-with-check wrappers are very common */
1201 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1202 if ((patch_info->type == MONO_PATCH_INFO_METHOD) &&
1203 ((patch_info->data.method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)))
1204 patch_info->type = MONO_PATCH_INFO_WRAPPER;
1208 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1209 if ((patch_info->type == MONO_PATCH_INFO_METHOD ||
1210 patch_info->type == MONO_PATCH_INFO_METHODCONST) &&
1211 patch_info->data.method->wrapper_type) {
1212 /* unable to handle this */
1213 //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));
1224 //printf ("Compile: %s\n", mono_method_full_name (method, TRUE));
1227 emit_method (acfg, cfg);
1229 mono_destroy_compile (cfg);
1235 * The icall and image tables are small but referenced in a lot of places.
1236 * So we emit them at once, and reference their elements by an index
1237 * instead of an assembly label to cut back on the number of relocations.
1240 /* Emit icall table */
1242 symbol = g_strdup_printf ("mono_icall_table");
1243 emit_section_change (tmpfp, ".text", 1);
1244 fprintf (tmpfp, ".globl %s\n", symbol);
1245 fprintf (tmpfp, "\t.align 8\n");
1246 fprintf (tmpfp, "%s:\n", symbol);
1247 fprintf (tmpfp, ".long %d\n", acfg->icall_table->len);
1248 for (i = 0; i < acfg->icall_table->len; i++)
1249 fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, (char*)g_ptr_array_index (acfg->icall_table, i));
1251 /* Emit image table */
1253 symbol = g_strdup_printf ("mono_image_table");
1254 emit_section_change (tmpfp, ".text", 1);
1255 fprintf (tmpfp, ".globl %s\n", symbol);
1256 fprintf (tmpfp, "\t.align 8\n");
1257 fprintf (tmpfp, "%s:\n", symbol);
1258 fprintf (tmpfp, ".long %d\n", acfg->image_table->len);
1259 for (i = 0; i < acfg->image_table->len; i++)
1260 fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, ((MonoImage*)g_ptr_array_index (acfg->image_table, i))->guid);
1263 * g_module_symbol takes a lot of time for failed lookups, so we emit
1264 * a table which contains one bit for each method. This bit specifies
1265 * whenever the method is emitted or not.
1268 symbol = g_strdup_printf ("mono_methods_present_table");
1269 emit_section_change (tmpfp, ".text", 1);
1270 fprintf (tmpfp, ".globl %s\n", symbol);
1271 fprintf (tmpfp, "\t.align 8\n");
1272 fprintf (tmpfp, "%s:\n", symbol);
1277 nrows = image->tables [MONO_TABLE_METHOD].rows;
1278 for (i = 0; i < nrows / 32 + 1; ++i) {
1280 for (k = 0; k < 32; ++k) {
1281 if (emitted [(i * 32) + k])
1284 //printf ("EMITTED [%d] = %d.\n", i, b);
1285 fprintf (tmpfp, "\t.long %d\n", w);
1291 com = g_strdup_printf ("as %s -o %s.o", tmpfname, tmpfname);
1292 printf ("Executing the native assembler: %s\n", com);
1293 if (system (com) != 0) {
1300 com = g_strdup_printf ("ld -shared -G -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
1302 com = g_strdup_printf ("ld -shared -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
1304 printf ("Executing the native linker: %s\n", com);
1305 if (system (com) != 0) {
1311 com = g_strdup_printf ("%s.o", tmpfname);
1314 /*com = g_strdup_printf ("strip --strip-unneeded %s%s", image->name, SHARED_EXT);
1315 printf ("Stripping the binary: %s\n", com);
1319 printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, mcount ? (ccount*100)/mcount : 100);
1320 printf ("%d methods contain absolute addresses (%d%%)\n", abscount, mcount ? (abscount*100)/mcount : 100);
1321 printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, mcount ? (wrappercount*100)/mcount : 100);
1322 printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, mcount ? (lmfcount*100)/mcount : 100);
1323 printf ("%d methods have other problems (%d%%)\n", ocount, mcount ? (ocount*100)/mcount : 100);
1324 //printf ("Retained input file.\n");