9bf96eaad2b8209089c02a6c841c785438143049
[mono.git] / mono / mini / aot.c
1 /*
2  * aot.c: mono Ahead of Time compiler
3  *
4  * Author:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include "config.h"
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <string.h>
15 #ifndef PLATFORM_WIN32
16 #include <sys/mman.h>
17 #else
18 #include <windows.h>
19 #endif
20
21 #include <limits.h>    /* for PAGESIZE */
22 #ifndef PAGESIZE
23 #define PAGESIZE 4096
24 #endif
25
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/os/gc_wrapper.h>
34
35 #include "mini.h"
36
37 #ifdef PLATFORM_WIN32
38 #define SHARED_EXT ".dll"
39 #else
40 #define SHARED_EXT ".so"
41 #endif
42
43 typedef struct MonoAotMethod {
44         MonoJitInfo *info;
45         MonoJumpInfo *patch_info;
46 } MonoAotMethod;
47
48 typedef struct MonoAotModule {
49         /* Optimization flags used to compile the module */
50         guint32 opts;
51         /* Maps MonoMethods to MonoAotMethodInfos */
52         MonoGHashTable *methods;
53         char **icall_table;
54         MonoImage **image_table;
55         guint32* methods_present_table;
56 } MonoAotModule;
57
58 typedef struct MonoAotCompile {
59         FILE *fp;
60         GHashTable *ref_hash;
61         GHashTable *icall_hash;
62         GPtrArray *icall_table;
63         GHashTable *image_hash;
64         GPtrArray *image_table;
65 } MonoAotCompile;
66
67 static MonoGHashTable *aot_modules;
68
69 static CRITICAL_SECTION aot_mutex;
70
71 static guint32 mono_aot_verbose = 0;
72
73 /* For debugging */
74 static gint32 mono_last_aot_method = -1;
75
76 static MonoClass * 
77 decode_class_info (MonoAotModule *module, gpointer *data)
78 {
79         MonoImage *image;
80         MonoClass *klass;
81         
82         image = module->image_table [(guint32)data [1]];
83         g_assert (image);
84
85         if (data [0]) {
86                 return mono_class_get (image, (guint32)data [0]);
87         } else {
88                 klass = decode_class_info (module, data [3]);
89                 return mono_array_class_get (klass, (guint32)data [2]);
90         }
91
92         return NULL;
93 }
94
95 static void
96 load_aot_module (MonoAssembly *assembly, gpointer user_data)
97 {
98         char *aot_name;
99         MonoAotModule *info;
100         gboolean usable = TRUE;
101         char *saved_guid = NULL;
102         char *aot_version = NULL;
103         char *opt_flags = NULL;
104
105         aot_name = g_strdup_printf ("%s.so", assembly->image->name);
106
107         assembly->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY);
108
109         if (!assembly->aot_module)
110                 return;
111
112         g_module_symbol (assembly->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid);
113         g_module_symbol (assembly->aot_module, "mono_aot_version", (gpointer *) &aot_version);
114         g_module_symbol (assembly->aot_module, "mono_aot_opt_flags", (gpointer *)&opt_flags);
115
116         if (!aot_version || strcmp (aot_version, MONO_AOT_FILE_VERSION)) {
117                 if (mono_aot_verbose > 0)
118                         printf ("AOT module %s has wrong file format version (expected %s got %s)\n", aot_name, MONO_AOT_FILE_VERSION, aot_version);
119                 usable = FALSE;
120         }
121         else
122                 if (!saved_guid || strcmp (assembly->image->guid, saved_guid)) {
123                         if (mono_aot_verbose > 0)
124                                 printf ("AOT module %s is out of date.\n", aot_name);
125                         usable = FALSE;
126                 }
127
128         if (!usable) {
129                 g_free (aot_name);
130                 g_module_close (assembly->aot_module);
131                 assembly->aot_module = NULL;
132                 return;
133         }
134
135         /*
136          * It seems that MonoGHashTables are in the GC heap, so structures
137          * containing them must be in the GC heap as well :(
138          */
139 #ifdef HAVE_BOEHM_GC
140         info = GC_MALLOC (sizeof (MonoAotModule));
141 #else
142         info = g_new0 (MonoAotModule, 1);
143 #endif
144         info->methods = mono_g_hash_table_new (NULL, NULL);
145         sscanf (opt_flags, "%d", &info->opts);
146
147         /* Read image table */
148         {
149                 guint32 table_len, i;
150                 char *table = NULL;
151
152                 g_module_symbol (assembly->aot_module, "mono_image_table", (gpointer *)&table);
153                 g_assert (table);
154
155                 table_len = *(guint32*)table;
156                 table += sizeof (guint32);
157                 info->image_table = g_new0 (MonoImage*, table_len);
158                 for (i = 0; i < table_len; ++i) {
159                         info->image_table [i] = mono_image_loaded_by_guid (table);
160                         if (!info->image_table [i]) {
161                                 if (mono_aot_verbose > 0)
162                                         printf ("AOT module %s is out of date.\n", aot_name);
163                                 g_free (info->methods);
164                                 g_free (info->image_table);
165                                 g_free (info);
166                                 g_free (aot_name);
167                                 g_module_close (assembly->aot_module);
168                                 assembly->aot_module = NULL;
169                                 return;
170                         }
171                         table += strlen (table) + 1;
172                 }
173         }
174
175         /* Read icall table */
176         {
177                 guint32 table_len, i;
178                 char *table = NULL;
179
180                 g_module_symbol (assembly->aot_module, "mono_icall_table", (gpointer *)&table);
181                 g_assert (table);
182
183                 table_len = *(guint32*)table;
184                 table += sizeof (guint32);
185                 info->icall_table = g_new0 (char*, table_len);
186                 for (i = 0; i < table_len; ++i) {
187                         info->icall_table [i] = table;
188                         table += strlen (table) + 1;
189                 }
190         }
191
192         /* Read methods present table */
193         g_module_symbol (assembly->aot_module, "mono_methods_present_table", (gpointer *)&info->methods_present_table);
194         g_assert (info->methods_present_table);
195
196         EnterCriticalSection (&aot_mutex);
197         mono_g_hash_table_insert (aot_modules, assembly, info);
198         LeaveCriticalSection (&aot_mutex);
199
200         if (mono_aot_verbose > 0)
201                 printf ("Loaded AOT Module for %s.\n", assembly->image->name);
202 }
203
204 void
205 mono_aot_init (void)
206 {
207         InitializeCriticalSection (&aot_mutex);
208
209         aot_modules = mono_g_hash_table_new (NULL, NULL);
210
211         mono_install_assembly_load_hook (load_aot_module, NULL);
212
213         if (getenv ("MONO_LASTAOT"))
214                 mono_last_aot_method = atoi (getenv ("MONO_LASTAOT"));
215 }
216  
217 static MonoJitInfo *
218 mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
219 {
220         MonoClass *klass = method->klass;
221         MonoAssembly *ass = klass->image->assembly;
222         MonoJumpInfo *patch_info = NULL;
223         GModule *module = ass->aot_module;
224         char method_label [256];
225         char info_label [256];
226         guint8 *code = NULL;
227         gpointer *info;
228         guint code_len, used_int_regs, used_strings;
229         MonoAotModule *aot_module;
230         MonoAotMethod *minfo;
231         MonoJitInfo *jinfo;
232         MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
233         int i;
234
235         if (!module)
236                 return NULL;
237
238         if (!method->token)
239                 return NULL;
240
241         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
242                 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
243                 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
244                 (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
245                 return NULL;
246
247         aot_module = (MonoAotModule*)mono_g_hash_table_lookup (aot_modules, ass);
248
249         g_assert (klass->inited);
250
251         minfo = mono_g_hash_table_lookup (aot_module->methods, method);
252         if (minfo) {
253                 /* Duplicate jinfo */
254                 jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
255                 memcpy (jinfo, minfo->info, sizeof (MonoJitInfo));
256                 if (jinfo->clauses) {
257                         jinfo->clauses = 
258                                 mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
259                         memcpy (jinfo->clauses, minfo->info->clauses, sizeof (MonoJitExceptionInfo) * header->num_clauses);
260                 }
261
262                 /* This method was already loaded in another appdomain */
263                 if (aot_module->opts & MONO_OPT_SHARED)
264                         /* Use the same method in the new appdomain */
265                         ;
266                 else if (!minfo->patch_info)
267                         /* Use the same method in the new appdomain */
268                         ;                       
269                 else {
270                         /* Create a copy of the original method and apply relocations */
271
272                         code = mono_mempool_alloc (domain->code_mp, minfo->info->code_size);
273                         memcpy (code, minfo->info->code_start, minfo->info->code_size);
274
275                         if (mono_aot_verbose > 1)
276                                 printf ("REUSE METHOD: %s %p - %p.\n", mono_method_full_name (method, TRUE), code, (char*)code + code_len);
277
278                         /* Do this outside the lock to avoid deadlocks */
279                         LeaveCriticalSection (&aot_mutex);
280                         mono_arch_patch_code (method, domain, code, minfo->patch_info);
281                         EnterCriticalSection (&aot_mutex);
282
283                         /* Relocate jinfo */
284                         jinfo->code_start = code;
285                         if (jinfo->clauses) {
286                                 for (i = 0; i < header->num_clauses; ++i) {
287                                         MonoJitExceptionInfo *ei = &jinfo->clauses [i];
288                                         gint32 offset = code - (guint8*)minfo->info->code_start;
289
290                                         if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
291                                                 ei->data.filter = (guint8*)ei->data.filter + offset;
292                                         ei->try_start = (guint8*)ei->try_start + offset;
293                                         ei->try_end = (guint8*)ei->try_end + offset;
294                                         ei->handler_start = (guint8*)ei->handler_start + offset;
295                                 }
296                         }
297                 }
298
299                 return jinfo;
300         }
301
302         /* Do a fast check to see whenever the method exists */
303         {
304                 guint32 index = mono_metadata_token_index (method->token) - 1;
305                 guint32 w;
306                 w = aot_module->methods_present_table [index / 32];
307                 if (! (w & (1 << (index % 32)))) {
308                         if (mono_aot_verbose > 1)
309                                 printf ("NOT FOUND: %s.\n", mono_method_full_name (method, TRUE));
310                         return NULL;
311                 }
312         }
313
314         sprintf (method_label, "m_%x", mono_metadata_token_index (method->token));
315
316         if (!g_module_symbol (module, method_label, (gpointer *)&code))
317                 return NULL;
318
319         sprintf (info_label, "%s_p", method_label);
320
321         if (!g_module_symbol (module, info_label, (gpointer *)&info))
322                 return NULL;
323
324         {
325                 static int count = 0;
326
327                 count ++;
328
329                 if (mono_last_aot_method != -1) {
330                         if (count > mono_last_aot_method)
331                                 return NULL;
332                         else
333                                 if (count == mono_last_aot_method)
334                                         printf ("LAST AOT METHOD: %s.%s.%s.\n", klass->name_space, klass->name, method->name);
335                 }
336         }
337
338 #ifdef HAVE_BOEHM_GC
339         minfo = GC_MALLOC (sizeof (MonoAotMethod));
340 #else
341         minfo = g_new0 (MonoAotMethod, 1);
342 #endif
343
344         jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
345
346         code_len = GPOINTER_TO_UINT (*((gpointer **)info));
347         info++;
348         used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info));
349         info++;
350
351         /* 
352          * Enabling this will place the caller and the callee close to each other
353          * in memory, possibly improving cache behavior.
354          */
355 /*
356         {
357                 guint8 *code2;
358                 code2 = mono_mempool_alloc (domain->code_mp, code_len);
359                 memcpy (code2, code, code_len);
360                 code = code2;
361         }
362 */
363
364         if (mono_aot_verbose > 1)
365                 printf ("FOUND AOT compiled code for %s %p - %p %p\n", mono_method_full_name (method, TRUE), code, code + code_len, info);
366
367         /* Exception table */
368         if (header->num_clauses) {
369                 jinfo->clauses = 
370                         mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
371                 jinfo->num_clauses = header->num_clauses;
372
373                 jinfo->exvar_offset = GPOINTER_TO_UINT (*((gpointer**)info));
374                 info ++;
375
376                 for (i = 0; i < header->num_clauses; ++i) {
377                         MonoExceptionClause *ec = &header->clauses [i];                         
378                         MonoJitExceptionInfo *ei = &jinfo->clauses [i];
379
380                         ei->flags = ec->flags;
381                         if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
382                                 ei->data.filter = code + GPOINTER_TO_UINT (*((gpointer**)info));
383                         else
384                                 ei->data.token = GPOINTER_TO_UINT (*((gpointer**)info));
385                         info ++;
386                         ei->try_start = code + GPOINTER_TO_UINT (*((gpointer**)info));
387                         info ++;
388                         ei->try_end = code + GPOINTER_TO_UINT (*((gpointer**)info));
389                         info ++;
390                         ei->handler_start = code + GPOINTER_TO_UINT (*((gpointer**)info));
391                         info ++;
392                 }
393         }
394
395         if (aot_module->opts & MONO_OPT_SHARED) {
396                 used_strings = GPOINTER_TO_UINT (*((gpointer **)info));
397                 info++;
398         }
399         else
400                 used_strings = 0;
401
402         for (i = 0; i < used_strings; i++) {
403                 guint token =  GPOINTER_TO_UINT (*((gpointer **)info));
404                 info++;
405                 mono_ldstr (mono_root_domain, klass->image, mono_metadata_token_index (token));
406         }
407
408         if (*info) {
409                 MonoMemPool *mp;
410                 MonoImage *image;
411                 guint8 *page_start;
412                 gpointer *table;
413                 int pages;
414                 int i, err;
415                 guint32 last_offset;
416
417                 if (aot_module->opts & MONO_OPT_SHARED)
418                         mp = mono_mempool_new ();
419                 else
420                         mp = domain->mp;
421
422                 last_offset = 0;
423                 while (*info) {
424                         MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
425                         gpointer *data;
426
427                         guint8 b1, b2;
428
429                         b1 = *(guint8*)info;
430                         b2 = *((guint8*)info + 1);
431
432                         info = (gpointer*)((guint8*)info + 2);
433
434                         ji->type = b1 >> 2;
435
436                         if (((b1 & (1 + 2)) == 3) && (b2 == 255)) {
437                                 ji->ip.i = GPOINTER_TO_UINT (*info);
438                                 info ++;
439                         }
440                         else
441                                 ji->ip.i = (((guint32)(b1 & (1 + 2))) << 8) + b2;
442
443                         ji->ip.i += last_offset;
444                         last_offset = ji->ip.i;
445                         //printf ("T: %d O: %d.\n", ji->type, ji->ip.i);
446
447                         data = *((gpointer **)info);
448
449                         switch (ji->type) {
450                         case MONO_PATCH_INFO_CLASS:
451                         case MONO_PATCH_INFO_IID:
452                                 ji->data.klass = decode_class_info (aot_module, data);
453                                 g_assert (ji->data.klass);
454                                 mono_class_init (ji->data.klass);
455                                 break;
456                         case MONO_PATCH_INFO_VTABLE:
457                                 ji->data.klass = decode_class_info (aot_module, data);
458                                 g_assert (ji->data.klass);
459                                 mono_class_init (ji->data.klass);
460                                 break;
461                         case MONO_PATCH_INFO_IMAGE:
462                                 ji->data.image = aot_module->image_table [(guint32)data];
463                                 g_assert (ji->data.image);
464                                 break;
465                         case MONO_PATCH_INFO_METHOD:
466                         case MONO_PATCH_INFO_METHODCONST: {
467                                 guint32 image_index, token;
468
469                                 image_index = (guint32)data >> 24;
470                                 token = MONO_TOKEN_METHOD_DEF | ((guint32)data & 0xffffff);
471
472                                 image = aot_module->image_table [image_index];
473                                 ji->data.method = mono_get_method (image, token, NULL);
474                                 g_assert (ji->data.method);
475                                 mono_class_init (ji->data.method->klass);
476
477                                 break;
478                         }
479                         case MONO_PATCH_INFO_FIELD:
480                         case MONO_PATCH_INFO_SFLDA: {
481                                 MonoClass *klass = decode_class_info (aot_module, data [1]);
482                                 mono_class_init (klass);
483                                 ji->data.field = mono_class_get_field (klass, (guint32)data [0]);
484                                 break;
485                         }
486                         case MONO_PATCH_INFO_INTERNAL_METHOD:
487                                 ji->data.name = aot_module->icall_table [(guint32)data];
488                                 g_assert (ji->data.name);
489                                 //printf ("A: %s.\n", ji->data.name);
490                                 break;
491                         case MONO_PATCH_INFO_SWITCH:
492                                 ji->table_size = (int)data [0];
493                                 table = g_new (gpointer, ji->table_size);
494                                 ji->data.target = table;
495                                 for (i = 0; i < ji->table_size; i++) {
496                                         table [i] = data [i + 1];
497                                 }
498                                 break;
499                         case MONO_PATCH_INFO_R4:
500                         case MONO_PATCH_INFO_R8:
501                                 ji->data.target = data;
502                                 break;
503                         case MONO_PATCH_INFO_LDSTR:
504                         case MONO_PATCH_INFO_LDTOKEN:
505                         case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
506                                 ji->data.target = *data;
507                                 break;
508                         case MONO_PATCH_INFO_EXC_NAME:
509                                 ji->data.klass = decode_class_info (aot_module, data);
510                                 g_assert (ji->data.klass);
511                                 mono_class_init (ji->data.klass);
512                                 ji->data.name = ji->data.klass->name;
513                                 break;
514                         case MONO_PATCH_INFO_METHOD_REL:
515                                 ji->data.offset = (int)data [0];
516                                 break;
517                         default:
518                                 g_warning ("unhandled type %d", ji->type);
519                                 g_assert_not_reached ();
520                         }
521
522                         info++;
523                         ji->next = patch_info;
524                         patch_info = ji;
525                 }
526
527 #ifndef PLATFORM_WIN32
528                 /* disable write protection */
529                 page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
530                 pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
531                 err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
532                 g_assert (err == 0);
533 #else
534                 {
535                         DWORD oldp;
536                         g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
537                 }
538 #endif
539
540                 /* Do this outside the lock to avoid deadlocks */
541                 LeaveCriticalSection (&aot_mutex);
542                 mono_arch_patch_code (method, domain, code, patch_info);
543                 EnterCriticalSection (&aot_mutex);
544
545                 if (aot_module->opts & MONO_OPT_SHARED)
546                         /* No need to cache patches */
547                         mono_mempool_destroy (mp);
548                 else
549                         minfo->patch_info = patch_info;
550         }
551
552         mono_jit_stats.methods_aot++;
553
554         {
555                 jinfo->code_size = code_len;
556                 jinfo->used_regs = used_int_regs;
557                 jinfo->method = method;
558                 jinfo->code_start = code;
559                 jinfo->domain_neutral = (aot_module->opts & MONO_OPT_SHARED) != 0;
560
561                 minfo->info = jinfo;
562                 mono_g_hash_table_insert (aot_module->methods, method, minfo);
563
564                 return jinfo;
565         }
566 }
567
568 MonoJitInfo*
569 mono_aot_get_method (MonoDomain *domain, MonoMethod *method)
570 {
571         MonoJitInfo *info;
572
573         EnterCriticalSection (&aot_mutex);
574         info = mono_aot_get_method_inner (domain, method);
575         LeaveCriticalSection (&aot_mutex);
576
577         /* Do this outside the lock */
578         if (info) {
579                 mono_jit_info_table_add (domain, info);
580                 return info;
581         }
582         else
583                 return NULL;
584 }
585
586 #if 0
587 static void
588 write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align)
589 {
590         int i;
591
592         fprintf (fp, ".globl %s\n", name);
593         fprintf (fp, ".text 1 \n\t.align %d\n", align);
594         fprintf (fp, "\t.type %s,@object\n", name);
595         fprintf (fp, "\t.size %s,%d\n", name, size);
596         fprintf (fp, "%s:\n", name);
597         for (i = 0; i < size; i++) { 
598                 fprintf (fp, ".byte %d\n", buf [i]);
599         }
600         
601 }
602 #endif
603
604 static void
605 write_string_symbol (FILE *fp, const char *name, const char *value)
606 {
607         fprintf (fp, ".globl %s\n", name);
608         fprintf (fp, ".text 1\n");
609         fprintf (fp, "%s:\n", name);
610         fprintf (fp, "\t.string \"%s\"\n", value);
611 }
612
613 static guint32
614 mono_get_field_token (MonoClassField *field) 
615 {
616         MonoClass *klass = field->parent;
617         int i;
618
619         for (i = 0; i < klass->field.count; ++i) {
620                 if (field == &klass->fields [i])
621                         return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i);
622         }
623
624         g_assert_not_reached ();
625         return 0;
626 }
627
628 static guint32
629 get_image_index (MonoAotCompile *cfg, MonoImage *image)
630 {
631         guint32 index;
632
633         index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image));
634         if (index)
635                 return index - 1;
636         else {
637                 index = g_hash_table_size (cfg->image_hash);
638                 g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1));
639                 g_ptr_array_add (cfg->image_table, image);
640                 return index;
641         }
642 }
643
644 static void
645 emit_image_index (MonoAotCompile *cfg, MonoImage *image)
646 {
647         guint32 image_index;
648
649         image_index = get_image_index (cfg, image);
650
651         fprintf (cfg->fp, "\t.long %d\n", image_index);
652 }
653
654 static char *
655 cond_emit_klass_label (MonoAotCompile *cfg, MonoClass *klass)
656 {
657         char *l1, *el = NULL;
658
659         if ((l1 = g_hash_table_lookup (cfg->ref_hash, klass))) 
660                 return l1;
661
662         if (!klass->type_token) {
663                 g_assert (klass->rank > 0);
664                 el = cond_emit_klass_label (cfg, klass->element_class);
665         }
666         
667         fprintf (cfg->fp, "\t.align %d\n", sizeof (gpointer));
668         l1 = g_strdup_printf ("klass_p_%08x_%p", klass->type_token, klass);
669         fprintf (cfg->fp, "%s:\n", l1);
670         fprintf (cfg->fp, "\t.long 0x%08x\n", klass->type_token);
671         emit_image_index (cfg, klass->image);
672
673         if (el) {
674                 fprintf (cfg->fp, "\t.long %d\n", klass->rank); 
675                 fprintf (cfg->fp, "\t.long %s\n", el);
676         }
677
678         g_hash_table_insert (cfg->ref_hash, klass, l1);
679
680         return l1;
681 }
682
683 static char *
684 cond_emit_field_label (MonoAotCompile *cfg, MonoJumpInfo *patch_info)
685 {
686         MonoClassField *field = patch_info->data.field;
687         char *l1, *l2;
688         guint token;
689
690         if ((l1 = g_hash_table_lookup (cfg->ref_hash, field))) 
691                 return l1;
692
693         l2 = cond_emit_klass_label (cfg, field->parent);
694         fprintf (cfg->fp, "\t.align %d\n", sizeof (gpointer));
695         token = mono_get_field_token (field);
696         g_assert (token);
697         l1 = g_strdup_printf ("klass_p_%08x_%p", token, field);
698         fprintf (cfg->fp, "%s:\n", l1);
699         fprintf (cfg->fp, "\t.long 0x%08x\n", token);
700         fprintf (cfg->fp, "\t.long %s\n", l2);
701                 
702         g_hash_table_insert (cfg->ref_hash, field, l1);
703
704         return l1;
705 }
706
707 static gint
708 compare_patches (gconstpointer a, gconstpointer b)
709 {
710         int i, j;
711
712         i = (*(MonoJumpInfo**)a)->ip.i;
713         j = (*(MonoJumpInfo**)b)->ip.i;
714
715         if (i < j)
716                 return -1;
717         else
718                 if (i > j)
719                         return 1;
720         else
721                 return 0;
722 }
723
724 static void
725 emit_method (MonoAotCompile *acfg, MonoCompile *cfg)
726 {
727         MonoMethod *method;
728         GList *l;
729         FILE *tmpfp;
730         int i, j, k, pindex;
731         guint8 *code, *mname;
732         int func_alignment = 16;
733         GPtrArray *patches;
734         MonoJumpInfo *patch_info;
735         MonoMethodHeader *header;
736
737         tmpfp = acfg->fp;
738         method = cfg->method;
739         code = cfg->native_code;
740         header = ((MonoMethodNormal*)method)->header;
741
742         fprintf (tmpfp, ".text 0\n");
743         mname = g_strdup_printf ("m_%x", mono_metadata_token_index (method->token));
744         fprintf (tmpfp, "\t.align %d\n", func_alignment);
745         fprintf (tmpfp, ".globl %s\n", mname);
746         fprintf (tmpfp, "\t.type %s,@function\n", mname);
747         fprintf (tmpfp, "%s:\n", mname);
748
749         for (i = 0; i < cfg->code_len; i++) 
750                 fprintf (tmpfp, ".byte %d\n", (unsigned int) code [i]);
751
752         fprintf (tmpfp, ".text 1\n");
753
754         /* Sort relocations */
755         patches = g_ptr_array_new ();
756         for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next)
757                 g_ptr_array_add (patches, patch_info);
758         g_ptr_array_sort (patches, compare_patches);
759
760         j = 0;
761         for (pindex = 0; pindex < patches->len; ++pindex) {
762                 patch_info = g_ptr_array_index (patches, pindex);
763                 switch (patch_info->type) {
764                 case MONO_PATCH_INFO_LABEL:
765                 case MONO_PATCH_INFO_BB:
766                         /* relative jumps are no problem, there is no need to handle then here */
767                         break;
768                 case MONO_PATCH_INFO_SWITCH: {
769                         gpointer *table = (gpointer *)patch_info->data.target;
770                         int k;
771
772                         fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
773                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
774                         fprintf (tmpfp, "\t.long %d\n", patch_info->table_size);
775                         
776                         for (k = 0; k < patch_info->table_size; k++) {
777                                 fprintf (tmpfp, "\t.long %d\n", (int)table [k]);
778                         }
779                         j++;
780                         break;
781                 }
782                 case MONO_PATCH_INFO_INTERNAL_METHOD: {
783                         guint32 icall_index;
784
785                         icall_index = (guint32)g_hash_table_lookup (acfg->icall_hash, patch_info->data.name);
786                         if (!icall_index) {
787                                 icall_index = g_hash_table_size (acfg->icall_hash) + 1;
788                                 g_hash_table_insert (acfg->icall_hash, (gpointer)patch_info->data.name,
789                                                                          GUINT_TO_POINTER (icall_index));
790                                 g_ptr_array_add (acfg->icall_table, (gpointer)patch_info->data.name);
791                         }
792                         patch_info->data.name = g_strdup_printf ("%d", icall_index - 1);
793                         j++;
794                         break;
795                 }
796                 case MONO_PATCH_INFO_METHODCONST:
797                 case MONO_PATCH_INFO_METHOD: {
798                         /*
799                          * The majority of patches are for methods, so we emit
800                          * them inline instead of defining a label for them to
801                          * decrease the number of relocations.
802                          */
803                         guint32 image_index = get_image_index (acfg, patch_info->data.method->klass->image);
804                         guint32 token = patch_info->data.method->token;
805                         g_assert (image_index < 256);
806                         g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
807
808                         patch_info->data.name = 
809                                 g_strdup_printf ("%d", (image_index << 24) + (mono_metadata_token_index (token)));
810                         j++;
811                         break;
812                 }
813                 case MONO_PATCH_INFO_FIELD:
814                         patch_info->data.name = cond_emit_field_label (acfg, patch_info);
815                         j++;
816                         break;
817                 case MONO_PATCH_INFO_CLASS:
818                 case MONO_PATCH_INFO_IID:
819                         patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
820                         j++;
821                         break;
822                 case MONO_PATCH_INFO_IMAGE:
823                         patch_info->data.name = g_strdup_printf ("%d", get_image_index (acfg, patch_info->data.image));
824                         j++;
825                         break;
826                 case MONO_PATCH_INFO_EXC_NAME: {
827                         MonoClass *ex_class;
828                         
829                         ex_class =
830                                 mono_class_from_name (mono_defaults.exception_class->image,
831                                                                           "System", patch_info->data.target);
832                         g_assert (ex_class);
833                         patch_info->data.name = cond_emit_klass_label (acfg, ex_class);
834                         j++;
835                         break;
836                 }
837                 case MONO_PATCH_INFO_R4:
838                         fprintf (tmpfp, "\t.align 8\n");
839                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
840                         fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));     
841                         j++;
842                         break;
843                 case MONO_PATCH_INFO_R8:
844                         fprintf (tmpfp, "\t.align 8\n");
845                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
846                         fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
847                         fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1));
848                         j++;
849                         break;
850                 case MONO_PATCH_INFO_METHOD_REL:
851                         fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
852                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
853                         fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.offset);
854                         j++;
855                         break;
856                 case MONO_PATCH_INFO_VTABLE:
857                         patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
858                         j++;
859                         break;
860                 case MONO_PATCH_INFO_SFLDA:
861                         patch_info->data.name = cond_emit_field_label (acfg, patch_info);
862                         j++;
863                         break;
864                 case MONO_PATCH_INFO_LDSTR:
865                 case MONO_PATCH_INFO_LDTOKEN:
866                 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
867                         fprintf (tmpfp, "\t.align 8\n");
868                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
869                         fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.token);
870                         j++;
871                         break;
872                 default:
873                         g_warning ("unable to handle jump info %d", patch_info->type);
874                         g_assert_not_reached ();
875                 }
876         }
877
878         fprintf (tmpfp, ".globl %s_p\n", mname);
879         fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
880         fprintf (tmpfp, "%s_p:\n", mname);
881
882         fprintf (tmpfp, "\t.long %d\n", cfg->code_len);
883         fprintf (tmpfp, "\t.long %d\n", cfg->used_int_regs);
884
885         /* Exception table */
886         if (header->num_clauses) {
887                 MonoJitInfo *jinfo = cfg->jit_info;
888
889                 fprintf (tmpfp, "\t.long %d\n", jinfo->exvar_offset);
890
891                 for (k = 0; k < header->num_clauses; ++k) {
892                         MonoJitExceptionInfo *ei = &jinfo->clauses [k];
893
894                         if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
895                                 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->data.filter - code);
896                         else
897                                 /* fixme: tokens are not global */
898                                 fprintf (tmpfp, "\t.long %d\n", ei->data.token);
899
900                         fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_start - code);
901                         fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_end - code);
902                         fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->handler_start - code);
903                 }
904         }
905
906         /* String table */
907         if (cfg->opt & MONO_OPT_SHARED) {
908                 fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list));
909                 for (l = cfg->ldstr_list; l; l = l->next) {
910                         fprintf (tmpfp, "\t.long 0x%08lx\n", (long)l->data);
911                 }
912         }
913         else
914                 /* Used only in shared mode */
915                 g_assert (!cfg->ldstr_list);
916
917         //printf ("M: %s (%s).\n", mono_method_full_name (method, TRUE), mname);
918
919         if (j) {
920                 guint32 last_offset;
921                 last_offset = 0;
922
923                 j = 0;
924                 for (pindex = 0; pindex < patches->len; ++pindex) {
925                         guint32 offset;
926                         patch_info = g_ptr_array_index (patches, pindex);
927
928                         if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
929                                 (patch_info->type == MONO_PATCH_INFO_BB))
930                                 /* Nothing to do */
931                                 continue;
932
933                         //printf ("T: %d O: %d.\n", patch_info->type, patch_info->ip.i);
934                         offset = patch_info->ip.i - last_offset;
935                         last_offset = patch_info->ip.i;
936
937                         /* Encode type+position compactly */
938                         g_assert (patch_info->type < 64);
939                         if (offset < 1024 - 1) {
940                                 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + (offset >> 8));
941                                 fprintf (tmpfp, "\t.byte %d\n", offset & ((1 << 8) - 1));
942                         }
943                         else {
944                                 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + 3);
945                                 fprintf (tmpfp, "\t.byte %d\n", 255);
946                                 fprintf (tmpfp, "\t.long %d\n", offset);
947                         }
948
949                         switch (patch_info->type) {
950                         case MONO_PATCH_INFO_METHODCONST:
951                         case MONO_PATCH_INFO_METHOD:
952                         case MONO_PATCH_INFO_CLASS:
953                         case MONO_PATCH_INFO_IID:
954                         case MONO_PATCH_INFO_FIELD:
955                         case MONO_PATCH_INFO_INTERNAL_METHOD:
956                         case MONO_PATCH_INFO_IMAGE:
957                         case MONO_PATCH_INFO_VTABLE:
958                         case MONO_PATCH_INFO_SFLDA:
959                         case MONO_PATCH_INFO_EXC_NAME:
960                                 fprintf (tmpfp, "\t.long %s\n", patch_info->data.name);
961                                 j++;
962                                 break;
963                         case MONO_PATCH_INFO_SWITCH:
964                         case MONO_PATCH_INFO_R4:
965                         case MONO_PATCH_INFO_R8:
966                         case MONO_PATCH_INFO_METHOD_REL:
967                         case MONO_PATCH_INFO_LDSTR:
968                         case MONO_PATCH_INFO_LDTOKEN:
969                         case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
970                                 fprintf (tmpfp, "\t.long %s_p_%d\n", mname, j);
971                                 j++;
972                                 break;
973                         case MONO_PATCH_INFO_LABEL:
974                         case MONO_PATCH_INFO_BB:
975                                 break;
976                         default:
977                                 g_warning ("unable to handle jump info %d", patch_info->type);
978                                 g_assert_not_reached ();
979                         }
980
981                 }
982         }
983
984         /*
985          * 0 is PATCH_INFO_BB, which can't be in the file.
986          */
987         /* NULL terminated array */
988         fprintf (tmpfp, "\t.long 0\n");
989
990         /* fixme: save the rest of the required infos */
991
992         g_free (mname);
993 }
994
995 int
996 mono_compile_assembly (MonoAssembly *ass, guint32 opts)
997 {
998         MonoCompile *cfg;
999         MonoImage *image = ass->image;
1000         MonoMethod *method;
1001         char *com, *tmpfname, *opts_str;
1002         FILE *tmpfp;
1003         int i;
1004         guint8 *symbol;
1005         int ccount = 0, mcount = 0, lmfcount = 0, abscount = 0, wrappercount = 0, ocount = 0;
1006         GHashTable *ref_hash;
1007         MonoAotCompile *acfg;
1008         gboolean *emitted;
1009
1010         printf ("Mono AOT compiler - compiling assembly %s\n", image->name);
1011
1012         i = g_file_open_tmp ("mono_aot_XXXXXX", &tmpfname, NULL);
1013         tmpfp = fdopen (i, "w+");
1014         g_assert (tmpfp);
1015
1016         ref_hash = g_hash_table_new (NULL, NULL);
1017
1018         acfg = g_new0 (MonoAotCompile, 1);
1019         acfg->fp = tmpfp;
1020         acfg->ref_hash = ref_hash;
1021         acfg->icall_hash = g_hash_table_new (NULL, NULL);
1022         acfg->icall_table = g_ptr_array_new ();
1023         acfg->image_hash = g_hash_table_new (NULL, NULL);
1024         acfg->image_table = g_ptr_array_new ();
1025
1026         write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid);
1027
1028         write_string_symbol (tmpfp, "mono_aot_version", MONO_AOT_FILE_VERSION);
1029
1030         opts_str = g_strdup_printf ("%d", opts);
1031         write_string_symbol (tmpfp, "mono_aot_opt_flags", opts_str);
1032         g_free (opts_str);
1033
1034         emitted = g_new0 (gboolean, image->tables [MONO_TABLE_METHOD].rows);
1035
1036         for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
1037                 MonoJumpInfo *patch_info;
1038                 gboolean skip;
1039                 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
1040                 method = mono_get_method (image, token, NULL);
1041                 
1042                 /* fixme: maybe we can also precompile wrapper methods */
1043                 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
1044                     (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
1045                     (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
1046                     (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
1047                         //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
1048                         continue;
1049                 }
1050
1051                 mcount++;
1052
1053                 /* fixme: we need to patch the IP for the LMF in that case */
1054                 if (method->save_lmf) {
1055                         //printf ("Skip (needs lmf):  %s\n", mono_method_full_name (method, TRUE));
1056                         lmfcount++;
1057                         continue;
1058                 }
1059
1060                 //printf ("START:           %s\n", mono_method_full_name (method, TRUE));
1061                 //mono_compile_method (method);
1062
1063                 cfg = mini_method_compile (method, opts, mono_root_domain, 0);
1064                 g_assert (cfg);
1065
1066                 if (cfg->disable_aot) {
1067                         printf ("Skip (other): %s\n", mono_method_full_name (method, TRUE));
1068                         ocount++;
1069                         continue;
1070                 }
1071
1072                 skip = FALSE;
1073                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1074                         if (patch_info->type == MONO_PATCH_INFO_ABS) {
1075                                 /* unable to handle this */
1076                                 //printf ("Skip (abs addr):   %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
1077                                 skip = TRUE;    
1078                                 break;
1079                         }
1080                 }
1081
1082                 if (skip) {
1083                         abscount++;
1084                         continue;
1085                 }
1086
1087                 skip = FALSE;
1088                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1089                         if ((patch_info->type == MONO_PATCH_INFO_METHOD ||
1090                              patch_info->type == MONO_PATCH_INFO_METHODCONST) &&
1091                             patch_info->data.method->wrapper_type) {
1092                                 /* unable to handle this */
1093                                 //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));
1094                                 skip = TRUE;    
1095                                 break;
1096                         }
1097                 }
1098
1099                 if (skip) {
1100                         wrappercount++;
1101                         continue;
1102                 }
1103
1104                 //printf ("Compile:           %s\n", mono_method_full_name (method, TRUE));
1105
1106                 emitted [i] = TRUE;
1107                 emit_method (acfg, cfg);
1108
1109                 mono_destroy_compile (cfg);
1110
1111                 ccount++;
1112         }
1113
1114         /*
1115          * The icall and image tables are small but referenced in a lot of places.
1116          * So we emit them at once, and reference their elements by an index
1117          * instead of an assembly label to cut back on the number of relocations.
1118          */
1119
1120         /* Emit icall table */
1121
1122         symbol = g_strdup_printf ("mono_icall_table");
1123         fprintf (tmpfp, ".globl %s\n", symbol);
1124         fprintf (tmpfp, ".text 1 \n");
1125         fprintf (tmpfp, "\t.align 8\n");
1126         fprintf (tmpfp, "%s:\n", symbol);
1127         fprintf (tmpfp, ".long %d\n", acfg->icall_table->len);
1128         for (i = 0; i < acfg->icall_table->len; i++)
1129                 fprintf (tmpfp, ".string \"%s\"\n", (char*)g_ptr_array_index (acfg->icall_table, i));
1130
1131         /* Emit image table */
1132
1133         symbol = g_strdup_printf ("mono_image_table");
1134         fprintf (tmpfp, ".globl %s\n", symbol);
1135         fprintf (tmpfp, ".text 1 \n");
1136         fprintf (tmpfp, "\t.align 8\n");
1137         fprintf (tmpfp, "%s:\n", symbol);
1138         fprintf (tmpfp, ".long %d\n", acfg->image_table->len);
1139         for (i = 0; i < acfg->image_table->len; i++)
1140                 fprintf (tmpfp, ".string \"%s\"\n", ((MonoImage*)g_ptr_array_index (acfg->image_table, i))->guid);
1141
1142         /*
1143          * g_module_symbol takes a lot of time for failed lookups, so we emit
1144          * a table which contains one bit for each method. This bit specifies
1145          * whenever the method is emitted or not.
1146          */
1147
1148         symbol = g_strdup_printf ("mono_methods_present_table");
1149         fprintf (tmpfp, ".globl %s\n", symbol);
1150         fprintf (tmpfp, ".text 1 \n");
1151         fprintf (tmpfp, "\t.align 8\n");
1152         fprintf (tmpfp, "%s:\n", symbol);
1153         {
1154                 guint32 k, nrows;
1155                 guint32 w;
1156
1157                 nrows = image->tables [MONO_TABLE_METHOD].rows;
1158                 for (i = 0; i < nrows / 32 + 1; ++i) {
1159                         w = 0;
1160                         for (k = 0; k < 32; ++k) {
1161                                 if (emitted [(i * 32) + k])
1162                                         w += (1 << k);
1163                         }
1164                         //printf ("EMITTED [%d] = %d.\n", i, b);
1165                         fprintf (tmpfp, "\t.long %d\n", w);
1166                 }
1167         }
1168
1169         fclose (tmpfp);
1170
1171         com = g_strdup_printf ("as %s -o %s.o", tmpfname, tmpfname);
1172         printf ("Executing the native assembler: %s\n", com);
1173         system (com);
1174         g_free (com);
1175         com = g_strdup_printf ("ld -shared -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
1176         printf ("Executing the native linker: %s\n", com);
1177         system (com);
1178         g_free (com);
1179         com = g_strdup_printf ("%s.o", tmpfname);
1180         unlink (com);
1181         g_free (com);
1182         /*com = g_strdup_printf ("strip --strip-unneeded %s%s", image->name, SHARED_EXT);
1183         printf ("Stripping the binary: %s\n", com);
1184         system (com);
1185         g_free (com);*/
1186
1187         printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, (ccount*100)/mcount);
1188         printf ("%d methods contain absolute addresses (%d%%)\n", abscount, (abscount*100)/mcount);
1189         printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, (wrappercount*100)/mcount);
1190         printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, (lmfcount*100)/mcount);
1191         printf ("%d methods have other problems (%d%%)\n", ocount, (ocount*100)/mcount);
1192         unlink (tmpfname);
1193
1194         return 0;
1195 }
1196
1197