Tue Aug 5 12:21:08 CEST 2003 Paolo Molaro <lupus@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 #ifndef PLATFORM_WIN32
15 #include <sys/mman.h>
16 #else
17 #include <windows.h>
18 #endif
19
20 #include <limits.h>    /* for PAGESIZE */
21 #ifndef PAGESIZE
22 #define PAGESIZE 4096
23 #endif
24
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/class.h>
27 #include <mono/metadata/object.h>
28 #include <mono/metadata/tokentype.h>
29 #include <mono/metadata/appdomain.h>
30 #include <mono/metadata/debug-helpers.h>
31
32 #include "mini.h"
33
34 #define ENCODE_TYPE_POS(t,l) (((t) << 24) | (l))
35 #define DECODE_TYPE(v) ((v) >> 24)
36 #define DECODE_POS(v) ((v) & 0xffffff)
37
38 #ifdef PLATFORM_WIN32
39 #define SHARED_EXT ".dll"
40 #else
41 #define SHARED_EXT ".so"
42 #endif
43
44 static MonoClass * 
45 decode_class_info (gpointer *data)
46 {
47         MonoImage *image;
48         MonoClass *klass;
49         
50         image = mono_image_loaded_by_guid ((char *)data [1]);
51         g_assert (image);
52
53         if (data [0]) {
54                 return mono_class_get (image, (guint32)data [0]);
55         } else {
56                 klass = decode_class_info (data [3]);
57                 return mono_array_class_get (klass, (guint32)data [2]);
58         }
59
60         return NULL;
61 }
62  
63 gpointer
64 mono_aot_get_method (MonoMethod *method)
65 {
66         MonoClass *klass = method->klass;
67         MonoAssembly *ass = klass->image->assembly;
68         MonoJumpInfo *patch_info = NULL;
69         GModule *module = ass->aot_module;
70         char *method_label, *info_label;
71         guint8 *code = NULL;
72         gpointer *info;
73         guint code_len, used_int_regs, used_strings;
74         int i;
75
76         if (!module)
77                 return NULL;
78
79         if (!method->token)
80                 return NULL;
81
82         g_assert (klass->inited);
83
84         method_label = g_strdup_printf ("method_%08X", method->token);
85
86         if (!g_module_symbol (module, method_label, (gpointer *)&code)) {
87                 g_free (method_label);          
88                 return NULL;
89         }
90
91         info_label = g_strdup_printf ("%s_patch_info", method_label);
92         if (!g_module_symbol (module, info_label, (gpointer *)&info)) {
93                 g_free (method_label);          
94                 g_free (info_label);
95                 return NULL;
96         }
97
98         //printf ("FOUND AOT compiled code for %s %p %p\n", mono_method_full_name (method, TRUE), code, info);
99
100         code_len = GPOINTER_TO_UINT (*((gpointer **)info));
101         info++;
102         used_int_regs = GPOINTER_TO_UINT (*((gpointer **)info));
103         info++;
104         used_strings = GPOINTER_TO_UINT (*((gpointer **)info));
105         info++;
106
107         for (i = 0; i < used_strings; i++) {
108                 guint token =  GPOINTER_TO_UINT (*((gpointer **)info));
109                 info++;
110                 mono_ldstr (mono_root_domain, klass->image, mono_metadata_token_index (token));
111         }
112
113
114         if (info) {
115                 MonoMemPool *mp = mono_mempool_new (); 
116                 MonoImage *image;
117                 guint8 *page_start;
118                 gpointer *table;
119                 int pages;
120                 int i, err;
121
122                 while (*info) {
123                         MonoJumpInfo *ji = mono_mempool_alloc0 (mp, sizeof (MonoJumpInfo));
124                         gpointer *data = *((gpointer **)info);
125                         info++;
126                         ji->type = DECODE_TYPE (GPOINTER_TO_UINT (*info));
127                         ji->ip.i = DECODE_POS (GPOINTER_TO_UINT (*info));
128
129                         switch (ji->type) {
130                         case MONO_PATCH_INFO_CLASS:
131                                 ji->data.klass = decode_class_info (data);
132                                 g_assert (ji->data.klass);
133                                 mono_class_init (ji->data.klass);
134                                 break;
135                         case MONO_PATCH_INFO_IMAGE:
136                                 ji->data.image = mono_image_loaded_by_guid ((char *)data);
137                                 g_assert (ji->data.image);
138                                 break;
139                         case MONO_PATCH_INFO_METHOD:
140                         case MONO_PATCH_INFO_METHODCONST:
141                                 image = mono_image_loaded_by_guid ((char *)data [1]);
142                                 g_assert (image);
143                                 ji->data.method = mono_get_method (image, (guint32)data [0], NULL);
144                                 g_assert (ji->data.method);
145                                 mono_class_init (ji->data.method->klass);
146                                 break;
147                         case MONO_PATCH_INFO_FIELD:
148                                 image = mono_image_loaded_by_guid ((char *)data [1]);
149                                 g_assert (image);
150                                 ji->data.field = mono_field_from_token (image, (guint32)data [0], NULL);
151                                 mono_class_init (ji->data.field->parent);
152                                 g_assert (ji->data.field);
153                                 break;
154                         case MONO_PATCH_INFO_INTERNAL_METHOD:
155                                 ji->data.name = (char *)data;
156                                 g_assert (ji->data.name);
157                                 break;
158                         case MONO_PATCH_INFO_SWITCH:
159                                 ji->table_size = (int)data [0];
160                                 table = g_new (gpointer, ji->table_size);
161                                 ji->data.target = table;
162                                 for (i = 0; i < ji->table_size; i++) {
163                                         table [i] = data [i + 1];
164                                 }
165                                 break;
166                         case MONO_PATCH_INFO_R4:
167                         case MONO_PATCH_INFO_R8:
168                                 ji->data.target = data;
169                                 break;
170                         default:
171                                 g_warning ("unhandled type %d", ji->type);
172                                 g_assert_not_reached ();
173                         }
174
175                         info++;
176                         ji->next = patch_info;
177                         patch_info = ji;
178                 }
179
180 #ifndef PLATFORM_WIN32
181                 /* disable write protection */
182                 page_start = (char *) (((int) (code)) & ~ (PAGESIZE - 1));
183                 pages = (code + code_len - page_start + PAGESIZE - 1) / PAGESIZE;
184                 err = mprotect (page_start, pages * PAGESIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
185                 g_assert (err == 0);
186 #else
187                 {
188                         DWORD oldp;
189                         g_assert (VirtualProtect (code, code_len, PAGE_EXECUTE_READWRITE, &oldp) != 0);
190                 }
191 #endif
192
193                 mono_arch_patch_code (method, mono_root_domain, code, patch_info);
194                 mono_mempool_destroy (mp);
195         }
196
197         g_free (info_label);
198         g_free (method_label);
199
200         {
201                 MonoJitInfo *info;
202                 info = mono_mempool_alloc0 (mono_root_domain->mp, sizeof (MonoJitInfo));
203                 info->code_size = code_len;
204                 info->used_regs = used_int_regs;
205                 info->method = method;
206                 info->code_start = code;
207                 mono_jit_info_table_add (mono_root_domain, info);
208         }
209         mono_jit_stats.methods_aot++;
210
211         return code;
212 }
213
214 #if 0
215 static void
216 write_data_symbol (FILE *fp, const char *name, guint8 *buf, int size, int align)
217 {
218         int i;
219
220         fprintf (fp, ".globl %s\n", name);
221         fprintf (fp, ".data\n\t.align %d\n", align);
222         fprintf (fp, "\t.type %s,@object\n", name);
223         fprintf (fp, "\t.size %s,%d\n", name, size);
224         fprintf (fp, "%s:\n", name);
225         for (i = 0; i < size; i++) { 
226                 fprintf (fp, ".byte %d\n", buf [i]);
227         }
228         
229 }
230 #endif
231
232 static void
233 write_string_symbol (FILE *fp, const char *name, char *value)
234 {
235         fprintf (fp, ".globl %s\n", name);
236         fprintf (fp, ".data\n");
237         fprintf (fp, "%s:\n", name);
238         fprintf (fp, "\t.string \"%s\"\n", value);
239 }
240
241 static guint32
242 mono_get_field_token (MonoClassField *field) 
243 {
244         MonoClass *klass = field->parent;
245         int i;
246
247         for (i = 0; i < klass->field.count; ++i) {
248                 if (field == &klass->fields [i])
249                         return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i);
250         }
251
252         g_assert_not_reached ();
253         return 0;
254 }
255
256 static char *
257 cond_emit_image_label (FILE *tmpfp, GHashTable *image_hash, MonoImage *image)
258 {
259         char *label;
260         
261         if ((label = g_hash_table_lookup (image_hash, image))) 
262                 return label;
263
264         label = g_strdup_printf ("image_patch_info_%p", image);
265         fprintf (tmpfp, "%s:\n", label);
266         fprintf (tmpfp, "\t.string \"%s\"\n", image->guid);
267
268         g_hash_table_insert (image_hash, image, label);
269
270         return label;
271 }
272
273 static char *
274 cond_emit_icall_label (FILE *tmpfp, GHashTable *hash, const char *icall_name)
275 {
276         char *label;
277
278         if ((label = g_hash_table_lookup (hash, icall_name))) 
279                 return label;
280
281         label = g_strdup_printf ("icall_patch_info_%s", icall_name);
282         fprintf (tmpfp, "%s:\n", label);
283         fprintf (tmpfp, "\t.string \"%s\"\n", icall_name);
284
285         g_hash_table_insert (hash, (gpointer)icall_name, label);
286
287         return label;
288 }
289
290 static char *
291 cond_emit_method_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info)
292 {
293         MonoMethod *method = patch_info->data.method;
294         char *l1, *l2;
295
296         if ((l1 = g_hash_table_lookup (hash, method))) 
297                 return l1;
298         
299         l2 = cond_emit_image_label (tmpfp, hash, method->klass->image);
300         fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
301         l1 = g_strdup_printf ("method_patch_info_%08x_%p", method->token, method);
302         fprintf (tmpfp, "%s:\n", l1);
303         fprintf (tmpfp, "\t.long 0x%08x\n", method->token);
304         g_assert (method->token);
305         fprintf (tmpfp, "\t.long %s\n", l2);
306                 
307         g_hash_table_insert (hash, method, l1);
308
309         return l1;
310 }
311
312 static char *
313 cond_emit_klass_label (FILE *tmpfp, GHashTable *hash, MonoClass *klass)
314 {
315         char *l1, *l2, *el = NULL;
316
317         if ((l1 = g_hash_table_lookup (hash, klass))) 
318                 return l1;
319
320         if (!klass->type_token) {
321                 g_assert (klass->rank > 0);
322                 el = cond_emit_klass_label (tmpfp, hash, klass->element_class);
323         }
324         
325         l2 = cond_emit_image_label (tmpfp, hash, klass->image);
326         fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
327         l1 = g_strdup_printf ("klass_patch_info_%08x_%p", klass->type_token, klass);
328         fprintf (tmpfp, "%s:\n", l1);
329         fprintf (tmpfp, "\t.long 0x%08x\n", klass->type_token);
330         fprintf (tmpfp, "\t.long %s\n", l2);
331
332         if (el) {
333                 fprintf (tmpfp, "\t.long %d\n", klass->rank);   
334                 fprintf (tmpfp, "\t.long %s\n", el);
335         }
336
337         g_hash_table_insert (hash, klass, l1);
338
339         return l1;
340 }
341
342 static char *
343 cond_emit_field_label (FILE *tmpfp, GHashTable *hash, MonoJumpInfo *patch_info)
344 {
345         MonoClassField *field = patch_info->data.field;
346         char *l1, *l2;
347         guint token;
348
349         if ((l1 = g_hash_table_lookup (hash, field))) 
350                 return l1;
351         
352         l2 = cond_emit_image_label (tmpfp, hash, field->parent->image);
353         fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
354         token = mono_get_field_token (field);
355         l1 = g_strdup_printf ("klass_patch_info_%08x_%p", token, field);
356         fprintf (tmpfp, "%s:\n", l1);
357         fprintf (tmpfp, "\t.long 0x%08x\n", token);
358         g_assert (token);
359         fprintf (tmpfp, "\t.long %s\n", l2);
360                 
361         g_hash_table_insert (hash, field, l1);
362
363         return l1;
364 }
365
366 int
367 mono_compile_assembly (MonoAssembly *ass, guint32 opts)
368 {
369         MonoCompile *cfg;
370         MonoImage *image = ass->image;
371         MonoMethod *method;
372         GList *l;
373         char *com, *tmpfname;
374         FILE *tmpfp;
375         int i, j;
376         guint8 *code, *mname;
377         int ccount = 0, mcount = 0, lmfcount = 0, ecount = 0, abscount = 0, wrappercount = 0, ocount = 0;
378         GHashTable *ref_hash;
379         int func_alignment = 16;
380
381         printf ("Mono AOT compiler - compiling assembly %s\n", image->name);
382
383         i = g_file_open_tmp ("mono_aot_XXXXXX", &tmpfname, NULL);
384         tmpfp = fdopen (i, "w+");
385         g_assert (tmpfp);
386
387         ref_hash = g_hash_table_new (NULL, NULL);
388
389         write_string_symbol (tmpfp, "mono_assembly_guid" , image->guid);
390                 
391         for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
392                 MonoJumpInfo *patch_info;
393                 gboolean skip;
394                 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
395                 method = mono_get_method (image, token, NULL);
396                 
397                 /* fixme: maybe we can also precompile wrapper methods */
398                 if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
399                     (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
400                     (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
401                     (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
402                         //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
403                         continue;
404                 }
405
406                 mcount++;
407
408                 /* fixme: we need to patch the IP for the LMF in that case */
409                 if (method->save_lmf) {
410                         //printf ("Skip (needs lmf):  %s\n", mono_method_full_name (method, TRUE));
411                         lmfcount++;
412                         continue;
413                 }
414
415                 /* fixme: add methods with exception tables */
416                 if (((MonoMethodNormal *)method)->header->num_clauses) {
417                         //printf ("Skip (exceptions): %s\n", mono_method_full_name (method, TRUE));
418                         ecount++;
419                         continue;
420                 }
421
422                 //printf ("START:           %s\n", mono_method_full_name (method, TRUE));
423                 //mono_compile_method (method);
424
425                 cfg = mini_method_compile (method, opts, mono_root_domain, 0);
426                 g_assert (cfg);
427
428                 if (cfg->disable_aot) {
429                         ocount++;
430                         continue;
431                 }
432
433                 skip = FALSE;
434                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
435                         if (patch_info->type == MONO_PATCH_INFO_ABS) {
436                                 /* unable to handle this */
437                                 //printf ("Skip (abs addr):   %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
438                                 skip = TRUE;    
439                                 break;
440                         }
441                 }
442
443                 if (skip) {
444                         abscount++;
445                         continue;
446                 }
447
448                 skip = FALSE;
449                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
450                         if ((patch_info->type == MONO_PATCH_INFO_METHOD ||
451                              patch_info->type == MONO_PATCH_INFO_METHODCONST) &&
452                             patch_info->data.method->wrapper_type) {
453                                 /* unable to handle this */
454                                 //printf ("Skip (wrapper call):   %s %d\n", mono_method_full_name (method, TRUE), patch_info->type);
455                                 skip = TRUE;    
456                                 break;
457                         }
458                 }
459
460                 if (skip) {
461                         wrappercount++;
462                         continue;
463                 }
464
465                 //printf ("Compile:           %s\n", mono_method_full_name (method, TRUE));
466
467                 code = cfg->native_code;
468
469                 fprintf (tmpfp, ".text\n");
470                 mname = g_strdup_printf ("method_%08X", token);
471                 fprintf (tmpfp, "\t.align %d\n", func_alignment);
472                 fprintf (tmpfp, ".globl %s\n", mname);
473                 fprintf (tmpfp, "\t.type %s,@function\n", mname);
474                 fprintf (tmpfp, "%s:\n", mname);
475
476                 for (j = 0; j < cfg->code_len; j++) 
477                         fprintf (tmpfp, ".byte %d\n", (unsigned int) code [j]);
478
479                 fprintf (tmpfp, ".data\n");
480
481                 j = 0;
482                 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
483                         switch (patch_info->type) {
484                         case MONO_PATCH_INFO_LABEL:
485                         case MONO_PATCH_INFO_BB:
486                                 /* relative jumps are no problem, there is no need to handle then here */
487                                 break;
488                         case MONO_PATCH_INFO_SWITCH: {
489                                 gpointer *table = (gpointer *)patch_info->data.target;
490                                 int k;
491
492                                 fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
493                                 fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j);
494                                 fprintf (tmpfp, "\t.long %d\n", patch_info->table_size);
495
496                                 for (k = 0; k < patch_info->table_size; k++) {
497                                         fprintf (tmpfp, "\t.long %d\n", (guint8 *)table [k] - code);
498                                 }
499                                 j++;
500                                 break;
501                         }
502                         case MONO_PATCH_INFO_INTERNAL_METHOD:
503                                 patch_info->data.name = cond_emit_icall_label (tmpfp, ref_hash, patch_info->data.name);
504                                 j++;
505                                 break;
506                         case MONO_PATCH_INFO_METHODCONST:
507                         case MONO_PATCH_INFO_METHOD:
508                                 patch_info->data.name = cond_emit_method_label (tmpfp, ref_hash, patch_info);
509                                 j++;
510                                 break;
511                         case MONO_PATCH_INFO_FIELD:
512                                 patch_info->data.name = cond_emit_field_label (tmpfp, ref_hash, patch_info);
513                                 j++;
514                                 break;
515                         case MONO_PATCH_INFO_CLASS:
516                                 patch_info->data.name = cond_emit_klass_label (tmpfp, ref_hash, patch_info->data.klass);
517                                 j++;
518                                 break;
519                         case MONO_PATCH_INFO_IMAGE:
520                                 patch_info->data.name = cond_emit_image_label (tmpfp, ref_hash, patch_info->data.image);
521                                 j++;
522                                 break;
523                         case MONO_PATCH_INFO_R4:
524                                 fprintf (tmpfp, "\t.align 8\n");
525                                 fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j);
526                                 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
527                                 
528                                 j++;
529                                 break;
530                         case MONO_PATCH_INFO_R8:
531                                 fprintf (tmpfp, "\t.align 8\n");
532                                 fprintf (tmpfp, "%s_patch_info_%d:\n", mname, j);
533                                 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target));
534                                 fprintf (tmpfp, "\t.long 0x%08x\n", *((guint32 *)patch_info->data.target + 1));
535                                 
536                                 j++;
537                                 break;
538                         default:
539                                 g_warning ("unable to handle jump info %d", patch_info->type);
540                                 g_assert_not_reached ();
541                         }
542                 }
543                 
544                 fprintf (tmpfp, ".globl %s_patch_info\n", mname);
545                 fprintf (tmpfp, "\t.align %d\n", sizeof (gpointer));
546                 fprintf (tmpfp, "%s_patch_info:\n", mname);
547                 
548                 fprintf (tmpfp, "\t.long %d\n", cfg->code_len);
549                 fprintf (tmpfp, "\t.long %d\n", cfg->used_int_regs);
550
551                 fprintf (tmpfp, "\t.long %d\n", g_list_length (cfg->ldstr_list));
552                 for (l = cfg->ldstr_list; l; l = l->next) {
553                         fprintf (tmpfp, "\t.long 0x%08x\n", l->data);
554                 }
555
556                 if (j) {
557                         j = 0;
558                         for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
559                                 switch (patch_info->type) {
560                                 case MONO_PATCH_INFO_METHODCONST:
561                                 case MONO_PATCH_INFO_METHOD:
562                                 case MONO_PATCH_INFO_CLASS:
563                                 case MONO_PATCH_INFO_FIELD:
564                                 case MONO_PATCH_INFO_INTERNAL_METHOD:
565                                 case MONO_PATCH_INFO_IMAGE:
566                                         fprintf (tmpfp, "\t.long %s\n", patch_info->data.name);
567                                         fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i));
568                                         j++;
569                                         break;
570                                 case MONO_PATCH_INFO_SWITCH:
571                                 case MONO_PATCH_INFO_R4:
572                                 case MONO_PATCH_INFO_R8:
573                                         fprintf (tmpfp, "\t.long %s_patch_info_%d\n", mname, j);
574                                         fprintf (tmpfp, "\t.long 0x%08x\n", ENCODE_TYPE_POS (patch_info->type, patch_info->ip.i));
575                                         j++;
576                                         break;
577                                 case MONO_PATCH_INFO_LABEL:
578                                 case MONO_PATCH_INFO_BB:
579                                         break;
580                                 default:
581                                         g_warning ("unable to handle jump info %d", patch_info->type);
582                                         g_assert_not_reached ();
583                                 }
584
585                         }
586                 }
587                 /* NULL terminated array */
588                 fprintf (tmpfp, "\t.long 0\n");
589
590                 /* fixme: save the rest of the required infos */
591
592                 g_free (mname);
593                 mono_destroy_compile (cfg);
594
595                 ccount++;
596         }
597
598         fclose (tmpfp);
599
600         com = g_strdup_printf ("as %s -o %s.o", tmpfname, tmpfname);
601         printf ("Executing the native assembler: %s\n", com);
602         system (com);
603         g_free (com);
604         com = g_strdup_printf ("ld -shared -o %s%s %s.o", image->name, SHARED_EXT, tmpfname);
605         printf ("Executing the native linker: %s\n", com);
606         system (com);
607         g_free (com);
608         com = g_strdup_printf ("%s.o", tmpfname);
609         unlink (com);
610         g_free (com);
611         /*com = g_strdup_printf ("strip --strip-unneeded %s%s", image->name, SHARED_EXT);
612         printf ("Stripping the binary: %s\n", com);
613         system (com);
614         g_free (com);*/
615
616         printf ("Compiled %d out of %d methods (%d%%)\n", ccount, mcount, (ccount*100)/mcount);
617         printf ("%d methods contain exception tables (%d%%)\n", ecount, (ecount*100)/mcount);
618         printf ("%d methods contain absolute addresses (%d%%)\n", abscount, (abscount*100)/mcount);
619         printf ("%d methods contain wrapper references (%d%%)\n", wrappercount, (wrappercount*100)/mcount);
620         printf ("%d methods contain lmf pointers (%d%%)\n", lmfcount, (lmfcount*100)/mcount);
621         printf ("%d methods have other problems (%d%%)\n", ocount, (ocount*100)/mcount);
622         unlink (tmpfname);
623
624         return 0;
625 }