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