Apply changes from https://github.com/dotnet/runtime/commit/eb1756e97d23df13bc6fe798e...
[mono-project.git] / mono / mini / aot-compiler.c
blob95c04b7ca14447700735afed3809b996acba3c5d
1 /**
2 * \file
3 * mono Ahead of Time compiler
5 * Author:
6 * Dietmar Maurer (dietmar@ximian.com)
7 * Zoltan Varga (vargaz@gmail.com)
8 * Johan Lorensson (lateralusx.github@gmail.com)
10 * (C) 2002 Ximian, Inc.
11 * Copyright 2003-2011 Novell, Inc
12 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
13 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
16 #include "config.h"
17 #include <sys/types.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #ifdef HAVE_STDINT_H
22 #include <stdint.h>
23 #endif
24 #include <fcntl.h>
25 #include <ctype.h>
26 #include <string.h>
27 #ifndef HOST_WIN32
28 #include <sys/time.h>
29 #else
30 #include <winsock2.h>
31 #include <windows.h>
32 #endif
34 #include <errno.h>
35 #include <sys/stat.h>
37 #include <mono/metadata/abi-details.h>
38 #include <mono/metadata/tabledefs.h>
39 #include <mono/metadata/class.h>
40 #include <mono/metadata/object.h>
41 #include <mono/metadata/tokentype.h>
42 #include <mono/metadata/appdomain.h>
43 #include <mono/metadata/debug-helpers.h>
44 #include <mono/metadata/assembly.h>
45 #include <mono/metadata/metadata-internals.h>
46 #include <mono/metadata/reflection-internals.h>
47 #include <mono/metadata/marshal.h>
48 #include <mono/metadata/gc-internals.h>
49 #include <mono/metadata/mempool-internals.h>
50 #include <mono/metadata/mono-endian.h>
51 #include <mono/metadata/threads-types.h>
52 #include <mono/metadata/custom-attrs-internals.h>
53 #include <mono/utils/mono-logger-internals.h>
54 #include <mono/utils/mono-compiler.h>
55 #include <mono/utils/mono-time.h>
56 #include <mono/utils/mono-mmap.h>
57 #include <mono/utils/mono-rand.h>
58 #include <mono/utils/json.h>
59 #include <mono/utils/mono-threads-coop.h>
60 #include <mono/profiler/aot.h>
61 #include <mono/utils/w32api.h>
63 #include "aot-compiler.h"
64 #include "aot-runtime.h"
65 #include "seq-points.h"
66 #include "image-writer.h"
67 #include "dwarfwriter.h"
68 #include "mini-gc.h"
69 #include "mini-llvm.h"
70 #include "mini-runtime.h"
71 #include "interp/interp.h"
73 static MonoMethod*
74 try_get_method_nofail (MonoClass *klass, const char *method_name, int param_count, int flags)
76 MonoMethod *result;
77 ERROR_DECL (error);
78 result = mono_class_get_method_from_name_checked (klass, method_name, param_count, flags, error);
79 mono_error_assert_ok (error);
80 return result;
83 // FIXME Consolidate the multiple functions named get_method_nofail.
84 static MonoMethod*
85 get_method_nofail (MonoClass *klass, const char *method_name, int param_count, int flags)
87 MonoMethod *result = try_get_method_nofail (klass, method_name, param_count, flags);
88 g_assertf (result, "Expected to find method %s in klass %s", method_name, m_class_get_name (klass));
89 return result;
92 #if !defined(DISABLE_AOT) && !defined(DISABLE_JIT)
94 // Use MSVC toolchain, Clang for MSVC using MSVC codegen and linker, when compiling for AMD64
95 // targeting WIN32 platforms running AOT compiler on WIN32 platform with VS installation.
96 #if defined(TARGET_AMD64) && defined(TARGET_WIN32) && defined(HOST_WIN32) && defined(_MSC_VER)
97 #define TARGET_X86_64_WIN32_MSVC
98 #endif
100 #if defined(TARGET_X86_64_WIN32_MSVC)
101 #define TARGET_WIN32_MSVC
102 #endif
104 // Emit native unwind info on Windows platforms (different from DWARF). Emitted unwind info
105 // works when using the MSVC toolchain using Clang for MSVC codegen and linker. Only supported when
106 // compiling for AMD64 (Windows x64 platforms).
107 #if defined(TARGET_WIN32_MSVC) && defined(MONO_ARCH_HAVE_UNWIND_TABLE)
108 #define EMIT_WIN32_UNWIND_INFO
109 #endif
111 #if defined(TARGET_ANDROID) || defined(__linux__)
112 #define RODATA_REL_SECT ".data.rel.ro"
113 #else
114 #define RODATA_REL_SECT ".text"
115 #endif
117 #if defined(__linux__)
118 #define RODATA_SECT ".rodata"
119 #elif defined(TARGET_MACH)
120 #define RODATA_SECT ".section __TEXT, __const"
121 #elif defined(TARGET_WIN32_MSVC)
122 #define RODATA_SECT ".rdata"
123 #else
124 #define RODATA_SECT ".text"
125 #endif
127 #define TV_DECLARE(name) gint64 name
128 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
129 #define TV_ELAPSED(start,end) (((end) - (start)) / 10)
131 #define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1))
133 typedef struct {
134 char *name;
135 MonoImage *image;
136 } ImageProfileData;
138 typedef struct ClassProfileData ClassProfileData;
140 typedef struct {
141 int argc;
142 ClassProfileData **argv;
143 MonoGenericInst *inst;
144 } GInstProfileData;
146 struct ClassProfileData {
147 ImageProfileData *image;
148 char *ns, *name;
149 GInstProfileData *inst;
150 MonoClass *klass;
153 typedef struct {
154 ClassProfileData *klass;
155 int id;
156 char *name;
157 int param_count;
158 char *signature;
159 GInstProfileData *inst;
160 MonoMethod *method;
161 } MethodProfileData;
163 typedef struct {
164 GHashTable *images, *classes, *ginsts, *methods;
165 } ProfileData;
167 /* predefined values for static readonly fields without needed to run the .cctor */
168 typedef struct _ReadOnlyValue ReadOnlyValue;
169 struct _ReadOnlyValue {
170 ReadOnlyValue *next;
171 char *name;
172 int type; /* to be used later for typechecking to prevent user errors */
173 union {
174 guint8 i1;
175 guint16 i2;
176 guint32 i4;
177 guint64 i8;
178 gpointer ptr;
179 } value;
181 static ReadOnlyValue *readonly_values;
183 typedef struct MonoAotOptions {
184 char *outfile;
185 char *llvm_outfile;
186 char *data_outfile;
187 GList *profile_files;
188 gboolean save_temps;
189 gboolean write_symbols;
190 gboolean metadata_only;
191 gboolean bind_to_runtime_version;
192 MonoAotMode mode;
193 gboolean interp;
194 gboolean no_dlsym;
195 gboolean static_link;
196 gboolean asm_only;
197 gboolean asm_writer;
198 gboolean nodebug;
199 gboolean dwarf_debug;
200 gboolean soft_debug;
201 gboolean log_generics;
202 gboolean log_instances;
203 gboolean gen_msym_dir;
204 char *gen_msym_dir_path;
205 gboolean direct_pinvoke;
206 gboolean direct_icalls;
207 gboolean direct_extern_calls;
208 gboolean no_direct_calls;
209 gboolean use_trampolines_page;
210 gboolean no_instances;
211 // We are collecting inflated methods and emitting non-inflated
212 gboolean dedup;
213 // The name of the assembly for which the AOT module is going to have all deduped methods moved to.
214 // When set, we are emitting inflated methods only
215 char *dedup_include;
216 gboolean gnu_asm;
217 gboolean try_llvm;
218 gboolean llvm;
219 gboolean llvm_only;
220 int nthreads;
221 int ntrampolines;
222 int nrgctx_trampolines;
223 int nimt_trampolines;
224 int ngsharedvt_arg_trampolines;
225 int nftnptr_arg_trampolines;
226 int nrgctx_fetch_trampolines;
227 int nunbox_arbitrary_trampolines;
228 gboolean print_skipped_methods;
229 gboolean stats;
230 gboolean verbose;
231 gboolean deterministic;
232 char *tool_prefix;
233 char *ld_flags;
234 char *mtriple;
235 char *llvm_path;
236 char *temp_path;
237 char *instances_logfile_path;
238 char *logfile;
239 char *llvm_opts;
240 char *llvm_llc;
241 char *llvm_cpu_attr;
242 gboolean use_current_cpu;
243 gboolean dump_json;
244 gboolean profile_only;
245 gboolean no_opt;
246 char *clangxx;
247 char *depfile;
248 } MonoAotOptions;
250 typedef enum {
251 METHOD_CAT_NORMAL,
252 METHOD_CAT_GSHAREDVT,
253 METHOD_CAT_INST,
254 METHOD_CAT_WRAPPER,
255 METHOD_CAT_NUM
256 } MethodCategory;
258 typedef struct MonoAotStats {
259 int ccount, mcount, lmfcount, abscount, gcount, ocount, genericcount;
260 gint64 code_size, method_info_size, ex_info_size, unwind_info_size, got_size, class_info_size, got_info_size, plt_size, blob_size;
261 int methods_without_got_slots, direct_calls, all_calls, llvm_count;
262 int got_slots, offsets_size;
263 int method_categories [METHOD_CAT_NUM];
264 int got_slot_types [MONO_PATCH_INFO_NUM];
265 int got_slot_info_sizes [MONO_PATCH_INFO_NUM];
266 int jit_time, gen_time, link_time;
267 int method_ref_count, method_ref_size;
268 int class_ref_count, class_ref_size;
269 int ginst_count, ginst_size;
270 } MonoAotStats;
272 typedef struct GotInfo {
273 GHashTable *patch_to_got_offset;
274 GHashTable **patch_to_got_offset_by_type;
275 GPtrArray *got_patches;
276 } GotInfo;
278 #ifdef EMIT_WIN32_UNWIND_INFO
279 typedef struct _UnwindInfoSectionCacheItem {
280 char *xdata_section_label;
281 PUNWIND_INFO unwind_info;
282 gboolean xdata_section_emitted;
283 } UnwindInfoSectionCacheItem;
284 #endif
286 typedef struct MonoAotCompile {
287 MonoImage *image;
288 GPtrArray *methods;
289 GHashTable *method_indexes;
290 GHashTable *method_depth;
291 MonoCompile **cfgs;
292 int cfgs_size;
293 GHashTable **patch_to_plt_entry;
294 GHashTable *plt_offset_to_entry;
295 //GHashTable *patch_to_got_offset;
296 //GHashTable **patch_to_got_offset_by_type;
297 //GPtrArray *got_patches;
298 GotInfo got_info, llvm_got_info;
299 GHashTable *image_hash;
300 GHashTable *method_to_cfg;
301 GHashTable *token_info_hash;
302 GHashTable *method_to_pinvoke_import;
303 GHashTable *method_to_external_icall_symbol_name;
304 GPtrArray *extra_methods;
305 GPtrArray *image_table;
306 GPtrArray *globals;
307 GPtrArray *method_order;
308 GHashTable *dedup_stats;
309 GHashTable *dedup_cache;
310 gboolean dedup_cache_changed;
311 GHashTable *export_names;
312 /* Maps MonoClass* -> blob offset */
313 GHashTable *klass_blob_hash;
314 /* Maps MonoMethod* -> blob offset */
315 GHashTable *method_blob_hash;
316 /* Maps MonoGenericInst* -> blob offset */
317 GHashTable *ginst_blob_hash;
318 GHashTable *gsharedvt_in_signatures;
319 GHashTable *gsharedvt_out_signatures;
320 guint32 *plt_got_info_offsets;
321 guint32 got_offset, llvm_got_offset, plt_offset, plt_got_offset_base, plt_got_info_offset_base, nshared_got_entries;
322 /* Number of GOT entries reserved for trampolines */
323 guint32 num_trampoline_got_entries;
324 guint32 tramp_page_size;
326 guint32 table_offsets [MONO_AOT_TABLE_NUM];
327 guint32 num_trampolines [MONO_AOT_TRAMP_NUM];
328 guint32 trampoline_got_offset_base [MONO_AOT_TRAMP_NUM];
329 guint32 trampoline_size [MONO_AOT_TRAMP_NUM];
330 guint32 tramp_page_code_offsets [MONO_AOT_TRAMP_NUM];
332 MonoAotOptions aot_opts;
333 guint32 nmethods;
334 int call_table_entry_size;
335 guint32 nextra_methods;
336 guint32 jit_opts;
337 guint32 simd_opts;
338 MonoMemPool *mempool;
339 MonoAotStats stats;
340 int method_index;
341 char *static_linking_symbol;
342 mono_mutex_t mutex;
343 gboolean gas_line_numbers;
344 /* Whenever to emit an object file directly from llc */
345 gboolean llvm_owriter;
346 gboolean llvm_owriter_supported;
347 MonoImageWriter *w;
348 MonoDwarfWriter *dwarf;
349 FILE *fp;
350 char *tmpbasename;
351 char *tmpfname;
352 char *temp_dir_to_delete;
353 char *llvm_sfile;
354 char *llvm_ofile;
355 GSList *cie_program;
356 GHashTable *unwind_info_offsets;
357 GPtrArray *unwind_ops;
358 guint32 unwind_info_offset;
359 char *global_prefix;
360 char *got_symbol;
361 char *llvm_got_symbol;
362 char *plt_symbol;
363 char *llvm_eh_frame_symbol;
364 GHashTable *method_label_hash;
365 const char *temp_prefix;
366 const char *user_symbol_prefix;
367 const char *llvm_label_prefix;
368 const char *inst_directive;
369 int align_pad_value;
370 guint32 label_generator;
371 gboolean llvm;
372 gboolean has_jitted_code;
373 gboolean is_full_aot;
374 gboolean dedup_collect_only;
375 MonoAotFileFlags flags;
376 MonoDynamicStream blob;
377 gboolean blob_closed;
378 GHashTable *typespec_classes;
379 GString *llc_args;
380 GString *as_args;
381 char *assembly_name_sym;
382 GHashTable *plt_entry_debug_sym_cache;
383 gboolean thumb_mixed, need_no_dead_strip, need_pt_gnu_stack;
384 GHashTable *ginst_hash;
385 GHashTable *dwarf_ln_filenames;
386 gboolean global_symbols;
387 int objc_selector_index, objc_selector_index_2;
388 GPtrArray *objc_selectors;
389 GHashTable *objc_selector_to_index;
390 GList *profile_data;
391 GHashTable *profile_methods;
392 #ifdef EMIT_WIN32_UNWIND_INFO
393 GList *unwind_info_section_cache;
394 #endif
395 FILE *logfile;
396 FILE *instances_logfile;
397 FILE *data_outfile;
398 int datafile_offset;
399 int gc_name_offset;
400 // In this mode, we are emitting dedupable methods that we encounter
401 gboolean dedup_emit_mode;
402 } MonoAotCompile;
404 typedef struct {
405 int plt_offset;
406 char *symbol, *llvm_symbol, *debug_sym;
407 MonoJumpInfo *ji;
408 gboolean jit_used, llvm_used;
409 } MonoPltEntry;
411 #define mono_acfg_lock(acfg) mono_os_mutex_lock (&((acfg)->mutex))
412 #define mono_acfg_unlock(acfg) mono_os_mutex_unlock (&((acfg)->mutex))
414 /* This points to the current acfg in LLVM mode */
415 static MonoAotCompile *llvm_acfg;
416 static MonoAotCompile *current_acfg;
418 /* Cache of decoded method external icall symbol names. */
419 /* Owned by acfg, but kept in this static as well since it is */
420 /* accessed by code paths not having access to acfg. */
421 static GHashTable *method_to_external_icall_symbol_name;
423 // This, instead of an array of pointers, to optimize away a pointer and a relocation per string.
424 #define MSGSTRFIELD(line) MSGSTRFIELD1(line)
425 #define MSGSTRFIELD1(line) str##line
426 static const struct msgstr_t {
427 #define PATCH_INFO(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)];
428 #include "patch-info.h"
429 #undef PATCH_INFO
430 } opstr = {
431 #define PATCH_INFO(a,b) b,
432 #include "patch-info.h"
433 #undef PATCH_INFO
435 static const gint16 opidx [] = {
436 #define PATCH_INFO(a,b) offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
437 #include "patch-info.h"
438 #undef PATCH_INFO
441 static G_GNUC_UNUSED const char*
442 get_patch_name (int info)
444 return (const char*)&opstr + opidx [info];
447 static int
448 emit_aot_image (MonoAotCompile *acfg);
450 static void
451 mono_flush_method_cache (MonoAotCompile *acfg);
453 static void
454 mono_read_method_cache (MonoAotCompile *acfg);
456 static guint32
457 get_unwind_info_offset (MonoAotCompile *acfg, guint8 *encoded, guint32 encoded_len);
459 static char*
460 get_plt_entry_debug_sym (MonoAotCompile *acfg, MonoJumpInfo *ji, GHashTable *cache);
462 static void
463 add_gsharedvt_wrappers (MonoAotCompile *acfg, MonoMethodSignature *sig, gboolean gsharedvt_in, gboolean gsharedvt_out, gboolean interp_in);
465 static void
466 add_profile_instances (MonoAotCompile *acfg, ProfileData *data);
468 static gboolean
469 ignore_cfg (MonoCompile *cfg)
471 return !cfg || cfg->skip;
474 static void
475 aot_printf (MonoAotCompile *acfg, const gchar *format, ...)
477 FILE *output;
478 va_list args;
480 if (acfg->logfile)
481 output = acfg->logfile;
482 else
483 output = stdout;
485 va_start (args, format);
486 vfprintf (output, format, args);
487 va_end (args);
490 static void
491 aot_printerrf (MonoAotCompile *acfg, const gchar *format, ...)
493 FILE *output;
494 va_list args;
496 if (acfg->logfile)
497 output = acfg->logfile;
498 else
499 output = stderr;
501 va_start (args, format);
502 vfprintf (output, format, args);
503 va_end (args);
506 static void
507 report_loader_error (MonoAotCompile *acfg, MonoError *error, gboolean fatal, const char *format, ...)
509 FILE *output;
510 va_list args;
512 if (is_ok (error))
513 return;
515 if (acfg->logfile)
516 output = acfg->logfile;
517 else
518 output = stderr;
520 va_start (args, format);
521 vfprintf (output, format, args);
522 va_end (args);
523 mono_error_cleanup (error);
525 if (acfg->is_full_aot && fatal) {
526 fprintf (output, "FullAOT cannot continue if there are loader errors.\n");
527 exit (1);
531 /* Wrappers around the image writer functions */
533 #define MAX_SYMBOL_SIZE 256
535 #if defined(TARGET_WIN32) && defined(TARGET_X86)
536 static const char *
537 mangle_symbol (const char * symbol, char * mangled_symbol, gsize length)
539 gsize needed_size = length;
541 g_assert (NULL != symbol);
542 g_assert (NULL != mangled_symbol);
543 g_assert (0 != length);
545 if (symbol && '_' != symbol [0]) {
546 needed_size = g_snprintf (mangled_symbol, length, "_%s", symbol);
547 } else {
548 needed_size = g_snprintf (mangled_symbol, length, "%s", symbol);
551 g_assert (0 <= needed_size && needed_size < length);
552 return mangled_symbol;
555 static char *
556 mangle_symbol_alloc (const char * symbol)
558 g_assert (NULL != symbol);
560 if (symbol && '_' != symbol [0]) {
561 return g_strdup_printf ("_%s", symbol);
563 else {
564 return g_strdup_printf ("%s", symbol);
567 #endif
569 static void
570 emit_section_change (MonoAotCompile *acfg, const char *section_name, int subsection_index)
572 mono_img_writer_emit_section_change (acfg->w, section_name, subsection_index);
575 #if defined(TARGET_WIN32) && defined(TARGET_X86)
577 static void
578 emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func)
580 const char * mangled_symbol_name = name;
581 char * mangled_symbol_name_alloc = NULL;
583 if (TRUE == func) {
584 mangled_symbol_name_alloc = mangle_symbol_alloc (name);
585 mangled_symbol_name = mangled_symbol_name_alloc;
588 if (name != mangled_symbol_name && 0 != g_strcasecmp (name, mangled_symbol_name)) {
589 mono_img_writer_emit_label (acfg->w, mangled_symbol_name);
591 mono_img_writer_emit_local_symbol (acfg->w, mangled_symbol_name, end_label, func);
593 if (NULL != mangled_symbol_name_alloc) {
594 g_free (mangled_symbol_name_alloc);
598 #else
600 static void
601 emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func)
603 mono_img_writer_emit_local_symbol (acfg->w, name, end_label, func);
606 #endif
608 static void
609 emit_label (MonoAotCompile *acfg, const char *name)
611 mono_img_writer_emit_label (acfg->w, name);
614 static void
615 emit_bytes (MonoAotCompile *acfg, const guint8* buf, int size)
617 mono_img_writer_emit_bytes (acfg->w, buf, size);
620 static void
621 emit_string (MonoAotCompile *acfg, const char *value)
623 mono_img_writer_emit_string (acfg->w, value);
626 static void
627 emit_line (MonoAotCompile *acfg)
629 mono_img_writer_emit_line (acfg->w);
632 static void
633 emit_alignment (MonoAotCompile *acfg, int size)
635 mono_img_writer_emit_alignment (acfg->w, size);
638 static void
639 emit_alignment_code (MonoAotCompile *acfg, int size)
641 if (acfg->align_pad_value)
642 mono_img_writer_emit_alignment_fill (acfg->w, size, acfg->align_pad_value);
643 else
644 mono_img_writer_emit_alignment (acfg->w, size);
647 static void
648 emit_padding (MonoAotCompile *acfg, int size)
650 int i;
651 guint8 buf [16];
653 if (acfg->align_pad_value) {
654 for (i = 0; i < 16; ++i)
655 buf [i] = acfg->align_pad_value;
656 } else {
657 memset (buf, 0, sizeof (buf));
660 for (i = 0; i < size; i += 16) {
661 if (size - i < 16)
662 emit_bytes (acfg, buf, size - i);
663 else
664 emit_bytes (acfg, buf, 16);
668 static void
669 emit_pointer (MonoAotCompile *acfg, const char *target)
671 mono_img_writer_emit_pointer (acfg->w, target);
674 static void
675 emit_pointer_2 (MonoAotCompile *acfg, const char *prefix, const char *target)
677 if (prefix [0] != '\0') {
678 char *s = g_strdup_printf ("%s%s", prefix, target);
679 mono_img_writer_emit_pointer (acfg->w, s);
680 g_free (s);
681 } else {
682 mono_img_writer_emit_pointer (acfg->w, target);
686 static void
687 emit_int16 (MonoAotCompile *acfg, int value)
689 mono_img_writer_emit_int16 (acfg->w, value);
692 static void
693 emit_int32 (MonoAotCompile *acfg, int value)
695 mono_img_writer_emit_int32 (acfg->w, value);
698 static void
699 emit_symbol_diff (MonoAotCompile *acfg, const char *end, const char* start, int offset)
701 mono_img_writer_emit_symbol_diff (acfg->w, end, start, offset);
704 static void
705 emit_zero_bytes (MonoAotCompile *acfg, int num)
707 mono_img_writer_emit_zero_bytes (acfg->w, num);
710 static void
711 emit_byte (MonoAotCompile *acfg, guint8 val)
713 mono_img_writer_emit_byte (acfg->w, val);
716 #if defined(TARGET_WIN32) && defined(TARGET_X86)
718 static G_GNUC_UNUSED void
719 emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func)
721 const char * mangled_symbol_name = name;
722 char * mangled_symbol_name_alloc = NULL;
724 mangled_symbol_name_alloc = mangle_symbol_alloc (name);
725 mangled_symbol_name = mangled_symbol_name_alloc;
727 if (0 != g_strcasecmp (name, mangled_symbol_name)) {
728 mono_img_writer_emit_label (acfg->w, mangled_symbol_name);
730 mono_img_writer_emit_global (acfg->w, mangled_symbol_name, func);
732 if (NULL != mangled_symbol_name_alloc) {
733 g_free (mangled_symbol_name_alloc);
737 #else
739 static G_GNUC_UNUSED void
740 emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func)
742 mono_img_writer_emit_global (acfg->w, name, func);
745 #endif
747 #ifdef TARGET_WIN32_MSVC
748 static gboolean
749 link_shared_library (MonoAotCompile *acfg)
751 return !acfg->aot_opts.static_link && !acfg->aot_opts.asm_only;
753 #endif
755 static gboolean
756 add_to_global_symbol_table (MonoAotCompile *acfg)
758 #ifdef TARGET_WIN32_MSVC
759 return acfg->aot_opts.no_dlsym || link_shared_library (acfg);
760 #else
761 return acfg->aot_opts.no_dlsym;
762 #endif
765 static void
766 emit_global (MonoAotCompile *acfg, const char *name, gboolean func)
768 if (add_to_global_symbol_table (acfg))
769 g_ptr_array_add (acfg->globals, g_strdup (name));
771 if (acfg->aot_opts.no_dlsym) {
772 mono_img_writer_emit_local_symbol (acfg->w, name, NULL, func);
773 } else {
774 emit_global_inner (acfg, name, func);
778 static void
779 emit_symbol_size (MonoAotCompile *acfg, const char *name, const char *end_label)
781 mono_img_writer_emit_symbol_size (acfg->w, name, end_label);
784 /* Emit a symbol which is referenced by the MonoAotFileInfo structure */
785 static void
786 emit_info_symbol (MonoAotCompile *acfg, const char *name, gboolean func)
788 char symbol [MAX_SYMBOL_SIZE];
790 if (acfg->llvm) {
791 emit_label (acfg, name);
792 /* LLVM generated code references this */
793 sprintf (symbol, "%s%s%s", acfg->user_symbol_prefix, acfg->global_prefix, name);
794 emit_label (acfg, symbol);
796 #ifndef TARGET_WIN32_MSVC
797 emit_global_inner (acfg, symbol, FALSE);
798 #else
799 emit_global_inner (acfg, symbol, func);
800 #endif
801 } else {
802 emit_label (acfg, name);
806 static void
807 emit_string_symbol (MonoAotCompile *acfg, const char *name, const char *value)
809 if (acfg->llvm) {
810 mono_llvm_emit_aot_data (name, (guint8*)value, strlen (value) + 1);
811 return;
814 mono_img_writer_emit_section_change (acfg->w, RODATA_SECT, 1);
815 #ifdef TARGET_MACH
816 /* On apple, all symbols need to be aligned to avoid warnings from ld */
817 emit_alignment (acfg, 4);
818 #endif
819 mono_img_writer_emit_label (acfg->w, name);
820 mono_img_writer_emit_string (acfg->w, value);
823 static G_GNUC_UNUSED void
824 emit_uleb128 (MonoAotCompile *acfg, guint32 value)
826 do {
827 guint8 b = value & 0x7f;
828 value >>= 7;
829 if (value != 0) /* more bytes to come */
830 b |= 0x80;
831 emit_byte (acfg, b);
832 } while (value);
835 static G_GNUC_UNUSED void
836 emit_sleb128 (MonoAotCompile *acfg, gint64 value)
838 gboolean more = 1;
839 gboolean negative = (value < 0);
840 guint32 size = 64;
841 guint8 byte;
843 while (more) {
844 byte = value & 0x7f;
845 value >>= 7;
846 /* the following is unnecessary if the
847 * implementation of >>= uses an arithmetic rather
848 * than logical shift for a signed left operand
850 if (negative)
851 /* sign extend */
852 value |= - ((gint64)1 <<(size - 7));
853 /* sign bit of byte is second high order bit (0x40) */
854 if ((value == 0 && !(byte & 0x40)) ||
855 (value == -1 && (byte & 0x40)))
856 more = 0;
857 else
858 byte |= 0x80;
859 emit_byte (acfg, byte);
863 static G_GNUC_UNUSED void
864 encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
866 guint8 *p = buf;
868 do {
869 guint8 b = value & 0x7f;
870 value >>= 7;
871 if (value != 0) /* more bytes to come */
872 b |= 0x80;
873 *p ++ = b;
874 } while (value);
876 *endbuf = p;
879 static G_GNUC_UNUSED void
880 encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf)
882 gboolean more = 1;
883 gboolean negative = (value < 0);
884 guint32 size = 32;
885 guint8 byte;
886 guint8 *p = buf;
888 while (more) {
889 byte = value & 0x7f;
890 value >>= 7;
891 /* the following is unnecessary if the
892 * implementation of >>= uses an arithmetic rather
893 * than logical shift for a signed left operand
895 if (negative)
896 /* sign extend */
897 value |= - (1 <<(size - 7));
898 /* sign bit of byte is second high order bit (0x40) */
899 if ((value == 0 && !(byte & 0x40)) ||
900 (value == -1 && (byte & 0x40)))
901 more = 0;
902 else
903 byte |= 0x80;
904 *p ++= byte;
907 *endbuf = p;
910 static void
911 encode_int (gint32 val, guint8 *buf, guint8 **endbuf)
913 // FIXME: Big-endian
914 buf [0] = (val >> 0) & 0xff;
915 buf [1] = (val >> 8) & 0xff;
916 buf [2] = (val >> 16) & 0xff;
917 buf [3] = (val >> 24) & 0xff;
919 *endbuf = buf + 4;
922 static void
923 encode_int16 (guint16 val, guint8 *buf, guint8 **endbuf)
925 buf [0] = (val >> 0) & 0xff;
926 buf [1] = (val >> 8) & 0xff;
928 *endbuf = buf + 2;
931 static void
932 encode_string (const char *s, guint8 *buf, guint8 **endbuf)
934 int len = strlen (s);
936 memcpy (buf, s, len + 1);
937 *endbuf = buf + len + 1;
940 static void
941 emit_unset_mode (MonoAotCompile *acfg)
943 mono_img_writer_emit_unset_mode (acfg->w);
946 static G_GNUC_UNUSED void
947 emit_set_thumb_mode (MonoAotCompile *acfg)
949 emit_unset_mode (acfg);
950 fprintf (acfg->fp, ".code 16\n");
953 static G_GNUC_UNUSED void
954 emit_set_arm_mode (MonoAotCompile *acfg)
956 emit_unset_mode (acfg);
957 fprintf (acfg->fp, ".code 32\n");
960 static void
961 emit_code_bytes (MonoAotCompile *acfg, const guint8* buf, int size)
963 #ifdef TARGET_ARM64
964 int i;
966 g_assert (size % 4 == 0);
967 emit_unset_mode (acfg);
968 for (i = 0; i < size; i += 4)
969 fprintf (acfg->fp, "%s 0x%x\n", acfg->inst_directive, *(guint32*)(buf + i));
970 #else
971 emit_bytes (acfg, buf, size);
972 #endif
975 /* ARCHITECTURE SPECIFIC CODE */
977 #if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) || defined(TARGET_POWERPC) || defined(TARGET_ARM64) || defined (TARGET_RISCV)
978 #define EMIT_DWARF_INFO 1
979 #endif
981 #ifdef TARGET_WIN32_MSVC
982 #undef EMIT_DWARF_INFO
983 #define EMIT_WIN32_CODEVIEW_INFO
984 #endif
986 #ifdef EMIT_WIN32_UNWIND_INFO
987 static UnwindInfoSectionCacheItem *
988 get_cached_unwind_info_section_item_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops);
990 static void
991 free_unwind_info_section_cache_win32 (MonoAotCompile *acfg);
993 static void
994 emit_unwind_info_data_win32 (MonoAotCompile *acfg, PUNWIND_INFO unwind_info);
996 static void
997 emit_unwind_info_sections_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops);
998 #endif
1000 static void
1001 arch_free_unwind_info_section_cache (MonoAotCompile *acfg)
1003 #ifdef EMIT_WIN32_UNWIND_INFO
1004 free_unwind_info_section_cache_win32 (acfg);
1005 #endif
1008 static void
1009 arch_emit_unwind_info_sections (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops)
1011 #ifdef EMIT_WIN32_UNWIND_INFO
1012 gboolean own_unwind_ops = FALSE;
1013 if (!unwind_ops) {
1014 unwind_ops = mono_unwind_get_cie_program ();
1015 own_unwind_ops = TRUE;
1018 emit_unwind_info_sections_win32 (acfg, function_start, function_end, unwind_ops);
1020 if (own_unwind_ops)
1021 mono_free_unwind_info (unwind_ops);
1022 #endif
1025 #if defined(TARGET_ARM)
1026 #define AOT_FUNC_ALIGNMENT 4
1027 #else
1028 #define AOT_FUNC_ALIGNMENT 16
1029 #endif
1031 #if defined(TARGET_POWERPC64) && !defined(MONO_ARCH_ILP32)
1032 #define PPC_LD_OP "ld"
1033 #define PPC_LDX_OP "ldx"
1034 #else
1035 #define PPC_LD_OP "lwz"
1036 #define PPC_LDX_OP "lwzx"
1037 #endif
1039 #ifdef TARGET_X86_64_WIN32_MSVC
1040 #define AOT_TARGET_STR "AMD64 (WIN32) (MSVC codegen)"
1041 #elif TARGET_AMD64
1042 #define AOT_TARGET_STR "AMD64"
1043 #endif
1045 #ifdef TARGET_ARM
1046 #ifdef TARGET_MACH
1047 #define AOT_TARGET_STR "ARM (MACH)"
1048 #else
1049 #define AOT_TARGET_STR "ARM (!MACH)"
1050 #endif
1051 #endif
1053 #ifdef TARGET_ARM64
1054 #ifdef TARGET_MACH
1055 #define AOT_TARGET_STR "ARM64 (MACH)"
1056 #else
1057 #define AOT_TARGET_STR "ARM64 (!MACH)"
1058 #endif
1059 #endif
1061 #ifdef TARGET_POWERPC64
1062 #ifdef MONO_ARCH_ILP32
1063 #define AOT_TARGET_STR "POWERPC64 (mono ilp32)"
1064 #else
1065 #define AOT_TARGET_STR "POWERPC64 (!mono ilp32)"
1066 #endif
1067 #else
1068 #ifdef TARGET_POWERPC
1069 #ifdef MONO_ARCH_ILP32
1070 #define AOT_TARGET_STR "POWERPC (mono ilp32)"
1071 #else
1072 #define AOT_TARGET_STR "POWERPC (!mono ilp32)"
1073 #endif
1074 #endif
1075 #endif
1077 #ifdef TARGET_RISCV32
1078 #define AOT_TARGET_STR "RISCV32"
1079 #endif
1081 #ifdef TARGET_RISCV64
1082 #define AOT_TARGET_STR "RISCV64"
1083 #endif
1085 #ifdef TARGET_X86
1086 #ifdef TARGET_WIN32
1087 #define AOT_TARGET_STR "X86 (WIN32)"
1088 #else
1089 #define AOT_TARGET_STR "X86"
1090 #endif
1091 #endif
1093 #ifndef AOT_TARGET_STR
1094 #define AOT_TARGET_STR ""
1095 #endif
1097 static void
1098 arch_init (MonoAotCompile *acfg)
1100 acfg->llc_args = g_string_new ("");
1101 acfg->as_args = g_string_new ("");
1102 acfg->llvm_owriter_supported = TRUE;
1105 * The prefix LLVM likes to put in front of symbol names on darwin.
1106 * The mach-os specs require this for globals, but LLVM puts them in front of all
1107 * symbols. We need to handle this, since we need to refer to LLVM generated
1108 * symbols.
1110 acfg->llvm_label_prefix = "";
1111 acfg->user_symbol_prefix = "";
1113 #if TARGET_X86 || TARGET_AMD64
1114 const gboolean has_custom_args = !!acfg->aot_opts.llvm_llc || acfg->aot_opts.use_current_cpu;
1115 #endif
1117 #if defined(TARGET_X86)
1118 g_string_append_printf (acfg->llc_args, " -march=x86 %s", has_custom_args ? "" : "-mcpu=generic");
1119 #endif
1121 #if defined(TARGET_AMD64)
1122 g_string_append_printf (acfg->llc_args, " -march=x86-64 %s", has_custom_args ? "" : "-mcpu=generic");
1123 /* NOP */
1124 acfg->align_pad_value = 0x90;
1125 #ifdef MONO_ARCH_CODE_EXEC_ONLY
1126 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY);
1127 #endif
1128 #endif
1129 g_string_append (acfg->llc_args, " -enable-implicit-null-checks -disable-fault-maps");
1131 if (mono_use_fast_math) {
1132 // same parameters are passed to opt and LLVM JIT
1133 g_string_append (acfg->llc_args, " -fp-contract=fast -enable-no-infs-fp-math -enable-no-nans-fp-math -enable-no-signed-zeros-fp-math -enable-no-trapping-fp-math -enable-unsafe-fp-math");
1136 #ifdef TARGET_ARM
1137 if (acfg->aot_opts.mtriple && strstr (acfg->aot_opts.mtriple, "darwin")) {
1138 g_string_append (acfg->llc_args, "-mattr=+v6");
1139 } else {
1140 if (!acfg->aot_opts.mtriple) {
1141 g_string_append (acfg->llc_args, " -march=arm");
1142 } else {
1143 /* arch will be defined via mtriple, e.g. armv7s-ios or thumb. */
1145 if (strstr (acfg->aot_opts.mtriple, "ios")) {
1146 g_string_append (acfg->llc_args, " -mattr=+v7");
1147 g_string_append (acfg->llc_args, " -exception-model=dwarf -disable-fp-elim");
1151 #if defined(ARM_FPU_VFP_HARD)
1152 g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16 -float-abi=hard");
1153 g_string_append (acfg->as_args, " -mfpu=vfp3");
1154 #elif defined(ARM_FPU_VFP)
1155 g_string_append (acfg->llc_args, " -mattr=+vfp2,-neon,+d16");
1156 g_string_append (acfg->as_args, " -mfpu=vfp3");
1157 #else
1158 g_string_append (acfg->llc_args, " -mattr=+soft-float");
1159 #endif
1161 if (acfg->aot_opts.mtriple && strstr (acfg->aot_opts.mtriple, "thumb"))
1162 acfg->thumb_mixed = TRUE;
1164 if (acfg->aot_opts.mtriple)
1165 mono_arch_set_target (acfg->aot_opts.mtriple);
1166 #endif
1168 #ifdef TARGET_ARM64
1169 g_string_append (acfg->llc_args, " -march=aarch64");
1170 acfg->inst_directive = ".inst";
1171 if (acfg->aot_opts.mtriple)
1172 mono_arch_set_target (acfg->aot_opts.mtriple);
1173 #endif
1175 #ifdef TARGET_MACH
1176 acfg->user_symbol_prefix = "_";
1177 acfg->llvm_label_prefix = "_";
1178 acfg->inst_directive = ".word";
1179 acfg->need_no_dead_strip = TRUE;
1180 acfg->aot_opts.gnu_asm = TRUE;
1181 #endif
1183 #if defined(__linux__) && !defined(TARGET_ARM)
1184 acfg->need_pt_gnu_stack = TRUE;
1185 #endif
1187 #ifdef TARGET_RISCV
1188 if (acfg->aot_opts.mtriple)
1189 mono_arch_set_target (acfg->aot_opts.mtriple);
1191 #ifdef TARGET_RISCV64
1193 g_string_append (acfg->as_args, " -march=rv64i ");
1194 #ifdef RISCV_FPABI_DOUBLE
1195 g_string_append (acfg->as_args, " -mabi=lp64d");
1196 #else
1197 g_string_append (acfg->as_args, " -mabi=lp64");
1198 #endif
1200 #else
1202 g_string_append (acfg->as_args, " -march=rv32i ");
1203 #ifdef RISCV_FPABI_DOUBLE
1204 g_string_append (acfg->as_args, " -mabi=ilp32d ");
1205 #else
1206 g_string_append (acfg->as_args, " -mabi=ilp32 ");
1207 #endif
1209 #endif
1211 #endif
1213 #ifdef MONOTOUCH
1214 acfg->global_symbols = TRUE;
1215 #endif
1217 #ifdef TARGET_ANDROID
1218 acfg->llvm_owriter_supported = FALSE;
1219 #endif
1222 #ifdef TARGET_ARM64
1225 /* Load the contents of GOT_SLOT into dreg, clobbering ip0 */
1226 /* Must emit 12 bytes of instructions */
1227 static void
1228 arm64_emit_load_got_slot (MonoAotCompile *acfg, int dreg, int got_slot)
1230 int offset;
1232 g_assert (acfg->fp);
1233 emit_unset_mode (acfg);
1234 /* r16==ip0 */
1235 offset = (int)(got_slot * TARGET_SIZEOF_VOID_P);
1236 #ifdef TARGET_MACH
1237 /* clang's integrated assembler */
1238 fprintf (acfg->fp, "adrp x16, %s@PAGE+%d\n", acfg->got_symbol, offset & 0xfffff000);
1239 #ifdef MONO_ARCH_ILP32
1240 fprintf (acfg->fp, "add x16, x16, %s@PAGEOFF+%d\n", acfg->got_symbol, offset & 0xfff);
1241 fprintf (acfg->fp, "ldr w%d, [x16, #%d]\n", dreg, 0);
1242 #else
1243 fprintf (acfg->fp, "add x16, x16, %s@PAGEOFF\n", acfg->got_symbol);
1244 fprintf (acfg->fp, "ldr x%d, [x16, #%d]\n", dreg, offset & 0xfff);
1245 #endif
1246 #else
1247 /* Linux GAS */
1248 fprintf (acfg->fp, "adrp x16, %s+%d\n", acfg->got_symbol, offset & 0xfffff000);
1249 fprintf (acfg->fp, "add x16, x16, :lo12:%s\n", acfg->got_symbol);
1250 fprintf (acfg->fp, "ldr x%d, [x16, %d]\n", dreg, offset & 0xfff);
1251 #endif
1254 static void
1255 arm64_emit_objc_selector_ref (MonoAotCompile *acfg, guint8 *code, int index, int *code_size)
1257 int reg;
1259 g_assert (acfg->fp);
1260 emit_unset_mode (acfg);
1262 /* ldr rt, target */
1263 reg = arm_get_ldr_lit_reg (code);
1265 fprintf (acfg->fp, "adrp x%d, L_OBJC_SELECTOR_REFERENCES_%d@PAGE\n", reg, index);
1266 fprintf (acfg->fp, "add x%d, x%d, L_OBJC_SELECTOR_REFERENCES_%d@PAGEOFF\n", reg, reg, index);
1267 fprintf (acfg->fp, "ldr x%d, [x%d]\n", reg, reg);
1269 *code_size = 12;
1272 static void
1273 arm64_emit_direct_call (MonoAotCompile *acfg, const char *target, gboolean external, gboolean thumb, MonoJumpInfo *ji, int *call_size)
1275 g_assert (acfg->fp);
1276 emit_unset_mode (acfg);
1277 if (ji && ji->relocation == MONO_R_ARM64_B) {
1278 fprintf (acfg->fp, "b %s\n", target);
1279 } else {
1280 if (ji)
1281 g_assert (ji->relocation == MONO_R_ARM64_BL);
1282 fprintf (acfg->fp, "bl %s\n", target);
1284 *call_size = 4;
1287 static void
1288 arm64_emit_got_access (MonoAotCompile *acfg, guint8 *code, int got_slot, int *code_size)
1290 int reg;
1292 /* ldr rt, target */
1293 reg = arm_get_ldr_lit_reg (code);
1294 arm64_emit_load_got_slot (acfg, reg, got_slot);
1295 *code_size = 12;
1298 static void
1299 arm64_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int offset, int info_offset)
1301 arm64_emit_load_got_slot (acfg, ARMREG_R16, offset / sizeof (target_mgreg_t));
1302 fprintf (acfg->fp, "br x16\n");
1303 /* Used by mono_aot_get_plt_info_offset () */
1304 fprintf (acfg->fp, "%s %d\n", acfg->inst_directive, info_offset);
1307 static void
1308 arm64_emit_tramp_page_common_code (MonoAotCompile *acfg, int pagesize, int arg_reg, int *size)
1310 guint8 buf [256];
1311 guint8 *code;
1312 int imm;
1314 /* The common code */
1315 code = buf;
1316 imm = pagesize;
1317 /* The trampoline address is in IP0 */
1318 arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0);
1319 arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16);
1320 /* Compute the data slot address */
1321 arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
1322 /* Trampoline argument */
1323 arm_ldrp (code, arg_reg, ARMREG_IP0, 0);
1324 /* Address */
1325 arm_ldrp (code, ARMREG_IP0, ARMREG_IP0, TARGET_SIZEOF_VOID_P);
1326 arm_brx (code, ARMREG_IP0);
1328 /* Emit it */
1329 emit_code_bytes (acfg, buf, code - buf);
1331 *size = code - buf;
1334 static void
1335 arm64_emit_tramp_page_specific_code (MonoAotCompile *acfg, int pagesize, int common_tramp_size, int specific_tramp_size)
1337 guint8 buf [256];
1338 guint8 *code;
1339 int i, count;
1341 count = (pagesize - common_tramp_size) / specific_tramp_size;
1342 for (i = 0; i < count; ++i) {
1343 code = buf;
1344 arm_adrx (code, ARMREG_IP0, code);
1345 /* Branch to the generic code */
1346 arm_b (code, code - 4 - (i * specific_tramp_size) - common_tramp_size);
1347 #ifndef MONO_ARCH_ILP32
1348 /* This has to be 2 pointers long */
1349 arm_nop (code);
1350 arm_nop (code);
1351 #endif
1352 g_assert (code - buf == specific_tramp_size);
1353 emit_code_bytes (acfg, buf, code - buf);
1357 static void
1358 arm64_emit_specific_trampoline_pages (MonoAotCompile *acfg)
1360 guint8 buf [128];
1361 guint8 *code;
1362 guint8 *labels [16];
1363 int common_tramp_size;
1364 int specific_tramp_size = 2 * TARGET_SIZEOF_VOID_P;
1365 int imm, pagesize;
1366 char symbol [128];
1368 if (!acfg->aot_opts.use_trampolines_page)
1369 return;
1371 #ifdef TARGET_MACH
1372 /* Have to match the target pagesize */
1373 pagesize = 16384;
1374 #else
1375 pagesize = mono_pagesize ();
1376 #endif
1377 acfg->tramp_page_size = pagesize;
1379 /* The specific trampolines */
1380 sprintf (symbol, "%sspecific_trampolines_page", acfg->user_symbol_prefix);
1381 emit_alignment (acfg, pagesize);
1382 emit_global (acfg, symbol, TRUE);
1383 emit_label (acfg, symbol);
1385 /* The common code */
1386 arm64_emit_tramp_page_common_code (acfg, pagesize, ARMREG_IP1, &common_tramp_size);
1387 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_SPECIFIC] = common_tramp_size;
1389 arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
1391 /* The rgctx trampolines */
1392 /* These are the same as the specific trampolines, but they load the argument into MONO_ARCH_RGCTX_REG */
1393 sprintf (symbol, "%srgctx_trampolines_page", acfg->user_symbol_prefix);
1394 emit_alignment (acfg, pagesize);
1395 emit_global (acfg, symbol, TRUE);
1396 emit_label (acfg, symbol);
1398 /* The common code */
1399 arm64_emit_tramp_page_common_code (acfg, pagesize, MONO_ARCH_RGCTX_REG, &common_tramp_size);
1400 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_STATIC_RGCTX] = common_tramp_size;
1402 arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
1404 /* The gsharedvt arg trampolines */
1405 /* These are the same as the specific trampolines */
1406 sprintf (symbol, "%sgsharedvt_arg_trampolines_page", acfg->user_symbol_prefix);
1407 emit_alignment (acfg, pagesize);
1408 emit_global (acfg, symbol, TRUE);
1409 emit_label (acfg, symbol);
1411 arm64_emit_tramp_page_common_code (acfg, pagesize, ARMREG_IP1, &common_tramp_size);
1412 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_GSHAREDVT_ARG] = common_tramp_size;
1414 arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
1416 /* Unbox arbitrary trampolines */
1417 sprintf (symbol, "%sunbox_arbitrary_trampolines_page", acfg->user_symbol_prefix);
1418 emit_alignment (acfg, pagesize);
1419 emit_global (acfg, symbol, TRUE);
1420 emit_label (acfg, symbol);
1422 code = buf;
1423 imm = pagesize;
1425 /* Unbox this arg */
1426 arm_addx_imm (code, ARMREG_R0, ARMREG_R0, MONO_ABI_SIZEOF (MonoObject));
1428 /* The trampoline address is in IP0 */
1429 arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0);
1430 arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16);
1431 /* Compute the data slot address */
1432 arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
1433 /* Address */
1434 arm_ldrp (code, ARMREG_IP0, ARMREG_IP0, 0);
1435 arm_brx (code, ARMREG_IP0);
1437 /* Emit it */
1438 emit_code_bytes (acfg, buf, code - buf);
1440 common_tramp_size = code - buf;
1441 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_UNBOX_ARBITRARY] = common_tramp_size;
1443 arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
1445 /* The IMT trampolines */
1446 sprintf (symbol, "%simt_trampolines_page", acfg->user_symbol_prefix);
1447 emit_alignment (acfg, pagesize);
1448 emit_global (acfg, symbol, TRUE);
1449 emit_label (acfg, symbol);
1451 code = buf;
1452 imm = pagesize;
1453 /* The trampoline address is in IP0 */
1454 arm_movzx (code, ARMREG_IP1, imm & 0xffff, 0);
1455 arm_movkx (code, ARMREG_IP1, (imm >> 16) & 0xffff, 16);
1456 /* Compute the data slot address */
1457 arm_subx (code, ARMREG_IP0, ARMREG_IP0, ARMREG_IP1);
1458 /* Trampoline argument */
1459 arm_ldrp (code, ARMREG_IP1, ARMREG_IP0, 0);
1461 /* Same as arch_emit_imt_trampoline () */
1462 labels [0] = code;
1463 arm_ldrp (code, ARMREG_IP0, ARMREG_IP1, 0);
1464 arm_cmpp (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG);
1465 labels [1] = code;
1466 arm_bcc (code, ARMCOND_EQ, 0);
1468 /* End-of-loop check */
1469 labels [2] = code;
1470 arm_cbzx (code, ARMREG_IP0, 0);
1472 /* Loop footer */
1473 arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 2 * TARGET_SIZEOF_VOID_P);
1474 arm_b (code, labels [0]);
1476 /* Match */
1477 mono_arm_patch (labels [1], code, MONO_R_ARM64_BCC);
1478 /* Load vtable slot addr */
1479 arm_ldrp (code, ARMREG_IP0, ARMREG_IP1, TARGET_SIZEOF_VOID_P);
1480 /* Load vtable slot */
1481 arm_ldrp (code, ARMREG_IP0, ARMREG_IP0, 0);
1482 arm_brx (code, ARMREG_IP0);
1484 /* No match */
1485 mono_arm_patch (labels [2], code, MONO_R_ARM64_CBZ);
1486 /* Load fail addr */
1487 arm_ldrp (code, ARMREG_IP0, ARMREG_IP1, TARGET_SIZEOF_VOID_P);
1488 arm_brx (code, ARMREG_IP0);
1490 emit_code_bytes (acfg, buf, code - buf);
1492 common_tramp_size = code - buf;
1493 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_IMT] = common_tramp_size;
1495 arm64_emit_tramp_page_specific_code (acfg, pagesize, common_tramp_size, specific_tramp_size);
1498 static void
1499 arm64_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
1501 /* Load argument from second GOT slot */
1502 arm64_emit_load_got_slot (acfg, ARMREG_R17, offset + 1);
1503 /* Load generic trampoline address from first GOT slot */
1504 arm64_emit_load_got_slot (acfg, ARMREG_R16, offset);
1505 fprintf (acfg->fp, "br x16\n");
1506 *tramp_size = 7 * 4;
1509 static void
1510 arm64_emit_unbox_trampoline (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, const char *call_target)
1512 emit_unset_mode (acfg);
1513 fprintf (acfg->fp, "add x0, x0, %d\n", (int)(MONO_ABI_SIZEOF (MonoObject)));
1514 fprintf (acfg->fp, "b %s\n", call_target);
1517 static void
1518 arm64_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
1520 /* Similar to the specific trampolines, but use the rgctx reg instead of ip1 */
1522 /* Load argument from first GOT slot */
1523 arm64_emit_load_got_slot (acfg, MONO_ARCH_RGCTX_REG, offset);
1524 /* Load generic trampoline address from second GOT slot */
1525 arm64_emit_load_got_slot (acfg, ARMREG_R16, offset + 1);
1526 fprintf (acfg->fp, "br x16\n");
1527 *tramp_size = 7 * 4;
1530 static void
1531 arm64_emit_imt_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
1533 guint8 buf [128];
1534 guint8 *code, *labels [16];
1536 /* Load parameter from GOT slot into ip1 */
1537 arm64_emit_load_got_slot (acfg, ARMREG_R17, offset);
1539 code = buf;
1540 labels [0] = code;
1541 arm_ldrp (code, ARMREG_IP0, ARMREG_IP1, 0);
1542 arm_cmpp (code, ARMREG_IP0, MONO_ARCH_RGCTX_REG);
1543 labels [1] = code;
1544 arm_bcc (code, ARMCOND_EQ, 0);
1546 /* End-of-loop check */
1547 labels [2] = code;
1548 arm_cbzx (code, ARMREG_IP0, 0);
1550 /* Loop footer */
1551 arm_addx_imm (code, ARMREG_IP1, ARMREG_IP1, 2 * 8);
1552 arm_b (code, labels [0]);
1554 /* Match */
1555 mono_arm_patch (labels [1], code, MONO_R_ARM64_BCC);
1556 /* Load vtable slot addr */
1557 arm_ldrp (code, ARMREG_IP0, ARMREG_IP1, TARGET_SIZEOF_VOID_P);
1558 /* Load vtable slot */
1559 arm_ldrp (code, ARMREG_IP0, ARMREG_IP0, 0);
1560 arm_brx (code, ARMREG_IP0);
1562 /* No match */
1563 mono_arm_patch (labels [2], code, MONO_R_ARM64_CBZ);
1564 /* Load fail addr */
1565 arm_ldrp (code, ARMREG_IP0, ARMREG_IP1, TARGET_SIZEOF_VOID_P);
1566 arm_brx (code, ARMREG_IP0);
1568 emit_code_bytes (acfg, buf, code - buf);
1570 *tramp_size = code - buf + (3 * 4);
1573 static void
1574 arm64_emit_gsharedvt_arg_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
1576 /* Similar to the specific trampolines, but the address is in the second slot */
1577 /* Load argument from first GOT slot */
1578 arm64_emit_load_got_slot (acfg, ARMREG_R17, offset);
1579 /* Load generic trampoline address from second GOT slot */
1580 arm64_emit_load_got_slot (acfg, ARMREG_R16, offset + 1);
1581 fprintf (acfg->fp, "br x16\n");
1582 *tramp_size = 7 * 4;
1586 #endif
1588 #ifdef MONO_ARCH_AOT_SUPPORTED
1590 * arch_emit_direct_call:
1592 * Emit a direct call to the symbol TARGET. CALL_SIZE is set to the size of the
1593 * calling code.
1595 static void
1596 arch_emit_direct_call (MonoAotCompile *acfg, const char *target, gboolean external, gboolean thumb, MonoJumpInfo *ji, int *call_size)
1598 #if defined(TARGET_X86) || defined(TARGET_AMD64)
1599 /* Need to make sure this is exactly 5 bytes long */
1600 emit_unset_mode (acfg);
1601 fprintf (acfg->fp, "call %s\n", target);
1602 *call_size = 5;
1603 #elif defined(TARGET_ARM)
1604 emit_unset_mode (acfg);
1605 if (thumb)
1606 fprintf (acfg->fp, "blx %s\n", target);
1607 else
1608 fprintf (acfg->fp, "bl %s\n", target);
1609 *call_size = 4;
1610 #elif defined(TARGET_ARM64)
1611 arm64_emit_direct_call (acfg, target, external, thumb, ji, call_size);
1612 #elif defined(TARGET_POWERPC)
1613 emit_unset_mode (acfg);
1614 fprintf (acfg->fp, "bl %s\n", target);
1615 *call_size = 4;
1616 #else
1617 g_assert_not_reached ();
1618 #endif
1621 static void
1622 arch_emit_label_address (MonoAotCompile *acfg, const char *target, gboolean external_call, gboolean thumb, MonoJumpInfo *ji, int *call_size)
1624 #if defined(TARGET_ARM) && defined(TARGET_ANDROID)
1625 /* binutils ld does not support branch islands on arm32 */
1626 if (!thumb) {
1627 emit_unset_mode (acfg);
1628 fprintf (acfg->fp, "ldr pc,=%s\n", target);
1629 fprintf (acfg->fp, ".ltorg\n");
1630 *call_size = 8;
1631 } else
1632 #endif
1633 arch_emit_direct_call (acfg, target, external_call, thumb, ji, call_size);
1635 #endif
1638 * PPC32 design:
1639 * - we use an approach similar to the x86 abi: reserve a register (r30) to hold
1640 * the GOT pointer.
1641 * - The full-aot trampolines need access to the GOT of mscorlib, so we store
1642 * in in the 2. slot of every GOT, and require every method to place the GOT
1643 * address in r30, even when it doesn't access the GOT otherwise. This way,
1644 * the trampolines can compute the mscorlib GOT address by loading 4(r30).
1648 * PPC64 design:
1649 * PPC64 uses function descriptors which greatly complicate all code, since
1650 * these are used very inconsistently in the runtime. Some functions like
1651 * mono_compile_method () return ftn descriptors, while others like the
1652 * trampoline creation functions do not.
1653 * We assume that all GOT slots contain function descriptors, and create
1654 * descriptors in aot-runtime.c when needed.
1655 * The ppc64 abi uses r2 to hold the address of the TOC/GOT, which is loaded
1656 * from function descriptors, we could do the same, but it would require
1657 * rewriting all the ppc/aot code to handle function descriptors properly.
1658 * So instead, we use the same approach as on PPC32.
1659 * This is a horrible mess, but fixing it would probably lead to an even bigger
1660 * one.
1664 * X86 design:
1665 * - similar to the PPC32 design, we reserve EBX to hold the GOT pointer.
1668 #ifdef MONO_ARCH_AOT_SUPPORTED
1670 * arch_emit_got_offset:
1672 * The memory pointed to by CODE should hold native code for computing the GOT
1673 * address (OP_LOAD_GOTADDR). Emit this code while patching it with the offset
1674 * between code and the GOT. CODE_SIZE is set to the number of bytes emitted.
1676 static void
1677 arch_emit_got_offset (MonoAotCompile *acfg, guint8 *code, int *code_size)
1679 #if defined(TARGET_POWERPC64)
1680 emit_unset_mode (acfg);
1682 * The ppc32 code doesn't seem to work on ppc64, the assembler complains about
1683 * unsupported relocations. So we store the got address into the .Lgot_addr
1684 * symbol which is in the text segment, compute its address, and load it.
1686 fprintf (acfg->fp, ".L%d:\n", acfg->label_generator);
1687 fprintf (acfg->fp, "lis 0, (.Lgot_addr + 4 - .L%d)@h\n", acfg->label_generator);
1688 fprintf (acfg->fp, "ori 0, 0, (.Lgot_addr + 4 - .L%d)@l\n", acfg->label_generator);
1689 fprintf (acfg->fp, "add 30, 30, 0\n");
1690 fprintf (acfg->fp, "%s 30, 0(30)\n", PPC_LD_OP);
1691 acfg->label_generator ++;
1692 *code_size = 16;
1693 #elif defined(TARGET_POWERPC)
1694 emit_unset_mode (acfg);
1695 fprintf (acfg->fp, ".L%d:\n", acfg->label_generator);
1696 fprintf (acfg->fp, "lis 0, (%s + 4 - .L%d)@h\n", acfg->got_symbol, acfg->label_generator);
1697 fprintf (acfg->fp, "ori 0, 0, (%s + 4 - .L%d)@l\n", acfg->got_symbol, acfg->label_generator);
1698 acfg->label_generator ++;
1699 *code_size = 8;
1700 #else
1701 guint32 offset = mono_arch_get_patch_offset (code);
1702 emit_bytes (acfg, code, offset);
1703 emit_symbol_diff (acfg, acfg->got_symbol, ".", offset);
1705 *code_size = offset + 4;
1706 #endif
1710 * arch_emit_got_access:
1712 * The memory pointed to by CODE should hold native code for loading a GOT
1713 * slot (OP_AOTCONST/OP_GOT_ENTRY). Emit this code while patching it so it accesses the
1714 * GOT slot GOT_SLOT. CODE_SIZE is set to the number of bytes emitted.
1716 static void
1717 arch_emit_got_access (MonoAotCompile *acfg, const char *got_symbol, guint8 *code, int got_slot, int *code_size)
1719 #ifdef TARGET_AMD64
1720 /* mov reg, got+offset(%rip) */
1721 if (acfg->llvm) {
1722 /* The GOT symbol is in the LLVM module, the clang assembler has problems emitting symbol diffs for it */
1723 int dreg;
1724 int rex_r;
1726 /* Decode reg, see amd64_mov_reg_membase () */
1727 rex_r = code [0] & AMD64_REX_R;
1728 g_assert (code [0] == 0x49 + rex_r);
1729 g_assert (code [1] == 0x8b);
1730 dreg = ((code [2] >> 3) & 0x7) + (rex_r ? 8 : 0);
1732 emit_unset_mode (acfg);
1733 fprintf (acfg->fp, "mov %s+%d(%%rip), %s\n", got_symbol, (unsigned int) ((got_slot * sizeof (target_mgreg_t))), mono_arch_regname (dreg));
1734 *code_size = 7;
1735 } else {
1736 emit_bytes (acfg, code, mono_arch_get_patch_offset (code));
1737 emit_symbol_diff (acfg, got_symbol, ".", (unsigned int) ((got_slot * sizeof (target_mgreg_t)) - 4));
1738 *code_size = mono_arch_get_patch_offset (code) + 4;
1740 #elif defined(TARGET_X86)
1741 emit_bytes (acfg, code, mono_arch_get_patch_offset (code));
1742 emit_int32 (acfg, (unsigned int) ((got_slot * sizeof (target_mgreg_t))));
1743 *code_size = mono_arch_get_patch_offset (code) + 4;
1744 #elif defined(TARGET_ARM)
1745 emit_bytes (acfg, code, mono_arch_get_patch_offset (code));
1746 emit_symbol_diff (acfg, got_symbol, ".", (unsigned int) ((got_slot * sizeof (target_mgreg_t))) - 12);
1747 *code_size = mono_arch_get_patch_offset (code) + 4;
1748 #elif defined(TARGET_ARM64)
1749 emit_bytes (acfg, code, mono_arch_get_patch_offset (code));
1750 arm64_emit_got_access (acfg, code, got_slot, code_size);
1751 #elif defined(TARGET_POWERPC)
1753 guint8 buf [32];
1755 emit_bytes (acfg, code, mono_arch_get_patch_offset (code));
1756 code = buf;
1757 ppc_load32 (code, ppc_r0, got_slot * sizeof (target_mgreg_t));
1758 g_assert (code - buf == 8);
1759 emit_bytes (acfg, buf, code - buf);
1760 *code_size = code - buf;
1762 #else
1763 g_assert_not_reached ();
1764 #endif
1767 #endif
1769 #ifdef MONO_ARCH_AOT_SUPPORTED
1771 * arch_emit_objc_selector_ref:
1773 * Emit the implementation of OP_OBJC_GET_SELECTOR, which itself implements @selector(foo:) in objective-c.
1775 static void
1776 arch_emit_objc_selector_ref (MonoAotCompile *acfg, guint8 *code, int index, int *code_size)
1778 #if defined(TARGET_ARM)
1779 char symbol1 [MAX_SYMBOL_SIZE];
1780 char symbol2 [MAX_SYMBOL_SIZE];
1781 int lindex = acfg->objc_selector_index_2 ++;
1783 /* Emit ldr.imm/b */
1784 emit_bytes (acfg, code, 8);
1786 sprintf (symbol1, "L_OBJC_SELECTOR_%d", lindex);
1787 sprintf (symbol2, "L_OBJC_SELECTOR_REFERENCES_%d", index);
1789 emit_label (acfg, symbol1);
1790 mono_img_writer_emit_unset_mode (acfg->w);
1791 fprintf (acfg->fp, ".long %s-(%s+12)", symbol2, symbol1);
1793 *code_size = 12;
1794 #elif defined(TARGET_ARM64)
1795 arm64_emit_objc_selector_ref (acfg, code, index, code_size);
1796 #else
1797 g_assert_not_reached ();
1798 #endif
1800 #endif
1802 #ifdef MONO_ARCH_CODE_EXEC_ONLY
1803 #if defined(TARGET_AMD64)
1804 /* Keep in sync with tramp-amd64.c, aot_arch_get_plt_entry_index. */
1805 #define PLT_ENTRY_OFFSET_REG AMD64_RAX
1806 #endif
1807 #endif
1810 * arch_emit_plt_entry:
1812 * Emit code for the PLT entry.
1813 * The plt entry should look like this on architectures where code is read/execute:
1814 * <indirect jump to GOT_SYMBOL + OFFSET>
1815 * <INFO_OFFSET embedded into the instruction stream>
1816 * The plt entry should look like this on architectures where code is execute only:
1817 * mov RAX, PLT entry offset
1818 * <indirect jump to GOT_SYMBOL + OFFSET>
1820 static void
1821 arch_emit_plt_entry (MonoAotCompile *acfg, const char *got_symbol, guint32 plt_index, int offset, int info_offset)
1823 #if defined(TARGET_X86)
1824 /* jmp *<offset>(%ebx) */
1825 emit_byte (acfg, 0xff);
1826 emit_byte (acfg, 0xa3);
1827 emit_int32 (acfg, offset);
1828 /* Used by mono_aot_get_plt_info_offset */
1829 emit_int32 (acfg, info_offset);
1830 #elif defined(TARGET_AMD64)
1831 #ifdef MONO_ARCH_CODE_EXEC_ONLY
1832 guint8 buf [16];
1833 guint8 *code = buf;
1835 /* Emit smallest possible imm size 1, 2 or 4 bytes based on total number of PLT entries. */
1836 if (acfg->plt_offset <= (guint32)0xFF) {
1837 amd64_emit_rex(code, sizeof (guint8), 0, 0, PLT_ENTRY_OFFSET_REG);
1838 *(code)++ = (unsigned char)0xb0 + (PLT_ENTRY_OFFSET_REG & 0x7);
1839 x86_imm_emit8 (code, (guint8)(plt_index));
1840 } else if (acfg->plt_offset <= (guint32)0xFFFF) {
1841 x86_prefix(code, X86_OPERAND_PREFIX);
1842 amd64_emit_rex(code, sizeof (guint16), 0, 0, PLT_ENTRY_OFFSET_REG);
1843 *(code)++ = (unsigned char)0xb8 + (PLT_ENTRY_OFFSET_REG & 0x7);
1844 x86_imm_emit16 (code, (guint16)(plt_index));
1845 } else {
1846 amd64_mov_reg_imm_size (code, PLT_ENTRY_OFFSET_REG, plt_index, sizeof(plt_index));
1848 emit_bytes (acfg, buf, code - buf);
1849 acfg->stats.plt_size += code - buf;
1851 emit_unset_mode (acfg);
1852 fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", got_symbol, offset);
1853 acfg->stats.plt_size += 6;
1854 #else
1855 fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", got_symbol, offset);
1856 /* Used by mono_aot_get_plt_info_offset */
1857 emit_int32 (acfg, info_offset);
1858 acfg->stats.plt_size += 10;
1859 #endif
1860 #elif defined(TARGET_ARM)
1861 guint8 buf [256];
1862 guint8 *code;
1864 code = buf;
1865 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
1866 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
1867 emit_bytes (acfg, buf, code - buf);
1868 emit_symbol_diff (acfg, got_symbol, ".", offset - 4);
1869 /* Used by mono_aot_get_plt_info_offset */
1870 emit_int32 (acfg, info_offset);
1871 #elif defined(TARGET_ARM64)
1872 arm64_emit_plt_entry (acfg, got_symbol, offset, info_offset);
1873 #elif defined(TARGET_POWERPC)
1874 /* The GOT address is guaranteed to be in r30 by OP_LOAD_GOTADDR */
1875 emit_unset_mode (acfg);
1876 fprintf (acfg->fp, "lis 11, %d@h\n", offset);
1877 fprintf (acfg->fp, "ori 11, 11, %d@l\n", offset);
1878 fprintf (acfg->fp, "add 11, 11, 30\n");
1879 fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP);
1880 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
1881 fprintf (acfg->fp, "%s 2, %d(11)\n", PPC_LD_OP, (int)sizeof (target_mgreg_t));
1882 fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP);
1883 #endif
1884 fprintf (acfg->fp, "mtctr 11\n");
1885 fprintf (acfg->fp, "bctr\n");
1886 emit_int32 (acfg, info_offset);
1887 #else
1888 g_assert_not_reached ();
1889 #endif
1893 * arch_emit_llvm_plt_entry:
1895 * Same as arch_emit_plt_entry, but handles calls from LLVM generated code.
1896 * This is only needed on arm to handle thumb interop.
1898 static void
1899 arch_emit_llvm_plt_entry (MonoAotCompile *acfg, const char *got_symbol, int plt_index, int offset, int info_offset)
1901 #if defined(TARGET_ARM)
1902 /* LLVM calls the PLT entries using bl, so these have to be thumb2 */
1903 /* The caller already transitioned to thumb */
1904 /* The code below should be 12 bytes long */
1905 /* clang has trouble encoding these instructions, so emit the binary */
1906 #if 0
1907 fprintf (acfg->fp, "ldr ip, [pc, #8]\n");
1908 /* thumb can't encode ld pc, [pc, ip] */
1909 fprintf (acfg->fp, "add ip, pc, ip\n");
1910 fprintf (acfg->fp, "ldr ip, [ip, #0]\n");
1911 fprintf (acfg->fp, "bx ip\n");
1912 #endif
1913 emit_set_thumb_mode (acfg);
1914 fprintf (acfg->fp, ".4byte 0xc008f8df\n");
1915 fprintf (acfg->fp, ".2byte 0x44fc\n");
1916 fprintf (acfg->fp, ".4byte 0xc000f8dc\n");
1917 fprintf (acfg->fp, ".2byte 0x4760\n");
1918 emit_symbol_diff (acfg, got_symbol, ".", offset + 4);
1919 emit_int32 (acfg, info_offset);
1920 emit_unset_mode (acfg);
1921 emit_set_arm_mode (acfg);
1922 #else
1923 g_assert_not_reached ();
1924 #endif
1927 /* Save unwind_info in the module and emit the offset to the information at symbol */
1928 static void save_unwind_info (MonoAotCompile *acfg, char *symbol, GSList *unwind_ops)
1930 guint32 uw_offset, encoded_len;
1931 guint8 *encoded;
1933 emit_section_change (acfg, RODATA_SECT, 0);
1934 emit_global (acfg, symbol, FALSE);
1935 emit_label (acfg, symbol);
1937 encoded = mono_unwind_ops_encode (unwind_ops, &encoded_len);
1938 uw_offset = get_unwind_info_offset (acfg, encoded, encoded_len);
1939 g_free (encoded);
1940 emit_int32 (acfg, uw_offset);
1944 * arch_emit_specific_trampoline_pages:
1946 * Emits a page full of trampolines: each trampoline uses its own address to
1947 * lookup both the generic trampoline code and the data argument.
1948 * This page can be remapped in process multiple times so we can get an
1949 * unlimited number of trampolines.
1950 * Specifically this implementation uses the following trick: two memory pages
1951 * are allocated, with the first containing the data and the second containing the trampolines.
1952 * To reduce trampoline size, each trampoline jumps at the start of the page where a common
1953 * implementation does all the lifting.
1954 * Note that the ARM single trampoline size is 8 bytes, exactly like the data that needs to be stored
1955 * on the arm 32 bit system.
1957 static void
1958 arch_emit_specific_trampoline_pages (MonoAotCompile *acfg)
1960 #if defined(TARGET_ARM)
1961 guint8 buf [128];
1962 guint8 *code;
1963 guint8 *loop_start, *loop_branch_back, *loop_end_check, *imt_found_check;
1964 int i;
1965 int pagesize = MONO_AOT_TRAMP_PAGE_SIZE;
1966 GSList *unwind_ops = NULL;
1967 #define COMMON_TRAMP_SIZE 16
1968 int count = (pagesize - COMMON_TRAMP_SIZE) / 8;
1969 int imm8, rot_amount;
1970 char symbol [128];
1972 if (!acfg->aot_opts.use_trampolines_page)
1973 return;
1975 acfg->tramp_page_size = pagesize;
1977 sprintf (symbol, "%sspecific_trampolines_page", acfg->user_symbol_prefix);
1978 emit_alignment (acfg, pagesize);
1979 emit_global (acfg, symbol, TRUE);
1980 emit_label (acfg, symbol);
1982 /* emit the generic code first, the trampoline address + 8 is in the lr register */
1983 code = buf;
1984 imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount);
1985 ARM_SUB_REG_IMM (code, ARMREG_LR, ARMREG_LR, imm8, rot_amount);
1986 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, -8);
1987 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_LR, -4);
1988 ARM_NOP (code);
1989 g_assert (code - buf == COMMON_TRAMP_SIZE);
1991 /* Emit it */
1992 emit_bytes (acfg, buf, code - buf);
1994 for (i = 0; i < count; ++i) {
1995 code = buf;
1996 ARM_PUSH (code, 0x5fff);
1997 ARM_BL (code, 0);
1998 arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1));
1999 g_assert (code - buf == 8);
2000 emit_bytes (acfg, buf, code - buf);
2003 /* now the rgctx trampolines: each specific trampolines puts in the ip register
2004 * the instruction pointer address, so the generic trampoline at the start of the page
2005 * subtracts 4096 to get to the data page and loads the values
2006 * We again fit the generic trampiline in 16 bytes.
2008 sprintf (symbol, "%srgctx_trampolines_page", acfg->user_symbol_prefix);
2009 emit_global (acfg, symbol, TRUE);
2010 emit_label (acfg, symbol);
2011 code = buf;
2012 imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount);
2013 ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
2014 ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, ARMREG_IP, -8);
2015 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, -4);
2016 ARM_NOP (code);
2017 g_assert (code - buf == COMMON_TRAMP_SIZE);
2019 /* Emit it */
2020 emit_bytes (acfg, buf, code - buf);
2022 for (i = 0; i < count; ++i) {
2023 code = buf;
2024 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC);
2025 ARM_B (code, 0);
2026 arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1));
2027 g_assert (code - buf == 8);
2028 emit_bytes (acfg, buf, code - buf);
2032 * gsharedvt arg trampolines: see arch_emit_gsharedvt_arg_trampoline ()
2034 sprintf (symbol, "%sgsharedvt_arg_trampolines_page", acfg->user_symbol_prefix);
2035 emit_global (acfg, symbol, TRUE);
2036 emit_label (acfg, symbol);
2037 code = buf;
2038 ARM_PUSH (code, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3));
2039 imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount);
2040 ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
2041 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_IP, -8);
2042 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, -4);
2043 g_assert (code - buf == COMMON_TRAMP_SIZE);
2044 /* Emit it */
2045 emit_bytes (acfg, buf, code - buf);
2047 for (i = 0; i < count; ++i) {
2048 code = buf;
2049 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC);
2050 ARM_B (code, 0);
2051 arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1));
2052 g_assert (code - buf == 8);
2053 emit_bytes (acfg, buf, code - buf);
2056 /* now the unbox arbitrary trampolines: each specific trampolines puts in the ip register
2057 * the instruction pointer address, so the generic trampoline at the start of the page
2058 * subtracts 4096 to get to the data page and loads the target addr.
2059 * We again fit the generic trampoline in 16 bytes.
2061 sprintf (symbol, "%sunbox_arbitrary_trampolines_page", acfg->user_symbol_prefix);
2062 emit_global (acfg, symbol, TRUE);
2063 emit_label (acfg, symbol);
2064 code = buf;
2066 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, MONO_ABI_SIZEOF (MonoObject));
2067 imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount);
2068 ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
2069 ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, -4);
2070 ARM_NOP (code);
2071 g_assert (code - buf == COMMON_TRAMP_SIZE);
2073 /* Emit it */
2074 emit_bytes (acfg, buf, code - buf);
2076 for (i = 0; i < count; ++i) {
2077 code = buf;
2078 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC);
2079 ARM_B (code, 0);
2080 arm_patch (code - 4, code - COMMON_TRAMP_SIZE - 8 * (i + 1));
2081 g_assert (code - buf == 8);
2082 emit_bytes (acfg, buf, code - buf);
2085 /* now the imt trampolines: each specific trampolines puts in the ip register
2086 * the instruction pointer address, so the generic trampoline at the start of the page
2087 * subtracts 4096 to get to the data page and loads the values
2089 #define IMT_TRAMP_SIZE 72
2090 sprintf (symbol, "%simt_trampolines_page", acfg->user_symbol_prefix);
2091 emit_global (acfg, symbol, TRUE);
2092 emit_label (acfg, symbol);
2093 code = buf;
2094 /* Need at least two free registers, plus a slot for storing the pc */
2095 ARM_PUSH (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_R2));
2097 imm8 = mono_arm_is_rotated_imm8 (pagesize, &rot_amount);
2098 ARM_SUB_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
2099 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_IP, -8);
2101 /* The IMT method is in v5, r0 has the imt array address */
2103 loop_start = code;
2104 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, 0);
2105 ARM_CMP_REG_REG (code, ARMREG_R1, ARMREG_V5);
2106 imt_found_check = code;
2107 ARM_B_COND (code, ARMCOND_EQ, 0);
2109 /* End-of-loop check */
2110 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
2111 loop_end_check = code;
2112 ARM_B_COND (code, ARMCOND_EQ, 0);
2114 /* Loop footer */
2115 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (target_mgreg_t) * 2);
2116 loop_branch_back = code;
2117 ARM_B (code, 0);
2118 arm_patch (loop_branch_back, loop_start);
2120 /* Match */
2121 arm_patch (imt_found_check, code);
2122 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4);
2123 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 0);
2124 /* Save it to the third stack slot */
2125 ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8);
2126 /* Restore the registers and branch */
2127 ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC));
2129 /* No match */
2130 arm_patch (loop_end_check, code);
2131 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4);
2132 ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8);
2133 ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC));
2134 ARM_NOP (code);
2136 /* Emit it */
2137 g_assert (code - buf == IMT_TRAMP_SIZE);
2138 emit_bytes (acfg, buf, code - buf);
2140 for (i = 0; i < count; ++i) {
2141 code = buf;
2142 ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_PC);
2143 ARM_B (code, 0);
2144 arm_patch (code - 4, code - IMT_TRAMP_SIZE - 8 * (i + 1));
2145 g_assert (code - buf == 8);
2146 emit_bytes (acfg, buf, code - buf);
2149 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_SPECIFIC] = 16;
2150 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_STATIC_RGCTX] = 16;
2151 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_IMT] = 72;
2152 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_GSHAREDVT_ARG] = 16;
2153 acfg->tramp_page_code_offsets [MONO_AOT_TRAMP_UNBOX_ARBITRARY] = 16;
2155 /* Unwind info for specifc trampolines */
2156 sprintf (symbol, "%sspecific_trampolines_page_gen_p", acfg->user_symbol_prefix);
2157 /* We unwind to the original caller, from the stack, since lr is clobbered */
2158 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 14 * sizeof (target_mgreg_t));
2159 mono_add_unwind_op_offset (unwind_ops, 0, 0, ARMREG_LR, -4);
2160 save_unwind_info (acfg, symbol, unwind_ops);
2161 mono_free_unwind_info (unwind_ops);
2163 sprintf (symbol, "%sspecific_trampolines_page_sp_p", acfg->user_symbol_prefix);
2164 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2165 mono_add_unwind_op_def_cfa_offset (unwind_ops, 4, 0, 14 * sizeof (target_mgreg_t));
2166 save_unwind_info (acfg, symbol, unwind_ops);
2167 mono_free_unwind_info (unwind_ops);
2169 /* Unwind info for rgctx trampolines */
2170 sprintf (symbol, "%srgctx_trampolines_page_gen_p", acfg->user_symbol_prefix);
2171 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2172 save_unwind_info (acfg, symbol, unwind_ops);
2174 sprintf (symbol, "%srgctx_trampolines_page_sp_p", acfg->user_symbol_prefix);
2175 save_unwind_info (acfg, symbol, unwind_ops);
2176 mono_free_unwind_info (unwind_ops);
2178 /* Unwind info for gsharedvt trampolines */
2179 sprintf (symbol, "%sgsharedvt_trampolines_page_gen_p", acfg->user_symbol_prefix);
2180 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2181 mono_add_unwind_op_def_cfa_offset (unwind_ops, 4, 0, 4 * sizeof (target_mgreg_t));
2182 save_unwind_info (acfg, symbol, unwind_ops);
2183 mono_free_unwind_info (unwind_ops);
2185 sprintf (symbol, "%sgsharedvt_trampolines_page_sp_p", acfg->user_symbol_prefix);
2186 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2187 save_unwind_info (acfg, symbol, unwind_ops);
2188 mono_free_unwind_info (unwind_ops);
2190 /* Unwind info for unbox arbitrary trampolines */
2191 sprintf (symbol, "%sunbox_arbitrary_trampolines_page_gen_p", acfg->user_symbol_prefix);
2192 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2193 save_unwind_info (acfg, symbol, unwind_ops);
2195 sprintf (symbol, "%sunbox_arbitrary_trampolines_page_sp_p", acfg->user_symbol_prefix);
2196 save_unwind_info (acfg, symbol, unwind_ops);
2197 mono_free_unwind_info (unwind_ops);
2199 /* Unwind info for imt trampolines */
2200 sprintf (symbol, "%simt_trampolines_page_gen_p", acfg->user_symbol_prefix);
2201 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2202 mono_add_unwind_op_def_cfa_offset (unwind_ops, 4, 0, 3 * sizeof (target_mgreg_t));
2203 save_unwind_info (acfg, symbol, unwind_ops);
2204 mono_free_unwind_info (unwind_ops);
2206 sprintf (symbol, "%simt_trampolines_page_sp_p", acfg->user_symbol_prefix);
2207 mono_add_unwind_op_def_cfa (unwind_ops, 0, 0, ARMREG_SP, 0);
2208 save_unwind_info (acfg, symbol, unwind_ops);
2209 mono_free_unwind_info (unwind_ops);
2210 #elif defined(TARGET_ARM64)
2211 arm64_emit_specific_trampoline_pages (acfg);
2212 #endif
2216 * arch_emit_specific_trampoline:
2218 * Emit code for a specific trampoline. OFFSET is the offset of the first of
2219 * two GOT slots which contain the generic trampoline address and the trampoline
2220 * argument. TRAMP_SIZE is set to the size of the emitted trampoline.
2222 static void
2223 arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
2226 * The trampolines created here are variations of the specific
2227 * trampolines created in mono_arch_create_specific_trampoline (). The
2228 * differences are:
2229 * - the generic trampoline address is taken from a got slot.
2230 * - the offset of the got slot where the trampoline argument is stored
2231 * is embedded in the instruction stream, and the generic trampoline
2232 * can load the argument by loading the offset, adding it to the
2233 * address of the trampoline to get the address of the got slot, and
2234 * loading the argument from there.
2235 * - all the trampolines should be of the same length.
2237 #if defined(TARGET_AMD64)
2238 /* This should be exactly 8 bytes long */
2239 *tramp_size = 8;
2240 /* call *<offset>(%rip) */
2241 if (acfg->llvm) {
2242 emit_byte (acfg, '\x41');
2243 emit_unset_mode (acfg);
2244 fprintf (acfg->fp, "call *%s+%d(%%rip)\n", acfg->got_symbol, (int)(offset * sizeof (target_mgreg_t)));
2245 emit_zero_bytes (acfg, 1);
2246 } else {
2247 emit_byte (acfg, '\x41');
2248 emit_byte (acfg, '\xff');
2249 emit_byte (acfg, '\x15');
2250 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) - 4);
2251 emit_zero_bytes (acfg, 1);
2253 #elif defined(TARGET_ARM)
2254 guint8 buf [128];
2255 guint8 *code;
2257 /* This should be exactly 20 bytes long */
2258 *tramp_size = 20;
2259 code = buf;
2260 ARM_PUSH (code, 0x5fff);
2261 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 4);
2262 /* Load the value from the GOT */
2263 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_PC, ARMREG_R1);
2264 /* Branch to it */
2265 ARM_BLX_REG (code, ARMREG_R1);
2267 g_assert (code - buf == 16);
2269 /* Emit it */
2270 emit_bytes (acfg, buf, code - buf);
2272 * Only one offset is needed, since the second one would be equal to the
2273 * first one.
2275 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) - 4 + 4);
2276 //emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (target_mgreg_t)) - 4 + 8);
2277 #elif defined(TARGET_ARM64)
2278 arm64_emit_specific_trampoline (acfg, offset, tramp_size);
2279 #elif defined(TARGET_POWERPC)
2280 guint8 buf [128];
2281 guint8 *code;
2283 *tramp_size = 4;
2284 code = buf;
2287 * PPC has no ip relative addressing, so we need to compute the address
2288 * of the mscorlib got. That is slow and complex, so instead, we store it
2289 * in the second got slot of every aot image. The caller already computed
2290 * the address of its got and placed it into r30.
2292 emit_unset_mode (acfg);
2293 /* Load mscorlib got address */
2294 fprintf (acfg->fp, "%s 0, %d(30)\n", PPC_LD_OP, (int)sizeof (target_mgreg_t));
2295 /* Load generic trampoline address */
2296 fprintf (acfg->fp, "lis 11, %d@h\n", (int)(offset * sizeof (target_mgreg_t)));
2297 fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)(offset * sizeof (target_mgreg_t)));
2298 fprintf (acfg->fp, "%s 11, 11, 0\n", PPC_LDX_OP);
2299 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
2300 fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP);
2301 #endif
2302 fprintf (acfg->fp, "mtctr 11\n");
2303 /* Load trampoline argument */
2304 /* On ppc, we pass it normally to the generic trampoline */
2305 fprintf (acfg->fp, "lis 11, %d@h\n", (int)((offset + 1) * sizeof (target_mgreg_t)));
2306 fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)((offset + 1) * sizeof (target_mgreg_t)));
2307 fprintf (acfg->fp, "%s 0, 11, 0\n", PPC_LDX_OP);
2308 /* Branch to generic trampoline */
2309 fprintf (acfg->fp, "bctr\n");
2311 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
2312 *tramp_size = 10 * 4;
2313 #else
2314 *tramp_size = 9 * 4;
2315 #endif
2316 #elif defined(TARGET_X86)
2317 guint8 buf [128];
2318 guint8 *code;
2320 /* Similar to the PPC code above */
2322 /* FIXME: Could this clobber the register needed by get_vcall_slot () ? */
2324 code = buf;
2325 /* Load mscorlib got address */
2326 x86_mov_reg_membase (code, X86_ECX, MONO_ARCH_GOT_REG, sizeof (target_mgreg_t), 4);
2327 /* Push trampoline argument */
2328 x86_push_membase (code, X86_ECX, (offset + 1) * sizeof (target_mgreg_t));
2329 /* Load generic trampoline address */
2330 x86_mov_reg_membase (code, X86_ECX, X86_ECX, offset * sizeof (target_mgreg_t), 4);
2331 /* Branch to generic trampoline */
2332 x86_jump_reg (code, X86_ECX);
2334 emit_bytes (acfg, buf, code - buf);
2336 *tramp_size = 17;
2337 g_assert (code - buf == *tramp_size);
2338 #else
2339 g_assert_not_reached ();
2340 #endif
2344 * arch_emit_unbox_trampoline:
2346 * Emit code for the unbox trampoline for METHOD used in the full-aot case.
2347 * CALL_TARGET is the symbol pointing to the native code of METHOD.
2349 * See mono_aot_get_unbox_trampoline.
2351 static void
2352 arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, const char *call_target)
2354 #if defined(TARGET_AMD64)
2355 guint8 buf [32];
2356 guint8 *code;
2357 int this_reg;
2359 this_reg = mono_arch_get_this_arg_reg (NULL);
2360 code = buf;
2361 amd64_alu_reg_imm (code, X86_ADD, this_reg, MONO_ABI_SIZEOF (MonoObject));
2363 emit_bytes (acfg, buf, code - buf);
2364 /* jump <method> */
2365 if (acfg->llvm) {
2366 emit_unset_mode (acfg);
2367 fprintf (acfg->fp, "jmp %s\n", call_target);
2368 } else {
2369 emit_byte (acfg, '\xe9');
2370 emit_symbol_diff (acfg, call_target, ".", -4);
2372 #elif defined(TARGET_X86)
2373 guint8 buf [32];
2374 guint8 *code;
2375 int this_pos = 4;
2377 code = buf;
2379 x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, MONO_ABI_SIZEOF (MonoObject));
2381 emit_bytes (acfg, buf, code - buf);
2383 /* jump <method> */
2384 emit_byte (acfg, '\xe9');
2385 emit_symbol_diff (acfg, call_target, ".", -4);
2386 #elif defined(TARGET_ARM)
2387 guint8 buf [128];
2388 guint8 *code;
2390 if (acfg->thumb_mixed && cfg->compile_llvm) {
2391 fprintf (acfg->fp, "add r0, r0, #%d\n", (int)MONO_ABI_SIZEOF (MonoObject));
2392 fprintf (acfg->fp, "b %s\n", call_target);
2393 fprintf (acfg->fp, ".arm\n");
2394 fprintf (acfg->fp, ".align 2\n");
2395 return;
2398 code = buf;
2400 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, MONO_ABI_SIZEOF (MonoObject));
2402 emit_bytes (acfg, buf, code - buf);
2403 /* jump to method */
2404 if (acfg->thumb_mixed && cfg->compile_llvm)
2405 fprintf (acfg->fp, "\n\tbx %s\n", call_target);
2406 else
2407 fprintf (acfg->fp, "\n\tb %s\n", call_target);
2408 #elif defined(TARGET_ARM64)
2409 arm64_emit_unbox_trampoline (acfg, cfg, method, call_target);
2410 #elif defined(TARGET_POWERPC)
2411 int this_pos = 3;
2413 fprintf (acfg->fp, "\n\taddi %d, %d, %d\n", this_pos, this_pos, (int)MONO_ABI_SIZEOF (MonoObject));
2414 fprintf (acfg->fp, "\n\tb %s\n", call_target);
2415 #else
2416 g_assert_not_reached ();
2417 #endif
2421 * arch_emit_static_rgctx_trampoline:
2423 * Emit code for a static rgctx trampoline. OFFSET is the offset of the first of
2424 * two GOT slots which contain the rgctx argument, and the method to jump to.
2425 * TRAMP_SIZE is set to the size of the emitted trampoline.
2426 * These kinds of trampolines cannot be enumerated statically, since there could
2427 * be one trampoline per method instantiation, so we emit the same code for all
2428 * trampolines, and parameterize them using two GOT slots.
2430 static void
2431 arch_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
2433 #if defined(TARGET_AMD64)
2434 /* This should be exactly 13 bytes long */
2435 *tramp_size = 13;
2437 if (acfg->llvm) {
2438 emit_unset_mode (acfg);
2439 fprintf (acfg->fp, "mov %s+%d(%%rip), %%r10\n", acfg->got_symbol, (int)(offset * sizeof (target_mgreg_t)));
2440 fprintf (acfg->fp, "jmp *%s+%d(%%rip)\n", acfg->got_symbol, (int)((offset + 1) * sizeof (target_mgreg_t)));
2441 } else {
2442 /* mov <OFFSET>(%rip), %r10 */
2443 emit_byte (acfg, '\x4d');
2444 emit_byte (acfg, '\x8b');
2445 emit_byte (acfg, '\x15');
2446 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) - 4);
2448 /* jmp *<offset>(%rip) */
2449 emit_byte (acfg, '\xff');
2450 emit_byte (acfg, '\x25');
2451 emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (target_mgreg_t)) - 4);
2453 #elif defined(TARGET_ARM)
2454 guint8 buf [128];
2455 guint8 *code;
2457 /* This should be exactly 24 bytes long */
2458 *tramp_size = 24;
2459 code = buf;
2460 /* Load rgctx value */
2461 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 8);
2462 ARM_LDR_REG_REG (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, ARMREG_IP);
2463 /* Load branch addr + branch */
2464 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4);
2465 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2467 g_assert (code - buf == 16);
2469 /* Emit it */
2470 emit_bytes (acfg, buf, code - buf);
2471 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) - 4 + 8);
2472 emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (target_mgreg_t)) - 4 + 4);
2473 #elif defined(TARGET_ARM64)
2474 arm64_emit_static_rgctx_trampoline (acfg, offset, tramp_size);
2475 #elif defined(TARGET_POWERPC)
2476 guint8 buf [128];
2477 guint8 *code;
2479 *tramp_size = 4;
2480 code = buf;
2483 * PPC has no ip relative addressing, so we need to compute the address
2484 * of the mscorlib got. That is slow and complex, so instead, we store it
2485 * in the second got slot of every aot image. The caller already computed
2486 * the address of its got and placed it into r30.
2488 emit_unset_mode (acfg);
2489 /* Load mscorlib got address */
2490 fprintf (acfg->fp, "%s 0, %d(30)\n", PPC_LD_OP, (int)sizeof (target_mgreg_t));
2491 /* Load rgctx */
2492 fprintf (acfg->fp, "lis 11, %d@h\n", (int)(offset * sizeof (target_mgreg_t)));
2493 fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)(offset * sizeof (target_mgreg_t)));
2494 fprintf (acfg->fp, "%s %d, 11, 0\n", PPC_LDX_OP, MONO_ARCH_RGCTX_REG);
2495 /* Load target address */
2496 fprintf (acfg->fp, "lis 11, %d@h\n", (int)((offset + 1) * sizeof (target_mgreg_t)));
2497 fprintf (acfg->fp, "ori 11, 11, %d@l\n", (int)((offset + 1) * sizeof (target_mgreg_t)));
2498 fprintf (acfg->fp, "%s 11, 11, 0\n", PPC_LDX_OP);
2499 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
2500 fprintf (acfg->fp, "%s 2, %d(11)\n", PPC_LD_OP, (int)sizeof (target_mgreg_t));
2501 fprintf (acfg->fp, "%s 11, 0(11)\n", PPC_LD_OP);
2502 #endif
2503 fprintf (acfg->fp, "mtctr 11\n");
2504 /* Branch to the target address */
2505 fprintf (acfg->fp, "bctr\n");
2507 #ifdef PPC_USES_FUNCTION_DESCRIPTOR
2508 *tramp_size = 11 * 4;
2509 #else
2510 *tramp_size = 9 * 4;
2511 #endif
2513 #elif defined(TARGET_X86)
2514 guint8 buf [128];
2515 guint8 *code;
2517 /* Similar to the PPC code above */
2519 g_assert (MONO_ARCH_RGCTX_REG != X86_ECX);
2521 code = buf;
2522 /* Load mscorlib got address */
2523 x86_mov_reg_membase (code, X86_ECX, MONO_ARCH_GOT_REG, sizeof (target_mgreg_t), 4);
2524 /* Load arg */
2525 x86_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, X86_ECX, offset * sizeof (target_mgreg_t), 4);
2526 /* Branch to the target address */
2527 x86_jump_membase (code, X86_ECX, (offset + 1) * sizeof (target_mgreg_t));
2529 emit_bytes (acfg, buf, code - buf);
2531 *tramp_size = 15;
2532 g_assert (code - buf == *tramp_size);
2533 #else
2534 g_assert_not_reached ();
2535 #endif
2539 * arch_emit_imt_trampoline:
2541 * Emit an IMT trampoline usable in full-aot mode. The trampoline uses 1 got slot which
2542 * points to an array of pointer pairs. The pairs of the form [key, ptr], where
2543 * key is the IMT key, and ptr holds the address of a memory location holding
2544 * the address to branch to if the IMT arg matches the key. The array is
2545 * terminated by a pair whose key is NULL, and whose ptr is the address of the
2546 * fail_tramp.
2547 * TRAMP_SIZE is set to the size of the emitted trampoline.
2549 static void
2550 arch_emit_imt_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
2552 #if defined(TARGET_AMD64)
2553 guint8 *buf, *code;
2554 guint8 *labels [16];
2555 guint8 mov_buf[3];
2556 guint8 *mov_buf_ptr = mov_buf;
2558 const int kSizeOfMove = 7;
2560 code = buf = (guint8 *)g_malloc (256);
2562 /* FIXME: Optimize this, i.e. use binary search etc. */
2563 /* Maybe move the body into a separate function (slower, but much smaller) */
2565 /* MONO_ARCH_IMT_SCRATCH_REG is a free register */
2567 if (acfg->llvm) {
2568 emit_unset_mode (acfg);
2569 fprintf (acfg->fp, "mov %s+%d(%%rip), %s\n", acfg->got_symbol, (int)(offset * sizeof (target_mgreg_t)), mono_arch_regname (MONO_ARCH_IMT_SCRATCH_REG));
2572 labels [0] = code;
2573 amd64_alu_membase_imm (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, 0);
2574 labels [1] = code;
2575 amd64_branch8 (code, X86_CC_Z, 0, FALSE);
2577 /* Check key */
2578 amd64_alu_membase_reg_size (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, MONO_ARCH_IMT_REG, sizeof (target_mgreg_t));
2579 labels [2] = code;
2580 amd64_branch8 (code, X86_CC_Z, 0, FALSE);
2582 /* Loop footer */
2583 amd64_alu_reg_imm (code, X86_ADD, MONO_ARCH_IMT_SCRATCH_REG, 2 * sizeof (target_mgreg_t));
2584 amd64_jump_code (code, labels [0]);
2586 /* Match */
2587 mono_amd64_patch (labels [2], code);
2588 amd64_mov_reg_membase (code, MONO_ARCH_IMT_SCRATCH_REG, MONO_ARCH_IMT_SCRATCH_REG, sizeof (target_mgreg_t), sizeof (target_mgreg_t));
2589 amd64_jump_membase (code, MONO_ARCH_IMT_SCRATCH_REG, 0);
2591 /* No match */
2592 mono_amd64_patch (labels [1], code);
2593 /* Load fail tramp */
2594 amd64_alu_reg_imm (code, X86_ADD, MONO_ARCH_IMT_SCRATCH_REG, sizeof (target_mgreg_t));
2595 /* Check if there is a fail tramp */
2596 amd64_alu_membase_imm (code, X86_CMP, MONO_ARCH_IMT_SCRATCH_REG, 0, 0);
2597 labels [3] = code;
2598 amd64_branch8 (code, X86_CC_Z, 0, FALSE);
2599 /* Jump to fail tramp */
2600 amd64_jump_membase (code, MONO_ARCH_IMT_SCRATCH_REG, 0);
2602 /* Fail */
2603 mono_amd64_patch (labels [3], code);
2604 x86_breakpoint (code);
2606 if (!acfg->llvm) {
2607 /* mov <OFFSET>(%rip), MONO_ARCH_IMT_SCRATCH_REG */
2608 amd64_emit_rex (mov_buf_ptr, sizeof(gpointer), MONO_ARCH_IMT_SCRATCH_REG, 0, AMD64_RIP);
2609 *(mov_buf_ptr)++ = (unsigned char)0x8b; /* mov opcode */
2610 x86_address_byte (mov_buf_ptr, 0, MONO_ARCH_IMT_SCRATCH_REG & 0x7, 5);
2611 emit_bytes (acfg, mov_buf, mov_buf_ptr - mov_buf);
2612 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) - 4);
2614 emit_bytes (acfg, buf, code - buf);
2616 *tramp_size = code - buf + kSizeOfMove;
2618 g_free (buf);
2620 #elif defined(TARGET_X86)
2621 guint8 *buf, *code;
2622 guint8 *labels [16];
2624 code = buf = g_malloc (256);
2626 /* Allocate a temporary stack slot */
2627 x86_push_reg (code, X86_EAX);
2628 /* Save EAX */
2629 x86_push_reg (code, X86_EAX);
2631 /* Load mscorlib got address */
2632 x86_mov_reg_membase (code, X86_EAX, MONO_ARCH_GOT_REG, sizeof (target_mgreg_t), 4);
2633 /* Load arg */
2634 x86_mov_reg_membase (code, X86_EAX, X86_EAX, offset * sizeof (target_mgreg_t), 4);
2636 labels [0] = code;
2637 x86_alu_membase_imm (code, X86_CMP, X86_EAX, 0, 0);
2638 labels [1] = code;
2639 x86_branch8 (code, X86_CC_Z, FALSE, 0);
2641 /* Check key */
2642 x86_alu_membase_reg (code, X86_CMP, X86_EAX, 0, MONO_ARCH_IMT_REG);
2643 labels [2] = code;
2644 x86_branch8 (code, X86_CC_Z, FALSE, 0);
2646 /* Loop footer */
2647 x86_alu_reg_imm (code, X86_ADD, X86_EAX, 2 * sizeof (target_mgreg_t));
2648 x86_jump_code (code, labels [0]);
2650 /* Match */
2651 mono_x86_patch (labels [2], code);
2652 x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (target_mgreg_t), 4);
2653 x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
2654 /* Save the target address to the temporary stack location */
2655 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
2656 /* Restore EAX */
2657 x86_pop_reg (code, X86_EAX);
2658 /* Jump to the target address */
2659 x86_ret (code);
2661 /* No match */
2662 mono_x86_patch (labels [1], code);
2663 /* Load fail tramp */
2664 x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (target_mgreg_t), 4);
2665 x86_alu_membase_imm (code, X86_CMP, X86_EAX, 0, 0);
2666 labels [3] = code;
2667 x86_branch8 (code, X86_CC_Z, FALSE, 0);
2668 /* Jump to fail tramp */
2669 x86_mov_membase_reg (code, X86_ESP, 4, X86_EAX, 4);
2670 x86_pop_reg (code, X86_EAX);
2671 x86_ret (code);
2673 /* Fail */
2674 mono_x86_patch (labels [3], code);
2675 x86_breakpoint (code);
2677 emit_bytes (acfg, buf, code - buf);
2679 *tramp_size = code - buf;
2681 g_free (buf);
2683 #elif defined(TARGET_ARM)
2684 guint8 buf [128];
2685 guint8 *code, *code2, *labels [16];
2687 code = buf;
2689 /* The IMT method is in v5 */
2691 /* Need at least two free registers, plus a slot for storing the pc */
2692 ARM_PUSH (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_R2));
2693 labels [0] = code;
2694 /* Load the parameter from the GOT */
2695 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
2696 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
2698 labels [1] = code;
2699 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, 0);
2700 ARM_CMP_REG_REG (code, ARMREG_R1, ARMREG_V5);
2701 labels [2] = code;
2702 ARM_B_COND (code, ARMCOND_EQ, 0);
2704 /* End-of-loop check */
2705 ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
2706 labels [3] = code;
2707 ARM_B_COND (code, ARMCOND_EQ, 0);
2709 /* Loop footer */
2710 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, sizeof (target_mgreg_t) * 2);
2711 labels [4] = code;
2712 ARM_B (code, 0);
2713 arm_patch (labels [4], labels [1]);
2715 /* Match */
2716 arm_patch (labels [2], code);
2717 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4);
2718 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 0);
2719 /* Save it to the third stack slot */
2720 ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8);
2721 /* Restore the registers and branch */
2722 ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC));
2724 /* No match */
2725 arm_patch (labels [3], code);
2726 ARM_LDR_IMM (code, ARMREG_R0, ARMREG_R0, 4);
2727 ARM_STR_IMM (code, ARMREG_R0, ARMREG_SP, 8);
2728 ARM_POP (code, (1 << ARMREG_R0)|(1 << ARMREG_R1)|(1 << ARMREG_PC));
2730 /* Fixup offset */
2731 code2 = labels [0];
2732 ARM_LDR_IMM (code2, ARMREG_R0, ARMREG_PC, (code - (labels [0] + 8)));
2734 emit_bytes (acfg, buf, code - buf);
2735 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) + (code - (labels [0] + 8)) - 4);
2737 *tramp_size = code - buf + 4;
2738 #elif defined(TARGET_ARM64)
2739 arm64_emit_imt_trampoline (acfg, offset, tramp_size);
2740 #elif defined(TARGET_POWERPC)
2741 guint8 buf [128];
2742 guint8 *code, *labels [16];
2744 code = buf;
2746 /* Load the mscorlib got address */
2747 ppc_ldptr (code, ppc_r12, sizeof (target_mgreg_t), ppc_r30);
2748 /* Load the parameter from the GOT */
2749 ppc_load (code, ppc_r0, offset * sizeof (target_mgreg_t));
2750 ppc_ldptr_indexed (code, ppc_r12, ppc_r12, ppc_r0);
2752 /* Load and check key */
2753 labels [1] = code;
2754 ppc_ldptr (code, ppc_r0, 0, ppc_r12);
2755 ppc_cmp (code, 0, sizeof (target_mgreg_t) == 8 ? 1 : 0, ppc_r0, MONO_ARCH_IMT_REG);
2756 labels [2] = code;
2757 ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0);
2759 /* End-of-loop check */
2760 ppc_cmpi (code, 0, sizeof (target_mgreg_t) == 8 ? 1 : 0, ppc_r0, 0);
2761 labels [3] = code;
2762 ppc_bc (code, PPC_BR_TRUE, PPC_BR_EQ, 0);
2764 /* Loop footer */
2765 ppc_addi (code, ppc_r12, ppc_r12, 2 * sizeof (target_mgreg_t));
2766 labels [4] = code;
2767 ppc_b (code, 0);
2768 mono_ppc_patch (labels [4], labels [1]);
2770 /* Match */
2771 mono_ppc_patch (labels [2], code);
2772 ppc_ldptr (code, ppc_r12, sizeof (target_mgreg_t), ppc_r12);
2773 /* r12 now contains the value of the vtable slot */
2774 /* this is not a function descriptor on ppc64 */
2775 ppc_ldptr (code, ppc_r12, 0, ppc_r12);
2776 ppc_mtctr (code, ppc_r12);
2777 ppc_bcctr (code, PPC_BR_ALWAYS, 0);
2779 /* Fail */
2780 mono_ppc_patch (labels [3], code);
2781 /* FIXME: */
2782 ppc_break (code);
2784 *tramp_size = code - buf;
2786 emit_bytes (acfg, buf, code - buf);
2787 #else
2788 g_assert_not_reached ();
2789 #endif
2793 #if defined (TARGET_AMD64)
2795 static void
2796 amd64_emit_load_got_slot (MonoAotCompile *acfg, int dreg, int got_slot)
2799 g_assert (acfg->fp);
2800 emit_unset_mode (acfg);
2802 fprintf (acfg->fp, "mov %s+%d(%%rip), %s\n", acfg->got_symbol, (unsigned int) ((got_slot * sizeof (target_mgreg_t))), mono_arch_regname (dreg));
2805 #endif
2809 * arch_emit_gsharedvt_arg_trampoline:
2811 * Emit code for a gsharedvt arg trampoline. OFFSET is the offset of the first of
2812 * two GOT slots which contain the argument, and the code to jump to.
2813 * TRAMP_SIZE is set to the size of the emitted trampoline.
2814 * These kinds of trampolines cannot be enumerated statically, since there could
2815 * be one trampoline per method instantiation, so we emit the same code for all
2816 * trampolines, and parameterize them using two GOT slots.
2818 static void
2819 arch_emit_gsharedvt_arg_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
2821 #if defined(TARGET_X86)
2822 guint8 buf [128];
2823 guint8 *code;
2825 /* Similar to the PPC code above */
2827 g_assert (MONO_ARCH_RGCTX_REG != X86_ECX);
2829 code = buf;
2830 /* Load mscorlib got address */
2831 x86_mov_reg_membase (code, X86_ECX, MONO_ARCH_GOT_REG, sizeof (target_mgreg_t), 4);
2832 /* Load arg */
2833 x86_mov_reg_membase (code, X86_EAX, X86_ECX, offset * sizeof (target_mgreg_t), 4);
2834 /* Branch to the target address */
2835 x86_jump_membase (code, X86_ECX, (offset + 1) * sizeof (target_mgreg_t));
2837 emit_bytes (acfg, buf, code - buf);
2839 *tramp_size = 15;
2840 g_assert (code - buf == *tramp_size);
2841 #elif defined(TARGET_ARM)
2842 guint8 buf [128];
2843 guint8 *code;
2845 /* The same as mono_arch_get_gsharedvt_arg_trampoline (), but for AOT */
2846 /* Similar to arch_emit_specific_trampoline () */
2847 *tramp_size = 24;
2848 code = buf;
2849 ARM_PUSH (code, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3));
2850 ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8);
2851 /* Load the arg value from the GOT */
2852 ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R1);
2853 /* Load the addr from the GOT */
2854 ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_PC, ARMREG_R1);
2855 /* Branch to it */
2856 ARM_BX (code, ARMREG_R1);
2858 g_assert (code - buf == 20);
2860 /* Emit it */
2861 emit_bytes (acfg, buf, code - buf);
2862 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) + 4);
2863 #elif defined(TARGET_ARM64)
2864 arm64_emit_gsharedvt_arg_trampoline (acfg, offset, tramp_size);
2865 #elif defined (TARGET_AMD64)
2867 amd64_emit_load_got_slot (acfg, AMD64_RAX, offset);
2868 amd64_emit_load_got_slot (acfg, MONO_ARCH_IMT_SCRATCH_REG, offset + 1);
2869 g_assert (AMD64_R11 == MONO_ARCH_IMT_SCRATCH_REG);
2870 fprintf (acfg->fp, "jmp *%%r11\n");
2872 *tramp_size = 0x11;
2873 #else
2874 g_assert_not_reached ();
2875 #endif
2878 static void
2879 arch_emit_ftnptr_arg_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
2881 #if defined(TARGET_ARM)
2882 guint8 buf [128];
2883 guint8 *code;
2885 *tramp_size = 32;
2886 code = buf;
2888 /* Load target address and push it on stack */
2889 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 16);
2890 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
2891 ARM_PUSH (code, 1 << ARMREG_IP);
2892 /* Load argument in ARMREG_IP */
2893 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 8);
2894 ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
2895 /* Branch */
2896 ARM_POP (code, 1 << ARMREG_PC);
2898 g_assert (code - buf == 24);
2900 /* Emit it */
2901 emit_bytes (acfg, buf, code - buf);
2902 emit_symbol_diff (acfg, acfg->got_symbol, ".", ((offset + 1) * sizeof (target_mgreg_t)) + 12); // offset from ldr pc to addr
2903 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) + 4); // offset from ldr pc to arg
2904 #else
2905 g_assert_not_reached ();
2906 #endif
2909 static void
2910 arch_emit_unbox_arbitrary_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size)
2912 #if defined(TARGET_ARM64)
2913 emit_unset_mode (acfg);
2914 fprintf (acfg->fp, "add x0, x0, %d\n", (int)(MONO_ABI_SIZEOF (MonoObject)));
2915 arm64_emit_load_got_slot (acfg, ARMREG_R17, offset);
2916 fprintf (acfg->fp, "br x17\n");
2917 *tramp_size = 5 * 4;
2918 #elif defined (TARGET_AMD64)
2919 guint8 buf [32];
2920 guint8 *code;
2921 int this_reg;
2923 this_reg = mono_arch_get_this_arg_reg (NULL);
2924 code = buf;
2925 amd64_alu_reg_imm (code, X86_ADD, this_reg, MONO_ABI_SIZEOF (MonoObject));
2926 emit_bytes (acfg, buf, code - buf);
2928 amd64_emit_load_got_slot (acfg, AMD64_RAX, offset);
2929 fprintf (acfg->fp, "jmp *%%rax\n");
2931 *tramp_size = 13;
2932 #elif defined (TARGET_ARM)
2933 guint8 buf [32];
2934 guint8 *code, *label;
2936 code = buf;
2937 /* Unbox */
2938 ARM_ADD_REG_IMM8 (code, ARMREG_R0, ARMREG_R0, MONO_ABI_SIZEOF (MonoObject));
2940 label = code;
2941 /* Calculate GOT slot */
2942 ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
2943 /* Load target addr into PC*/
2944 ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_IP);
2946 g_assert (code - buf == 12);
2948 /* Emit it */
2949 emit_bytes (acfg, buf, code - buf);
2950 emit_symbol_diff (acfg, acfg->got_symbol, ".", (offset * sizeof (target_mgreg_t)) + (code - (label + 8)) - 4);
2951 *tramp_size = 4 * 4;
2952 #else
2953 g_error ("NOT IMPLEMENTED: needed for AOT<>interp mixed mode transition");
2954 #endif
2957 /* END OF ARCH SPECIFIC CODE */
2959 static guint32
2960 mono_get_field_token (MonoClassField *field)
2962 MonoClass *klass = field->parent;
2963 int i;
2965 int fcount = mono_class_get_field_count (klass);
2966 MonoClassField *klass_fields = m_class_get_fields (klass);
2967 for (i = 0; i < fcount; ++i) {
2968 if (field == &klass_fields [i])
2969 return MONO_TOKEN_FIELD_DEF | (mono_class_get_first_field_idx (klass) + 1 + i);
2972 g_assert_not_reached ();
2973 return 0;
2976 static void
2977 encode_value (gint32 value, guint8 *buf, guint8 **endbuf)
2979 guint8 *p = buf;
2981 //printf ("ENCODE: %d 0x%x.\n", value, value);
2984 * Same encoding as the one used in the metadata, extended to handle values
2985 * greater than 0x1fffffff.
2987 if ((value >= 0) && (value <= 127))
2988 *p++ = value;
2989 else if ((value >= 0) && (value <= 16383)) {
2990 p [0] = 0x80 | (value >> 8);
2991 p [1] = value & 0xff;
2992 p += 2;
2993 } else if ((value >= 0) && (value <= 0x1fffffff)) {
2994 p [0] = (value >> 24) | 0xc0;
2995 p [1] = (value >> 16) & 0xff;
2996 p [2] = (value >> 8) & 0xff;
2997 p [3] = value & 0xff;
2998 p += 4;
3000 else {
3001 p [0] = 0xff;
3002 p [1] = (value >> 24) & 0xff;
3003 p [2] = (value >> 16) & 0xff;
3004 p [3] = (value >> 8) & 0xff;
3005 p [4] = value & 0xff;
3006 p += 5;
3008 if (endbuf)
3009 *endbuf = p;
3012 static void
3013 stream_init (MonoDynamicStream *sh)
3015 sh->index = 0;
3016 sh->alloc_size = 4096;
3017 sh->data = (char *)g_malloc (4096);
3019 /* So offsets are > 0 */
3020 sh->data [0] = 0;
3021 sh->index ++;
3024 static void
3025 make_room_in_stream (MonoDynamicStream *stream, int size)
3027 if (size <= stream->alloc_size)
3028 return;
3030 while (stream->alloc_size <= size) {
3031 if (stream->alloc_size < 4096)
3032 stream->alloc_size = 4096;
3033 else
3034 stream->alloc_size *= 2;
3037 stream->data = (char *)g_realloc (stream->data, stream->alloc_size);
3040 static guint32
3041 add_stream_data (MonoDynamicStream *stream, const char *data, guint32 len)
3043 guint32 idx;
3045 make_room_in_stream (stream, stream->index + len);
3046 memcpy (stream->data + stream->index, data, len);
3047 idx = stream->index;
3048 stream->index += len;
3049 return idx;
3053 * add_to_blob:
3055 * Add data to the binary blob inside the aot image. Returns the offset inside the
3056 * blob where the data was stored.
3058 static guint32
3059 add_to_blob (MonoAotCompile *acfg, const guint8 *data, guint32 data_len)
3061 g_assert (!acfg->blob_closed);
3063 if (acfg->blob.alloc_size == 0)
3064 stream_init (&acfg->blob);
3066 acfg->stats.blob_size += data_len;
3068 return add_stream_data (&acfg->blob, (char*)data, data_len);
3071 static guint32
3072 add_to_blob_aligned (MonoAotCompile *acfg, const guint8 *data, guint32 data_len, guint32 align)
3074 char buf [4] = {0};
3075 guint32 count;
3077 if (acfg->blob.alloc_size == 0)
3078 stream_init (&acfg->blob);
3080 count = acfg->blob.index % align;
3082 /* we assume the stream data will be aligned */
3083 if (count)
3084 add_stream_data (&acfg->blob, buf, 4 - count);
3086 return add_stream_data (&acfg->blob, (char*)data, data_len);
3089 /* Emit a table of data into the aot image */
3090 static void
3091 emit_aot_data (MonoAotCompile *acfg, MonoAotFileTable table, const char *symbol, guint8 *data, int size)
3093 if (acfg->data_outfile) {
3094 acfg->table_offsets [(int)table] = acfg->datafile_offset;
3095 fwrite (data,1, size, acfg->data_outfile);
3096 acfg->datafile_offset += size;
3097 // align the data to 8 bytes. Put zeros in the file (so that every build results in consistent output).
3098 int align = 8 - size % 8;
3099 acfg->datafile_offset += align;
3100 guint8 align_buf [16];
3101 memset (&align_buf, 0, sizeof (align_buf));
3102 fwrite (align_buf, align, 1, acfg->data_outfile);
3103 } else if (acfg->llvm) {
3104 mono_llvm_emit_aot_data (symbol, data, size);
3105 } else {
3106 emit_section_change (acfg, RODATA_SECT, 0);
3107 emit_alignment (acfg, 8);
3108 emit_label (acfg, symbol);
3109 emit_bytes (acfg, data, size);
3114 * emit_offset_table:
3116 * Emit a table of increasing offsets in a compact form using differential encoding.
3117 * There is an index entry for each GROUP_SIZE number of entries. The greater the
3118 * group size, the more compact the table becomes, but the slower it becomes to compute
3119 * a given entry. Returns the size of the table.
3121 static guint32
3122 emit_offset_table (MonoAotCompile *acfg, const char *symbol, MonoAotFileTable table, int noffsets, int group_size, gint32 *offsets)
3124 gint32 current_offset;
3125 int i, buf_size, ngroups, index_entry_size;
3126 guint8 *p, *buf;
3127 guint8 *data_p, *data_buf;
3128 guint32 *index_offsets;
3130 ngroups = (noffsets + (group_size - 1)) / group_size;
3132 index_offsets = g_new0 (guint32, ngroups);
3134 buf_size = noffsets * 4;
3135 p = buf = (guint8 *)g_malloc0 (buf_size);
3137 current_offset = 0;
3138 for (i = 0; i < noffsets; ++i) {
3139 //printf ("D: %d -> %d\n", i, offsets [i]);
3140 if ((i % group_size) == 0) {
3141 index_offsets [i / group_size] = p - buf;
3142 /* Emit the full value for these entries */
3143 encode_value (offsets [i], p, &p);
3144 } else {
3145 /* The offsets are allowed to be non-increasing */
3146 //g_assert (offsets [i] >= current_offset);
3147 encode_value (offsets [i] - current_offset, p, &p);
3149 current_offset = offsets [i];
3151 data_buf = buf;
3152 data_p = p;
3154 if (ngroups && index_offsets [ngroups - 1] < 65000)
3155 index_entry_size = 2;
3156 else
3157 index_entry_size = 4;
3159 buf_size = (data_p - data_buf) + (ngroups * 4) + 16;
3160 p = buf = (guint8 *)g_malloc0 (buf_size);
3162 /* Emit the header */
3163 encode_int (noffsets, p, &p);
3164 encode_int (group_size, p, &p);
3165 encode_int (ngroups, p, &p);
3166 encode_int (index_entry_size, p, &p);
3168 /* Emit the index */
3169 for (i = 0; i < ngroups; ++i) {
3170 if (index_entry_size == 2)
3171 encode_int16 (index_offsets [i], p, &p);
3172 else
3173 encode_int (index_offsets [i], p, &p);
3175 /* Emit the data */
3176 memcpy (p, data_buf, data_p - data_buf);
3177 p += data_p - data_buf;
3179 g_assert (p - buf <= buf_size);
3181 emit_aot_data (acfg, table, symbol, buf, p - buf);
3183 g_free (buf);
3184 g_free (data_buf);
3186 return (int)(p - buf);
3189 static guint32
3190 get_image_index (MonoAotCompile *cfg, MonoImage *image)
3192 guint32 index;
3194 index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image));
3195 if (index)
3196 return index - 1;
3197 else {
3198 index = g_hash_table_size (cfg->image_hash);
3199 g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1));
3200 g_ptr_array_add (cfg->image_table, image);
3201 return index;
3205 static guint32
3206 find_typespec_for_class (MonoAotCompile *acfg, MonoClass *klass)
3208 int i;
3209 int len = acfg->image->tables [MONO_TABLE_TYPESPEC].rows;
3211 /* FIXME: Search referenced images as well */
3212 if (!acfg->typespec_classes) {
3213 acfg->typespec_classes = g_hash_table_new (NULL, NULL);
3214 for (i = 0; i < len; i++) {
3215 ERROR_DECL (error);
3216 int typespec = MONO_TOKEN_TYPE_SPEC | (i + 1);
3217 MonoClass *klass_key = mono_class_get_and_inflate_typespec_checked (acfg->image, typespec, NULL, error);
3218 if (!is_ok (error)) {
3219 mono_error_cleanup (error);
3220 continue;
3222 g_hash_table_insert (acfg->typespec_classes, klass_key, GINT_TO_POINTER (typespec));
3225 return GPOINTER_TO_INT (g_hash_table_lookup (acfg->typespec_classes, klass));
3228 static void
3229 encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf);
3231 static void
3232 encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf);
3234 static void
3235 encode_ginst (MonoAotCompile *acfg, MonoGenericInst *inst, guint8 *buf, guint8 **endbuf);
3237 static void
3238 encode_type (MonoAotCompile *acfg, MonoType *t, guint8 *buf, guint8 **endbuf);
3240 static guint32
3241 get_shared_ginst_ref (MonoAotCompile *acfg, MonoGenericInst *ginst);
3243 static void
3244 encode_klass_ref_inner (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf)
3246 guint8 *p = buf;
3249 * The encoding begins with one of the MONO_AOT_TYPEREF values, followed by additional
3250 * information.
3253 if (mono_class_is_ginst (klass)) {
3254 guint32 token;
3255 g_assert (m_class_get_type_token (klass));
3257 /* Find a typespec for a class if possible */
3258 token = find_typespec_for_class (acfg, klass);
3259 if (token) {
3260 encode_value (MONO_AOT_TYPEREF_TYPESPEC_TOKEN, p, &p);
3261 encode_value (token, p, &p);
3262 } else {
3263 MonoClass *gclass = mono_class_get_generic_class (klass)->container_class;
3264 MonoGenericInst *inst = mono_class_get_generic_class (klass)->context.class_inst;
3265 static int count = 0;
3266 guint8 *p1 = p;
3268 encode_value (MONO_AOT_TYPEREF_GINST, p, &p);
3269 encode_klass_ref (acfg, gclass, p, &p);
3270 guint32 offset = get_shared_ginst_ref (acfg, inst);
3271 encode_value (offset, p, &p);
3273 count += p - p1;
3275 } else if (m_class_get_type_token (klass)) {
3276 int iindex = get_image_index (acfg, m_class_get_image (klass));
3278 g_assert (mono_metadata_token_code (m_class_get_type_token (klass)) == MONO_TOKEN_TYPE_DEF);
3279 if (iindex == 0) {
3280 encode_value (MONO_AOT_TYPEREF_TYPEDEF_INDEX, p, &p);
3281 encode_value (m_class_get_type_token (klass) - MONO_TOKEN_TYPE_DEF, p, &p);
3282 } else {
3283 encode_value (MONO_AOT_TYPEREF_TYPEDEF_INDEX_IMAGE, p, &p);
3284 encode_value (m_class_get_type_token (klass) - MONO_TOKEN_TYPE_DEF, p, &p);
3285 encode_value (get_image_index (acfg, m_class_get_image (klass)), p, &p);
3287 } else if ((m_class_get_byval_arg (klass)->type == MONO_TYPE_VAR) || (m_class_get_byval_arg (klass)->type == MONO_TYPE_MVAR)) {
3288 MonoGenericContainer *container = mono_type_get_generic_param_owner (m_class_get_byval_arg (klass));
3289 MonoGenericParam *par = m_class_get_byval_arg (klass)->data.generic_param;
3291 encode_value (MONO_AOT_TYPEREF_VAR, p, &p);
3293 encode_value (par->gshared_constraint ? 1 : 0, p, &p);
3294 if (par->gshared_constraint) {
3295 MonoGSharedGenericParam *gpar = (MonoGSharedGenericParam*)par;
3296 encode_type (acfg, par->gshared_constraint, p, &p);
3297 encode_klass_ref (acfg, mono_class_create_generic_parameter (gpar->parent), p, &p);
3298 } else {
3299 encode_value (m_class_get_byval_arg (klass)->type, p, &p);
3300 encode_value (mono_type_get_generic_param_num (m_class_get_byval_arg (klass)), p, &p);
3302 encode_value (container->is_anonymous ? 0 : 1, p, &p);
3304 if (!container->is_anonymous) {
3305 encode_value (container->is_method, p, &p);
3306 if (container->is_method)
3307 encode_method_ref (acfg, container->owner.method, p, &p);
3308 else
3309 encode_klass_ref (acfg, container->owner.klass, p, &p);
3312 } else if (m_class_get_byval_arg (klass)->type == MONO_TYPE_PTR) {
3313 encode_value (MONO_AOT_TYPEREF_PTR, p, &p);
3314 encode_type (acfg, m_class_get_byval_arg (klass), p, &p);
3315 } else {
3316 /* Array class */
3317 g_assert (m_class_get_rank (klass) > 0);
3318 encode_value (MONO_AOT_TYPEREF_ARRAY, p, &p);
3319 encode_value (m_class_get_rank (klass), p, &p);
3320 encode_klass_ref (acfg, m_class_get_element_class (klass), p, &p);
3323 acfg->stats.class_ref_count++;
3324 acfg->stats.class_ref_size += p - buf;
3326 *endbuf = p;
3329 static guint32
3330 get_shared_klass_ref (MonoAotCompile *acfg, MonoClass *klass)
3332 guint offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->klass_blob_hash, klass));
3333 guint8 *buf2, *p;
3335 if (!offset) {
3336 buf2 = (guint8 *)g_malloc (1024);
3337 p = buf2;
3339 encode_klass_ref_inner (acfg, klass, p, &p);
3340 g_assert (p - buf2 < 1024);
3342 offset = add_to_blob (acfg, buf2, p - buf2);
3343 g_free (buf2);
3345 g_hash_table_insert (acfg->klass_blob_hash, klass, GUINT_TO_POINTER (offset + 1));
3346 } else {
3347 offset --;
3350 return offset;
3354 * encode_klass_ref:
3356 * Encode a reference to KLASS. We use our home-grown encoding instead of the
3357 * standard metadata encoding.
3359 static void
3360 encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf)
3362 gboolean shared = FALSE;
3365 * The encoding of generic instances is large so emit them only once.
3367 if (mono_class_is_ginst (klass)) {
3368 guint32 token;
3369 g_assert (m_class_get_type_token (klass));
3371 /* Find a typespec for a class if possible */
3372 token = find_typespec_for_class (acfg, klass);
3373 if (!token)
3374 shared = TRUE;
3375 } else if ((m_class_get_byval_arg (klass)->type == MONO_TYPE_VAR) || (m_class_get_byval_arg (klass)->type == MONO_TYPE_MVAR)) {
3376 shared = TRUE;
3379 if (shared) {
3380 guint8 *p;
3381 guint32 offset = get_shared_klass_ref (acfg, klass);
3383 p = buf;
3384 encode_value (MONO_AOT_TYPEREF_BLOB_INDEX, p, &p);
3385 encode_value (offset, p, &p);
3386 *endbuf = p;
3387 return;
3390 encode_klass_ref_inner (acfg, klass, buf, endbuf);
3393 static void
3394 encode_field_info (MonoAotCompile *cfg, MonoClassField *field, guint8 *buf, guint8 **endbuf)
3396 guint32 token = mono_get_field_token (field);
3397 guint8 *p = buf;
3399 encode_klass_ref (cfg, field->parent, p, &p);
3400 g_assert (mono_metadata_token_code (token) == MONO_TOKEN_FIELD_DEF);
3401 encode_value (token - MONO_TOKEN_FIELD_DEF, p, &p);
3402 *endbuf = p;
3405 static void
3406 encode_ginst (MonoAotCompile *acfg, MonoGenericInst *inst, guint8 *buf, guint8 **endbuf)
3408 guint8 *p = buf;
3409 int i;
3411 encode_value (inst->type_argc, p, &p);
3412 for (i = 0; i < inst->type_argc; ++i)
3413 encode_klass_ref (acfg, mono_class_from_mono_type_internal (inst->type_argv [i]), p, &p);
3415 acfg->stats.ginst_count++;
3416 acfg->stats.ginst_size += p - buf;
3418 *endbuf = p;
3421 static guint32
3422 get_shared_ginst_ref (MonoAotCompile *acfg, MonoGenericInst *ginst)
3424 guint32 offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->ginst_blob_hash, ginst));
3425 if (!offset) {
3426 guint8 *buf2, *p2;
3427 int len;
3429 len = 1024 + (ginst->type_argc * 32);
3430 buf2 = (guint8 *)g_malloc (len);
3431 p2 = buf2;
3433 encode_ginst (acfg, ginst, p2, &p2);
3434 g_assert (p2 - buf2 < len);
3436 offset = add_to_blob (acfg, buf2, p2 - buf2);
3437 g_free (buf2);
3439 g_hash_table_insert (acfg->ginst_blob_hash, ginst, GUINT_TO_POINTER (offset + 1));
3440 } else {
3441 offset --;
3443 return offset;
3446 static void
3447 encode_generic_context (MonoAotCompile *acfg, MonoGenericContext *context, guint8 *buf, guint8 **endbuf)
3449 guint8 *p = buf;
3450 MonoGenericInst *inst;
3451 guint32 flags = (context->class_inst ? 1 : 0) | (context->method_inst ? 2 : 0);
3453 g_assert (flags);
3455 encode_value (flags, p, &p);
3456 inst = context->class_inst;
3457 if (inst) {
3458 guint32 offset = get_shared_ginst_ref (acfg, inst);
3459 encode_value (offset, p, &p);
3461 inst = context->method_inst;
3462 if (inst) {
3463 guint32 offset = get_shared_ginst_ref (acfg, inst);
3464 encode_value (offset, p, &p);
3466 *endbuf = p;
3469 static void
3470 encode_type (MonoAotCompile *acfg, MonoType *t, guint8 *buf, guint8 **endbuf)
3472 guint8 *p = buf;
3474 if (t->has_cmods) {
3475 int count = mono_type_custom_modifier_count (t);
3477 *p = MONO_TYPE_CMOD_REQD;
3478 ++p;
3480 encode_value (count, p, &p);
3481 for (int i = 0; i < count; ++i) {
3482 ERROR_DECL (error);
3483 gboolean required;
3484 MonoType *cmod_type = mono_type_get_custom_modifier (t, i, &required, error);
3485 mono_error_assert_ok (error);
3486 encode_value (required, p, &p);
3487 encode_type (acfg, cmod_type, p, &p);
3491 /* t->attrs can be ignored */
3492 //g_assert (t->attrs == 0);
3494 if (t->pinned) {
3495 *p = MONO_TYPE_PINNED;
3496 ++p;
3498 if (t->byref) {
3499 *p = MONO_TYPE_BYREF;
3500 ++p;
3503 *p = t->type;
3504 p ++;
3506 switch (t->type) {
3507 case MONO_TYPE_VOID:
3508 case MONO_TYPE_BOOLEAN:
3509 case MONO_TYPE_CHAR:
3510 case MONO_TYPE_I1:
3511 case MONO_TYPE_U1:
3512 case MONO_TYPE_I2:
3513 case MONO_TYPE_U2:
3514 case MONO_TYPE_I4:
3515 case MONO_TYPE_U4:
3516 case MONO_TYPE_I8:
3517 case MONO_TYPE_U8:
3518 case MONO_TYPE_R4:
3519 case MONO_TYPE_R8:
3520 case MONO_TYPE_I:
3521 case MONO_TYPE_U:
3522 case MONO_TYPE_STRING:
3523 case MONO_TYPE_OBJECT:
3524 case MONO_TYPE_TYPEDBYREF:
3525 break;
3526 case MONO_TYPE_VALUETYPE:
3527 case MONO_TYPE_CLASS:
3528 encode_klass_ref (acfg, mono_class_from_mono_type_internal (t), p, &p);
3529 break;
3530 case MONO_TYPE_SZARRAY:
3531 encode_klass_ref (acfg, t->data.klass, p, &p);
3532 break;
3533 case MONO_TYPE_PTR:
3534 encode_type (acfg, t->data.type, p, &p);
3535 break;
3536 case MONO_TYPE_GENERICINST: {
3537 MonoClass *gclass = t->data.generic_class->container_class;
3538 MonoGenericInst *inst = t->data.generic_class->context.class_inst;
3540 encode_klass_ref (acfg, gclass, p, &p);
3541 encode_ginst (acfg, inst, p, &p);
3542 break;
3544 case MONO_TYPE_ARRAY: {
3545 MonoArrayType *array = t->data.array;
3546 int i;
3548 encode_klass_ref (acfg, array->eklass, p, &p);
3549 encode_value (array->rank, p, &p);
3550 encode_value (array->numsizes, p, &p);
3551 for (i = 0; i < array->numsizes; ++i)
3552 encode_value (array->sizes [i], p, &p);
3553 encode_value (array->numlobounds, p, &p);
3554 for (i = 0; i < array->numlobounds; ++i)
3555 encode_value (array->lobounds [i], p, &p);
3556 break;
3558 case MONO_TYPE_VAR:
3559 case MONO_TYPE_MVAR:
3560 encode_klass_ref (acfg, mono_class_from_mono_type_internal (t), p, &p);
3561 break;
3562 default:
3563 g_assert_not_reached ();
3566 *endbuf = p;
3569 static void
3570 encode_signature (MonoAotCompile *acfg, MonoMethodSignature *sig, guint8 *buf, guint8 **endbuf)
3572 guint8 *p = buf;
3573 guint32 flags = 0;
3574 int i;
3576 /* Similar to the metadata encoding */
3577 if (sig->generic_param_count)
3578 flags |= 0x10;
3579 if (sig->hasthis)
3580 flags |= 0x20;
3581 if (sig->explicit_this)
3582 flags |= 0x40;
3583 flags |= (sig->call_convention & 0x0F);
3585 *p = flags;
3586 ++p;
3587 if (sig->generic_param_count)
3588 encode_value (sig->generic_param_count, p, &p);
3589 encode_value (sig->param_count, p, &p);
3591 encode_type (acfg, sig->ret, p, &p);
3592 for (i = 0; i < sig->param_count; ++i) {
3593 if (sig->sentinelpos == i) {
3594 *p = MONO_TYPE_SENTINEL;
3595 ++p;
3597 encode_type (acfg, sig->params [i], p, &p);
3600 *endbuf = p;
3603 #define MAX_IMAGE_INDEX 250
3605 static void
3606 encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf)
3608 guint32 image_index = get_image_index (acfg, m_class_get_image (method->klass));
3609 guint32 token = method->token;
3610 MonoJumpInfoToken *ji;
3611 guint8 *p = buf;
3614 * The encoding for most methods is as follows:
3615 * - image index encoded as a leb128
3616 * - token index encoded as a leb128
3617 * Values of image index >= MONO_AOT_METHODREF_MIN are used to mark additional
3618 * types of method encodings.
3621 /* Mark methods which can't use aot trampolines because they need the further
3622 * processing in mono_magic_trampoline () which requires a MonoMethod*.
3624 if ((method->is_generic && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) ||
3625 (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED))
3626 encode_value ((MONO_AOT_METHODREF_NO_AOT_TRAMPOLINE << 24), p, &p);
3628 if (method->wrapper_type) {
3629 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
3631 encode_value ((MONO_AOT_METHODREF_WRAPPER << 24), p, &p);
3633 encode_value (method->wrapper_type, p, &p);
3635 switch (method->wrapper_type) {
3636 case MONO_WRAPPER_REMOTING_INVOKE:
3637 case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
3638 case MONO_WRAPPER_XDOMAIN_INVOKE: {
3639 MonoMethod *m;
3641 m = mono_marshal_method_from_wrapper (method);
3642 g_assert (m);
3643 encode_method_ref (acfg, m, p, &p);
3644 break;
3646 case MONO_WRAPPER_PROXY_ISINST:
3647 case MONO_WRAPPER_LDFLD:
3648 case MONO_WRAPPER_LDFLDA:
3649 case MONO_WRAPPER_STFLD: {
3650 g_assert (info);
3651 encode_klass_ref (acfg, info->d.proxy.klass, p, &p);
3652 break;
3654 case MONO_WRAPPER_ALLOC: {
3655 /* The GC name is saved once in MonoAotFileInfo */
3656 g_assert (info->d.alloc.alloc_type != -1);
3657 encode_value (info->d.alloc.alloc_type, p, &p);
3658 break;
3660 case MONO_WRAPPER_WRITE_BARRIER: {
3661 g_assert (info);
3662 break;
3664 case MONO_WRAPPER_STELEMREF: {
3665 g_assert (info);
3666 encode_value (info->subtype, p, &p);
3667 if (info->subtype == WRAPPER_SUBTYPE_VIRTUAL_STELEMREF)
3668 encode_value (info->d.virtual_stelemref.kind, p, &p);
3669 break;
3671 case MONO_WRAPPER_OTHER: {
3672 g_assert (info);
3673 encode_value (info->subtype, p, &p);
3674 if (info->subtype == WRAPPER_SUBTYPE_PTR_TO_STRUCTURE ||
3675 info->subtype == WRAPPER_SUBTYPE_STRUCTURE_TO_PTR)
3676 encode_klass_ref (acfg, method->klass, p, &p);
3677 else if (info->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER)
3678 encode_method_ref (acfg, info->d.synchronized_inner.method, p, &p);
3679 else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR)
3680 encode_method_ref (acfg, info->d.array_accessor.method, p, &p);
3681 else if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN)
3682 encode_signature (acfg, info->d.interp_in.sig, p, &p);
3683 else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG)
3684 encode_signature (acfg, info->d.gsharedvt.sig, p, &p);
3685 else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG)
3686 encode_signature (acfg, info->d.gsharedvt.sig, p, &p);
3687 else if (info->subtype == WRAPPER_SUBTYPE_INTERP_LMF)
3688 encode_value (info->d.icall.jit_icall_id, p, &p);
3689 else if (info->subtype == WRAPPER_SUBTYPE_AOT_INIT)
3690 encode_value (info->d.aot_init.subtype, p, &p);
3691 else if (info->subtype == WRAPPER_SUBTYPE_LLVM_FUNC)
3692 encode_value (info->d.llvm_func.subtype, p, &p);
3693 break;
3695 case MONO_WRAPPER_MANAGED_TO_NATIVE: {
3696 g_assert (info);
3697 encode_value (info->subtype, p, &p);
3698 if (info->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) {
3699 encode_value (info->d.icall.jit_icall_id, p, &p);
3700 } else if (info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT) {
3701 encode_method_ref (acfg, info->d.managed_to_native.method, p, &p);
3702 } else {
3703 g_assert (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_PINVOKE);
3704 encode_method_ref (acfg, info->d.managed_to_native.method, p, &p);
3706 break;
3708 case MONO_WRAPPER_SYNCHRONIZED: {
3709 MonoMethod *m;
3711 m = mono_marshal_method_from_wrapper (method);
3712 g_assert (m);
3713 g_assert (m != method);
3714 encode_method_ref (acfg, m, p, &p);
3715 break;
3717 case MONO_WRAPPER_MANAGED_TO_MANAGED: {
3718 g_assert (info);
3719 encode_value (info->subtype, p, &p);
3721 if (info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR) {
3722 encode_value (info->d.element_addr.rank, p, &p);
3723 encode_value (info->d.element_addr.elem_size, p, &p);
3724 } else if (info->subtype == WRAPPER_SUBTYPE_STRING_CTOR) {
3725 encode_method_ref (acfg, info->d.string_ctor.method, p, &p);
3726 } else if (info->subtype == WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER) {
3727 encode_klass_ref (acfg, info->d.generic_array_helper.klass, p, &p);
3728 encode_method_ref (acfg, info->d.generic_array_helper.method, p, &p);
3729 int len = strlen (info->d.generic_array_helper.name);
3730 guint32 idx = add_to_blob (acfg, (guint8*)info->d.generic_array_helper.name, len + 1);
3731 encode_value (idx, p, &p);
3732 } else {
3733 g_assert_not_reached ();
3735 break;
3737 case MONO_WRAPPER_CASTCLASS: {
3738 g_assert (info);
3739 encode_value (info->subtype, p, &p);
3740 break;
3742 case MONO_WRAPPER_RUNTIME_INVOKE: {
3743 g_assert (info);
3744 encode_value (info->subtype, p, &p);
3745 if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)
3746 encode_method_ref (acfg, info->d.runtime_invoke.method, p, &p);
3747 else if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL)
3748 encode_signature (acfg, info->d.runtime_invoke.sig, p, &p);
3749 break;
3751 case MONO_WRAPPER_DELEGATE_INVOKE:
3752 case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE:
3753 case MONO_WRAPPER_DELEGATE_END_INVOKE: {
3754 if (method->is_inflated) {
3755 /* These wrappers are identified by their class */
3756 encode_value (1, p, &p);
3757 encode_klass_ref (acfg, method->klass, p, &p);
3758 } else {
3759 MonoMethodSignature *sig = mono_method_signature_internal (method);
3760 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
3762 encode_value (0, p, &p);
3763 if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE)
3764 encode_value (info ? info->subtype : 0, p, &p);
3765 encode_signature (acfg, sig, p, &p);
3767 break;
3769 case MONO_WRAPPER_NATIVE_TO_MANAGED: {
3770 g_assert (info);
3771 encode_method_ref (acfg, info->d.native_to_managed.method, p, &p);
3772 MonoClass *klass = info->d.native_to_managed.klass;
3773 if (!klass) {
3774 encode_value (0, p, &p);
3775 } else {
3776 encode_value (1, p, &p);
3777 encode_klass_ref (acfg, klass, p, &p);
3779 break;
3781 default:
3782 g_assert_not_reached ();
3784 } else if (mono_method_signature_internal (method)->is_inflated) {
3786 * This is a generic method, find the original token which referenced it and
3787 * encode that.
3788 * Obtain the token from information recorded by the JIT.
3790 ji = (MonoJumpInfoToken *)g_hash_table_lookup (acfg->token_info_hash, method);
3791 if (ji) {
3792 image_index = get_image_index (acfg, ji->image);
3793 g_assert (image_index < MAX_IMAGE_INDEX);
3794 token = ji->token;
3796 encode_value ((MONO_AOT_METHODREF_METHODSPEC << 24), p, &p);
3797 encode_value (image_index, p, &p);
3798 encode_value (token, p, &p);
3799 } else if (g_hash_table_lookup (acfg->method_blob_hash, method)) {
3800 /* Already emitted as part of an rgctx fetch */
3801 guint32 offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_blob_hash, method));
3802 offset --;
3804 encode_value ((MONO_AOT_METHODREF_BLOB_INDEX << 24), p, &p);
3805 encode_value (offset, p, &p);
3806 } else {
3807 MonoMethod *declaring;
3808 MonoGenericContext *context = mono_method_get_context (method);
3810 g_assert (method->is_inflated);
3811 declaring = ((MonoMethodInflated*)method)->declaring;
3814 * This might be a non-generic method of a generic instance, which
3815 * doesn't have a token since the reference is generated by the JIT
3816 * like Nullable:Box/Unbox, or by generic sharing.
3818 encode_value ((MONO_AOT_METHODREF_GINST << 24), p, &p);
3819 /* Encode the klass */
3820 encode_klass_ref (acfg, method->klass, p, &p);
3821 /* Encode the method */
3822 image_index = get_image_index (acfg, m_class_get_image (method->klass));
3823 g_assert (image_index < MAX_IMAGE_INDEX);
3824 g_assert (declaring->token);
3825 token = declaring->token;
3826 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
3827 encode_value (image_index, p, &p);
3828 encode_value (mono_metadata_token_index (token), p, &p);
3829 encode_generic_context (acfg, context, p, &p);
3831 } else if (token == 0) {
3832 /* This might be a method of a constructed type like int[,].Set */
3833 /* Obtain the token from information recorded by the JIT */
3834 ji = (MonoJumpInfoToken *)g_hash_table_lookup (acfg->token_info_hash, method);
3835 if (ji) {
3836 image_index = get_image_index (acfg, ji->image);
3837 g_assert (image_index < MAX_IMAGE_INDEX);
3838 token = ji->token;
3840 encode_value ((MONO_AOT_METHODREF_METHODSPEC << 24), p, &p);
3841 encode_value (image_index, p, &p);
3842 encode_value (token, p, &p);
3843 } else {
3844 /* Array methods */
3845 g_assert (m_class_get_rank (method->klass));
3847 /* Encode directly */
3848 encode_value ((MONO_AOT_METHODREF_ARRAY << 24), p, &p);
3849 encode_klass_ref (acfg, method->klass, p, &p);
3850 if (!strcmp (method->name, ".ctor") && mono_method_signature_internal (method)->param_count == m_class_get_rank (method->klass))
3851 encode_value (0, p, &p);
3852 else if (!strcmp (method->name, ".ctor") && mono_method_signature_internal (method)->param_count == m_class_get_rank (method->klass) * 2)
3853 encode_value (1, p, &p);
3854 else if (!strcmp (method->name, "Get"))
3855 encode_value (2, p, &p);
3856 else if (!strcmp (method->name, "Address"))
3857 encode_value (3, p, &p);
3858 else if (!strcmp (method->name, "Set"))
3859 encode_value (4, p, &p);
3860 else
3861 g_assert_not_reached ();
3863 } else {
3864 g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
3866 if (image_index >= MONO_AOT_METHODREF_MIN) {
3867 encode_value ((MONO_AOT_METHODREF_LARGE_IMAGE_INDEX << 24), p, &p);
3868 encode_value (image_index, p, &p);
3869 encode_value (mono_metadata_token_index (token), p, &p);
3870 } else {
3871 encode_value ((image_index << 24) | mono_metadata_token_index (token), p, &p);
3875 acfg->stats.method_ref_count++;
3876 acfg->stats.method_ref_size += p - buf;
3878 *endbuf = p;
3881 static guint32
3882 get_shared_method_ref (MonoAotCompile *acfg, MonoMethod *method)
3884 guint32 offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_blob_hash, method));
3885 if (!offset) {
3886 guint8 *buf2, *p2;
3888 buf2 = (guint8 *)g_malloc (1024);
3889 p2 = buf2;
3891 encode_method_ref (acfg, method, p2, &p2);
3892 g_assert (p2 - buf2 < 1024);
3894 offset = add_to_blob (acfg, buf2, p2 - buf2);
3895 g_free (buf2);
3897 g_hash_table_insert (acfg->method_blob_hash, method, GUINT_TO_POINTER (offset + 1));
3898 } else {
3899 offset --;
3901 return offset;
3904 static gint
3905 compare_patches (gconstpointer a, gconstpointer b)
3907 int i, j;
3909 i = (*(MonoJumpInfo**)a)->ip.i;
3910 j = (*(MonoJumpInfo**)b)->ip.i;
3912 if (i < j)
3913 return -1;
3914 else
3915 if (i > j)
3916 return 1;
3917 else
3918 return 0;
3921 static G_GNUC_UNUSED char*
3922 patch_to_string (MonoJumpInfo *patch_info)
3924 GString *str;
3926 str = g_string_new ("");
3928 g_string_append_printf (str, "%s(", get_patch_name (patch_info->type));
3930 switch (patch_info->type) {
3931 case MONO_PATCH_INFO_VTABLE:
3932 mono_type_get_desc (str, m_class_get_byval_arg (patch_info->data.klass), TRUE);
3933 break;
3934 default:
3935 break;
3937 g_string_append_printf (str, ")");
3938 return g_string_free (str, FALSE);
3942 * is_plt_patch:
3944 * Return whenever PATCH_INFO refers to a direct call, and thus requires a
3945 * PLT entry.
3947 static gboolean
3948 is_plt_patch (MonoJumpInfo *patch_info)
3950 switch (patch_info->type) {
3951 case MONO_PATCH_INFO_METHOD:
3952 case MONO_PATCH_INFO_JIT_ICALL_ID:
3953 case MONO_PATCH_INFO_JIT_ICALL_ADDR:
3954 case MONO_PATCH_INFO_ICALL_ADDR_CALL:
3955 case MONO_PATCH_INFO_RGCTX_FETCH:
3956 case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR:
3957 return TRUE;
3958 case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL:
3959 default:
3960 return FALSE;
3965 * get_plt_symbol:
3967 * Return the symbol identifying the plt entry PLT_OFFSET.
3969 static char*
3970 get_plt_symbol (MonoAotCompile *acfg, int plt_offset, MonoJumpInfo *patch_info)
3972 #ifdef TARGET_MACH
3974 * The Apple linker reorganizes object files, so it doesn't like branches to local
3975 * labels, since those have no relocations.
3977 return g_strdup_printf ("%sp_%d", acfg->llvm_label_prefix, plt_offset);
3978 #else
3979 return g_strdup_printf ("%sp_%d", acfg->temp_prefix, plt_offset);
3980 #endif
3984 * get_plt_entry:
3986 * Return a PLT entry which belongs to the method identified by PATCH_INFO.
3988 static MonoPltEntry*
3989 get_plt_entry (MonoAotCompile *acfg, MonoJumpInfo *patch_info)
3991 MonoPltEntry *res;
3992 gboolean synchronized = FALSE;
3993 static int synchronized_symbol_idx;
3995 if (!is_plt_patch (patch_info))
3996 return NULL;
3998 if (!acfg->patch_to_plt_entry [patch_info->type])
3999 acfg->patch_to_plt_entry [patch_info->type] = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal);
4000 res = (MonoPltEntry *)g_hash_table_lookup (acfg->patch_to_plt_entry [patch_info->type], patch_info);
4002 if (!acfg->llvm && patch_info->type == MONO_PATCH_INFO_METHOD && (patch_info->data.method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) {
4004 * Allocate a separate PLT slot for each such patch, since some plt
4005 * entries will refer to the method itself, and some will refer to the
4006 * wrapper.
4008 res = NULL;
4009 synchronized = TRUE;
4012 if (!res) {
4013 MonoJumpInfo *new_ji;
4015 new_ji = mono_patch_info_dup_mp (acfg->mempool, patch_info);
4017 res = (MonoPltEntry *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoPltEntry));
4018 res->plt_offset = acfg->plt_offset;
4019 res->ji = new_ji;
4020 res->symbol = get_plt_symbol (acfg, res->plt_offset, patch_info);
4021 if (acfg->aot_opts.write_symbols)
4022 res->debug_sym = get_plt_entry_debug_sym (acfg, res->ji, acfg->plt_entry_debug_sym_cache);
4023 if (synchronized) {
4024 /* Avoid duplicate symbols because we don't cache */
4025 res->symbol = g_strdup_printf ("%s_%d", res->symbol, synchronized_symbol_idx);
4026 if (res->debug_sym)
4027 res->debug_sym = g_strdup_printf ("%s_%d", res->debug_sym, synchronized_symbol_idx);
4028 synchronized_symbol_idx ++;
4031 if (res->debug_sym)
4032 res->llvm_symbol = g_strdup_printf ("%s_%s_llvm", res->symbol, res->debug_sym);
4033 else
4034 res->llvm_symbol = g_strdup_printf ("%s_llvm", res->symbol);
4035 if (strstr (res->llvm_symbol, acfg->temp_prefix) == res->llvm_symbol) {
4036 /* The llvm symbol shouldn't be temporary, since the llvm generated object file references it */
4037 char *tmp = res->llvm_symbol;
4038 res->llvm_symbol = g_strdup (res->llvm_symbol + strlen (acfg->temp_prefix));
4039 g_free (tmp);
4042 g_hash_table_insert (acfg->patch_to_plt_entry [new_ji->type], new_ji, res);
4044 g_hash_table_insert (acfg->plt_offset_to_entry, GUINT_TO_POINTER (res->plt_offset), res);
4046 //g_assert (mono_patch_info_equal (patch_info, new_ji));
4047 //mono_print_ji (patch_info); printf ("\n");
4048 //g_hash_table_print_stats (acfg->patch_to_plt_entry);
4050 acfg->plt_offset ++;
4053 return res;
4056 static guint32
4057 lookup_got_offset (MonoAotCompile *acfg, gboolean llvm, MonoJumpInfo *ji)
4059 guint32 got_offset;
4060 GotInfo *info = llvm ? &acfg->llvm_got_info : &acfg->got_info;
4062 got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (info->patch_to_got_offset_by_type [ji->type], ji));
4063 if (got_offset)
4064 return got_offset - 1;
4065 g_assert_not_reached ();
4069 * get_got_offset:
4071 * Returns the offset of the GOT slot where the runtime object resulting from resolving
4072 * JI could be found if it exists, otherwise allocates a new one.
4074 static guint32
4075 get_got_offset (MonoAotCompile *acfg, gboolean llvm, MonoJumpInfo *ji)
4077 guint32 got_offset;
4078 GotInfo *info = llvm ? &acfg->llvm_got_info : &acfg->got_info;
4080 got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (info->patch_to_got_offset_by_type [ji->type], ji));
4081 if (got_offset)
4082 return got_offset - 1;
4084 if (llvm) {
4085 got_offset = acfg->llvm_got_offset;
4086 acfg->llvm_got_offset ++;
4087 } else {
4088 got_offset = acfg->got_offset;
4089 acfg->got_offset ++;
4092 acfg->stats.got_slots ++;
4093 acfg->stats.got_slot_types [ji->type] ++;
4095 g_hash_table_insert (info->patch_to_got_offset, ji, GUINT_TO_POINTER (got_offset + 1));
4096 g_hash_table_insert (info->patch_to_got_offset_by_type [ji->type], ji, GUINT_TO_POINTER (got_offset + 1));
4097 g_ptr_array_add (info->got_patches, ji);
4099 return got_offset;
4102 /* Add a method to the list of methods which need to be emitted */
4103 static void
4104 add_method_with_index (MonoAotCompile *acfg, MonoMethod *method, int index, gboolean extra)
4106 g_assert (method);
4107 if (!g_hash_table_lookup (acfg->method_indexes, method)) {
4108 g_ptr_array_add (acfg->methods, method);
4109 g_hash_table_insert (acfg->method_indexes, method, GUINT_TO_POINTER (index + 1));
4110 acfg->nmethods = acfg->methods->len + 1;
4113 if (method->wrapper_type || extra) {
4114 int token = mono_metadata_token_index (method->token) - 1;
4115 if (token < 0)
4116 acfg->nextra_methods++;
4117 g_ptr_array_add (acfg->extra_methods, method);
4121 static gboolean
4122 prefer_gsharedvt_method (MonoAotCompile *acfg, MonoMethod *method)
4124 /* One instantiation with valuetypes is generated for each async method */
4125 if (m_class_get_image (method->klass) == mono_defaults.corlib && (!strcmp (m_class_get_name (method->klass), "AsyncMethodBuilderCore") || !strcmp (m_class_get_name (method->klass), "AsyncVoidMethodBuilder")))
4126 return TRUE;
4127 else
4128 return FALSE;
4131 static guint32
4132 get_method_index (MonoAotCompile *acfg, MonoMethod *method)
4134 int index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method));
4136 g_assert (index);
4138 return index - 1;
4141 static int
4142 add_method_full (MonoAotCompile *acfg, MonoMethod *method, gboolean extra, int depth)
4144 int index;
4146 index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method));
4147 if (index)
4148 return index - 1;
4150 index = acfg->method_index;
4151 add_method_with_index (acfg, method, index, extra);
4153 g_ptr_array_add (acfg->method_order, GUINT_TO_POINTER (index));
4155 g_hash_table_insert (acfg->method_depth, method, GUINT_TO_POINTER (depth));
4157 acfg->method_index ++;
4159 return index;
4162 static int
4163 add_method (MonoAotCompile *acfg, MonoMethod *method)
4165 return add_method_full (acfg, method, FALSE, 0);
4168 static void
4169 mono_dedup_cache_method (MonoAotCompile *acfg, MonoMethod *method)
4171 g_assert (acfg->dedup_stats);
4173 char *name = mono_aot_get_mangled_method_name (method);
4174 g_assert (name);
4176 // For stats
4177 char *stats_name = g_strdup (name);
4179 g_assert (acfg->dedup_cache);
4181 if (!g_hash_table_lookup (acfg->dedup_cache, name)) {
4182 // This AOTCompile owns this method
4183 // We do this to decide whether to write it to disk
4184 // during a dedup run (first phase, where we skip).
4186 // If never changed, then maybe can avoid a recompile
4187 // of the cache.
4189 // Files not read in during last phase.
4190 acfg->dedup_cache_changed = TRUE;
4192 // owns name
4193 g_hash_table_insert (acfg->dedup_cache, name, method);
4194 } else {
4195 // owns name
4196 g_free (name);
4199 guint count = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->dedup_stats, stats_name));
4200 count++;
4201 g_hash_table_insert (acfg->dedup_stats, stats_name, GUINT_TO_POINTER (count));
4204 static void
4205 add_extra_method_with_depth (MonoAotCompile *acfg, MonoMethod *method, int depth)
4207 ERROR_DECL (error);
4209 if (mono_method_is_generic_sharable_full (method, TRUE, TRUE, FALSE)) {
4210 method = mini_get_shared_method_full (method, SHARE_MODE_NONE, error);
4211 if (!is_ok (error)) {
4212 /* vtype constraint */
4213 mono_error_cleanup (error);
4214 return;
4216 } else if ((acfg->jit_opts & MONO_OPT_GSHAREDVT) && prefer_gsharedvt_method (acfg, method) && mono_method_is_generic_sharable_full (method, FALSE, FALSE, TRUE)) {
4217 /* Use the gsharedvt version */
4218 method = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
4219 mono_error_assert_ok (error);
4222 if ((acfg->aot_opts.dedup || acfg->aot_opts.dedup_include) && mono_aot_can_dedup (method)) {
4223 mono_dedup_cache_method (acfg, method);
4225 if (!acfg->dedup_emit_mode)
4226 return;
4229 if (acfg->aot_opts.log_generics)
4230 aot_printf (acfg, "%*sAdding method %s.\n", depth, "", mono_method_get_full_name (method));
4232 add_method_full (acfg, method, TRUE, depth);
4235 static void
4236 add_extra_method (MonoAotCompile *acfg, MonoMethod *method)
4238 add_extra_method_with_depth (acfg, method, 0);
4241 static void
4242 add_jit_icall_wrapper (MonoAotCompile *acfg, MonoJitICallInfo *callinfo)
4244 if (!callinfo->sig)
4245 return;
4247 g_assert (callinfo->name && callinfo->func);
4249 add_method (acfg, mono_marshal_get_icall_wrapper (callinfo, TRUE));
4252 #if ENABLE_LLVM
4254 static void
4255 add_lazy_init_wrappers (MonoAotCompile *acfg)
4257 add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD));
4258 add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD_GSHARED_MRGCTX));
4259 add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD_GSHARED_VTABLE));
4260 add_method (acfg, mono_marshal_get_aot_init_wrapper (AOT_INIT_METHOD_GSHARED_THIS));
4263 #endif
4265 static MonoMethod*
4266 get_runtime_invoke_sig (MonoMethodSignature *sig)
4268 MonoMethodBuilder *mb;
4269 MonoMethod *m;
4271 mb = mono_mb_new (mono_defaults.object_class, "FOO", MONO_WRAPPER_NONE);
4272 m = mono_mb_create_method (mb, sig, 16);
4273 MonoMethod *invoke = mono_marshal_get_runtime_invoke (m, FALSE);
4274 mono_mb_free (mb);
4275 return invoke;
4278 static MonoMethod*
4279 get_runtime_invoke (MonoAotCompile *acfg, MonoMethod *method, gboolean virtual_)
4281 return mono_marshal_get_runtime_invoke (method, virtual_);
4284 static gboolean
4285 can_marshal_struct (MonoClass *klass)
4287 MonoClassField *field;
4288 gboolean can_marshal = TRUE;
4289 gpointer iter = NULL;
4290 MonoMarshalType *info;
4291 int i;
4293 if (mono_class_is_auto_layout (klass))
4294 return FALSE;
4296 info = mono_marshal_load_type_info (klass);
4298 /* Only allow a few field types to avoid asserts in the marshalling code */
4299 while ((field = mono_class_get_fields_internal (klass, &iter))) {
4300 if ((field->type->attrs & FIELD_ATTRIBUTE_STATIC))
4301 continue;
4303 switch (field->type->type) {
4304 case MONO_TYPE_I4:
4305 case MONO_TYPE_U4:
4306 case MONO_TYPE_I1:
4307 case MONO_TYPE_U1:
4308 case MONO_TYPE_BOOLEAN:
4309 case MONO_TYPE_I2:
4310 case MONO_TYPE_U2:
4311 case MONO_TYPE_CHAR:
4312 case MONO_TYPE_I8:
4313 case MONO_TYPE_U8:
4314 case MONO_TYPE_I:
4315 case MONO_TYPE_U:
4316 case MONO_TYPE_PTR:
4317 case MONO_TYPE_R4:
4318 case MONO_TYPE_R8:
4319 case MONO_TYPE_STRING:
4320 break;
4321 case MONO_TYPE_VALUETYPE:
4322 if (!m_class_is_enumtype (mono_class_from_mono_type_internal (field->type)) && !can_marshal_struct (mono_class_from_mono_type_internal (field->type)))
4323 can_marshal = FALSE;
4324 break;
4325 case MONO_TYPE_SZARRAY: {
4326 gboolean has_mspec = FALSE;
4328 if (info) {
4329 for (i = 0; i < info->num_fields; ++i) {
4330 if (info->fields [i].field == field && info->fields [i].mspec)
4331 has_mspec = TRUE;
4334 if (!has_mspec)
4335 can_marshal = FALSE;
4336 break;
4338 default:
4339 can_marshal = FALSE;
4340 break;
4344 /* Special cases */
4345 /* Its hard to compute whenever these can be marshalled or not */
4346 if (!strcmp (m_class_get_name_space (klass), "System.Net.NetworkInformation.MacOsStructs") && strcmp (m_class_get_name (klass), "sockaddr_dl"))
4347 return TRUE;
4349 return can_marshal;
4352 static void
4353 create_gsharedvt_inst (MonoAotCompile *acfg, MonoMethod *method, MonoGenericContext *ctx)
4355 /* Create a vtype instantiation */
4356 MonoGenericContext shared_context;
4357 MonoType **args;
4358 MonoGenericInst *inst;
4359 MonoGenericContainer *container;
4360 MonoClass **constraints;
4361 int i;
4363 memset (ctx, 0, sizeof (MonoGenericContext));
4365 if (mono_class_is_gtd (method->klass)) {
4366 shared_context = mono_class_get_generic_container (method->klass)->context;
4367 inst = shared_context.class_inst;
4369 args = g_new0 (MonoType*, inst->type_argc);
4370 for (i = 0; i < inst->type_argc; ++i) {
4371 args [i] = mono_get_int_type ();
4373 ctx->class_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
4375 if (method->is_generic) {
4376 container = mono_method_get_generic_container (method);
4377 g_assert (!container->is_anonymous && container->is_method);
4378 shared_context = container->context;
4379 inst = shared_context.method_inst;
4381 args = g_new0 (MonoType*, inst->type_argc);
4382 for (i = 0; i < container->type_argc; ++i) {
4383 MonoGenericParamInfo *info = mono_generic_param_info (&container->type_params [i]);
4384 gboolean ref_only = FALSE;
4386 if (info && info->constraints) {
4387 constraints = info->constraints;
4389 while (*constraints) {
4390 MonoClass *cklass = *constraints;
4391 if (!(cklass == mono_defaults.object_class || (m_class_get_image (cklass) == mono_defaults.corlib && !strcmp (m_class_get_name (cklass), "ValueType"))))
4392 /* Inflaring the method with our vtype would not be valid */
4393 ref_only = TRUE;
4394 constraints ++;
4398 if (ref_only)
4399 args [i] = mono_get_object_type ();
4400 else
4401 args [i] = mono_get_int_type ();
4403 ctx->method_inst = mono_metadata_get_generic_inst (inst->type_argc, args);
4407 static void
4408 add_gc_wrappers (MonoAotCompile *acfg)
4410 MonoMethod *m;
4411 /* Managed Allocators */
4412 int nallocators = mono_gc_get_managed_allocator_types ();
4413 for (int i = 0; i < nallocators; ++i) {
4414 if ((m = mono_gc_get_managed_allocator_by_type (i, MANAGED_ALLOCATOR_REGULAR)))
4415 add_method (acfg, m);
4416 if ((m = mono_gc_get_managed_allocator_by_type (i, MANAGED_ALLOCATOR_SLOW_PATH)))
4417 add_method (acfg, m);
4418 if ((m = mono_gc_get_managed_allocator_by_type (i, MANAGED_ALLOCATOR_PROFILER)))
4419 add_method (acfg, m);
4422 /* write barriers */
4423 if (mono_gc_is_moving ()) {
4424 add_method (acfg, mono_gc_get_specific_write_barrier (FALSE));
4425 add_method (acfg, mono_gc_get_specific_write_barrier (TRUE));
4429 static gboolean
4430 contains_disable_reflection_attribute (MonoCustomAttrInfo *cattr)
4432 for (int i = 0; i < cattr->num_attrs; ++i) {
4433 MonoCustomAttrEntry *attr = &cattr->attrs [i];
4435 if (!attr->ctor)
4436 return FALSE;
4438 if (strcmp (m_class_get_name_space (attr->ctor->klass), "System.Runtime.CompilerServices"))
4439 return FALSE;
4441 if (strcmp (m_class_get_name (attr->ctor->klass), "DisablePrivateReflectionAttribute"))
4442 return FALSE;
4445 return TRUE;
4448 gboolean
4449 mono_aot_can_specialize (MonoMethod *method)
4451 if (!method)
4452 return FALSE;
4454 if (method->wrapper_type != MONO_WRAPPER_NONE)
4455 return FALSE;
4457 // If it's not private, we can't specialize
4458 if ((method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) != METHOD_ATTRIBUTE_PRIVATE)
4459 return FALSE;
4461 // If it has the attribute disabling the specialization, we can't specialize
4463 // Set by linker, indicates that the method can be found through reflection
4464 // and that call-site specialization shouldn't be done.
4466 // Important that this attribute is used for *nothing else*
4468 // If future authors make use of it (to disable more optimizations),
4469 // change this place to use a new attribute.
4470 ERROR_DECL (cattr_error);
4471 MonoCustomAttrInfo *cattr = mono_custom_attrs_from_class_checked (method->klass, cattr_error);
4473 if (!is_ok (cattr_error)) {
4474 mono_error_cleanup (cattr_error);
4475 goto cleanup_false;
4476 } else if (cattr && contains_disable_reflection_attribute (cattr)) {
4477 goto cleanup_true;
4480 cattr = mono_custom_attrs_from_method_checked (method, cattr_error);
4482 if (!is_ok (cattr_error)) {
4483 mono_error_cleanup (cattr_error);
4484 goto cleanup_false;
4485 } else if (cattr && contains_disable_reflection_attribute (cattr)) {
4486 goto cleanup_true;
4487 } else {
4488 goto cleanup_false;
4491 cleanup_false:
4492 if (cattr)
4493 mono_custom_attrs_free (cattr);
4494 return FALSE;
4496 cleanup_true:
4497 if (cattr)
4498 mono_custom_attrs_free (cattr);
4499 return TRUE;
4502 static gboolean
4503 always_aot (MonoMethod *method)
4506 * Calls to these methods do not go through the normal call processing code so
4507 * calling code cannot enter the interpreter. So always aot them even in profile guided aot mode.
4509 if (method->klass == mono_get_string_class () && (strstr (method->name, "memcpy") || strstr (method->name, "bzero")))
4510 return TRUE;
4511 if (method->string_ctor)
4512 return TRUE;
4513 return FALSE;
4516 gboolean
4517 mono_aot_can_enter_interp (MonoMethod *method)
4519 MonoAotCompile *acfg = current_acfg;
4521 g_assert (acfg);
4522 if (always_aot (method))
4523 return FALSE;
4524 if (acfg->aot_opts.profile_only && !g_hash_table_lookup (acfg->profile_methods, method))
4525 return TRUE;
4526 return FALSE;
4529 static void
4530 add_wrappers (MonoAotCompile *acfg)
4532 MonoMethod *method, *m;
4533 int i, j;
4534 MonoMethodSignature *sig, *csig;
4535 guint32 token;
4538 * FIXME: Instead of AOTing all the wrappers, it might be better to redesign them
4539 * so there is only one wrapper of a given type, or inlining their contents into their
4540 * callers.
4542 for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
4543 ERROR_DECL (error);
4544 MonoMethod *method;
4545 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
4546 gboolean skip = FALSE;
4548 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
4549 report_loader_error (acfg, error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (error));
4551 if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
4552 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
4553 (method->flags & METHOD_ATTRIBUTE_ABSTRACT))
4554 skip = TRUE;
4556 /* Skip methods which can not be handled by get_runtime_invoke () */
4557 sig = mono_method_signature_internal (method);
4558 if (!sig)
4559 continue;
4560 if ((sig->ret->type == MONO_TYPE_PTR) ||
4561 (sig->ret->type == MONO_TYPE_TYPEDBYREF))
4562 skip = TRUE;
4563 if (mono_class_is_open_constructed_type (sig->ret))
4564 skip = TRUE;
4566 for (j = 0; j < sig->param_count; j++) {
4567 if (sig->params [j]->type == MONO_TYPE_TYPEDBYREF)
4568 skip = TRUE;
4569 if (mono_class_is_open_constructed_type (sig->params [j]))
4570 skip = TRUE;
4573 #ifdef MONO_ARCH_DYN_CALL_SUPPORTED
4574 if (!mono_class_is_contextbound (method->klass)) {
4575 MonoDynCallInfo *info = mono_arch_dyn_call_prepare (sig);
4576 gboolean has_nullable = FALSE;
4578 for (j = 0; j < sig->param_count; j++) {
4579 if (sig->params [j]->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (sig->params [j])))
4580 has_nullable = TRUE;
4583 if (info && !has_nullable && !acfg->aot_opts.llvm_only) {
4584 /* Supported by the dynamic runtime-invoke wrapper */
4585 skip = TRUE;
4587 if (info)
4588 mono_arch_dyn_call_free (info);
4590 #endif
4592 if (acfg->aot_opts.llvm_only)
4593 /* Supported by the gsharedvt based runtime-invoke wrapper */
4594 skip = TRUE;
4596 if (!skip) {
4597 //printf ("%s\n", mono_method_full_name (method, TRUE));
4598 add_method (acfg, get_runtime_invoke (acfg, method, FALSE));
4602 if (mono_is_corlib_image (acfg->image->assembly->image)) {
4603 /* Runtime invoke wrappers */
4605 MonoType *void_type = mono_get_void_type ();
4606 MonoType *string_type = m_class_get_byval_arg (mono_defaults.string_class);
4608 /* void runtime-invoke () [.cctor] */
4609 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
4610 csig->ret = void_type;
4611 add_method (acfg, get_runtime_invoke_sig (csig));
4613 /* void runtime-invoke () [Finalize] */
4614 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
4615 csig->hasthis = 1;
4616 csig->ret = void_type;
4617 add_method (acfg, get_runtime_invoke_sig (csig));
4619 /* void runtime-invoke (string) [exception ctor] */
4620 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
4621 csig->hasthis = 1;
4622 csig->ret = void_type;
4623 csig->params [0] = string_type;
4624 add_method (acfg, get_runtime_invoke_sig (csig));
4626 /* void runtime-invoke (string, string) [exception ctor] */
4627 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
4628 csig->hasthis = 1;
4629 csig->ret = void_type;
4630 csig->params [0] = string_type;
4631 csig->params [1] = string_type;
4632 add_method (acfg, get_runtime_invoke_sig (csig));
4634 /* string runtime-invoke () [Exception.ToString ()] */
4635 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
4636 csig->hasthis = 1;
4637 csig->ret = string_type;
4638 add_method (acfg, get_runtime_invoke_sig (csig));
4640 /* void runtime-invoke (string, Exception) [exception ctor] */
4641 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
4642 csig->hasthis = 1;
4643 csig->ret = void_type;
4644 csig->params [0] = string_type;
4645 csig->params [1] = m_class_get_byval_arg (mono_defaults.exception_class);
4646 add_method (acfg, get_runtime_invoke_sig (csig));
4648 /* Assembly runtime-invoke (string, Assembly, bool) [DoAssemblyResolve] */
4649 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
4650 csig->hasthis = 1;
4651 csig->ret = m_class_get_byval_arg (mono_class_load_from_name (mono_defaults.corlib, "System.Reflection", "Assembly"));
4652 csig->params [0] = string_type;
4653 csig->params [1] = m_class_get_byval_arg (mono_class_load_from_name (mono_defaults.corlib, "System.Reflection", "Assembly"));
4654 csig->params [2] = m_class_get_byval_arg (mono_defaults.boolean_class);
4655 add_method (acfg, get_runtime_invoke_sig (csig));
4657 /* runtime-invoke used by finalizers */
4658 add_method (acfg, get_runtime_invoke (acfg, get_method_nofail (mono_defaults.object_class, "Finalize", 0, 0), TRUE));
4660 /* This is used by mono_runtime_capture_context () */
4661 method = mono_get_context_capture_method ();
4662 if (method)
4663 add_method (acfg, get_runtime_invoke (acfg, method, FALSE));
4665 #ifdef MONO_ARCH_DYN_CALL_SUPPORTED
4666 if (!acfg->aot_opts.llvm_only)
4667 add_method (acfg, mono_marshal_get_runtime_invoke_dynamic ());
4668 #endif
4670 /* These are used by mono_jit_runtime_invoke () to calls gsharedvt out wrappers */
4671 if (acfg->aot_opts.llvm_only) {
4672 int variants;
4674 /* Create simplified signatures which match the signature used by the gsharedvt out wrappers */
4675 for (variants = 0; variants < 4; ++variants) {
4676 for (i = 0; i < 40; ++i) {
4677 sig = mini_get_gsharedvt_out_sig_wrapper_signature ((variants & 1) > 0, (variants & 2) > 0, i);
4678 add_extra_method (acfg, mono_marshal_get_runtime_invoke_for_sig (sig));
4680 g_free (sig);
4685 /* stelemref */
4686 add_method (acfg, mono_marshal_get_stelemref ());
4688 add_gc_wrappers (acfg);
4690 /* Stelemref wrappers */
4692 MonoMethod **wrappers;
4693 int nwrappers;
4695 wrappers = mono_marshal_get_virtual_stelemref_wrappers (&nwrappers);
4696 for (i = 0; i < nwrappers; ++i)
4697 add_method (acfg, wrappers [i]);
4698 g_free (wrappers);
4701 /* castclass_with_check wrapper */
4702 add_method (acfg, mono_marshal_get_castclass_with_cache ());
4703 /* isinst_with_check wrapper */
4704 add_method (acfg, mono_marshal_get_isinst_with_cache ());
4706 /* JIT icall wrappers */
4707 /* FIXME: locking - this is "safe" as full-AOT threads don't mutate the icall data */
4708 for (int i = 0; i < MONO_JIT_ICALL_count; ++i)
4709 add_jit_icall_wrapper (acfg, mono_find_jit_icall_info ((MonoJitICallId)i));
4713 * remoting-invoke-with-check wrappers are very frequent, so avoid emitting them,
4714 * we use the original method instead at runtime.
4715 * Since full-aot doesn't support remoting, this is not a problem.
4717 #if 0
4718 /* remoting-invoke wrappers */
4719 for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
4720 ERROR_DECL (error);
4721 MonoMethodSignature *sig;
4723 token = MONO_TOKEN_METHOD_DEF | (i + 1);
4724 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
4725 g_assert (is_ok (error)); /* FIXME don't swallow the error */
4727 sig = mono_method_signature_internal (method);
4729 if (sig->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) {
4730 m = mono_marshal_get_remoting_invoke_with_check (method);
4732 add_method (acfg, m);
4735 #endif
4737 /* delegate-invoke wrappers */
4738 for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) {
4739 ERROR_DECL (error);
4740 MonoClass *klass;
4742 token = MONO_TOKEN_TYPE_DEF | (i + 1);
4743 klass = mono_class_get_checked (acfg->image, token, error);
4745 if (!klass) {
4746 mono_error_cleanup (error);
4747 continue;
4750 if (!m_class_is_delegate (klass) || klass == mono_defaults.delegate_class || klass == mono_defaults.multicastdelegate_class)
4751 continue;
4753 if (!mono_class_is_gtd (klass)) {
4754 method = mono_get_delegate_invoke_internal (klass);
4756 m = mono_marshal_get_delegate_invoke (method, NULL);
4758 add_method (acfg, m);
4760 method = try_get_method_nofail (klass, "BeginInvoke", -1, 0);
4761 if (method)
4762 add_method (acfg, mono_marshal_get_delegate_begin_invoke (method));
4764 method = try_get_method_nofail (klass, "EndInvoke", -1, 0);
4765 if (method)
4766 add_method (acfg, mono_marshal_get_delegate_end_invoke (method));
4768 MonoCustomAttrInfo *cattr;
4769 cattr = mono_custom_attrs_from_class_checked (klass, error);
4770 if (!is_ok (error)) {
4771 mono_error_cleanup (error);
4772 g_assert (!cattr);
4773 continue;
4776 if (cattr) {
4777 int j;
4779 for (j = 0; j < cattr->num_attrs; ++j)
4780 if (cattr->attrs [j].ctor && (!strcmp (m_class_get_name (cattr->attrs [j].ctor->klass), "MonoNativeFunctionWrapperAttribute") || !strcmp (m_class_get_name (cattr->attrs [j].ctor->klass), "UnmanagedFunctionPointerAttribute")))
4781 break;
4782 if (j < cattr->num_attrs) {
4783 MonoMethod *invoke;
4784 MonoMethod *wrapper;
4785 MonoMethod *del_invoke;
4787 /* Add wrappers needed by mono_ftnptr_to_delegate () */
4788 invoke = mono_get_delegate_invoke_internal (klass);
4789 wrapper = mono_marshal_get_native_func_wrapper_aot (klass);
4790 del_invoke = mono_marshal_get_delegate_invoke_internal (invoke, FALSE, TRUE, wrapper);
4791 add_method (acfg, wrapper);
4792 add_method (acfg, del_invoke);
4794 mono_custom_attrs_free (cattr);
4796 } else if ((acfg->jit_opts & MONO_OPT_GSHAREDVT) && mono_class_is_gtd (klass)) {
4797 ERROR_DECL (error);
4798 MonoGenericContext ctx;
4799 MonoMethod *inst, *gshared;
4802 * Emit gsharedvt versions of the generic delegate-invoke wrappers
4804 /* Invoke */
4805 method = mono_get_delegate_invoke_internal (klass);
4806 create_gsharedvt_inst (acfg, method, &ctx);
4808 inst = mono_class_inflate_generic_method_checked (method, &ctx, error);
4809 g_assert (is_ok (error)); /* FIXME don't swallow the error */
4811 m = mono_marshal_get_delegate_invoke (inst, NULL);
4812 g_assert (m->is_inflated);
4814 gshared = mini_get_shared_method_full (m, SHARE_MODE_GSHAREDVT, error);
4815 mono_error_assert_ok (error);
4817 add_extra_method (acfg, gshared);
4819 /* begin-invoke */
4820 method = mono_get_delegate_begin_invoke_internal (klass);
4821 if (method) {
4822 create_gsharedvt_inst (acfg, method, &ctx);
4824 inst = mono_class_inflate_generic_method_checked (method, &ctx, error);
4825 g_assert (is_ok (error)); /* FIXME don't swallow the error */
4827 m = mono_marshal_get_delegate_begin_invoke (inst);
4828 g_assert (m->is_inflated);
4830 gshared = mini_get_shared_method_full (m, SHARE_MODE_GSHAREDVT, error);
4831 mono_error_assert_ok (error);
4833 add_extra_method (acfg, gshared);
4836 /* end-invoke */
4837 method = mono_get_delegate_end_invoke_internal (klass);
4838 if (method) {
4839 create_gsharedvt_inst (acfg, method, &ctx);
4841 inst = mono_class_inflate_generic_method_checked (method, &ctx, error);
4842 g_assert (is_ok (error)); /* FIXME don't swallow the error */
4844 m = mono_marshal_get_delegate_end_invoke (inst);
4845 g_assert (m->is_inflated);
4847 gshared = mini_get_shared_method_full (m, SHARE_MODE_GSHAREDVT, error);
4848 mono_error_assert_ok (error);
4850 add_extra_method (acfg, gshared);
4855 /* array access wrappers */
4856 for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) {
4857 ERROR_DECL (error);
4858 MonoClass *klass;
4860 token = MONO_TOKEN_TYPE_SPEC | (i + 1);
4861 klass = mono_class_get_checked (acfg->image, token, error);
4863 if (!klass) {
4864 mono_error_cleanup (error);
4865 continue;
4868 if (m_class_get_rank (klass) && MONO_TYPE_IS_PRIMITIVE (m_class_get_byval_arg (m_class_get_element_class (klass)))) {
4869 MonoMethod *m, *wrapper;
4871 /* Add runtime-invoke wrappers too */
4873 m = get_method_nofail (klass, "Get", -1, 0);
4874 g_assert (m);
4875 wrapper = mono_marshal_get_array_accessor_wrapper (m);
4876 add_extra_method (acfg, wrapper);
4877 if (!acfg->aot_opts.llvm_only)
4878 add_extra_method (acfg, get_runtime_invoke (acfg, wrapper, FALSE));
4880 m = get_method_nofail (klass, "Set", -1, 0);
4881 g_assert (m);
4882 wrapper = mono_marshal_get_array_accessor_wrapper (m);
4883 add_extra_method (acfg, wrapper);
4884 if (!acfg->aot_opts.llvm_only)
4885 add_extra_method (acfg, get_runtime_invoke (acfg, wrapper, FALSE));
4889 /* Synchronized wrappers */
4890 for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
4891 ERROR_DECL (error);
4892 token = MONO_TOKEN_METHOD_DEF | (i + 1);
4893 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
4894 report_loader_error (acfg, error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (error));
4896 if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) {
4897 if (method->is_generic) {
4898 // FIXME:
4899 } else if ((acfg->jit_opts & MONO_OPT_GSHAREDVT) && mono_class_is_gtd (method->klass)) {
4900 ERROR_DECL (error);
4901 MonoGenericContext ctx;
4902 MonoMethod *inst, *gshared, *m;
4905 * Create a generic wrapper for a generic instance, and AOT that.
4907 create_gsharedvt_inst (acfg, method, &ctx);
4908 inst = mono_class_inflate_generic_method_checked (method, &ctx, error);
4909 g_assert (is_ok (error)); /* FIXME don't swallow the error */
4910 m = mono_marshal_get_synchronized_wrapper (inst);
4911 g_assert (m->is_inflated);
4912 gshared = mini_get_shared_method_full (m, SHARE_MODE_GSHAREDVT, error);
4913 mono_error_assert_ok (error);
4915 add_method (acfg, gshared);
4916 } else {
4917 add_method (acfg, mono_marshal_get_synchronized_wrapper (method));
4922 /* pinvoke wrappers */
4923 for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
4924 ERROR_DECL (error);
4925 MonoMethod *method;
4926 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
4928 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
4929 report_loader_error (acfg, error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (error));
4931 if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
4932 (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) {
4933 add_method (acfg, mono_marshal_get_native_wrapper (method, TRUE, TRUE));
4936 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
4937 if (acfg->aot_opts.llvm_only) {
4938 /* The wrappers have a different signature (hasthis is not set) so need to add this too */
4939 add_gsharedvt_wrappers (acfg, mono_method_signature_internal (method), FALSE, TRUE, FALSE);
4944 /* native-to-managed wrappers */
4945 for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
4946 ERROR_DECL (error);
4947 MonoMethod *method;
4948 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
4949 MonoCustomAttrInfo *cattr;
4950 int j;
4952 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
4953 report_loader_error (acfg, error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (error));
4956 * Only generate native-to-managed wrappers for methods which have an
4957 * attribute named MonoPInvokeCallbackAttribute. We search for the attribute by
4958 * name to avoid defining a new assembly to contain it.
4960 cattr = mono_custom_attrs_from_method_checked (method, error);
4961 if (!is_ok (error)) {
4962 char *name = mono_method_get_full_name (method);
4963 report_loader_error (acfg, error, TRUE, "Failed to load custom attributes from method %s due to %s\n", name, mono_error_get_message (error));
4964 g_free (name);
4967 if (cattr) {
4968 for (j = 0; j < cattr->num_attrs; ++j)
4969 if (cattr->attrs [j].ctor && !strcmp (m_class_get_name (cattr->attrs [j].ctor->klass), "MonoPInvokeCallbackAttribute"))
4970 break;
4971 if (j < cattr->num_attrs) {
4972 MonoCustomAttrEntry *e = &cattr->attrs [j];
4973 MonoMethodSignature *sig = mono_method_signature_internal (e->ctor);
4974 const char *p = (const char*)e->data;
4975 const char *named;
4976 int slen, num_named, named_type;
4977 char *n;
4978 MonoType *t;
4979 MonoClass *klass;
4980 char *export_name = NULL;
4981 MonoMethod *wrapper;
4983 /* this cannot be enforced by the C# compiler so we must give the user some warning before aborting */
4984 if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) {
4985 g_warning ("AOT restriction: Method '%s' must be static since it is decorated with [MonoPInvokeCallback]. See https://docs.microsoft.com/xamarin/ios/internals/limitations#reverse-callbacks",
4986 mono_method_full_name (method, TRUE));
4987 exit (1);
4990 g_assert (sig->param_count == 1);
4991 g_assert (sig->params [0]->type == MONO_TYPE_CLASS && !strcmp (m_class_get_name (mono_class_from_mono_type_internal (sig->params [0])), "Type"));
4994 * Decode the cattr manually since we can't create objects
4995 * during aot compilation.
4998 /* Skip prolog */
4999 p += 2;
5001 /* From load_cattr_value () in reflection.c */
5002 slen = mono_metadata_decode_value (p, &p);
5003 n = (char *)g_memdup (p, slen + 1);
5004 n [slen] = 0;
5005 t = mono_reflection_type_from_name_checked (n, mono_domain_ambient_alc (mono_domain_get ()), acfg->image, error);
5006 g_assert (t);
5007 mono_error_assert_ok (error);
5008 g_free (n);
5010 klass = mono_class_from_mono_type_internal (t);
5011 g_assert (m_class_get_parent (klass) == mono_defaults.multicastdelegate_class);
5013 p += slen;
5015 num_named = read16 (p);
5016 p += 2;
5018 g_assert (num_named < 2);
5019 if (num_named == 1) {
5020 int name_len;
5021 char *name;
5023 /* parse ExportSymbol attribute */
5024 named = p;
5025 named_type = *named;
5026 named += 1;
5027 /* data_type = *named; */
5028 named += 1;
5030 name_len = mono_metadata_decode_blob_size (named, &named);
5031 name = (char *)g_malloc (name_len + 1);
5032 memcpy (name, named, name_len);
5033 name [name_len] = 0;
5034 named += name_len;
5036 g_assert (named_type == 0x54);
5037 g_assert (!strcmp (name, "ExportSymbol"));
5039 /* load_cattr_value (), string case */
5040 MONO_DISABLE_WARNING (4310) // cast truncates constant value
5041 g_assert (*named != (char)0xFF);
5042 MONO_RESTORE_WARNING
5043 slen = mono_metadata_decode_value (named, &named);
5044 export_name = (char *)g_malloc (slen + 1);
5045 memcpy (export_name, named, slen);
5046 export_name [slen] = 0;
5047 named += slen;
5050 wrapper = mono_marshal_get_managed_wrapper (method, klass, 0, error);
5051 mono_error_assert_ok (error);
5053 add_method (acfg, wrapper);
5054 if (export_name)
5055 g_hash_table_insert (acfg->export_names, wrapper, export_name);
5057 g_free (cattr);
5060 if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
5061 (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) {
5062 add_method (acfg, mono_marshal_get_native_wrapper (method, TRUE, TRUE));
5066 /* StructureToPtr/PtrToStructure wrappers */
5067 for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) {
5068 ERROR_DECL (error);
5069 MonoClass *klass;
5071 token = MONO_TOKEN_TYPE_DEF | (i + 1);
5072 klass = mono_class_get_checked (acfg->image, token, error);
5074 if (!klass) {
5075 mono_error_cleanup (error);
5076 continue;
5079 if (m_class_is_valuetype (klass) && !mono_class_is_gtd (klass) && can_marshal_struct (klass) &&
5080 !(m_class_get_nested_in (klass) && strstr (m_class_get_name (m_class_get_nested_in (klass)), "<PrivateImplementationDetails>") == m_class_get_name (m_class_get_nested_in (klass)))) {
5081 add_method (acfg, mono_marshal_get_struct_to_ptr (klass));
5082 add_method (acfg, mono_marshal_get_ptr_to_struct (klass));
5087 static gboolean
5088 has_type_vars (MonoClass *klass)
5090 if ((m_class_get_byval_arg (klass)->type == MONO_TYPE_VAR) || (m_class_get_byval_arg (klass)->type == MONO_TYPE_MVAR))
5091 return TRUE;
5092 if (m_class_get_rank (klass))
5093 return has_type_vars (m_class_get_element_class (klass));
5094 if (mono_class_is_ginst (klass)) {
5095 MonoGenericContext *context = &mono_class_get_generic_class (klass)->context;
5096 if (context->class_inst) {
5097 int i;
5099 for (i = 0; i < context->class_inst->type_argc; ++i)
5100 if (has_type_vars (mono_class_from_mono_type_internal (context->class_inst->type_argv [i])))
5101 return TRUE;
5104 if (mono_class_is_gtd (klass))
5105 return TRUE;
5106 return FALSE;
5109 static gboolean
5110 is_vt_inst (MonoGenericInst *inst)
5112 int i;
5114 for (i = 0; i < inst->type_argc; ++i) {
5115 MonoType *t = inst->type_argv [i];
5116 if (MONO_TYPE_ISSTRUCT (t) || t->type == MONO_TYPE_VALUETYPE)
5117 return TRUE;
5119 return FALSE;
5122 static gboolean
5123 method_has_type_vars (MonoMethod *method)
5125 if (has_type_vars (method->klass))
5126 return TRUE;
5128 if (method->is_inflated) {
5129 MonoGenericContext *context = mono_method_get_context (method);
5130 if (context->method_inst) {
5131 int i;
5133 for (i = 0; i < context->method_inst->type_argc; ++i)
5134 if (has_type_vars (mono_class_from_mono_type_internal (context->method_inst->type_argv [i])))
5135 return TRUE;
5138 return FALSE;
5141 static
5142 gboolean mono_aot_mode_is_full (MonoAotOptions *opts)
5144 return opts->mode == MONO_AOT_MODE_FULL;
5147 static
5148 gboolean mono_aot_mode_is_interp (MonoAotOptions *opts)
5150 return opts->interp;
5153 static
5154 gboolean mono_aot_mode_is_hybrid (MonoAotOptions *opts)
5156 return opts->mode == MONO_AOT_MODE_HYBRID;
5159 static void add_generic_class_with_depth (MonoAotCompile *acfg, MonoClass *klass, int depth, const char *ref);
5161 static void
5162 add_generic_class (MonoAotCompile *acfg, MonoClass *klass, gboolean force, const char *ref)
5164 /* This might lead to a huge code blowup so only do it if neccesary */
5165 if (!mono_aot_mode_is_full (&acfg->aot_opts) && !mono_aot_mode_is_hybrid (&acfg->aot_opts) && !force)
5166 return;
5168 add_generic_class_with_depth (acfg, klass, 0, ref);
5171 static gboolean
5172 check_type_depth (MonoType *t, int depth)
5174 int i;
5176 if (depth > 8)
5177 return TRUE;
5179 switch (t->type) {
5180 case MONO_TYPE_GENERICINST: {
5181 MonoGenericClass *gklass = t->data.generic_class;
5182 MonoGenericInst *ginst = gklass->context.class_inst;
5184 if (ginst) {
5185 for (i = 0; i < ginst->type_argc; ++i) {
5186 if (check_type_depth (ginst->type_argv [i], depth + 1))
5187 return TRUE;
5190 break;
5192 default:
5193 break;
5196 return FALSE;
5199 static void
5200 add_types_from_method_header (MonoAotCompile *acfg, MonoMethod *method);
5202 static gboolean
5203 inst_has_vtypes (MonoGenericInst *inst)
5205 for (int i = 0; i < inst->type_argc; ++i) {
5206 MonoType *t = inst->type_argv [i];
5207 if (MONO_TYPE_ISSTRUCT (t))
5208 return TRUE;
5210 return FALSE;
5214 * add_generic_class:
5216 * Add all methods of a generic class.
5218 static void
5219 add_generic_class_with_depth (MonoAotCompile *acfg, MonoClass *klass, int depth, const char *ref)
5221 MonoMethod *method;
5222 MonoClassField *field;
5223 gpointer iter;
5224 gboolean use_gsharedvt = FALSE;
5225 gboolean use_gsharedvt_for_array = FALSE;
5227 if (!acfg->ginst_hash)
5228 acfg->ginst_hash = g_hash_table_new (NULL, NULL);
5230 mono_class_init_internal (klass);
5232 if (mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->context.class_inst->is_open)
5233 return;
5235 if (has_type_vars (klass))
5236 return;
5238 if (!mono_class_is_ginst (klass) && !m_class_get_rank (klass))
5239 return;
5241 if (mono_class_has_failure (klass))
5242 return;
5244 if (!acfg->ginst_hash)
5245 acfg->ginst_hash = g_hash_table_new (NULL, NULL);
5247 if (g_hash_table_lookup (acfg->ginst_hash, klass))
5248 return;
5250 if (check_type_depth (m_class_get_byval_arg (klass), 0))
5251 return;
5253 if (acfg->aot_opts.log_generics) {
5254 char *s = mono_type_full_name (m_class_get_byval_arg (klass));
5255 aot_printf (acfg, "%*sAdding generic instance %s [%s].\n", depth, "", s, ref);
5256 g_free (s);
5259 g_hash_table_insert (acfg->ginst_hash, klass, klass);
5262 * Use gsharedvt for generic collections with vtype arguments to avoid code blowup.
5263 * Enable this only for some classes since gsharedvt might not support all methods.
5265 if ((acfg->jit_opts & MONO_OPT_GSHAREDVT) && m_class_get_image (klass) == mono_defaults.corlib && mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->context.class_inst && is_vt_inst (mono_class_get_generic_class (klass)->context.class_inst) &&
5266 (!strcmp (m_class_get_name (klass), "Dictionary`2") || !strcmp (m_class_get_name (klass), "List`1") || !strcmp (m_class_get_name (klass), "ReadOnlyCollection`1")))
5267 use_gsharedvt = TRUE;
5269 #ifdef TARGET_WASM
5271 * Use gsharedvt for instances with vtype arguments.
5272 * WASM only since other platforms depend on the
5273 * previous behavior.
5275 if ((acfg->jit_opts & MONO_OPT_GSHAREDVT) && mono_class_is_ginst (klass) && mono_class_get_generic_class (klass)->context.class_inst && is_vt_inst (mono_class_get_generic_class (klass)->context.class_inst)) {
5276 use_gsharedvt = TRUE;
5277 use_gsharedvt_for_array = TRUE;
5279 #endif
5281 iter = NULL;
5282 while ((method = mono_class_get_methods (klass, &iter))) {
5283 if ((acfg->jit_opts & MONO_OPT_GSHAREDVT) && method->is_inflated && mono_method_get_context (method)->method_inst) {
5285 * This is partial sharing, and we can't handle it yet
5287 continue;
5290 if (mono_method_is_generic_sharable_full (method, FALSE, FALSE, use_gsharedvt)) {
5291 /* Already added */
5292 add_types_from_method_header (acfg, method);
5293 continue;
5296 if (method->is_generic)
5297 /* FIXME: */
5298 continue;
5301 * FIXME: Instances which are referenced by these methods are not added,
5302 * for example Array.Resize<int> for List<int>.Add ().
5304 add_extra_method_with_depth (acfg, method, depth + 1);
5307 iter = NULL;
5308 while ((field = mono_class_get_fields_internal (klass, &iter))) {
5309 if (field->type->type == MONO_TYPE_GENERICINST)
5310 add_generic_class_with_depth (acfg, mono_class_from_mono_type_internal (field->type), depth + 1, "field");
5313 if (m_class_is_delegate (klass)) {
5314 method = mono_get_delegate_invoke_internal (klass);
5316 method = mono_marshal_get_delegate_invoke (method, NULL);
5318 if (acfg->aot_opts.log_generics)
5319 aot_printf (acfg, "%*sAdding method %s.\n", depth, "", mono_method_get_full_name (method));
5321 add_method (acfg, method);
5324 /* Add superclasses */
5325 if (m_class_get_parent (klass))
5326 add_generic_class_with_depth (acfg, m_class_get_parent (klass), depth, "parent");
5328 const char *klass_name = m_class_get_name (klass);
5329 const char *klass_name_space = m_class_get_name_space (klass);
5330 const gboolean in_corlib = m_class_get_image (klass) == mono_defaults.corlib;
5332 * For ICollection<T>, add instances of the helper methods
5333 * in Array, since a T[] could be cast to ICollection<T>.
5335 if (in_corlib && !strcmp (klass_name_space, "System.Collections.Generic") &&
5336 (!strcmp(klass_name, "ICollection`1") || !strcmp (klass_name, "IEnumerable`1") || !strcmp (klass_name, "IList`1") || !strcmp (klass_name, "IEnumerator`1") || !strcmp (klass_name, "IReadOnlyList`1"))) {
5337 MonoClass *tclass = mono_class_from_mono_type_internal (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]);
5338 MonoClass *array_class = mono_class_create_bounded_array (tclass, 1, FALSE);
5339 gpointer iter;
5340 char *name_prefix;
5342 if (!strcmp (klass_name, "IEnumerator`1"))
5343 name_prefix = g_strdup_printf ("%s.%s", klass_name_space, "IEnumerable`1");
5344 else
5345 name_prefix = g_strdup_printf ("%s.%s", klass_name_space, klass_name);
5347 #ifndef ENABLE_NETCORE
5348 /* Add the T[]/InternalEnumerator class */
5349 if (!strcmp (klass_name, "IEnumerable`1") || !strcmp (klass_name, "IEnumerator`1")) {
5350 ERROR_DECL (error);
5351 MonoClass *nclass;
5353 iter = NULL;
5354 while ((nclass = mono_class_get_nested_types (m_class_get_parent (array_class), &iter))) {
5355 if (!strcmp (m_class_get_name (nclass), "InternalEnumerator`1"))
5356 break;
5358 g_assert (nclass);
5359 nclass = mono_class_inflate_generic_class_checked (nclass, mono_generic_class_get_context (mono_class_get_generic_class (klass)), error);
5360 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5361 add_generic_class (acfg, nclass, FALSE, "ICollection<T>");
5363 #endif
5365 iter = NULL;
5366 while ((method = mono_class_get_methods (array_class, &iter))) {
5367 if (!strncmp (method->name, name_prefix, strlen (name_prefix))) {
5368 MonoMethod *m = mono_aot_get_array_helper_from_wrapper (method);
5370 if (m->is_inflated && !mono_method_is_generic_sharable_full (m, FALSE, FALSE, use_gsharedvt_for_array))
5371 add_extra_method_with_depth (acfg, m, depth);
5375 g_free (name_prefix);
5378 /* Add an instance of GenericComparer<T> which is created dynamically by Comparer<T> */
5379 if (in_corlib && !strcmp (klass_name_space, "System.Collections.Generic") && !strcmp (klass_name, "Comparer`1")) {
5380 ERROR_DECL (error);
5381 MonoClass *tclass = mono_class_from_mono_type_internal (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]);
5382 MonoClass *icomparable, *gcomparer, *icomparable_inst;
5383 MonoGenericContext ctx;
5385 memset (&ctx, 0, sizeof (ctx));
5387 icomparable = mono_class_load_from_name (mono_defaults.corlib, "System", "IComparable`1");
5389 MonoType *args [ ] = { m_class_get_byval_arg (tclass) };
5390 ctx.class_inst = mono_metadata_get_generic_inst (1, args);
5392 icomparable_inst = mono_class_inflate_generic_class_checked (icomparable, &ctx, error);
5393 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5395 if (mono_class_is_assignable_from_internal (icomparable_inst, tclass)) {
5396 MonoClass *gcomparer_inst;
5397 gcomparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "GenericComparer`1");
5398 gcomparer_inst = mono_class_inflate_generic_class_checked (gcomparer, &ctx, error);
5399 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5401 add_generic_class (acfg, gcomparer_inst, FALSE, "Comparer<T>");
5405 /* Add an instance of GenericEqualityComparer<T> which is created dynamically by EqualityComparer<T> */
5406 if (in_corlib && !strcmp (klass_name_space, "System.Collections.Generic") && !strcmp (klass_name, "EqualityComparer`1")) {
5407 ERROR_DECL (error);
5408 MonoClass *tclass = mono_class_from_mono_type_internal (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]);
5409 MonoClass *iface, *gcomparer, *iface_inst;
5410 MonoGenericContext ctx;
5412 memset (&ctx, 0, sizeof (ctx));
5414 iface = mono_class_load_from_name (mono_defaults.corlib, "System", "IEquatable`1");
5415 g_assert (iface);
5416 MonoType *args [ ] = { m_class_get_byval_arg (tclass) };
5417 ctx.class_inst = mono_metadata_get_generic_inst (1, args);
5419 iface_inst = mono_class_inflate_generic_class_checked (iface, &ctx, error);
5420 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5422 if (mono_class_is_assignable_from_internal (iface_inst, tclass)) {
5423 MonoClass *gcomparer_inst;
5424 ERROR_DECL (error);
5426 gcomparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "GenericEqualityComparer`1");
5427 gcomparer_inst = mono_class_inflate_generic_class_checked (gcomparer, &ctx, error);
5428 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5429 add_generic_class (acfg, gcomparer_inst, FALSE, "EqualityComparer<T>");
5433 /* Add an instance of EnumComparer<T> which is created dynamically by EqualityComparer<T> for enums */
5434 if (in_corlib && !strcmp (klass_name_space, "System.Collections.Generic") && !strcmp (klass_name, "EqualityComparer`1")) {
5435 MonoClass *enum_comparer;
5436 MonoClass *tclass = mono_class_from_mono_type_internal (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]);
5437 MonoGenericContext ctx;
5439 if (m_class_is_enumtype (tclass)) {
5440 MonoClass *enum_comparer_inst;
5441 ERROR_DECL (error);
5443 memset (&ctx, 0, sizeof (ctx));
5444 MonoType *args [ ] = { m_class_get_byval_arg (tclass) };
5445 ctx.class_inst = mono_metadata_get_generic_inst (1, args);
5447 enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "EnumEqualityComparer`1");
5448 enum_comparer_inst = mono_class_inflate_generic_class_checked (enum_comparer, &ctx, error);
5449 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5450 add_generic_class (acfg, enum_comparer_inst, FALSE, "EqualityComparer<T>");
5454 /* Add an instance of ObjectComparer<T> which is created dynamically by Comparer<T> for enums */
5455 if (in_corlib && !strcmp (klass_name_space, "System.Collections.Generic") && !strcmp (klass_name, "Comparer`1")) {
5456 MonoClass *comparer;
5457 MonoClass *tclass = mono_class_from_mono_type_internal (mono_class_get_generic_class (klass)->context.class_inst->type_argv [0]);
5458 MonoGenericContext ctx;
5460 if (m_class_is_enumtype (tclass)) {
5461 MonoClass *comparer_inst;
5462 ERROR_DECL (error);
5464 memset (&ctx, 0, sizeof (ctx));
5465 MonoType *args [ ] = { m_class_get_byval_arg (tclass) };
5466 ctx.class_inst = mono_metadata_get_generic_inst (1, args);
5468 comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "ObjectComparer`1");
5469 comparer_inst = mono_class_inflate_generic_class_checked (comparer, &ctx, error);
5470 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5471 add_generic_class (acfg, comparer_inst, FALSE, "Comparer<T>");
5476 static void
5477 add_instances_of (MonoAotCompile *acfg, MonoClass *klass, MonoType **insts, int ninsts, gboolean force)
5479 int i;
5480 MonoGenericContext ctx;
5482 if (acfg->aot_opts.no_instances)
5483 return;
5485 memset (&ctx, 0, sizeof (ctx));
5487 for (i = 0; i < ninsts; ++i) {
5488 ERROR_DECL (error);
5489 MonoClass *generic_inst;
5490 MonoType *args [ ] = { insts [i] };
5491 ctx.class_inst = mono_metadata_get_generic_inst (1, args);
5492 generic_inst = mono_class_inflate_generic_class_checked (klass, &ctx, error);
5493 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5494 add_generic_class (acfg, generic_inst, force, "");
5498 static void
5499 add_types_from_method_header (MonoAotCompile *acfg, MonoMethod *method)
5501 ERROR_DECL (error);
5502 MonoMethodHeader *header;
5503 MonoMethodSignature *sig;
5504 int j, depth;
5506 depth = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_depth, method));
5508 sig = mono_method_signature_internal (method);
5510 if (sig) {
5511 for (j = 0; j < sig->param_count; ++j)
5512 if (sig->params [j]->type == MONO_TYPE_GENERICINST)
5513 add_generic_class_with_depth (acfg, mono_class_from_mono_type_internal (sig->params [j]), depth + 1, "arg");
5516 header = mono_method_get_header_checked (method, error);
5518 if (header) {
5519 for (j = 0; j < header->num_locals; ++j)
5520 if (header->locals [j]->type == MONO_TYPE_GENERICINST)
5521 add_generic_class_with_depth (acfg, mono_class_from_mono_type_internal (header->locals [j]), depth + 1, "local");
5522 mono_metadata_free_mh (header);
5523 } else {
5524 mono_error_cleanup (error); /* FIXME report the error */
5530 * add_generic_instances:
5532 * Add instances referenced by the METHODSPEC/TYPESPEC table.
5534 static void
5535 add_generic_instances (MonoAotCompile *acfg)
5537 int i;
5538 guint32 token;
5539 MonoMethod *method;
5540 MonoGenericContext *context;
5542 if (acfg->aot_opts.no_instances)
5543 return;
5545 for (i = 0; i < acfg->image->tables [MONO_TABLE_METHODSPEC].rows; ++i) {
5546 ERROR_DECL (error);
5547 token = MONO_TOKEN_METHOD_SPEC | (i + 1);
5548 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
5550 if (!method) {
5551 aot_printerrf (acfg, "Failed to load methodspec 0x%x due to %s.\n", token, mono_error_get_message (error));
5552 aot_printerrf (acfg, "Run with MONO_LOG_LEVEL=debug for more information.\n");
5553 mono_error_cleanup (error);
5554 continue;
5557 if (m_class_get_image (method->klass) != acfg->image)
5558 continue;
5560 context = mono_method_get_context (method);
5562 if (context && ((context->class_inst && context->class_inst->is_open)))
5563 continue;
5566 * For open methods, create an instantiation which can be passed to the JIT.
5567 * FIXME: Handle class_inst as well.
5569 if (context && context->method_inst && context->method_inst->is_open) {
5570 ERROR_DECL (error);
5571 MonoGenericContext shared_context;
5572 MonoGenericInst *inst;
5573 MonoType **type_argv;
5574 int i;
5575 MonoMethod *declaring_method;
5576 gboolean supported = TRUE;
5578 /* Check that the context doesn't contain open constructed types */
5579 if (context->class_inst) {
5580 inst = context->class_inst;
5581 for (i = 0; i < inst->type_argc; ++i) {
5582 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
5583 continue;
5584 if (mono_class_is_open_constructed_type (inst->type_argv [i]))
5585 supported = FALSE;
5588 if (context->method_inst) {
5589 inst = context->method_inst;
5590 for (i = 0; i < inst->type_argc; ++i) {
5591 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
5592 continue;
5593 if (mono_class_is_open_constructed_type (inst->type_argv [i]))
5594 supported = FALSE;
5598 if (!supported)
5599 continue;
5601 memset (&shared_context, 0, sizeof (MonoGenericContext));
5603 inst = context->class_inst;
5604 if (inst) {
5605 type_argv = g_new0 (MonoType*, inst->type_argc);
5606 for (i = 0; i < inst->type_argc; ++i) {
5607 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
5608 type_argv [i] = mono_get_object_type ();
5609 else
5610 type_argv [i] = inst->type_argv [i];
5613 shared_context.class_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
5614 g_free (type_argv);
5617 inst = context->method_inst;
5618 if (inst) {
5619 type_argv = g_new0 (MonoType*, inst->type_argc);
5620 for (i = 0; i < inst->type_argc; ++i) {
5621 if (MONO_TYPE_IS_REFERENCE (inst->type_argv [i]) || inst->type_argv [i]->type == MONO_TYPE_VAR || inst->type_argv [i]->type == MONO_TYPE_MVAR)
5622 type_argv [i] = mono_get_object_type ();
5623 else
5624 type_argv [i] = inst->type_argv [i];
5627 shared_context.method_inst = mono_metadata_get_generic_inst (inst->type_argc, type_argv);
5628 g_free (type_argv);
5631 if (method->is_generic || mono_class_is_gtd (method->klass))
5632 declaring_method = method;
5633 else
5634 declaring_method = mono_method_get_declaring_generic_method (method);
5636 method = mono_class_inflate_generic_method_checked (declaring_method, &shared_context, error);
5637 g_assert (is_ok (error)); /* FIXME don't swallow the error */
5641 * If the method is fully sharable, it was already added in place of its
5642 * generic definition.
5644 if (mono_method_is_generic_sharable_full (method, FALSE, FALSE, FALSE))
5645 continue;
5648 * FIXME: Partially shared methods are not shared here, so we end up with
5649 * many identical methods.
5651 add_extra_method (acfg, method);
5654 for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) {
5655 ERROR_DECL (error);
5656 MonoClass *klass;
5658 token = MONO_TOKEN_TYPE_SPEC | (i + 1);
5660 klass = mono_class_get_checked (acfg->image, token, error);
5661 if (!klass || m_class_get_rank (klass)) {
5662 mono_error_cleanup (error);
5663 continue;
5666 add_generic_class (acfg, klass, FALSE, "typespec");
5669 /* Add types of args/locals */
5670 for (i = 0; i < acfg->methods->len; ++i) {
5671 method = (MonoMethod *)g_ptr_array_index (acfg->methods, i);
5672 add_types_from_method_header (acfg, method);
5675 if (acfg->image == mono_defaults.corlib) {
5676 MonoClass *klass;
5677 MonoType *insts [256];
5678 int ninsts = 0;
5680 MonoType *byte_type = m_class_get_byval_arg (mono_defaults.byte_class);
5681 MonoType *sbyte_type = m_class_get_byval_arg (mono_defaults.sbyte_class);
5682 MonoType *int16_type = m_class_get_byval_arg (mono_defaults.int16_class);
5683 MonoType *uint16_type = m_class_get_byval_arg (mono_defaults.uint16_class);
5684 MonoType *int32_type = mono_get_int32_type ();
5685 MonoType *uint32_type = m_class_get_byval_arg (mono_defaults.uint32_class);
5686 MonoType *int64_type = m_class_get_byval_arg (mono_defaults.int64_class);
5687 MonoType *uint64_type = m_class_get_byval_arg (mono_defaults.uint64_class);
5688 MonoType *object_type = mono_get_object_type ();
5690 insts [ninsts ++] = byte_type;
5691 insts [ninsts ++] = sbyte_type;
5692 insts [ninsts ++] = int16_type;
5693 insts [ninsts ++] = uint16_type;
5694 insts [ninsts ++] = int32_type;
5695 insts [ninsts ++] = uint32_type;
5696 insts [ninsts ++] = int64_type;
5697 insts [ninsts ++] = uint64_type;
5698 insts [ninsts ++] = m_class_get_byval_arg (mono_defaults.single_class);
5699 insts [ninsts ++] = m_class_get_byval_arg (mono_defaults.double_class);
5700 insts [ninsts ++] = m_class_get_byval_arg (mono_defaults.char_class);
5701 insts [ninsts ++] = m_class_get_byval_arg (mono_defaults.boolean_class);
5703 /* Add GenericComparer<T> instances for primitive types for Enum.ToString () */
5704 klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "GenericComparer`1");
5705 if (klass)
5706 add_instances_of (acfg, klass, insts, ninsts, TRUE);
5707 klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "GenericEqualityComparer`1");
5708 if (klass)
5709 add_instances_of (acfg, klass, insts, ninsts, TRUE);
5711 /* Add instances of EnumEqualityComparer which are created by EqualityComparer<T> for enums */
5713 MonoClass *enum_comparer;
5714 MonoType *insts [16];
5715 int ninsts;
5717 ninsts = 0;
5718 insts [ninsts ++] = int32_type;
5719 insts [ninsts ++] = uint32_type;
5720 insts [ninsts ++] = uint16_type;
5721 insts [ninsts ++] = byte_type;
5722 enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "EnumEqualityComparer`1");
5723 add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE);
5725 #ifndef ENABLE_NETCORE
5726 ninsts = 0;
5727 insts [ninsts ++] = int16_type;
5728 enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "ShortEnumEqualityComparer`1");
5729 add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE);
5731 ninsts = 0;
5732 insts [ninsts ++] = sbyte_type;
5733 enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "SByteEnumEqualityComparer`1");
5734 add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE);
5736 enum_comparer = mono_class_load_from_name (mono_defaults.corlib, "System.Collections.Generic", "LongEnumEqualityComparer`1");
5737 ninsts = 0;
5738 insts [ninsts ++] = int64_type;
5739 insts [ninsts ++] = uint64_type;
5740 add_instances_of (acfg, enum_comparer, insts, ninsts, FALSE);
5741 #endif
5744 /* Add instances of the array generic interfaces for primitive types */
5745 /* This will add instances of the InternalArray_ helper methods in Array too */
5746 klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "ICollection`1");
5747 if (klass)
5748 add_instances_of (acfg, klass, insts, ninsts, TRUE);
5750 klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "IList`1");
5751 if (klass)
5752 add_instances_of (acfg, klass, insts, ninsts, TRUE);
5754 klass = mono_class_try_load_from_name (acfg->image, "System.Collections.Generic", "IEnumerable`1");
5755 if (klass)
5756 add_instances_of (acfg, klass, insts, ninsts, TRUE);
5759 * Add a managed-to-native wrapper of Array.GetGenericValue_icall<object>, which is
5760 * used for all instances of GetGenericValue_icall by the AOT runtime.
5763 ERROR_DECL (error);
5764 MonoGenericContext ctx;
5765 MonoMethod *get_method;
5766 MonoClass *array_klass = m_class_get_parent (mono_class_create_array (mono_defaults.object_class, 1));
5768 get_method = mono_class_get_method_from_name_checked (array_klass, "GetGenericValue_icall", 3, 0, error);
5769 mono_error_assert_ok (error);
5771 if (get_method) {
5772 memset (&ctx, 0, sizeof (ctx));
5773 MonoType *args [ ] = { object_type };
5774 ctx.method_inst = mono_metadata_get_generic_inst (1, args);
5775 add_extra_method (acfg, mono_marshal_get_native_wrapper (mono_class_inflate_generic_method_checked (get_method, &ctx, error), TRUE, TRUE));
5776 mono_error_assert_ok (error); /* FIXME don't swallow the error */
5780 /* object[] accessor wrappers. */
5781 for (i = 1; i < 4; ++i) {
5782 MonoClass *obj_array_class = mono_class_create_array (mono_defaults.object_class, i);
5783 MonoMethod *m;
5785 m = get_method_nofail (obj_array_class, "Get", i, 0);
5786 g_assert (m);
5788 m = mono_marshal_get_array_accessor_wrapper (m);
5789 add_extra_method (acfg, m);
5791 m = get_method_nofail (obj_array_class, "Address", i, 0);
5792 g_assert (m);
5794 m = mono_marshal_get_array_accessor_wrapper (m);
5795 add_extra_method (acfg, m);
5797 m = get_method_nofail (obj_array_class, "Set", i + 1, 0);
5798 g_assert (m);
5800 m = mono_marshal_get_array_accessor_wrapper (m);
5801 add_extra_method (acfg, m);
5806 static char *
5807 decode_direct_icall_symbol_name_attribute (MonoMethod *method)
5809 ERROR_DECL (error);
5811 int j = 0;
5812 char *symbol_name = NULL;
5814 MonoCustomAttrInfo *cattr = mono_custom_attrs_from_method_checked (method, error);
5815 if (is_ok(error) && cattr) {
5816 for (j = 0; j < cattr->num_attrs; j++)
5817 if (cattr->attrs [j].ctor && !strcmp (m_class_get_name (cattr->attrs [j].ctor->klass), "MonoDirectICallSymbolNameAttribute"))
5818 break;
5820 if (j < cattr->num_attrs) {
5821 MonoCustomAttrEntry *e = &cattr->attrs [j];
5822 MonoMethodSignature *sig = mono_method_signature_internal (e->ctor);
5823 if (e->data && sig && sig->param_count == 1 && sig->params [0]->type == MONO_TYPE_STRING) {
5825 * Decode the cattr manually since we can't create objects
5826 * during aot compilation.
5829 /* Skip prolog */
5830 const char *p = ((const char*)e->data) + 2;
5831 int slen = mono_metadata_decode_value (p, &p);
5833 symbol_name = (char *)g_memdup (p, slen + 1);
5834 if (symbol_name)
5835 symbol_name [slen] = 0;
5840 return symbol_name;
5842 static const char*
5843 lookup_external_icall_symbol_name_aot (MonoMethod *method)
5845 g_assert (method_to_external_icall_symbol_name);
5847 gpointer key, value;
5848 if (g_hash_table_lookup_extended (method_to_external_icall_symbol_name, method, &key, &value))
5849 return (const char*)value;
5851 char *symbol_name = decode_direct_icall_symbol_name_attribute (method);
5852 g_hash_table_insert (method_to_external_icall_symbol_name, method, symbol_name);
5854 return symbol_name;
5857 static const char*
5858 lookup_icall_symbol_name_aot (MonoMethod *method)
5860 const char * symbol_name = mono_lookup_icall_symbol (method);
5861 if (!symbol_name)
5862 symbol_name = lookup_external_icall_symbol_name_aot (method);
5864 return symbol_name;
5867 gboolean
5868 mono_aot_direct_icalls_enabled_for_method (MonoCompile *cfg, MonoMethod *method)
5870 gboolean enable_icall = FALSE;
5871 if (cfg->compile_aot)
5872 enable_icall = lookup_external_icall_symbol_name_aot (method) ? TRUE : FALSE;
5873 else
5874 enable_icall = FALSE;
5876 return enable_icall;
5880 * method_is_externally_callable:
5882 * Return whenever METHOD can be directly called from other AOT images
5883 * without going through a PLT.
5885 static gboolean
5886 method_is_externally_callable (MonoAotCompile *acfg, MonoMethod *method)
5888 // FIXME: Unify
5889 if (acfg->aot_opts.llvm_only) {
5890 if (!acfg->aot_opts.static_link)
5891 return FALSE;
5892 if (method->wrapper_type == MONO_WRAPPER_ALLOC)
5893 return TRUE;
5894 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE)
5895 return TRUE;
5896 if (method->string_ctor)
5897 return FALSE;
5898 if (method->wrapper_type)
5899 return FALSE;
5900 if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))
5901 return FALSE;
5902 if (method->is_inflated)
5903 return FALSE;
5904 if (!((mono_class_get_flags (method->klass) & TYPE_ATTRIBUTE_PUBLIC) && (method->flags & METHOD_ATTRIBUTE_PUBLIC)))
5905 return FALSE;
5906 /* Can't enable this as the callee might fail llvm compilation */
5907 //return TRUE;
5908 return FALSE;
5909 } else {
5910 if (!acfg->aot_opts.direct_extern_calls)
5911 return FALSE;
5912 if (!acfg->llvm)
5913 return FALSE;
5914 if (acfg->aot_opts.soft_debug || acfg->aot_opts.no_direct_calls)
5915 return FALSE;
5916 if (method->wrapper_type == MONO_WRAPPER_ALLOC)
5917 return FALSE;
5918 if (method->string_ctor)
5919 return FALSE;
5920 if (method->wrapper_type)
5921 return FALSE;
5922 if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))
5923 return FALSE;
5924 if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
5925 return FALSE;
5926 if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)
5927 return FALSE;
5928 if (method->is_inflated)
5929 return FALSE;
5930 if (!((mono_class_get_flags (method->klass) & TYPE_ATTRIBUTE_PUBLIC) && (method->flags & METHOD_ATTRIBUTE_PUBLIC)))
5931 return FALSE;
5932 return TRUE;
5937 * is_direct_callable:
5939 * Return whenever the method identified by JI is directly callable without
5940 * going through the PLT.
5942 static gboolean
5943 is_direct_callable (MonoAotCompile *acfg, MonoMethod *method, MonoJumpInfo *patch_info)
5945 if ((patch_info->type == MONO_PATCH_INFO_METHOD) && (m_class_get_image (patch_info->data.method->klass) == acfg->image)) {
5946 MonoCompile *callee_cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, patch_info->data.method);
5947 if (callee_cfg) {
5948 gboolean direct_callable = TRUE;
5950 if (direct_callable && (acfg->aot_opts.dedup || acfg->aot_opts.dedup_include) && mono_aot_can_dedup (patch_info->data.method))
5951 direct_callable = FALSE;
5953 if (direct_callable && !acfg->llvm && !(!callee_cfg->has_got_slots && mono_class_is_before_field_init (callee_cfg->method->klass)))
5954 direct_callable = FALSE;
5956 if (direct_callable && !strcmp (callee_cfg->method->name, ".cctor"))
5957 direct_callable = FALSE;
5960 // FIXME: Support inflated methods, it asserts in mini_llvm_init_gshared_method_this () because the method is not in
5961 // amodule->extra_methods.
5963 if (direct_callable && callee_cfg->method->is_inflated)
5964 direct_callable = FALSE;
5966 if (direct_callable && (callee_cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) && (!method || method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED))
5967 // FIXME: Maybe call the wrapper directly ?
5968 direct_callable = FALSE;
5970 if (direct_callable && (acfg->aot_opts.soft_debug || acfg->aot_opts.no_direct_calls)) {
5971 /* Disable this so all calls go through load_method (), see the
5972 * mini_get_debug_options ()->load_aot_jit_info_eagerly = TRUE; line in
5973 * mono_debugger_agent_init ().
5975 direct_callable = FALSE;
5978 if (direct_callable && (callee_cfg->method->wrapper_type == MONO_WRAPPER_ALLOC))
5979 /* sgen does some initialization when the allocator method is created */
5980 direct_callable = FALSE;
5981 if (direct_callable && (callee_cfg->method->wrapper_type == MONO_WRAPPER_WRITE_BARRIER))
5982 /* we don't know at compile time whether sgen is concurrent or not */
5983 direct_callable = FALSE;
5985 if (direct_callable)
5986 return TRUE;
5988 } else if ((patch_info->type == MONO_PATCH_INFO_METHOD) && (m_class_get_image (patch_info->data.method->klass) != acfg->image)) {
5989 /* Cross assembly calls */
5990 return method_is_externally_callable (acfg, patch_info->data.method);
5991 } else if ((patch_info->type == MONO_PATCH_INFO_ICALL_ADDR_CALL && patch_info->data.method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
5992 if (acfg->aot_opts.direct_pinvoke)
5993 return TRUE;
5994 } else if (patch_info->type == MONO_PATCH_INFO_ICALL_ADDR_CALL) {
5995 if (acfg->aot_opts.direct_icalls)
5996 return TRUE;
5997 return FALSE;
6000 return FALSE;
6003 #ifdef MONO_ARCH_AOT_SUPPORTED
6004 static const char *
6005 get_pinvoke_import (MonoAotCompile *acfg, MonoMethod *method)
6007 MonoImage *image = m_class_get_image (method->klass);
6008 MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method;
6009 MonoTableInfo *tables = image->tables;
6010 MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP];
6011 MonoTableInfo *mr = &tables [MONO_TABLE_MODULEREF];
6012 guint32 im_cols [MONO_IMPLMAP_SIZE];
6013 char *import;
6015 import = (char *)g_hash_table_lookup (acfg->method_to_pinvoke_import, method);
6016 if (import != NULL)
6017 return import;
6019 if (!piinfo->implmap_idx || piinfo->implmap_idx > im->rows)
6020 return NULL;
6022 mono_metadata_decode_row (im, piinfo->implmap_idx - 1, im_cols, MONO_IMPLMAP_SIZE);
6024 if (!im_cols [MONO_IMPLMAP_SCOPE] || im_cols [MONO_IMPLMAP_SCOPE] > mr->rows)
6025 return NULL;
6027 import = g_strdup_printf ("%s", mono_metadata_string_heap (image, im_cols [MONO_IMPLMAP_NAME]));
6029 g_hash_table_insert (acfg->method_to_pinvoke_import, method, import);
6031 return import;
6033 #else
6034 static const char *
6035 get_pinvoke_import (MonoAotCompile *acfg, MonoMethod *method)
6037 return NULL;
6039 #endif
6041 static gint
6042 compare_lne (MonoDebugLineNumberEntry *a, MonoDebugLineNumberEntry *b)
6044 if (a->native_offset == b->native_offset)
6045 return a->il_offset - b->il_offset;
6046 else
6047 return a->native_offset - b->native_offset;
6051 * compute_line_numbers:
6053 * Returns a sparse array of size CODE_SIZE containing MonoDebugSourceLocation* entries for the native offsets which have a corresponding line number
6054 * entry.
6056 static MonoDebugSourceLocation**
6057 compute_line_numbers (MonoMethod *method, int code_size, MonoDebugMethodJitInfo *debug_info)
6059 MonoDebugMethodInfo *minfo;
6060 MonoDebugLineNumberEntry *ln_array;
6061 MonoDebugSourceLocation *loc;
6062 int i, prev_line, prev_il_offset;
6063 int *native_to_il_offset = NULL;
6064 MonoDebugSourceLocation **res;
6065 gboolean first;
6067 minfo = mono_debug_lookup_method (method);
6068 if (!minfo)
6069 return NULL;
6070 // FIXME: This seems to happen when two methods have the same cfg->method_to_register
6071 if (debug_info->code_size != code_size)
6072 return NULL;
6074 g_assert (code_size);
6076 /* Compute the native->IL offset mapping */
6078 ln_array = g_new0 (MonoDebugLineNumberEntry, debug_info->num_line_numbers);
6079 memcpy (ln_array, debug_info->line_numbers, debug_info->num_line_numbers * sizeof (MonoDebugLineNumberEntry));
6081 mono_qsort (ln_array, debug_info->num_line_numbers, sizeof (MonoDebugLineNumberEntry), (int (*)(const void *, const void *))compare_lne);
6083 native_to_il_offset = g_new0 (int, code_size + 1);
6085 for (i = 0; i < debug_info->num_line_numbers; ++i) {
6086 int j;
6087 MonoDebugLineNumberEntry *lne = &ln_array [i];
6089 if (i == 0) {
6090 for (j = 0; j < lne->native_offset; ++j)
6091 native_to_il_offset [j] = -1;
6094 if (i < debug_info->num_line_numbers - 1) {
6095 MonoDebugLineNumberEntry *lne_next = &ln_array [i + 1];
6097 for (j = lne->native_offset; j < lne_next->native_offset; ++j)
6098 native_to_il_offset [j] = lne->il_offset;
6099 } else {
6100 for (j = lne->native_offset; j < code_size; ++j)
6101 native_to_il_offset [j] = lne->il_offset;
6104 g_free (ln_array);
6106 /* Compute the native->line number mapping */
6107 res = g_new0 (MonoDebugSourceLocation*, code_size);
6108 prev_il_offset = -1;
6109 prev_line = -1;
6110 first = TRUE;
6111 for (i = 0; i < code_size; ++i) {
6112 int il_offset = native_to_il_offset [i];
6114 if (il_offset == -1 || il_offset == prev_il_offset)
6115 continue;
6116 prev_il_offset = il_offset;
6117 loc = mono_debug_method_lookup_location (minfo, il_offset);
6118 if (!(loc && loc->source_file))
6119 continue;
6120 if (loc->row == prev_line) {
6121 mono_debug_free_source_location (loc);
6122 continue;
6124 prev_line = loc->row;
6125 //printf ("D: %s:%d il=%x native=%x\n", loc->source_file, loc->row, il_offset, i);
6126 if (first)
6127 /* This will cover the prolog too */
6128 res [0] = loc;
6129 else
6130 res [i] = loc;
6131 first = FALSE;
6133 return res;
6136 static int
6137 get_file_index (MonoAotCompile *acfg, const char *source_file)
6139 int findex;
6141 // FIXME: Free these
6142 if (!acfg->dwarf_ln_filenames)
6143 acfg->dwarf_ln_filenames = g_hash_table_new (g_str_hash, g_str_equal);
6144 findex = GPOINTER_TO_INT (g_hash_table_lookup (acfg->dwarf_ln_filenames, source_file));
6145 if (!findex) {
6146 findex = g_hash_table_size (acfg->dwarf_ln_filenames) + 1;
6147 g_hash_table_insert (acfg->dwarf_ln_filenames, g_strdup (source_file), GINT_TO_POINTER (findex));
6148 emit_unset_mode (acfg);
6149 fprintf (acfg->fp, ".file %d \"%s\"\n", findex, mono_dwarf_escape_path (source_file));
6151 return findex;
6154 #ifdef TARGET_ARM64
6155 #define INST_LEN 4
6156 #else
6157 #define INST_LEN 1
6158 #endif
6161 * emit_and_reloc_code:
6163 * Emit the native code in CODE, handling relocations along the way. If GOT_ONLY
6164 * is true, calls are made through the GOT too. This is used for emitting trampolines
6165 * in full-aot mode, since calls made from trampolines couldn't go through the PLT,
6166 * since trampolines are needed to make PLT work.
6168 static void
6169 emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, guint32 code_len, MonoJumpInfo *relocs, gboolean got_only, MonoDebugMethodJitInfo *debug_info)
6171 int i, pindex, start_index;
6172 GPtrArray *patches;
6173 MonoJumpInfo *patch_info;
6174 MonoDebugSourceLocation **locs = NULL;
6175 gboolean skip, prologue_end = FALSE;
6176 #ifdef MONO_ARCH_AOT_SUPPORTED
6177 gboolean direct_call, external_call;
6178 guint32 got_slot;
6179 const char *direct_call_target = 0;
6180 const char *direct_pinvoke;
6181 #endif
6183 if (acfg->gas_line_numbers && method && debug_info) {
6184 locs = compute_line_numbers (method, code_len, debug_info);
6185 if (!locs) {
6186 int findex = get_file_index (acfg, "<unknown>");
6187 emit_unset_mode (acfg);
6188 fprintf (acfg->fp, ".loc %d %d 0\n", findex, 1);
6192 /* Collect and sort relocations */
6193 patches = g_ptr_array_new ();
6194 for (patch_info = relocs; patch_info; patch_info = patch_info->next)
6195 g_ptr_array_add (patches, patch_info);
6196 g_ptr_array_sort (patches, compare_patches);
6198 start_index = 0;
6199 for (i = 0; i < code_len; i += INST_LEN) {
6200 patch_info = NULL;
6201 for (pindex = start_index; pindex < patches->len; ++pindex) {
6202 patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex);
6203 if (patch_info->ip.i >= i)
6204 break;
6207 if (locs && locs [i]) {
6208 MonoDebugSourceLocation *loc = locs [i];
6209 int findex;
6210 const char *options;
6212 findex = get_file_index (acfg, loc->source_file);
6213 emit_unset_mode (acfg);
6214 if (!prologue_end)
6215 options = " prologue_end";
6216 else
6217 options = "";
6218 prologue_end = TRUE;
6219 fprintf (acfg->fp, ".loc %d %d 0%s\n", findex, loc->row, options);
6220 mono_debug_free_source_location (loc);
6223 skip = FALSE;
6224 #ifdef MONO_ARCH_AOT_SUPPORTED
6225 if (patch_info && (patch_info->ip.i == i) && (pindex < patches->len)) {
6226 start_index = pindex;
6228 switch (patch_info->type) {
6229 case MONO_PATCH_INFO_NONE:
6230 break;
6231 case MONO_PATCH_INFO_GOT_OFFSET: {
6232 int code_size;
6234 arch_emit_got_offset (acfg, code + i, &code_size);
6235 i += code_size - INST_LEN;
6236 skip = TRUE;
6237 patch_info->type = MONO_PATCH_INFO_NONE;
6238 break;
6240 case MONO_PATCH_INFO_OBJC_SELECTOR_REF: {
6241 int code_size, index;
6242 char *selector = (char *)patch_info->data.target;
6244 if (!acfg->objc_selector_to_index)
6245 acfg->objc_selector_to_index = g_hash_table_new (g_str_hash, g_str_equal);
6246 if (!acfg->objc_selectors)
6247 acfg->objc_selectors = g_ptr_array_new ();
6248 index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->objc_selector_to_index, selector));
6249 if (index)
6250 index --;
6251 else {
6252 index = acfg->objc_selector_index;
6253 g_ptr_array_add (acfg->objc_selectors, (void*)patch_info->data.target);
6254 g_hash_table_insert (acfg->objc_selector_to_index, selector, GUINT_TO_POINTER (index + 1));
6255 acfg->objc_selector_index ++;
6258 arch_emit_objc_selector_ref (acfg, code + i, index, &code_size);
6259 i += code_size - INST_LEN;
6260 skip = TRUE;
6261 patch_info->type = MONO_PATCH_INFO_NONE;
6262 break;
6264 default: {
6266 * If this patch is a call, try emitting a direct call instead of
6267 * through a PLT entry. This is possible if the called method is in
6268 * the same assembly and requires no initialization.
6270 direct_call = FALSE;
6271 external_call = FALSE;
6272 if (patch_info->type == MONO_PATCH_INFO_METHOD) {
6273 MonoMethod *cmethod = patch_info->data.method;
6274 if (cmethod->wrapper_type == MONO_WRAPPER_OTHER && mono_marshal_get_wrapper_info (cmethod)->subtype == WRAPPER_SUBTYPE_AOT_INIT) {
6275 WrapperInfo *info = mono_marshal_get_wrapper_info (cmethod);
6278 * This is a call from a JITted method to the init wrapper emitted by LLVM.
6280 g_assert (acfg->aot_opts.llvm && acfg->aot_opts.direct_extern_calls);
6282 const char *init_name = mono_marshal_get_aot_init_wrapper_name (info->d.aot_init.subtype);
6283 char *symbol = g_strdup_printf ("%s%s_%s", acfg->user_symbol_prefix, acfg->global_prefix, init_name);
6285 direct_call = TRUE;
6286 direct_call_target = symbol;
6287 patch_info->type = MONO_PATCH_INFO_NONE;
6288 } else if ((m_class_get_image (patch_info->data.method->klass) == acfg->image) && !got_only && is_direct_callable (acfg, method, patch_info)) {
6289 MonoCompile *callee_cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, cmethod);
6291 // Don't compile inflated methods if we're doing dedup
6292 if (acfg->aot_opts.dedup && !mono_aot_can_dedup (cmethod)) {
6293 char *name = mono_aot_get_mangled_method_name (cmethod);
6294 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_AOT, "DIRECT CALL: %s by %s", name, method ? mono_method_full_name (method, TRUE) : "");
6295 g_free (name);
6297 direct_call = TRUE;
6298 direct_call_target = callee_cfg->asm_symbol;
6299 patch_info->type = MONO_PATCH_INFO_NONE;
6300 acfg->stats.direct_calls ++;
6304 acfg->stats.all_calls ++;
6305 } else if (patch_info->type == MONO_PATCH_INFO_ICALL_ADDR_CALL) {
6306 if (!got_only && is_direct_callable (acfg, method, patch_info)) {
6307 if (!(patch_info->data.method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
6308 direct_pinvoke = lookup_icall_symbol_name_aot (patch_info->data.method);
6309 else
6310 direct_pinvoke = get_pinvoke_import (acfg, patch_info->data.method);
6311 if (direct_pinvoke) {
6312 direct_call = TRUE;
6313 g_assert (strlen (direct_pinvoke) < 1000);
6314 direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, direct_pinvoke);
6317 } else if (patch_info->type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
6318 const char *sym = mono_find_jit_icall_info (patch_info->data.jit_icall_id)->c_symbol;
6319 if (!got_only && sym && acfg->aot_opts.direct_icalls) {
6320 /* Call to a C function implementing a jit icall */
6321 direct_call = TRUE;
6322 external_call = TRUE;
6323 g_assert (strlen (sym) < 1000);
6324 direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, sym);
6326 } else if (patch_info->type == MONO_PATCH_INFO_JIT_ICALL_ID) {
6327 MonoJitICallInfo * const info = mono_find_jit_icall_info (patch_info->data.jit_icall_id);
6328 const char * const sym = info->c_symbol;
6329 if (!got_only && sym && acfg->aot_opts.direct_icalls && info->func == info->wrapper) {
6330 /* Call to a jit icall without a wrapper */
6331 direct_call = TRUE;
6332 external_call = TRUE;
6333 g_assert (strlen (sym) < 1000);
6334 direct_call_target = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, sym);
6337 if (direct_call) {
6338 patch_info->type = MONO_PATCH_INFO_NONE;
6339 acfg->stats.direct_calls ++;
6342 if (!got_only && !direct_call) {
6343 MonoPltEntry *plt_entry = get_plt_entry (acfg, patch_info);
6344 if (plt_entry) {
6345 /* This patch has a PLT entry, so we must emit a call to the PLT entry */
6346 direct_call = TRUE;
6347 direct_call_target = plt_entry->symbol;
6349 /* Nullify the patch */
6350 patch_info->type = MONO_PATCH_INFO_NONE;
6351 plt_entry->jit_used = TRUE;
6355 if (direct_call) {
6356 int call_size;
6358 arch_emit_direct_call (acfg, direct_call_target, external_call, FALSE, patch_info, &call_size);
6359 i += call_size - INST_LEN;
6360 } else {
6361 int code_size;
6363 got_slot = get_got_offset (acfg, FALSE, patch_info);
6365 arch_emit_got_access (acfg, acfg->got_symbol, code + i, got_slot, &code_size);
6366 i += code_size - INST_LEN;
6368 skip = TRUE;
6372 #endif /* MONO_ARCH_AOT_SUPPORTED */
6374 if (!skip) {
6375 /* Find next patch */
6376 patch_info = NULL;
6377 for (pindex = start_index; pindex < patches->len; ++pindex) {
6378 patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex);
6379 if (patch_info->ip.i >= i)
6380 break;
6383 /* Try to emit multiple bytes at once */
6384 if (pindex < patches->len && patch_info->ip.i > i) {
6385 int limit;
6387 for (limit = i + INST_LEN; limit < patch_info->ip.i; limit += INST_LEN) {
6388 if (locs && locs [limit])
6389 break;
6392 emit_code_bytes (acfg, code + i, limit - i);
6393 i = limit - INST_LEN;
6394 } else {
6395 emit_code_bytes (acfg, code + i, INST_LEN);
6400 g_ptr_array_free (patches, TRUE);
6401 g_free (locs);
6405 * sanitize_symbol:
6407 * Return a modified version of S which only includes characters permissible in symbols.
6409 static char*
6410 sanitize_symbol (MonoAotCompile *acfg, char *s)
6412 gboolean process = FALSE;
6413 int i, len;
6414 GString *gs;
6415 char *res;
6417 if (!s)
6418 return s;
6420 len = strlen (s);
6421 for (i = 0; i < len; ++i)
6422 if (!(s [i] <= 0x7f && (isalnum (s [i]) || s [i] == '_')))
6423 process = TRUE;
6424 if (!process)
6425 return s;
6427 gs = g_string_sized_new (len);
6428 for (i = 0; i < len; ++i) {
6429 guint8 c = s [i];
6430 if (c <= 0x7f && (isalnum (c) || c == '_')) {
6431 g_string_append_c (gs, c);
6432 } else if (c > 0x7f) {
6433 /* multi-byte utf8 */
6434 g_string_append_printf (gs, "_0x%x", c);
6435 i ++;
6436 c = s [i];
6437 while (c >> 6 == 0x2) {
6438 g_string_append_printf (gs, "%x", c);
6439 i ++;
6440 c = s [i];
6442 g_string_append_printf (gs, "_");
6443 i --;
6444 } else {
6445 g_string_append_c (gs, '_');
6449 res = mono_mempool_strdup (acfg->mempool, gs->str);
6450 g_string_free (gs, TRUE);
6451 return res;
6454 static char*
6455 get_debug_sym (MonoMethod *method, const char *prefix, GHashTable *cache)
6457 char *name1, *name2, *cached;
6458 int i, j, len, count;
6459 MonoMethod *cached_method;
6461 name1 = mono_method_full_name (method, TRUE);
6463 #ifdef TARGET_MACH
6464 // This is so that we don't accidentally create a local symbol (which starts with 'L')
6465 if ((!prefix || !*prefix) && name1 [0] == 'L')
6466 prefix = "_";
6467 #endif
6469 #if defined(TARGET_WIN32) && defined(TARGET_X86)
6470 char adjustedPrefix [MAX_SYMBOL_SIZE];
6471 prefix = mangle_symbol (prefix, adjustedPrefix, G_N_ELEMENTS (adjustedPrefix));
6472 #endif
6474 len = strlen (name1);
6475 name2 = (char *) g_malloc (strlen (prefix) + len + 16);
6476 memcpy (name2, prefix, strlen (prefix));
6477 j = strlen (prefix);
6478 for (i = 0; i < len; ++i) {
6479 if (i == 0 && name1 [0] >= '0' && name1 [0] <= '9') {
6480 name2 [j ++] = '_';
6481 } else if (isalnum (name1 [i])) {
6482 name2 [j ++] = name1 [i];
6483 } else if (name1 [i] == ' ' && name1 [i + 1] == '(' && name1 [i + 2] == ')') {
6484 i += 2;
6485 } else if (name1 [i] == ',' && name1 [i + 1] == ' ') {
6486 name2 [j ++] = '_';
6487 i++;
6488 } else if (name1 [i] == '(' || name1 [i] == ')' || name1 [i] == '>') {
6489 } else
6490 name2 [j ++] = '_';
6492 name2 [j] = '\0';
6494 g_free (name1);
6496 count = 0;
6497 while (TRUE) {
6498 cached_method = (MonoMethod *)g_hash_table_lookup (cache, name2);
6499 if (!(cached_method && cached_method != method))
6500 break;
6501 sprintf (name2 + j, "_%d", count);
6502 count ++;
6505 cached = g_strdup (name2);
6506 g_hash_table_insert (cache, cached, method);
6508 return name2;
6511 static void
6512 emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg)
6514 MonoMethod *method;
6515 int method_index;
6516 guint8 *code;
6517 char *debug_sym = NULL;
6518 char *symbol = NULL;
6519 int func_alignment = AOT_FUNC_ALIGNMENT;
6520 char *export_name;
6522 g_assert (!ignore_cfg (cfg));
6524 method = cfg->orig_method;
6525 code = cfg->native_code;
6527 method_index = get_method_index (acfg, method);
6528 symbol = g_strdup_printf ("%sme_%x", acfg->temp_prefix, method_index);
6530 /* Make the labels local */
6531 emit_section_change (acfg, ".text", 0);
6532 emit_alignment_code (acfg, func_alignment);
6534 if (acfg->global_symbols && acfg->need_no_dead_strip)
6535 fprintf (acfg->fp, " .no_dead_strip %s\n", cfg->asm_symbol);
6537 emit_label (acfg, cfg->asm_symbol);
6539 if (acfg->aot_opts.write_symbols && !acfg->global_symbols && !acfg->llvm) {
6541 * Write a C style symbol for every method, this has two uses:
6542 * - it works on platforms where the dwarf debugging info is not
6543 * yet supported.
6544 * - it allows the setting of breakpoints of aot-ed methods.
6547 // Comment out to force dedup to link these symbols and forbid compiling
6548 // in duplicated code. This is an "assert when linking if broken" trick.
6549 /*if (mono_aot_can_dedup (method) && (acfg->aot_opts.dedup || acfg->aot_opts.dedup_include))*/
6550 /*debug_sym = mono_aot_get_mangled_method_name (method);*/
6551 /*else*/
6552 debug_sym = get_debug_sym (method, "", acfg->method_label_hash);
6554 cfg->asm_debug_symbol = g_strdup (debug_sym);
6556 if (acfg->need_no_dead_strip)
6557 fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym);
6559 // Comment out to force dedup to link these symbols and forbid compiling
6560 // in duplicated code. This is an "assert when linking if broken" trick.
6561 /*if (mono_aot_can_dedup (method) && (acfg->aot_opts.dedup || acfg->aot_opts.dedup_include))*/
6562 /*emit_global_inner (acfg, debug_sym, TRUE);*/
6563 /*else*/
6564 emit_local_symbol (acfg, debug_sym, symbol, TRUE);
6566 emit_label (acfg, debug_sym);
6569 export_name = (char *)g_hash_table_lookup (acfg->export_names, method);
6570 if (export_name) {
6571 /* Emit a global symbol for the method */
6572 emit_global_inner (acfg, export_name, TRUE);
6573 emit_label (acfg, export_name);
6576 if (cfg->verbose_level > 0 && !ignore_cfg (cfg))
6577 g_print ("Method %s emitted as %s\n", mono_method_get_full_name (method), cfg->asm_symbol);
6579 acfg->stats.code_size += cfg->code_len;
6581 acfg->cfgs [method_index]->got_offset = acfg->got_offset;
6583 emit_and_reloc_code (acfg, method, code, cfg->code_len, cfg->patch_info, FALSE, mono_debug_find_method (cfg->jit_info->d.method, mono_domain_get ()));
6585 emit_line (acfg);
6587 if (acfg->aot_opts.write_symbols) {
6588 if (debug_sym)
6589 emit_symbol_size (acfg, debug_sym, ".");
6590 else
6591 emit_symbol_size (acfg, cfg->asm_symbol, ".");
6592 g_free (debug_sym);
6595 emit_label (acfg, symbol);
6597 arch_emit_unwind_info_sections (acfg, cfg->asm_symbol, symbol, cfg->unwind_ops);
6599 g_free (symbol);
6603 * encode_patch:
6605 * Encode PATCH_INFO into its disk representation.
6607 static void
6608 encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint8 **endbuf)
6610 guint8 *p = buf;
6612 switch (patch_info->type) {
6613 case MONO_PATCH_INFO_NONE:
6614 break;
6615 case MONO_PATCH_INFO_IMAGE:
6616 encode_value (get_image_index (acfg, patch_info->data.image), p, &p);
6617 break;
6618 case MONO_PATCH_INFO_MSCORLIB_GOT_ADDR:
6619 case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR:
6620 case MONO_PATCH_INFO_GC_NURSERY_START:
6621 case MONO_PATCH_INFO_GC_NURSERY_BITS:
6622 break;
6623 case MONO_PATCH_INFO_SWITCH: {
6624 gpointer *table = (gpointer *)patch_info->data.table->table;
6625 int k;
6627 encode_value (patch_info->data.table->table_size, p, &p);
6628 for (k = 0; k < patch_info->data.table->table_size; k++)
6629 encode_value ((int)(gssize)table [k], p, &p);
6630 break;
6632 case MONO_PATCH_INFO_METHODCONST:
6633 case MONO_PATCH_INFO_METHOD:
6634 case MONO_PATCH_INFO_METHOD_JUMP:
6635 case MONO_PATCH_INFO_METHOD_FTNDESC:
6636 case MONO_PATCH_INFO_ICALL_ADDR:
6637 case MONO_PATCH_INFO_ICALL_ADDR_CALL:
6638 case MONO_PATCH_INFO_METHOD_RGCTX:
6639 case MONO_PATCH_INFO_METHOD_CODE_SLOT:
6640 case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE:
6641 encode_method_ref (acfg, patch_info->data.method, p, &p);
6642 break;
6643 case MONO_PATCH_INFO_AOT_JIT_INFO:
6644 case MONO_PATCH_INFO_CASTCLASS_CACHE:
6645 encode_value (patch_info->data.index, p, &p);
6646 break;
6647 case MONO_PATCH_INFO_JIT_ICALL_ID:
6648 case MONO_PATCH_INFO_JIT_ICALL_ADDR:
6649 case MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL:
6650 encode_value (patch_info->data.jit_icall_id, p, &p);
6651 break;
6652 case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR:
6653 encode_value (patch_info->data.uindex, p, &p);
6654 break;
6655 case MONO_PATCH_INFO_LDSTR_LIT: {
6656 guint32 len = strlen (patch_info->data.name);
6657 encode_value (len, p, &p);
6658 memcpy (p, patch_info->data.name, len + 1);
6659 p += len + 1;
6660 break;
6662 case MONO_PATCH_INFO_LDSTR: {
6663 guint32 image_index = get_image_index (acfg, patch_info->data.token->image);
6664 guint32 token = patch_info->data.token->token;
6665 g_assert (mono_metadata_token_code (token) == MONO_TOKEN_STRING);
6666 encode_value (image_index, p, &p);
6667 encode_value (patch_info->data.token->token - MONO_TOKEN_STRING, p, &p);
6668 break;
6670 case MONO_PATCH_INFO_RVA:
6671 case MONO_PATCH_INFO_DECLSEC:
6672 case MONO_PATCH_INFO_LDTOKEN:
6673 case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
6674 encode_value (get_image_index (acfg, patch_info->data.token->image), p, &p);
6675 encode_value (patch_info->data.token->token, p, &p);
6676 encode_value (patch_info->data.token->has_context, p, &p);
6677 if (patch_info->data.token->has_context)
6678 encode_generic_context (acfg, &patch_info->data.token->context, p, &p);
6679 break;
6680 case MONO_PATCH_INFO_EXC_NAME: {
6681 MonoClass *ex_class;
6683 ex_class =
6684 mono_class_load_from_name (m_class_get_image (mono_defaults.exception_class),
6685 "System", (const char *)patch_info->data.target);
6686 encode_klass_ref (acfg, ex_class, p, &p);
6687 break;
6689 case MONO_PATCH_INFO_R4:
6690 case MONO_PATCH_INFO_R4_GOT:
6691 encode_value (*((guint32 *)patch_info->data.target), p, &p);
6692 break;
6693 case MONO_PATCH_INFO_R8:
6694 case MONO_PATCH_INFO_R8_GOT:
6695 encode_value (((guint32 *)patch_info->data.target) [MINI_LS_WORD_IDX], p, &p);
6696 encode_value (((guint32 *)patch_info->data.target) [MINI_MS_WORD_IDX], p, &p);
6697 break;
6698 case MONO_PATCH_INFO_VTABLE:
6699 case MONO_PATCH_INFO_CLASS:
6700 case MONO_PATCH_INFO_IID:
6701 case MONO_PATCH_INFO_ADJUSTED_IID:
6702 encode_klass_ref (acfg, patch_info->data.klass, p, &p);
6703 break;
6704 case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE:
6705 encode_klass_ref (acfg, patch_info->data.del_tramp->klass, p, &p);
6706 if (patch_info->data.del_tramp->method) {
6707 encode_value (1, p, &p);
6708 encode_method_ref (acfg, patch_info->data.del_tramp->method, p, &p);
6709 } else {
6710 encode_value (0, p, &p);
6712 encode_value (patch_info->data.del_tramp->is_virtual, p, &p);
6713 break;
6714 case MONO_PATCH_INFO_FIELD:
6715 case MONO_PATCH_INFO_SFLDA:
6716 encode_field_info (acfg, patch_info->data.field, p, &p);
6717 break;
6718 case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG:
6719 break;
6720 case MONO_PATCH_INFO_PROFILER_ALLOCATION_COUNT:
6721 case MONO_PATCH_INFO_PROFILER_CLAUSE_COUNT:
6722 break;
6723 case MONO_PATCH_INFO_RGCTX_FETCH:
6724 case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: {
6725 MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry;
6726 guint32 offset;
6729 * entry->d.klass/method has a lenghtly encoding and multiple rgctx_fetch entries
6730 * reference the same klass/method, so encode it only once.
6731 * For patches which refer to got entries, this sharing is done by get_got_offset, but
6732 * these are not got entries.
6734 if (entry->in_mrgctx) {
6735 offset = get_shared_method_ref (acfg, entry->d.method);
6736 } else {
6737 offset = get_shared_klass_ref (acfg, entry->d.klass);
6740 encode_value (offset, p, &p);
6741 g_assert ((int)entry->info_type < 256);
6742 g_assert (entry->data->type < 256);
6743 encode_value ((entry->in_mrgctx ? 1 : 0) | (entry->info_type << 1) | (entry->data->type << 9), p, &p);
6744 encode_patch (acfg, entry->data, p, &p);
6745 break;
6747 case MONO_PATCH_INFO_SEQ_POINT_INFO:
6748 case MONO_PATCH_INFO_AOT_MODULE:
6749 break;
6750 case MONO_PATCH_INFO_SIGNATURE:
6751 case MONO_PATCH_INFO_GSHAREDVT_IN_WRAPPER:
6752 encode_signature (acfg, (MonoMethodSignature*)patch_info->data.target, p, &p);
6753 break;
6754 case MONO_PATCH_INFO_GSHAREDVT_CALL:
6755 encode_signature (acfg, (MonoMethodSignature*)patch_info->data.gsharedvt->sig, p, &p);
6756 encode_method_ref (acfg, patch_info->data.gsharedvt->method, p, &p);
6757 break;
6758 case MONO_PATCH_INFO_GSHAREDVT_METHOD: {
6759 MonoGSharedVtMethodInfo *info = patch_info->data.gsharedvt_method;
6760 int i;
6762 encode_method_ref (acfg, info->method, p, &p);
6763 encode_value (info->num_entries, p, &p);
6764 for (i = 0; i < info->num_entries; ++i) {
6765 MonoRuntimeGenericContextInfoTemplate *template_ = &info->entries [i];
6767 encode_value (template_->info_type, p, &p);
6768 switch (mini_rgctx_info_type_to_patch_info_type (template_->info_type)) {
6769 case MONO_PATCH_INFO_CLASS:
6770 encode_klass_ref (acfg, mono_class_from_mono_type_internal ((MonoType *)template_->data), p, &p);
6771 break;
6772 case MONO_PATCH_INFO_FIELD:
6773 encode_field_info (acfg, (MonoClassField *)template_->data, p, &p);
6774 break;
6775 default:
6776 g_assert_not_reached ();
6777 break;
6780 break;
6782 case MONO_PATCH_INFO_VIRT_METHOD:
6783 encode_klass_ref (acfg, patch_info->data.virt_method->klass, p, &p);
6784 encode_method_ref (acfg, patch_info->data.virt_method->method, p, &p);
6785 break;
6786 case MONO_PATCH_INFO_GC_SAFE_POINT_FLAG:
6787 case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINES:
6788 case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINES_GOT_SLOTS_BASE:
6789 break;
6790 default:
6791 g_error ("unable to handle jump info %d", patch_info->type);
6794 *endbuf = p;
6797 static void
6798 encode_patch_list (MonoAotCompile *acfg, GPtrArray *patches, int n_patches, gboolean llvm, guint8 *buf, guint8 **endbuf)
6800 guint8 *p = buf;
6801 guint32 pindex, offset;
6802 MonoJumpInfo *patch_info;
6804 encode_value (n_patches, p, &p);
6806 for (pindex = 0; pindex < patches->len; ++pindex) {
6807 patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex);
6809 if (patch_info->type == MONO_PATCH_INFO_NONE || patch_info->type == MONO_PATCH_INFO_BB)
6810 /* Nothing to do */
6811 continue;
6812 /* This shouldn't allocate a new offset */
6813 offset = lookup_got_offset (acfg, llvm, patch_info);
6814 encode_value (offset, p, &p);
6817 *endbuf = p;
6820 static void
6821 emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg)
6823 MonoMethod *method;
6824 int pindex, buf_size, n_patches;
6825 GPtrArray *patches;
6826 MonoJumpInfo *patch_info;
6827 guint8 *p, *buf;
6828 guint32 offset;
6829 gboolean needs_ctx = FALSE;
6831 method = cfg->orig_method;
6833 (void)get_method_index (acfg, method);
6835 /* Sort relocations */
6836 patches = g_ptr_array_new ();
6837 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next)
6838 g_ptr_array_add (patches, patch_info);
6839 if (!acfg->aot_opts.llvm_only)
6840 g_ptr_array_sort (patches, compare_patches);
6842 /**********************/
6843 /* Encode method info */
6844 /**********************/
6846 g_assert (!(cfg->opt & MONO_OPT_SHARED));
6848 guint32 *got_offsets = g_new0 (guint32, patches->len);
6850 n_patches = 0;
6851 for (pindex = 0; pindex < patches->len; ++pindex) {
6852 patch_info = (MonoJumpInfo *)g_ptr_array_index (patches, pindex);
6854 if ((patch_info->type == MONO_PATCH_INFO_GOT_OFFSET) ||
6855 (patch_info->type == MONO_PATCH_INFO_NONE)) {
6856 patch_info->type = MONO_PATCH_INFO_NONE;
6857 /* Nothing to do */
6858 continue;
6861 if ((patch_info->type == MONO_PATCH_INFO_IMAGE) && (patch_info->data.image == acfg->image)) {
6862 /* Stored in a GOT slot initialized at module load time */
6863 patch_info->type = MONO_PATCH_INFO_NONE;
6864 continue;
6867 if (patch_info->type == MONO_PATCH_INFO_GC_CARD_TABLE_ADDR ||
6868 patch_info->type == MONO_PATCH_INFO_GC_NURSERY_START ||
6869 patch_info->type == MONO_PATCH_INFO_GC_NURSERY_BITS ||
6870 patch_info->type == MONO_PATCH_INFO_AOT_MODULE) {
6871 /* Stored in a GOT slot initialized at module load time */
6872 patch_info->type = MONO_PATCH_INFO_NONE;
6873 continue;
6876 if (is_plt_patch (patch_info) && !(cfg->compile_llvm && acfg->aot_opts.llvm_only)) {
6877 /* Calls are made through the PLT */
6878 patch_info->type = MONO_PATCH_INFO_NONE;
6879 continue;
6882 if (acfg->aot_opts.llvm_only && patch_info->type == MONO_PATCH_INFO_METHOD)
6883 needs_ctx = TRUE;
6885 /* This shouldn't allocate a new offset */
6886 offset = lookup_got_offset (acfg, cfg->compile_llvm, patch_info);
6887 if (offset >= acfg->nshared_got_entries)
6888 got_offsets [n_patches ++] = offset;
6891 if (n_patches)
6892 g_assert (cfg->has_got_slots);
6894 buf_size = (patches->len < 1000) ? 40960 : 40960 + (patches->len * 64);
6895 p = buf = (guint8 *)g_malloc (buf_size);
6897 MonoGenericContext *ctx = mono_method_get_context (cfg->method);
6899 guint8 flags = 0;
6900 if (mono_class_get_cctor (method->klass))
6901 flags |= MONO_AOT_METHOD_FLAG_HAS_CCTOR;
6902 if (mini_jit_info_is_gsharedvt (cfg->jit_info) && mini_is_gsharedvt_variable_signature (mono_method_signature_internal (jinfo_get_method (cfg->jit_info))))
6903 flags |= MONO_AOT_METHOD_FLAG_GSHAREDVT_VARIABLE;
6904 if (n_patches)
6905 flags |= MONO_AOT_METHOD_FLAG_HAS_PATCHES;
6906 if (needs_ctx && ctx)
6907 flags |= MONO_AOT_METHOD_FLAG_HAS_CTX;
6908 /* Saved into another table so it can be accessed without having access to this data */
6909 cfg->aot_method_flags = flags;
6911 encode_int (cfg->method_index, p, &p);
6912 if (flags & MONO_AOT_METHOD_FLAG_HAS_CCTOR)
6913 encode_klass_ref (acfg, method->klass, p, &p);
6914 if (needs_ctx && ctx)
6915 encode_generic_context (acfg, ctx, p, &p);
6917 if (n_patches) {
6918 encode_value (n_patches, p, &p);
6919 for (int i = 0; i < n_patches; ++i)
6920 encode_value (got_offsets [i], p, &p);
6923 g_ptr_array_free (patches, TRUE);
6924 g_free (got_offsets);
6926 acfg->stats.method_info_size += p - buf;
6928 g_assert (p - buf < buf_size);
6930 if (cfg->compile_llvm) {
6931 char *symbol = g_strdup_printf ("info_%s", cfg->llvm_method_name);
6932 cfg->llvm_info_var = mono_llvm_emit_aot_data_aligned (symbol, buf, p - buf, 1);
6933 g_free (symbol);
6934 /* aot-runtime.c will use this to check whenever this is an llvm method */
6935 cfg->method_info_offset = 0;
6936 } else {
6937 cfg->method_info_offset = add_to_blob (acfg, buf, p - buf);
6939 g_free (buf);
6942 static guint32
6943 get_unwind_info_offset (MonoAotCompile *acfg, guint8 *encoded, guint32 encoded_len)
6945 guint32 cache_index;
6946 guint32 offset;
6948 /* Reuse the unwind module to canonize and store unwind info entries */
6949 cache_index = mono_cache_unwind_info (encoded, encoded_len);
6951 /* Use +/- 1 to distinguish 0s from missing entries */
6952 offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->unwind_info_offsets, GUINT_TO_POINTER (cache_index + 1)));
6953 if (offset)
6954 return offset - 1;
6955 else {
6956 guint8 buf [16];
6957 guint8 *p;
6960 * It would be easier to use assembler symbols, but the caller needs an
6961 * offset now.
6963 offset = acfg->unwind_info_offset;
6964 g_hash_table_insert (acfg->unwind_info_offsets, GUINT_TO_POINTER (cache_index + 1), GUINT_TO_POINTER (offset + 1));
6965 g_ptr_array_add (acfg->unwind_ops, GUINT_TO_POINTER (cache_index));
6967 p = buf;
6968 encode_value (encoded_len, p, &p);
6970 acfg->unwind_info_offset += encoded_len + (p - buf);
6971 return offset;
6975 static void
6976 emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg, gboolean store_seq_points)
6978 int i, k, buf_size;
6979 guint32 debug_info_size, seq_points_size;
6980 guint8 *code;
6981 MonoMethodHeader *header;
6982 guint8 *p, *buf, *debug_info;
6983 MonoJitInfo *jinfo = cfg->jit_info;
6984 guint32 flags;
6985 gboolean use_unwind_ops = FALSE;
6986 MonoSeqPointInfo *seq_points;
6988 code = cfg->native_code;
6989 header = cfg->header;
6991 if (!acfg->aot_opts.nodebug) {
6992 mono_debug_serialize_debug_info (cfg, &debug_info, &debug_info_size);
6993 } else {
6994 debug_info = NULL;
6995 debug_info_size = 0;
6998 seq_points = cfg->seq_point_info;
6999 seq_points_size = (store_seq_points)? mono_seq_point_info_get_write_size (seq_points) : 0;
7001 buf_size = header->num_clauses * 256 + debug_info_size + 2048 + seq_points_size + cfg->gc_map_size;
7002 if (jinfo->has_try_block_holes) {
7003 MonoTryBlockHoleTableJitInfo *table = mono_jit_info_get_try_block_hole_table_info (jinfo);
7004 buf_size += table->num_holes * 16;
7007 p = buf = (guint8 *)g_malloc (buf_size);
7009 use_unwind_ops = cfg->unwind_ops != NULL;
7011 flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0) | (header->num_clauses ? 4 : 0) | (seq_points_size ? 8 : 0) | (cfg->compile_llvm ? 16 : 0) | (jinfo->has_try_block_holes ? 32 : 0) | (cfg->gc_map ? 64 : 0) | (jinfo->has_arch_eh_info ? 128 : 0);
7013 encode_value (flags, p, &p);
7015 if (use_unwind_ops) {
7016 guint32 encoded_len;
7017 guint8 *encoded;
7018 guint32 unwind_desc;
7020 encoded = mono_unwind_ops_encode (cfg->unwind_ops, &encoded_len);
7022 unwind_desc = get_unwind_info_offset (acfg, encoded, encoded_len);
7023 encode_value (unwind_desc, p, &p);
7025 g_free (encoded);
7026 } else {
7027 encode_value (jinfo->unwind_info, p, &p);
7030 /*Encode the number of holes before the number of clauses to make decoding easier*/
7031 if (jinfo->has_try_block_holes) {
7032 MonoTryBlockHoleTableJitInfo *table = mono_jit_info_get_try_block_hole_table_info (jinfo);
7033 encode_value (table->num_holes, p, &p);
7036 if (jinfo->has_arch_eh_info) {
7038 * In AOT mode, the code length is calculated from the address of the previous method,
7039 * which could include alignment padding, so calculating the start of the epilog as
7040 * code_len - epilog_size is correct any more. Save the real code len as a workaround.
7042 encode_value (jinfo->code_size, p, &p);
7045 /* Exception table */
7046 if (cfg->compile_llvm) {
7048 * When using LLVM, we can't emit some data, like pc offsets, this reg/offset etc.,
7049 * since the information is only available to llc. Instead, we let llc save the data
7050 * into the LSDA, and read it from there at runtime.
7052 /* The assembly might be CIL stripped so emit the data ourselves */
7053 if (header->num_clauses)
7054 encode_value (header->num_clauses, p, &p);
7056 for (k = 0; k < header->num_clauses; ++k) {
7057 MonoExceptionClause *clause;
7059 clause = &header->clauses [k];
7061 encode_value (clause->flags, p, &p);
7062 if (!(clause->flags == MONO_EXCEPTION_CLAUSE_FILTER || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) {
7063 if (clause->data.catch_class) {
7064 guint8 *buf2, *p2;
7065 int len;
7067 buf2 = (guint8 *)g_malloc (4096);
7068 p2 = buf2;
7069 encode_klass_ref (acfg, clause->data.catch_class, p2, &p2);
7070 len = p2 - buf2;
7071 g_assert (len < 4096);
7072 encode_value (len, p, &p);
7073 memcpy (p, buf2, len);
7074 p += p2 - buf2;
7075 g_free (buf2);
7076 } else {
7077 encode_value (0, p, &p);
7081 /* Emit the IL ranges too, since they might not be available at runtime */
7082 encode_value (clause->try_offset, p, &p);
7083 encode_value (clause->try_len, p, &p);
7084 encode_value (clause->handler_offset, p, &p);
7085 encode_value (clause->handler_len, p, &p);
7087 /* Emit a list of nesting clauses */
7088 for (i = 0; i < header->num_clauses; ++i) {
7089 gint32 cindex1 = k;
7090 MonoExceptionClause *clause1 = &header->clauses [cindex1];
7091 gint32 cindex2 = i;
7092 MonoExceptionClause *clause2 = &header->clauses [cindex2];
7094 if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset)
7095 encode_value (i, p, &p);
7097 encode_value (-1, p, &p);
7099 } else {
7100 if (jinfo->num_clauses)
7101 encode_value (jinfo->num_clauses, p, &p);
7103 for (k = 0; k < jinfo->num_clauses; ++k) {
7104 MonoJitExceptionInfo *ei = &jinfo->clauses [k];
7106 encode_value (ei->flags, p, &p);
7107 #ifdef MONO_CONTEXT_SET_LLVM_EXC_REG
7108 /* Not used for catch clauses */
7109 if (ei->flags != MONO_EXCEPTION_CLAUSE_NONE)
7110 encode_value (ei->exvar_offset, p, &p);
7111 #else
7112 encode_value (ei->exvar_offset, p, &p);
7113 #endif
7115 if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)
7116 encode_value ((gint)((guint8*)ei->data.filter - code), p, &p);
7117 else {
7118 if (ei->data.catch_class) {
7119 guint8 *buf2, *p2;
7120 int len;
7122 buf2 = (guint8 *)g_malloc (4096);
7123 p2 = buf2;
7124 encode_klass_ref (acfg, ei->data.catch_class, p2, &p2);
7125 len = p2 - buf2;
7126 g_assert (len < 4096);
7127 encode_value (len, p, &p);
7128 memcpy (p, buf2, len);
7129 p += p2 - buf2;
7130 g_free (buf2);
7131 } else {
7132 encode_value (0, p, &p);
7136 encode_value ((gint)((guint8*)ei->try_start - code), p, &p);
7137 encode_value ((gint)((guint8*)ei->try_end - code), p, &p);
7138 encode_value ((gint)((guint8*)ei->handler_start - code), p, &p);
7142 if (jinfo->has_try_block_holes) {
7143 MonoTryBlockHoleTableJitInfo *table = mono_jit_info_get_try_block_hole_table_info (jinfo);
7144 for (i = 0; i < table->num_holes; ++i) {
7145 MonoTryBlockHoleJitInfo *hole = &table->holes [i];
7146 encode_value (hole->clause, p, &p);
7147 encode_value (hole->length, p, &p);
7148 encode_value (hole->offset, p, &p);
7152 if (jinfo->has_arch_eh_info) {
7153 MonoArchEHJitInfo *eh_info;
7155 eh_info = mono_jit_info_get_arch_eh_info (jinfo);
7156 encode_value (eh_info->stack_size, p, &p);
7157 encode_value (eh_info->epilog_size, p, &p);
7160 if (jinfo->has_generic_jit_info) {
7161 MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (jinfo);
7162 MonoGenericSharingContext* gsctx = gi->generic_sharing_context;
7163 guint8 *buf2, *p2;
7164 int len;
7166 encode_value (gi->nlocs, p, &p);
7167 if (gi->nlocs) {
7168 for (i = 0; i < gi->nlocs; ++i) {
7169 MonoDwarfLocListEntry *entry = &gi->locations [i];
7171 encode_value (entry->is_reg ? 1 : 0, p, &p);
7172 encode_value (entry->reg, p, &p);
7173 if (!entry->is_reg)
7174 encode_value (entry->offset, p, &p);
7175 if (i == 0)
7176 g_assert (entry->from == 0);
7177 else
7178 encode_value (entry->from, p, &p);
7179 encode_value (entry->to, p, &p);
7181 } else {
7182 if (!cfg->compile_llvm) {
7183 encode_value (gi->has_this ? 1 : 0, p, &p);
7184 encode_value (gi->this_reg, p, &p);
7185 encode_value (gi->this_offset, p, &p);
7190 * Need to encode jinfo->method too, since it is not equal to 'method'
7191 * when using generic sharing.
7193 buf2 = (guint8 *)g_malloc (4096);
7194 p2 = buf2;
7195 encode_method_ref (acfg, jinfo->d.method, p2, &p2);
7196 len = p2 - buf2;
7197 g_assert (len < 4096);
7198 encode_value (len, p, &p);
7199 memcpy (p, buf2, len);
7200 p += p2 - buf2;
7201 g_free (buf2);
7203 if (gsctx && gsctx->is_gsharedvt) {
7204 encode_value (1, p, &p);
7205 } else {
7206 encode_value (0, p, &p);
7210 if (seq_points_size)
7211 p += mono_seq_point_info_write (seq_points, p);
7213 g_assert (debug_info_size < buf_size);
7215 encode_value (debug_info_size, p, &p);
7216 if (debug_info_size) {
7217 memcpy (p, debug_info, debug_info_size);
7218 p += debug_info_size;
7219 g_free (debug_info);
7222 /* GC Map */
7223 if (cfg->gc_map) {
7224 encode_value (cfg->gc_map_size, p, &p);
7225 /* The GC map requires 4 bytes of alignment */
7226 while ((gsize)p % 4)
7227 p ++;
7228 memcpy (p, cfg->gc_map, cfg->gc_map_size);
7229 p += cfg->gc_map_size;
7232 acfg->stats.ex_info_size += p - buf;
7234 g_assert (p - buf < buf_size);
7236 /* Emit info */
7237 /* The GC Map requires 4 byte alignment */
7238 cfg->ex_info_offset = add_to_blob_aligned (acfg, buf, p - buf, cfg->gc_map ? 4 : 1);
7239 g_free (buf);
7242 static guint32
7243 emit_klass_info (MonoAotCompile *acfg, guint32 token)
7245 ERROR_DECL (error);
7246 MonoClass *klass = mono_class_get_checked (acfg->image, token, error);
7247 guint8 *p, *buf;
7248 int i, buf_size, res;
7249 gboolean no_special_static, cant_encode;
7250 gpointer iter = NULL;
7252 if (!klass) {
7253 mono_error_cleanup (error);
7255 buf_size = 16;
7257 p = buf = (guint8 *)g_malloc (buf_size);
7259 /* Mark as unusable */
7260 encode_value (-1, p, &p);
7262 res = add_to_blob (acfg, buf, p - buf);
7263 g_free (buf);
7265 return res;
7268 buf_size = 10240 + (m_class_get_vtable_size (klass) * 16);
7269 p = buf = (guint8 *)g_malloc (buf_size);
7271 g_assert (klass);
7273 mono_class_init_internal (klass);
7275 mono_class_get_nested_types (klass, &iter);
7276 g_assert (m_class_is_nested_classes_inited (klass));
7278 mono_class_setup_vtable (klass);
7281 * Emit all the information which is required for creating vtables so
7282 * the runtime does not need to create the MonoMethod structures which
7283 * take up a lot of space.
7286 no_special_static = !mono_class_has_special_static_fields (klass);
7288 /* Check whenever we have enough info to encode the vtable */
7289 cant_encode = FALSE;
7290 MonoMethod **klass_vtable = m_class_get_vtable (klass);
7291 for (i = 0; i < m_class_get_vtable_size (klass); ++i) {
7292 MonoMethod *cm = klass_vtable [i];
7294 if (cm && mono_method_signature_internal (cm)->is_inflated && !g_hash_table_lookup (acfg->token_info_hash, cm))
7295 cant_encode = TRUE;
7298 mono_class_has_finalizer (klass);
7299 if (mono_class_has_failure (klass))
7300 cant_encode = TRUE;
7302 if (mono_class_is_gtd (klass) || cant_encode) {
7303 encode_value (-1, p, &p);
7304 } else {
7305 gboolean has_nested = mono_class_get_nested_classes_property (klass) != NULL;
7306 encode_value (m_class_get_vtable_size (klass), p, &p);
7307 encode_value ((m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p);
7308 if (m_class_has_cctor (klass))
7309 encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p);
7310 if (m_class_has_finalize (klass))
7311 encode_method_ref (acfg, mono_class_get_finalizer (klass), p, &p);
7313 encode_value (m_class_get_instance_size (klass), p, &p);
7314 encode_value (mono_class_data_size (klass), p, &p);
7315 encode_value (m_class_get_packing_size (klass), p, &p);
7316 encode_value (m_class_get_min_align (klass), p, &p);
7318 for (i = 0; i < m_class_get_vtable_size (klass); ++i) {
7319 MonoMethod *cm = klass_vtable [i];
7321 if (cm)
7322 encode_method_ref (acfg, cm, p, &p);
7323 else
7324 encode_value (0, p, &p);
7328 acfg->stats.class_info_size += p - buf;
7330 g_assert (p - buf < buf_size);
7331 res = add_to_blob (acfg, buf, p - buf);
7332 g_free (buf);
7334 return res;
7337 static char*
7338 get_plt_entry_debug_sym (MonoAotCompile *acfg, MonoJumpInfo *ji, GHashTable *cache)
7340 char *debug_sym = NULL;
7341 char *prefix;
7343 if (acfg->llvm && llvm_acfg->aot_opts.static_link) {
7344 /* Need to add a prefix to create unique symbols */
7345 prefix = g_strdup_printf ("plt_%s_", acfg->assembly_name_sym);
7346 } else {
7347 #if defined(TARGET_WIN32) && defined(TARGET_X86)
7348 prefix = mangle_symbol_alloc ("plt_");
7349 #else
7350 prefix = g_strdup ("plt_");
7351 #endif
7354 switch (ji->type) {
7355 case MONO_PATCH_INFO_METHOD:
7356 debug_sym = get_debug_sym (ji->data.method, prefix, cache);
7357 break;
7358 case MONO_PATCH_INFO_JIT_ICALL_ID:
7359 debug_sym = g_strdup_printf ("%s_jit_icall_%s", prefix, mono_find_jit_icall_info (ji->data.jit_icall_id)->name);
7360 break;
7361 case MONO_PATCH_INFO_RGCTX_FETCH:
7362 debug_sym = g_strdup_printf ("%s_rgctx_fetch_%d", prefix, acfg->label_generator ++);
7363 break;
7364 case MONO_PATCH_INFO_ICALL_ADDR:
7365 case MONO_PATCH_INFO_ICALL_ADDR_CALL: {
7366 char *s = get_debug_sym (ji->data.method, "", cache);
7368 debug_sym = g_strdup_printf ("%s_icall_native_%s", prefix, s);
7369 g_free (s);
7370 break;
7372 case MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR:
7373 debug_sym = g_strdup_printf ("%s_jit_icall_native_specific_trampoline_lazy_fetch_%lu", prefix, (gulong)ji->data.uindex);
7374 break;
7375 case MONO_PATCH_INFO_JIT_ICALL_ADDR:
7376 debug_sym = g_strdup_printf ("%s_jit_icall_native_%s", prefix, mono_find_jit_icall_info (ji->data.jit_icall_id)->name);
7377 break;
7378 default:
7379 break;
7382 g_free (prefix);
7384 return sanitize_symbol (acfg, debug_sym);
7388 * Calls made from AOTed code are routed through a table of jumps similar to the
7389 * ELF PLT (Program Linkage Table). Initially the PLT entries jump to code which transfers
7390 * control to the AOT runtime through a trampoline.
7392 static void
7393 emit_plt (MonoAotCompile *acfg)
7395 int i;
7397 if (acfg->aot_opts.llvm_only) {
7398 g_assert (acfg->plt_offset == 1);
7399 return;
7402 emit_line (acfg);
7404 emit_section_change (acfg, ".text", 0);
7405 emit_alignment_code (acfg, 16);
7406 emit_info_symbol (acfg, "plt", TRUE);
7407 emit_label (acfg, acfg->plt_symbol);
7409 for (i = 0; i < acfg->plt_offset; ++i) {
7410 char *debug_sym = NULL;
7411 MonoPltEntry *plt_entry = NULL;
7413 if (i == 0)
7415 * The first plt entry is unused.
7417 continue;
7419 plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i));
7421 debug_sym = plt_entry->debug_sym;
7423 if (acfg->thumb_mixed && !plt_entry->jit_used)
7424 /* Emit only a thumb version */
7425 continue;
7427 /* Skip plt entries not actually called */
7428 if (!plt_entry->jit_used && !plt_entry->llvm_used)
7429 continue;
7431 if (acfg->llvm && !acfg->thumb_mixed) {
7432 emit_label (acfg, plt_entry->llvm_symbol);
7433 if (acfg->llvm) {
7434 emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE);
7435 #if defined(TARGET_MACH)
7436 fprintf (acfg->fp, ".private_extern %s\n", plt_entry->llvm_symbol);
7437 #endif
7441 if (debug_sym) {
7442 if (acfg->need_no_dead_strip) {
7443 emit_unset_mode (acfg);
7444 fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym);
7446 emit_local_symbol (acfg, debug_sym, NULL, TRUE);
7447 emit_label (acfg, debug_sym);
7450 emit_label (acfg, plt_entry->symbol);
7452 arch_emit_plt_entry (acfg, acfg->got_symbol, i, (acfg->plt_got_offset_base + i) * sizeof (target_mgreg_t), acfg->plt_got_info_offsets [i]);
7454 if (debug_sym)
7455 emit_symbol_size (acfg, debug_sym, ".");
7458 if (acfg->thumb_mixed) {
7459 /* Make sure the ARM symbols don't alias the thumb ones */
7460 emit_zero_bytes (acfg, 16);
7463 * Emit a separate set of PLT entries using thumb2 which is called by LLVM generated
7464 * code.
7466 for (i = 0; i < acfg->plt_offset; ++i) {
7467 char *debug_sym = NULL;
7468 MonoPltEntry *plt_entry = NULL;
7470 if (i == 0)
7471 continue;
7473 plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i));
7475 /* Skip plt entries not actually called by LLVM code */
7476 if (!plt_entry->llvm_used)
7477 continue;
7479 if (acfg->aot_opts.write_symbols) {
7480 if (plt_entry->debug_sym)
7481 debug_sym = g_strdup_printf ("%s_thumb", plt_entry->debug_sym);
7484 if (debug_sym) {
7485 #if defined(TARGET_MACH)
7486 fprintf (acfg->fp, " .thumb_func %s\n", debug_sym);
7487 fprintf (acfg->fp, " .no_dead_strip %s\n", debug_sym);
7488 #endif
7489 emit_local_symbol (acfg, debug_sym, NULL, TRUE);
7490 emit_label (acfg, debug_sym);
7492 fprintf (acfg->fp, "\n.thumb_func\n");
7494 emit_label (acfg, plt_entry->llvm_symbol);
7496 if (acfg->llvm)
7497 emit_global_inner (acfg, plt_entry->llvm_symbol, TRUE);
7499 arch_emit_llvm_plt_entry (acfg, acfg->got_symbol, i, (acfg->plt_got_offset_base + i) * sizeof (target_mgreg_t), acfg->plt_got_info_offsets [i]);
7501 if (debug_sym) {
7502 emit_symbol_size (acfg, debug_sym, ".");
7503 g_free (debug_sym);
7508 emit_symbol_size (acfg, acfg->plt_symbol, ".");
7510 emit_info_symbol (acfg, "plt_end", TRUE);
7512 arch_emit_unwind_info_sections (acfg, "plt", "plt_end", NULL);
7516 * emit_trampoline_full:
7518 * If EMIT_TINFO is TRUE, emit additional information which can be used to create a MonoJitInfo for this trampoline by
7519 * create_jit_info_for_trampoline ().
7521 static G_GNUC_UNUSED void
7522 emit_trampoline_full (MonoAotCompile *acfg, MonoTrampInfo *info, gboolean emit_tinfo)
7524 char start_symbol [MAX_SYMBOL_SIZE];
7525 char end_symbol [MAX_SYMBOL_SIZE];
7526 char symbol [MAX_SYMBOL_SIZE];
7527 guint32 buf_size, info_offset;
7528 MonoJumpInfo *patch_info;
7529 guint8 *buf, *p;
7530 GPtrArray *patches;
7531 char *name;
7532 guint8 *code;
7533 guint32 code_size;
7534 MonoJumpInfo *ji;
7535 GSList *unwind_ops;
7537 g_assert (info);
7539 name = info->name;
7540 code = info->code;
7541 code_size = info->code_size;
7542 ji = info->ji;
7543 unwind_ops = info->unwind_ops;
7545 /* Emit code */
7547 sprintf (start_symbol, "%s%s", acfg->user_symbol_prefix, name);
7549 emit_section_change (acfg, ".text", 0);
7550 emit_global (acfg, start_symbol, TRUE);
7551 emit_alignment_code (acfg, AOT_FUNC_ALIGNMENT);
7552 emit_label (acfg, start_symbol);
7554 sprintf (symbol, "%snamed_%s", acfg->temp_prefix, name);
7555 emit_label (acfg, symbol);
7558 * The code should access everything through the GOT, so we pass
7559 * TRUE here.
7561 emit_and_reloc_code (acfg, NULL, code, code_size, ji, TRUE, NULL);
7563 emit_symbol_size (acfg, start_symbol, ".");
7565 if (emit_tinfo) {
7566 sprintf (end_symbol, "%snamede_%s", acfg->temp_prefix, name);
7567 emit_label (acfg, end_symbol);
7570 /* Emit info */
7572 /* Sort relocations */
7573 patches = g_ptr_array_new ();
7574 for (patch_info = ji; patch_info; patch_info = patch_info->next)
7575 if (patch_info->type != MONO_PATCH_INFO_NONE)
7576 g_ptr_array_add (patches, patch_info);
7577 g_ptr_array_sort (patches, compare_patches);
7579 buf_size = patches->len * 128 + 128;
7580 buf = (guint8 *)g_malloc (buf_size);
7581 p = buf;
7583 encode_patch_list (acfg, patches, patches->len, FALSE, p, &p);
7584 g_assert (p - buf < buf_size);
7585 g_ptr_array_free (patches, TRUE);
7587 sprintf (symbol, "%s%s_p", acfg->user_symbol_prefix, name);
7589 info_offset = add_to_blob (acfg, buf, p - buf);
7591 emit_section_change (acfg, RODATA_SECT, 0);
7592 emit_global (acfg, symbol, FALSE);
7593 emit_label (acfg, symbol);
7595 emit_int32 (acfg, info_offset);
7597 if (emit_tinfo) {
7598 guint8 *encoded;
7599 guint32 encoded_len;
7600 guint32 uw_offset;
7603 * Emit additional information which can be used to reconstruct a partial MonoTrampInfo.
7605 encoded = mono_unwind_ops_encode (info->unwind_ops, &encoded_len);
7606 uw_offset = get_unwind_info_offset (acfg, encoded, encoded_len);
7607 g_free (encoded);
7609 emit_symbol_diff (acfg, end_symbol, start_symbol, 0);
7610 emit_int32 (acfg, uw_offset);
7613 /* Emit debug info */
7614 if (unwind_ops) {
7615 char symbol2 [MAX_SYMBOL_SIZE];
7617 sprintf (symbol, "%s", name);
7618 sprintf (symbol2, "%snamed_%s", acfg->temp_prefix, name);
7620 arch_emit_unwind_info_sections (acfg, start_symbol, end_symbol, unwind_ops);
7622 if (acfg->dwarf)
7623 mono_dwarf_writer_emit_trampoline (acfg->dwarf, symbol, symbol2, NULL, NULL, code_size, unwind_ops);
7626 g_free (buf);
7629 static G_GNUC_UNUSED void
7630 emit_trampoline (MonoAotCompile *acfg, MonoTrampInfo *info)
7632 emit_trampoline_full (acfg, info, TRUE);
7635 static void
7636 emit_trampolines (MonoAotCompile *acfg)
7638 char symbol [MAX_SYMBOL_SIZE];
7639 char end_symbol [MAX_SYMBOL_SIZE + 2];
7640 int i, tramp_got_offset;
7641 int ntype;
7642 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
7643 int tramp_type;
7644 #endif
7646 if (!mono_aot_mode_is_full (&acfg->aot_opts) && !mono_aot_mode_is_interp (&acfg->aot_opts))
7647 return;
7648 if (acfg->aot_opts.llvm_only)
7649 return;
7651 g_assert (acfg->image->assembly);
7653 /* Currently, we emit most trampolines into the mscorlib AOT image. */
7654 if (mono_is_corlib_image(acfg->image->assembly->image)) {
7655 #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
7656 MonoTrampInfo *info;
7659 * Emit the generic trampolines.
7661 * We could save some code by treating the generic trampolines as a wrapper
7662 * method, but that approach has its own complexities, so we choose the simpler
7663 * method.
7665 for (tramp_type = 0; tramp_type < MONO_TRAMPOLINE_NUM; ++tramp_type) {
7666 /* we overload the boolean here to indicate the slightly different trampoline needed, see mono_arch_create_generic_trampoline() */
7667 #ifdef DISABLE_REMOTING
7668 if (tramp_type == MONO_TRAMPOLINE_GENERIC_VIRTUAL_REMOTING)
7669 continue;
7670 #endif
7671 mono_arch_create_generic_trampoline ((MonoTrampolineType)tramp_type, &info, acfg->aot_opts.use_trampolines_page? 2: TRUE);
7672 emit_trampoline (acfg, info);
7673 mono_tramp_info_free (info);
7676 /* Emit the exception related code pieces */
7677 mono_arch_get_restore_context (&info, TRUE);
7678 emit_trampoline (acfg, info);
7679 mono_tramp_info_free (info);
7681 mono_arch_get_call_filter (&info, TRUE);
7682 emit_trampoline (acfg, info);
7683 mono_tramp_info_free (info);
7685 mono_arch_get_throw_exception (&info, TRUE);
7686 emit_trampoline (acfg, info);
7687 mono_tramp_info_free (info);
7689 mono_arch_get_rethrow_exception (&info, TRUE);
7690 emit_trampoline (acfg, info);
7691 mono_tramp_info_free (info);
7693 mono_arch_get_rethrow_preserve_exception (&info, TRUE);
7694 emit_trampoline (acfg, info);
7695 mono_tramp_info_free (info);
7697 mono_arch_get_throw_corlib_exception (&info, TRUE);
7698 emit_trampoline (acfg, info);
7699 mono_tramp_info_free (info);
7701 #ifdef MONO_ARCH_HAVE_SDB_TRAMPOLINES
7702 mono_arch_create_sdb_trampoline (TRUE, &info, TRUE);
7703 emit_trampoline (acfg, info);
7704 mono_tramp_info_free (info);
7706 mono_arch_create_sdb_trampoline (FALSE, &info, TRUE);
7707 emit_trampoline (acfg, info);
7708 mono_tramp_info_free (info);
7709 #endif
7711 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
7712 mono_arch_get_gsharedvt_trampoline (&info, TRUE);
7713 if (info) {
7714 emit_trampoline_full (acfg, info, TRUE);
7716 /* Create a separate out trampoline for more information in stack traces */
7717 info->name = g_strdup ("gsharedvt_out_trampoline");
7718 emit_trampoline_full (acfg, info, TRUE);
7719 mono_tramp_info_free (info);
7721 #endif
7723 #if defined(MONO_ARCH_HAVE_GET_TRAMPOLINES)
7725 GSList *l = mono_arch_get_trampolines (TRUE);
7727 while (l) {
7728 MonoTrampInfo *info = (MonoTrampInfo *)l->data;
7730 emit_trampoline (acfg, info);
7731 l = l->next;
7734 #endif
7736 for (i = 0; i < acfg->aot_opts.nrgctx_fetch_trampolines; ++i) {
7737 int offset;
7739 offset = MONO_RGCTX_SLOT_MAKE_RGCTX (i);
7740 mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE);
7741 emit_trampoline (acfg, info);
7742 mono_tramp_info_free (info);
7744 offset = MONO_RGCTX_SLOT_MAKE_MRGCTX (i);
7745 mono_arch_create_rgctx_lazy_fetch_trampoline (offset, &info, TRUE);
7746 emit_trampoline (acfg, info);
7747 mono_tramp_info_free (info);
7750 #ifdef MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE
7751 mono_arch_create_general_rgctx_lazy_fetch_trampoline (&info, TRUE);
7752 emit_trampoline (acfg, info);
7753 mono_tramp_info_free (info);
7754 #endif
7757 GSList *l;
7759 /* delegate_invoke_impl trampolines */
7760 l = mono_arch_get_delegate_invoke_impls ();
7761 while (l) {
7762 MonoTrampInfo *info = (MonoTrampInfo *)l->data;
7764 emit_trampoline (acfg, info);
7765 l = l->next;
7769 if (mono_aot_mode_is_interp (&acfg->aot_opts) && mono_is_corlib_image (acfg->image->assembly->image)) {
7770 mono_arch_get_interp_to_native_trampoline (&info);
7771 emit_trampoline (acfg, info);
7773 #ifdef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
7774 mono_arch_get_native_to_interp_trampoline (&info);
7775 emit_trampoline (acfg, info);
7776 #endif
7779 #endif /* #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES */
7781 /* Emit trampolines which are numerous */
7784 * These include the following:
7785 * - specific trampolines
7786 * - static rgctx invoke trampolines
7787 * - imt trampolines
7788 * These trampolines have the same code, they are parameterized by GOT
7789 * slots.
7790 * They are defined in this file, in the arch_... routines instead of
7791 * in tramp-<ARCH>.c, since it is easier to do it this way.
7795 * When running in aot-only mode, we can't create specific trampolines at
7796 * runtime, so we create a few, and save them in the AOT file.
7797 * Normal trampolines embed their argument as a literal inside the
7798 * trampoline code, we can't do that here, so instead we embed an offset
7799 * which needs to be added to the trampoline address to get the address of
7800 * the GOT slot which contains the argument value.
7801 * The generated trampolines jump to the generic trampolines using another
7802 * GOT slot, which will be setup by the AOT loader to point to the
7803 * generic trampoline code of the given type.
7807 * FIXME: Maybe we should use more specific trampolines (i.e. one class init for
7808 * each class).
7811 emit_section_change (acfg, ".text", 0);
7813 tramp_got_offset = acfg->got_offset;
7815 for (ntype = 0; ntype < MONO_AOT_TRAMP_NUM; ++ntype) {
7816 switch (ntype) {
7817 case MONO_AOT_TRAMP_SPECIFIC:
7818 sprintf (symbol, "specific_trampolines");
7819 break;
7820 case MONO_AOT_TRAMP_STATIC_RGCTX:
7821 sprintf (symbol, "static_rgctx_trampolines");
7822 break;
7823 case MONO_AOT_TRAMP_IMT:
7824 sprintf (symbol, "imt_trampolines");
7825 break;
7826 case MONO_AOT_TRAMP_GSHAREDVT_ARG:
7827 sprintf (symbol, "gsharedvt_arg_trampolines");
7828 break;
7829 case MONO_AOT_TRAMP_FTNPTR_ARG:
7830 sprintf (symbol, "ftnptr_arg_trampolines");
7831 break;
7832 case MONO_AOT_TRAMP_UNBOX_ARBITRARY:
7833 sprintf (symbol, "unbox_arbitrary_trampolines");
7834 break;
7835 default:
7836 g_assert_not_reached ();
7839 sprintf (end_symbol, "%s_e", symbol);
7841 if (acfg->aot_opts.write_symbols)
7842 emit_local_symbol (acfg, symbol, end_symbol, TRUE);
7844 emit_alignment_code (acfg, AOT_FUNC_ALIGNMENT);
7845 emit_info_symbol (acfg, symbol, TRUE);
7847 acfg->trampoline_got_offset_base [ntype] = tramp_got_offset;
7849 for (i = 0; i < acfg->num_trampolines [ntype]; ++i) {
7850 int tramp_size = 0;
7852 switch (ntype) {
7853 case MONO_AOT_TRAMP_SPECIFIC:
7854 arch_emit_specific_trampoline (acfg, tramp_got_offset, &tramp_size);
7855 tramp_got_offset += 2;
7856 break;
7857 case MONO_AOT_TRAMP_STATIC_RGCTX:
7858 arch_emit_static_rgctx_trampoline (acfg, tramp_got_offset, &tramp_size);
7859 tramp_got_offset += 2;
7860 break;
7861 case MONO_AOT_TRAMP_IMT:
7862 arch_emit_imt_trampoline (acfg, tramp_got_offset, &tramp_size);
7863 tramp_got_offset += 1;
7864 break;
7865 case MONO_AOT_TRAMP_GSHAREDVT_ARG:
7866 arch_emit_gsharedvt_arg_trampoline (acfg, tramp_got_offset, &tramp_size);
7867 tramp_got_offset += 2;
7868 break;
7869 case MONO_AOT_TRAMP_FTNPTR_ARG:
7870 arch_emit_ftnptr_arg_trampoline (acfg, tramp_got_offset, &tramp_size);
7871 tramp_got_offset += 2;
7872 break;
7873 case MONO_AOT_TRAMP_UNBOX_ARBITRARY:
7874 arch_emit_unbox_arbitrary_trampoline (acfg, tramp_got_offset, &tramp_size);
7875 tramp_got_offset += 1;
7876 break;
7877 default:
7878 g_assert_not_reached ();
7880 if (!acfg->trampoline_size [ntype]) {
7881 g_assert (tramp_size);
7882 acfg->trampoline_size [ntype] = tramp_size;
7886 emit_label (acfg, end_symbol);
7887 emit_int32 (acfg, 0);
7890 arch_emit_specific_trampoline_pages (acfg);
7892 /* Reserve some entries at the end of the GOT for our use */
7893 acfg->num_trampoline_got_entries = tramp_got_offset - acfg->got_offset;
7896 acfg->got_offset += acfg->num_trampoline_got_entries;
7899 static gboolean
7900 str_begins_with (const char *str1, const char *str2)
7902 int len = strlen (str2);
7903 return strncmp (str1, str2, len) == 0;
7906 void*
7907 mono_aot_readonly_field_override (MonoClassField *field)
7909 ReadOnlyValue *rdv;
7910 for (rdv = readonly_values; rdv; rdv = rdv->next) {
7911 char *p = rdv->name;
7912 int len;
7913 len = strlen (m_class_get_name_space (field->parent));
7914 if (strncmp (p, m_class_get_name_space (field->parent), len))
7915 continue;
7916 p += len;
7917 if (*p++ != '.')
7918 continue;
7919 len = strlen (m_class_get_name (field->parent));
7920 if (strncmp (p, m_class_get_name (field->parent), len))
7921 continue;
7922 p += len;
7923 if (*p++ != '.')
7924 continue;
7925 if (strcmp (p, field->name))
7926 continue;
7927 switch (rdv->type) {
7928 case MONO_TYPE_I1:
7929 return &rdv->value.i1;
7930 case MONO_TYPE_I2:
7931 return &rdv->value.i2;
7932 case MONO_TYPE_I4:
7933 return &rdv->value.i4;
7934 default:
7935 break;
7938 return NULL;
7941 static void
7942 add_readonly_value (MonoAotOptions *opts, const char *val)
7944 ReadOnlyValue *rdv;
7945 const char *fval;
7946 const char *tval;
7947 /* the format of val is:
7948 * namespace.typename.fieldname=type/value
7949 * type can be i1 for uint8/int8/boolean, i2 for uint16/int16/char, i4 for uint32/int32
7951 fval = strrchr (val, '/');
7952 if (!fval) {
7953 fprintf (stderr, "AOT : invalid format for readonly field '%s', missing /.\n", val);
7954 exit (1);
7956 tval = strrchr (val, '=');
7957 if (!tval) {
7958 fprintf (stderr, "AOT : invalid format for readonly field '%s', missing =.\n", val);
7959 exit (1);
7961 rdv = g_new0 (ReadOnlyValue, 1);
7962 rdv->name = (char *)g_malloc0 (tval - val + 1);
7963 memcpy (rdv->name, val, tval - val);
7964 tval++;
7965 fval++;
7966 if (strncmp (tval, "i1", 2) == 0) {
7967 rdv->value.i1 = atoi (fval);
7968 rdv->type = MONO_TYPE_I1;
7969 } else if (strncmp (tval, "i2", 2) == 0) {
7970 rdv->value.i2 = atoi (fval);
7971 rdv->type = MONO_TYPE_I2;
7972 } else if (strncmp (tval, "i4", 2) == 0) {
7973 rdv->value.i4 = atoi (fval);
7974 rdv->type = MONO_TYPE_I4;
7975 } else {
7976 fprintf (stderr, "AOT : unsupported type for readonly field '%s'.\n", tval);
7977 exit (1);
7979 rdv->next = readonly_values;
7980 readonly_values = rdv;
7983 static gchar *
7984 clean_path (gchar * path)
7986 if (!path)
7987 return NULL;
7989 if (g_str_has_suffix (path, G_DIR_SEPARATOR_S))
7990 return path;
7992 gchar *clean = g_strconcat (path, G_DIR_SEPARATOR_S, (const char*)NULL);
7993 g_free (path);
7995 return clean;
7998 static const gchar *
7999 wrap_path (const gchar * path)
8001 int len;
8002 if (!path)
8003 return NULL;
8005 // If the string contains no spaces, just return the original string.
8006 if (strstr (path, " ") == NULL)
8007 return path;
8009 // If the string is already wrapped in quotes, return it.
8010 len = strlen (path);
8011 if (len >= 2 && path[0] == '\"' && path[len-1] == '\"')
8012 return path;
8014 // If the string contains spaces, then wrap it in quotes.
8015 gchar *clean = g_strdup_printf ("\"%s\"", path);
8017 return clean;
8020 // Duplicate a char range and add it to a ptrarray, but only if it is nonempty
8021 static void
8022 ptr_array_add_range_if_nonempty(GPtrArray *args, gchar const *start, gchar const *end)
8024 ptrdiff_t len = end-start;
8025 if (len > 0)
8026 g_ptr_array_add (args, g_strndup (start, len));
8029 static GPtrArray *
8030 mono_aot_split_options (const char *aot_options)
8032 enum MonoAotOptionState {
8033 MONO_AOT_OPTION_STATE_DEFAULT,
8034 MONO_AOT_OPTION_STATE_STRING,
8035 MONO_AOT_OPTION_STATE_ESCAPE,
8038 GPtrArray *args = g_ptr_array_new ();
8039 enum MonoAotOptionState state = MONO_AOT_OPTION_STATE_DEFAULT;
8040 gchar const *opt_start = aot_options;
8041 gboolean end_of_string = FALSE;
8042 gchar cur;
8044 g_return_val_if_fail (aot_options != NULL, NULL);
8046 while ((cur = *aot_options) != '\0') {
8047 if (state == MONO_AOT_OPTION_STATE_ESCAPE)
8048 goto next;
8050 switch (cur) {
8051 case '"':
8052 // If we find a quote, then if we're in the default case then
8053 // it means we've found the start of a string, if not then it
8054 // means we've found the end of the string and should switch
8055 // back to the default case.
8056 switch (state) {
8057 case MONO_AOT_OPTION_STATE_DEFAULT:
8058 state = MONO_AOT_OPTION_STATE_STRING;
8059 break;
8060 case MONO_AOT_OPTION_STATE_STRING:
8061 state = MONO_AOT_OPTION_STATE_DEFAULT;
8062 break;
8063 case MONO_AOT_OPTION_STATE_ESCAPE:
8064 g_assert_not_reached ();
8065 break;
8067 break;
8068 case '\\':
8069 // If we've found an escaping operator, then this means we
8070 // should not process the next character if inside a string.
8071 if (state == MONO_AOT_OPTION_STATE_STRING)
8072 state = MONO_AOT_OPTION_STATE_ESCAPE;
8073 break;
8074 case ',':
8075 // If we're in the default state then this means we've found
8076 // an option, store it for later processing.
8077 if (state == MONO_AOT_OPTION_STATE_DEFAULT)
8078 goto new_opt;
8079 break;
8082 next:
8083 aot_options++;
8084 restart:
8085 // If the next character is end of string, then process the last option.
8086 if (*(aot_options) == '\0') {
8087 end_of_string = TRUE;
8088 goto new_opt;
8090 continue;
8092 new_opt:
8093 ptr_array_add_range_if_nonempty (args, opt_start, aot_options);
8094 opt_start = ++aot_options;
8095 if (end_of_string)
8096 break;
8097 goto restart; // Check for null and continue loop
8100 return args;
8103 static gboolean
8104 parse_cpu_features (const gchar *attr)
8106 if (!attr || strlen (attr) < 2) {
8107 fprintf (stderr, "Invalid attribute");
8108 return FALSE;
8111 //+foo - enable foo
8112 //foo - enable foo
8113 //-foo - disable foo
8114 gboolean enabled = TRUE;
8115 if (attr [0] == '-')
8116 enabled = FALSE;
8117 int prefix = (attr [0] == '-' || attr [0] == '+') ? 1 : 0;
8118 MonoCPUFeatures feature = (MonoCPUFeatures) 0;
8120 #if defined(TARGET_X86) || defined(TARGET_AMD64)
8121 // e.g.:
8122 // `mattr=+sse3` = +sse,+sse2,+sse3
8123 // `mattr=-sse3` = -sse3,-ssse3,-sse4.1,-sse4.2,-popcnt,-avx,-avx2,-fma
8124 if (!strcmp (attr + prefix, "sse"))
8125 feature = MONO_CPU_X86_SSE_COMBINED;
8126 else if (!strcmp (attr + prefix, "sse2"))
8127 feature = MONO_CPU_X86_SSE2_COMBINED;
8128 else if (!strcmp (attr + prefix, "sse3"))
8129 feature = MONO_CPU_X86_SSE3_COMBINED;
8130 else if (!strcmp (attr + prefix, "ssse3"))
8131 feature = MONO_CPU_X86_SSSE3_COMBINED;
8132 else if (!strcmp (attr + prefix, "sse4.1"))
8133 feature = MONO_CPU_X86_SSE41_COMBINED;
8134 else if (!strcmp (attr + prefix, "sse4.2"))
8135 feature = MONO_CPU_X86_SSE42_COMBINED;
8136 else if (!strcmp (attr + prefix, "avx"))
8137 feature = MONO_CPU_X86_AVX_COMBINED;
8138 else if (!strcmp (attr + prefix, "avx2"))
8139 feature = MONO_CPU_X86_AVX2_COMBINED;
8140 else if (!strcmp (attr + prefix, "pclmul"))
8141 feature = MONO_CPU_X86_PCLMUL_COMBINED;
8142 else if (!strcmp (attr + prefix, "aes"))
8143 feature = MONO_CPU_X86_AES_COMBINED;
8144 else if (!strcmp (attr + prefix, "popcnt"))
8145 feature = MONO_CPU_X86_POPCNT_COMBINED;
8146 else if (!strcmp (attr + prefix, "fma"))
8147 feature = MONO_CPU_X86_FMA_COMBINED;
8148 // these are independent
8149 else if (!strcmp (attr + prefix, "lzcnt")) // technically, it'a a part of BMI but only on Intel
8150 feature = MONO_CPU_X86_LZCNT;
8151 else if (!strcmp (attr + prefix, "bmi")) // NOTE: it's not "bmi1"
8152 feature = MONO_CPU_X86_BMI1;
8153 else if (!strcmp (attr + prefix, "bmi2"))
8154 feature = MONO_CPU_X86_BMI2; // BMI2 doesn't imply BMI1
8155 else {
8156 // we don't have a flag for it but it's probably recognized by opt/llc so let's don't fire an error here
8157 // printf ("Unknown cpu feature: %s\n", attr);
8160 // if we disable a feature from the SSE-AVX tree we also need to disable all dependencies
8161 if (!enabled && (feature & MONO_CPU_X86_FULL_SSEAVX_COMBINED))
8162 feature = (MonoCPUFeatures) (MONO_CPU_X86_FULL_SSEAVX_COMBINED & ~feature);
8164 #elif defined(TARGET_ARM64)
8165 if (!strcmp (attr + prefix, "base"))
8166 feature = MONO_CPU_ARM64_BASE;
8167 else if (!strcmp (attr + prefix, "crc"))
8168 feature = MONO_CPU_ARM64_CRC;
8169 #elif defined(TARGET_WASM)
8170 if (!strcmp (attr + prefix, "simd"))
8171 feature = MONO_CPU_WASM_SIMD;
8172 #endif
8174 if (enabled)
8175 mono_cpu_features_enabled = (MonoCPUFeatures) (mono_cpu_features_enabled | feature);
8176 else
8177 mono_cpu_features_disabled = (MonoCPUFeatures) (mono_cpu_features_disabled | feature);
8179 return TRUE;
8182 static void
8183 mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts)
8185 GPtrArray* args;
8187 args = mono_aot_split_options (aot_options ? aot_options : "");
8188 for (int i = 0; i < args->len; ++i) {
8189 const char *arg = (const char *)g_ptr_array_index (args, i);
8191 if (str_begins_with (arg, "outfile=")) {
8192 opts->outfile = g_strdup (arg + strlen ("outfile="));
8193 } else if (str_begins_with (arg, "llvm-outfile=")) {
8194 opts->llvm_outfile = g_strdup (arg + strlen ("llvm-outfile="));
8195 } else if (str_begins_with (arg, "temp-path=")) {
8196 opts->temp_path = clean_path (g_strdup (arg + strlen ("temp-path=")));
8197 } else if (str_begins_with (arg, "save-temps")) {
8198 opts->save_temps = TRUE;
8199 } else if (str_begins_with (arg, "keep-temps")) {
8200 opts->save_temps = TRUE;
8201 } else if (str_begins_with (arg, "write-symbols")) {
8202 opts->write_symbols = TRUE;
8203 } else if (str_begins_with (arg, "no-write-symbols")) {
8204 opts->write_symbols = FALSE;
8205 // Intentionally undocumented -- one-off experiment
8206 } else if (str_begins_with (arg, "metadata-only")) {
8207 opts->metadata_only = TRUE;
8208 } else if (str_begins_with (arg, "bind-to-runtime-version")) {
8209 opts->bind_to_runtime_version = TRUE;
8210 } else if (str_begins_with (arg, "full")) {
8211 opts->mode = MONO_AOT_MODE_FULL;
8212 } else if (str_begins_with (arg, "hybrid")) {
8213 opts->mode = MONO_AOT_MODE_HYBRID;
8214 } else if (str_begins_with (arg, "interp")) {
8215 opts->interp = TRUE;
8216 } else if (str_begins_with (arg, "threads=")) {
8217 opts->nthreads = atoi (arg + strlen ("threads="));
8218 } else if (str_begins_with (arg, "static")) {
8219 opts->static_link = TRUE;
8220 opts->no_dlsym = TRUE;
8221 } else if (str_begins_with (arg, "asmonly")) {
8222 opts->asm_only = TRUE;
8223 } else if (str_begins_with (arg, "asmwriter")) {
8224 opts->asm_writer = TRUE;
8225 } else if (str_begins_with (arg, "nodebug")) {
8226 opts->nodebug = TRUE;
8227 } else if (str_begins_with (arg, "dwarfdebug")) {
8228 opts->dwarf_debug = TRUE;
8229 // Intentionally undocumented -- No one remembers what this does. It appears to be ARM-only
8230 } else if (str_begins_with (arg, "nopagetrampolines")) {
8231 opts->use_trampolines_page = FALSE;
8232 } else if (str_begins_with (arg, "ntrampolines=")) {
8233 opts->ntrampolines = atoi (arg + strlen ("ntrampolines="));
8234 } else if (str_begins_with (arg, "nrgctx-trampolines=")) {
8235 opts->nrgctx_trampolines = atoi (arg + strlen ("nrgctx-trampolines="));
8236 } else if (str_begins_with (arg, "nrgctx-fetch-trampolines=")) {
8237 opts->nrgctx_fetch_trampolines = atoi (arg + strlen ("nrgctx-fetch-trampolines="));
8238 } else if (str_begins_with (arg, "nimt-trampolines=")) {
8239 opts->nimt_trampolines = atoi (arg + strlen ("nimt-trampolines="));
8240 } else if (str_begins_with (arg, "ngsharedvt-trampolines=")) {
8241 opts->ngsharedvt_arg_trampolines = atoi (arg + strlen ("ngsharedvt-trampolines="));
8242 } else if (str_begins_with (arg, "nftnptr-arg-trampolines=")) {
8243 opts->nftnptr_arg_trampolines = atoi (arg + strlen ("nftnptr-arg-trampolines="));
8244 } else if (str_begins_with (arg, "nunbox-arbitrary-trampolines=")) {
8245 opts->nunbox_arbitrary_trampolines = atoi (arg + strlen ("unbox-arbitrary-trampolines="));
8246 } else if (str_begins_with (arg, "tool-prefix=")) {
8247 opts->tool_prefix = g_strdup (arg + strlen ("tool-prefix="));
8248 } else if (str_begins_with (arg, "ld-flags=")) {
8249 opts->ld_flags = g_strdup (arg + strlen ("ld-flags="));
8250 } else if (str_begins_with (arg, "soft-debug")) {
8251 opts->soft_debug = TRUE;
8252 // Intentionally undocumented x2-- deprecated
8253 } else if (str_begins_with (arg, "gen-seq-points-file=")) {
8254 fprintf (stderr, "Mono Warning: aot option gen-seq-points-file= is deprecated.\n");
8255 } else if (str_begins_with (arg, "gen-seq-points-file")) {
8256 fprintf (stderr, "Mono Warning: aot option gen-seq-points-file is deprecated.\n");
8257 } else if (str_begins_with (arg, "msym-dir=")) {
8258 mini_debug_options.no_seq_points_compact_data = FALSE;
8259 opts->gen_msym_dir = TRUE;
8260 opts->gen_msym_dir_path = g_strdup (arg + strlen ("msym_dir="));
8261 } else if (str_begins_with (arg, "direct-pinvoke")) {
8262 opts->direct_pinvoke = TRUE;
8263 } else if (str_begins_with (arg, "direct-icalls")) {
8264 opts->direct_icalls = TRUE;
8265 } else if (str_begins_with (arg, "direct-extern-calls")) {
8266 opts->direct_extern_calls = TRUE;
8267 } else if (str_begins_with (arg, "no-direct-calls")) {
8268 opts->no_direct_calls = TRUE;
8269 } else if (str_begins_with (arg, "print-skipped")) {
8270 opts->print_skipped_methods = TRUE;
8271 } else if (str_begins_with (arg, "stats")) {
8272 opts->stats = TRUE;
8273 // Intentionally undocumented-- has no known function other than to debug the compiler
8274 } else if (str_begins_with (arg, "no-instances")) {
8275 opts->no_instances = TRUE;
8276 // Intentionally undocumented x4-- Used for internal debugging of compiler
8277 } else if (str_begins_with (arg, "log-generics")) {
8278 opts->log_generics = TRUE;
8279 } else if (str_begins_with (arg, "log-instances=")) {
8280 opts->log_instances = TRUE;
8281 opts->instances_logfile_path = g_strdup (arg + strlen ("log-instances="));
8282 } else if (str_begins_with (arg, "log-instances")) {
8283 opts->log_instances = TRUE;
8284 } else if (str_begins_with (arg, "internal-logfile=")) {
8285 opts->logfile = g_strdup (arg + strlen ("internal-logfile="));
8286 } else if (str_begins_with (arg, "dedup-skip")) {
8287 opts->dedup = TRUE;
8288 } else if (str_begins_with (arg, "dedup-include=")) {
8289 opts->dedup_include = g_strdup (arg + strlen ("dedup-include="));
8290 } else if (str_begins_with (arg, "mtriple=")) {
8291 opts->mtriple = g_strdup (arg + strlen ("mtriple="));
8292 } else if (str_begins_with (arg, "llvm-path=")) {
8293 opts->llvm_path = clean_path (g_strdup (arg + strlen ("llvm-path=")));
8294 } else if (!strcmp (arg, "try-llvm")) {
8295 // If we can load LLVM, use it
8296 // Note: if you call this function from anywhere but mono_compile_assembly,
8297 // this will only set the try_llvm attribute and not do the probing / set the
8298 // attribute.
8299 opts->try_llvm = TRUE;
8300 } else if (!strcmp (arg, "llvm")) {
8301 opts->llvm = TRUE;
8302 } else if (str_begins_with (arg, "readonly-value=")) {
8303 add_readonly_value (opts, arg + strlen ("readonly-value="));
8304 } else if (str_begins_with (arg, "info")) {
8305 printf ("AOT target setup: %s.\n", AOT_TARGET_STR);
8306 exit (0);
8307 // Intentionally undocumented: Used for precise stack maps, which are not available yet
8308 } else if (str_begins_with (arg, "gc-maps")) {
8309 mini_gc_enable_gc_maps_for_aot ();
8310 // Intentionally undocumented: Used for internal debugging
8311 } else if (str_begins_with (arg, "dump")) {
8312 opts->dump_json = TRUE;
8313 } else if (str_begins_with (arg, "llvmonly")) {
8314 opts->mode = MONO_AOT_MODE_FULL;
8315 opts->llvm = TRUE;
8316 opts->llvm_only = TRUE;
8317 } else if (str_begins_with (arg, "data-outfile=")) {
8318 opts->data_outfile = g_strdup (arg + strlen ("data-outfile="));
8319 } else if (str_begins_with (arg, "profile=")) {
8320 opts->profile_files = g_list_append (opts->profile_files, g_strdup (arg + strlen ("profile=")));
8321 } else if (!strcmp (arg, "profile-only")) {
8322 opts->profile_only = TRUE;
8323 } else if (!strcmp (arg, "verbose")) {
8324 opts->verbose = TRUE;
8325 } else if (str_begins_with (arg, "llvmopts=")){
8326 if (opts->llvm_opts) {
8327 char *s = g_strdup_printf ("%s %s", opts->llvm_opts, arg + strlen ("llvmopts="));
8328 g_free (opts->llvm_opts);
8329 opts->llvm_opts = s;
8330 } else {
8331 opts->llvm_opts = g_strdup (arg + strlen ("llvmopts="));
8333 } else if (str_begins_with (arg, "llvmllc=")){
8334 opts->llvm_llc = g_strdup (arg + strlen ("llvmllc="));
8335 } else if (!strcmp (arg, "deterministic")) {
8336 opts->deterministic = TRUE;
8337 } else if (!strcmp (arg, "no-opt")) {
8338 opts->no_opt = TRUE;
8339 } else if (str_begins_with (arg, "clangxx=")) {
8340 opts->clangxx = g_strdup (arg + strlen ("clangxx="));
8341 } else if (str_begins_with (arg, "mcpu=")) {
8342 if (!strcmp(arg, "mcpu=native")) {
8343 opts->use_current_cpu = TRUE;
8344 } else if (!strcmp(arg, "mcpu=generic")) {
8345 opts->use_current_cpu = FALSE;
8346 } else {
8347 printf ("mcpu can only be 'native' or 'generic' (default).\n");
8348 exit (0);
8350 } else if (str_begins_with (arg, "mattr=")) {
8351 gchar* attr = g_strdup (arg + strlen ("mattr="));
8352 if (!parse_cpu_features (attr))
8353 exit (0);
8354 // mattr can be declared more than once, e.g.
8355 // `mattr=avx2,mattr=lzcnt,mattr=bmi2`
8356 if (!opts->llvm_cpu_attr)
8357 opts->llvm_cpu_attr = attr;
8358 else {
8359 char* old_attrs = opts->llvm_cpu_attr;
8360 opts->llvm_cpu_attr = g_strdup_printf ("%s,%s", opts->llvm_cpu_attr, attr);
8361 g_free (old_attrs);
8363 } else if (str_begins_with (arg, "depfile=")) {
8364 opts->depfile = g_strdup (arg + strlen ("depfile="));
8365 } else if (str_begins_with (arg, "help") || str_begins_with (arg, "?")) {
8366 printf ("Supported options for --aot:\n");
8367 printf (" asmonly\n");
8368 printf (" bind-to-runtime-version\n");
8369 printf (" bitcode\n");
8370 printf (" data-outfile=\n");
8371 printf (" direct-icalls\n");
8372 printf (" direct-pinvoke\n");
8373 printf (" dwarfdebug\n");
8374 printf (" full\n");
8375 printf (" hybrid\n");
8376 printf (" info\n");
8377 printf (" keep-temps\n");
8378 printf (" llvm\n");
8379 printf (" llvmonly\n");
8380 printf (" llvm-outfile=\n");
8381 printf (" llvm-path=\n");
8382 printf (" msym-dir=\n");
8383 printf (" mtriple\n");
8384 printf (" nimt-trampolines=\n");
8385 printf (" nodebug\n");
8386 printf (" no-direct-calls\n");
8387 printf (" no-write-symbols\n");
8388 printf (" nrgctx-trampolines=\n");
8389 printf (" nrgctx-fetch-trampolines=\n");
8390 printf (" ngsharedvt-trampolines=\n");
8391 printf (" nftnptr-arg-trampolines=\n");
8392 printf (" nunbox-arbitrary-trampolines=\n");
8393 printf (" ntrampolines=\n");
8394 printf (" outfile=\n");
8395 printf (" profile=\n");
8396 printf (" profile-only\n");
8397 printf (" print-skipped-methods\n");
8398 printf (" readonly-value=\n");
8399 printf (" save-temps\n");
8400 printf (" soft-debug\n");
8401 printf (" static\n");
8402 printf (" stats\n");
8403 printf (" temp-path=\n");
8404 printf (" tool-prefix=\n");
8405 printf (" threads=\n");
8406 printf (" write-symbols\n");
8407 printf (" verbose\n");
8408 printf (" no-opt\n");
8409 printf (" llvmopts=\n");
8410 printf (" llvmllc=\n");
8411 printf (" clangxx=\n");
8412 printf (" depfile=\n");
8413 printf (" mcpu=\n");
8414 printf (" mattr=\n");
8415 printf (" help/?\n");
8416 exit (0);
8417 } else {
8418 fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg);
8419 exit (1);
8422 g_free ((gpointer) arg);
8425 if (opts->use_trampolines_page) {
8426 opts->ntrampolines = 0;
8427 opts->nrgctx_trampolines = 0;
8428 opts->nimt_trampolines = 0;
8429 opts->ngsharedvt_arg_trampolines = 0;
8430 opts->nftnptr_arg_trampolines = 0;
8431 opts->nunbox_arbitrary_trampolines = 0;
8434 g_ptr_array_free (args, /*free_seg=*/TRUE);
8437 static void
8438 add_token_info_hash (gpointer key, gpointer value, gpointer user_data)
8440 MonoMethod *method = (MonoMethod*)key;
8441 MonoJumpInfoToken *ji = (MonoJumpInfoToken*)value;
8442 MonoAotCompile *acfg = (MonoAotCompile *)user_data;
8443 MonoJumpInfoToken *new_ji;
8445 new_ji = (MonoJumpInfoToken *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfoToken));
8446 new_ji->image = ji->image;
8447 new_ji->token = ji->token;
8448 g_hash_table_insert (acfg->token_info_hash, method, new_ji);
8451 static gboolean
8452 can_encode_class (MonoAotCompile *acfg, MonoClass *klass)
8454 if (m_class_get_type_token (klass))
8455 return TRUE;
8456 if ((m_class_get_byval_arg (klass)->type == MONO_TYPE_VAR) || (m_class_get_byval_arg (klass)->type == MONO_TYPE_MVAR) || (m_class_get_byval_arg (klass)->type == MONO_TYPE_PTR))
8457 return TRUE;
8458 if (m_class_get_rank (klass))
8459 return can_encode_class (acfg, m_class_get_element_class (klass));
8460 return FALSE;
8463 static gboolean
8464 can_encode_method (MonoAotCompile *acfg, MonoMethod *method)
8466 if (method->wrapper_type) {
8467 switch (method->wrapper_type) {
8468 case MONO_WRAPPER_NONE:
8469 case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
8470 case MONO_WRAPPER_XDOMAIN_INVOKE:
8471 case MONO_WRAPPER_STFLD:
8472 case MONO_WRAPPER_LDFLD:
8473 case MONO_WRAPPER_LDFLDA:
8474 case MONO_WRAPPER_STELEMREF:
8475 case MONO_WRAPPER_PROXY_ISINST:
8476 case MONO_WRAPPER_ALLOC:
8477 case MONO_WRAPPER_REMOTING_INVOKE:
8478 case MONO_WRAPPER_OTHER:
8479 case MONO_WRAPPER_WRITE_BARRIER:
8480 case MONO_WRAPPER_DELEGATE_INVOKE:
8481 case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE:
8482 case MONO_WRAPPER_DELEGATE_END_INVOKE:
8483 case MONO_WRAPPER_SYNCHRONIZED:
8484 case MONO_WRAPPER_MANAGED_TO_NATIVE:
8485 break;
8486 case MONO_WRAPPER_MANAGED_TO_MANAGED:
8487 case MONO_WRAPPER_CASTCLASS: {
8488 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
8490 if (info)
8491 return TRUE;
8492 else
8493 return FALSE;
8494 break;
8496 default:
8497 //printf ("Skip (wrapper call): %d -> %s\n", patch_info->type, mono_method_full_name (patch_info->data.method, TRUE));
8498 return FALSE;
8500 } else {
8501 if (!method->token) {
8502 /* The method is part of a constructed type like Int[,].Set (). */
8503 if (!g_hash_table_lookup (acfg->token_info_hash, method)) {
8504 if (m_class_get_rank (method->klass))
8505 return TRUE;
8506 return FALSE;
8510 return TRUE;
8513 static gboolean
8514 can_encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info)
8516 switch (patch_info->type) {
8517 case MONO_PATCH_INFO_METHOD:
8518 case MONO_PATCH_INFO_METHOD_FTNDESC:
8519 case MONO_PATCH_INFO_METHODCONST:
8520 case MONO_PATCH_INFO_METHOD_CODE_SLOT:
8521 case MONO_PATCH_INFO_METHOD_PINVOKE_ADDR_CACHE: {
8522 MonoMethod *method = patch_info->data.method;
8524 return can_encode_method (acfg, method);
8526 case MONO_PATCH_INFO_VTABLE:
8527 case MONO_PATCH_INFO_CLASS:
8528 case MONO_PATCH_INFO_IID:
8529 case MONO_PATCH_INFO_ADJUSTED_IID:
8530 if (!can_encode_class (acfg, patch_info->data.klass)) {
8531 //printf ("Skip: %s\n", mono_type_full_name (m_class_get_byval_arg (patch_info->data.klass)));
8532 return FALSE;
8534 break;
8535 case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: {
8536 if (!can_encode_class (acfg, patch_info->data.del_tramp->klass)) {
8537 //printf ("Skip: %s\n", mono_type_full_name (m_class_get_byval_arg (patch_info->data.klass)));
8538 return FALSE;
8540 break;
8542 case MONO_PATCH_INFO_RGCTX_FETCH:
8543 case MONO_PATCH_INFO_RGCTX_SLOT_INDEX: {
8544 MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry;
8546 if (entry->in_mrgctx) {
8547 if (!can_encode_method (acfg, entry->d.method))
8548 return FALSE;
8549 } else {
8550 if (!can_encode_class (acfg, entry->d.klass))
8551 return FALSE;
8553 if (!can_encode_patch (acfg, entry->data))
8554 return FALSE;
8555 break;
8557 default:
8558 break;
8561 return TRUE;
8564 static gboolean
8565 is_concrete_type (MonoType *t)
8567 MonoClass *klass;
8568 int i;
8570 if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR)
8571 return FALSE;
8572 if (t->type == MONO_TYPE_GENERICINST) {
8573 MonoGenericContext *orig_ctx;
8574 MonoGenericInst *inst;
8575 MonoType *arg;
8577 if (!MONO_TYPE_ISSTRUCT (t))
8578 return TRUE;
8579 klass = mono_class_from_mono_type_internal (t);
8580 orig_ctx = &mono_class_get_generic_class (klass)->context;
8582 inst = orig_ctx->class_inst;
8583 if (inst) {
8584 for (i = 0; i < inst->type_argc; ++i) {
8585 arg = mini_get_underlying_type (inst->type_argv [i]);
8586 if (!is_concrete_type (arg))
8587 return FALSE;
8590 inst = orig_ctx->method_inst;
8591 if (inst) {
8592 for (i = 0; i < inst->type_argc; ++i) {
8593 arg = mini_get_underlying_type (inst->type_argv [i]);
8594 if (!is_concrete_type (arg))
8595 return FALSE;
8599 return TRUE;
8602 static MonoMethodSignature*
8603 get_concrete_sig (MonoMethodSignature *sig)
8605 gboolean concrete = TRUE;
8607 if (!sig->has_type_parameters)
8608 return sig;
8610 /* For signatures created during generic sharing, convert them to a concrete signature if possible */
8611 MonoMethodSignature *copy = mono_metadata_signature_dup (sig);
8612 int i;
8614 //printf ("%s\n", mono_signature_full_name (sig));
8616 if (sig->ret->byref)
8617 copy->ret = m_class_get_this_arg (mono_defaults.int_class);
8618 else
8619 copy->ret = mini_get_underlying_type (sig->ret);
8620 if (!is_concrete_type (copy->ret))
8621 concrete = FALSE;
8622 for (i = 0; i < sig->param_count; ++i) {
8623 if (sig->params [i]->byref) {
8624 MonoType *t = m_class_get_byval_arg (mono_class_from_mono_type_internal (sig->params [i]));
8625 t = mini_get_underlying_type (t);
8626 copy->params [i] = m_class_get_this_arg (mono_class_from_mono_type_internal (t));
8627 } else {
8628 copy->params [i] = mini_get_underlying_type (sig->params [i]);
8630 if (!is_concrete_type (copy->params [i]))
8631 concrete = FALSE;
8633 copy->has_type_parameters = 0;
8634 if (!concrete)
8635 return NULL;
8636 return copy;
8639 /* LOCKING: Assumes the loader lock is held */
8640 static void
8641 add_gsharedvt_wrappers (MonoAotCompile *acfg, MonoMethodSignature *sig, gboolean gsharedvt_in, gboolean gsharedvt_out, gboolean interp_in)
8643 MonoMethod *wrapper;
8644 gboolean add_in = gsharedvt_in;
8645 gboolean add_out = gsharedvt_out;
8647 if (gsharedvt_in && g_hash_table_lookup (acfg->gsharedvt_in_signatures, sig))
8648 add_in = FALSE;
8649 if (gsharedvt_out && g_hash_table_lookup (acfg->gsharedvt_out_signatures, sig))
8650 add_out = FALSE;
8652 if (!add_in && !add_out && !interp_in)
8653 return;
8655 if (mini_is_gsharedvt_variable_signature (sig))
8656 return;
8658 if (add_in)
8659 g_hash_table_insert (acfg->gsharedvt_in_signatures, sig, sig);
8660 if (add_out)
8661 g_hash_table_insert (acfg->gsharedvt_out_signatures, sig, sig);
8663 sig = get_concrete_sig (sig);
8664 if (!sig)
8665 return;
8666 //printf ("%s\n", mono_signature_full_name (sig));
8668 if (gsharedvt_in) {
8669 wrapper = mini_get_gsharedvt_in_sig_wrapper (sig);
8670 add_extra_method (acfg, wrapper);
8672 if (gsharedvt_out) {
8673 wrapper = mini_get_gsharedvt_out_sig_wrapper (sig);
8674 add_extra_method (acfg, wrapper);
8677 #ifndef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
8678 if (interp_in) {
8679 wrapper = mini_get_interp_in_wrapper (sig);
8680 add_extra_method (acfg, wrapper);
8682 #endif
8686 * compile_method:
8688 * AOT compile a given method.
8689 * This function might be called by multiple threads, so it must be thread-safe.
8691 static void
8692 compile_method (MonoAotCompile *acfg, MonoMethod *method)
8694 MonoCompile *cfg;
8695 MonoJumpInfo *patch_info;
8696 gboolean skip;
8697 int index, depth;
8698 MonoMethod *wrapped;
8699 gint64 jit_time_start;
8700 JitFlags flags;
8702 if (acfg->aot_opts.metadata_only)
8703 return;
8705 mono_acfg_lock (acfg);
8706 index = get_method_index (acfg, method);
8707 mono_acfg_unlock (acfg);
8709 /* fixme: maybe we can also precompile wrapper methods */
8710 if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
8711 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
8712 (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
8713 //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE));
8714 return;
8717 if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
8718 return;
8720 wrapped = mono_marshal_method_from_wrapper (method);
8721 if (wrapped && (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && wrapped->is_generic)
8722 // FIXME: The wrapper should be generic too, but it is not
8723 return;
8725 if (method->wrapper_type == MONO_WRAPPER_COMINTEROP)
8726 return;
8728 if (acfg->aot_opts.profile_only && !g_hash_table_lookup (acfg->profile_methods, method)) {
8729 if (acfg->aot_opts.llvm_only) {
8730 gboolean keep = FALSE;
8731 if (method->wrapper_type) {
8732 /* Keep most wrappers */
8733 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
8734 switch (info->subtype) {
8735 case WRAPPER_SUBTYPE_PTR_TO_STRUCTURE:
8736 case WRAPPER_SUBTYPE_STRUCTURE_TO_PTR:
8737 break;
8738 default:
8739 keep = TRUE;
8740 break;
8743 if (always_aot (method))
8744 keep = TRUE;
8745 if (!keep)
8746 return;
8747 } else {
8748 if (!method->is_inflated)
8749 return;
8753 mono_atomic_inc_i32 (&acfg->stats.mcount);
8755 #if 0
8756 if (method->is_generic || mono_class_is_gtd (method->klass)) {
8757 mono_atomic_inc_i32 (&acfg->stats.genericcount);
8758 return;
8760 #endif
8762 //acfg->aot_opts.print_skipped_methods = TRUE;
8765 * Since these methods are the only ones which are compiled with
8766 * AOT support, and they are not used by runtime startup/shutdown code,
8767 * the runtime will not see AOT methods during AOT compilation,so it
8768 * does not need to support them by creating a fake GOT etc.
8770 flags = JIT_FLAG_AOT;
8771 if (mono_aot_mode_is_full (&acfg->aot_opts))
8772 flags = (JitFlags)(flags | JIT_FLAG_FULL_AOT);
8773 if (acfg->llvm)
8774 flags = (JitFlags)(flags | JIT_FLAG_LLVM);
8775 if (acfg->aot_opts.llvm_only)
8776 flags = (JitFlags)(flags | JIT_FLAG_LLVM_ONLY | JIT_FLAG_EXPLICIT_NULL_CHECKS);
8777 if (acfg->aot_opts.no_direct_calls)
8778 flags = (JitFlags)(flags | JIT_FLAG_NO_DIRECT_ICALLS);
8779 if (acfg->aot_opts.direct_pinvoke)
8780 flags = (JitFlags)(flags | JIT_FLAG_DIRECT_PINVOKE);
8781 if (acfg->aot_opts.interp)
8782 flags = (JitFlags)(flags | JIT_FLAG_INTERP);
8783 if (acfg->aot_opts.use_current_cpu)
8784 flags = (JitFlags)(flags | JIT_FLAG_USE_CURRENT_CPU);
8785 if (method_is_externally_callable (acfg, method))
8786 flags = (JitFlags)(flags | JIT_FLAG_SELF_INIT);
8787 if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY)
8788 flags = (JitFlags)(flags | JIT_FLAG_CODE_EXEC_ONLY);
8790 jit_time_start = mono_time_track_start ();
8791 cfg = mini_method_compile (method, acfg->jit_opts, mono_get_root_domain (), flags, 0, index);
8792 mono_time_track_end (&mono_jit_stats.jit_time, jit_time_start);
8794 if (cfg->exception_type == MONO_EXCEPTION_GENERIC_SHARING_FAILED) {
8795 if (acfg->aot_opts.print_skipped_methods)
8796 printf ("Skip (gshared failure): %s (%s)\n", mono_method_get_full_name (method), cfg->exception_message);
8797 mono_atomic_inc_i32 (&acfg->stats.genericcount);
8798 return;
8800 if (cfg->exception_type != MONO_EXCEPTION_NONE) {
8801 /* Some instances cannot be JITted due to constraints etc. */
8802 if (!method->is_inflated)
8803 report_loader_error (acfg, cfg->error, FALSE, "Unable to compile method '%s' due to: '%s'.\n", mono_method_get_full_name (method), mono_error_get_message (cfg->error));
8804 /* Let the exception happen at runtime */
8805 return;
8808 if (cfg->disable_aot) {
8809 if (acfg->aot_opts.print_skipped_methods)
8810 printf ("Skip (disabled): %s\n", mono_method_get_full_name (method));
8811 mono_atomic_inc_i32 (&acfg->stats.ocount);
8812 return;
8814 cfg->method_index = index;
8816 /* Nullify patches which need no aot processing */
8817 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
8818 switch (patch_info->type) {
8819 case MONO_PATCH_INFO_LABEL:
8820 case MONO_PATCH_INFO_BB:
8821 patch_info->type = MONO_PATCH_INFO_NONE;
8822 break;
8823 default:
8824 break;
8828 /* Collect method->token associations from the cfg */
8829 mono_acfg_lock (acfg);
8830 g_hash_table_foreach (cfg->token_info_hash, add_token_info_hash, acfg);
8831 mono_acfg_unlock (acfg);
8832 g_hash_table_destroy (cfg->token_info_hash);
8833 cfg->token_info_hash = NULL;
8836 * Check for absolute addresses.
8838 skip = FALSE;
8839 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
8840 switch (patch_info->type) {
8841 case MONO_PATCH_INFO_ABS:
8842 /* unable to handle this */
8843 skip = TRUE;
8844 break;
8845 default:
8846 break;
8850 if (skip) {
8851 if (acfg->aot_opts.print_skipped_methods)
8852 printf ("Skip (abs call): %s\n", mono_method_get_full_name (method));
8853 mono_atomic_inc_i32 (&acfg->stats.abscount);
8854 return;
8857 /* Lock for the rest of the code */
8858 mono_acfg_lock (acfg);
8860 if (cfg->gsharedvt)
8861 acfg->stats.method_categories [METHOD_CAT_GSHAREDVT] ++;
8862 else if (cfg->gshared)
8863 acfg->stats.method_categories [METHOD_CAT_INST] ++;
8864 else if (cfg->method->wrapper_type)
8865 acfg->stats.method_categories [METHOD_CAT_WRAPPER] ++;
8866 else
8867 acfg->stats.method_categories [METHOD_CAT_NORMAL] ++;
8870 * Check for methods/klasses we can't encode.
8872 skip = FALSE;
8873 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
8874 if (!can_encode_patch (acfg, patch_info))
8875 skip = TRUE;
8878 if (skip) {
8879 if (acfg->aot_opts.print_skipped_methods)
8880 printf ("Skip (patches): %s\n", mono_method_get_full_name (method));
8881 acfg->stats.ocount++;
8882 mono_acfg_unlock (acfg);
8883 return;
8886 if (!cfg->compile_llvm)
8887 acfg->has_jitted_code = TRUE;
8889 if (method->is_inflated && acfg->aot_opts.log_instances) {
8890 if (acfg->instances_logfile)
8891 fprintf (acfg->instances_logfile, "%s ### %d\n", mono_method_get_full_name (method), cfg->code_size);
8892 else
8893 printf ("%s ### %d\n", mono_method_get_full_name (method), cfg->code_size);
8896 /* Adds generic instances referenced by this method */
8898 * The depth is used to avoid infinite loops when generic virtual recursion is
8899 * encountered.
8901 depth = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_depth, method));
8902 if (!acfg->aot_opts.no_instances && depth < 32 && (mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts))) {
8903 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
8904 switch (patch_info->type) {
8905 case MONO_PATCH_INFO_RGCTX_FETCH:
8906 case MONO_PATCH_INFO_RGCTX_SLOT_INDEX:
8907 case MONO_PATCH_INFO_METHOD:
8908 case MONO_PATCH_INFO_METHOD_FTNDESC:
8909 case MONO_PATCH_INFO_METHOD_RGCTX: {
8910 MonoMethod *m = NULL;
8912 if (patch_info->type == MONO_PATCH_INFO_RGCTX_FETCH || patch_info->type == MONO_PATCH_INFO_RGCTX_SLOT_INDEX) {
8913 MonoJumpInfoRgctxEntry *e = patch_info->data.rgctx_entry;
8915 if (e->info_type == MONO_RGCTX_INFO_GENERIC_METHOD_CODE || e->info_type == MONO_RGCTX_INFO_METHOD_FTNDESC)
8916 m = e->data->data.method;
8917 } else {
8918 m = patch_info->data.method;
8921 if (!m)
8922 break;
8923 if (m->is_inflated && (mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts))) {
8924 if (!(mono_class_generic_sharing_enabled (m->klass) &&
8925 mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) &&
8926 (!method_has_type_vars (m) || mono_method_is_generic_sharable_full (m, TRUE, TRUE, FALSE))) {
8927 if (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
8928 if (mono_aot_mode_is_full (&acfg->aot_opts) && !method_has_type_vars (m))
8929 add_extra_method_with_depth (acfg, mono_marshal_get_native_wrapper (m, TRUE, TRUE), depth + 1);
8930 } else {
8931 add_extra_method_with_depth (acfg, m, depth + 1);
8932 add_types_from_method_header (acfg, m);
8935 add_generic_class_with_depth (acfg, m->klass, depth + 5, "method");
8937 if (m->wrapper_type == MONO_WRAPPER_MANAGED_TO_MANAGED) {
8938 WrapperInfo *info = mono_marshal_get_wrapper_info (m);
8940 if (info && info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR)
8941 add_extra_method_with_depth (acfg, m, depth + 1);
8943 break;
8945 case MONO_PATCH_INFO_VTABLE: {
8946 MonoClass *klass = patch_info->data.klass;
8948 if (mono_class_is_ginst (klass) && !mini_class_is_generic_sharable (klass))
8949 add_generic_class_with_depth (acfg, klass, depth + 5, "vtable");
8950 break;
8952 case MONO_PATCH_INFO_SFLDA: {
8953 MonoClass *klass = patch_info->data.field->parent;
8955 /* The .cctor needs to run at runtime. */
8956 if (mono_class_is_ginst (klass) && !mono_generic_context_is_sharable_full (&mono_class_get_generic_class (klass)->context, FALSE, FALSE) && mono_class_get_cctor (klass))
8957 add_extra_method_with_depth (acfg, mono_class_get_cctor (klass), depth + 1);
8958 break;
8960 default:
8961 break;
8966 /* Determine whenever the method has GOT slots */
8967 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
8968 switch (patch_info->type) {
8969 case MONO_PATCH_INFO_GOT_OFFSET:
8970 case MONO_PATCH_INFO_NONE:
8971 case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR:
8972 case MONO_PATCH_INFO_GC_NURSERY_START:
8973 case MONO_PATCH_INFO_GC_NURSERY_BITS:
8974 break;
8975 case MONO_PATCH_INFO_IMAGE:
8976 /* The assembly is stored in GOT slot 0 */
8977 if (patch_info->data.image != acfg->image)
8978 cfg->has_got_slots = TRUE;
8979 break;
8980 default:
8981 if (!is_plt_patch (patch_info) || (cfg->compile_llvm && acfg->aot_opts.llvm_only))
8982 cfg->has_got_slots = TRUE;
8983 break;
8987 if (!cfg->has_got_slots)
8988 mono_atomic_inc_i32 (&acfg->stats.methods_without_got_slots);
8990 /* Add gsharedvt wrappers for signatures used by the method */
8991 if (acfg->aot_opts.llvm_only) {
8992 GSList *l;
8994 if (!cfg->method->wrapper_type || cfg->method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE)
8995 /* These only need out wrappers */
8996 add_gsharedvt_wrappers (acfg, mono_method_signature_internal (cfg->method), FALSE, TRUE, FALSE);
8998 for (l = cfg->signatures; l; l = l->next) {
8999 MonoMethodSignature *sig = mono_metadata_signature_dup ((MonoMethodSignature*)l->data);
9001 /* These only need in wrappers */
9002 add_gsharedvt_wrappers (acfg, sig, TRUE, FALSE, FALSE);
9005 for (l = cfg->interp_in_signatures; l; l = l->next) {
9006 MonoMethodSignature *sig = mono_metadata_signature_dup ((MonoMethodSignature*)l->data);
9009 * Interpreter methods in llvmonly+interp mode are called using gsharedvt_in wrappers,
9010 * since we already generate those in llvmonly mode. But methods with a large
9011 * number of arguments need special processing (see interp_create_method_pointer_llvmonly),
9012 * which only interp_in wrappers do.
9014 if (sig->param_count > MAX_INTERP_ENTRY_ARGS)
9015 add_gsharedvt_wrappers (acfg, sig, FALSE, FALSE, TRUE);
9016 else
9017 add_gsharedvt_wrappers (acfg, sig, TRUE, FALSE, FALSE);
9019 } else if (mono_aot_mode_is_full (&acfg->aot_opts) && mono_aot_mode_is_interp (&acfg->aot_opts)) {
9020 /* The interpreter uses these wrappers to call aot-ed code */
9021 if (!cfg->method->wrapper_type || cfg->method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE)
9022 add_gsharedvt_wrappers (acfg, mono_method_signature_internal (cfg->method), FALSE, TRUE, TRUE);
9025 if (cfg->llvm_only)
9026 acfg->stats.llvm_count ++;
9028 if (acfg->llvm && !cfg->compile_llvm && method_is_externally_callable (acfg, cfg->method)) {
9030 * This is a JITted fallback method for a method which failed LLVM compilation, emit a global
9031 * symbol for it with the same name the LLVM method would get.
9033 char *name = mono_aot_get_mangled_method_name (cfg->method);
9034 char *export_name = g_strdup_printf ("%s%s", acfg->user_symbol_prefix, name);
9035 g_hash_table_insert (acfg->export_names, cfg->method, export_name);
9039 * FIXME: Instead of this mess, allocate the patches from the aot mempool.
9041 /* Make a copy of the patch info which is in the mempool */
9043 MonoJumpInfo *patches = NULL, *patches_end = NULL;
9045 for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
9046 MonoJumpInfo *new_patch_info = mono_patch_info_dup_mp (acfg->mempool, patch_info);
9048 if (!patches)
9049 patches = new_patch_info;
9050 else
9051 patches_end->next = new_patch_info;
9052 patches_end = new_patch_info;
9054 cfg->patch_info = patches;
9056 /* Make a copy of the unwind info */
9058 GSList *l, *unwind_ops;
9059 MonoUnwindOp *op;
9061 unwind_ops = NULL;
9062 for (l = cfg->unwind_ops; l; l = l->next) {
9063 op = (MonoUnwindOp *)mono_mempool_alloc (acfg->mempool, sizeof (MonoUnwindOp));
9064 memcpy (op, l->data, sizeof (MonoUnwindOp));
9065 unwind_ops = g_slist_prepend_mempool (acfg->mempool, unwind_ops, op);
9067 cfg->unwind_ops = g_slist_reverse (unwind_ops);
9069 /* Make a copy of the argument/local info */
9071 ERROR_DECL (error);
9072 MonoInst **args, **locals;
9073 MonoMethodSignature *sig;
9074 MonoMethodHeader *header;
9075 int i;
9077 sig = mono_method_signature_internal (method);
9078 args = (MonoInst **)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst*) * (sig->param_count + sig->hasthis));
9079 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
9080 args [i] = (MonoInst *)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst));
9081 memcpy (args [i], cfg->args [i], sizeof (MonoInst));
9083 cfg->args = args;
9085 header = mono_method_get_header_checked (method, error);
9086 mono_error_assert_ok (error); /* FIXME don't swallow the error */
9087 locals = (MonoInst **)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst*) * header->num_locals);
9088 for (i = 0; i < header->num_locals; ++i) {
9089 locals [i] = (MonoInst *)mono_mempool_alloc (acfg->mempool, sizeof (MonoInst));
9090 memcpy (locals [i], cfg->locals [i], sizeof (MonoInst));
9092 mono_metadata_free_mh (header);
9093 cfg->locals = locals;
9096 /* Free some fields used by cfg to conserve memory */
9097 mono_empty_compile (cfg);
9099 //printf ("Compile: %s\n", mono_method_full_name (method, TRUE));
9101 while (index >= acfg->cfgs_size) {
9102 MonoCompile **new_cfgs;
9103 int new_size;
9105 new_size = acfg->cfgs_size * 2;
9106 new_cfgs = g_new0 (MonoCompile*, new_size);
9107 memcpy (new_cfgs, acfg->cfgs, sizeof (MonoCompile*) * acfg->cfgs_size);
9108 g_free (acfg->cfgs);
9109 acfg->cfgs = new_cfgs;
9110 acfg->cfgs_size = new_size;
9112 acfg->cfgs [index] = cfg;
9114 g_hash_table_insert (acfg->method_to_cfg, cfg->orig_method, cfg);
9116 /* Update global stats while holding a lock. */
9117 mono_update_jit_stats (cfg);
9120 if (cfg->orig_method->wrapper_type)
9121 g_ptr_array_add (acfg->extra_methods, cfg->orig_method);
9124 mono_acfg_unlock (acfg);
9126 mono_atomic_inc_i32 (&acfg->stats.ccount);
9129 static mono_thread_start_return_t WINAPI
9130 compile_thread_main (gpointer user_data)
9132 MonoAotCompile *acfg = ((MonoAotCompile **)user_data) [0];
9133 GPtrArray *methods = ((GPtrArray **)user_data) [1];
9134 int i;
9136 mono_thread_set_name_constant_ignore_error (mono_thread_internal_current (), "AOT compiler", MonoSetThreadNameFlag_Permanent);
9138 for (i = 0; i < methods->len; ++i)
9139 compile_method (acfg, (MonoMethod *)g_ptr_array_index (methods, i));
9141 return 0;
9144 /* Used by the LLVM backend */
9145 guint32
9146 mono_aot_get_got_offset (MonoJumpInfo *ji)
9148 return get_got_offset (llvm_acfg, TRUE, ji);
9152 * mono_aot_is_shared_got_offset:
9154 * Return whenever OFFSET refers to a GOT slot which is preinitialized
9155 * when the AOT image is loaded.
9157 gboolean
9158 mono_aot_is_shared_got_offset (int offset)
9160 return offset < llvm_acfg->nshared_got_entries;
9163 gboolean
9164 mono_aot_is_externally_callable (MonoMethod *cmethod)
9166 return method_is_externally_callable (llvm_acfg, cmethod);
9169 char*
9170 mono_aot_get_method_name (MonoCompile *cfg)
9172 MonoMethod *method = cfg->orig_method;
9174 /* Use the mangled name if possible */
9175 if (method->wrapper_type == MONO_WRAPPER_OTHER) {
9176 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
9177 if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG || info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG) {
9178 char *name, *s;
9179 name = mono_aot_get_mangled_method_name (method);
9180 if (llvm_acfg->aot_opts.static_link) {
9181 /* Include the assembly name too to avoid duplicate symbol errors */
9182 s = g_strdup_printf ("%s_%s", llvm_acfg->assembly_name_sym, name);
9183 g_free (name);
9184 return s;
9185 } else {
9186 return name;
9191 if (llvm_acfg->aot_opts.static_link)
9192 /* Include the assembly name too to avoid duplicate symbol errors */
9193 return g_strdup_printf ("%s_%s", llvm_acfg->assembly_name_sym, get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash));
9194 else
9195 return get_debug_sym (cfg->orig_method, "", llvm_acfg->method_label_hash);
9198 static gboolean
9199 append_mangled_type (GString *s, MonoType *t)
9201 if (t->byref)
9202 g_string_append_printf (s, "b");
9203 switch (t->type) {
9204 case MONO_TYPE_VOID:
9205 g_string_append_printf (s, "void");
9206 break;
9207 case MONO_TYPE_BOOLEAN:
9208 g_string_append_printf (s, "bool");
9209 break;
9210 case MONO_TYPE_CHAR:
9211 g_string_append_printf (s, "char");
9212 break;
9213 case MONO_TYPE_I1:
9214 g_string_append_printf (s, "i1");
9215 break;
9216 case MONO_TYPE_U1:
9217 g_string_append_printf (s, "u1");
9218 break;
9219 case MONO_TYPE_I2:
9220 g_string_append_printf (s, "i2");
9221 break;
9222 case MONO_TYPE_U2:
9223 g_string_append_printf (s, "u2");
9224 break;
9225 case MONO_TYPE_I4:
9226 g_string_append_printf (s, "i4");
9227 break;
9228 case MONO_TYPE_U4:
9229 g_string_append_printf (s, "u4");
9230 break;
9231 case MONO_TYPE_I8:
9232 g_string_append_printf (s, "i8");
9233 break;
9234 case MONO_TYPE_U8:
9235 g_string_append_printf (s, "u8");
9236 break;
9237 case MONO_TYPE_I:
9238 g_string_append_printf (s, "ii");
9239 break;
9240 case MONO_TYPE_U:
9241 g_string_append_printf (s, "ui");
9242 break;
9243 case MONO_TYPE_R4:
9244 g_string_append_printf (s, "fl");
9245 break;
9246 case MONO_TYPE_R8:
9247 g_string_append_printf (s, "do");
9248 break;
9249 case MONO_TYPE_OBJECT:
9250 g_string_append_printf (s, "obj");
9251 break;
9252 default: {
9253 char *fullname = mono_type_full_name (t);
9254 char *name = fullname;
9255 GString *temp;
9256 char *temps;
9257 gboolean is_system = FALSE;
9258 int i, len;
9260 len = strlen ("System.");
9261 if (strncmp (fullname, "System.", len) == 0) {
9262 name = fullname + len;
9263 is_system = TRUE;
9266 * Have to create a mangled name which is:
9267 * - a valid symbol
9268 * - unique
9270 temp = g_string_new ("");
9271 len = strlen (name);
9272 for (i = 0; i < len; ++i) {
9273 char c = name [i];
9274 if (isalnum (c)) {
9275 g_string_append_c (temp, c);
9276 } else if (c == '_') {
9277 g_string_append_c (temp, '_');
9278 g_string_append_c (temp, '_');
9279 } else {
9280 g_string_append_c (temp, '_');
9281 if (c == '.')
9282 g_string_append_c (temp, 'd');
9283 else
9284 g_string_append_printf (temp, "%x", (int)c);
9287 temps = g_string_free (temp, FALSE);
9288 /* Include the length to avoid different length type names aliasing each other */
9289 g_string_append_printf (s, "cl%s%x_%s_", is_system ? "s" : "", (int)strlen (temps), temps);
9290 g_free (temps);
9291 g_free (fullname);
9294 if (t->attrs)
9295 g_string_append_printf (s, "_attrs_%d", t->attrs);
9296 return TRUE;
9299 static gboolean
9300 append_mangled_signature (GString *s, MonoMethodSignature *sig)
9302 int i;
9303 gboolean supported;
9305 if (sig->pinvoke)
9306 g_string_append_printf (s, "pinvoke_");
9307 supported = append_mangled_type (s, sig->ret);
9308 if (!supported)
9309 return FALSE;
9310 g_string_append_printf (s, "_");
9311 if (sig->hasthis)
9312 g_string_append_printf (s, "this_");
9313 for (i = 0; i < sig->param_count; ++i) {
9314 supported = append_mangled_type (s, sig->params [i]);
9315 if (!supported)
9316 return FALSE;
9319 return TRUE;
9322 static void
9323 append_mangled_wrapper_type (GString *s, guint32 wrapper_type)
9325 const char *label;
9327 switch (wrapper_type) {
9328 case MONO_WRAPPER_REMOTING_INVOKE:
9329 label = "remoting_invoke";
9330 break;
9331 case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
9332 label = "remoting_invoke_check";
9333 break;
9334 case MONO_WRAPPER_XDOMAIN_INVOKE:
9335 label = "remoting_invoke_xdomain";
9336 break;
9337 case MONO_WRAPPER_PROXY_ISINST:
9338 label = "proxy_isinst";
9339 break;
9340 case MONO_WRAPPER_LDFLD:
9341 label = "ldfld";
9342 break;
9343 case MONO_WRAPPER_LDFLDA:
9344 label = "ldflda";
9345 break;
9346 case MONO_WRAPPER_STFLD:
9347 label = "stfld";
9348 break;
9349 case MONO_WRAPPER_ALLOC:
9350 label = "alloc";
9351 break;
9352 case MONO_WRAPPER_WRITE_BARRIER:
9353 label = "write_barrier";
9354 break;
9355 case MONO_WRAPPER_STELEMREF:
9356 label = "stelemref";
9357 break;
9358 case MONO_WRAPPER_OTHER:
9359 label = "unknown";
9360 break;
9361 case MONO_WRAPPER_MANAGED_TO_NATIVE:
9362 label = "man2native";
9363 break;
9364 case MONO_WRAPPER_SYNCHRONIZED:
9365 label = "synch";
9366 break;
9367 case MONO_WRAPPER_MANAGED_TO_MANAGED:
9368 label = "man2man";
9369 break;
9370 case MONO_WRAPPER_CASTCLASS:
9371 label = "castclass";
9372 break;
9373 case MONO_WRAPPER_RUNTIME_INVOKE:
9374 label = "run_invoke";
9375 break;
9376 case MONO_WRAPPER_DELEGATE_INVOKE:
9377 label = "del_inv";
9378 break;
9379 case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE:
9380 label = "del_beg_inv";
9381 break;
9382 case MONO_WRAPPER_DELEGATE_END_INVOKE:
9383 label = "del_end_inv";
9384 break;
9385 case MONO_WRAPPER_NATIVE_TO_MANAGED:
9386 label = "native2man";
9387 break;
9388 default:
9389 g_assert_not_reached ();
9392 g_string_append_printf (s, "%s_", label);
9395 static void
9396 append_mangled_wrapper_subtype (GString *s, WrapperSubtype subtype)
9398 const char *label;
9400 switch (subtype)
9402 case WRAPPER_SUBTYPE_NONE:
9403 return;
9404 case WRAPPER_SUBTYPE_ELEMENT_ADDR:
9405 label = "elem_addr";
9406 break;
9407 case WRAPPER_SUBTYPE_STRING_CTOR:
9408 label = "str_ctor";
9409 break;
9410 case WRAPPER_SUBTYPE_VIRTUAL_STELEMREF:
9411 label = "virt_stelem";
9412 break;
9413 case WRAPPER_SUBTYPE_FAST_MONITOR_ENTER:
9414 label = "fast_mon_enter";
9415 break;
9416 case WRAPPER_SUBTYPE_FAST_MONITOR_ENTER_V4:
9417 label = "fast_mon_enter_4";
9418 break;
9419 case WRAPPER_SUBTYPE_FAST_MONITOR_EXIT:
9420 label = "fast_monitor_exit";
9421 break;
9422 case WRAPPER_SUBTYPE_PTR_TO_STRUCTURE:
9423 label = "ptr2struct";
9424 break;
9425 case WRAPPER_SUBTYPE_STRUCTURE_TO_PTR:
9426 label = "struct2ptr";
9427 break;
9428 case WRAPPER_SUBTYPE_CASTCLASS_WITH_CACHE:
9429 label = "castclass_w_cache";
9430 break;
9431 case WRAPPER_SUBTYPE_ISINST_WITH_CACHE:
9432 label = "isinst_w_cache";
9433 break;
9434 case WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL:
9435 label = "run_inv_norm";
9436 break;
9437 case WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC:
9438 label = "run_inv_dyn";
9439 break;
9440 case WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT:
9441 label = "run_inv_dir";
9442 break;
9443 case WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL:
9444 label = "run_inv_vir";
9445 break;
9446 case WRAPPER_SUBTYPE_ICALL_WRAPPER:
9447 label = "icall";
9448 break;
9449 case WRAPPER_SUBTYPE_NATIVE_FUNC_AOT:
9450 label = "native_func_aot";
9451 break;
9452 case WRAPPER_SUBTYPE_PINVOKE:
9453 label = "pinvoke";
9454 break;
9455 case WRAPPER_SUBTYPE_SYNCHRONIZED_INNER:
9456 label = "synch_inner";
9457 break;
9458 case WRAPPER_SUBTYPE_GSHAREDVT_IN:
9459 label = "gshared_in";
9460 break;
9461 case WRAPPER_SUBTYPE_GSHAREDVT_OUT:
9462 label = "gshared_out";
9463 break;
9464 case WRAPPER_SUBTYPE_ARRAY_ACCESSOR:
9465 label = "array_acc";
9466 break;
9467 case WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER:
9468 label = "generic_arry_help";
9469 break;
9470 case WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL:
9471 label = "del_inv_virt";
9472 break;
9473 case WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND:
9474 label = "del_inv_bound";
9475 break;
9476 case WRAPPER_SUBTYPE_INTERP_IN:
9477 label = "interp_in";
9478 break;
9479 case WRAPPER_SUBTYPE_INTERP_LMF:
9480 label = "interp_lmf";
9481 break;
9482 case WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG:
9483 label = "gsharedvt_in_sig";
9484 break;
9485 case WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG:
9486 label = "gsharedvt_out_sig";
9487 break;
9488 case WRAPPER_SUBTYPE_AOT_INIT:
9489 label = "aot_init";
9490 break;
9491 default:
9492 g_assert_not_reached ();
9495 g_string_append_printf (s, "%s_", label);
9498 static char *
9499 sanitize_mangled_string (const char *input)
9501 GString *s = g_string_new ("");
9503 for (int i=0; input [i] != '\0'; i++) {
9504 char c = input [i];
9505 switch (c) {
9506 case '.':
9507 g_string_append (s, "_dot_");
9508 break;
9509 case ' ':
9510 g_string_append (s, "_");
9511 break;
9512 case '`':
9513 g_string_append (s, "_bt_");
9514 break;
9515 case '<':
9516 g_string_append (s, "_le_");
9517 break;
9518 case '>':
9519 g_string_append (s, "_gt_");
9520 break;
9521 case '/':
9522 g_string_append (s, "_sl_");
9523 break;
9524 case '[':
9525 g_string_append (s, "_lbrack_");
9526 break;
9527 case ']':
9528 g_string_append (s, "_rbrack_");
9529 break;
9530 case '(':
9531 g_string_append (s, "_lparen_");
9532 break;
9533 case '-':
9534 g_string_append (s, "_dash_");
9535 break;
9536 case ')':
9537 g_string_append (s, "_rparen_");
9538 break;
9539 case ',':
9540 g_string_append (s, "_comma_");
9541 break;
9542 case ':':
9543 g_string_append (s, "_colon_");
9544 break;
9545 case '|':
9546 g_string_append (s, "_verbar_");
9547 break;
9548 default:
9549 g_string_append_c (s, c);
9553 return g_string_free (s, FALSE);
9556 static gboolean
9557 append_mangled_klass (GString *s, MonoClass *klass)
9559 char *klass_desc = mono_class_full_name (klass);
9560 g_string_append_printf (s, "_%s_%s_", m_class_get_name_space (klass), klass_desc);
9561 g_free (klass_desc);
9563 // Success
9564 return TRUE;
9567 static gboolean
9568 append_mangled_method (GString *s, MonoMethod *method);
9570 static gboolean
9571 append_mangled_wrapper (GString *s, MonoMethod *method)
9573 gboolean success = TRUE;
9574 gboolean append_sig = TRUE;
9575 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
9576 g_string_append_printf (s, "wrapper_");
9577 /* Most wrappers are in mscorlib */
9578 if (m_class_get_image (method->klass) != mono_get_corlib ())
9579 g_string_append_printf (s, "%s_", m_class_get_image (method->klass)->assembly->aname.name);
9581 if (method->wrapper_type != MONO_WRAPPER_OTHER && method->wrapper_type != MONO_WRAPPER_MANAGED_TO_NATIVE)
9582 append_mangled_wrapper_type (s, method->wrapper_type);
9584 switch (method->wrapper_type) {
9585 case MONO_WRAPPER_REMOTING_INVOKE:
9586 case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
9587 case MONO_WRAPPER_XDOMAIN_INVOKE: {
9588 MonoMethod *m = mono_marshal_method_from_wrapper (method);
9589 g_assert (m);
9590 success = success && append_mangled_method (s, m);
9591 break;
9593 case MONO_WRAPPER_PROXY_ISINST:
9594 case MONO_WRAPPER_LDFLD:
9595 case MONO_WRAPPER_LDFLDA:
9596 case MONO_WRAPPER_STFLD: {
9597 g_assert (info);
9598 success = success && append_mangled_klass (s, info->d.proxy.klass);
9599 break;
9601 case MONO_WRAPPER_ALLOC: {
9602 /* The GC name is saved once in MonoAotFileInfo */
9603 g_assert (info->d.alloc.alloc_type != -1);
9604 g_string_append_printf (s, "%d_", info->d.alloc.alloc_type);
9605 // SlowAlloc, etc
9606 g_string_append_printf (s, "%s_", method->name);
9607 break;
9609 case MONO_WRAPPER_WRITE_BARRIER: {
9610 g_string_append_printf (s, "%s_", method->name);
9611 break;
9613 case MONO_WRAPPER_STELEMREF: {
9614 append_mangled_wrapper_subtype (s, info->subtype);
9615 if (info->subtype == WRAPPER_SUBTYPE_VIRTUAL_STELEMREF)
9616 g_string_append_printf (s, "%d", info->d.virtual_stelemref.kind);
9617 break;
9619 case MONO_WRAPPER_OTHER: {
9620 append_mangled_wrapper_subtype (s, info->subtype);
9621 if (info->subtype == WRAPPER_SUBTYPE_PTR_TO_STRUCTURE ||
9622 info->subtype == WRAPPER_SUBTYPE_STRUCTURE_TO_PTR)
9623 success = success && append_mangled_klass (s, method->klass);
9624 else if (info->subtype == WRAPPER_SUBTYPE_SYNCHRONIZED_INNER)
9625 success = success && append_mangled_method (s, info->d.synchronized_inner.method);
9626 else if (info->subtype == WRAPPER_SUBTYPE_ARRAY_ACCESSOR)
9627 success = success && append_mangled_method (s, info->d.array_accessor.method);
9628 else if (info->subtype == WRAPPER_SUBTYPE_INTERP_IN)
9629 append_mangled_signature (s, info->d.interp_in.sig);
9630 else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) {
9631 append_mangled_signature (s, info->d.gsharedvt.sig);
9632 append_sig = FALSE;
9633 } else if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT_SIG) {
9634 append_mangled_signature (s, info->d.gsharedvt.sig);
9635 append_sig = FALSE;
9636 } else if (info->subtype == WRAPPER_SUBTYPE_INTERP_LMF)
9637 g_string_append_printf (s, "%s", method->name);
9638 else if (info->subtype == WRAPPER_SUBTYPE_AOT_INIT) {
9639 g_string_append_printf (s, "%s_%d_", m_class_get_image (method->klass)->assembly->aname.name, info->d.aot_init.subtype);
9640 append_sig = FALSE;
9642 break;
9644 case MONO_WRAPPER_MANAGED_TO_NATIVE: {
9645 append_mangled_wrapper_subtype (s, info->subtype);
9646 if (info->subtype == WRAPPER_SUBTYPE_ICALL_WRAPPER) {
9647 const char *name = method->name;
9648 const char *prefix = "__icall_wrapper_";
9649 if (strstr (name, prefix) == name)
9650 name += strlen (prefix);
9651 g_string_append_printf (s, "%s", name);
9652 append_sig = FALSE;
9653 } else if (info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT) {
9654 success = success && append_mangled_method (s, info->d.managed_to_native.method);
9655 } else {
9656 g_assert (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_PINVOKE);
9657 success = success && append_mangled_method (s, info->d.managed_to_native.method);
9659 break;
9661 case MONO_WRAPPER_SYNCHRONIZED: {
9662 MonoMethod *m;
9664 m = mono_marshal_method_from_wrapper (method);
9665 g_assert (m);
9666 g_assert (m != method);
9667 success = success && append_mangled_method (s, m);
9668 break;
9670 case MONO_WRAPPER_MANAGED_TO_MANAGED: {
9671 append_mangled_wrapper_subtype (s, info->subtype);
9673 if (info->subtype == WRAPPER_SUBTYPE_ELEMENT_ADDR) {
9674 g_string_append_printf (s, "%d_", info->d.element_addr.rank);
9675 g_string_append_printf (s, "%d_", info->d.element_addr.elem_size);
9676 } else if (info->subtype == WRAPPER_SUBTYPE_STRING_CTOR) {
9677 success = success && append_mangled_method (s, info->d.string_ctor.method);
9678 } else if (info->subtype == WRAPPER_SUBTYPE_GENERIC_ARRAY_HELPER) {
9679 success = success && append_mangled_method (s, info->d.generic_array_helper.method);
9680 } else {
9681 success = FALSE;
9683 break;
9685 case MONO_WRAPPER_CASTCLASS: {
9686 append_mangled_wrapper_subtype (s, info->subtype);
9687 break;
9689 case MONO_WRAPPER_RUNTIME_INVOKE: {
9690 append_mangled_wrapper_subtype (s, info->subtype);
9691 if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL)
9692 success = success && append_mangled_method (s, info->d.runtime_invoke.method);
9693 else if (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL)
9694 success = success && append_mangled_signature (s, info->d.runtime_invoke.sig);
9695 break;
9697 case MONO_WRAPPER_DELEGATE_INVOKE:
9698 case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE:
9699 case MONO_WRAPPER_DELEGATE_END_INVOKE: {
9700 if (method->is_inflated) {
9701 /* These wrappers are identified by their class */
9702 g_string_append_printf (s, "i_");
9703 success = success && append_mangled_klass (s, method->klass);
9704 } else {
9705 WrapperInfo *info = mono_marshal_get_wrapper_info (method);
9707 g_string_append_printf (s, "u_");
9708 if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE)
9709 append_mangled_wrapper_subtype (s, info->subtype);
9710 g_string_append_printf (s, "u_sigstart");
9712 break;
9714 case MONO_WRAPPER_NATIVE_TO_MANAGED: {
9715 g_assert (info);
9716 success = success && append_mangled_method (s, info->d.native_to_managed.method);
9717 success = success && append_mangled_klass (s, method->klass);
9718 break;
9720 default:
9721 g_assert_not_reached ();
9723 if (success && append_sig)
9724 success = append_mangled_signature (s, mono_method_signature_internal (method));
9725 return success;
9728 static void
9729 append_mangled_ginst (GString *str, MonoGenericInst *ginst)
9731 int i;
9733 for (i = 0; i < ginst->type_argc; ++i) {
9734 if (i > 0)
9735 g_string_append (str, ", ");
9736 MonoType *type = ginst->type_argv [i];
9737 switch (type->type) {
9738 case MONO_TYPE_VAR:
9739 case MONO_TYPE_MVAR: {
9740 MonoType *constraint = NULL;
9741 if (type->data.generic_param)
9742 constraint = type->data.generic_param->gshared_constraint;
9743 if (constraint) {
9744 g_assert (constraint->type != MONO_TYPE_VAR && constraint->type != MONO_TYPE_MVAR);
9745 g_string_append (str, "gshared:");
9746 mono_type_get_desc (str, constraint, TRUE);
9747 break;
9749 // Else falls through to common case
9751 default:
9752 mono_type_get_desc (str, type, TRUE);
9757 static void
9758 append_mangled_context (GString *str, MonoGenericContext *context)
9760 GString *res = g_string_new ("");
9762 g_string_append_printf (res, "gens_");
9763 g_string_append (res, "00");
9765 gboolean good = context->class_inst && context->class_inst->type_argc > 0;
9766 good = good || (context->method_inst && context->method_inst->type_argc > 0);
9767 g_assert (good);
9769 if (context->class_inst)
9770 append_mangled_ginst (res, context->class_inst);
9771 if (context->method_inst) {
9772 if (context->class_inst)
9773 g_string_append (res, "11");
9774 append_mangled_ginst (res, context->method_inst);
9776 g_string_append_printf (str, "gens_%s", res->str);
9777 g_free (res);
9780 static gboolean
9781 append_mangled_method (GString *s, MonoMethod *method)
9783 if (method->wrapper_type)
9784 return append_mangled_wrapper (s, method);
9786 if (method->is_inflated) {
9787 g_string_append_printf (s, "inflated_");
9788 MonoMethodInflated *imethod = (MonoMethodInflated*) method;
9789 g_assert (imethod->context.class_inst != NULL || imethod->context.method_inst != NULL);
9791 append_mangled_context (s, &imethod->context);
9792 g_string_append_printf (s, "_declared_by_%s_", m_class_get_image (imethod->declaring->klass)->assembly->aname.name);
9793 append_mangled_method (s, imethod->declaring);
9794 } else if (method->is_generic) {
9795 g_string_append_printf (s, "%s_", m_class_get_image (method->klass)->assembly->aname.name);
9797 g_string_append_printf (s, "generic_");
9798 append_mangled_klass (s, method->klass);
9799 g_string_append_printf (s, "_%s_", method->name);
9801 MonoGenericContainer *container = mono_method_get_generic_container (method);
9802 g_string_append_printf (s, "_");
9803 append_mangled_context (s, &container->context);
9805 return append_mangled_signature (s, mono_method_signature_internal (method));
9806 } else {
9807 g_string_append_printf (s, "%s", m_class_get_image (method->klass)->assembly->aname.name);
9808 append_mangled_klass (s, method->klass);
9809 g_string_append_printf (s, "_%s_", method->name);
9810 if (!append_mangled_signature (s, mono_method_signature_internal (method))) {
9811 g_string_free (s, TRUE);
9812 return FALSE;
9816 return TRUE;
9820 * mono_aot_get_mangled_method_name:
9822 * Return a unique mangled name for METHOD, or NULL.
9824 char*
9825 mono_aot_get_mangled_method_name (MonoMethod *method)
9827 // FIXME: use static cache (mempool?)
9828 // We call this a *lot*
9830 GString *s = g_string_new ("aot_");
9831 if (!append_mangled_method (s, method)) {
9832 g_string_free (s, TRUE);
9833 return NULL;
9834 } else {
9835 char *out = g_string_free (s, FALSE);
9836 // Scrub method and class names
9837 char *cleaned = sanitize_mangled_string (out);
9838 g_free (out);
9839 return cleaned;
9843 gboolean
9844 mono_aot_is_direct_callable (MonoJumpInfo *patch_info)
9846 return is_direct_callable (llvm_acfg, NULL, patch_info);
9849 void
9850 mono_aot_mark_unused_llvm_plt_entry (MonoJumpInfo *patch_info)
9852 MonoPltEntry *plt_entry;
9854 plt_entry = get_plt_entry (llvm_acfg, patch_info);
9855 plt_entry->llvm_used = FALSE;
9858 char*
9859 mono_aot_get_direct_call_symbol (MonoJumpInfoType type, gconstpointer data)
9861 const char *sym = NULL;
9863 if (llvm_acfg->aot_opts.direct_icalls) {
9864 if (type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
9865 /* Call to a C function implementing a jit icall */
9866 sym = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data)->c_symbol;
9867 } else if (type == MONO_PATCH_INFO_ICALL_ADDR_CALL) {
9868 MonoMethod *method = (MonoMethod *)data;
9869 if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
9870 sym = lookup_icall_symbol_name_aot (method);
9871 else if (llvm_acfg->aot_opts.direct_pinvoke)
9872 sym = get_pinvoke_import (llvm_acfg, method);
9873 } else if (type == MONO_PATCH_INFO_JIT_ICALL_ID) {
9874 MonoJitICallInfo const * const info = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data);
9875 char const * const name = info->c_symbol;
9876 if (name && info->func == info->wrapper)
9877 sym = name;
9879 if (sym)
9880 return g_strdup (sym);
9882 return NULL;
9885 char*
9886 mono_aot_get_plt_symbol (MonoJumpInfoType type, gconstpointer data)
9888 MonoJumpInfo *ji = (MonoJumpInfo *)mono_mempool_alloc (llvm_acfg->mempool, sizeof (MonoJumpInfo));
9889 MonoPltEntry *plt_entry;
9890 const char *sym = NULL;
9892 ji->type = type;
9893 ji->data.target = data;
9895 if (!can_encode_patch (llvm_acfg, ji))
9896 return NULL;
9898 if (llvm_acfg->aot_opts.direct_icalls) {
9899 if (type == MONO_PATCH_INFO_JIT_ICALL_ADDR) {
9900 /* Call to a C function implementing a jit icall */
9901 sym = mono_find_jit_icall_info ((MonoJitICallId)(gsize)data)->c_symbol;
9902 } else if (type == MONO_PATCH_INFO_ICALL_ADDR_CALL) {
9903 MonoMethod *method = (MonoMethod *)data;
9904 if (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
9905 sym = lookup_icall_symbol_name_aot (method);
9907 if (sym)
9908 return g_strdup (sym);
9911 plt_entry = get_plt_entry (llvm_acfg, ji);
9912 plt_entry->llvm_used = TRUE;
9914 #if defined(TARGET_MACH)
9915 return g_strdup (plt_entry->llvm_symbol + strlen (llvm_acfg->llvm_label_prefix));
9916 #else
9917 return g_strdup (plt_entry->llvm_symbol);
9918 #endif
9922 mono_aot_get_method_index (MonoMethod *method)
9924 g_assert (llvm_acfg);
9925 return get_method_index (llvm_acfg, method);
9928 MonoJumpInfo*
9929 mono_aot_patch_info_dup (MonoJumpInfo* ji)
9931 MonoJumpInfo *res;
9933 mono_acfg_lock (llvm_acfg);
9934 res = mono_patch_info_dup_mp (llvm_acfg->mempool, ji);
9935 mono_acfg_unlock (llvm_acfg);
9937 return res;
9940 static int
9941 execute_system (const char * command)
9943 int status = 0;
9945 #if defined (HOST_WIN32)
9946 // We need an extra set of quotes around the whole command to properly handle commands
9947 // with spaces since internally the command is called through "cmd /c.
9948 char * quoted_command = g_strdup_printf ("\"%s\"", command);
9950 int size = MultiByteToWideChar (CP_UTF8, 0 , quoted_command , -1, NULL , 0);
9951 wchar_t* wstr = g_malloc (sizeof (wchar_t) * size);
9952 MultiByteToWideChar (CP_UTF8, 0, quoted_command, -1, wstr , size);
9953 status = _wsystem (wstr);
9954 g_free (wstr);
9956 g_free (quoted_command);
9957 #elif defined (HAVE_SYSTEM)
9958 status = system (command);
9959 #else
9960 g_assert_not_reached ();
9961 #endif
9963 return status;
9966 #ifdef ENABLE_LLVM
9969 * emit_llvm_file:
9971 * Emit the LLVM code into an LLVM bytecode file, and compile it using the LLVM
9972 * tools.
9974 static gboolean
9975 emit_llvm_file (MonoAotCompile *acfg)
9977 char *command, *opts, *tempbc, *optbc, *output_fname;
9979 if (acfg->aot_opts.llvm_only && acfg->aot_opts.asm_only) {
9980 if (acfg->aot_opts.no_opt)
9981 tempbc = g_strdup (acfg->aot_opts.llvm_outfile);
9982 else
9983 tempbc = g_strdup_printf ("%s.bc", acfg->tmpbasename);
9984 optbc = g_strdup (acfg->aot_opts.llvm_outfile);
9985 } else {
9986 tempbc = g_strdup_printf ("%s.bc", acfg->tmpbasename);
9987 optbc = g_strdup_printf ("%s.opt.bc", acfg->tmpbasename);
9990 mono_llvm_emit_aot_module (tempbc, g_path_get_basename (acfg->image->name));
9992 if (acfg->aot_opts.no_opt)
9993 return TRUE;
9995 * FIXME: Experiment with adding optimizations, the -std-compile-opts set takes
9996 * a lot of time, and doesn't seem to save much space.
9997 * The following optimizations cannot be enabled:
9998 * - 'tailcallelim'
9999 * - 'jump-threading' changes our blockaddress references to int constants.
10000 * - 'basiccg' fails because it contains:
10001 * if (CS && !isa<IntrinsicInst>(II)) {
10002 * and isa<IntrinsicInst> is false for invokes to intrinsics (iltests.exe).
10003 * - 'prune-eh' and 'functionattrs' depend on 'basiccg'.
10004 * The opt list below was produced by taking the output of:
10005 * llvm-as < /dev/null | opt -O2 -disable-output -debug-pass=Arguments
10006 * then removing tailcallelim + the global opts.
10007 * strip-dead-prototypes deletes unused intrinsics definitions.
10009 /* The dse pass is disabled because of #13734 and #17616 */
10011 * The dse bug is in DeadStoreElimination.cpp:isOverwrite ():
10012 * // If we have no DataLayout information around, then the size of the store
10013 * // is inferrable from the pointee type. If they are the same type, then
10014 * // we know that the store is safe.
10015 * if (AA.getDataLayout() == 0 &&
10016 * Later.Ptr->getType() == Earlier.Ptr->getType()) {
10017 * return OverwriteComplete;
10018 * Here, if 'Earlier' refers to a memset, and Later has no size info, it mistakenly thinks the memset is redundant.
10020 if (acfg->aot_opts.llvm_only) {
10021 // FIXME: This doesn't work yet
10022 opts = g_strdup ("");
10023 } else {
10024 opts = g_strdup ("-disable-tail-calls -place-safepoints -spp-all-backedges");
10027 if (acfg->aot_opts.llvm_opts) {
10028 opts = g_strdup_printf ("%s %s", acfg->aot_opts.llvm_opts, opts);
10029 } else if (!acfg->aot_opts.llvm_only) {
10030 opts = g_strdup_printf ("-O2 %s", opts);
10033 if (acfg->aot_opts.use_current_cpu) {
10034 opts = g_strdup_printf ("%s -mcpu=native", opts);
10037 if (acfg->aot_opts.llvm_cpu_attr) {
10038 opts = g_strdup_printf ("%s -mattr=%s", opts, acfg->aot_opts.llvm_cpu_attr);
10041 if (mono_use_fast_math) {
10042 // same parameters are passed to llc and LLVM JIT
10043 opts = g_strdup_printf ("%s -fp-contract=fast -enable-no-infs-fp-math -enable-no-nans-fp-math -enable-no-signed-zeros-fp-math -enable-no-trapping-fp-math -enable-unsafe-fp-math", opts);
10046 command = g_strdup_printf ("\"%sopt\" -f %s -o \"%s\" \"%s\"", acfg->aot_opts.llvm_path, opts, optbc, tempbc);
10047 aot_printf (acfg, "Executing opt: %s\n", command);
10048 if (execute_system (command) != 0)
10049 return FALSE;
10050 g_free (opts);
10052 if (acfg->aot_opts.llvm_only && acfg->aot_opts.asm_only)
10053 /* Nothing else to do */
10054 return TRUE;
10056 if (acfg->aot_opts.llvm_only) {
10057 /* Use the stock clang from xcode */
10058 // FIXME: arch
10059 command = g_strdup_printf ("%s -fexceptions -fpic -O2 -fno-optimize-sibling-calls -Wno-override-module -c -o \"%s\" \"%s.opt.bc\"", acfg->aot_opts.clangxx, acfg->llvm_ofile, acfg->tmpbasename);
10061 aot_printf (acfg, "Executing clang: %s\n", command);
10062 if (execute_system (command) != 0)
10063 return FALSE;
10064 return TRUE;
10067 if (!acfg->llc_args)
10068 acfg->llc_args = g_string_new ("");
10070 /* Verbose asm slows down llc greatly */
10071 g_string_append (acfg->llc_args, " -asm-verbose=false");
10073 if (acfg->aot_opts.mtriple)
10074 g_string_append_printf (acfg->llc_args, " -mtriple=%s", acfg->aot_opts.mtriple);
10076 #if defined(TARGET_X86_64_WIN32_MSVC)
10077 if (!acfg->aot_opts.mtriple)
10078 g_string_append_printf (acfg->llc_args, " -mtriple=%s", "x86_64-pc-windows-msvc");
10079 #endif
10081 g_string_append (acfg->llc_args, " -disable-gnu-eh-frame -enable-mono-eh-frame");
10083 g_string_append_printf (acfg->llc_args, " -mono-eh-frame-symbol=%s%s", acfg->user_symbol_prefix, acfg->llvm_eh_frame_symbol);
10085 g_string_append_printf (acfg->llc_args, " -disable-tail-calls");
10087 #if defined(TARGET_AMD64) || defined(TARGET_X86)
10088 /* This generates stack adjustments in the middle of functions breaking unwind info */
10089 g_string_append_printf (acfg->llc_args, " -no-x86-call-frame-opt");
10090 #endif
10092 #if ( defined(TARGET_MACH) && defined(TARGET_ARM) ) || defined(TARGET_ORBIS) || defined(TARGET_X86_64_WIN32_MSVC)
10093 g_string_append_printf (acfg->llc_args, " -relocation-model=pic");
10094 #else
10095 if (llvm_acfg->aot_opts.static_link)
10096 g_string_append_printf (acfg->llc_args, " -relocation-model=static");
10097 else
10098 g_string_append_printf (acfg->llc_args, " -relocation-model=pic");
10099 #endif
10101 if (acfg->llvm_owriter) {
10102 /* Emit an object file directly */
10103 output_fname = g_strdup_printf ("%s", acfg->llvm_ofile);
10104 g_string_append_printf (acfg->llc_args, " -filetype=obj");
10105 } else {
10106 output_fname = g_strdup_printf ("%s", acfg->llvm_sfile);
10109 if (acfg->aot_opts.llvm_llc) {
10110 g_string_append_printf (acfg->llc_args, " %s", acfg->aot_opts.llvm_llc);
10113 if (acfg->aot_opts.use_current_cpu) {
10114 g_string_append (acfg->llc_args, " -mcpu=native");
10117 if (acfg->aot_opts.llvm_cpu_attr) {
10118 g_string_append_printf (acfg->llc_args, " -mattr=%s", acfg->aot_opts.llvm_cpu_attr);
10121 command = g_strdup_printf ("\"%sllc\" %s -o \"%s\" \"%s.opt.bc\"", acfg->aot_opts.llvm_path, acfg->llc_args->str, output_fname, acfg->tmpbasename);
10122 g_free (output_fname);
10124 aot_printf (acfg, "Executing llc: %s\n", command);
10126 if (execute_system (command) != 0)
10127 return FALSE;
10128 return TRUE;
10130 #endif
10132 /* Set the skip flag for methods which do not need to be emitted because of dedup */
10133 static void
10134 dedup_skip_methods (MonoAotCompile *acfg)
10136 int oindex, i;
10138 if (acfg->aot_opts.llvm_only)
10139 return;
10141 for (oindex = 0; oindex < acfg->method_order->len; ++oindex) {
10142 MonoCompile *cfg;
10143 MonoMethod *method;
10145 i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex));
10147 cfg = acfg->cfgs [i];
10149 if (!cfg)
10150 continue;
10152 method = cfg->orig_method;
10154 gboolean dedup_collect = acfg->aot_opts.dedup || (acfg->aot_opts.dedup_include && !acfg->dedup_emit_mode);
10155 gboolean dedupable = mono_aot_can_dedup (method);
10157 // cfg->skip is vital for LLVM to work, can't just continue in this loop
10158 if (dedupable && strcmp (method->name, "wbarrier_conc") && dedup_collect) {
10159 mono_dedup_cache_method (acfg, method);
10161 // Don't compile inflated methods if we're in first phase of
10162 // dedup
10164 // In second phase, we emit methods that
10165 // are dedupable. We also emit later methods
10166 // which are referenced by them and added later.
10167 // For this reason, when in the dedup_include mode,
10168 // we never set skip.
10169 if (acfg->aot_opts.dedup)
10170 cfg->skip = TRUE;
10173 // Don't compile anything in this mode
10174 if (acfg->aot_opts.dedup_include && !acfg->dedup_emit_mode)
10175 cfg->skip = TRUE;
10177 // Compile everything in this mode
10178 if (acfg->aot_opts.dedup_include && acfg->dedup_emit_mode)
10179 cfg->skip = FALSE;
10181 /*if (dedup_collect) {*/
10182 /*char *name = mono_aot_get_mangled_method_name (method);*/
10184 /*if (ignore_cfg (cfg))*/
10185 /*aot_printf (acfg, "Dedup Skipping %s\n", acfg->image->name, name);*/
10186 /*else*/
10187 /*aot_printf (acfg, "Dedup Keeping %s\n", acfg->image->name, name);*/
10189 /*g_free (name);*/
10190 /*}*/
10194 static void
10195 emit_code (MonoAotCompile *acfg)
10197 int oindex, i, prev_index;
10198 gboolean saved_unbox_info = FALSE; // See mono_aot_get_unbox_trampoline.
10199 char symbol [MAX_SYMBOL_SIZE];
10201 if (acfg->aot_opts.llvm_only)
10202 return;
10204 #if defined(TARGET_POWERPC64)
10205 sprintf (symbol, ".Lgot_addr");
10206 emit_section_change (acfg, ".text", 0);
10207 emit_alignment (acfg, 8);
10208 emit_label (acfg, symbol);
10209 emit_pointer (acfg, acfg->got_symbol);
10210 #endif
10213 * This global symbol is used to compute the address of each method using the
10214 * code_offsets array. It is also used to compute the memory ranges occupied by
10215 * AOT code, so it must be equal to the address of the first emitted method.
10217 emit_section_change (acfg, ".text", 0);
10218 emit_alignment_code (acfg, 8);
10219 emit_info_symbol (acfg, "jit_code_start", TRUE);
10222 * Emit some padding so the local symbol for the first method doesn't have the
10223 * same address as 'methods'.
10225 emit_padding (acfg, 16);
10227 for (oindex = 0; oindex < acfg->method_order->len; ++oindex) {
10228 MonoCompile *cfg;
10229 MonoMethod *method;
10231 i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex));
10233 cfg = acfg->cfgs [i];
10235 if (!cfg)
10236 continue;
10238 method = cfg->orig_method;
10240 if (ignore_cfg (cfg))
10241 continue;
10243 /* Emit unbox trampoline */
10244 if (mono_aot_mode_is_full (&acfg->aot_opts) && m_class_is_valuetype (cfg->orig_method->klass)) {
10245 sprintf (symbol, "ut_%d", get_method_index (acfg, method));
10247 emit_section_change (acfg, ".text", 0);
10249 if (acfg->thumb_mixed && cfg->compile_llvm) {
10250 emit_set_thumb_mode (acfg);
10251 fprintf (acfg->fp, "\n.thumb_func\n");
10254 emit_label (acfg, symbol);
10256 arch_emit_unbox_trampoline (acfg, cfg, cfg->orig_method, cfg->asm_symbol);
10258 if (acfg->thumb_mixed && cfg->compile_llvm)
10259 emit_set_arm_mode (acfg);
10261 if (!saved_unbox_info) {
10262 char user_symbol [128];
10263 GSList *unwind_ops;
10264 sprintf (user_symbol, "%sunbox_trampoline_p", acfg->user_symbol_prefix);
10266 emit_label (acfg, "ut_end");
10268 unwind_ops = mono_unwind_get_cie_program ();
10269 save_unwind_info (acfg, user_symbol, unwind_ops);
10270 mono_free_unwind_info (unwind_ops);
10272 /* Save the unbox trampoline size */
10273 #ifdef TARGET_AMD64
10274 // LLVM unbox trampolines vary in size, 6 or 9 bytes,
10275 // due to the last instruction being 2 or 5 bytes.
10276 // There is no need to describe interior bytes of instructions
10277 // however, so state the size as if the last instruction is size 1.
10278 emit_int32 (acfg, 5);
10279 #else
10280 emit_symbol_diff (acfg, "ut_end", symbol, 0);
10281 #endif
10282 saved_unbox_info = TRUE;
10286 if (cfg->compile_llvm) {
10287 acfg->stats.llvm_count ++;
10288 } else {
10289 emit_method_code (acfg, cfg);
10293 emit_section_change (acfg, ".text", 0);
10294 emit_alignment_code (acfg, 8);
10295 emit_info_symbol (acfg, "jit_code_end", TRUE);
10297 /* To distinguish it from the next symbol */
10298 emit_padding (acfg, 4);
10301 * Add .no_dead_strip directives for all LLVM methods to prevent the OSX linker
10302 * from optimizing them away, since it doesn't see that code_offsets references them.
10303 * JITted methods don't need this since they are referenced using assembler local
10304 * symbols.
10305 * FIXME: This is why write-symbols doesn't work on OSX ?
10307 if (acfg->llvm && acfg->need_no_dead_strip) {
10308 fprintf (acfg->fp, "\n");
10309 for (i = 0; i < acfg->nmethods; ++i) {
10310 if (acfg->cfgs [i] && acfg->cfgs [i]->compile_llvm)
10311 fprintf (acfg->fp, ".no_dead_strip %s\n", acfg->cfgs [i]->asm_symbol);
10316 * To work around linker issues, we emit a table of branches, and disassemble them at runtime.
10317 * This is PIE code, and the linker can update it if needed.
10319 #if defined(TARGET_ANDROID) || defined(__linux__)
10320 gboolean is_func = FALSE;
10321 #else
10322 gboolean is_func = TRUE;
10323 #endif
10325 sprintf (symbol, "method_addresses");
10326 if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) {
10327 /* Emit the method address table as a table of pointers */
10328 emit_section_change (acfg, ".data", 0);
10329 } else {
10330 emit_section_change (acfg, RODATA_REL_SECT, !!is_func);
10332 emit_alignment_code (acfg, 8);
10333 emit_info_symbol (acfg, symbol, is_func);
10334 if (acfg->aot_opts.write_symbols)
10335 emit_local_symbol (acfg, symbol, "method_addresses_end", is_func);
10336 emit_unset_mode (acfg);
10337 if (acfg->need_no_dead_strip)
10338 fprintf (acfg->fp, " .no_dead_strip %s\n", symbol);
10340 for (i = 0; i < acfg->nmethods; ++i) {
10341 #ifdef MONO_ARCH_AOT_SUPPORTED
10342 if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) {
10343 if (!ignore_cfg (acfg->cfgs [i]))
10344 emit_pointer (acfg, acfg->cfgs [i]->asm_symbol);
10345 else
10346 emit_pointer (acfg, NULL);
10347 } else {
10348 if (!ignore_cfg (acfg->cfgs [i])) {
10349 arch_emit_label_address (acfg, acfg->cfgs [i]->asm_symbol, FALSE, acfg->thumb_mixed && acfg->cfgs [i]->compile_llvm, NULL, &acfg->call_table_entry_size);
10350 } else {
10351 arch_emit_label_address (acfg, symbol, FALSE, FALSE, NULL, &acfg->call_table_entry_size);
10354 #endif
10357 sprintf (symbol, "method_addresses_end");
10358 emit_label (acfg, symbol);
10359 emit_line (acfg);
10361 /* Emit a sorted table mapping methods to the index of their unbox trampolines */
10362 sprintf (symbol, "unbox_trampolines");
10363 emit_section_change (acfg, RODATA_SECT, 0);
10364 emit_alignment (acfg, 8);
10365 emit_info_symbol (acfg, symbol, FALSE);
10367 prev_index = -1;
10368 for (i = 0; i < acfg->nmethods; ++i) {
10369 MonoCompile *cfg;
10370 MonoMethod *method;
10371 int index;
10373 cfg = acfg->cfgs [i];
10374 if (ignore_cfg (cfg))
10375 continue;
10377 method = cfg->orig_method;
10379 if (mono_aot_mode_is_full (&acfg->aot_opts) && m_class_is_valuetype (cfg->orig_method->klass)) {
10380 index = get_method_index (acfg, method);
10382 emit_int32 (acfg, index);
10383 /* Make sure the table is sorted by index */
10384 g_assert (index > prev_index);
10385 prev_index = index;
10388 sprintf (symbol, "unbox_trampolines_end");
10389 emit_info_symbol (acfg, symbol, FALSE);
10390 emit_int32 (acfg, 0);
10392 /* Emit a separate table with the trampoline addresses/offsets */
10393 sprintf (symbol, "unbox_trampoline_addresses");
10394 if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) {
10395 /* Emit the unbox trampoline address table as a table of pointers */
10396 emit_section_change (acfg, ".data", 0);
10397 } else {
10398 emit_section_change (acfg, ".text", 0);
10400 emit_alignment_code (acfg, 8);
10401 emit_info_symbol (acfg, symbol, TRUE);
10403 for (i = 0; i < acfg->nmethods; ++i) {
10404 MonoCompile *cfg;
10405 MonoMethod *method;
10407 cfg = acfg->cfgs [i];
10408 if (ignore_cfg (cfg))
10409 continue;
10411 method = cfg->orig_method;
10412 (void)method;
10414 if (mono_aot_mode_is_full (&acfg->aot_opts) && m_class_is_valuetype (cfg->orig_method->klass)) {
10415 #ifdef MONO_ARCH_AOT_SUPPORTED
10416 const int index = get_method_index (acfg, method);
10417 sprintf (symbol, "ut_%d", index);
10419 if (acfg->flags & MONO_AOT_FILE_FLAG_CODE_EXEC_ONLY) {
10420 emit_pointer (acfg, symbol);
10421 } else {
10422 int call_size;
10423 arch_emit_direct_call (acfg, symbol, FALSE, acfg->thumb_mixed && cfg->compile_llvm, NULL, &call_size);
10425 #endif
10428 emit_int32 (acfg, 0);
10431 static void
10432 emit_method_info_table (MonoAotCompile *acfg)
10434 int oindex, i;
10435 gint32 *offsets;
10436 guint8 *method_flags;
10438 offsets = g_new0 (gint32, acfg->nmethods);
10440 for (oindex = 0; oindex < acfg->method_order->len; ++oindex) {
10441 i = GPOINTER_TO_UINT (g_ptr_array_index (acfg->method_order, oindex));
10443 if (acfg->cfgs [i]) {
10444 emit_method_info (acfg, acfg->cfgs [i]);
10445 offsets [i] = acfg->cfgs [i]->method_info_offset;
10446 } else {
10447 offsets [i] = 0;
10451 acfg->stats.offsets_size += emit_offset_table (acfg, "method_info_offsets", MONO_AOT_TABLE_METHOD_INFO_OFFSETS, acfg->nmethods, 10, offsets);
10453 g_free (offsets);
10455 /* Emit a separate table for method flags, its needed at runtime */
10456 method_flags = g_new0 (guint8, acfg->nmethods);
10457 for (i = 0; i < acfg->nmethods; ++i) {
10458 if (acfg->cfgs [i])
10459 method_flags [acfg->cfgs [i]->method_index] = acfg->cfgs [i]->aot_method_flags;
10461 emit_aot_data (acfg, MONO_AOT_TABLE_METHOD_FLAGS_TABLE, "method_flags_table", method_flags, acfg->nmethods);
10464 #endif /* #if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) */
10466 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
10467 #define mix(a,b,c) { \
10468 a -= c; a ^= rot(c, 4); c += b; \
10469 b -= a; b ^= rot(a, 6); a += c; \
10470 c -= b; c ^= rot(b, 8); b += a; \
10471 a -= c; a ^= rot(c,16); c += b; \
10472 b -= a; b ^= rot(a,19); a += c; \
10473 c -= b; c ^= rot(b, 4); b += a; \
10475 #define mono_final(a,b,c) { \
10476 c ^= b; c -= rot(b,14); \
10477 a ^= c; a -= rot(c,11); \
10478 b ^= a; b -= rot(a,25); \
10479 c ^= b; c -= rot(b,16); \
10480 a ^= c; a -= rot(c,4); \
10481 b ^= a; b -= rot(a,14); \
10482 c ^= b; c -= rot(b,24); \
10485 static guint
10486 mono_aot_type_hash (MonoType *t1)
10488 guint hash = t1->type;
10490 hash |= t1->byref << 6; /* do not collide with t1->type values */
10491 switch (t1->type) {
10492 case MONO_TYPE_VALUETYPE:
10493 case MONO_TYPE_CLASS:
10494 case MONO_TYPE_SZARRAY:
10495 /* check if the distribution is good enough */
10496 return ((hash << 5) - hash) ^ mono_metadata_str_hash (m_class_get_name (t1->data.klass));
10497 case MONO_TYPE_PTR:
10498 return ((hash << 5) - hash) ^ mono_metadata_type_hash (t1->data.type);
10499 case MONO_TYPE_ARRAY:
10500 return ((hash << 5) - hash) ^ mono_metadata_type_hash (m_class_get_byval_arg (t1->data.array->eklass));
10501 case MONO_TYPE_GENERICINST:
10502 return ((hash << 5) - hash) ^ 0;
10503 default:
10504 return hash;
10509 * mono_aot_method_hash:
10511 * Return a hash code for methods which only depends on metadata.
10513 guint32
10514 mono_aot_method_hash (MonoMethod *method)
10516 MonoMethodSignature *sig;
10517 MonoClass *klass;
10518 int i, hindex;
10519 int hashes_count;
10520 guint32 *hashes_start, *hashes;
10521 guint32 a, b, c;
10522 MonoGenericInst *class_ginst = NULL;
10523 MonoGenericInst *ginst = NULL;
10525 /* Similar to the hash in mono_method_get_imt_slot () */
10527 sig = mono_method_signature_internal (method);
10529 if (mono_class_is_ginst (method->klass))
10530 class_ginst = mono_class_get_generic_class (method->klass)->context.class_inst;
10531 if (method->is_inflated)
10532 ginst = ((MonoMethodInflated*)method)->context.method_inst;
10534 hashes_count = sig->param_count + 5 + (class_ginst ? class_ginst->type_argc : 0) + (ginst ? ginst->type_argc : 0);
10535 hashes_start = (guint32 *)g_malloc0 (hashes_count * sizeof (guint32));
10536 hashes = hashes_start;
10538 /* Some wrappers are assigned to random classes */
10539 if (!method->wrapper_type || method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK)
10540 klass = method->klass;
10541 else
10542 klass = mono_defaults.object_class;
10544 if (!method->wrapper_type) {
10545 char *full_name;
10547 if (mono_class_is_ginst (klass))
10548 full_name = mono_type_full_name (m_class_get_byval_arg (mono_class_get_generic_class (klass)->container_class));
10549 else
10550 full_name = mono_type_full_name (m_class_get_byval_arg (klass));
10552 hashes [0] = mono_metadata_str_hash (full_name);
10553 hashes [1] = 0;
10554 g_free (full_name);
10555 } else {
10556 hashes [0] = mono_metadata_str_hash (m_class_get_name (klass));
10557 hashes [1] = mono_metadata_str_hash (m_class_get_name_space (klass));
10559 if (method->wrapper_type == MONO_WRAPPER_STFLD || method->wrapper_type == MONO_WRAPPER_LDFLD || method->wrapper_type == MONO_WRAPPER_LDFLDA)
10560 /* The method name includes a stringified pointer */
10561 hashes [2] = 0;
10562 else
10563 hashes [2] = mono_metadata_str_hash (method->name);
10564 hashes [3] = method->wrapper_type;
10565 hashes [4] = mono_aot_type_hash (sig->ret);
10566 hindex = 5;
10567 for (i = 0; i < sig->param_count; i++) {
10568 hashes [hindex ++] = mono_aot_type_hash (sig->params [i]);
10570 if (class_ginst) {
10571 for (i = 0; i < class_ginst->type_argc; ++i)
10572 hashes [hindex ++] = mono_aot_type_hash (class_ginst->type_argv [i]);
10574 if (ginst) {
10575 for (i = 0; i < ginst->type_argc; ++i)
10576 hashes [hindex ++] = mono_aot_type_hash (ginst->type_argv [i]);
10578 g_assert (hindex == hashes_count);
10580 /* Setup internal state */
10581 a = b = c = 0xdeadbeef + (((guint32)hashes_count)<<2);
10583 /* Handle most of the hashes */
10584 while (hashes_count > 3) {
10585 a += hashes [0];
10586 b += hashes [1];
10587 c += hashes [2];
10588 mix (a,b,c);
10589 hashes_count -= 3;
10590 hashes += 3;
10593 /* Handle the last 3 hashes (all the case statements fall through) */
10594 switch (hashes_count) {
10595 case 3 : c += hashes [2];
10596 case 2 : b += hashes [1];
10597 case 1 : a += hashes [0];
10598 mono_final (a,b,c);
10599 case 0: /* nothing left to add */
10600 break;
10603 g_free (hashes_start);
10605 return c;
10607 #undef rot
10608 #undef mix
10609 #undef mono_final
10612 * mono_aot_get_array_helper_from_wrapper;
10614 * Get the helper method in Array called by an array wrapper method.
10616 MonoMethod*
10617 mono_aot_get_array_helper_from_wrapper (MonoMethod *method)
10619 MonoMethod *m;
10620 const char *prefix;
10621 MonoGenericContext ctx;
10622 char *mname, *iname, *s, *s2, *helper_name = NULL;
10624 prefix = "System.Collections.Generic";
10625 s = g_strdup_printf ("%s", method->name + strlen (prefix) + 1);
10626 s2 = strstr (s, "`1.");
10627 g_assert (s2);
10628 s2 [0] = '\0';
10629 iname = s;
10630 mname = s2 + 3;
10632 //printf ("X: %s %s\n", iname, mname);
10634 if (!strcmp (iname, "IList"))
10635 helper_name = g_strdup_printf ("InternalArray__%s", mname);
10636 else
10637 helper_name = g_strdup_printf ("InternalArray__%s_%s", iname, mname);
10638 m = get_method_nofail (mono_defaults.array_class, helper_name, mono_method_signature_internal (method)->param_count, 0);
10639 g_assert (m);
10640 g_free (helper_name);
10641 g_free (s);
10643 if (m->is_generic) {
10644 ERROR_DECL (error);
10645 memset (&ctx, 0, sizeof (ctx));
10646 MonoType *args [ ] = { m_class_get_byval_arg (m_class_get_element_class (method->klass)) };
10647 ctx.method_inst = mono_metadata_get_generic_inst (1, args);
10648 m = mono_class_inflate_generic_method_checked (m, &ctx, error);
10649 g_assert (is_ok (error)); /* FIXME don't swallow the error */
10652 return m;
10655 #if !defined(DISABLE_AOT) && !defined(DISABLE_JIT)
10657 typedef struct HashEntry {
10658 guint32 key, value, index;
10659 struct HashEntry *next;
10660 } HashEntry;
10663 * emit_extra_methods:
10665 * Emit methods which are not in the METHOD table, like wrappers.
10667 static void
10668 emit_extra_methods (MonoAotCompile *acfg)
10670 int i, table_size, buf_size;
10671 guint8 *p, *buf;
10672 guint32 *info_offsets;
10673 guint32 hash;
10674 GPtrArray *table;
10675 HashEntry *entry, *new_entry;
10676 int nmethods, max_chain_length;
10677 int *chain_lengths;
10679 info_offsets = g_new0 (guint32, acfg->extra_methods->len);
10681 /* Emit method info */
10682 nmethods = 0;
10683 for (i = 0; i < acfg->extra_methods->len; ++i) {
10684 MonoMethod *method = (MonoMethod *)g_ptr_array_index (acfg->extra_methods, i);
10685 MonoCompile *cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, method);
10687 if (ignore_cfg (cfg))
10688 continue;
10690 buf_size = 10240;
10691 p = buf = (guint8 *)g_malloc (buf_size);
10693 nmethods ++;
10695 method = cfg->method_to_register;
10697 encode_method_ref (acfg, method, p, &p);
10699 g_assert ((p - buf) < buf_size);
10701 info_offsets [i] = add_to_blob (acfg, buf, p - buf);
10702 g_free (buf);
10706 * Construct a chained hash table for mapping indexes in extra_method_info to
10707 * method indexes.
10709 table_size = g_spaced_primes_closest ((int)(nmethods * 1.5));
10710 table = g_ptr_array_sized_new (table_size);
10711 for (i = 0; i < table_size; ++i)
10712 g_ptr_array_add (table, NULL);
10713 chain_lengths = g_new0 (int, table_size);
10714 max_chain_length = 0;
10715 for (i = 0; i < acfg->extra_methods->len; ++i) {
10716 MonoMethod *method = (MonoMethod *)g_ptr_array_index (acfg->extra_methods, i);
10717 MonoCompile *cfg = (MonoCompile *)g_hash_table_lookup (acfg->method_to_cfg, method);
10718 guint32 key, value;
10720 if (ignore_cfg (cfg))
10721 continue;
10723 key = info_offsets [i];
10724 value = get_method_index (acfg, method);
10726 hash = mono_aot_method_hash (method) % table_size;
10727 //printf ("X: %s %x\n", mono_method_get_full_name (method), mono_aot_method_hash (method));
10729 chain_lengths [hash] ++;
10730 max_chain_length = MAX (max_chain_length, chain_lengths [hash]);
10732 new_entry = (HashEntry *)mono_mempool_alloc0 (acfg->mempool, sizeof (HashEntry));
10733 new_entry->key = key;
10734 new_entry->value = value;
10736 entry = (HashEntry *)g_ptr_array_index (table, hash);
10737 if (entry == NULL) {
10738 new_entry->index = hash;
10739 g_ptr_array_index (table, hash) = new_entry;
10740 } else {
10741 while (entry->next)
10742 entry = entry->next;
10744 entry->next = new_entry;
10745 new_entry->index = table->len;
10746 g_ptr_array_add (table, new_entry);
10749 g_free (chain_lengths);
10751 //printf ("MAX: %d\n", max_chain_length);
10753 buf_size = table->len * 12 + 4;
10754 p = buf = (guint8 *)g_malloc (buf_size);
10755 encode_int (table_size, p, &p);
10757 for (i = 0; i < table->len; ++i) {
10758 HashEntry *entry = (HashEntry *)g_ptr_array_index (table, i);
10760 if (entry == NULL) {
10761 encode_int (0, p, &p);
10762 encode_int (0, p, &p);
10763 encode_int (0, p, &p);
10764 } else {
10765 //g_assert (entry->key > 0);
10766 encode_int (entry->key, p, &p);
10767 encode_int (entry->value, p, &p);
10768 if (entry->next)
10769 encode_int (entry->next->index, p, &p);
10770 else
10771 encode_int (0, p, &p);
10774 g_assert (p - buf <= buf_size);
10776 /* Emit the table */
10777 emit_aot_data (acfg, MONO_AOT_TABLE_EXTRA_METHOD_TABLE, "extra_method_table", buf, p - buf);
10779 g_free (buf);
10782 * Emit a table reverse mapping method indexes to their index in extra_method_info.
10783 * This is used by mono_aot_find_jit_info ().
10785 buf_size = acfg->extra_methods->len * 8 + 4;
10786 p = buf = (guint8 *)g_malloc (buf_size);
10787 encode_int (acfg->extra_methods->len, p, &p);
10788 for (i = 0; i < acfg->extra_methods->len; ++i) {
10789 MonoMethod *method = (MonoMethod *)g_ptr_array_index (acfg->extra_methods, i);
10791 encode_int (get_method_index (acfg, method), p, &p);
10792 encode_int (info_offsets [i], p, &p);
10794 emit_aot_data (acfg, MONO_AOT_TABLE_EXTRA_METHOD_INFO_OFFSETS, "extra_method_info_offsets", buf, p - buf);
10796 g_free (buf);
10797 g_free (info_offsets);
10798 g_ptr_array_free (table, TRUE);
10801 static void
10802 generate_aotid (guint8* aotid)
10804 gpointer rand_handle;
10805 ERROR_DECL (error);
10807 mono_rand_open ();
10808 rand_handle = mono_rand_init (NULL, 0);
10810 mono_rand_try_get_bytes (&rand_handle, aotid, 16, error);
10811 mono_error_assert_ok (error);
10813 mono_rand_close (rand_handle);
10816 static void
10817 emit_exception_info (MonoAotCompile *acfg)
10819 int i;
10820 gint32 *offsets;
10821 SeqPointData sp_data;
10822 gboolean seq_points_to_file = FALSE;
10824 offsets = g_new0 (gint32, acfg->nmethods);
10825 for (i = 0; i < acfg->nmethods; ++i) {
10826 if (acfg->cfgs [i]) {
10827 MonoCompile *cfg = acfg->cfgs [i];
10829 // By design aot-runtime decode_exception_debug_info is not able to load sequence point debug data from a file.
10830 // As it is not possible to load debug data from a file its is also not possible to store it in a file.
10831 gboolean method_seq_points_to_file = acfg->aot_opts.gen_msym_dir &&
10832 cfg->gen_seq_points && !cfg->gen_sdb_seq_points;
10833 gboolean method_seq_points_to_binary = cfg->gen_seq_points && !method_seq_points_to_file;
10835 emit_exception_debug_info (acfg, cfg, method_seq_points_to_binary);
10836 offsets [i] = cfg->ex_info_offset;
10838 if (method_seq_points_to_file) {
10839 if (!seq_points_to_file) {
10840 mono_seq_point_data_init (&sp_data, acfg->nmethods);
10841 seq_points_to_file = TRUE;
10843 mono_seq_point_data_add (&sp_data, cfg->method->token, cfg->method_index, cfg->seq_point_info);
10845 } else {
10846 offsets [i] = 0;
10850 if (seq_points_to_file) {
10851 char *aotid = mono_guid_to_string_minimal (acfg->image->aotid);
10852 char *dir = g_build_filename (acfg->aot_opts.gen_msym_dir_path, aotid, (const char*)NULL);
10853 char *image_basename = g_path_get_basename (acfg->image->name);
10854 char *aot_file = g_strdup_printf("%s%s", image_basename, SEQ_POINT_AOT_EXT);
10855 char *aot_file_path = g_build_filename (dir, aot_file, (const char*)NULL);
10857 if (g_ensure_directory_exists (aot_file_path) == FALSE) {
10858 fprintf (stderr, "AOT : failed to create msym directory: %s\n", aot_file_path);
10859 exit (1);
10862 mono_seq_point_data_write (&sp_data, aot_file_path);
10863 mono_seq_point_data_free (&sp_data);
10865 g_free (aotid);
10866 g_free (dir);
10867 g_free (image_basename);
10868 g_free (aot_file);
10869 g_free (aot_file_path);
10872 acfg->stats.offsets_size += emit_offset_table (acfg, "ex_info_offsets", MONO_AOT_TABLE_EX_INFO_OFFSETS, acfg->nmethods, 10, offsets);
10873 g_free (offsets);
10876 static void
10877 emit_unwind_info (MonoAotCompile *acfg)
10879 int i;
10880 char symbol [128];
10882 if (acfg->aot_opts.llvm_only) {
10883 g_assert (acfg->unwind_ops->len == 0);
10884 return;
10888 * The unwind info contains a lot of duplicates so we emit each unique
10889 * entry once, and only store the offset from the start of the table in the
10890 * exception info.
10893 sprintf (symbol, "unwind_info");
10894 emit_section_change (acfg, RODATA_SECT, 1);
10895 emit_alignment (acfg, 8);
10896 emit_info_symbol (acfg, symbol, TRUE);
10898 for (i = 0; i < acfg->unwind_ops->len; ++i) {
10899 guint32 index = GPOINTER_TO_UINT (g_ptr_array_index (acfg->unwind_ops, i));
10900 guint8 *unwind_info;
10901 guint32 unwind_info_len;
10902 guint8 buf [16];
10903 guint8 *p;
10905 unwind_info = mono_get_cached_unwind_info (index, &unwind_info_len);
10907 p = buf;
10908 encode_value (unwind_info_len, p, &p);
10909 emit_bytes (acfg, buf, p - buf);
10910 emit_bytes (acfg, unwind_info, unwind_info_len);
10912 acfg->stats.unwind_info_size += (p - buf) + unwind_info_len;
10916 static void
10917 emit_class_info (MonoAotCompile *acfg)
10919 int i;
10920 gint32 *offsets;
10922 offsets = g_new0 (gint32, acfg->image->tables [MONO_TABLE_TYPEDEF].rows);
10923 for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i)
10924 offsets [i] = emit_klass_info (acfg, MONO_TOKEN_TYPE_DEF | (i + 1));
10926 acfg->stats.offsets_size += emit_offset_table (acfg, "class_info_offsets", MONO_AOT_TABLE_CLASS_INFO_OFFSETS, acfg->image->tables [MONO_TABLE_TYPEDEF].rows, 10, offsets);
10927 g_free (offsets);
10930 typedef struct ClassNameTableEntry {
10931 guint32 token, index;
10932 struct ClassNameTableEntry *next;
10933 } ClassNameTableEntry;
10935 static void
10936 emit_class_name_table (MonoAotCompile *acfg)
10938 int i, table_size, buf_size;
10939 guint32 token, hash;
10940 MonoClass *klass;
10941 GPtrArray *table;
10942 char *full_name;
10943 guint8 *buf, *p;
10944 ClassNameTableEntry *entry, *new_entry;
10947 * Construct a chained hash table for mapping class names to typedef tokens.
10949 table_size = g_spaced_primes_closest ((int)(acfg->image->tables [MONO_TABLE_TYPEDEF].rows * 1.5));
10950 table = g_ptr_array_sized_new (table_size);
10951 for (i = 0; i < table_size; ++i)
10952 g_ptr_array_add (table, NULL);
10953 for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) {
10954 ERROR_DECL (error);
10955 token = MONO_TOKEN_TYPE_DEF | (i + 1);
10956 klass = mono_class_get_checked (acfg->image, token, error);
10957 if (!klass) {
10958 mono_error_cleanup (error);
10959 continue;
10961 full_name = mono_type_get_name_full (m_class_get_byval_arg (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME);
10962 hash = mono_metadata_str_hash (full_name) % table_size;
10963 g_free (full_name);
10965 /* FIXME: Allocate from the mempool */
10966 new_entry = g_new0 (ClassNameTableEntry, 1);
10967 new_entry->token = token;
10969 entry = (ClassNameTableEntry *)g_ptr_array_index (table, hash);
10970 if (entry == NULL) {
10971 new_entry->index = hash;
10972 g_ptr_array_index (table, hash) = new_entry;
10973 } else {
10974 while (entry->next)
10975 entry = entry->next;
10977 entry->next = new_entry;
10978 new_entry->index = table->len;
10979 g_ptr_array_add (table, new_entry);
10983 /* Emit the table */
10984 buf_size = table->len * 4 + 4;
10985 p = buf = (guint8 *)g_malloc0 (buf_size);
10987 /* FIXME: Optimize memory usage */
10988 g_assert (table_size < 65000);
10989 encode_int16 (table_size, p, &p);
10990 g_assert (table->len < 65000);
10991 for (i = 0; i < table->len; ++i) {
10992 ClassNameTableEntry *entry = (ClassNameTableEntry *)g_ptr_array_index (table, i);
10994 if (entry == NULL) {
10995 encode_int16 (0, p, &p);
10996 encode_int16 (0, p, &p);
10997 } else {
10998 encode_int16 (mono_metadata_token_index (entry->token), p, &p);
10999 if (entry->next)
11000 encode_int16 (entry->next->index, p, &p);
11001 else
11002 encode_int16 (0, p, &p);
11004 g_free (entry);
11006 g_assert (p - buf <= buf_size);
11007 g_ptr_array_free (table, TRUE);
11009 emit_aot_data (acfg, MONO_AOT_TABLE_CLASS_NAME, "class_name_table", buf, p - buf);
11011 g_free (buf);
11014 static void
11015 emit_image_table (MonoAotCompile *acfg)
11017 int i, buf_size;
11018 guint8 *buf, *p;
11021 * The image table is small but referenced in a lot of places.
11022 * So we emit it at once, and reference its elements by an index.
11024 buf_size = acfg->image_table->len * 28 + 4;
11025 for (i = 0; i < acfg->image_table->len; i++) {
11026 MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i);
11027 MonoAssemblyName *aname = &image->assembly->aname;
11029 buf_size += strlen (image->assembly_name) + strlen (image->guid) + (aname->culture ? strlen (aname->culture) : 1) + strlen ((char*)aname->public_key_token) + 4;
11032 buf = p = (guint8 *)g_malloc0 (buf_size);
11033 encode_int (acfg->image_table->len, p, &p);
11034 for (i = 0; i < acfg->image_table->len; i++) {
11035 MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i);
11036 MonoAssemblyName *aname = &image->assembly->aname;
11038 /* FIXME: Support multi-module assemblies */
11039 g_assert (image->assembly->image == image);
11041 encode_string (image->assembly_name, p, &p);
11042 encode_string (image->guid, p, &p);
11043 encode_string (aname->culture ? aname->culture : "", p, &p);
11044 encode_string ((const char*)aname->public_key_token, p, &p);
11046 while (GPOINTER_TO_UINT (p) % 8 != 0)
11047 p ++;
11049 encode_int (aname->flags, p, &p);
11050 encode_int (aname->major, p, &p);
11051 encode_int (aname->minor, p, &p);
11052 encode_int (aname->build, p, &p);
11053 encode_int (aname->revision, p, &p);
11055 g_assert (p - buf <= buf_size);
11057 emit_aot_data (acfg, MONO_AOT_TABLE_IMAGE_TABLE, "image_table", buf, p - buf);
11059 g_free (buf);
11062 static void
11063 emit_weak_field_indexes (MonoAotCompile *acfg)
11065 GHashTable *indexes;
11066 GHashTableIter iter;
11067 gpointer key, value;
11068 int buf_size;
11069 guint8 *buf, *p;
11071 /* Emit a table of weak field indexes, since computing these at runtime is expensive */
11072 mono_assembly_init_weak_fields (acfg->image);
11073 indexes = acfg->image->weak_field_indexes;
11074 g_assert (indexes);
11076 buf_size = (g_hash_table_size (indexes) + 1) * 4;
11077 buf = p = (guint8 *)g_malloc0 (buf_size);
11079 encode_int (g_hash_table_size (indexes), p, &p);
11080 g_hash_table_iter_init (&iter, indexes);
11081 while (g_hash_table_iter_next (&iter, &key, &value)) {
11082 guint32 index = GPOINTER_TO_UINT (key);
11083 encode_int (index, p, &p);
11085 g_assert (p - buf <= buf_size);
11087 emit_aot_data (acfg, MONO_AOT_TABLE_WEAK_FIELD_INDEXES, "weak_field_indexes", buf, p - buf);
11089 g_free (buf);
11092 static void
11093 emit_got_info (MonoAotCompile *acfg, gboolean llvm)
11095 int i, first_plt_got_patch = 0, buf_size;
11096 guint8 *p, *buf;
11097 guint32 *got_info_offsets;
11098 GotInfo *info = llvm ? &acfg->llvm_got_info : &acfg->got_info;
11100 /* Add the patches needed by the PLT to the GOT */
11101 if (!llvm) {
11102 acfg->plt_got_offset_base = acfg->got_offset;
11103 acfg->plt_got_info_offset_base = info->got_patches->len;
11104 first_plt_got_patch = acfg->plt_got_info_offset_base;
11105 for (i = 1; i < acfg->plt_offset; ++i) {
11106 MonoPltEntry *plt_entry = (MonoPltEntry *)g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i));
11108 g_ptr_array_add (info->got_patches, plt_entry->ji);
11110 acfg->stats.got_slot_types [plt_entry->ji->type] ++;
11113 acfg->got_offset += acfg->plt_offset;
11117 * FIXME:
11118 * - optimize offsets table.
11119 * - reduce number of exported symbols.
11120 * - emit info for a klass only once.
11121 * - determine when a method uses a GOT slot which is guaranteed to be already
11122 * initialized.
11123 * - clean up and document the code.
11124 * - use String.Empty in class libs.
11127 /* Encode info required to decode shared GOT entries */
11128 buf_size = info->got_patches->len * 128;
11129 p = buf = (guint8 *)mono_mempool_alloc (acfg->mempool, buf_size);
11130 got_info_offsets = (guint32 *)mono_mempool_alloc (acfg->mempool, info->got_patches->len * sizeof (guint32));
11131 if (!llvm) {
11132 acfg->plt_got_info_offsets = (guint32 *)mono_mempool_alloc (acfg->mempool, acfg->plt_offset * sizeof (guint32));
11133 /* Unused */
11134 if (acfg->plt_offset)
11135 acfg->plt_got_info_offsets [0] = 0;
11137 for (i = 0; i < info->got_patches->len; ++i) {
11138 MonoJumpInfo *ji = (MonoJumpInfo *)g_ptr_array_index (info->got_patches, i);
11139 guint8 *p2;
11141 p = buf;
11143 encode_value (ji->type, p, &p);
11144 p2 = p;
11145 encode_patch (acfg, ji, p, &p);
11146 acfg->stats.got_slot_info_sizes [ji->type] += p - p2;
11147 g_assert (p - buf <= buf_size);
11148 got_info_offsets [i] = add_to_blob (acfg, buf, p - buf);
11150 if (!llvm && i >= first_plt_got_patch)
11151 acfg->plt_got_info_offsets [i - first_plt_got_patch + 1] = got_info_offsets [i];
11152 acfg->stats.got_info_size += p - buf;
11155 /* Emit got_info_offsets table */
11156 #ifdef MONO_ARCH_CODE_EXEC_ONLY
11157 int got_info_offsets_to_emit = info->got_patches->len;
11158 #else
11159 /* No need to emit offsets for the got plt entries, the plt embeds them directly */
11160 int got_info_offsets_to_emit = first_plt_got_patch;
11161 #endif
11162 acfg->stats.offsets_size += emit_offset_table (acfg, llvm ? "llvm_got_info_offsets" : "got_info_offsets", llvm ? MONO_AOT_TABLE_LLVM_GOT_INFO_OFFSETS : MONO_AOT_TABLE_GOT_INFO_OFFSETS, llvm ? acfg->llvm_got_offset : got_info_offsets_to_emit, 10, (gint32*)got_info_offsets);
11165 static void
11166 emit_got (MonoAotCompile *acfg)
11168 char symbol [MAX_SYMBOL_SIZE];
11170 if (acfg->aot_opts.llvm_only)
11171 return;
11173 /* Don't make GOT global so accesses to it don't need relocations */
11174 sprintf (symbol, "%s", acfg->got_symbol);
11176 #ifdef TARGET_MACH
11177 emit_unset_mode (acfg);
11178 fprintf (acfg->fp, ".section __DATA, __bss\n");
11179 emit_alignment (acfg, 8);
11180 if (acfg->llvm)
11181 emit_info_symbol (acfg, "jit_got", FALSE);
11182 fprintf (acfg->fp, ".lcomm %s, %d\n", acfg->got_symbol, (int)(acfg->got_offset * sizeof (target_mgreg_t)));
11183 #else
11184 emit_section_change (acfg, ".bss", 0);
11185 emit_alignment (acfg, 8);
11186 if (acfg->aot_opts.write_symbols)
11187 emit_local_symbol (acfg, symbol, "got_end", FALSE);
11188 emit_label (acfg, symbol);
11189 if (acfg->llvm)
11190 emit_info_symbol (acfg, "jit_got", FALSE);
11191 if (acfg->got_offset > 0)
11192 emit_zero_bytes (acfg, (int)(acfg->got_offset * sizeof (target_mgreg_t)));
11193 #endif
11195 sprintf (symbol, "got_end");
11196 emit_label (acfg, symbol);
11199 typedef struct GlobalsTableEntry {
11200 guint32 value, index;
11201 struct GlobalsTableEntry *next;
11202 } GlobalsTableEntry;
11204 #ifdef TARGET_WIN32_MSVC
11205 #define DLL_ENTRY_POINT "DllMain"
11207 static void
11208 emit_library_info (MonoAotCompile *acfg)
11210 // Only include for shared libraries linked directly from generated object.
11211 if (link_shared_library (acfg)) {
11212 char *name = NULL;
11213 char symbol [MAX_SYMBOL_SIZE];
11215 // Ask linker to export all global symbols.
11216 emit_section_change (acfg, ".drectve", 0);
11217 for (guint i = 0; i < acfg->globals->len; ++i) {
11218 name = (char *)g_ptr_array_index (acfg->globals, i);
11219 g_assert (name != NULL);
11220 sprintf_s (symbol, MAX_SYMBOL_SIZE, " /EXPORT:%s", name);
11221 emit_string (acfg, symbol);
11224 // Emit DLLMain function, needed by MSVC linker for DLL's.
11225 // NOTE, DllMain should not go into exports above.
11226 emit_section_change (acfg, ".text", 0);
11227 emit_global (acfg, DLL_ENTRY_POINT, TRUE);
11228 emit_label (acfg, DLL_ENTRY_POINT);
11230 // Simple implementation of DLLMain, just returning TRUE.
11231 // For more information about DLLMain: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx
11232 fprintf (acfg->fp, "movl $1, %%eax\n");
11233 fprintf (acfg->fp, "ret\n");
11235 // Inform linker about our dll entry function.
11236 emit_section_change (acfg, ".drectve", 0);
11237 emit_string (acfg, "/ENTRY:" DLL_ENTRY_POINT);
11238 return;
11242 #else
11244 static void
11245 emit_library_info (MonoAotCompile *acfg)
11247 return;
11249 #endif
11251 static void
11252 emit_globals (MonoAotCompile *acfg)
11254 int i, table_size;
11255 guint32 hash;
11256 GPtrArray *table;
11257 char symbol [1024];
11258 GlobalsTableEntry *entry, *new_entry;
11260 if (!acfg->aot_opts.static_link)
11261 return;
11263 if (acfg->aot_opts.llvm_only) {
11264 g_assert (acfg->globals->len == 0);
11265 return;
11269 * When static linking, we emit a table containing our globals.
11273 * Construct a chained hash table for mapping global names to their index in
11274 * the globals table.
11276 table_size = g_spaced_primes_closest ((int)(acfg->globals->len * 1.5));
11277 table = g_ptr_array_sized_new (table_size);
11278 for (i = 0; i < table_size; ++i)
11279 g_ptr_array_add (table, NULL);
11280 for (i = 0; i < acfg->globals->len; ++i) {
11281 char *name = (char *)g_ptr_array_index (acfg->globals, i);
11283 hash = mono_metadata_str_hash (name) % table_size;
11285 /* FIXME: Allocate from the mempool */
11286 new_entry = g_new0 (GlobalsTableEntry, 1);
11287 new_entry->value = i;
11289 entry = (GlobalsTableEntry *)g_ptr_array_index (table, hash);
11290 if (entry == NULL) {
11291 new_entry->index = hash;
11292 g_ptr_array_index (table, hash) = new_entry;
11293 } else {
11294 while (entry->next)
11295 entry = entry->next;
11297 entry->next = new_entry;
11298 new_entry->index = table->len;
11299 g_ptr_array_add (table, new_entry);
11303 /* Emit the table */
11304 sprintf (symbol, ".Lglobals_hash");
11305 emit_section_change (acfg, RODATA_SECT, 0);
11306 emit_alignment (acfg, 8);
11307 emit_label (acfg, symbol);
11309 /* FIXME: Optimize memory usage */
11310 g_assert (table_size < 65000);
11311 emit_int16 (acfg, table_size);
11312 for (i = 0; i < table->len; ++i) {
11313 GlobalsTableEntry *entry = (GlobalsTableEntry *)g_ptr_array_index (table, i);
11315 if (entry == NULL) {
11316 emit_int16 (acfg, 0);
11317 emit_int16 (acfg, 0);
11318 } else {
11319 emit_int16 (acfg, entry->value + 1);
11320 if (entry->next)
11321 emit_int16 (acfg, entry->next->index);
11322 else
11323 emit_int16 (acfg, 0);
11327 /* Emit the names */
11328 for (i = 0; i < acfg->globals->len; ++i) {
11329 char *name = (char *)g_ptr_array_index (acfg->globals, i);
11331 sprintf (symbol, "name_%d", i);
11332 emit_section_change (acfg, RODATA_SECT, 1);
11333 #ifdef TARGET_MACH
11334 emit_alignment (acfg, 4);
11335 #endif
11336 emit_label (acfg, symbol);
11337 emit_string (acfg, name);
11340 /* Emit the globals table */
11341 sprintf (symbol, "globals");
11342 emit_section_change (acfg, ".data", 0);
11343 /* This is not a global, since it is accessed by the init function */
11344 emit_alignment (acfg, 8);
11345 emit_info_symbol (acfg, symbol, FALSE);
11347 sprintf (symbol, "%sglobals_hash", acfg->temp_prefix);
11348 emit_pointer (acfg, symbol);
11350 for (i = 0; i < acfg->globals->len; ++i) {
11351 char *name = (char *)g_ptr_array_index (acfg->globals, i);
11353 sprintf (symbol, "name_%d", i);
11354 emit_pointer (acfg, symbol);
11356 g_assert (strlen (name) < sizeof (symbol));
11357 sprintf (symbol, "%s", name);
11358 emit_pointer (acfg, symbol);
11360 /* Null terminate the table */
11361 emit_int32 (acfg, 0);
11362 emit_int32 (acfg, 0);
11365 static void
11366 emit_mem_end (MonoAotCompile *acfg)
11368 char symbol [128];
11370 if (acfg->aot_opts.llvm_only)
11371 return;
11373 sprintf (symbol, "mem_end");
11374 emit_section_change (acfg, ".text", 1);
11375 emit_alignment_code (acfg, 8);
11376 emit_label (acfg, symbol);
11379 static void
11380 init_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info)
11382 int i;
11384 info->version = MONO_AOT_FILE_VERSION;
11385 info->plt_got_offset_base = acfg->plt_got_offset_base;
11386 info->plt_got_info_offset_base = acfg->plt_got_info_offset_base;
11387 info->got_size = acfg->got_offset * sizeof (target_mgreg_t);
11388 info->llvm_got_size = acfg->llvm_got_offset * sizeof (target_mgreg_t);
11389 info->plt_size = acfg->plt_offset;
11390 info->nmethods = acfg->nmethods;
11391 info->call_table_entry_size = acfg->call_table_entry_size;
11392 info->nextra_methods = acfg->nextra_methods;
11393 info->flags = acfg->flags;
11394 info->opts = acfg->jit_opts;
11395 info->simd_opts = acfg->simd_opts;
11396 info->gc_name_index = acfg->gc_name_offset;
11397 info->datafile_size = acfg->datafile_offset;
11398 for (i = 0; i < MONO_AOT_TABLE_NUM; ++i)
11399 info->table_offsets [i] = acfg->table_offsets [i];
11400 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11401 info->num_trampolines [i] = acfg->num_trampolines [i];
11402 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11403 info->trampoline_got_offset_base [i] = acfg->trampoline_got_offset_base [i];
11404 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11405 info->trampoline_size [i] = acfg->trampoline_size [i];
11406 info->num_rgctx_fetch_trampolines = acfg->aot_opts.nrgctx_fetch_trampolines;
11408 int card_table_shift_bits = 0;
11409 target_mgreg_t card_table_mask = 0;
11411 mono_gc_get_target_card_table (&card_table_shift_bits, &card_table_mask);
11414 * Sanity checking variables used to make sure the host and target
11415 * environment matches when cross compiling.
11417 info->double_align = MONO_ABI_ALIGNOF (double);
11418 info->long_align = MONO_ABI_ALIGNOF (gint64);
11419 info->generic_tramp_num = MONO_TRAMPOLINE_NUM;
11420 info->card_table_shift_bits = card_table_shift_bits;
11421 info->card_table_mask = card_table_mask;
11423 info->tramp_page_size = acfg->tramp_page_size;
11424 info->nshared_got_entries = acfg->nshared_got_entries;
11425 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11426 info->tramp_page_code_offsets [i] = acfg->tramp_page_code_offsets [i];
11428 memcpy(&info->aotid, acfg->image->aotid, 16);
11431 static void
11432 emit_aot_file_info (MonoAotCompile *acfg, MonoAotFileInfo *info)
11434 char symbol [MAX_SYMBOL_SIZE];
11435 int i, sindex;
11436 const char **symbols;
11438 symbols = g_new0 (const char *, MONO_AOT_FILE_INFO_NUM_SYMBOLS);
11439 sindex = 0;
11440 symbols [sindex ++] = acfg->got_symbol;
11441 if (acfg->llvm) {
11442 symbols [sindex ++] = acfg->llvm_eh_frame_symbol;
11443 } else {
11444 symbols [sindex ++] = NULL;
11446 /* llvm_get_method */
11447 symbols [sindex ++] = NULL;
11448 /* llvm_get_unbox_tramp */
11449 symbols [sindex ++] = NULL;
11450 /* llvm_init_aotconst */
11451 symbols [sindex ++] = NULL;
11452 if (!acfg->aot_opts.llvm_only) {
11453 symbols [sindex ++] = "jit_code_start";
11454 symbols [sindex ++] = "jit_code_end";
11455 symbols [sindex ++] = "method_addresses";
11456 } else {
11457 symbols [sindex ++] = NULL;
11458 symbols [sindex ++] = NULL;
11459 symbols [sindex ++] = NULL;
11461 symbols [sindex ++] = NULL;
11462 symbols [sindex ++] = NULL;
11464 if (acfg->data_outfile) {
11465 for (i = 0; i < MONO_AOT_TABLE_NUM; ++i)
11466 symbols [sindex ++] = NULL;
11467 } else {
11468 symbols [sindex ++] = "blob";
11469 symbols [sindex ++] = "class_name_table";
11470 symbols [sindex ++] = "class_info_offsets";
11471 symbols [sindex ++] = "method_info_offsets";
11472 symbols [sindex ++] = "ex_info_offsets";
11473 symbols [sindex ++] = "extra_method_info_offsets";
11474 symbols [sindex ++] = "extra_method_table";
11475 symbols [sindex ++] = "got_info_offsets";
11476 if (acfg->llvm)
11477 symbols [sindex ++] = "llvm_got_info_offsets";
11478 else
11479 symbols [sindex ++] = NULL;
11480 symbols [sindex ++] = "image_table";
11481 symbols [sindex ++] = "weak_field_indexes";
11482 symbols [sindex ++] = "method_flags_table";
11485 symbols [sindex ++] = "mem_end";
11486 symbols [sindex ++] = "assembly_guid";
11487 symbols [sindex ++] = "runtime_version";
11488 if (acfg->num_trampoline_got_entries) {
11489 symbols [sindex ++] = "specific_trampolines";
11490 symbols [sindex ++] = "static_rgctx_trampolines";
11491 symbols [sindex ++] = "imt_trampolines";
11492 symbols [sindex ++] = "gsharedvt_arg_trampolines";
11493 symbols [sindex ++] = "ftnptr_arg_trampolines";
11494 symbols [sindex ++] = "unbox_arbitrary_trampolines";
11495 } else {
11496 symbols [sindex ++] = NULL;
11497 symbols [sindex ++] = NULL;
11498 symbols [sindex ++] = NULL;
11499 symbols [sindex ++] = NULL;
11500 symbols [sindex ++] = NULL;
11501 symbols [sindex ++] = NULL;
11503 if (acfg->aot_opts.static_link) {
11504 symbols [sindex ++] = "globals";
11505 } else {
11506 symbols [sindex ++] = NULL;
11508 symbols [sindex ++] = "assembly_name";
11509 symbols [sindex ++] = "plt";
11510 symbols [sindex ++] = "plt_end";
11511 symbols [sindex ++] = "unwind_info";
11512 if (!acfg->aot_opts.llvm_only) {
11513 symbols [sindex ++] = "unbox_trampolines";
11514 symbols [sindex ++] = "unbox_trampolines_end";
11515 symbols [sindex ++] = "unbox_trampoline_addresses";
11516 } else {
11517 symbols [sindex ++] = NULL;
11518 symbols [sindex ++] = NULL;
11519 symbols [sindex ++] = NULL;
11522 g_assert (sindex == MONO_AOT_FILE_INFO_NUM_SYMBOLS);
11524 sprintf (symbol, "%smono_aot_file_info", acfg->user_symbol_prefix);
11525 emit_section_change (acfg, ".data", 0);
11526 emit_alignment (acfg, 8);
11527 emit_label (acfg, symbol);
11528 if (!acfg->aot_opts.static_link)
11529 emit_global (acfg, symbol, FALSE);
11531 /* The data emitted here must match MonoAotFileInfo. */
11533 emit_int32 (acfg, info->version);
11534 emit_int32 (acfg, info->dummy);
11537 * We emit pointers to our data structures instead of emitting global symbols which
11538 * point to them, to reduce the number of globals, and because using globals leads to
11539 * various problems (i.e. arm/thumb).
11541 for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i)
11542 emit_pointer (acfg, symbols [i]);
11544 emit_int32 (acfg, info->plt_got_offset_base);
11545 emit_int32 (acfg, info->plt_got_info_offset_base);
11546 emit_int32 (acfg, info->got_size);
11547 emit_int32 (acfg, info->llvm_got_size);
11548 emit_int32 (acfg, info->plt_size);
11549 emit_int32 (acfg, info->nmethods);
11550 emit_int32 (acfg, info->nextra_methods);
11551 emit_int32 (acfg, info->flags);
11552 emit_int32 (acfg, info->opts);
11553 emit_int32 (acfg, info->simd_opts);
11554 emit_int32 (acfg, info->gc_name_index);
11555 emit_int32 (acfg, info->num_rgctx_fetch_trampolines);
11556 emit_int32 (acfg, info->double_align);
11557 emit_int32 (acfg, info->long_align);
11558 emit_int32 (acfg, info->generic_tramp_num);
11559 emit_int32 (acfg, info->card_table_shift_bits);
11560 emit_int32 (acfg, info->card_table_mask);
11561 emit_int32 (acfg, info->tramp_page_size);
11562 emit_int32 (acfg, info->call_table_entry_size);
11563 emit_int32 (acfg, info->nshared_got_entries);
11564 emit_int32 (acfg, info->datafile_size);
11565 emit_int32 (acfg, 0);
11566 emit_int32 (acfg, 0);
11568 for (i = 0; i < MONO_AOT_TABLE_NUM; ++i)
11569 emit_int32 (acfg, info->table_offsets [i]);
11570 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11571 emit_int32 (acfg, info->num_trampolines [i]);
11572 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11573 emit_int32 (acfg, info->trampoline_got_offset_base [i]);
11574 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11575 emit_int32 (acfg, info->trampoline_size [i]);
11576 for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i)
11577 emit_int32 (acfg, info->tramp_page_code_offsets [i]);
11579 emit_bytes (acfg, info->aotid, 16);
11581 if (acfg->aot_opts.static_link) {
11582 emit_global_inner (acfg, acfg->static_linking_symbol, FALSE);
11583 emit_alignment (acfg, sizeof (target_mgreg_t));
11584 emit_label (acfg, acfg->static_linking_symbol);
11585 emit_pointer_2 (acfg, acfg->user_symbol_prefix, "mono_aot_file_info");
11590 * Emit a structure containing all the information not stored elsewhere.
11592 static void
11593 emit_file_info (MonoAotCompile *acfg)
11595 char *build_info;
11596 MonoAotFileInfo *info;
11598 if (acfg->aot_opts.bind_to_runtime_version) {
11599 build_info = mono_get_runtime_build_info ();
11600 emit_string_symbol (acfg, "runtime_version", build_info);
11601 g_free (build_info);
11602 } else {
11603 emit_string_symbol (acfg, "runtime_version", "");
11606 emit_string_symbol (acfg, "assembly_guid" , acfg->image->guid);
11608 /* Emit a string holding the assembly name */
11609 emit_string_symbol (acfg, "assembly_name", acfg->image->assembly->aname.name);
11611 info = g_new0 (MonoAotFileInfo, 1);
11612 init_aot_file_info (acfg, info);
11614 if (acfg->aot_opts.static_link) {
11615 char symbol [MAX_SYMBOL_SIZE];
11616 char *p;
11619 * Emit a global symbol which can be passed by an embedding app to
11620 * mono_aot_register_module (). The symbol points to a pointer to the the file info
11621 * structure.
11623 sprintf (symbol, "%smono_aot_module_%s_info", acfg->user_symbol_prefix, acfg->image->assembly->aname.name);
11625 /* Get rid of characters which cannot occur in symbols */
11626 p = symbol;
11627 for (p = symbol; *p; ++p) {
11628 if (!(isalnum (*p) || *p == '_'))
11629 *p = '_';
11631 acfg->static_linking_symbol = g_strdup (symbol);
11634 if (acfg->llvm)
11635 mono_llvm_emit_aot_file_info (info, acfg->has_jitted_code);
11636 else
11637 emit_aot_file_info (acfg, info);
11640 static void
11641 emit_blob (MonoAotCompile *acfg)
11643 acfg->blob_closed = TRUE;
11645 emit_aot_data (acfg, MONO_AOT_TABLE_BLOB, "blob", (guint8*)acfg->blob.data, acfg->blob.index);
11648 static void
11649 emit_objc_selectors (MonoAotCompile *acfg)
11651 int i;
11652 char symbol [128];
11654 if (!acfg->objc_selectors || acfg->objc_selectors->len == 0)
11655 return;
11658 * From
11659 * cat > foo.m << EOF
11660 * void *ret ()
11662 * return @selector(print:);
11664 * EOF
11667 mono_img_writer_emit_unset_mode (acfg->w);
11668 g_assert (acfg->fp);
11669 fprintf (acfg->fp, ".section __DATA,__objc_selrefs,literal_pointers,no_dead_strip\n");
11670 fprintf (acfg->fp, ".align 3\n");
11671 for (i = 0; i < acfg->objc_selectors->len; ++i) {
11672 sprintf (symbol, "L_OBJC_SELECTOR_REFERENCES_%d", i);
11673 emit_label (acfg, symbol);
11674 sprintf (symbol, "L_OBJC_METH_VAR_NAME_%d", i);
11675 emit_pointer (acfg, symbol);
11678 fprintf (acfg->fp, ".section __TEXT,__cstring,cstring_literals\n");
11679 for (i = 0; i < acfg->objc_selectors->len; ++i) {
11680 fprintf (acfg->fp, "L_OBJC_METH_VAR_NAME_%d:\n", i);
11681 fprintf (acfg->fp, ".asciz \"%s\"\n", (char*)g_ptr_array_index (acfg->objc_selectors, i));
11684 fprintf (acfg->fp, ".section __DATA,__objc_imageinfo,regular,no_dead_strip\n");
11685 fprintf (acfg->fp, ".align 3\n");
11686 fprintf (acfg->fp, "L_OBJC_IMAGE_INFO:\n");
11687 fprintf (acfg->fp, ".long 0\n");
11688 fprintf (acfg->fp, ".long 16\n");
11691 static void
11692 emit_dwarf_info (MonoAotCompile *acfg)
11694 #ifdef EMIT_DWARF_INFO
11695 int i;
11696 char symbol2 [128];
11698 /* DIEs for methods */
11699 for (i = 0; i < acfg->nmethods; ++i) {
11700 MonoCompile *cfg = acfg->cfgs [i];
11702 if (ignore_cfg (cfg))
11703 continue;
11705 // FIXME: LLVM doesn't define .Lme_...
11706 if (cfg->compile_llvm)
11707 continue;
11709 sprintf (symbol2, "%sme_%x", acfg->temp_prefix, i);
11711 mono_dwarf_writer_emit_method (acfg->dwarf, cfg, cfg->method, cfg->asm_symbol, symbol2, cfg->asm_debug_symbol, (guint8 *)cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->d.method, mono_domain_get ()));
11713 #endif
11716 #ifdef EMIT_WIN32_CODEVIEW_INFO
11717 typedef struct _CodeViewSubSectionData
11719 gchar *start_section;
11720 gchar *end_section;
11721 gchar *start_section_record;
11722 gchar *end_section_record;
11723 int section_type;
11724 int section_record_type;
11725 int section_id;
11726 } CodeViewSubsectionData;
11728 typedef struct _CodeViewCompilerVersion
11730 gint major;
11731 gint minor;
11732 gint revision;
11733 gint patch;
11734 } CodeViewCompilerVersion;
11736 #define CODEVIEW_SUBSECTION_SYMBOL_TYPE 0xF1
11737 #define CODEVIEW_SUBSECTION_RECORD_COMPILER_TYPE 0x113c
11738 #define CODEVIEW_SUBSECTION_RECORD_FUNCTION_START_TYPE 0x1147
11739 #define CODEVIEW_SUBSECTION_RECORD_FUNCTION_END_TYPE 0x114F
11740 #define CODEVIEW_CSHARP_LANGUAGE_TYPE 0x0A
11741 #define CODEVIEW_CPU_TYPE 0x0
11742 #define CODEVIEW_MAGIC_HEADER 0x4
11744 static void
11745 codeview_clear_subsection_data (CodeViewSubsectionData *section_data)
11747 g_free (section_data->start_section);
11748 g_free (section_data->end_section);
11749 g_free (section_data->start_section_record);
11750 g_free (section_data->end_section_record);
11752 memset (section_data, 0, sizeof (CodeViewSubsectionData));
11755 static void
11756 codeview_parse_compiler_version (gchar *version, CodeViewCompilerVersion *data)
11758 gint values[4] = { 0 };
11759 gint *value = values;
11761 while (*version && (value < values + G_N_ELEMENTS (values))) {
11762 if (isdigit (*version)) {
11763 *value *= 10;
11764 *value += *version - '0';
11766 else if (*version == '.') {
11767 value++;
11770 version++;
11773 data->major = values[0];
11774 data->minor = values[1];
11775 data->revision = values[2];
11776 data->patch = values[3];
11779 static void
11780 emit_codeview_start_subsection (MonoAotCompile *acfg, int section_id, int section_type, int section_record_type, CodeViewSubsectionData *section_data)
11782 // Starting a new subsection, clear old data.
11783 codeview_clear_subsection_data (section_data);
11785 // Keep subsection data.
11786 section_data->section_id = section_id;
11787 section_data->section_type = section_type;
11788 section_data->section_record_type = section_record_type;
11790 // Allocate all labels used in subsection.
11791 section_data->start_section = g_strdup_printf ("%scvs_%d", acfg->temp_prefix, section_data->section_id);
11792 section_data->end_section = g_strdup_printf ("%scvse_%d", acfg->temp_prefix, section_data->section_id);
11793 section_data->start_section_record = g_strdup_printf ("%scvsr_%d", acfg->temp_prefix, section_data->section_id);
11794 section_data->end_section_record = g_strdup_printf ("%scvsre_%d", acfg->temp_prefix, section_data->section_id);
11796 // Subsection type, function symbol.
11797 emit_int32 (acfg, section_data->section_type);
11799 // Subsection size.
11800 emit_symbol_diff (acfg, section_data->end_section, section_data->start_section, 0);
11801 emit_label (acfg, section_data->start_section);
11803 // Subsection record size.
11804 fprintf (acfg->fp, "\t.word %s - %s\n", section_data->end_section_record, section_data->start_section_record);
11805 emit_label (acfg, section_data->start_section_record);
11807 // Subsection record type.
11808 emit_int16 (acfg, section_record_type);
11811 static void
11812 emit_codeview_end_subsection (MonoAotCompile *acfg, CodeViewSubsectionData *section_data, int *section_id)
11814 g_assert (section_data->start_section);
11815 g_assert (section_data->end_section);
11816 g_assert (section_data->start_section_record);
11817 g_assert (section_data->end_section_record);
11819 emit_label (acfg, section_data->end_section_record);
11821 if (section_data->section_record_type == CODEVIEW_SUBSECTION_RECORD_FUNCTION_START_TYPE) {
11822 // Emit record length.
11823 emit_int16 (acfg, 2);
11825 // Emit specific record type end.
11826 emit_int16 (acfg, CODEVIEW_SUBSECTION_RECORD_FUNCTION_END_TYPE);
11829 emit_label (acfg, section_data->end_section);
11831 // Next subsection needs to be 4 byte aligned.
11832 emit_alignment (acfg, 4);
11834 *section_id = section_data->section_id + 1;
11835 codeview_clear_subsection_data (section_data);
11838 inline static void
11839 emit_codeview_start_symbol_subsection (MonoAotCompile *acfg, int section_id, int section_record_type, CodeViewSubsectionData *section_data)
11841 emit_codeview_start_subsection (acfg, section_id, CODEVIEW_SUBSECTION_SYMBOL_TYPE, section_record_type, section_data);
11844 inline static void
11845 emit_codeview_end_symbol_subsection (MonoAotCompile *acfg, CodeViewSubsectionData *section_data, int *section_id)
11847 emit_codeview_end_subsection (acfg, section_data, section_id);
11850 static void
11851 emit_codeview_compiler_info (MonoAotCompile *acfg, int *section_id)
11853 CodeViewSubsectionData section_data = { 0 };
11854 CodeViewCompilerVersion compiler_version = { 0 };
11856 // Start new compiler record subsection.
11857 emit_codeview_start_symbol_subsection (acfg, *section_id, CODEVIEW_SUBSECTION_RECORD_COMPILER_TYPE, &section_data);
11859 emit_int32 (acfg, CODEVIEW_CSHARP_LANGUAGE_TYPE);
11860 emit_int16 (acfg, CODEVIEW_CPU_TYPE);
11862 // Get compiler version information.
11863 codeview_parse_compiler_version (VERSION, &compiler_version);
11865 // Compiler frontend version, 4 digits.
11866 emit_int16 (acfg, compiler_version.major);
11867 emit_int16 (acfg, compiler_version.minor);
11868 emit_int16 (acfg, compiler_version.revision);
11869 emit_int16 (acfg, compiler_version.patch);
11871 // Compiler backend version, 4 digits (currently same as frontend).
11872 emit_int16 (acfg, compiler_version.major);
11873 emit_int16 (acfg, compiler_version.minor);
11874 emit_int16 (acfg, compiler_version.revision);
11875 emit_int16 (acfg, compiler_version.patch);
11877 // Compiler string.
11878 emit_string (acfg, "Mono AOT compiler");
11880 // Done with section.
11881 emit_codeview_end_symbol_subsection (acfg, &section_data, section_id);
11884 static void
11885 emit_codeview_function_info (MonoAotCompile *acfg, MonoMethod *method, int *section_id, gchar *symbol, gchar *symbol_start, gchar *symbol_end)
11887 CodeViewSubsectionData section_data = { 0 };
11888 gchar *full_method_name = NULL;
11890 // Start new function record subsection.
11891 emit_codeview_start_symbol_subsection (acfg, *section_id, CODEVIEW_SUBSECTION_RECORD_FUNCTION_START_TYPE, &section_data);
11893 // Emit 3 int 0 byte padding, currently not used.
11894 emit_zero_bytes (acfg, sizeof (int) * 3);
11896 // Emit size of function.
11897 emit_symbol_diff (acfg, symbol_end, symbol_start, 0);
11899 // Emit 3 int 0 byte padding, currently not used.
11900 emit_zero_bytes (acfg, sizeof (int) * 3);
11902 // Emit reallocation info.
11903 fprintf (acfg->fp, "\t.secrel32 %s\n", symbol);
11904 fprintf (acfg->fp, "\t.secidx %s\n", symbol);
11906 // Emit flag, currently not used.
11907 emit_zero_bytes (acfg, 1);
11909 // Emit function name, exclude signature since it should be described by own metadata.
11910 full_method_name = mono_method_full_name (method, FALSE);
11911 emit_string (acfg, full_method_name ? full_method_name : "");
11912 g_free (full_method_name);
11914 // Done with section.
11915 emit_codeview_end_symbol_subsection (acfg, &section_data, section_id);
11918 static void
11919 emit_codeview_info (MonoAotCompile *acfg)
11921 int i;
11922 int section_id = 0;
11923 gchar symbol_buffer[MAX_SYMBOL_SIZE];
11925 // Emit codeview debug info section
11926 emit_section_change (acfg, ".debug$S", 0);
11928 // Emit magic header.
11929 emit_int32 (acfg, CODEVIEW_MAGIC_HEADER);
11931 emit_codeview_compiler_info (acfg, &section_id);
11933 for (i = 0; i < acfg->nmethods; ++i) {
11934 MonoCompile *cfg = acfg->cfgs[i];
11936 if (!cfg || COMPILE_LLVM (cfg))
11937 continue;
11939 int ret = g_snprintf (symbol_buffer, G_N_ELEMENTS (symbol_buffer), "%sme_%x", acfg->temp_prefix, i);
11940 if (ret > 0 && ret < G_N_ELEMENTS (symbol_buffer))
11941 emit_codeview_function_info (acfg, cfg->method, &section_id, cfg->asm_debug_symbol, cfg->asm_symbol, symbol_buffer);
11944 #else
11945 static void
11946 emit_codeview_info (MonoAotCompile *acfg)
11949 #endif /* EMIT_WIN32_CODEVIEW_INFO */
11951 #ifdef EMIT_WIN32_UNWIND_INFO
11952 static UnwindInfoSectionCacheItem *
11953 get_cached_unwind_info_section_item_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops)
11955 UnwindInfoSectionCacheItem *item = NULL;
11957 if (!acfg->unwind_info_section_cache)
11958 acfg->unwind_info_section_cache = g_list_alloc ();
11960 PUNWIND_INFO unwind_info = mono_arch_unwindinfo_alloc_unwind_info (unwind_ops);
11962 // Search for unwind info in cache.
11963 GList *list = acfg->unwind_info_section_cache;
11964 int list_size = 0;
11965 while (list && list->data) {
11966 item = (UnwindInfoSectionCacheItem*)list->data;
11967 if (!memcmp (unwind_info, item->unwind_info, sizeof (UNWIND_INFO))) {
11968 // Cache hit, return cached item.
11969 return item;
11971 list = list->next;
11972 list_size++;
11975 // Add to cache.
11976 if (acfg->unwind_info_section_cache) {
11977 item = g_new0 (UnwindInfoSectionCacheItem, 1);
11978 if (item) {
11979 // Format .xdata section label for function, used to get unwind info address RVA.
11980 // Since the unwind info is similar for most functions, the symbol will be reused.
11981 item->xdata_section_label = g_strdup_printf ("%sunwind_%d", acfg->temp_prefix, list_size);
11983 // Cache unwind info data, used when checking cache for matching unwind info. NOTE, cache takes
11984 //over ownership of unwind info.
11985 item->unwind_info = unwind_info;
11987 // Needs to be emitted once.
11988 item->xdata_section_emitted = FALSE;
11990 // Prepend to beginning of list to speed up inserts.
11991 acfg->unwind_info_section_cache = g_list_prepend (acfg->unwind_info_section_cache, (gpointer)item);
11995 return item;
11998 static void
11999 free_unwind_info_section_cache_win32 (MonoAotCompile *acfg)
12001 GList *list = acfg->unwind_info_section_cache;
12003 while (list) {
12004 UnwindInfoSectionCacheItem *item = (UnwindInfoSectionCacheItem *)list->data;
12005 if (item) {
12006 g_free (item->xdata_section_label);
12007 mono_arch_unwindinfo_free_unwind_info (item->unwind_info);
12009 g_free (item);
12010 list->data = NULL;
12013 list = list->next;
12016 g_list_free (acfg->unwind_info_section_cache);
12017 acfg->unwind_info_section_cache = NULL;
12020 static void
12021 emit_unwind_info_data_win32 (MonoAotCompile *acfg, PUNWIND_INFO unwind_info)
12023 // Emit the unwind info struct.
12024 emit_bytes (acfg, (guint8*)unwind_info, sizeof (UNWIND_INFO) - (sizeof (UNWIND_CODE) * MONO_MAX_UNWIND_CODES));
12026 // Emit all unwind codes encoded in unwind info struct.
12027 PUNWIND_CODE current_unwind_node = &unwind_info->UnwindCode[MONO_MAX_UNWIND_CODES - unwind_info->CountOfCodes];
12028 PUNWIND_CODE last_unwind_node = &unwind_info->UnwindCode[MONO_MAX_UNWIND_CODES];
12030 while (current_unwind_node < last_unwind_node) {
12031 guint8 node_count = 0;
12032 switch (current_unwind_node->UnwindOp) {
12033 case UWOP_PUSH_NONVOL:
12034 case UWOP_ALLOC_SMALL:
12035 case UWOP_SET_FPREG:
12036 case UWOP_PUSH_MACHFRAME:
12037 node_count = 1;
12038 break;
12039 case UWOP_SAVE_NONVOL:
12040 case UWOP_SAVE_XMM128:
12041 node_count = 2;
12042 break;
12043 case UWOP_SAVE_NONVOL_FAR:
12044 case UWOP_SAVE_XMM128_FAR:
12045 node_count = 3;
12046 break;
12047 case UWOP_ALLOC_LARGE:
12048 if (current_unwind_node->OpInfo == 0)
12049 node_count = 2;
12050 else
12051 node_count = 3;
12052 break;
12053 default:
12054 g_assert (!"Unknown unwind opcode.");
12057 while (node_count > 0) {
12058 g_assert (current_unwind_node < last_unwind_node);
12060 //Emit current node.
12061 emit_bytes (acfg, (guint8*)current_unwind_node, sizeof (UNWIND_CODE));
12063 node_count--;
12064 current_unwind_node++;
12069 // Emit unwind info sections for each function. Unwind info on Windows x64 is emitted into two different sections.
12070 // .pdata includes the serialized DWORD aligned RVA's of function start, end and address of serialized
12071 // UNWIND_INFO struct emitted into .xdata, see https://msdn.microsoft.com/en-us/library/ft9x1kdx.aspx.
12072 // .xdata section includes DWORD aligned serialized version of UNWIND_INFO struct, https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx.
12073 static void
12074 emit_unwind_info_sections_win32 (MonoAotCompile *acfg, const char *function_start, const char *function_end, GSList *unwind_ops)
12076 char *pdata_section_label = NULL;
12078 int temp_prefix_len = (acfg->temp_prefix != NULL) ? strlen (acfg->temp_prefix) : 0;
12079 if (strncmp (function_start, acfg->temp_prefix, temp_prefix_len)) {
12080 temp_prefix_len = 0;
12083 // Format .pdata section label for function.
12084 pdata_section_label = g_strdup_printf ("%spdata_%s", acfg->temp_prefix, function_start + temp_prefix_len);
12086 UnwindInfoSectionCacheItem *cache_item = get_cached_unwind_info_section_item_win32 (acfg, function_start, function_end, unwind_ops);
12087 g_assert (cache_item && cache_item->xdata_section_label && cache_item->unwind_info);
12089 // Emit .pdata section.
12090 emit_section_change (acfg, ".pdata", 0);
12091 emit_alignment (acfg, sizeof (DWORD));
12092 emit_label (acfg, pdata_section_label);
12094 // Emit function start address RVA.
12095 fprintf (acfg->fp, "\t.long %s@IMGREL\n", function_start);
12097 // Emit function end address RVA.
12098 fprintf (acfg->fp, "\t.long %s@IMGREL\n", function_end);
12100 // Emit unwind info address RVA.
12101 fprintf (acfg->fp, "\t.long %s@IMGREL\n", cache_item->xdata_section_label);
12103 if (!cache_item->xdata_section_emitted) {
12104 // Emit .xdata section.
12105 emit_section_change (acfg, ".xdata", 0);
12106 emit_alignment (acfg, sizeof (DWORD));
12107 emit_label (acfg, cache_item->xdata_section_label);
12109 // Emit unwind info into .xdata section.
12110 emit_unwind_info_data_win32 (acfg, cache_item->unwind_info);
12111 cache_item->xdata_section_emitted = TRUE;
12114 g_free (pdata_section_label);
12116 #endif
12118 static gboolean
12119 should_emit_gsharedvt_method (MonoAotCompile *acfg, MonoMethod *method)
12121 #ifdef TARGET_WASM
12122 if (acfg->image == mono_get_corlib () && !strcmp (m_class_get_name (method->klass), "Vector`1"))
12123 /* The SIMD fallback code in Vector<T> is very large, and not likely to be used */
12124 return FALSE;
12125 #endif
12126 if (acfg->image == mono_get_corlib () && !strcmp (m_class_get_name (method->klass), "Volatile"))
12127 /* Read<T>/Write<T> are not needed and cause JIT failures */
12128 return FALSE;
12129 return TRUE;
12132 static gboolean
12133 collect_methods (MonoAotCompile *acfg)
12135 int mindex, i;
12136 MonoImage *image = acfg->image;
12138 /* Collect methods */
12139 for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) {
12140 ERROR_DECL (error);
12141 MonoMethod *method;
12142 guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1);
12144 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
12146 if (!method) {
12147 aot_printerrf (acfg, "Failed to load method 0x%x from '%s' due to %s.\n", token, image->name, mono_error_get_message (error));
12148 aot_printerrf (acfg, "Run with MONO_LOG_LEVEL=debug for more information.\n");
12149 mono_error_cleanup (error);
12150 return FALSE;
12153 /* Load all methods eagerly to skip the slower lazy loading code */
12154 mono_class_setup_methods (method->klass);
12156 if (mono_aot_mode_is_full (&acfg->aot_opts) && method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
12157 /* Compile the wrapper instead */
12158 /* We do this here instead of add_wrappers () because it is easy to do it here */
12159 MonoMethod *wrapper = mono_marshal_get_native_wrapper (method, TRUE, TRUE);
12160 method = wrapper;
12163 /* FIXME: Some mscorlib methods don't have debug info */
12165 if (acfg->aot_opts.soft_debug && !method->wrapper_type) {
12166 if (!((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
12167 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
12168 (method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
12169 (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))) {
12170 if (!mono_debug_lookup_method (method)) {
12171 fprintf (stderr, "Method %s has no debug info, probably the .mdb file for the assembly is missing.\n", mono_method_get_full_name (method));
12172 exit (1);
12178 if (method->is_generic || mono_class_is_gtd (method->klass)) {
12179 /* Compile the ref shared version instead */
12180 method = mini_get_shared_method_full (method, SHARE_MODE_NONE, error);
12181 if (!method) {
12182 aot_printerrf (acfg, "Failed to load method 0x%x from '%s' due to %s.\n", token, image->name, mono_error_get_message (error));
12183 aot_printerrf (acfg, "Run with MONO_LOG_LEVEL=debug for more information.\n");
12184 mono_error_cleanup (error);
12185 return FALSE;
12189 /* Since we add the normal methods first, their index will be equal to their zero based token index */
12190 add_method_with_index (acfg, method, i, FALSE);
12191 acfg->method_index ++;
12194 /* gsharedvt methods */
12195 for (mindex = 0; mindex < image->tables [MONO_TABLE_METHOD].rows; ++mindex) {
12196 ERROR_DECL (error);
12197 MonoMethod *method;
12198 guint32 token = MONO_TOKEN_METHOD_DEF | (mindex + 1);
12200 if (!(acfg->jit_opts & MONO_OPT_GSHAREDVT))
12201 continue;
12203 method = mono_get_method_checked (acfg->image, token, NULL, NULL, error);
12204 report_loader_error (acfg, error, TRUE, "Failed to load method token 0x%x due to %s\n", i, mono_error_get_message (error));
12206 if ((method->is_generic || mono_class_is_gtd (method->klass)) && should_emit_gsharedvt_method (acfg, method)) {
12207 MonoMethod *gshared;
12209 gshared = mini_get_shared_method_full (method, SHARE_MODE_GSHAREDVT, error);
12210 mono_error_assert_ok (error);
12212 add_extra_method (acfg, gshared);
12216 if (mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts))
12217 add_generic_instances (acfg);
12219 if (mono_aot_mode_is_full (&acfg->aot_opts))
12220 add_wrappers (acfg);
12221 return TRUE;
12224 static void
12225 compile_methods (MonoAotCompile *acfg)
12227 int i, methods_len;
12229 if (acfg->aot_opts.nthreads > 0) {
12230 GPtrArray *frag;
12231 int len, j;
12232 GPtrArray *threads;
12233 MonoThreadHandle *thread_handle;
12234 gpointer *user_data;
12235 MonoMethod **methods;
12237 methods_len = acfg->methods->len;
12239 len = acfg->methods->len / acfg->aot_opts.nthreads;
12240 g_assert (len > 0);
12242 * Partition the list of methods into fragments, and hand it to threads to
12243 * process.
12245 threads = g_ptr_array_new ();
12246 /* Make a copy since acfg->methods is modified by compile_method () */
12247 methods = g_new0 (MonoMethod*, methods_len);
12248 //memcpy (methods, g_ptr_array_index (acfg->methods, 0), sizeof (MonoMethod*) * methods_len);
12249 for (i = 0; i < methods_len; ++i)
12250 methods [i] = (MonoMethod *)g_ptr_array_index (acfg->methods, i);
12251 i = 0;
12252 while (i < methods_len) {
12253 ERROR_DECL (error);
12254 MonoInternalThread *thread;
12256 frag = g_ptr_array_new ();
12257 for (j = 0; j < len; ++j) {
12258 if (i < methods_len) {
12259 g_ptr_array_add (frag, methods [i]);
12260 i ++;
12264 user_data = g_new0 (gpointer, 3);
12265 user_data [0] = acfg;
12266 user_data [1] = frag;
12268 thread = mono_thread_create_internal (mono_domain_get (), (gpointer)compile_thread_main, user_data, MONO_THREAD_CREATE_FLAGS_NONE, error);
12269 mono_error_assert_ok (error);
12271 thread_handle = mono_threads_open_thread_handle (thread->handle);
12272 g_ptr_array_add (threads, thread_handle);
12274 g_free (methods);
12276 for (i = 0; i < threads->len; ++i) {
12277 mono_thread_info_wait_one_handle ((MonoThreadHandle*)g_ptr_array_index (threads, i), MONO_INFINITE_WAIT, FALSE);
12278 mono_threads_close_thread_handle ((MonoThreadHandle*)g_ptr_array_index (threads, i));
12280 } else {
12281 methods_len = 0;
12284 /* Compile methods added by compile_method () or all methods if nthreads == 0 */
12285 for (i = methods_len; i < acfg->methods->len; ++i) {
12286 /* This can add new methods to acfg->methods */
12287 compile_method (acfg, (MonoMethod *)g_ptr_array_index (acfg->methods, i));
12290 #ifdef ENABLE_LLVM
12291 if (acfg->llvm)
12292 mono_llvm_fixup_aot_module ();
12293 #endif
12296 static int
12297 compile_asm (MonoAotCompile *acfg)
12299 char *command, *objfile;
12300 char *outfile_name, *tmp_outfile_name, *llvm_ofile;
12301 const char *tool_prefix = acfg->aot_opts.tool_prefix ? acfg->aot_opts.tool_prefix : "";
12302 char *ld_flags = acfg->aot_opts.ld_flags ? acfg->aot_opts.ld_flags : g_strdup("");
12304 #ifdef TARGET_WIN32_MSVC
12305 #define AS_OPTIONS "--target=x86_64-pc-windows-msvc -c -x assembler"
12306 #elif defined(TARGET_AMD64) && !defined(TARGET_MACH)
12307 #define AS_OPTIONS "--64"
12308 #elif defined(TARGET_POWERPC64)
12309 #define AS_OPTIONS "-a64 -mppc64"
12310 #elif defined(sparc) && TARGET_SIZEOF_VOID_P == 8
12311 #define AS_OPTIONS "-xarch=v9"
12312 #elif defined(TARGET_X86) && defined(TARGET_MACH)
12313 #define AS_OPTIONS "-arch i386"
12314 #elif defined(TARGET_X86) && !defined(TARGET_MACH)
12315 #define AS_OPTIONS "--32"
12316 #else
12317 #define AS_OPTIONS ""
12318 #endif
12320 #if defined(TARGET_OSX)
12321 #define AS_NAME "clang"
12322 #elif defined(TARGET_WIN32_MSVC)
12323 #define AS_NAME "clang.exe"
12324 #else
12325 #define AS_NAME "as"
12326 #endif
12328 #ifdef TARGET_WIN32_MSVC
12329 #define AS_OBJECT_FILE_SUFFIX "obj"
12330 #else
12331 #define AS_OBJECT_FILE_SUFFIX "o"
12332 #endif
12334 #if defined(sparc)
12335 #define LD_NAME "ld"
12336 #define LD_OPTIONS "-shared -G -Bsymbolic"
12337 #elif defined(__ppc__) && defined(TARGET_MACH)
12338 #define LD_NAME "gcc"
12339 #define LD_OPTIONS "-dynamiclib -Wl,-Bsymbolic"
12340 #elif defined(TARGET_AMD64) && defined(TARGET_MACH)
12341 #define LD_NAME "clang"
12342 #define LD_OPTIONS "--shared"
12343 #elif defined(TARGET_ARM64) && defined(TARGET_OSX)
12344 #define LD_NAME "clang"
12345 #define LD_OPTIONS "--shared"
12346 #elif defined(TARGET_WIN32_MSVC)
12347 #define LD_NAME "link.exe"
12348 #define LD_OPTIONS "/DLL /MACHINE:X64 /NOLOGO /INCREMENTAL:NO"
12349 #define LD_DEBUG_OPTIONS LD_OPTIONS " /DEBUG"
12350 #elif defined(TARGET_WIN32) && !defined(TARGET_ANDROID)
12351 #define LD_NAME "gcc"
12352 #define LD_OPTIONS "-shared -Wl,-Bsymbolic"
12353 #elif defined(TARGET_X86) && defined(TARGET_MACH)
12354 #define LD_NAME "clang"
12355 #define LD_OPTIONS "-m32 -dynamiclib"
12356 #elif defined(TARGET_X86) && !defined(TARGET_MACH)
12357 #define LD_OPTIONS "-m elf_i386 -Bsymbolic"
12358 #elif defined(TARGET_ARM) && !defined(TARGET_ANDROID)
12359 #define LD_NAME "gcc"
12360 #define LD_OPTIONS "--shared -Wl,-Bsymbolic"
12361 #elif defined(TARGET_POWERPC64)
12362 #define LD_OPTIONS "-m elf64ppc -Bsymbolic"
12363 #endif
12365 #ifndef LD_OPTIONS
12366 #define LD_OPTIONS "-Bsymbolic"
12367 #endif
12369 if (acfg->aot_opts.asm_only) {
12370 aot_printf (acfg, "Output file: '%s'.\n", acfg->tmpfname);
12371 if (acfg->aot_opts.static_link)
12372 aot_printf (acfg, "Linking symbol: '%s'.\n", acfg->static_linking_symbol);
12373 if (acfg->llvm)
12374 aot_printf (acfg, "LLVM output file: '%s'.\n", acfg->llvm_sfile);
12375 return 0;
12378 if (acfg->aot_opts.static_link) {
12379 if (acfg->aot_opts.outfile)
12380 objfile = g_strdup_printf ("%s", acfg->aot_opts.outfile);
12381 else
12382 objfile = g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->image->name);
12383 } else {
12384 objfile = g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->tmpfname);
12387 #ifdef TARGET_OSX
12388 g_string_append (acfg->as_args, "-c -x assembler");
12389 #endif
12391 command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS,
12392 acfg->as_args ? acfg->as_args->str : "",
12393 wrap_path (objfile), wrap_path (acfg->tmpfname));
12394 aot_printf (acfg, "Executing the native assembler: %s\n", command);
12395 if (execute_system (command) != 0) {
12396 g_free (command);
12397 g_free (objfile);
12398 return 1;
12401 if (acfg->llvm && !acfg->llvm_owriter) {
12402 command = g_strdup_printf ("\"%s%s\" %s %s -o %s %s", tool_prefix, AS_NAME, AS_OPTIONS,
12403 acfg->as_args ? acfg->as_args->str : "",
12404 wrap_path (acfg->llvm_ofile), wrap_path (acfg->llvm_sfile));
12405 aot_printf (acfg, "Executing the native assembler: %s\n", command);
12406 if (execute_system (command) != 0) {
12407 g_free (command);
12408 g_free (objfile);
12409 return 1;
12413 g_free (command);
12415 if (acfg->aot_opts.static_link) {
12416 aot_printf (acfg, "Output file: '%s'.\n", objfile);
12417 aot_printf (acfg, "Linking symbol: '%s'.\n", acfg->static_linking_symbol);
12418 g_free (objfile);
12419 return 0;
12422 if (acfg->aot_opts.outfile)
12423 outfile_name = g_strdup_printf ("%s", acfg->aot_opts.outfile);
12424 else
12425 outfile_name = g_strdup_printf ("%s%s", acfg->image->name, MONO_SOLIB_EXT);
12427 tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name);
12429 if (acfg->llvm) {
12430 llvm_ofile = g_strdup_printf ("\"%s\"", acfg->llvm_ofile);
12431 } else {
12432 llvm_ofile = g_strdup ("");
12435 /* replace the ; flags separators with spaces */
12436 g_strdelimit (ld_flags, ';', ' ');
12438 if (acfg->aot_opts.llvm_only)
12439 ld_flags = g_strdup_printf ("%s %s", ld_flags, "-lstdc++");
12441 #ifdef TARGET_WIN32_MSVC
12442 g_assert (tmp_outfile_name != NULL);
12443 g_assert (objfile != NULL);
12444 command = g_strdup_printf ("\"%s%s\" %s %s /OUT:%s %s %s", tool_prefix, LD_NAME,
12445 acfg->aot_opts.nodebug ? LD_OPTIONS : LD_DEBUG_OPTIONS, ld_flags, wrap_path (tmp_outfile_name), wrap_path (objfile), wrap_path (llvm_ofile));
12446 #else
12447 GString *str;
12449 str = g_string_new ("");
12450 #if defined(LD_NAME)
12451 g_string_append_printf (str, "%s%s %s", tool_prefix, LD_NAME, LD_OPTIONS);
12452 #else
12453 // Default (linux)
12454 if (acfg->aot_opts.tool_prefix)
12455 /* Cross compiling */
12456 g_string_append_printf (str, "\"%sld\" %s", tool_prefix, LD_OPTIONS);
12457 else if (acfg->aot_opts.llvm_only)
12458 g_string_append_printf (str, "%s", acfg->aot_opts.clangxx);
12459 else
12460 g_string_append_printf (str, "\"%sld\"", tool_prefix);
12461 g_string_append_printf (str, " -shared");
12462 #endif
12463 g_string_append_printf (str, " -o %s %s %s %s",
12464 wrap_path (tmp_outfile_name), wrap_path (llvm_ofile),
12465 wrap_path (g_strdup_printf ("%s." AS_OBJECT_FILE_SUFFIX, acfg->tmpfname)), ld_flags);
12467 #if defined(TARGET_MACH)
12468 g_string_append_printf (str, " -Wl,-install_name,%s%s", g_path_get_basename (acfg->image->name), MONO_SOLIB_EXT);
12469 #endif
12471 command = g_string_free (str, FALSE);
12472 #endif
12473 aot_printf (acfg, "Executing the native linker: %s\n", command);
12474 if (execute_system (command) != 0) {
12475 g_free (tmp_outfile_name);
12476 g_free (outfile_name);
12477 g_free (command);
12478 g_free (objfile);
12479 g_free (ld_flags);
12480 return 1;
12483 g_free (command);
12485 /*com = g_strdup_printf ("strip --strip-unneeded %s%s", acfg->image->name, MONO_SOLIB_EXT);
12486 printf ("Stripping the binary: %s\n", com);
12487 execute_system (com);
12488 g_free (com);*/
12490 #if defined(TARGET_ARM) && !defined(TARGET_MACH)
12492 * gas generates 'mapping symbols' each time code and data is mixed, which
12493 * happens a lot in emit_and_reloc_code (), so we need to get rid of them.
12495 command = g_strdup_printf ("\"%sstrip\" --strip-symbol=\\$a --strip-symbol=\\$d %s", tool_prefix, wrap_path(tmp_outfile_name));
12496 aot_printf (acfg, "Stripping the binary: %s\n", command);
12497 if (execute_system (command) != 0) {
12498 g_free (tmp_outfile_name);
12499 g_free (outfile_name);
12500 g_free (command);
12501 g_free (objfile);
12502 return 1;
12504 #endif
12506 if (0 != rename (tmp_outfile_name, outfile_name)) {
12507 if (G_FILE_ERROR_EXIST == g_file_error_from_errno (errno)) {
12508 /* Since we are rebuilding the module we need to be able to replace any old copies. Remove old file and retry rename operation. */
12509 unlink (outfile_name);
12510 rename (tmp_outfile_name, outfile_name);
12514 #if defined(TARGET_MACH)
12515 command = g_strdup_printf ("dsymutil \"%s\"", outfile_name);
12516 aot_printf (acfg, "Executing dsymutil: %s\n", command);
12517 if (execute_system (command) != 0) {
12518 return 1;
12520 #endif
12522 if (!acfg->aot_opts.save_temps)
12523 unlink (objfile);
12525 g_free (tmp_outfile_name);
12526 g_free (outfile_name);
12527 g_free (objfile);
12529 if (acfg->aot_opts.save_temps)
12530 aot_printf (acfg, "Retained input file.\n");
12531 else
12532 unlink (acfg->tmpfname);
12534 return 0;
12537 static guint8
12538 profread_byte (FILE *infile)
12540 guint8 i;
12541 int res;
12543 res = fread (&i, 1, 1, infile);
12544 g_assert (res == 1);
12545 return i;
12548 static int
12549 profread_int (FILE *infile)
12551 int i, res;
12553 res = fread (&i, 4, 1, infile);
12554 g_assert (res == 1);
12555 return i;
12558 static char*
12559 profread_string (FILE *infile)
12561 int len, res;
12562 char *pbuf;
12564 len = profread_int (infile);
12565 pbuf = (char*)g_malloc (len + 1);
12566 res = fread (pbuf, 1, len, infile);
12567 g_assert (res == len);
12568 pbuf [len] = '\0';
12569 return pbuf;
12572 static void
12573 load_profile_file (MonoAotCompile *acfg, char *filename)
12575 FILE *infile;
12576 char buf [1024];
12577 int res, len, version;
12578 char magic [32];
12580 infile = fopen (filename, "rb");
12581 if (!infile) {
12582 fprintf (stderr, "Unable to open file '%s': %s.\n", filename, strerror (errno));
12583 exit (1);
12586 printf ("Using profile data file '%s'\n", filename);
12588 sprintf (magic, AOT_PROFILER_MAGIC);
12589 len = strlen (magic);
12590 res = fread (buf, 1, len, infile);
12591 magic [len] = '\0';
12592 buf [len] = '\0';
12593 if ((res != len) || strcmp (buf, magic) != 0) {
12594 printf ("Profile file has wrong header: '%s'.\n", buf);
12595 fclose (infile);
12596 exit (1);
12598 guint32 expected_version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
12599 version = profread_int (infile);
12600 if (version != expected_version) {
12601 printf ("Profile file has wrong version 0x%4x, expected 0x%4x.\n", version, expected_version);
12602 fclose (infile);
12603 exit (1);
12606 ProfileData *data = g_new0 (ProfileData, 1);
12607 data->images = g_hash_table_new (NULL, NULL);
12608 data->classes = g_hash_table_new (NULL, NULL);
12609 data->ginsts = g_hash_table_new (NULL, NULL);
12610 data->methods = g_hash_table_new (NULL, NULL);
12612 while (TRUE) {
12613 int type = profread_byte (infile);
12614 int id = profread_int (infile);
12616 if (type == AOTPROF_RECORD_NONE)
12617 break;
12619 switch (type) {
12620 case AOTPROF_RECORD_IMAGE: {
12621 ImageProfileData *idata = g_new0 (ImageProfileData, 1);
12622 idata->name = profread_string (infile);
12623 char *mvid = profread_string (infile);
12624 g_free (mvid);
12625 g_hash_table_insert (data->images, GINT_TO_POINTER (id), idata);
12626 break;
12628 case AOTPROF_RECORD_GINST: {
12629 int i;
12630 int len = profread_int (infile);
12632 GInstProfileData *gdata = g_new0 (GInstProfileData, 1);
12633 gdata->argc = len;
12634 gdata->argv = g_new0 (ClassProfileData*, len);
12636 for (i = 0; i < len; ++i) {
12637 int class_id = profread_int (infile);
12639 gdata->argv [i] = (ClassProfileData*)g_hash_table_lookup (data->classes, GINT_TO_POINTER (class_id));
12640 g_assert (gdata->argv [i]);
12642 g_hash_table_insert (data->ginsts, GINT_TO_POINTER (id), gdata);
12643 break;
12645 case AOTPROF_RECORD_TYPE: {
12646 int type = profread_byte (infile);
12648 switch (type) {
12649 case MONO_TYPE_CLASS: {
12650 int image_id = profread_int (infile);
12651 int ginst_id = profread_int (infile);
12652 char *class_name = profread_string (infile);
12654 ImageProfileData *image = (ImageProfileData*)g_hash_table_lookup (data->images, GINT_TO_POINTER (image_id));
12655 g_assert (image);
12657 char *p = strrchr (class_name, '.');
12658 g_assert (p);
12659 *p = '\0';
12661 ClassProfileData *cdata = g_new0 (ClassProfileData, 1);
12662 cdata->image = image;
12663 cdata->ns = g_strdup (class_name);
12664 cdata->name = g_strdup (p + 1);
12666 if (ginst_id != -1) {
12667 cdata->inst = (GInstProfileData*)g_hash_table_lookup (data->ginsts, GINT_TO_POINTER (ginst_id));
12668 g_assert (cdata->inst);
12670 g_free (class_name);
12672 g_hash_table_insert (data->classes, GINT_TO_POINTER (id), cdata);
12673 break;
12675 #if 0
12676 case MONO_TYPE_SZARRAY: {
12677 int elem_id = profread_int (infile);
12678 // FIXME:
12679 break;
12681 #endif
12682 default:
12683 g_assert_not_reached ();
12684 break;
12686 break;
12688 case AOTPROF_RECORD_METHOD: {
12689 int class_id = profread_int (infile);
12690 int ginst_id = profread_int (infile);
12691 int param_count = profread_int (infile);
12692 char *method_name = profread_string (infile);
12693 char *sig = profread_string (infile);
12695 ClassProfileData *klass = (ClassProfileData*)g_hash_table_lookup (data->classes, GINT_TO_POINTER (class_id));
12696 g_assert (klass);
12698 MethodProfileData *mdata = g_new0 (MethodProfileData, 1);
12699 mdata->id = id;
12700 mdata->klass = klass;
12701 mdata->name = method_name;
12702 mdata->signature = sig;
12703 mdata->param_count = param_count;
12705 if (ginst_id != -1) {
12706 mdata->inst = (GInstProfileData*)g_hash_table_lookup (data->ginsts, GINT_TO_POINTER (ginst_id));
12707 g_assert (mdata->inst);
12709 g_hash_table_insert (data->methods, GINT_TO_POINTER (id), mdata);
12710 break;
12712 default:
12713 printf ("%d\n", type);
12714 g_assert_not_reached ();
12715 break;
12719 fclose (infile);
12720 acfg->profile_data = g_list_append (acfg->profile_data, data);
12723 static void
12724 resolve_class (ClassProfileData *cdata);
12726 static void
12727 resolve_ginst (GInstProfileData *inst_data)
12729 int i;
12731 if (inst_data->inst)
12732 return;
12734 for (i = 0; i < inst_data->argc; ++i) {
12735 resolve_class (inst_data->argv [i]);
12736 if (!inst_data->argv [i]->klass)
12737 return;
12739 MonoType **args = g_new0 (MonoType*, inst_data->argc);
12740 for (i = 0; i < inst_data->argc; ++i)
12741 args [i] = m_class_get_byval_arg (inst_data->argv [i]->klass);
12743 inst_data->inst = mono_metadata_get_generic_inst (inst_data->argc, args);
12746 static void
12747 resolve_class (ClassProfileData *cdata)
12749 ERROR_DECL (error);
12750 MonoClass *klass;
12752 if (!cdata->image->image)
12753 return;
12755 klass = mono_class_from_name_checked (cdata->image->image, cdata->ns, cdata->name, error);
12756 if (!klass) {
12757 //printf ("[%s] %s.%s\n", cdata->image->name, cdata->ns, cdata->name);
12758 return;
12760 if (cdata->inst) {
12761 resolve_ginst (cdata->inst);
12762 if (!cdata->inst->inst)
12763 return;
12764 MonoGenericContext ctx;
12766 memset (&ctx, 0, sizeof (ctx));
12767 ctx.class_inst = cdata->inst->inst;
12768 cdata->klass = mono_class_inflate_generic_class_checked (klass, &ctx, error);
12769 } else {
12770 cdata->klass = klass;
12775 * Resolve the profile data to the corresponding loaded classes/methods etc. if possible.
12777 static void
12778 resolve_profile_data (MonoAotCompile *acfg, ProfileData *data, MonoAssembly* current)
12780 GHashTableIter iter;
12781 gpointer key, value;
12782 int i;
12784 if (!data)
12785 return;
12787 /* Images */
12788 GPtrArray *assemblies = mono_domain_get_assemblies (mono_get_root_domain (), FALSE);
12789 g_hash_table_iter_init (&iter, data->images);
12790 while (g_hash_table_iter_next (&iter, &key, &value)) {
12791 ImageProfileData *idata = (ImageProfileData*)value;
12793 if (!strcmp (current->aname.name, idata->name)) {
12794 idata->image = current->image;
12795 break;
12798 for (i = 0; i < assemblies->len; ++i) {
12799 MonoAssembly *ass = (MonoAssembly*)g_ptr_array_index (assemblies, i);
12801 if (!strcmp (ass->aname.name, idata->name)) {
12802 idata->image = ass->image;
12803 break;
12807 g_ptr_array_free (assemblies, TRUE);
12809 /* Classes */
12810 g_hash_table_iter_init (&iter, data->classes);
12811 while (g_hash_table_iter_next (&iter, &key, &value)) {
12812 ClassProfileData *cdata = (ClassProfileData*)value;
12814 if (!cdata->image->image) {
12815 if (acfg->aot_opts.verbose)
12816 printf ("Unable to load class '%s.%s' because its image '%s' is not loaded.\n", cdata->ns, cdata->name, cdata->image->name);
12817 continue;
12820 resolve_class (cdata);
12822 if (cdata->klass)
12823 printf ("%s %s %s\n", cdata->ns, cdata->name, mono_class_full_name (cdata->klass));
12827 /* Methods */
12828 g_hash_table_iter_init (&iter, data->methods);
12829 while (g_hash_table_iter_next (&iter, &key, &value)) {
12830 MethodProfileData *mdata = (MethodProfileData*)value;
12831 MonoClass *klass;
12832 MonoMethod *m;
12833 gpointer miter;
12835 resolve_class (mdata->klass);
12836 klass = mdata->klass->klass;
12837 if (!klass) {
12838 if (acfg->aot_opts.verbose)
12839 printf ("Unable to load method '%s' because its class '%s.%s' is not loaded.\n", mdata->name, mdata->klass->ns, mdata->klass->name);
12840 continue;
12842 miter = NULL;
12843 while ((m = mono_class_get_methods (klass, &miter))) {
12844 ERROR_DECL (error);
12846 if (strcmp (m->name, mdata->name))
12847 continue;
12848 MonoMethodSignature *sig = mono_method_signature_internal (m);
12849 if (!sig)
12850 continue;
12851 if (sig->param_count != mdata->param_count)
12852 continue;
12853 if (mdata->inst) {
12854 resolve_ginst (mdata->inst);
12855 if (!mdata->inst->inst)
12856 continue;
12857 MonoGenericContext ctx;
12859 memset (&ctx, 0, sizeof (ctx));
12860 ctx.method_inst = mdata->inst->inst;
12862 m = mono_class_inflate_generic_method_checked (m, &ctx, error);
12863 if (!m)
12864 continue;
12865 sig = mono_method_signature_checked (m, error);
12866 if (!is_ok (error)) {
12867 mono_error_cleanup (error);
12868 continue;
12871 char *sig_str = mono_signature_full_name (sig);
12872 gboolean match = !strcmp (sig_str, mdata->signature);
12873 g_free (sig_str);
12874 if (!match)
12876 continue;
12877 //printf ("%s\n", mono_method_full_name (m, 1));
12878 mdata->method = m;
12879 break;
12881 if (!mdata->method) {
12882 if (acfg->aot_opts.verbose)
12883 printf ("Unable to load method '%s' from class '%s', not found.\n", mdata->name, mono_class_full_name (klass));
12888 static gboolean
12889 inst_references_image (MonoGenericInst *inst, MonoImage *image)
12891 int i;
12893 for (i = 0; i < inst->type_argc; ++i) {
12894 MonoClass *k = mono_class_from_mono_type_internal (inst->type_argv [i]);
12895 if (m_class_get_image (k) == image)
12896 return TRUE;
12897 if (mono_class_is_ginst (k)) {
12898 MonoGenericInst *kinst = mono_class_get_context (k)->class_inst;
12899 if (inst_references_image (kinst, image))
12900 return TRUE;
12903 return FALSE;
12906 static gboolean
12907 is_local_inst (MonoGenericInst *inst, MonoImage *image)
12909 int i;
12911 for (i = 0; i < inst->type_argc; ++i) {
12912 MonoClass *k = mono_class_from_mono_type_internal (inst->type_argv [i]);
12913 if (!MONO_TYPE_IS_PRIMITIVE (inst->type_argv [i]) && m_class_get_image (k) != image)
12914 return FALSE;
12916 return TRUE;
12919 static void
12920 add_profile_instances (MonoAotCompile *acfg, ProfileData *data)
12922 GHashTableIter iter;
12923 gpointer key, value;
12924 int count = 0;
12926 if (!data)
12927 return;
12929 if (acfg->aot_opts.profile_only) {
12930 /* Add methods referenced by the profile */
12931 g_hash_table_iter_init (&iter, data->methods);
12932 while (g_hash_table_iter_next (&iter, &key, &value)) {
12933 MethodProfileData *mdata = (MethodProfileData*)value;
12934 MonoMethod *m = mdata->method;
12936 if (!m)
12937 continue;
12938 if (m->is_inflated)
12939 continue;
12940 add_extra_method (acfg, m);
12941 g_hash_table_insert (acfg->profile_methods, m, m);
12942 count ++;
12947 * Add method instances 'related' to this assembly to the AOT image.
12949 g_hash_table_iter_init (&iter, data->methods);
12950 while (g_hash_table_iter_next (&iter, &key, &value)) {
12951 MethodProfileData *mdata = (MethodProfileData*)value;
12952 MonoMethod *m = mdata->method;
12953 MonoGenericContext *ctx;
12955 if (!m)
12956 continue;
12957 if (!m->is_inflated)
12958 continue;
12960 ctx = mono_method_get_context (m);
12961 /* For simplicity, add instances which reference the assembly we are compiling */
12962 if (((ctx->class_inst && inst_references_image (ctx->class_inst, acfg->image)) ||
12963 (ctx->method_inst && inst_references_image (ctx->method_inst, acfg->image))) &&
12964 !mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) {
12965 //printf ("%s\n", mono_method_full_name (m, TRUE));
12966 add_extra_method (acfg, m);
12967 count ++;
12968 } else if (m_class_get_image (m->klass) == acfg->image &&
12969 ((ctx->class_inst && is_local_inst (ctx->class_inst, acfg->image)) ||
12970 (ctx->method_inst && is_local_inst (ctx->method_inst, acfg->image))) &&
12971 !mono_method_is_generic_sharable_full (m, FALSE, FALSE, FALSE)) {
12972 /* Add instances where the gtd is in the assembly and its inflated with types from this assembly or corlib */
12973 //printf ("%s\n", mono_method_full_name (m, TRUE));
12974 add_extra_method (acfg, m);
12975 count ++;
12978 * FIXME: We might skip some instances, for example:
12979 * Foo<Bar> won't be compiled when compiling Foo's assembly since it doesn't match the first case,
12980 * and it won't be compiled when compiling Bar's assembly if Foo's assembly is not loaded.
12984 printf ("Added %d methods from profile.\n", count);
12987 static void
12988 init_got_info (GotInfo *info)
12990 int i;
12992 info->patch_to_got_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal);
12993 info->patch_to_got_offset_by_type = g_new0 (GHashTable*, MONO_PATCH_INFO_NUM);
12994 for (i = 0; i < MONO_PATCH_INFO_NUM; ++i)
12995 info->patch_to_got_offset_by_type [i] = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal);
12996 info->got_patches = g_ptr_array_new ();
12999 static MonoAotCompile*
13000 acfg_create (MonoAssembly *ass, guint32 jit_opts)
13002 MonoImage *image = ass->image;
13003 MonoAotCompile *acfg;
13005 acfg = g_new0 (MonoAotCompile, 1);
13006 acfg->methods = g_ptr_array_new ();
13007 acfg->method_indexes = g_hash_table_new (NULL, NULL);
13008 acfg->method_depth = g_hash_table_new (NULL, NULL);
13009 acfg->plt_offset_to_entry = g_hash_table_new (NULL, NULL);
13010 acfg->patch_to_plt_entry = g_new0 (GHashTable*, MONO_PATCH_INFO_NUM);
13011 acfg->method_to_cfg = g_hash_table_new (NULL, NULL);
13012 acfg->token_info_hash = g_hash_table_new_full (NULL, NULL, NULL, NULL);
13013 acfg->method_to_pinvoke_import = g_hash_table_new_full (NULL, NULL, NULL, g_free);
13014 acfg->method_to_external_icall_symbol_name = g_hash_table_new_full (NULL, NULL, NULL, g_free);
13015 acfg->image_hash = g_hash_table_new (NULL, NULL);
13016 acfg->image_table = g_ptr_array_new ();
13017 acfg->globals = g_ptr_array_new ();
13018 acfg->image = image;
13019 acfg->jit_opts = jit_opts;
13020 acfg->mempool = mono_mempool_new ();
13021 acfg->extra_methods = g_ptr_array_new ();
13022 acfg->unwind_info_offsets = g_hash_table_new (NULL, NULL);
13023 acfg->unwind_ops = g_ptr_array_new ();
13024 acfg->method_label_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
13025 acfg->method_order = g_ptr_array_new ();
13026 acfg->export_names = g_hash_table_new (NULL, NULL);
13027 acfg->klass_blob_hash = g_hash_table_new (NULL, NULL);
13028 acfg->method_blob_hash = g_hash_table_new (NULL, NULL);
13029 acfg->ginst_blob_hash = g_hash_table_new (mono_metadata_generic_inst_hash, mono_metadata_generic_inst_equal);
13030 acfg->plt_entry_debug_sym_cache = g_hash_table_new (g_str_hash, g_str_equal);
13031 acfg->gsharedvt_in_signatures = g_hash_table_new ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal);
13032 acfg->gsharedvt_out_signatures = g_hash_table_new ((GHashFunc)mono_signature_hash, (GEqualFunc)mono_metadata_signature_equal);
13033 acfg->profile_methods = g_hash_table_new (NULL, NULL);
13034 mono_os_mutex_init_recursive (&acfg->mutex);
13036 init_got_info (&acfg->got_info);
13037 init_got_info (&acfg->llvm_got_info);
13039 method_to_external_icall_symbol_name = acfg->method_to_external_icall_symbol_name;
13040 return acfg;
13043 static void
13044 got_info_free (GotInfo *info)
13046 int i;
13048 for (i = 0; i < MONO_PATCH_INFO_NUM; ++i)
13049 g_hash_table_destroy (info->patch_to_got_offset_by_type [i]);
13050 g_free (info->patch_to_got_offset_by_type);
13051 g_hash_table_destroy (info->patch_to_got_offset);
13052 g_ptr_array_free (info->got_patches, TRUE);
13055 static void
13056 acfg_free (MonoAotCompile *acfg)
13058 int i;
13060 mono_img_writer_destroy (acfg->w);
13061 for (i = 0; i < acfg->nmethods; ++i)
13062 if (acfg->cfgs [i])
13063 mono_destroy_compile (acfg->cfgs [i]);
13065 g_free (acfg->cfgs);
13067 g_free (acfg->static_linking_symbol);
13068 g_free (acfg->got_symbol);
13069 g_free (acfg->plt_symbol);
13070 g_ptr_array_free (acfg->methods, TRUE);
13071 g_ptr_array_free (acfg->image_table, TRUE);
13072 g_ptr_array_free (acfg->globals, TRUE);
13073 g_ptr_array_free (acfg->unwind_ops, TRUE);
13074 g_hash_table_destroy (acfg->method_indexes);
13075 g_hash_table_destroy (acfg->method_depth);
13076 g_hash_table_destroy (acfg->plt_offset_to_entry);
13077 for (i = 0; i < MONO_PATCH_INFO_NUM; ++i)
13078 g_hash_table_destroy (acfg->patch_to_plt_entry [i]);
13079 g_free (acfg->patch_to_plt_entry);
13080 g_hash_table_destroy (acfg->method_to_cfg);
13081 g_hash_table_destroy (acfg->token_info_hash);
13082 g_hash_table_destroy (acfg->method_to_pinvoke_import);
13083 g_hash_table_destroy (acfg->method_to_external_icall_symbol_name);
13084 g_hash_table_destroy (acfg->image_hash);
13085 g_hash_table_destroy (acfg->unwind_info_offsets);
13086 g_hash_table_destroy (acfg->method_label_hash);
13087 g_hash_table_destroy (acfg->typespec_classes);
13088 g_hash_table_destroy (acfg->export_names);
13089 g_hash_table_destroy (acfg->plt_entry_debug_sym_cache);
13090 g_hash_table_destroy (acfg->klass_blob_hash);
13091 g_hash_table_destroy (acfg->method_blob_hash);
13092 got_info_free (&acfg->got_info);
13093 got_info_free (&acfg->llvm_got_info);
13094 arch_free_unwind_info_section_cache (acfg);
13095 mono_mempool_destroy (acfg->mempool);
13097 method_to_external_icall_symbol_name = NULL;
13098 g_free (acfg);
13101 #define WRAPPER(e,n) n,
13102 static const char* const
13103 wrapper_type_names [MONO_WRAPPER_NUM + 1] = {
13104 #include "mono/metadata/wrapper-types.h"
13105 NULL
13108 static G_GNUC_UNUSED const char*
13109 get_wrapper_type_name (int type)
13111 return wrapper_type_names [type];
13114 //#define DUMP_PLT
13115 //#define DUMP_GOT
13117 static void aot_dump (MonoAotCompile *acfg)
13119 FILE *dumpfile;
13120 char * dumpname;
13122 JsonWriter writer;
13123 mono_json_writer_init (&writer);
13125 mono_json_writer_object_begin(&writer);
13127 // Methods
13128 mono_json_writer_indent (&writer);
13129 mono_json_writer_object_key(&writer, "methods");
13130 mono_json_writer_array_begin (&writer);
13132 int i;
13133 for (i = 0; i < acfg->nmethods; ++i) {
13134 MonoCompile *cfg;
13135 MonoMethod *method;
13136 MonoClass *klass;
13138 cfg = acfg->cfgs [i];
13139 if (ignore_cfg (cfg))
13140 continue;
13142 method = cfg->orig_method;
13144 mono_json_writer_indent (&writer);
13145 mono_json_writer_object_begin(&writer);
13147 mono_json_writer_indent (&writer);
13148 mono_json_writer_object_key(&writer, "name");
13149 mono_json_writer_printf (&writer, "\"%s\",\n", method->name);
13151 mono_json_writer_indent (&writer);
13152 mono_json_writer_object_key(&writer, "signature");
13153 mono_json_writer_printf (&writer, "\"%s\",\n", mono_method_get_full_name (method));
13155 mono_json_writer_indent (&writer);
13156 mono_json_writer_object_key(&writer, "code_size");
13157 mono_json_writer_printf (&writer, "\"%d\",\n", cfg->code_size);
13159 klass = method->klass;
13161 mono_json_writer_indent (&writer);
13162 mono_json_writer_object_key(&writer, "class");
13163 mono_json_writer_printf (&writer, "\"%s\",\n", m_class_get_name (klass));
13165 mono_json_writer_indent (&writer);
13166 mono_json_writer_object_key(&writer, "namespace");
13167 mono_json_writer_printf (&writer, "\"%s\",\n", m_class_get_name_space (klass));
13169 mono_json_writer_indent (&writer);
13170 mono_json_writer_object_key(&writer, "wrapper_type");
13171 mono_json_writer_printf (&writer, "\"%s\",\n", get_wrapper_type_name(method->wrapper_type));
13173 mono_json_writer_indent_pop (&writer);
13174 mono_json_writer_indent (&writer);
13175 mono_json_writer_object_end (&writer);
13176 mono_json_writer_printf (&writer, ",\n");
13179 mono_json_writer_indent_pop (&writer);
13180 mono_json_writer_indent (&writer);
13181 mono_json_writer_array_end (&writer);
13182 mono_json_writer_printf (&writer, ",\n");
13184 // PLT entries
13185 #ifdef DUMP_PLT
13186 mono_json_writer_indent_push (&writer);
13187 mono_json_writer_indent (&writer);
13188 mono_json_writer_object_key(&writer, "plt");
13189 mono_json_writer_array_begin (&writer);
13191 for (i = 0; i < acfg->plt_offset; ++i) {
13192 MonoPltEntry *plt_entry = NULL;
13193 MonoJumpInfo *ji;
13195 if (i == 0)
13197 * The first plt entry is unused.
13199 continue;
13201 plt_entry = g_hash_table_lookup (acfg->plt_offset_to_entry, GUINT_TO_POINTER (i));
13202 ji = plt_entry->ji;
13204 mono_json_writer_indent (&writer);
13205 mono_json_writer_printf (&writer, "{ ");
13206 mono_json_writer_object_key(&writer, "symbol");
13207 mono_json_writer_printf (&writer, "\"%s\" },\n", plt_entry->symbol);
13210 mono_json_writer_indent_pop (&writer);
13211 mono_json_writer_indent (&writer);
13212 mono_json_writer_array_end (&writer);
13213 mono_json_writer_printf (&writer, ",\n");
13214 #endif
13216 // GOT entries
13217 #ifdef DUMP_GOT
13218 mono_json_writer_indent_push (&writer);
13219 mono_json_writer_indent (&writer);
13220 mono_json_writer_object_key(&writer, "got");
13221 mono_json_writer_array_begin (&writer);
13223 mono_json_writer_indent_push (&writer);
13224 for (i = 0; i < acfg->got_info.got_patches->len; ++i) {
13225 MonoJumpInfo *ji = g_ptr_array_index (acfg->got_info.got_patches, i);
13227 mono_json_writer_indent (&writer);
13228 mono_json_writer_printf (&writer, "{ ");
13229 mono_json_writer_object_key(&writer, "patch_name");
13230 mono_json_writer_printf (&writer, "\"%s\" },\n", get_patch_name (ji->type));
13233 mono_json_writer_indent_pop (&writer);
13234 mono_json_writer_indent (&writer);
13235 mono_json_writer_array_end (&writer);
13236 mono_json_writer_printf (&writer, ",\n");
13237 #endif
13239 mono_json_writer_indent_pop (&writer);
13240 mono_json_writer_indent (&writer);
13241 mono_json_writer_object_end (&writer);
13243 dumpname = g_strdup_printf ("%s.json", g_path_get_basename (acfg->image->name));
13244 dumpfile = fopen (dumpname, "w+");
13245 g_free (dumpname);
13247 fprintf (dumpfile, "%s", writer.text->str);
13248 fclose (dumpfile);
13250 mono_json_writer_destroy (&writer);
13253 static const MonoJitICallId preinited_jit_icalls [] = {
13254 MONO_JIT_ICALL_mini_llvm_init_method,
13255 MONO_JIT_ICALL_mini_llvmonly_throw_nullref_exception,
13256 MONO_JIT_ICALL_mono_llvm_throw_corlib_exception,
13257 MONO_JIT_ICALL_mono_threads_state_poll,
13258 MONO_JIT_ICALL_mini_llvmonly_init_vtable_slot,
13259 MONO_JIT_ICALL_mono_helper_ldstr_mscorlib,
13260 MONO_JIT_ICALL_mono_fill_method_rgctx,
13261 MONO_JIT_ICALL_mono_fill_class_rgctx
13264 static void
13265 add_preinit_slot (MonoAotCompile *acfg, MonoJumpInfo *ji)
13267 if (!acfg->aot_opts.llvm_only)
13268 get_got_offset (acfg, FALSE, ji);
13269 get_got_offset (acfg, TRUE, ji);
13272 static void
13273 add_preinit_got_slots (MonoAotCompile *acfg)
13275 MonoJumpInfo *ji;
13276 int i;
13279 * Allocate the first few GOT entries to information which is needed frequently, or it is needed
13280 * during method initialization etc.
13282 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13283 ji->type = MONO_PATCH_INFO_IMAGE;
13284 ji->data.image = acfg->image;
13285 add_preinit_slot (acfg, ji);
13287 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13288 ji->type = MONO_PATCH_INFO_MSCORLIB_GOT_ADDR;
13289 add_preinit_slot (acfg, ji);
13291 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13292 ji->type = MONO_PATCH_INFO_GC_CARD_TABLE_ADDR;
13293 add_preinit_slot (acfg, ji);
13295 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13296 ji->type = MONO_PATCH_INFO_GC_NURSERY_START;
13297 add_preinit_slot (acfg, ji);
13299 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13300 ji->type = MONO_PATCH_INFO_AOT_MODULE;
13301 add_preinit_slot (acfg, ji);
13303 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13304 ji->type = MONO_PATCH_INFO_GC_NURSERY_BITS;
13305 add_preinit_slot (acfg, ji);
13307 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13308 ji->type = MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG;
13309 add_preinit_slot (acfg, ji);
13311 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13312 ji->type = MONO_PATCH_INFO_GC_SAFE_POINT_FLAG;
13313 add_preinit_slot (acfg, ji);
13315 if (!acfg->aot_opts.llvm_only) {
13316 for (i = 0; i < TLS_KEY_NUM; i++) {
13317 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13318 ji->type = MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL;
13319 ji->data.jit_icall_id = mono_get_tls_key_to_jit_icall_id (i);
13320 add_preinit_slot (acfg, ji);
13324 /* Called by native-to-managed wrappers on possibly unattached threads */
13325 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13326 ji->type = MONO_PATCH_INFO_JIT_ICALL_ADDR_NOCALL;
13327 ji->data.jit_icall_id = MONO_JIT_ICALL_mono_threads_attach_coop;
13328 add_preinit_slot (acfg, ji);
13330 for (i = 0; i < G_N_ELEMENTS (preinited_jit_icalls); ++i) {
13331 ji = (MonoJumpInfo *)mono_mempool_alloc0 (acfg->mempool, sizeof (MonoJumpInfo));
13332 ji->type = MONO_PATCH_INFO_JIT_ICALL_ID;
13333 ji->data.jit_icall_id = preinited_jit_icalls [i];
13334 add_preinit_slot (acfg, ji);
13337 if (acfg->aot_opts.llvm_only)
13338 acfg->nshared_got_entries = acfg->llvm_got_offset;
13339 else
13340 acfg->nshared_got_entries = acfg->got_offset;
13341 g_assert (acfg->nshared_got_entries);
13344 static void
13345 mono_dedup_log_stats (MonoAotCompile *acfg)
13347 GHashTableIter iter;
13348 g_assert (acfg->dedup_stats);
13350 if (!acfg->dedup_emit_mode)
13351 return;
13353 // If dedup_emit_mode, acfg is the dummy dedup module that consolidates
13354 // deduped modules
13355 g_hash_table_iter_init (&iter, acfg->method_to_cfg);
13356 MonoCompile *dcfg = NULL;
13357 MonoMethod *method = NULL;
13359 size_t wrappers_size_saved = 0;
13360 size_t inflated_size_saved = 0;
13361 size_t copied_singles = 0;
13362 int wrappers_saved = 0;
13363 int instances_saved = 0;
13364 int copied = 0;
13366 while (g_hash_table_iter_next (&iter, (gpointer *) &method, (gpointer *)&dcfg)) {
13367 gchar *dedup_name = mono_aot_get_mangled_method_name (method);
13368 guint count = GPOINTER_TO_UINT(g_hash_table_lookup (acfg->dedup_stats, dedup_name));
13370 if (count == 0)
13371 continue;
13373 if (acfg->dedup_emit_mode) {
13374 // Size *saved* is the size due to things not emitted.
13375 if (count < 2) {
13376 // Just moved, didn't save space / dedup
13377 copied ++;
13378 copied_singles += dcfg->code_len;
13379 } else if (method->wrapper_type != MONO_WRAPPER_NONE) {
13380 wrappers_saved ++;
13381 wrappers_size_saved += dcfg->code_len * (count - 1);
13382 } else {
13383 instances_saved ++;
13384 inflated_size_saved += dcfg->code_len * (count - 1);
13387 if (acfg->aot_opts.dedup) {
13388 if (method->wrapper_type != MONO_WRAPPER_NONE) {
13389 wrappers_saved ++;
13390 wrappers_size_saved += dcfg->code_len * count;
13391 } else {
13392 instances_saved ++;
13393 inflated_size_saved += dcfg->code_len * count;
13398 aot_printf (acfg, "Dedup Pass: Size Saved From Deduped Wrappers:\t%d methods, %" G_GSIZE_FORMAT "u bytes\n", wrappers_saved, wrappers_size_saved);
13399 aot_printf (acfg, "Dedup Pass: Size Saved From Inflated Methods:\t%d methods, %" G_GSIZE_FORMAT "u bytes\n", instances_saved, inflated_size_saved);
13400 if (acfg->dedup_emit_mode)
13401 aot_printf (acfg, "Dedup Pass: Size of Moved But Not Deduped (only 1 copy) Methods:\t%d methods, %" G_GSIZE_FORMAT "u bytes\n", copied, copied_singles);
13403 g_hash_table_destroy (acfg->dedup_stats);
13404 acfg->dedup_stats = NULL;
13407 // Flush the cache to tell future calls what to skip
13408 static void
13409 mono_flush_method_cache (MonoAotCompile *acfg)
13411 GHashTable *method_cache = acfg->dedup_cache;
13412 char *filename = g_strdup_printf ("%s.dedup", acfg->image->name);
13413 if (!acfg->dedup_cache_changed || !acfg->aot_opts.dedup) {
13414 g_free (filename);
13415 return;
13418 acfg->dedup_cache = NULL;
13420 FILE *cache = fopen (filename, "w");
13422 if (!cache)
13423 g_error ("Could not create cache at %s because of error: %s\n", filename, strerror (errno));
13425 GHashTableIter iter;
13426 gchar *name = NULL;
13427 g_hash_table_iter_init (&iter, method_cache);
13428 gboolean cont = TRUE;
13429 while (cont && g_hash_table_iter_next (&iter, (gpointer *) &name, NULL)) {
13430 int res = fprintf (cache, "%s\n", name);
13431 cont = res >= 0;
13433 // FIXME: don't assert if error when flushing
13434 g_assert (cont);
13436 fclose (cache);
13437 g_free (filename);
13439 // The keys are all in the imageset, nothing to free
13440 // Values are just pointers to memory owned elsewhere, or sentinels
13441 g_hash_table_destroy (method_cache);
13444 // Read in what has been emitted by previous invocations,
13445 // what can be skipped
13446 static void
13447 mono_read_method_cache (MonoAotCompile *acfg)
13449 char *filename = g_strdup_printf ("%s.dedup", acfg->image->name);
13450 // Only do once, when dedup_cache is null
13451 if (acfg->dedup_cache)
13452 goto early_exit;
13454 if (acfg->aot_opts.dedup_include || acfg->aot_opts.dedup)
13455 g_assert (acfg->dedup_stats);
13457 // only in skip mode
13458 if (!acfg->aot_opts.dedup)
13459 goto early_exit;
13461 g_assert (acfg->dedup_cache);
13463 FILE *cache;
13464 cache = fopen (filename, "r");
13465 if (!cache)
13466 goto early_exit;
13468 // Since we do pointer comparisons, and it can't be allocated at
13469 // the address 0x1 due to alignment, we use this as a sentinel
13470 gpointer other_acfg_sentinel;
13471 other_acfg_sentinel = GINT_TO_POINTER (0x1);
13473 if (fseek (cache, 0L, SEEK_END))
13474 goto cleanup;
13476 size_t fileLength;
13477 fileLength = ftell (cache);
13478 g_assert (fileLength > 0);
13480 if (fseek (cache, 0L, SEEK_SET))
13481 goto cleanup;
13483 // Avoid thousands of new malloc entries
13484 // FIXME: allocate into imageset, so we don't need to free.
13485 // put the other mangled names there too.
13486 char *bulk;
13487 bulk = g_malloc0 (fileLength * sizeof (char));
13488 size_t offset;
13489 offset = 0;
13491 while (fgets (&bulk [offset], fileLength - offset, cache)) {
13492 // strip newline
13493 char *line = &bulk [offset];
13494 size_t len = strlen (line);
13495 if (len == 0)
13496 break;
13498 if (len >= 0 && line [len] == '\n')
13499 line [len] = '\0';
13500 offset += strlen (line) + 1;
13501 g_assert (fileLength >= offset);
13503 g_hash_table_insert (acfg->dedup_cache, line, other_acfg_sentinel);
13506 cleanup:
13507 fclose (cache);
13509 early_exit:
13510 g_free (filename);
13511 return;
13514 typedef struct {
13515 GHashTable *cache;
13516 GHashTable *stats;
13517 gboolean emit_inflated_methods;
13518 MonoAssembly *inflated_assembly;
13519 } MonoAotState;
13521 static MonoAotState *
13522 alloc_aot_state (void)
13524 MonoAotState *state = g_malloc (sizeof (MonoAotState));
13525 // FIXME: Should this own the memory?
13526 state->cache = g_hash_table_new (g_str_hash, g_str_equal);
13527 state->stats = g_hash_table_new (g_str_hash, g_str_equal);
13528 // Start in "collect mode"
13529 state->emit_inflated_methods = FALSE;
13530 state->inflated_assembly = NULL;
13531 return state;
13534 static void
13535 free_aot_state (MonoAotState *astate)
13537 g_hash_table_destroy (astate->cache);
13538 g_free (astate);
13541 static void
13542 mono_add_deferred_extra_methods (MonoAotCompile *acfg, MonoAotState *astate)
13544 GHashTableIter iter;
13545 gchar *name = NULL;
13546 MonoMethod *method = NULL;
13548 acfg->dedup_emit_mode = TRUE;
13550 g_hash_table_iter_init (&iter, astate->cache);
13551 while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &method)) {
13552 add_method_full (acfg, method, TRUE, 0);
13554 return;
13557 static void
13558 mono_setup_dedup_state (MonoAotCompile *acfg, MonoAotState **global_aot_state, MonoAssembly *ass, MonoAotState **astate, gboolean *is_dedup_dummy)
13560 if (!acfg->aot_opts.dedup_include && !acfg->aot_opts.dedup)
13561 return;
13563 if (global_aot_state && *global_aot_state && acfg->aot_opts.dedup_include) {
13564 // Thread the state through when making the inflate pass
13565 *astate = *global_aot_state;
13568 if (!*astate) {
13569 *astate = alloc_aot_state ();
13570 *global_aot_state = *astate;
13573 acfg->dedup_cache = (*astate)->cache;
13574 acfg->dedup_stats = (*astate)->stats;
13576 // fills out acfg->dedup_cache
13577 if (acfg->aot_opts.dedup)
13578 mono_read_method_cache (acfg);
13580 if (!(*astate)->inflated_assembly && acfg->aot_opts.dedup_include) {
13581 gchar **asm_path = g_strsplit (ass->image->name, G_DIR_SEPARATOR_S, 0);
13582 gchar *asm_file = NULL;
13584 // Get the last part of the path, the filename
13585 for (int i=0; asm_path [i] != NULL; i++)
13586 asm_file = asm_path [i];
13588 if (!strcmp (acfg->aot_opts.dedup_include, asm_file)) {
13589 // Save
13590 *is_dedup_dummy = TRUE;
13591 (*astate)->inflated_assembly = ass;
13593 g_strfreev (asm_path);
13594 } else if ((*astate)->inflated_assembly) {
13595 *is_dedup_dummy = (ass == (*astate)->inflated_assembly);
13599 int
13600 mono_compile_deferred_assemblies (guint32 opts, const char *aot_options, gpointer **aot_state)
13602 // create assembly, loop and add extra_methods
13603 // in add_generic_instances , rip out what's in that for loop
13604 // and apply that to this aot_state inside of mono_compile_assembly
13605 MonoAotState *astate;
13606 astate = *(MonoAotState **)aot_state;
13607 g_assert (astate);
13609 // FIXME: allow suffixes?
13610 if (!astate->inflated_assembly) {
13611 const char* inflate = strstr (aot_options, "dedup-inflate");
13612 if (!inflate)
13613 return 0;
13614 else
13615 g_error ("Error: mono was not given an assembly with the provided inflate name\n");
13618 // Switch modes
13619 astate->emit_inflated_methods = TRUE;
13621 int res = mono_compile_assembly (astate->inflated_assembly, opts, aot_options, aot_state);
13623 *aot_state = NULL;
13624 free_aot_state (astate);
13626 return res;
13629 #ifndef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
13630 static MonoMethodSignature * const * const interp_in_static_sigs [] = {
13631 &mono_icall_sig_bool_ptr_int32_ptrref,
13632 &mono_icall_sig_bool_ptr_ptrref,
13633 &mono_icall_sig_int32_int32_ptrref,
13634 &mono_icall_sig_int32_int32_ptr_ptrref,
13635 &mono_icall_sig_int32_ptr_int32_ptr,
13636 &mono_icall_sig_int32_ptr_int32_ptrref,
13637 &mono_icall_sig_int32_ptr_ptrref,
13638 &mono_icall_sig_object_object_ptr_ptr_ptr,
13639 &mono_icall_sig_object,
13640 &mono_icall_sig_ptr_int32_ptrref,
13641 &mono_icall_sig_ptr_ptr_int32_ptr_ptr_ptrref,
13642 &mono_icall_sig_ptr_ptr_int32_ptr_ptrref,
13643 &mono_icall_sig_ptr_ptr_int32_ptrref,
13644 &mono_icall_sig_ptr_ptr_ptr_int32_ptrref,
13645 &mono_icall_sig_ptr_ptr_ptr_ptrref_ptrref,
13646 &mono_icall_sig_ptr_ptr_ptr_ptr_ptrref,
13647 &mono_icall_sig_ptr_ptr_ptr_ptrref,
13648 &mono_icall_sig_ptr_ptr_ptrref,
13649 &mono_icall_sig_ptr_ptr_uint32_ptrref,
13650 &mono_icall_sig_ptr_uint32_ptrref,
13651 &mono_icall_sig_void_object_ptr_ptr_ptr,
13652 &mono_icall_sig_void_ptr_ptr_int32_ptr_ptrref_ptr_ptrref,
13653 &mono_icall_sig_void_ptr_ptr_int32_ptr_ptrref,
13654 &mono_icall_sig_void_ptr_ptr_ptrref,
13655 &mono_icall_sig_void_ptr_ptrref,
13656 &mono_icall_sig_void_ptr,
13657 &mono_icall_sig_void_int32_ptrref,
13658 &mono_icall_sig_void_uint32_ptrref,
13659 &mono_icall_sig_void,
13661 #else
13662 // Common signatures for which we use interp in wrappers even in fullaot + trampolines mode
13663 // for increased performance
13664 static MonoMethodSignature *const * const interp_in_static_common_sigs [] = {
13665 &mono_icall_sig_void,
13666 &mono_icall_sig_ptr,
13667 &mono_icall_sig_void_ptr,
13668 &mono_icall_sig_ptr_ptr,
13669 &mono_icall_sig_void_ptr_ptr,
13670 &mono_icall_sig_ptr_ptr_ptr,
13671 &mono_icall_sig_void_ptr_ptr_ptr,
13672 &mono_icall_sig_ptr_ptr_ptr_ptr,
13673 &mono_icall_sig_void_ptr_ptr_ptr_ptr,
13674 &mono_icall_sig_ptr_ptr_ptr_ptr_ptr,
13675 &mono_icall_sig_void_ptr_ptr_ptr_ptr_ptr,
13676 &mono_icall_sig_ptr_ptr_ptr_ptr_ptr_ptr,
13677 &mono_icall_sig_void_ptr_ptr_ptr_ptr_ptr_ptr,
13678 &mono_icall_sig_ptr_ptr_ptr_ptr_ptr_ptr_ptr,
13680 #endif
13682 static void
13683 add_interp_in_wrapper_for_sig (MonoAotCompile *acfg, MonoMethodSignature *sig)
13685 MonoMethod *wrapper;
13687 sig = mono_metadata_signature_dup_full (mono_get_corlib (), sig);
13688 sig->pinvoke = FALSE;
13689 wrapper = mini_get_interp_in_wrapper (sig);
13690 add_method (acfg, wrapper);
13694 mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options, gpointer **global_aot_state)
13696 MonoImage *image = ass->image;
13697 int res;
13698 MonoAotCompile *acfg;
13699 char *p;
13700 TV_DECLARE (atv);
13701 TV_DECLARE (btv);
13703 acfg = acfg_create (ass, opts);
13705 memset (&acfg->aot_opts, 0, sizeof (acfg->aot_opts));
13706 acfg->aot_opts.write_symbols = TRUE;
13707 acfg->aot_opts.ntrampolines = 4096;
13708 acfg->aot_opts.nrgctx_trampolines = 4096;
13709 acfg->aot_opts.nimt_trampolines = 512;
13710 acfg->aot_opts.nrgctx_fetch_trampolines = 128;
13711 acfg->aot_opts.ngsharedvt_arg_trampolines = 512;
13712 #ifdef MONO_ARCH_HAVE_FTNPTR_ARG_TRAMPOLINE
13713 acfg->aot_opts.nftnptr_arg_trampolines = 128;
13714 #endif
13715 acfg->aot_opts.nunbox_arbitrary_trampolines = 256;
13716 if (!acfg->aot_opts.llvm_path)
13717 acfg->aot_opts.llvm_path = g_strdup ("");
13718 acfg->aot_opts.temp_path = g_strdup ("");
13719 #ifdef MONOTOUCH
13720 acfg->aot_opts.use_trampolines_page = TRUE;
13721 #endif
13722 acfg->aot_opts.clangxx = g_strdup ("clang++");
13724 mono_aot_parse_options (aot_options, &acfg->aot_opts);
13726 if (acfg->aot_opts.direct_extern_calls && !(acfg->aot_opts.llvm && acfg->aot_opts.static_link)) {
13727 aot_printerrf (acfg, "The 'direct-extern-calls' option requires the 'llvm' and 'static' options.\n");
13728 return 1;
13731 // start dedup
13732 MonoAotState *astate = NULL;
13733 gboolean is_dedup_dummy = FALSE;
13734 mono_setup_dedup_state (acfg, (MonoAotState **) global_aot_state, ass, &astate, &is_dedup_dummy);
13736 // Process later
13737 if (is_dedup_dummy && astate && !astate->emit_inflated_methods)
13738 return 0;
13740 if (acfg->aot_opts.dedup_include && !is_dedup_dummy)
13741 acfg->dedup_collect_only = TRUE;
13742 // end dedup
13744 if (acfg->aot_opts.logfile) {
13745 acfg->logfile = fopen (acfg->aot_opts.logfile, "a+");
13748 if (acfg->aot_opts.data_outfile) {
13749 acfg->data_outfile = fopen (acfg->aot_opts.data_outfile, "w+");
13750 if (!acfg->data_outfile) {
13751 aot_printerrf (acfg, "Unable to create file '%s': %s\n", acfg->aot_opts.data_outfile, strerror (errno));
13752 return 1;
13754 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_SEPARATE_DATA);
13757 //acfg->aot_opts.print_skipped_methods = TRUE;
13759 #if !defined(MONO_ARCH_GSHAREDVT_SUPPORTED)
13760 if (acfg->jit_opts & MONO_OPT_GSHAREDVT) {
13761 aot_printerrf (acfg, "-O=gsharedvt not supported on this platform.\n");
13762 return 1;
13764 if (acfg->aot_opts.llvm_only) {
13765 aot_printerrf (acfg, "--aot=llvmonly requires a runtime that supports gsharedvt.\n");
13766 return 1;
13768 #else
13769 if (acfg->aot_opts.llvm_only || mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts))
13770 acfg->jit_opts |= MONO_OPT_GSHAREDVT;
13771 #endif
13773 #if !defined(ENABLE_LLVM)
13774 if (acfg->aot_opts.llvm_only) {
13775 aot_printerrf (acfg, "--aot=llvmonly requires a runtime compiled with llvm support.\n");
13776 return 1;
13778 if (mono_use_llvm || acfg->aot_opts.llvm) {
13779 aot_printerrf (acfg, "--aot=llvm requires a runtime compiled with llvm support.\n");
13780 return 1;
13782 #endif
13784 if (acfg->jit_opts & MONO_OPT_GSHAREDVT)
13785 mono_set_generic_sharing_vt_supported (TRUE);
13787 if (!acfg->dedup_collect_only)
13788 aot_printf (acfg, "Mono Ahead of Time compiler - compiling assembly %s\n", image->name);
13790 if (!acfg->aot_opts.deterministic)
13791 generate_aotid ((guint8*) &acfg->image->aotid);
13793 char *aotid = mono_guid_to_string (acfg->image->aotid);
13794 if (!acfg->dedup_collect_only && !acfg->aot_opts.deterministic)
13795 aot_printf (acfg, "AOTID %s\n", aotid);
13796 g_free (aotid);
13798 #ifndef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
13799 if (mono_aot_mode_is_full (&acfg->aot_opts)) {
13800 aot_printerrf (acfg, "--aot=full is not supported on this platform.\n");
13801 return 1;
13803 #endif
13805 if (acfg->aot_opts.direct_pinvoke && !acfg->aot_opts.static_link) {
13806 aot_printerrf (acfg, "The 'direct-pinvoke' AOT option also requires the 'static' AOT option.\n");
13807 return 1;
13810 if (acfg->aot_opts.static_link)
13811 acfg->aot_opts.asm_writer = TRUE;
13813 if (acfg->aot_opts.soft_debug) {
13814 mini_debug_options.mdb_optimizations = TRUE;
13815 mini_debug_options.gen_sdb_seq_points = TRUE;
13817 if (!mono_debug_enabled ()) {
13818 aot_printerrf (acfg, "The soft-debug AOT option requires the --debug option.\n");
13819 return 1;
13821 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_DEBUG);
13824 if (acfg->aot_opts.try_llvm)
13825 acfg->aot_opts.llvm = mini_llvm_init ();
13827 if (mono_use_llvm || acfg->aot_opts.llvm) {
13828 acfg->llvm = TRUE;
13829 acfg->aot_opts.asm_writer = TRUE;
13830 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_WITH_LLVM);
13832 if (acfg->aot_opts.soft_debug) {
13833 aot_printerrf (acfg, "The 'soft-debug' option is not supported when compiling with LLVM.\n");
13834 return 1;
13837 mini_llvm_init ();
13839 if (acfg->aot_opts.asm_only && !acfg->aot_opts.llvm_outfile) {
13840 aot_printerrf (acfg, "Compiling with LLVM and the asm-only option requires the llvm-outfile= option.\n");
13841 return 1;
13845 if (mono_aot_mode_is_full (&acfg->aot_opts)) {
13846 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_FULL_AOT);
13847 acfg->is_full_aot = TRUE;
13850 if (mono_aot_mode_is_interp (&acfg->aot_opts)) {
13851 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_INTERP);
13852 acfg->is_full_aot = TRUE;
13855 if (mini_safepoints_enabled ())
13856 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_SAFEPOINTS);
13858 // The methods in dedup-emit amodules must be available on runtime startup
13859 // Note: Only one such amodule can have this attribute
13860 if (astate && astate->emit_inflated_methods)
13861 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_EAGER_LOAD);
13864 if (acfg->aot_opts.instances_logfile_path) {
13865 acfg->instances_logfile = fopen (acfg->aot_opts.instances_logfile_path, "w");
13866 if (!acfg->instances_logfile) {
13867 aot_printerrf (acfg, "Unable to create logfile: '%s'.\n", acfg->aot_opts.instances_logfile_path);
13868 return 1;
13872 if (acfg->aot_opts.profile_files) {
13873 GList *l;
13875 for (l = acfg->aot_opts.profile_files; l; l = l->next) {
13876 load_profile_file (acfg, (char*)l->data);
13880 if (!(mono_aot_mode_is_interp (&acfg->aot_opts) && !mono_aot_mode_is_full (&acfg->aot_opts))) {
13881 for (int method_index = 0; method_index < acfg->image->tables [MONO_TABLE_METHOD].rows; ++method_index)
13882 g_ptr_array_add (acfg->method_order,GUINT_TO_POINTER (method_index));
13885 acfg->num_trampolines [MONO_AOT_TRAMP_SPECIFIC] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.ntrampolines : 0;
13886 #ifdef MONO_ARCH_GSHARED_SUPPORTED
13887 acfg->num_trampolines [MONO_AOT_TRAMP_STATIC_RGCTX] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.nrgctx_trampolines : 0;
13888 #endif
13889 acfg->num_trampolines [MONO_AOT_TRAMP_IMT] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.nimt_trampolines : 0;
13890 #ifdef MONO_ARCH_GSHAREDVT_SUPPORTED
13891 if (acfg->jit_opts & MONO_OPT_GSHAREDVT)
13892 acfg->num_trampolines [MONO_AOT_TRAMP_GSHAREDVT_ARG] = mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.ngsharedvt_arg_trampolines : 0;
13893 #endif
13894 #ifdef MONO_ARCH_HAVE_FTNPTR_ARG_TRAMPOLINE
13895 acfg->num_trampolines [MONO_AOT_TRAMP_FTNPTR_ARG] = mono_aot_mode_is_interp (&acfg->aot_opts) ? acfg->aot_opts.nftnptr_arg_trampolines : 0;
13896 #endif
13897 acfg->num_trampolines [MONO_AOT_TRAMP_UNBOX_ARBITRARY] = mono_aot_mode_is_interp (&acfg->aot_opts) && mono_aot_mode_is_full (&acfg->aot_opts) ? acfg->aot_opts.nunbox_arbitrary_trampolines : 0;
13899 acfg->temp_prefix = mono_img_writer_get_temp_label_prefix (NULL);
13901 arch_init (acfg);
13903 if (mono_use_llvm || acfg->aot_opts.llvm) {
13905 * Emit all LLVM code into a separate assembly/object file and link with it
13906 * normally.
13908 if (!acfg->aot_opts.asm_only && acfg->llvm_owriter_supported) {
13909 acfg->llvm_owriter = TRUE;
13910 } else if (acfg->aot_opts.llvm_outfile) {
13911 int len = strlen (acfg->aot_opts.llvm_outfile);
13913 if (len >= 2 && acfg->aot_opts.llvm_outfile [len - 2] == '.' && acfg->aot_opts.llvm_outfile [len - 1] == 'o')
13914 acfg->llvm_owriter = TRUE;
13918 if (acfg->llvm && acfg->thumb_mixed)
13919 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_LLVM_THUMB);
13920 if (acfg->aot_opts.llvm_only)
13921 acfg->flags = (MonoAotFileFlags)(acfg->flags | MONO_AOT_FILE_FLAG_LLVM_ONLY);
13923 acfg->assembly_name_sym = g_strdup (acfg->image->assembly->aname.name);
13924 /* Get rid of characters which cannot occur in symbols */
13925 for (p = acfg->assembly_name_sym; *p; ++p) {
13926 if (!(isalnum (*p) || *p == '_'))
13927 *p = '_';
13930 acfg->global_prefix = g_strdup_printf ("mono_aot_%s", acfg->assembly_name_sym);
13931 acfg->plt_symbol = g_strdup_printf ("%s_plt", acfg->global_prefix);
13932 acfg->got_symbol = g_strdup_printf ("%s_got", acfg->global_prefix);
13933 if (acfg->llvm) {
13934 acfg->llvm_got_symbol = g_strdup_printf ("%s_llvm_got", acfg->global_prefix);
13935 acfg->llvm_eh_frame_symbol = g_strdup_printf ("%s_eh_frame", acfg->global_prefix);
13938 acfg->method_index = 1;
13940 if (mono_aot_mode_is_full (&acfg->aot_opts) || mono_aot_mode_is_hybrid (&acfg->aot_opts))
13941 mono_set_partial_sharing_supported (TRUE);
13943 if (!(mono_aot_mode_is_interp (&acfg->aot_opts) && !mono_aot_mode_is_full (&acfg->aot_opts))) {
13944 res = collect_methods (acfg);
13945 if (!res)
13946 return 1;
13949 // If we're emitting all of the inflated methods into a dummy
13950 // Assembly, then after extra_methods is set up, we're done
13951 // in this function.
13952 if (astate && astate->emit_inflated_methods)
13953 mono_add_deferred_extra_methods (acfg, astate);
13956 GList *l;
13958 for (l = acfg->profile_data; l; l = l->next)
13959 resolve_profile_data (acfg, (ProfileData*)l->data, ass);
13960 for (l = acfg->profile_data; l; l = l->next)
13961 add_profile_instances (acfg, (ProfileData*)l->data);
13964 acfg->cfgs_size = acfg->methods->len + 32;
13965 acfg->cfgs = g_new0 (MonoCompile*, acfg->cfgs_size);
13967 /* PLT offset 0 is reserved for the PLT trampoline */
13968 acfg->plt_offset = 1;
13969 add_preinit_got_slots (acfg);
13971 current_acfg = acfg;
13973 #ifdef ENABLE_LLVM
13974 if (acfg->llvm) {
13975 llvm_acfg = acfg;
13976 LLVMModuleFlags flags = (LLVMModuleFlags)0;
13977 #ifdef EMIT_DWARF_INFO
13978 flags = LLVM_MODULE_FLAG_DWARF;
13979 #endif
13980 #ifdef EMIT_WIN32_CODEVIEW_INFO
13981 flags = LLVM_MODULE_FLAG_CODEVIEW;
13982 #endif
13983 if (acfg->aot_opts.static_link)
13984 flags = (LLVMModuleFlags)(flags | LLVM_MODULE_FLAG_STATIC);
13985 if (acfg->aot_opts.llvm_only)
13986 flags = (LLVMModuleFlags)(flags | LLVM_MODULE_FLAG_LLVM_ONLY);
13987 if (acfg->aot_opts.interp)
13988 flags = (LLVMModuleFlags)(flags | LLVM_MODULE_FLAG_INTERP);
13989 mono_llvm_create_aot_module (acfg->image->assembly, acfg->global_prefix, acfg->nshared_got_entries, flags);
13991 add_lazy_init_wrappers (acfg);
13992 add_method (acfg, mono_marshal_get_llvm_func_wrapper (LLVM_FUNC_WRAPPER_GC_POLL));
13994 #endif
13996 if (mono_aot_mode_is_interp (&acfg->aot_opts) && mono_is_corlib_image (acfg->image->assembly->image)) {
13997 MonoMethod *wrapper = mini_get_interp_lmf_wrapper ("mono_interp_to_native_trampoline", (gpointer) mono_interp_to_native_trampoline);
13998 add_method (acfg, wrapper);
14000 wrapper = mini_get_interp_lmf_wrapper ("mono_interp_entry_from_trampoline", (gpointer) mono_interp_entry_from_trampoline);
14001 add_method (acfg, wrapper);
14003 #ifndef MONO_ARCH_HAVE_INTERP_ENTRY_TRAMPOLINE
14004 for (int i = 0; i < G_N_ELEMENTS (interp_in_static_sigs); i++)
14005 add_interp_in_wrapper_for_sig (acfg, *interp_in_static_sigs [i]);
14006 #else
14007 for (int i = 0; i < G_N_ELEMENTS (interp_in_static_common_sigs); i++)
14008 add_interp_in_wrapper_for_sig (acfg, *interp_in_static_common_sigs [i]);
14009 #endif
14011 /* required for mixed mode */
14012 if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) {
14013 add_gc_wrappers (acfg);
14015 for (int i = 0; i < MONO_JIT_ICALL_count; ++i)
14016 add_jit_icall_wrapper (acfg, mono_find_jit_icall_info ((MonoJitICallId)i));
14020 TV_GETTIME (atv);
14022 compile_methods (acfg);
14024 TV_GETTIME (btv);
14026 acfg->stats.jit_time = TV_ELAPSED (atv, btv);
14028 dedup_skip_methods (acfg);
14030 if (acfg->aot_opts.dedup_include && !is_dedup_dummy)
14031 /* We only collected methods from this assembly */
14032 return 0;
14034 current_acfg = NULL;
14036 return emit_aot_image (acfg);
14039 static void
14040 print_stats (MonoAotCompile *acfg)
14042 int i;
14043 gint64 all_sizes;
14044 char llvm_stats_msg [256];
14046 if (acfg->llvm && !acfg->aot_opts.llvm_only)
14047 sprintf (llvm_stats_msg, ", LLVM: %d (%d%%)", acfg->stats.llvm_count, acfg->stats.mcount ? (acfg->stats.llvm_count * 100) / acfg->stats.mcount : 100);
14048 else
14049 strcpy (llvm_stats_msg, "");
14051 all_sizes = acfg->stats.code_size + acfg->stats.method_info_size + acfg->stats.ex_info_size + acfg->stats.unwind_info_size + acfg->stats.class_info_size + acfg->stats.got_info_size + acfg->stats.offsets_size + acfg->stats.plt_size;
14053 aot_printf (acfg, "Code: %d(%d%%) Info: %d(%d%%) Ex Info: %d(%d%%) Unwind Info: %d(%d%%) Class Info: %d(%d%%) PLT: %d(%d%%) GOT Info: %d(%d%%) Offsets: %d(%d%%) GOT: %d, BLOB: %d\n",
14054 (int)acfg->stats.code_size, (int)(acfg->stats.code_size * 100 / all_sizes),
14055 (int)acfg->stats.method_info_size, (int)(acfg->stats.method_info_size * 100 / all_sizes),
14056 (int)acfg->stats.ex_info_size, (int)(acfg->stats.ex_info_size * 100 / all_sizes),
14057 (int)acfg->stats.unwind_info_size, (int)(acfg->stats.unwind_info_size * 100 / all_sizes),
14058 (int)acfg->stats.class_info_size, (int)(acfg->stats.class_info_size * 100 / all_sizes),
14059 acfg->stats.plt_size ? (int)acfg->stats.plt_size : (int)acfg->plt_offset, acfg->stats.plt_size ? (int)(acfg->stats.plt_size * 100 / all_sizes) : 0,
14060 (int)acfg->stats.got_info_size, (int)(acfg->stats.got_info_size * 100 / all_sizes),
14061 (int)acfg->stats.offsets_size, (int)(acfg->stats.offsets_size * 100 / all_sizes),
14062 (int)(acfg->got_offset * sizeof (target_mgreg_t)),
14063 (int)acfg->stats.blob_size);
14064 aot_printf (acfg, "Compiled: %d/%d (%d%%)%s, No GOT slots: %d (%d%%), Direct calls: %d (%d%%)\n",
14065 acfg->stats.ccount, acfg->stats.mcount, acfg->stats.mcount ? (acfg->stats.ccount * 100) / acfg->stats.mcount : 100,
14066 llvm_stats_msg,
14067 acfg->stats.methods_without_got_slots, acfg->stats.mcount ? (acfg->stats.methods_without_got_slots * 100) / acfg->stats.mcount : 100,
14068 acfg->stats.direct_calls, acfg->stats.all_calls ? (acfg->stats.direct_calls * 100) / acfg->stats.all_calls : 100);
14069 if (acfg->stats.genericcount)
14070 aot_printf (acfg, "%d methods failed gsharing (%d%%)\n", acfg->stats.genericcount, acfg->stats.mcount ? (acfg->stats.genericcount * 100) / acfg->stats.mcount : 100);
14071 if (acfg->stats.abscount)
14072 aot_printf (acfg, "%d methods contain absolute addresses (%d%%)\n", acfg->stats.abscount, acfg->stats.mcount ? (acfg->stats.abscount * 100) / acfg->stats.mcount : 100);
14073 if (acfg->stats.lmfcount)
14074 aot_printf (acfg, "%d methods contain lmf pointers (%d%%)\n", acfg->stats.lmfcount, acfg->stats.mcount ? (acfg->stats.lmfcount * 100) / acfg->stats.mcount : 100);
14075 if (acfg->stats.ocount)
14076 aot_printf (acfg, "%d methods have other problems (%d%%)\n", acfg->stats.ocount, acfg->stats.mcount ? (acfg->stats.ocount * 100) / acfg->stats.mcount : 100);
14078 aot_printf (acfg, "GOT slot distribution:\n");
14079 int nslots = 0;
14080 int size = 0;
14081 for (i = 0; i < MONO_PATCH_INFO_NUM; ++i) {
14082 nslots += acfg->stats.got_slot_types [i];
14083 size += acfg->stats.got_slot_info_sizes [i];
14084 if (acfg->stats.got_slot_types [i])
14085 aot_printf (acfg, "\t%s: %d (%d)\n", get_patch_name (i), acfg->stats.got_slot_types [i], acfg->stats.got_slot_info_sizes [i]);
14087 aot_printf (acfg, "GOT SLOTS: %d, INFO SIZE: %d\n", nslots, size);
14089 aot_printf (acfg, "\nEncoding stats:\n");
14090 aot_printf (acfg, "\tMethod ref: %d (%dk)\n", acfg->stats.method_ref_count, acfg->stats.method_ref_size / 1024);
14091 aot_printf (acfg, "\tClass ref: %d (%dk)\n", acfg->stats.class_ref_count, acfg->stats.class_ref_size / 1024);
14092 aot_printf (acfg, "\tGinst: %d (%dk)\n", acfg->stats.ginst_count, acfg->stats.ginst_size / 1024);
14094 aot_printf (acfg, "\nMethod stats:\n");
14095 aot_printf (acfg, "\tNormal: %d\n", acfg->stats.method_categories [METHOD_CAT_NORMAL]);
14096 aot_printf (acfg, "\tInstance: %d\n", acfg->stats.method_categories [METHOD_CAT_INST]);
14097 aot_printf (acfg, "\tGSharedvt: %d\n", acfg->stats.method_categories [METHOD_CAT_GSHAREDVT]);
14098 aot_printf (acfg, "\tWrapper: %d\n", acfg->stats.method_categories [METHOD_CAT_WRAPPER]);
14100 if (acfg->aot_opts.dedup || acfg->dedup_emit_mode)
14101 mono_dedup_log_stats (acfg);
14104 static void
14105 create_depfile (MonoAotCompile *acfg)
14107 FILE *depfile;
14109 // FIXME: Support other configurations
14110 g_assert (acfg->aot_opts.llvm_only && acfg->aot_opts.asm_only && acfg->aot_opts.llvm_outfile);
14112 depfile = fopen (acfg->aot_opts.depfile, "w");
14113 g_assert (depfile);
14115 int ntargets = 1;
14116 char **targets = g_new0 (char*, ntargets);
14117 targets [0] = acfg->aot_opts.llvm_outfile;
14118 for (int tindex = 0; tindex < ntargets; ++tindex) {
14119 fprintf (depfile, "%s: ", targets [tindex]);
14120 for (int i = 0; i < acfg->image_table->len; i++) {
14121 MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i);
14122 fprintf (depfile, " %s", image->filename);
14124 fprintf (depfile, "\n");
14126 g_free (targets);
14127 fclose (depfile);
14130 static int
14131 emit_aot_image (MonoAotCompile *acfg)
14133 int i, res;
14134 TV_DECLARE (atv);
14135 TV_DECLARE (btv);
14137 TV_GETTIME (atv);
14139 #ifdef ENABLE_LLVM
14140 if (acfg->llvm) {
14141 if (acfg->aot_opts.asm_only) {
14142 if (acfg->aot_opts.outfile) {
14143 acfg->tmpfname = g_strdup_printf ("%s", acfg->aot_opts.outfile);
14144 acfg->tmpbasename = g_strdup (acfg->tmpfname);
14145 } else {
14146 acfg->tmpbasename = g_strdup_printf ("%s", acfg->image->name);
14147 acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename);
14149 g_assert (acfg->aot_opts.llvm_outfile);
14150 acfg->llvm_sfile = g_strdup (acfg->aot_opts.llvm_outfile);
14151 if (acfg->llvm_owriter)
14152 acfg->llvm_ofile = g_strdup (acfg->aot_opts.llvm_outfile);
14153 else
14154 acfg->llvm_sfile = g_strdup (acfg->aot_opts.llvm_outfile);
14155 } else {
14156 gchar *temp_path;
14157 if (strcmp (acfg->aot_opts.temp_path, "") != 0) {
14158 temp_path = g_strdup (acfg->aot_opts.temp_path);
14159 } else {
14160 temp_path = g_mkdtemp (g_strdup ("mono_aot_XXXXXX"));
14161 g_assertf (temp_path, "mkdtemp failed, error = (%d) %s", errno, g_strerror (errno));
14162 acfg->temp_dir_to_delete = g_strdup (temp_path);
14165 acfg->tmpbasename = g_build_filename (temp_path, "temp", (const char*)NULL);
14166 acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename);
14167 acfg->llvm_sfile = g_strdup_printf ("%s-llvm.s", acfg->tmpbasename);
14168 acfg->llvm_ofile = g_strdup_printf ("%s-llvm." AS_OBJECT_FILE_SUFFIX, acfg->tmpbasename);
14170 g_free (temp_path);
14173 #endif
14175 if (acfg->aot_opts.asm_only && !acfg->aot_opts.llvm_only) {
14176 if (acfg->aot_opts.outfile)
14177 acfg->tmpfname = g_strdup_printf ("%s", acfg->aot_opts.outfile);
14178 else
14179 acfg->tmpfname = g_strdup_printf ("%s.s", acfg->image->name);
14180 acfg->fp = fopen (acfg->tmpfname, "w+");
14181 } else {
14182 if (strcmp (acfg->aot_opts.temp_path, "") == 0) {
14183 int i = g_file_open_tmp ("mono_aot_XXXXXX", &acfg->tmpfname, NULL);
14184 acfg->fp = fdopen (i, "w+");
14185 } else {
14186 acfg->tmpbasename = g_build_filename (acfg->aot_opts.temp_path, "temp", (const char*)NULL);
14187 acfg->tmpfname = g_strdup_printf ("%s.s", acfg->tmpbasename);
14188 acfg->fp = fopen (acfg->tmpfname, "w+");
14191 if (acfg->fp == 0 && !acfg->aot_opts.llvm_only) {
14192 aot_printerrf (acfg, "Unable to open file '%s': %s\n", acfg->tmpfname, strerror (errno));
14193 return 1;
14195 if (acfg->fp)
14196 acfg->w = mono_img_writer_create (acfg->fp, FALSE);
14198 /* Compute symbols for methods */
14199 for (i = 0; i < acfg->nmethods; ++i) {
14200 if (acfg->cfgs [i]) {
14201 MonoCompile *cfg = acfg->cfgs [i];
14202 int method_index = get_method_index (acfg, cfg->orig_method);
14204 if (cfg->asm_symbol) {
14205 // Set by method emitter in backend
14206 if (acfg->llvm_label_prefix) {
14207 char *old_symbol = cfg->asm_symbol;
14208 cfg->asm_symbol = g_strdup_printf ("%s%s", acfg->llvm_label_prefix, cfg->asm_symbol);
14209 g_free (old_symbol);
14211 } else if (COMPILE_LLVM (cfg)) {
14212 cfg->asm_symbol = g_strdup_printf ("%s%s", acfg->llvm_label_prefix, cfg->llvm_method_name);
14213 } else if (acfg->global_symbols || acfg->llvm) {
14214 cfg->asm_symbol = get_debug_sym (cfg->orig_method, "", acfg->method_label_hash);
14215 } else {
14216 cfg->asm_symbol = g_strdup_printf ("%s%sm_%x", acfg->temp_prefix, acfg->llvm_label_prefix, method_index);
14218 cfg->asm_debug_symbol = cfg->asm_symbol;
14222 if (acfg->aot_opts.dwarf_debug && acfg->aot_opts.gnu_asm) {
14224 * CLANG supports GAS .file/.loc directives, so emit line number information this way
14226 acfg->gas_line_numbers = TRUE;
14229 #ifdef EMIT_DWARF_INFO
14230 if ((!acfg->aot_opts.nodebug || acfg->aot_opts.dwarf_debug) && acfg->has_jitted_code) {
14231 if (acfg->aot_opts.dwarf_debug && !mono_debug_enabled ()) {
14232 aot_printerrf (acfg, "The dwarf AOT option requires the --debug option.\n");
14233 return 1;
14235 acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL, 0, !acfg->gas_line_numbers);
14237 #endif /* EMIT_DWARF_INFO */
14239 if (acfg->w)
14240 mono_img_writer_emit_start (acfg->w);
14242 if (acfg->dwarf)
14243 mono_dwarf_writer_emit_base_info (acfg->dwarf, g_path_get_basename (acfg->image->name), mono_unwind_get_cie_program ());
14245 if (acfg->aot_opts.dedup)
14246 mono_flush_method_cache (acfg);
14248 emit_code (acfg);
14250 emit_method_info_table (acfg);
14252 emit_extra_methods (acfg);
14254 emit_trampolines (acfg);
14256 emit_class_name_table (acfg);
14258 emit_got_info (acfg, FALSE);
14259 if (acfg->llvm)
14260 emit_got_info (acfg, TRUE);
14262 emit_exception_info (acfg);
14264 emit_unwind_info (acfg);
14266 emit_class_info (acfg);
14268 emit_plt (acfg);
14270 emit_image_table (acfg);
14272 emit_weak_field_indexes (acfg);
14274 emit_got (acfg);
14278 * The managed allocators are GC specific, so can't use an AOT image created by one GC
14279 * in another.
14281 const char *gc_name = mono_gc_get_gc_name ();
14282 acfg->gc_name_offset = add_to_blob (acfg, (guint8*)gc_name, strlen (gc_name) + 1);
14285 emit_blob (acfg);
14287 emit_objc_selectors (acfg);
14289 emit_globals (acfg);
14291 emit_file_info (acfg);
14293 if (acfg->dwarf) {
14294 emit_dwarf_info (acfg);
14295 mono_dwarf_writer_close (acfg->dwarf);
14296 } else {
14297 if (!acfg->aot_opts.nodebug)
14298 emit_codeview_info (acfg);
14301 emit_mem_end (acfg);
14303 if (acfg->need_pt_gnu_stack) {
14304 /* This is required so the .so doesn't have an executable stack */
14305 /* The bin writer already emits this */
14306 fprintf (acfg->fp, "\n.section .note.GNU-stack,\"\",@progbits\n");
14309 if (acfg->aot_opts.data_outfile)
14310 fclose (acfg->data_outfile);
14312 #ifdef ENABLE_LLVM
14313 if (acfg->llvm) {
14314 gboolean res;
14316 res = emit_llvm_file (acfg);
14317 if (!res)
14318 return 1;
14320 #endif
14322 emit_library_info (acfg);
14324 TV_GETTIME (btv);
14326 acfg->stats.gen_time = TV_ELAPSED (atv, btv);
14328 if (!acfg->aot_opts.stats)
14329 aot_printf (acfg, "Compiled: %d/%d\n", acfg->stats.ccount, acfg->stats.mcount);
14331 TV_GETTIME (atv);
14332 if (acfg->w) {
14333 res = mono_img_writer_emit_writeout (acfg->w);
14334 if (res != 0) {
14335 acfg_free (acfg);
14336 return res;
14338 res = compile_asm (acfg);
14339 if (res != 0) {
14340 acfg_free (acfg);
14341 return res;
14344 TV_GETTIME (btv);
14345 acfg->stats.link_time = TV_ELAPSED (atv, btv);
14347 if (acfg->aot_opts.stats)
14348 print_stats (acfg);
14350 aot_printf (acfg, "JIT time: %d ms, Generation time: %d ms, Assembly+Link time: %d ms.\n", acfg->stats.jit_time / 1000, acfg->stats.gen_time / 1000, acfg->stats.link_time / 1000);
14352 if (acfg->aot_opts.depfile)
14353 create_depfile (acfg);
14355 if (acfg->aot_opts.dump_json)
14356 aot_dump (acfg);
14358 if (!acfg->aot_opts.save_temps && acfg->temp_dir_to_delete) {
14359 char *command = g_strdup_printf ("rm -r %s", acfg->temp_dir_to_delete);
14360 execute_system (command);
14361 g_free (command);
14364 acfg_free (acfg);
14366 return 0;
14369 #else
14371 /* AOT disabled */
14373 void*
14374 mono_aot_readonly_field_override (MonoClassField *field)
14376 return NULL;
14380 mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options, gpointer **aot_state)
14382 return 0;
14385 gboolean
14386 mono_aot_is_shared_got_offset (int offset)
14388 return FALSE;
14391 gboolean
14392 mono_aot_is_externally_callable (MonoMethod *cmethod)
14394 return FALSE;
14398 mono_compile_deferred_assemblies (guint32 opts, const char *aot_options, gpointer **aot_state)
14400 g_assert_not_reached ();
14401 return 0;
14404 gboolean
14405 mono_aot_direct_icalls_enabled_for_method (MonoCompile *cfg, MonoMethod *method)
14407 g_assert_not_reached ();
14408 return 0;
14411 gboolean
14412 mono_aot_can_enter_interp (MonoMethod *method)
14414 return FALSE;
14418 mono_aot_get_method_index (MonoMethod *method)
14420 g_assert_not_reached ();
14421 return 0;
14424 #endif