2004-07-09 Ben Maurer <bmaurer@ximian.com>
[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 <errno.h>
22 #include <sys/stat.h>
23 #include <limits.h>    /* for PAGESIZE */
24 #ifndef PAGESIZE
25 #define PAGESIZE 4096
26 #endif
27
28 #include <mono/metadata/tabledefs.h>
29 #include <mono/metadata/class.h>
30 #include <mono/metadata/object.h>
31 #include <mono/metadata/tokentype.h>
32 #include <mono/metadata/appdomain.h>
33 #include <mono/metadata/debug-helpers.h>
34 #include <mono/metadata/assembly.h>
35 #include <mono/metadata/metadata-internals.h>
36 #include <mono/metadata/marshal.h>
37 #include <mono/utils/mono-logger.h>
38 #include <mono/os/gc_wrapper.h>
39
40 #include "mini.h"
41
42 #ifdef PLATFORM_WIN32
43 #define SHARED_EXT ".dll"
44 #elif defined(__ppc__) && defined(__MACH__)
45 #define SHARED_EXT ".dylib"
46 #else
47 #define SHARED_EXT ".so"
48 #endif
49
50 #if defined(sparc) || defined(__ppc__)
51 #define AS_STRING_DIRECTIVE ".asciz"
52 #else
53 /* GNU as */
54 #define AS_STRING_DIRECTIVE ".string"
55 #endif
56
57 #define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1)))
58
59 typedef struct MonoAotMethod {
60         MonoJitInfo *info;
61         MonoJumpInfo *patch_info;
62         MonoDomain *domain;
63 } MonoAotMethod;
64
65 typedef struct MonoAotModule {
66         /* Optimization flags used to compile the module */
67         guint32 opts;
68         /* Maps MonoMethods to MonoAotMethodInfos */
69         MonoGHashTable *methods;
70         char **icall_table;
71         MonoImage **image_table;
72         guint32* methods_present_table;
73 } MonoAotModule;
74
75 typedef struct MonoAotCompile {
76         FILE *fp;
77         GHashTable *ref_hash;
78         GHashTable *icall_hash;
79         GPtrArray *icall_table;
80         GHashTable *image_hash;
81         GPtrArray *image_table;
82 } MonoAotCompile;
83
84 typedef struct MonoAotOptions {
85         char *outfile;
86 } MonoAotOptions;
87
88 static MonoGHashTable *aot_modules;
89
90 static CRITICAL_SECTION aot_mutex;
91
92 /*
93  * Disabling this will make a copy of the loaded code and use the copy instead 
94  * of the original. This will place the caller and the callee close to each 
95  * other in memory, possibly improving cache behavior. Since the original
96  * code is in copy-on-write memory, this will not increase the memory usage
97  * of the runtime.
98  */
99 static gboolean use_loaded_code = FALSE;
100
101 /*
102  * Whenever to AOT compile loaded assemblies on demand and store them in
103  * a cache under $HOME/.mono/aot-cache.
104  */
105 static gboolean use_aot_cache = FALSE;
106
107 /* For debugging */
108 static gint32 mono_last_aot_method = -1;
109
110 static MonoJitInfo*
111 mono_aot_load_method (MonoDomain *domain, MonoAotModule *aot_module, MonoMethod *method, guint8 *code, guint8 *info);
112
113 static MonoClass * 
114 decode_class_info (MonoAotModule *module, guint32 *data)
115 {
116         MonoImage *image;
117         MonoClass *klass;
118         
119         image = module->image_table [data [1]];
120         g_assert (image);
121
122         if (data [0]) {
123                 return mono_class_get (image, data [0]);
124         } else {
125                 /* the pointer is dword aligned */
126                 klass = decode_class_info (module, *(guint32**)(ALIGN_PTR_TO (&data[3], sizeof (gpointer))));
127                 return mono_array_class_get (klass, data [2]);
128         }
129
130         return NULL;
131 }
132
133 static void
134 create_cache_structure (void)
135 {
136         const char *home;
137         char *tmp;
138         int err;
139
140         home = g_get_home_dir ();
141         if (!home)
142                 return;
143
144         tmp = g_build_filename (home, ".mono", NULL);
145         if (!g_file_test (tmp, G_FILE_TEST_IS_DIR)) {
146                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT creating directory %s", tmp);
147 #ifdef PLATFORM_WIN32
148                 err = mkdir (tmp);
149 #else
150                 err = mkdir (tmp, 0777);
151 #endif
152                 if (err) {
153                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT failed: %s", g_strerror (errno));
154                         g_free (tmp);
155                         return;
156                 }
157         }
158         g_free (tmp);
159         tmp = g_build_filename (home, ".mono", "aot-cache", NULL);
160         if (!g_file_test (tmp, G_FILE_TEST_IS_DIR)) {
161                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT creating directory %s", tmp);
162 #ifdef PLATFORM_WIN32
163                 err = mkdir (tmp);
164 #else
165                 err = mkdir (tmp, 0777);
166 #endif
167                 if (err) {
168                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT failed: %s", g_strerror (errno));
169                         g_free (tmp);
170                         return;
171                 }
172         }
173         g_free (tmp);
174 }
175
176 /*
177  * load_aot_module_from_cache:
178  *
179  *  Experimental code to AOT compile loaded assemblies on demand. 
180  *
181  * FIXME: 
182  * - Add environment variable MONO_AOT_CACHE_OPTIONS
183  * - Add options for controlling the cache size
184  * - Handle full cache by deleting old assemblies lru style
185  * - Add options for excluding assemblies during development
186  * - Maybe add a threshold after an assembly is AOT compiled
187  * - invoking a new mono process is a security risk
188  */
189 static GModule*
190 load_aot_module_from_cache (MonoAssembly *assembly, char **aot_name)
191 {
192         char *fname, *cmd, *tmp2;
193         const char *home;
194         GModule *module;
195         gboolean res;
196         gchar *out, *err;
197
198         *aot_name = NULL;
199
200         if (assembly->image->dynamic)
201                 return NULL;
202
203         create_cache_structure ();
204
205         home = g_get_home_dir ();
206
207         tmp2 = g_strdup_printf ("%s-%s%s", assembly->image->assembly_name, assembly->image->guid, SHARED_EXT);
208         fname = g_build_filename (home, ".mono", "aot-cache", tmp2, NULL);
209         *aot_name = fname;
210         g_free (tmp2);
211
212         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT trying to load from cache: '%s'.", fname);
213         module = g_module_open (fname, G_MODULE_BIND_LAZY);     
214
215         if (!module) {
216                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT not found.");
217
218                 mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT precompiling assembly '%s'... ", assembly->image->name);
219
220                 /* FIXME: security */
221                 cmd = g_strdup_printf ("mono -O=all --aot=outfile=%s %s", fname, assembly->image->name);
222
223                 res = g_spawn_command_line_sync (cmd, &out, &err, NULL, NULL);
224                 g_free (cmd);
225                 if (!res) {
226                         mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT failed.");
227                         return NULL;
228                 }
229
230                 mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT succeeded.");
231
232                 module = g_module_open (fname, G_MODULE_BIND_LAZY);     
233         }
234
235         return module;
236 }
237
238 static void
239 load_aot_module (MonoAssembly *assembly, gpointer user_data)
240 {
241         char *aot_name;
242         MonoAotModule *info;
243         gboolean usable = TRUE;
244         char *saved_guid = NULL;
245         char *aot_version = NULL;
246         char *opt_flags = NULL;
247
248         if (mono_compile_aot)
249                 return;
250                                                         
251         if (use_aot_cache)
252                 assembly->aot_module = load_aot_module_from_cache (assembly, &aot_name);
253         else {
254                 aot_name = g_strdup_printf ("%s%s", assembly->image->name, SHARED_EXT);
255
256                 assembly->aot_module = g_module_open (aot_name, G_MODULE_BIND_LAZY);
257
258                 if (!assembly->aot_module) {
259                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT failed to load AOT module %s: %s\n", aot_name, g_module_error ());
260                 }
261         }
262
263         if (!assembly->aot_module) {
264                 g_free (aot_name);
265                 return;
266         }
267
268         g_module_symbol (assembly->aot_module, "mono_assembly_guid", (gpointer *) &saved_guid);
269         g_module_symbol (assembly->aot_module, "mono_aot_version", (gpointer *) &aot_version);
270         g_module_symbol (assembly->aot_module, "mono_aot_opt_flags", (gpointer *)&opt_flags);
271
272         if (!aot_version || strcmp (aot_version, MONO_AOT_FILE_VERSION)) {
273                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module %s has wrong file format version (expected %s got %s)\n", aot_name, MONO_AOT_FILE_VERSION, aot_version);
274                 usable = FALSE;
275         }
276         else
277                 if (!saved_guid || strcmp (assembly->image->guid, saved_guid)) {
278                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module %s is out of date.\n", aot_name);
279                         usable = FALSE;
280                 }
281
282         if (!usable) {
283                 g_free (aot_name);
284                 g_module_close (assembly->aot_module);
285                 assembly->aot_module = NULL;
286                 return;
287         }
288
289         /*
290          * It seems that MonoGHashTables are in the GC heap, so structures
291          * containing them must be in the GC heap as well :(
292          */
293 #ifdef HAVE_BOEHM_GC
294         info = GC_MALLOC (sizeof (MonoAotModule));
295 #else
296         info = g_new0 (MonoAotModule, 1);
297 #endif
298         info->methods = mono_g_hash_table_new (NULL, NULL);
299         sscanf (opt_flags, "%d", &info->opts);
300
301         /* Read image table */
302         {
303                 guint32 table_len, i;
304                 char *table = NULL;
305
306                 g_module_symbol (assembly->aot_module, "mono_image_table", (gpointer *)&table);
307                 g_assert (table);
308
309                 table_len = *(guint32*)table;
310                 table += sizeof (guint32);
311                 info->image_table = g_new0 (MonoImage*, table_len);
312                 for (i = 0; i < table_len; ++i) {
313                         info->image_table [i] = mono_image_loaded_by_guid (table);
314                         if (!info->image_table [i]) {
315                                 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT module %s is out of date.\n", aot_name);
316                                 mono_g_hash_table_destroy (info->methods);
317                                 g_free (info->image_table);
318 #ifndef HAVE_BOEHM_GC
319                                 g_free (info);
320 #endif
321                                 g_free (aot_name);
322                                 g_module_close (assembly->aot_module);
323                                 assembly->aot_module = NULL;
324                                 return;
325                         }
326                         table += strlen (table) + 1;
327                 }
328         }
329
330         /* Read icall table */
331         {
332                 guint32 table_len, i;
333                 char *table = NULL;
334
335                 g_module_symbol (assembly->aot_module, "mono_icall_table", (gpointer *)&table);
336                 g_assert (table);
337
338                 table_len = *(guint32*)table;
339                 table += sizeof (guint32);
340                 info->icall_table = g_new0 (char*, table_len);
341                 for (i = 0; i < table_len; ++i) {
342                         info->icall_table [i] = table;
343                         table += strlen (table) + 1;
344                 }
345         }
346
347         /* Read methods present table */
348         g_module_symbol (assembly->aot_module, "mono_methods_present_table", (gpointer *)&info->methods_present_table);
349         g_assert (info->methods_present_table);
350
351         EnterCriticalSection (&aot_mutex);
352         mono_g_hash_table_insert (aot_modules, assembly, info);
353         LeaveCriticalSection (&aot_mutex);
354
355         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT loaded AOT Module for %s.\n", assembly->image->name);
356 }
357
358 void
359 mono_aot_init (void)
360 {
361         InitializeCriticalSection (&aot_mutex);
362
363         MONO_GC_REGISTER_ROOT (aot_modules);
364         aot_modules = mono_g_hash_table_new (NULL, NULL);
365
366         mono_install_assembly_load_hook (load_aot_module, NULL);
367
368         if (getenv ("MONO_LASTAOT"))
369                 mono_last_aot_method = atoi (getenv ("MONO_LASTAOT"));
370 }
371  
372 static MonoJitInfo *
373 mono_aot_get_method_inner (MonoDomain *domain, MonoMethod *method)
374 {
375         MonoClass *klass = method->klass;
376         MonoAssembly *ass = klass->image->assembly;
377         GModule *module = ass->aot_module;
378         char method_label [256];
379         char info_label [256];
380         guint8 *code = NULL;
381         guint8 *info;
382         MonoAotModule *aot_module;
383         MonoAotMethod *minfo;
384         MonoJitInfo *jinfo;
385         MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
386         int i;
387
388         if (!module)
389                 return NULL;
390
391         if (!method->token)
392                 return NULL;
393
394         if (mono_profiler_get_events () & MONO_PROFILE_ENTER_LEAVE)
395                 return NULL;
396
397         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
398                 (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
399                 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
400                 (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
401                 return NULL;
402
403         aot_module = (MonoAotModule*)mono_g_hash_table_lookup (aot_modules, ass);
404
405         g_assert (klass->inited);
406
407         minfo = mono_g_hash_table_lookup (aot_module->methods, method);
408         /* Can't use code from non-root domains since they can be unloaded */
409         if (minfo && (minfo->domain == mono_get_root_domain ())) {
410                 /* This method was already loaded in another appdomain */
411
412                 /* Duplicate jinfo */
413                 jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
414                 memcpy (jinfo, minfo->info, sizeof (MonoJitInfo));
415                 if (jinfo->clauses) {
416                         jinfo->clauses = 
417                                 mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
418                         memcpy (jinfo->clauses, minfo->info->clauses, sizeof (MonoJitExceptionInfo) * header->num_clauses);
419                 }
420
421                 if (aot_module->opts & MONO_OPT_SHARED)
422                         /* Use the same method in the new appdomain */
423                         ;
424                 else if (!minfo->patch_info)
425                         /* Use the same method in the new appdomain */
426                         ;                       
427                 else {
428                         /* Create a copy of the original method and apply relocations */
429
430                         code = mono_code_manager_reserve (domain->code_mp, minfo->info->code_size);
431                         memcpy (code, minfo->info->code_start, minfo->info->code_size);
432
433                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT REUSE METHOD: %s %p - %p.\n", mono_method_full_name (method, TRUE), code, (char*)code + minfo->info->code_size);
434
435                         /* Do this outside the lock to avoid deadlocks */
436                         LeaveCriticalSection (&aot_mutex);
437                         mono_arch_patch_code (method, domain, code, minfo->patch_info, TRUE);
438                         EnterCriticalSection (&aot_mutex);
439                         mono_arch_flush_icache (code, minfo->info->code_size);
440
441                         /* Relocate jinfo */
442                         jinfo->code_start = code;
443                         if (jinfo->clauses) {
444                                 for (i = 0; i < header->num_clauses; ++i) {
445                                         MonoJitExceptionInfo *ei = &jinfo->clauses [i];
446                                         gint32 offset = code - (guint8*)minfo->info->code_start;
447
448                                         if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
449                                                 ei->data.filter = (guint8*)ei->data.filter + offset;
450                                         ei->try_start = (guint8*)ei->try_start + offset;
451                                         ei->try_end = (guint8*)ei->try_end + offset;
452                                         ei->handler_start = (guint8*)ei->handler_start + offset;
453                                 }
454                         }
455                 }
456
457                 return jinfo;
458         }
459
460         /* Do a fast check to see whenever the method exists */
461         {
462                 guint32 index = mono_metadata_token_index (method->token) - 1;
463                 guint32 w;
464                 w = aot_module->methods_present_table [index / 32];
465                 if (! (w & (1 << (index % 32)))) {
466                         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT NOT FOUND: %s.\n", mono_method_full_name (method, TRUE));
467                         return NULL;
468                 }
469         }
470
471         sprintf (method_label, "m_%x", mono_metadata_token_index (method->token));
472
473         if (!g_module_symbol (module, method_label, (gpointer *)&code))
474                 return NULL;
475
476         sprintf (info_label, "%s_p", method_label);
477
478         if (!g_module_symbol (module, info_label, (gpointer *)&info))
479                 return NULL;
480
481         if (mono_last_aot_method != -1) {
482                 if (mono_jit_stats.methods_aot > mono_last_aot_method)
483                                 return NULL;
484                 else
485                         if (mono_jit_stats.methods_aot == mono_last_aot_method)
486                                 printf ("LAST AOT METHOD: %s.%s.%s.\n", klass->name_space, klass->name, method->name);
487         }
488
489         return mono_aot_load_method (domain, aot_module, method, code, info);
490 }
491
492 static MonoJitInfo*
493 mono_aot_load_method (MonoDomain *domain, MonoAotModule *aot_module, MonoMethod *method, guint8 *code, guint8 *info)
494 {
495         MonoClass *klass = method->klass;
496         MonoJumpInfo *patch_info = NULL;
497         guint code_len, used_int_regs, used_strings;
498         MonoAotMethod *minfo;
499         MonoJitInfo *jinfo;
500         MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
501         GPtrArray *patches;
502         int i, pindex;
503
504 #ifdef HAVE_BOEHM_GC
505         minfo = GC_MALLOC (sizeof (MonoAotMethod));
506 #else
507         minfo = g_new0 (MonoAotMethod, 1);
508 #endif
509
510         minfo->domain = domain;
511         jinfo = mono_mempool_alloc0 (domain->mp, sizeof (MonoJitInfo));
512
513         code_len = *(guint32*)info;
514         info += 4;
515         used_int_regs = *(guint32*)info;
516         info += 4;
517
518         if (!use_loaded_code) {
519                 guint8 *code2;
520                 code2 = mono_code_manager_reserve (domain->code_mp, code_len);
521                 memcpy (code2, code, code_len);
522                 mono_arch_flush_icache (code2, code_len);
523                 code = code2;
524         }
525
526         mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "AOT FOUND AOT compiled code for %s %p - %p %p\n", mono_method_full_name (method, TRUE), code, code + code_len, info);
527
528         /* Exception table */
529         if (header->num_clauses) {
530                 jinfo->clauses = 
531                         mono_mempool_alloc0 (domain->mp, sizeof (MonoJitExceptionInfo) * header->num_clauses);
532                 jinfo->num_clauses = header->num_clauses;
533
534                 jinfo->exvar_offset = *(guint32*)info;
535                 info += 4;
536
537                 for (i = 0; i < header->num_clauses; ++i) {
538                         MonoExceptionClause *ec = &header->clauses [i];                         
539                         MonoJitExceptionInfo *ei = &jinfo->clauses [i];
540
541                         ei->flags = ec->flags;
542                         if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
543                                 ei->data.filter = code + *(guint32*)info;
544                         else
545                                 ei->data.token = *(guint32*)info;
546                         info += 4;
547                         ei->try_start = code + *(guint32*)info;
548                         info += 4;
549                         ei->try_end = code + *(guint32*)info;
550                         info += 4;
551                         ei->handler_start = code + *(guint32*)info;
552                         info += 4;
553                 }
554         }
555
556         if (aot_module->opts & MONO_OPT_SHARED) {
557                 used_strings = *(guint32*)info;
558                 info += 4;
559         }
560         else
561                 used_strings = 0;
562
563         for (i = 0; i < used_strings; i++) {
564                 guint token =  *(guint32*)info;
565                 info += 4;
566                 mono_ldstr (mono_get_root_domain (), klass->image, mono_metadata_token_index (token));
567         }
568
569         if (*info) {
570                 MonoMemPool *mp;
571                 MonoImage *image;
572                 guint8 *page_start;
573                 gpointer *table;
574                 int pages;
575                 int i, err;
576                 guint32 last_offset, buf_len;
577                 guint32 *data;
578
579                 if (aot_module->opts & MONO_OPT_SHARED)
580                         mp = mono_mempool_new ();
581                 else
582                         mp = domain->mp;
583
584                 /* First load the type + offset table */
585                 last_offset = 0;
586                 patches = g_ptr_array_new ();
587                 while (*info) {
588                         MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
589
590                         guint8 b1, b2;
591
592                         b1 = *(guint8*)info;
593                         b2 = *((guint8*)info + 1);
594                         
595                         info += 2;
596
597                         ji->type = b1 >> 2;
598
599                         if (((b1 & (1 + 2)) == 3) && (b2 == 255)) {
600                                 info = ALIGN_PTR_TO (info, 4);
601                                 ji->ip.i = *(guint32*)info;
602                                 info += 4;
603                         }
604                         else
605                                 ji->ip.i = (((guint32)(b1 & (1 + 2))) << 8) + b2;
606
607                         ji->ip.i += last_offset;
608                         last_offset = ji->ip.i;
609                         //printf ("T: %d O: %d.\n", ji->type, ji->ip.i);
610
611                         ji->next = patch_info;
612                         patch_info = ji;
613
614                         g_ptr_array_add (patches, ji);
615                 }
616                 info ++;
617
618                 info = ALIGN_PTR_TO (info, sizeof (gpointer));
619
620                 /* Then load the other data */
621                 for (pindex = 0; pindex < patches->len; ++pindex) {
622                         MonoJumpInfo *ji = g_ptr_array_index (patches, pindex);
623                 
624                         data = *((guint32 **)info);
625                         info += sizeof (gpointer);
626
627                         switch (ji->type) {
628                         case MONO_PATCH_INFO_CLASS:
629                         case MONO_PATCH_INFO_IID:
630                                 ji->data.klass = decode_class_info (aot_module, data);
631                                 g_assert (ji->data.klass);
632                                 mono_class_init (ji->data.klass);
633                                 break;
634                         case MONO_PATCH_INFO_VTABLE:
635                         case MONO_PATCH_INFO_CLASS_INIT:
636                                 ji->data.klass = decode_class_info (aot_module, data);
637                                 g_assert (ji->data.klass);
638                                 mono_class_init (ji->data.klass);
639                                 break;
640                         case MONO_PATCH_INFO_IMAGE:
641                                 ji->data.image = aot_module->image_table [(guint32)data];
642                                 g_assert (ji->data.image);
643                                 break;
644                         case MONO_PATCH_INFO_METHOD:
645                         case MONO_PATCH_INFO_METHODCONST:
646                         case MONO_PATCH_INFO_METHOD_JUMP: {
647                                 guint32 image_index, token;
648
649                                 image_index = (guint32)data >> 24;
650                                 token = MONO_TOKEN_METHOD_DEF | ((guint32)data & 0xffffff);
651
652                                 image = aot_module->image_table [image_index];
653                                 ji->data.method = mono_get_method (image, token, NULL);
654                                 g_assert (ji->data.method);
655                                 mono_class_init (ji->data.method->klass);
656
657                                 break;
658                         }
659                         case MONO_PATCH_INFO_WRAPPER: {
660                                 guint32 wrapper_type;
661
662                                 wrapper_type = (guint32)data[0];
663
664                                 switch (wrapper_type) {
665                                 case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: {
666                                         guint32 image_index, token;
667
668                                         image_index = (guint32)data[1] >> 24;
669                                         token = MONO_TOKEN_METHOD_DEF | ((guint32)data[1] & 0xffffff);
670
671                                         image = aot_module->image_table [image_index];
672                                         ji->data.method = mono_get_method (image, token, NULL);
673                                         g_assert (ji->data.method);
674                                         mono_class_init (ji->data.method->klass);
675
676                                         ji->type = MONO_PATCH_INFO_METHOD;
677                                         ji->data.method = mono_marshal_get_remoting_invoke_with_check (ji->data.method);
678                                         break;
679                                 }
680                                 case MONO_WRAPPER_PROXY_ISINST: {
681                                         /* The pointer is dword aligned */
682                                         gpointer class_ptr = *(gpointer*)(ALIGN_PTR_TO (&data[1], sizeof (gpointer)));
683                                         MonoClass *klass = decode_class_info (aot_module, class_ptr);
684                                         mono_class_init (klass);
685                                         ji->type = MONO_PATCH_INFO_METHODCONST;
686                                         ji->data.method = mono_marshal_get_proxy_cancast (klass);
687                                         break;
688                                 }
689                                 case MONO_WRAPPER_LDFLD:
690                                 case MONO_WRAPPER_STFLD: {
691                                         /* The pointer is dword aligned */
692                                         gpointer class_ptr = *(gpointer*)(ALIGN_PTR_TO (&data[1], sizeof (gpointer)));
693                                         MonoClass *klass = decode_class_info (aot_module, class_ptr);
694                                         mono_class_init (klass);
695                                         ji->type = MONO_PATCH_INFO_METHOD;
696                                         if (wrapper_type == MONO_WRAPPER_LDFLD)
697                                                 ji->data.method = mono_marshal_get_ldfld_wrapper (&klass->byval_arg);
698                                         else
699                                                 ji->data.method = mono_marshal_get_stfld_wrapper (&klass->byval_arg);
700                                         break;
701                                 }
702                                 default:
703                                         g_assert_not_reached ();
704                                 }
705                                 break;
706                         }
707                         case MONO_PATCH_INFO_FIELD:
708                         case MONO_PATCH_INFO_SFLDA: {
709                                 /* The pointer is dword aligned */
710                                 gpointer class_ptr = *(gpointer*)(ALIGN_PTR_TO (&data[1], sizeof (gpointer)));
711                                 MonoClass *klass = decode_class_info (aot_module, class_ptr);
712                                 mono_class_init (klass);
713                                 ji->data.field = mono_class_get_field (klass, data [0]);
714                                 break;
715                         }
716                         case MONO_PATCH_INFO_INTERNAL_METHOD:
717                                 ji->data.name = aot_module->icall_table [(guint32)data];
718                                 g_assert (ji->data.name);
719                                 //printf ("A: %s.\n", ji->data.name);
720                                 break;
721                         case MONO_PATCH_INFO_SWITCH:
722                                 ji->table_size = data [0];
723                                 table = g_new (gpointer, ji->table_size);
724                                 ji->data.target = table;
725                                 for (i = 0; i < ji->table_size; i++) {
726                                         table [i] = (gpointer)data [i + 1];
727                                 }
728                                 break;
729                         case MONO_PATCH_INFO_R4:
730                         case MONO_PATCH_INFO_R8:
731                                 ji->data.target = data;
732                                 break;
733                         case MONO_PATCH_INFO_LDSTR:
734                         case MONO_PATCH_INFO_LDTOKEN:
735                         case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
736                                 image = aot_module->image_table [data [0]];
737                                 ji->data.token = mono_jump_info_token_new (mp, image, data [1]);
738                                 break;
739                         case MONO_PATCH_INFO_EXC_NAME:
740                                 ji->data.klass = decode_class_info (aot_module, data);
741                                 g_assert (ji->data.klass);
742                                 mono_class_init (ji->data.klass);
743                                 ji->data.name = ji->data.klass->name;
744                                 break;
745                         case MONO_PATCH_INFO_METHOD_REL:
746                                 ji->data.offset = data [0];
747                                 break;
748                         default:
749                                 g_warning ("unhandled type %d", ji->type);
750                                 g_assert_not_reached ();
751                         }
752                 }
753
754                 g_ptr_array_free (patches, TRUE);
755
756                 buf_len = *(guint32*)info;
757                 info += 4;
758                 mono_debug_add_aot_method (domain, method, code, info, buf_len);
759
760                 if (use_loaded_code) {
761                 /* disable write protection */
762 #ifndef PLATFORM_WIN32
763                         page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
764                         pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
765                         err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
766                         g_assert (err == 0);
767 #else
768                         {
769                                 DWORD oldp;
770                                 g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
771                         }
772 #endif
773                 }
774
775                 /* Do this outside the lock to avoid deadlocks */
776                 LeaveCriticalSection (&aot_mutex);
777                 mono_arch_patch_code (method, domain, code, patch_info, TRUE);
778                 EnterCriticalSection (&aot_mutex);
779
780                 if (aot_module->opts & MONO_OPT_SHARED)
781                         /* No need to cache patches */
782                         mono_mempool_destroy (mp);
783                 else
784                         minfo->patch_info = patch_info;
785         }
786
787         mono_jit_stats.methods_aot++;
788
789         {
790                 jinfo->code_size = code_len;
791                 jinfo->used_regs = used_int_regs;
792                 jinfo->method = method;
793                 jinfo->code_start = code;
794                 jinfo->domain_neutral = (aot_module->opts & MONO_OPT_SHARED) != 0;
795
796                 minfo->info = jinfo;
797                 mono_g_hash_table_insert (aot_module->methods, method, minfo);
798
799                 return jinfo;
800         }
801 }
802
803 MonoJitInfo*
804 mono_aot_get_method (MonoDomain *domain, MonoMethod *method)
805 {
806         MonoJitInfo *info;
807
808         EnterCriticalSection (&aot_mutex);
809         info = mono_aot_get_method_inner (domain, method);
810         LeaveCriticalSection (&aot_mutex);
811
812         /* Do this outside the lock */
813         if (info) {
814                 mono_jit_info_table_add (domain, info);
815                 return info;
816         }
817         else
818                 return NULL;
819 }
820
821 static void
822 emit_section_change (FILE *fp, const char *section_name, int subsection_index)
823 {
824 #if defined(sparc)
825         /* For solaris as, GNU as should accept the same */
826         fprintf (fp, ".section \"%s\"\n", section_name);
827 #elif defined(__ppc__) && defined(__MACH__)
828         /* This needs to be made more precise on mach. */
829         fprintf (fp, "%s\n", subsection_index == 0 ? ".text" : ".data");
830 #else
831         fprintf (fp, "%s %d\n", section_name, subsection_index);
832 #endif
833 }
834
835 static void
836 emit_global (FILE *fp, const char *name)
837 {
838 #if defined(__ppc__) && defined(__MACH__)
839     // mach-o always uses a '_' prefix.
840         fprintf (fp, ".globl _%s\n", name);
841 #else
842         fprintf (fp, ".globl %s\n", name);
843 #endif
844 }
845
846 static void
847 emit_label (FILE *fp, const char *name)
848 {
849 #if defined(__ppc__) && defined(__MACH__)
850     // mach-o always uses a '_' prefix.
851         fprintf (fp, "_%s:\n", name);
852 #else
853         fprintf (fp, "%s:\n", name);
854 #endif
855 }
856
857 #if 0
858 static void
859 write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align)
860 {
861         int i;
862
863         emit_section_change (fp, ".text", 1);
864
865         fprintf (fp, ".globl %s\n", name);
866         fprintf (fp, "\t.align %d\n", align);
867         fprintf (fp, "\t.type %s,#object\n", name);
868         fprintf (fp, "\t.size %s,%d\n", name, size);
869         fprintf (fp, "%s:\n", name);
870         for (i = 0; i < size; i++) { 
871                 fprintf (fp, ".byte %d\n", buf [i]);
872         }
873         
874 }
875 #endif
876
877 static void
878 write_string_symbol (FILE *fp, const char *name, const char *value)
879 {
880         emit_section_change (fp, ".text", 1);
881         emit_global(fp, name);
882         emit_label(fp, name);
883         fprintf (fp, "\t%s \"%s\"\n", AS_STRING_DIRECTIVE, value);
884 }
885
886 static guint32
887 mono_get_field_token (MonoClassField *field) 
888 {
889         MonoClass *klass = field->parent;
890         int i;
891
892         for (i = 0; i < klass->field.count; ++i) {
893                 if (field == &klass->fields [i])
894                         return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i);
895         }
896
897         g_assert_not_reached ();
898         return 0;
899 }
900
901 static guint32
902 get_image_index (MonoAotCompile *cfg, MonoImage *image)
903 {
904         guint32 index;
905
906         index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image));
907         if (index)
908                 return index - 1;
909         else {
910                 index = g_hash_table_size (cfg->image_hash);
911                 g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1));
912                 g_ptr_array_add (cfg->image_table, image);
913                 return index;
914         }
915 }
916
917 static void
918 emit_image_index (MonoAotCompile *cfg, MonoImage *image)
919 {
920         guint32 image_index;
921
922         image_index = get_image_index (cfg, image);
923
924         fprintf (cfg->fp, "\t.long %d\n", image_index);
925 }
926
927 #if defined(__ppc__) && defined(__MACH__)
928 static int
929 ilog2(register int value)
930 {
931     int count = -1;
932     while (value & ~0xf) count += 4, value >>= 4;
933     while (value) count++, value >>= 1;
934     return count;
935 }
936 #endif
937
938 static void emit_alignment(FILE *fp, int size)
939 {
940 #if defined(__ppc__) && defined(__MACH__)
941         // the mach-o assembler specifies alignments as powers of 2.
942         fprintf (fp, "\t.align %d\t; ilog2\n", ilog2(size));
943 #elif defined(__powerpc__)
944         /* ignore on linux/ppc */
945 #else
946         fprintf (fp, "\t.align %d\n", size);
947 #endif
948 }
949
950 static void
951 emit_pointer (FILE *fp, const char *target)
952 {
953         emit_alignment (fp, sizeof (gpointer));
954 #if defined(sparc) && SIZEOF_VOID_P == 8
955         fprintf (fp, "\t.xword %s\n", target);
956 #else
957         fprintf (fp, "\t.long %s\n", target);
958 #endif
959 }
960
961 static char *
962 cond_emit_klass_label (MonoAotCompile *cfg, MonoClass *klass)
963 {
964         char *l1, *el = NULL;
965
966         if ((l1 = g_hash_table_lookup (cfg->ref_hash, klass))) 
967                 return l1;
968
969         if (!klass->type_token) {
970                 g_assert (klass->rank > 0);
971                 el = cond_emit_klass_label (cfg, klass->element_class);
972         }
973         
974         emit_alignment(cfg->fp, sizeof (gpointer));
975
976         l1 = g_strdup_printf ("klass_p_%08x_%p", klass->type_token, klass);
977         fprintf (cfg->fp, "%s:\n", l1);
978         fprintf (cfg->fp, "\t.long 0x%08x\n", klass->type_token);
979         emit_image_index (cfg, klass->image);
980
981         if (el) {
982                 fprintf (cfg->fp, "\t.long %d\n", klass->rank); 
983                 emit_pointer (cfg->fp, el);
984         }
985
986         g_hash_table_insert (cfg->ref_hash, klass, l1);
987
988         return l1;
989 }
990
991 static char *
992 cond_emit_field_label (MonoAotCompile *cfg, MonoJumpInfo *patch_info)
993 {
994         MonoClassField *field = patch_info->data.field;
995         char *l1, *l2;
996         guint token;
997
998         if ((l1 = g_hash_table_lookup (cfg->ref_hash, field))) 
999                 return l1;
1000
1001         l2 = cond_emit_klass_label (cfg, field->parent);
1002         emit_alignment(cfg->fp, sizeof (gpointer));
1003         token = mono_get_field_token (field);
1004         g_assert (token);
1005         l1 = g_strdup_printf ("klass_p_%08x_%p", token, field);
1006         fprintf (cfg->fp, "%s:\n", l1);
1007         fprintf (cfg->fp, "\t.long 0x%08x\n", token);
1008         emit_pointer (cfg->fp, l2);
1009                 
1010         g_hash_table_insert (cfg->ref_hash, field, l1);
1011
1012         return l1;
1013 }
1014
1015 static gint
1016 compare_patches (gconstpointer a, gconstpointer b)
1017 {
1018         int i, j;
1019
1020         i = (*(MonoJumpInfo**)a)->ip.i;
1021         j = (*(MonoJumpInfo**)b)->ip.i;
1022
1023         if (i < j)
1024                 return -1;
1025         else
1026                 if (i > j)
1027                         return 1;
1028         else
1029                 return 0;
1030 }
1031
1032 static void
1033 emit_method (MonoAotCompile *acfg, MonoCompile *cfg)
1034 {
1035         MonoMethod *method;
1036         GList *l;
1037         FILE *tmpfp;
1038         int i, j, k, pindex;
1039         guint8 *code, *mname, *mname_p;
1040         int func_alignment = 16;
1041         GPtrArray *patches;
1042         MonoJumpInfo *patch_info;
1043         MonoMethodHeader *header;
1044
1045         tmpfp = acfg->fp;
1046         method = cfg->method;
1047         code = cfg->native_code;
1048         header = ((MonoMethodNormal*)method)->header;
1049
1050         emit_section_change (tmpfp, ".text", 0);
1051         mname = g_strdup_printf ("m_%x", mono_metadata_token_index (method->token));
1052         mname_p = g_strdup_printf ("%s_p", mname);
1053         emit_alignment(tmpfp, func_alignment);
1054         emit_global(tmpfp, mname);
1055 #if defined(sparc)
1056         fprintf (tmpfp, "\t.type %s,#function\n", mname);
1057 #elif !(defined(__ppc__) && defined(__MACH__))
1058         fprintf (tmpfp, "\t.type %s,@function\n", mname);
1059 #endif
1060         emit_label(tmpfp, mname);
1061
1062         for (i = 0; i < cfg->code_len; i++) 
1063                 fprintf (tmpfp, ".byte %d\n", (unsigned int) code [i]);
1064
1065         emit_section_change (tmpfp, ".text", 1);
1066
1067         /* Sort relocations */
1068         patches = g_ptr_array_new ();
1069         for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next)
1070                 g_ptr_array_add (patches, patch_info);
1071         g_ptr_array_sort (patches, compare_patches);
1072
1073         /* Emit out of line info for relocations */
1074         j = 0;
1075         for (pindex = 0; pindex < patches->len; ++pindex) {
1076                 patch_info = g_ptr_array_index (patches, pindex);
1077                 switch (patch_info->type) {
1078                 case MONO_PATCH_INFO_LABEL:
1079                 case MONO_PATCH_INFO_BB:
1080                         /* relative jumps are no problem, there is no need to handle then here */
1081                         break;
1082                 case MONO_PATCH_INFO_SWITCH: {
1083                         gpointer *table = (gpointer *)patch_info->data.target;
1084                         int k;
1085
1086                         emit_alignment(tmpfp, sizeof (gpointer));
1087                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1088                         fprintf (tmpfp, "\t.long %d\n", patch_info->table_size);
1089                         
1090                         for (k = 0; k < patch_info->table_size; k++) {
1091                                 fprintf (tmpfp, "\t.long %d\n", (int)table [k]);
1092                         }
1093                         j++;
1094                         break;
1095                 }
1096                 case MONO_PATCH_INFO_INTERNAL_METHOD: {
1097                         guint32 icall_index;
1098
1099                         icall_index = (guint32)g_hash_table_lookup (acfg->icall_hash, patch_info->data.name);
1100                         if (!icall_index) {
1101                                 icall_index = g_hash_table_size (acfg->icall_hash) + 1;
1102                                 g_hash_table_insert (acfg->icall_hash, (gpointer)patch_info->data.name,
1103                                                                          GUINT_TO_POINTER (icall_index));
1104                                 g_ptr_array_add (acfg->icall_table, (gpointer)patch_info->data.name);
1105                         }
1106                         patch_info->data.name = g_strdup_printf ("%d", icall_index - 1);
1107                         j++;
1108                         break;
1109                 }
1110                 case MONO_PATCH_INFO_METHODCONST:
1111                 case MONO_PATCH_INFO_METHOD:
1112                 case MONO_PATCH_INFO_METHOD_JUMP: {
1113                         /*
1114                          * The majority of patches are for methods, so we emit
1115                          * them inline instead of defining a label for them to
1116                          * decrease the number of relocations.
1117                          */
1118                         guint32 image_index = get_image_index (acfg, patch_info->data.method->klass->image);
1119                         guint32 token = patch_info->data.method->token;
1120                         g_assert (image_index < 256);
1121                         g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
1122
1123                         patch_info->data.name = 
1124                                 g_strdup_printf ("%d", (image_index << 24) + (mono_metadata_token_index (token)));
1125                         j++;
1126                         break;
1127                 }
1128                 case MONO_PATCH_INFO_WRAPPER: {
1129                         switch (patch_info->data.method->wrapper_type) {
1130                         case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: {
1131                                 MonoMethod *m;
1132                                 guint32 image_index;
1133                                 guint32 token;
1134
1135                                 m = mono_marshal_method_from_wrapper (patch_info->data.method);
1136                                 image_index = get_image_index (acfg, m->klass->image);
1137                                 token = m->token;
1138                                 g_assert (image_index < 256);
1139                                 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
1140                                 emit_alignment(tmpfp, sizeof (gpointer));
1141                                 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1142                                 fprintf (tmpfp, "\t.long %d\n", patch_info->data.method->wrapper_type);
1143                                 fprintf (tmpfp, "\t.long %d\n", (image_index << 24) + (mono_metadata_token_index (token)));
1144                                 break;
1145                         }
1146                         case MONO_WRAPPER_PROXY_ISINST:
1147                         case MONO_WRAPPER_LDFLD:
1148                         case MONO_WRAPPER_STFLD: {
1149                                 MonoClass *proxy_class = (MonoClass*)mono_marshal_method_from_wrapper (patch_info->data.method);
1150                                 char *klass_label = cond_emit_klass_label (acfg, proxy_class);
1151
1152                                 fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1153                                 fprintf (tmpfp, "\t.long %d\n", patch_info->data.method->wrapper_type);
1154                                 emit_pointer (tmpfp, klass_label);
1155                                 break;
1156                         }
1157                         default:
1158                                 g_assert_not_reached ();
1159                         }
1160                         j++;
1161                         break;
1162                 }
1163                 case MONO_PATCH_INFO_FIELD:
1164                         patch_info->data.name = cond_emit_field_label (acfg, patch_info);
1165                         j++;
1166                         break;
1167                 case MONO_PATCH_INFO_CLASS:
1168                 case MONO_PATCH_INFO_IID:
1169                         patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
1170                         j++;
1171                         break;
1172                 case MONO_PATCH_INFO_IMAGE:
1173                         patch_info->data.name = g_strdup_printf ("%d", get_image_index (acfg, patch_info->data.image));
1174                         j++;
1175                         break;
1176                 case MONO_PATCH_INFO_EXC_NAME: {
1177                         MonoClass *ex_class;
1178                         
1179                         ex_class =
1180                                 mono_class_from_name (mono_defaults.exception_class->image,
1181                                                                           "System", patch_info->data.target);
1182                         g_assert (ex_class);
1183                         patch_info->data.name = cond_emit_klass_label (acfg, ex_class);
1184                         j++;
1185                         break;
1186                 }
1187                 case MONO_PATCH_INFO_R4:
1188                         emit_alignment(tmpfp, 8);
1189                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1190                         fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));     
1191                         j++;
1192                         break;
1193                 case MONO_PATCH_INFO_R8:
1194                         emit_alignment(tmpfp, 8);
1195                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1196                         fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
1197                         fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1));
1198                         j++;
1199                         break;
1200                 case MONO_PATCH_INFO_METHOD_REL:
1201                         emit_alignment(tmpfp, sizeof (gpointer));
1202                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1203                         fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.offset);
1204                         j++;
1205                         break;
1206                 case MONO_PATCH_INFO_VTABLE:
1207                 case MONO_PATCH_INFO_CLASS_INIT:
1208                         patch_info->data.name = cond_emit_klass_label (acfg, patch_info->data.klass);
1209                         j++;
1210                         break;
1211                 case MONO_PATCH_INFO_SFLDA:
1212                         patch_info->data.name = cond_emit_field_label (acfg, patch_info);
1213                         j++;
1214                         break;
1215                 case MONO_PATCH_INFO_LDSTR:
1216                 case MONO_PATCH_INFO_LDTOKEN:
1217                 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
1218                         emit_alignment(tmpfp, 8);
1219                         fprintf (tmpfp, "%s_p_%d:\n", mname, j);
1220                         fprintf (tmpfp, "\t.long 0x%08x\n", get_image_index (acfg, patch_info->data.token->image));
1221                         fprintf (tmpfp, "\t.long 0x%08x\n", patch_info->data.token->token);
1222                         j++;
1223                         break;
1224                 default:
1225                         g_warning ("unable to handle jump info %d", patch_info->type);
1226                         g_assert_not_reached ();
1227                 }
1228         }
1229
1230         emit_global (tmpfp, mname_p);
1231         emit_alignment (tmpfp, sizeof (gpointer));
1232         emit_label (tmpfp, mname_p);
1233
1234         fprintf (tmpfp, "\t.long %d\n", cfg->code_len);
1235         fprintf (tmpfp, "\t.long %ld\n", (long)cfg->used_int_regs);
1236
1237         /* Exception table */
1238         if (header->num_clauses) {
1239                 MonoJitInfo *jinfo = cfg->jit_info;
1240
1241                 fprintf (tmpfp, "\t.long %d\n", jinfo->exvar_offset);
1242
1243                 for (k = 0; k < header->num_clauses; ++k) {
1244                         MonoJitExceptionInfo *ei = &jinfo->clauses [k];
1245
1246                         if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER)
1247                                 fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->data.filter - code);
1248                         else
1249                                 /* fixme: tokens are not global */
1250                                 fprintf (tmpfp, "\t.long %d\n", ei->data.token);
1251
1252                         fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_start - code);
1253                         fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->try_end - code);
1254                         fprintf (tmpfp, "\t.long %d\n", (guint8*)ei->handler_start - code);
1255                 }
1256         }
1257
1258         /* String table */
1259         if (cfg->opt & MONO_OPT_SHARED) {
1260                 fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list));
1261                 for (l = cfg->ldstr_list; l; l = l->next) {
1262                         fprintf (tmpfp, "\t.long 0x%08lx\n", (long)l->data);
1263                 }
1264         }
1265         else
1266                 /* Used only in shared mode */
1267                 g_assert (!cfg->ldstr_list);
1268
1269         //printf ("M: %s (%s).\n", mono_method_full_name (method, TRUE), mname);
1270
1271         if (j) {
1272                 guint32 last_offset;
1273                 last_offset = 0;
1274
1275                 j = 0;
1276
1277                 /* First emit the type+position table */
1278                 for (pindex = 0; pindex < patches->len; ++pindex) {
1279                         guint32 offset;
1280                         patch_info = g_ptr_array_index (patches, pindex);
1281
1282                         if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
1283                                 (patch_info->type == MONO_PATCH_INFO_BB))
1284                                 /* Nothing to do */
1285                                 continue;
1286
1287                         //printf ("T: %d O: %d.\n", patch_info->type, patch_info->ip.i);
1288                         offset = patch_info->ip.i - last_offset;
1289                         last_offset = patch_info->ip.i;
1290
1291                         /* Encode type+position compactly */
1292                         g_assert (patch_info->type < 64);
1293                         if (offset < 1024 - 1) {
1294                                 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + (offset >> 8));
1295                                 fprintf (tmpfp, "\t.byte %d\n", offset & ((1 << 8) - 1));
1296                         }
1297                         else {
1298                                 fprintf (tmpfp, "\t.byte %d\n", (patch_info->type << 2) + 3);
1299                                 fprintf (tmpfp, "\t.byte %d\n", 255);
1300                                 emit_alignment(tmpfp, 4);
1301                                 fprintf (tmpfp, "\t.long %d\n", offset);
1302                         }
1303                 }
1304
1305                 /*
1306                  * 0 is PATCH_INFO_BB, which can't be in the file.
1307                  */
1308                 /* NULL terminated array */
1309                 fprintf (tmpfp, "\t.byte 0\n");
1310
1311                 emit_alignment (tmpfp, sizeof (gpointer));
1312
1313                 /* Then emit the other info */
1314                 for (pindex = 0; pindex < patches->len; ++pindex) {
1315                         patch_info = g_ptr_array_index (patches, pindex);
1316
1317                         if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
1318                                 (patch_info->type == MONO_PATCH_INFO_BB))
1319                                 /* Nothing to do */
1320                                 continue;
1321
1322                         switch (patch_info->type) {
1323                         case MONO_PATCH_INFO_METHODCONST:
1324                         case MONO_PATCH_INFO_METHOD:
1325                         case MONO_PATCH_INFO_METHOD_JUMP:
1326                         case MONO_PATCH_INFO_CLASS:
1327                         case MONO_PATCH_INFO_IID:
1328                         case MONO_PATCH_INFO_FIELD:
1329                         case MONO_PATCH_INFO_INTERNAL_METHOD:
1330                         case MONO_PATCH_INFO_IMAGE:
1331                         case MONO_PATCH_INFO_VTABLE:
1332                         case MONO_PATCH_INFO_CLASS_INIT:
1333                         case MONO_PATCH_INFO_SFLDA:
1334                         case MONO_PATCH_INFO_EXC_NAME:
1335                                 emit_pointer (tmpfp, patch_info->data.name);
1336                                 j++;
1337                                 break;
1338                         case MONO_PATCH_INFO_SWITCH:
1339                         case MONO_PATCH_INFO_R4:
1340                         case MONO_PATCH_INFO_R8:
1341                         case MONO_PATCH_INFO_METHOD_REL:
1342                         case MONO_PATCH_INFO_LDSTR:
1343                         case MONO_PATCH_INFO_LDTOKEN:
1344                         case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
1345                         case MONO_PATCH_INFO_WRAPPER: {
1346                                 char buf [256];
1347                                 sprintf (buf, "%s_p_%d", mname, j);
1348                                 emit_pointer (tmpfp, buf);
1349                                 j++;
1350                                 break;
1351                         }
1352                         case MONO_PATCH_INFO_LABEL:
1353                         case MONO_PATCH_INFO_BB:
1354                                 break;
1355                         default:
1356                                 g_warning ("unable to handle jump info %d", patch_info->type);
1357                                 g_assert_not_reached ();
1358                         }
1359
1360                 }
1361         }
1362
1363         {
1364                 guint8 *buf;
1365                 guint32 buf_len;
1366
1367                 mono_debug_serialize_debug_info (cfg, &buf, &buf_len);
1368
1369                 fprintf (tmpfp, "\t.long %d\n", buf_len);
1370
1371                 for (i = 0; i < buf_len; ++i)
1372                         fprintf (tmpfp, ".byte %d\n", (unsigned int) buf [i]);
1373
1374                 if (buf_len > 0)
1375                         g_free (buf);
1376         }
1377
1378         /* fixme: save the rest of the required infos */
1379
1380         g_free (mname);
1381         g_free (mname_p);
1382 }
1383
1384 static gboolean
1385 str_begins_with (const char *str1, const char *str2)
1386 {
1387         int len = strlen (str2);
1388         return strncmp (str1, str2, len) == 0;
1389 }
1390
1391 static void
1392 mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts)
1393 {
1394         gchar **args, **ptr;
1395
1396         memset (opts, 0, sizeof (*opts));
1397
1398         args = g_strsplit (aot_options ? aot_options : "", ",", -1);
1399         for (ptr = args; ptr && *ptr; ptr ++) {
1400                 const char *arg = *ptr;
1401
1402                 if (str_begins_with (arg, "outfile=")) {
1403                         opts->outfile = g_strdup (arg + strlen ("outfile="));
1404                 }
1405                 else {
1406                         fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg);
1407                         exit (1);
1408                 }
1409         }
1410 }
1411
1412 int
1413 mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options)
1414 {
1415         MonoCompile *cfg;
1416         MonoImage *image = ass->image;
1417         MonoMethod *method;
1418         char *com, *tmpfname, *opts_str;
1419         FILE *tmpfp;
1420         int i;
1421         guint8 *symbol;
1422         int ccount = 0, mcount = 0, lmfcount = 0, abscount = 0, wrappercount = 0, ocount = 0;
1423         GHashTable *ref_hash;
1424         MonoAotCompile *acfg;
1425         gboolean *emitted;
1426         MonoAotOptions aot_opts;
1427         char *outfile_name, *tmp_outfile_name;
1428
1429         printf ("Mono Ahead of Time compiler - compiling assembly %s\n", image->name);
1430
1431         mono_aot_parse_options (aot_options, &aot_opts);
1432
1433         i = g_file_open_tmp ("mono_aot_XXXXXX", &tmpfname, NULL);
1434         tmpfp = fdopen (i, "w+");
1435         g_assert (tmpfp);
1436
1437         ref_hash = g_hash_table_new (NULL, NULL);
1438
1439         acfg = g_new0 (MonoAotCompile, 1);
1440         acfg->fp = tmpfp;
1441         acfg->ref_hash = ref_hash;
1442         acfg->icall_hash = g_hash_table_new (NULL, NULL);
1443         acfg->icall_table = g_ptr_array_new ();
1444         acfg->image_hash = g_hash_table_new (NULL, NULL);
1445         acfg->image_table = g_ptr_array_new ();
1446
1447         write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid);
1448
1449         write_string_symbol (tmpfp, "mono_aot_version", MONO_AOT_FILE_VERSION);
1450
1451         opts_str = g_strdup_printf ("%d", opts);
1452         write_string_symbol (tmpfp, "mono_aot_opt_flags", opts_str);
1453         g_free (opts_str);
1454
1455         emitted = g_new0 (gboolean, image->tables [MONO_TABLE_METHOD].rows);
1456
1457         for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
1458                 MonoJumpInfo *patch_info;
1459                 gboolean skip;
1460                 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
1461                 method = mono_get_method (image, token, NULL);
1462                 
1463                 /* fixme: maybe we can also precompile wrapper methods */
1464                 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
1465                     (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
1466                     (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
1467                     (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
1468                         //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
1469                         continue;
1470                 }
1471
1472                 mcount++;
1473
1474                 /* fixme: we need to patch the IP for the LMF in that case */
1475                 if (method->save_lmf) {
1476                         //printf ("Skip (needs lmf):  %s\n", mono_method_full_name (method, TRUE));
1477                         lmfcount++;
1478                         continue;
1479                 }
1480
1481                 //printf ("START:           %s\n", mono_method_full_name (method, TRUE));
1482                 //mono_compile_method (method);
1483
1484                 cfg = mini_method_compile (method, opts, mono_get_root_domain (), FALSE, 0);
1485                 g_assert (cfg);
1486
1487                 if (cfg->disable_aot) {
1488                         printf ("Skip (other): %s\n", mono_method_full_name (method, TRUE));
1489                         ocount++;
1490                         continue;
1491                 }
1492
1493                 skip = FALSE;
1494                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1495                         if (patch_info->type == MONO_PATCH_INFO_ABS) {
1496                                 /* unable to handle this */
1497                                 //printf ("Skip (abs addr):   %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
1498                                 skip = TRUE;    
1499                                 break;
1500                         }
1501                 }
1502
1503                 if (skip) {
1504                         abscount++;
1505                         continue;
1506                 }
1507
1508                 /* some wrappers are very common */
1509                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1510                         if (patch_info->type == MONO_PATCH_INFO_METHODCONST) {
1511                                 switch (patch_info->data.method->wrapper_type) {
1512                                 case MONO_WRAPPER_PROXY_ISINST:
1513                                         patch_info->type = MONO_PATCH_INFO_WRAPPER;
1514                                 }
1515                         }
1516
1517                         if (patch_info->type == MONO_PATCH_INFO_METHOD) {
1518                                 switch (patch_info->data.method->wrapper_type) {
1519                                 case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
1520                                 case MONO_WRAPPER_STFLD:
1521                                 case MONO_WRAPPER_LDFLD:
1522                                         patch_info->type = MONO_PATCH_INFO_WRAPPER;
1523                                         break;
1524                                 }
1525                         }
1526                 }
1527
1528                 skip = FALSE;
1529                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
1530                         if ((patch_info->type == MONO_PATCH_INFO_METHOD ||
1531                              patch_info->type == MONO_PATCH_INFO_METHODCONST) &&
1532                             patch_info->data.method->wrapper_type) {
1533                                 /* unable to handle this */
1534                                 //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));
1535                                 skip = TRUE;    
1536                                 break;
1537                         }
1538                 }
1539
1540                 if (skip) {
1541                         wrappercount++;
1542                         continue;
1543                 }
1544
1545                 //printf ("Compile:           %s\n", mono_method_full_name (method, TRUE));
1546
1547                 emitted [i] = TRUE;
1548                 emit_method (acfg, cfg);
1549
1550                 mono_destroy_compile (cfg);
1551
1552                 ccount++;
1553         }
1554
1555         /*
1556          * The icall and image tables are small but referenced in a lot of places.
1557          * So we emit them at once, and reference their elements by an index
1558          * instead of an assembly label to cut back on the number of relocations.
1559          */
1560
1561         /* Emit icall table */
1562
1563         symbol = g_strdup_printf ("mono_icall_table");
1564         emit_section_change (tmpfp, ".text", 1);
1565         emit_global(tmpfp, symbol);
1566         emit_alignment(tmpfp, 8);
1567         emit_label(tmpfp, symbol);
1568         fprintf (tmpfp, ".long %d\n", acfg->icall_table->len);
1569         for (i = 0; i < acfg->icall_table->len; i++)
1570                 fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, (char*)g_ptr_array_index (acfg->icall_table, i));
1571
1572         /* Emit image table */
1573
1574         symbol = g_strdup_printf ("mono_image_table");
1575         emit_section_change (tmpfp, ".text", 1);
1576         emit_global(tmpfp, symbol);
1577         emit_alignment(tmpfp, 8);
1578         emit_label(tmpfp, symbol);
1579         fprintf (tmpfp, ".long %d\n", acfg->image_table->len);
1580         for (i = 0; i < acfg->image_table->len; i++)
1581                 fprintf (tmpfp, "%s \"%s\"\n", AS_STRING_DIRECTIVE, ((MonoImage*)g_ptr_array_index (acfg->image_table, i))->guid);
1582
1583         /*
1584          * g_module_symbol takes a lot of time for failed lookups, so we emit
1585          * a table which contains one bit for each method. This bit specifies
1586          * whenever the method is emitted or not.
1587          */
1588
1589         symbol = g_strdup_printf ("mono_methods_present_table");
1590         emit_section_change (tmpfp, ".text", 1);
1591         emit_global(tmpfp, symbol);
1592         emit_alignment(tmpfp, 8);
1593         emit_label(tmpfp, symbol);
1594         {
1595                 guint32 k, nrows;
1596                 guint32 w;
1597
1598                 nrows = image->tables [MONO_TABLE_METHOD].rows;
1599                 for (i = 0; i < nrows / 32 + 1; ++i) {
1600                         w = 0;
1601                         for (k = 0; k < 32; ++k) {
1602                                 if (emitted [(i * 32) + k])
1603                                         w += (1 << k);
1604                         }
1605                         //printf ("EMITTED [%d] = %d.\n", i, b);
1606                         fprintf (tmpfp, "\t.long %d\n", w);
1607                 }
1608         }
1609
1610         fclose (tmpfp);
1611
1612 #if defined(sparc) && SIZEOF_VOID_P == 8
1613         com = g_strdup_printf ("as -xarch=v9 %s -o %s.o", tmpfname, tmpfname);
1614 #else
1615         com = g_strdup_printf ("as %s -o %s.o", tmpfname, tmpfname);
1616 #endif
1617         printf ("Executing the native assembler: %s\n", com);
1618         if (system (com) != 0) {
1619                 g_free (com);
1620                 return 1;
1621         }
1622
1623         g_free (com);
1624
1625         if (aot_opts.outfile)
1626                 outfile_name = g_strdup_printf ("%s", aot_opts.outfile);
1627         else
1628                 outfile_name = g_strdup_printf ("%s%s", image->name, SHARED_EXT);
1629
1630         tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name);
1631
1632 #if defined(sparc)
1633         com = g_strdup_printf ("ld -shared -G -o %s %s.o", outfile_name, tmpfname);
1634 #elif defined(__ppc__) && defined(__MACH__)
1635         com = g_strdup_printf ("gcc -dynamiclib -o %s %s.o", outfile_name, tmpfname);
1636 #else
1637         com = g_strdup_printf ("ld -shared -o %s %s.o", outfile_name, tmpfname);
1638 #endif
1639         printf ("Executing the native linker: %s\n", com);
1640         if (system (com) != 0) {
1641                 g_free (tmp_outfile_name);
1642                 g_free (outfile_name);
1643                 g_free (com);
1644                 return 1;
1645         }
1646
1647         g_free (com);
1648         com = g_strdup_printf ("%s.o", tmpfname);
1649         unlink (com);
1650         g_free (com);
1651         /*com = g_strdup_printf ("strip --strip-unneeded %s%s", image->name, SHARED_EXT);
1652         printf ("Stripping the binary: %s\n", com);
1653         system (com);
1654         g_free (com);*/
1655
1656         rename (tmp_outfile_name, outfile_name);
1657
1658         g_free (tmp_outfile_name);
1659         g_free (outfile_name);
1660
1661         printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, mcount ? (ccount*100)/mcount : 100);
1662         printf ("%d methods contain absolute addresses (%d%%)\n", abscount, mcount ? (abscount*100)/mcount : 100);
1663         printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, mcount ? (wrappercount*100)/mcount : 100);
1664         printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, mcount ? (lmfcount*100)/mcount : 100);
1665         printf ("%d methods have other problems (%d%%)\n", ocount, mcount ? (ocount*100)/mcount : 100);
1666         //printf ("Retained input file.\n");
1667         unlink (tmpfname);
1668
1669         return 0;
1670 }
1671
1672