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