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