X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmini%2Faot-compiler.c;h=d3918ad382477675cdf3dd84a7fcc5c9137a7277;hb=fd41b46ebe37bb5bccf47ddffd87a56cc44a7c9f;hp=b70c3bfbac1f0e1bdf7360c206bf064f25d467a4;hpb=c86ba07983f92c5e6b6c8ce589254c3c7fd21e83;p=mono.git diff --git a/mono/mini/aot-compiler.c b/mono/mini/aot-compiler.c index b70c3bfbac1..d3918ad3824 100644 --- a/mono/mini/aot-compiler.c +++ b/mono/mini/aot-compiler.c @@ -1,5 +1,5 @@ /* - * aot.c: mono Ahead of Time compiler + * aot-compiler.c: mono Ahead of Time compiler * * Author: * Dietmar Maurer (dietmar@ximian.com) @@ -44,10 +44,6 @@ #include #include -#include /* for PAGESIZE */ -#ifndef PAGESIZE -#define PAGESIZE 4096 -#endif #include #include @@ -59,26 +55,20 @@ #include #include #include -#include #include #include #include -#include -#include +#include #include #include #include - -#ifndef PLATFORM_WIN32 -#include -#include -#endif - -#include +#include #include "mini.h" +#include "image-writer.h" +#include "dwarfwriter.h" -#ifndef DISABLE_AOT +#if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) #define TV_DECLARE(name) gint64 name #define TV_GETTIME(tv) tv = mono_100ns_ticks () @@ -92,20 +82,6 @@ #define SHARED_EXT ".so" #endif -#if defined(sparc) || defined(__ppc__) || defined(__powerpc__) || defined(__MACH__) -#define AS_STRING_DIRECTIVE ".asciz" -#else -/* GNU as */ -#define AS_STRING_DIRECTIVE ".string" -#endif - - -// __MACH__ -// .byte generates 1 byte per expression. -// .short generates 2 bytes per expression. -// .long generates 4 bytes per expression. -// .quad generates 8 bytes per expression. - #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1)) #define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1))) #define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1)) @@ -121,55 +97,21 @@ typedef struct MonoAotOptions { gboolean static_link; gboolean asm_only; gboolean asm_writer; + gboolean nodebug; int nthreads; + int ntrampolines; + gboolean print_skipped_methods; } MonoAotOptions; typedef struct MonoAotStats { int ccount, mcount, lmfcount, abscount, gcount, ocount, genericcount; - int code_size, info_size, ex_info_size, got_size, class_info_size, got_info_size, got_info_offsets_size; + int code_size, info_size, ex_info_size, unwind_info_size, got_size, class_info_size, got_info_size, got_info_offsets_size; int methods_without_got_slots, direct_calls, all_calls; int got_slots; int got_slot_types [MONO_PATCH_INFO_NONE]; int jit_time, gen_time, link_time; } MonoAotStats; -#if defined(__x86_64__) && !defined(PLATFORM_WIN32) -#define USE_ELF_WRITER 1 -#define USE_ELF_RELA 1 -#endif - -#if defined(__i386__) && !defined(PLATFORM_WIN32) -#define USE_ELF_WRITER 1 -#endif - -#if defined(__arm__) && !defined(__MACH__) -#define USE_ELF_WRITER 1 -#endif - -#if defined(__mips__) -#define USE_ELF_WRITER 1 -#endif - -#if defined(USE_ELF_WRITER) -#define USE_BIN_WRITER 1 -#endif - -#ifdef USE_BIN_WRITER - -typedef struct _BinSymbol BinSymbol; -typedef struct _BinReloc BinReloc; -typedef struct _BinSection BinSection; - -#endif - -/* emit mode */ -enum { - EMIT_NONE, - EMIT_BYTE, - EMIT_WORD, - EMIT_LONG -}; - typedef struct MonoAotCompile { MonoImage *image; GPtrArray *methods; @@ -188,12 +130,14 @@ typedef struct MonoAotCompile { GPtrArray *globals; GList *method_order; guint32 *plt_got_info_offsets; - /* Number of trampolines emitted into the AOT file */ - guint32 num_aot_trampolines; guint32 got_offset, plt_offset, plt_got_offset_base; /* Number of GOT entries reserved for trampolines */ guint32 num_trampoline_got_entries; - guint32 trampoline_got_offset_base; + + guint32 num_trampolines [MONO_AOT_TRAMP_NUM]; + guint32 trampoline_got_offset_base [MONO_AOT_TRAMP_NUM]; + guint32 trampoline_size [MONO_AOT_TRAMP_NUM]; + MonoAotOptions aot_opts; guint32 nmethods; guint32 opts; @@ -203,26 +147,14 @@ typedef struct MonoAotCompile { char *static_linking_symbol; CRITICAL_SECTION mutex; gboolean use_bin_writer; - /* Bin writer */ -#ifdef USE_BIN_WRITER - BinSymbol *symbols; - BinSection *sections; - BinSection *cur_section; - BinReloc *relocations; - GHashTable *labels; - int num_relocs; -#endif - /* Asm writer */ + MonoImageWriter *w; + MonoDwarfWriter *dwarf; FILE *fp; char *tmpfname; - int mode; /* emit mode */ - int col_count; /* bytes emitted per .byte line */ - /* xdebug */ - GHashTable *class_to_die; - int fde_index, tdie_index, line_number_file_index, line_number_dir_index; - GHashTable *file_to_index, *dir_to_index; - FILE *il_file; - int il_file_line_index, loclist_index; + GSList *cie_program; + GHashTable *unwind_info_offsets; + GPtrArray *unwind_ops; + guint32 unwind_info_offset; } MonoAotCompile; #define mono_acfg_lock(acfg) EnterCriticalSection (&((acfg)->mutex)) @@ -246,7 +178,7 @@ static const gint16 opidx [] = { #undef PATCH_INFO }; -static const char* +static G_GNUC_UNUSED const char* get_patch_name (int info) { return (const char*)&opstr + opidx [info]; @@ -268,2891 +200,2148 @@ get_patch_name (int info) #endif -static void -emit_global (MonoAotCompile *acfg, const char *name, gboolean func); +/* Wrappers around the image writer functions */ -static gboolean -is_got_patch (MonoJumpInfoType patch_type) +static inline void +emit_section_change (MonoAotCompile *acfg, const char *section_name, int subsection_index) { - return TRUE; + img_writer_emit_section_change (acfg->w, section_name, subsection_index); } -static G_GNUC_UNUSED int -ilog2(register int value) +static inline void +emit_push_section (MonoAotCompile *acfg, const char *section_name, int subsection) { - int count = -1; - while (value & ~0xf) count += 4, value >>= 4; - while (value) count++, value >>= 1; - return count; + img_writer_emit_push_section (acfg->w, section_name, subsection); } -#ifdef USE_BIN_WRITER - -typedef struct _BinLabel BinLabel; -struct _BinLabel { - char *name; - BinSection *section; - int offset; -}; - -struct _BinReloc { - BinReloc *next; - char *val1; - char *val2; - BinSection *val2_section; - int val2_offset; - int offset; - BinSection *section; - int section_offset; - int reloc_type; -}; - -struct _BinSymbol { - BinSymbol *next; - char *name; - BinSection *section; - int offset; - gboolean is_function; - gboolean is_global; - char *end_label; -}; - -struct _BinSection { - BinSection *next; - BinSection *parent; - char *name; - int subsection; - guint8 *data; - int data_len; - int cur_offset; - int file_offset; - int virt_offset; - int shidx; -}; - -static void -bin_writer_emit_start (MonoAotCompile *acfg) +static inline void +emit_pop_section (MonoAotCompile *acfg) { - acfg->labels = g_hash_table_new (g_str_hash, g_str_equal); + img_writer_emit_pop_section (acfg->w); } -static void -bin_writer_emit_section_change (MonoAotCompile *acfg, const char *section_name, int subsection_index) -{ - BinSection *section; - - if (acfg->cur_section && acfg->cur_section->subsection == subsection_index - && strcmp (acfg->cur_section->name, section_name) == 0) - return; - for (section = acfg->sections; section; section = section->next) { - if (section->subsection == subsection_index && strcmp (section->name, section_name) == 0) { - acfg->cur_section = section; - return; - } - } - if (!section) { - section = g_new0 (BinSection, 1); - section->name = g_strdup (section_name); - section->subsection = subsection_index; - section->next = acfg->sections; - acfg->sections = section; - acfg->cur_section = section; - } +static inline void +emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func) +{ + img_writer_emit_local_symbol (acfg->w, name, end_label, func); } -static void -bin_writer_emit_symbol_inner (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean is_global, gboolean func) -{ - BinSymbol *symbol = g_new0 (BinSymbol, 1); - symbol->name = g_strdup (name); - if (end_label) - symbol->end_label = g_strdup (end_label); - symbol->is_function = func; - symbol->is_global = is_global; - symbol->section = acfg->cur_section; - /* FIXME: we align after this call... */ - symbol->offset = symbol->section->cur_offset; - symbol->next = acfg->symbols; - acfg->symbols = symbol; +static inline void +emit_label (MonoAotCompile *acfg, const char *name) +{ + img_writer_emit_label (acfg->w, name); } -static void -bin_writer_emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func) -{ - bin_writer_emit_symbol_inner (acfg, name, NULL, TRUE, func); +static inline void +emit_bytes (MonoAotCompile *acfg, const guint8* buf, int size) +{ + img_writer_emit_bytes (acfg->w, buf, size); } -static void -bin_writer_emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func) -{ - bin_writer_emit_symbol_inner (acfg, name, end_label, FALSE, func); +static inline void +emit_string (MonoAotCompile *acfg, const char *value) +{ + img_writer_emit_string (acfg->w, value); } -static void -bin_writer_emit_label (MonoAotCompile *acfg, const char *name) -{ - BinLabel *label = g_new0 (BinLabel, 1); - label->name = g_strdup (name); - label->section = acfg->cur_section; - label->offset = acfg->cur_section->cur_offset; - g_hash_table_insert (acfg->labels, label->name, label); +static inline void +emit_line (MonoAotCompile *acfg) +{ + img_writer_emit_line (acfg->w); } -static void -bin_writer_emit_ensure_buffer (BinSection *section, int size) -{ - int new_offset = section->cur_offset + size; - if (new_offset >= section->data_len) { - int new_size = section->data_len? section->data_len * 2: 256; - guint8 *data; - while (new_size <= new_offset) - new_size *= 2; - data = g_malloc0 (new_size); - memcpy (data, section->data, section->data_len); - g_free (section->data); - section->data = data; - section->data_len = new_size; - } +static inline void +emit_alignment (MonoAotCompile *acfg, int size) +{ + img_writer_emit_alignment (acfg->w, size); } -static void -bin_writer_emit_bytes (MonoAotCompile *acfg, const guint8* buf, int size) -{ - bin_writer_emit_ensure_buffer (acfg->cur_section, size); - memcpy (acfg->cur_section->data + acfg->cur_section->cur_offset, buf, size); - acfg->cur_section->cur_offset += size; +static inline void +emit_pointer_unaligned (MonoAotCompile *acfg, const char *target) +{ + img_writer_emit_pointer_unaligned (acfg->w, target); } -static void -bin_writer_emit_string (MonoAotCompile *acfg, const char *value) -{ - int size = strlen (value) + 1; - bin_writer_emit_bytes (acfg, (const guint8*)value, size); +static inline void +emit_pointer (MonoAotCompile *acfg, const char *target) +{ + img_writer_emit_pointer (acfg->w, target); } -static void -bin_writer_emit_line (MonoAotCompile *acfg) -{ - /* Nothing to do in binary writer */ +static inline void +emit_int16 (MonoAotCompile *acfg, int value) +{ + img_writer_emit_int16 (acfg->w, value); } -static void -bin_writer_emit_alignment (MonoAotCompile *acfg, int size) -{ - int offset = acfg->cur_section->cur_offset; - int add; - offset += (size - 1); - offset &= ~(size - 1); - add = offset - acfg->cur_section->cur_offset; - if (add) { - bin_writer_emit_ensure_buffer (acfg->cur_section, add); - acfg->cur_section->cur_offset += add; - } +static inline void +emit_int32 (MonoAotCompile *acfg, int value) +{ + img_writer_emit_int32 (acfg->w, value); } -static void -bin_writer_emit_pointer_unaligned (MonoAotCompile *acfg, const char *target) -{ - BinReloc *reloc; - - if (!target) - // FIXME: - g_assert_not_reached (); - reloc = g_new0 (BinReloc, 1); - reloc->val1 = g_strdup (target); - reloc->section = acfg->cur_section; - reloc->section_offset = acfg->cur_section->cur_offset; - reloc->next = acfg->relocations; - acfg->relocations = reloc; - if (strcmp (reloc->section->name, ".data") == 0) { - acfg->num_relocs++; - g_print ("reloc: %s at %d\n", target, acfg->cur_section->cur_offset); - } - acfg->cur_section->cur_offset += sizeof (gpointer); +static inline void +emit_symbol_diff (MonoAotCompile *acfg, const char *end, const char* start, int offset) +{ + img_writer_emit_symbol_diff (acfg->w, end, start, offset); } -static void -bin_writer_emit_pointer (MonoAotCompile *acfg, const char *target) -{ - bin_writer_emit_alignment (acfg, sizeof (gpointer)); - bin_writer_emit_pointer_unaligned (acfg, target); +static inline void +emit_zero_bytes (MonoAotCompile *acfg, int num) +{ + img_writer_emit_zero_bytes (acfg->w, num); } -static void -bin_writer_emit_int16 (MonoAotCompile *acfg, int value) -{ - guint8 *data; - bin_writer_emit_ensure_buffer (acfg->cur_section, 2); - data = acfg->cur_section->data + acfg->cur_section->cur_offset; - acfg->cur_section->cur_offset += 2; - /* FIXME: little endian */ - data [0] = value; - data [1] = value >> 8; +static inline void +emit_byte (MonoAotCompile *acfg, guint8 val) +{ + img_writer_emit_byte (acfg->w, val); } -static void -bin_writer_emit_int32 (MonoAotCompile *acfg, int value) +static G_GNUC_UNUSED void +emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func) { - guint8 *data; - bin_writer_emit_ensure_buffer (acfg->cur_section, 4); - data = acfg->cur_section->data + acfg->cur_section->cur_offset; - acfg->cur_section->cur_offset += 4; - /* FIXME: little endian */ - data [0] = value; - data [1] = value >> 8; - data [2] = value >> 16; - data [3] = value >> 24; + img_writer_emit_global (acfg->w, name, func); } -static BinReloc* -create_reloc (MonoAotCompile *acfg, const char *end, const char* start, int offset) +static void +emit_global (MonoAotCompile *acfg, const char *name, gboolean func) { - BinReloc *reloc; - reloc = mono_mempool_alloc0 (acfg->mempool, sizeof (BinReloc)); - reloc->val1 = mono_mempool_strdup (acfg->mempool, end); - if (strcmp (start, ".") == 0) { - reloc->val2_section = acfg->cur_section; - reloc->val2_offset = acfg->cur_section->cur_offset; + if (acfg->aot_opts.no_dlsym) { + g_ptr_array_add (acfg->globals, g_strdup (name)); } else { - reloc->val2 = mono_mempool_strdup (acfg->mempool, start); + img_writer_emit_global (acfg->w, name, func); } - reloc->offset = offset; - reloc->section = acfg->cur_section; - reloc->section_offset = acfg->cur_section->cur_offset; - reloc->next = acfg->relocations; - acfg->relocations = reloc; - return reloc; } static void -bin_writer_emit_symbol_diff (MonoAotCompile *acfg, const char *end, const char* start, int offset) +emit_string_symbol (MonoAotCompile *acfg, const char *name, const char *value) { - create_reloc (acfg, end, start, offset); - acfg->cur_section->cur_offset += 4; - /*if (strcmp (reloc->section->name, ".data") == 0) { - acfg->num_relocs++; - g_print ("reloc: %s - %s + %d at %d\n", end, start, offset, acfg->cur_section->cur_offset - 4); - }*/ + img_writer_emit_section_change (acfg->w, ".text", 1); + emit_global (acfg, name, FALSE); + img_writer_emit_label (acfg->w, name); + img_writer_emit_string (acfg->w, value); } -/* - * Emit a relocation entry of type RELOC_TYPE against symbol SYMBOL at the current PC. - * Do not advance PC. - */ static G_GNUC_UNUSED void -bin_writer_emit_reloc (MonoAotCompile *acfg, int reloc_type, const char *symbol, int addend) +emit_uleb128 (MonoAotCompile *acfg, guint32 value) { - BinReloc *reloc = create_reloc (acfg, symbol, ".", addend); - reloc->reloc_type = reloc_type; + do { + guint8 b = value & 0x7f; + value >>= 7; + if (value != 0) /* more bytes to come */ + b |= 0x80; + emit_byte (acfg, b); + } while (value); } -static void -bin_writer_emit_zero_bytes (MonoAotCompile *acfg, int num) +static G_GNUC_UNUSED void +emit_sleb128 (MonoAotCompile *acfg, gint64 value) { - bin_writer_emit_ensure_buffer (acfg->cur_section, num); - acfg->cur_section->cur_offset += num; -} - -#ifdef USE_ELF_WRITER - -enum { - SECT_NULL, - SECT_HASH, - SECT_DYNSYM, - SECT_DYNSTR, - SECT_REL_DYN, - SECT_RELA_DYN, - SECT_TEXT, - SECT_DYNAMIC, - SECT_GOT_PLT, - SECT_DATA, - SECT_BSS, - SECT_DEBUG_FRAME, - SECT_DEBUG_INFO, - SECT_DEBUG_ABBREV, - SECT_DEBUG_LINE, - SECT_DEBUG_LOC, - SECT_SHSTRTAB, - SECT_SYMTAB, - SECT_STRTAB, - SECT_NUM -}; - -/* Relocation types */ -#define R_ARM_CALL 28 -#define R_ARM_ALU_PC_G0_NC 59 - -#if SIZEOF_VOID_P == 4 - -typedef Elf32_Ehdr ElfHeader; -typedef Elf32_Shdr ElfSectHeader; -typedef Elf32_Phdr ElfProgHeader; -typedef Elf32_Sym ElfSymbol; -typedef Elf32_Rel ElfReloc; -typedef Elf32_Rela ElfRelocA; -typedef Elf32_Dyn ElfDynamic; - -#else - -typedef Elf64_Ehdr ElfHeader; -typedef Elf64_Shdr ElfSectHeader; -typedef Elf64_Phdr ElfProgHeader; -typedef Elf64_Sym ElfSymbol; -typedef Elf64_Rel ElfReloc; -typedef Elf64_Rela ElfRelocA; -typedef Elf64_Dyn ElfDynamic; - -#endif - -typedef struct { - const char *name; - int type; - int esize; - int flags; - int align; -} SectInfo; - -static SectInfo section_info [] = { - {"", 0, 0, 0, 0}, - {".hash", SHT_HASH, 4, 2, SIZEOF_VOID_P}, - {".dynsym", SHT_DYNSYM, sizeof (ElfSymbol), 2, SIZEOF_VOID_P}, - {".dynstr", SHT_STRTAB, 0, 2, 1}, - {".rel.dyn", SHT_REL, sizeof (ElfReloc), 2, SIZEOF_VOID_P}, - {".rela.dyn", SHT_RELA, sizeof (ElfRelocA), 2, SIZEOF_VOID_P}, - {".text", SHT_PROGBITS, 0, 6, 4096}, - {".dynamic", SHT_DYNAMIC, sizeof (ElfDynamic), 3, SIZEOF_VOID_P}, - {".got.plt", SHT_PROGBITS, SIZEOF_VOID_P, 3, SIZEOF_VOID_P}, - {".data", SHT_PROGBITS, 0, 3, 8}, - {".bss", SHT_NOBITS, 0, 3, 8}, - {".debug_frame", SHT_PROGBITS, 0, 0, 8}, - {".debug_info", SHT_PROGBITS, 0, 0, 1}, - {".debug_abbrev", SHT_PROGBITS, 0, 0, 1}, - {".debug_line", SHT_PROGBITS, 0, 0, 1}, - {".debug_loc", SHT_PROGBITS, 0, 0, 1}, - {".shstrtab", SHT_STRTAB, 0, 0, 1}, - {".symtab", SHT_SYMTAB, sizeof (ElfSymbol), 0, SIZEOF_VOID_P}, - {".strtab", SHT_STRTAB, 0, 0, 1} -}; - -typedef struct { - GString *data; - GHashTable *hash; -} ElfStrTable; + gboolean more = 1; + gboolean negative = (value < 0); + guint32 size = 64; + guint8 byte; -static int -str_table_add (ElfStrTable *table, const char* value) -{ - int idx; - if (!table->data) { - table->data = g_string_new_len ("", 1); - table->hash = g_hash_table_new (g_str_hash, g_str_equal); + while (more) { + byte = value & 0x7f; + value >>= 7; + /* the following is unnecessary if the + * implementation of >>= uses an arithmetic rather + * than logical shift for a signed left operand + */ + if (negative) + /* sign extend */ + value |= - ((gint64)1 <<(size - 7)); + /* sign bit of byte is second high order bit (0x40) */ + if ((value == 0 && !(byte & 0x40)) || + (value == -1 && (byte & 0x40))) + more = 0; + else + byte |= 0x80; + emit_byte (acfg, byte); } - idx = GPOINTER_TO_UINT (g_hash_table_lookup (table->hash, value)); - if (idx) - return idx; - idx = table->data->len; - g_string_append (table->data, value); - g_string_append_c (table->data, 0); - g_hash_table_insert (table->hash, (void*)value, GUINT_TO_POINTER (idx)); - return idx; } -static void -append_subsection (MonoAotCompile *acfg, ElfSectHeader *sheaders, BinSection *sect, BinSection *add) +static G_GNUC_UNUSED void +encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf) { - int offset = sect->cur_offset; - /*offset += (sheaders [sect->shidx].sh_addralign - 1); - offset &= ~(sheaders [sect->shidx].sh_addralign - 1);*/ - offset += (8 - 1); - offset &= ~(8 - 1); - bin_writer_emit_ensure_buffer (sect, offset); - g_print ("section %s aligned to %d from %d\n", sect->name, offset, sect->cur_offset); - sect->cur_offset = offset; - - bin_writer_emit_ensure_buffer (sect, add->cur_offset); - memcpy (sect->data + sect->cur_offset, add->data, add->cur_offset); - add->parent = sect; - sect->cur_offset += add->cur_offset; - add->cur_offset = offset; /* it becomes the offset in the parent section */ - g_print ("subsection %d of %s added at offset %d (align: %d)\n", add->subsection, sect->name, add->cur_offset, (int)sheaders [sect->shidx].sh_addralign); - add->data = NULL; - add->data_len = 0; -} + guint8 *p = buf; -/* merge the subsections */ -static int -collect_sections (MonoAotCompile *acfg, ElfSectHeader *sheaders, BinSection **out, int num) -{ - int i, j, maxs, num_sections; - BinSection *sect; - - num_sections = 0; - maxs = 0; - for (sect = acfg->sections; sect; sect = sect->next) { - if (sect->subsection == 0) { - out [num_sections++] = sect; - g_assert (num_sections < num); - } - maxs = MAX (maxs, sect->subsection); - } - for (i = 0; i < num_sections; i++) { - for (j = 1; j <= maxs; ++j) { - for (sect = acfg->sections; sect; sect = sect->next) { - if (sect->subsection == j && strcmp (out [i]->name, sect->name) == 0) { - append_subsection (acfg, sheaders, out [i], sect); - } - } - } - } - return num_sections; -} + do { + guint8 b = value & 0x7f; + value >>= 7; + if (value != 0) /* more bytes to come */ + b |= 0x80; + *p ++ = b; + } while (value); -static unsigned long -elf_hash (const unsigned char *name) -{ - unsigned long h = 0, g; - while (*name) { - h = (h << 4) + *name++; - if ((g = h & 0xf0000000)) - h ^= g >> 24; - h &= ~g; - } - return h; + *endbuf = p; } -#define NUM_BUCKETS 17 - -static int* -build_hash (MonoAotCompile *acfg, int num_sections, ElfStrTable *dynstr) +static G_GNUC_UNUSED void +encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf) { - int *data; - int num_symbols = 1 + num_sections + 3; - BinSymbol *symbol; + gboolean more = 1; + gboolean negative = (value < 0); + guint32 size = 32; + guint8 byte; + guint8 *p = buf; - for (symbol = acfg->symbols; symbol; symbol = symbol->next) { - if (!symbol->is_global) - continue; - num_symbols++; - str_table_add (dynstr, symbol->name); - /*g_print ("adding sym: %s\n", symbol->name);*/ + while (more) { + byte = value & 0x7f; + value >>= 7; + /* the following is unnecessary if the + * implementation of >>= uses an arithmetic rather + * than logical shift for a signed left operand + */ + if (negative) + /* sign extend */ + value |= - (1 <<(size - 7)); + /* sign bit of byte is second high order bit (0x40) */ + if ((value == 0 && !(byte & 0x40)) || + (value == -1 && (byte & 0x40))) + more = 0; + else + byte |= 0x80; + *p ++= byte; } - str_table_add (dynstr, "__bss_start"); - str_table_add (dynstr, "_edata"); - str_table_add (dynstr, "_end"); - data = g_new0 (int, num_symbols + 2 + NUM_BUCKETS); - data [0] = NUM_BUCKETS; - data [1] = num_symbols; - - return data; + *endbuf = p; } -static gsize -get_label_addr (MonoAotCompile *acfg, const char *name) -{ - int offset; - BinLabel *lab; - BinSection *section; - gsize value; - - lab = g_hash_table_lookup (acfg->labels, name); - if (!lab) - g_error ("Undefined label: '%s'.\n", name); - section = lab->section; - offset = lab->offset; - if (section->parent) { - value = section->parent->virt_offset + section->cur_offset + offset; - } else { - value = section->virt_offset + offset; - } - return value; -} +/* ARCHITECTURE SPECIFIC CODE */ -static ElfSymbol* -collect_syms (MonoAotCompile *acfg, int *hash, ElfStrTable *strtab, ElfSectHeader *sheaders, int *num_syms) -{ - ElfSymbol *symbols; - BinSymbol *symbol; - BinSection *section; - int i; - int *bucket; - int *chain; - unsigned long hashc; +#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM) +#define EMIT_DWARF_INFO 1 +#endif - if (hash) - symbols = g_new0 (ElfSymbol, hash [1]); - else { - i = 0; - for (symbol = acfg->symbols; symbol; symbol = symbol->next) - i ++; - - symbols = g_new0 (ElfSymbol, i + SECT_NUM + 10); /* FIXME */ - } +/* + * arch_emit_direct_call: + * + * Emit a direct call to the symbol TARGET. CALL_SIZE is set to the size of the + * calling code. + */ +static void +arch_emit_direct_call (MonoAotCompile *acfg, const char *target, int *call_size) +{ +#if defined(TARGET_X86) || defined(TARGET_AMD64) + /* Need to make sure this is exactly 5 bytes long */ + emit_byte (acfg, '\xe8'); + emit_symbol_diff (acfg, target, ".", -4); + *call_size = 5; +#elif defined(TARGET_ARM) + if (acfg->use_bin_writer) { + guint8 buf [4]; + guint8 *code; - /* the first symbol is undef, all zeroes */ - i = 1; - if (sheaders) { - int j; - for (j = 1; j < SECT_NUM; ++j) { - symbols [i].st_info = ELF32_ST_INFO (STB_LOCAL, STT_SECTION); - symbols [i].st_shndx = j; - symbols [i].st_value = sheaders [j].sh_addr; - ++i; - } - } else { - for (section = acfg->sections; section; section = section->next) { - if (section->parent) - continue; - symbols [i].st_info = ELF32_ST_INFO (STB_LOCAL, STT_SECTION); - if (strcmp (section->name, ".text") == 0) { - symbols [i].st_shndx = SECT_TEXT; - section->shidx = SECT_TEXT; - section->file_offset = 4096; - symbols [i].st_value = section->virt_offset; - } else if (strcmp (section->name, ".data") == 0) { - symbols [i].st_shndx = SECT_DATA; - section->shidx = SECT_DATA; - section->file_offset = 4096 + 28; /* FIXME */ - symbols [i].st_value = section->virt_offset; - } else if (strcmp (section->name, ".bss") == 0) { - symbols [i].st_shndx = SECT_BSS; - section->shidx = SECT_BSS; - section->file_offset = 4096 + 28 + 8; /* FIXME */ - symbols [i].st_value = section->virt_offset; - } - ++i; - } - } - for (symbol = acfg->symbols; symbol; symbol = symbol->next) { - int offset; - BinLabel *lab; - if (!symbol->is_global && hash) - continue; - symbols [i].st_info = ELF32_ST_INFO (symbol->is_global ? STB_GLOBAL : STB_LOCAL, symbol->is_function? STT_FUNC : STT_OBJECT); - symbols [i].st_name = str_table_add (strtab, symbol->name); - /*g_print ("sym name %s tabled to %d\n", symbol->name, symbols [i].st_name);*/ - section = symbol->section; - symbols [i].st_shndx = section->parent? section->parent->shidx: section->shidx; - lab = g_hash_table_lookup (acfg->labels, symbol->name); - offset = lab->offset; - if (section->parent) { - symbols [i].st_value = section->parent->virt_offset + section->cur_offset + offset; - } else { - symbols [i].st_value = section->virt_offset + offset; - } + code = buf; + ARM_BL (code, 0); - if (symbol->end_label) { - BinLabel *elab = g_hash_table_lookup (acfg->labels, symbol->end_label); - g_assert (elab); - symbols [i].st_size = elab->offset - lab->offset; - } - ++i; - } - /* add special symbols */ - symbols [i].st_name = str_table_add (strtab, "__bss_start"); - symbols [i].st_shndx = 0xfff1; - symbols [i].st_info = ELF32_ST_INFO (STB_GLOBAL, 0); - ++i; - symbols [i].st_name = str_table_add (strtab, "_edata"); - symbols [i].st_shndx = 0xfff1; - symbols [i].st_info = ELF32_ST_INFO (STB_GLOBAL, 0); - ++i; - symbols [i].st_name = str_table_add (strtab, "_end"); - symbols [i].st_shndx = 0xfff1; - symbols [i].st_info = ELF32_ST_INFO (STB_GLOBAL, 0); - ++i; - - if (num_syms) - *num_syms = i; - - /* add to hash table */ - if (hash) { - bucket = hash + 2; - chain = hash + 2 + hash [0]; - for (i = 0; i < hash [1]; ++i) { - int slot; - /*g_print ("checking %d '%s' (sym %d)\n", symbols [i].st_name, strtab->data->str + symbols [i].st_name, i);*/ - if (!symbols [i].st_name) - continue; - hashc = elf_hash ((guint8*)strtab->data->str + symbols [i].st_name); - slot = hashc % hash [0]; - /*g_print ("hashing '%s' at slot %d (sym %d)\n", strtab->data->str + symbols [i].st_name, slot, i);*/ - if (bucket [slot]) { - chain [i] = bucket [slot]; - bucket [slot] = i; - } else { - bucket [slot] = i; - } - } + img_writer_emit_reloc (acfg->w, R_ARM_CALL, target, -8); + emit_bytes (acfg, buf, 4); + } else { + img_writer_emit_unset_mode (acfg->w); + fprintf (acfg->fp, "bl %s\n", target); } - return symbols; + *call_size = 4; +#else + g_assert_not_reached (); +#endif } +#ifdef MONO_ARCH_AOT_SUPPORTED +/* + * arch_emit_got_offset: + * + * The memory pointed to by CODE should hold native code for computing the GOT + * address. Emit this code while patching it with the offset between code and + * the GOT. CODE_SIZE is set to the number of bytes emitted. + */ static void -reloc_symbols (MonoAotCompile *acfg, ElfSymbol *symbols, ElfSectHeader *sheaders, ElfStrTable *strtab, gboolean dynamic) +arch_emit_got_offset (MonoAotCompile *acfg, guint8 *code, int *code_size) { - BinSection *section; - BinSymbol *symbol; - int i; + guint32 offset = mono_arch_get_patch_offset (code); + emit_bytes (acfg, code, offset); + emit_symbol_diff (acfg, "got", ".", offset); - i = 1; - if (dynamic) { - for (section = acfg->sections; section; section = section->next) { - if (section->parent) - continue; - symbols [i].st_value = sheaders [section->shidx].sh_addr; - ++i; - } - } else { - for (i = 1; i < SECT_NUM; ++i) { - symbols [i].st_value = sheaders [i].sh_addr; - } - } - for (symbol = acfg->symbols; symbol; symbol = symbol->next) { - int offset; - BinLabel *lab; - if (dynamic && !symbol->is_global) - continue; - section = symbol->section; - lab = g_hash_table_lookup (acfg->labels, symbol->name); - offset = lab->offset; - if (section->parent) { - symbols [i].st_value = sheaders [section->parent->shidx].sh_addr + section->cur_offset + offset; - } else { - symbols [i].st_value = sheaders [section->shidx].sh_addr + offset; - } - ++i; - } - /* __bss_start */ - symbols [i].st_value = sheaders [SECT_BSS].sh_addr; - ++i; - /* _edata */ - symbols [i].st_value = sheaders [SECT_DATA].sh_addr + sheaders [SECT_DATA].sh_size; - ++i; - /* _end */ - symbols [i].st_value = sheaders [SECT_BSS].sh_addr + sheaders [SECT_BSS].sh_size; - ++i; + *code_size = offset + 4; } -static void -resolve_reloc (MonoAotCompile *acfg, BinReloc *reloc, guint8 **out_data, gsize *out_vaddr, gsize *out_start_val, gsize *out_end_val) -{ - guint8 *data; - gssize end_val, start_val; - gsize vaddr; - - end_val = get_label_addr (acfg, reloc->val1); - if (reloc->val2) { - start_val = get_label_addr (acfg, reloc->val2); - } else if (reloc->val2_section) { - start_val = reloc->val2_offset; - if (reloc->val2_section->parent) - start_val += reloc->val2_section->parent->virt_offset + reloc->val2_section->cur_offset; - else - start_val += reloc->val2_section->virt_offset; - } else { - start_val = 0; - } - end_val = end_val - start_val + reloc->offset; - if (reloc->section->parent) { - data = reloc->section->parent->data; - data += reloc->section->cur_offset; - data += reloc->section_offset; - vaddr = reloc->section->parent->virt_offset; - vaddr += reloc->section->cur_offset; - vaddr += reloc->section_offset; - } else { - data = reloc->section->data; - data += reloc->section_offset; - vaddr = reloc->section->virt_offset; - vaddr += reloc->section_offset; - } +/* + * arch_emit_got_access: + * + * The memory pointed to by CODE should hold native code for loading a GOT + * slot. Emit this code while patching it so it accesses the GOT slot GOT_SLOT. + * CODE_SIZE is set to the number of bytes emitted. + */ +static void +arch_emit_got_access (MonoAotCompile *acfg, guint8 *code, int got_slot, int *code_size) +{ + /* Emit beginning of instruction */ + emit_bytes (acfg, code, mono_arch_get_patch_offset (code)); + + /* Emit the offset */ +#ifdef TARGET_AMD64 + emit_symbol_diff (acfg, "got", ".", (unsigned int) ((got_slot * sizeof (gpointer)) - 4)); +#elif defined(TARGET_X86) + emit_int32 (acfg, (unsigned int) ((got_slot * sizeof (gpointer)))); +#elif defined(TARGET_ARM) + emit_symbol_diff (acfg, "got", ".", (unsigned int) ((got_slot * sizeof (gpointer))) - 12); +#else + g_assert_not_reached (); +#endif - *out_start_val = start_val; - *out_end_val = end_val; - *out_data = data; - *out_vaddr = vaddr; + *code_size = mono_arch_get_patch_offset (code) + 4; } -#ifdef USE_ELF_RELA +#endif -static ElfRelocA* -resolve_relocations (MonoAotCompile *acfg) +/* + * arch_emit_plt_entry: + * + * Emit code for the PLT entry with index INDEX. + */ +static void +arch_emit_plt_entry (MonoAotCompile *acfg, int index) { - BinReloc *reloc; - guint8 *data; - gsize end_val, start_val; - ElfRelocA *rr; - int i; - gsize vaddr; - - rr = g_new0 (ElfRelocA, acfg->num_relocs); - i = 0; - - for (reloc = acfg->relocations; reloc; reloc = reloc->next) { - resolve_reloc (acfg, reloc, &data, &vaddr, &start_val, &end_val); - /* FIXME: little endian */ - data [0] = end_val; - data [1] = end_val >> 8; - data [2] = end_val >> 16; - data [3] = end_val >> 24; - // FIXME: - if (start_val == 0 && reloc->val1 [0] != '.') { - rr [i].r_offset = vaddr; - rr [i].r_info = R_X86_64_RELATIVE; - rr [i].r_addend = end_val; - ++i; - g_assert (i <= acfg->num_relocs); +#if defined(TARGET_X86) + if (index == 0) { + /* It is filled up during loading by the AOT loader. */ + emit_zero_bytes (acfg, 16); + } else { + /* Need to make sure this is 9 bytes long */ + emit_byte (acfg, '\xe9'); + emit_symbol_diff (acfg, "plt", ".", -4); + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); } - } - return rr; -} +#elif defined(TARGET_AMD64) + /* + * We can't emit jumps because they are 32 bits only so they can't be patched. + * So we make indirect calls through GOT entries which are patched by the AOT + * loader to point to .Lpd entries. + * An x86_64 plt entry is 10 bytes long, init_plt () depends on this. + */ + /* jmpq *(%rip) */ + emit_byte (acfg, '\xff'); + emit_byte (acfg, '\x25'); + emit_symbol_diff (acfg, "got", ".", ((acfg->plt_got_offset_base + index) * sizeof (gpointer)) -4); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); +#elif defined(TARGET_ARM) + guint8 buf [256]; + guint8 *code; -#else /* USE_ELF_RELA */ + /* FIXME: + * - optimize OP_AOTCONST implementation + * - optimize the PLT entries + * - optimize SWITCH AOT implementation + * - implement IMT support + */ + code = buf; + if (acfg->use_bin_writer) { + /* We only emit 1 relocation since we implement it ourselves anyway */ + img_writer_emit_reloc (acfg->w, R_ARM_ALU_PC_G0_NC, "got", ((acfg->plt_got_offset_base + index) * sizeof (gpointer)) - 8); + /* FIXME: A 2 instruction encoding is sufficient in most cases */ + ARM_ADD_REG_IMM (code, ARMREG_IP, ARMREG_PC, 0, 0); + ARM_ADD_REG_IMM (code, ARMREG_IP, ARMREG_IP, 0, 0); + ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0); + emit_bytes (acfg, buf, code - buf); + /* FIXME: Get rid of this */ + emit_symbol_diff (acfg, "got", ".", ((acfg->plt_got_offset_base + index) * sizeof (gpointer))); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); + } else { + ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4); + ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP); + ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0); + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, "got", ".", ((acfg->plt_got_offset_base + index) * sizeof (gpointer))); + /* Used by mono_aot_get_plt_info_offset */ + emit_int32 (acfg, acfg->plt_got_info_offsets [index]); + } +#else + g_assert_not_reached (); +#endif +} +/* + * arch_emit_specific_trampoline: + * + * Emit code for a specific trampoline. OFFSET is the offset of the first of + * two GOT slots which contain the generic trampoline address and the trampoline + * argument. TRAMP_SIZE is set to the size of the emitted trampoline. + */ static void -do_reloc (MonoAotCompile *acfg, BinReloc *reloc, guint8 *data, gssize addr) +arch_emit_specific_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) { -#ifdef __arm__ /* - * We use the official ARM relocation types, but implement only the stuff actually - * needed by the code we generate. + * The trampolines created here are variations of the specific + * trampolines created in mono_arch_create_specific_trampoline (). The + * differences are: + * - the generic trampoline address is taken from a got slot. + * - the offset of the got slot where the trampoline argument is stored + * is embedded in the instruction stream, and the generic trampoline + * can load the argument by loading the offset, adding it to the + * address of the trampoline to get the address of the got slot, and + * loading the argument from there. + * - all the trampolines should be of the same length. */ - switch (reloc->reloc_type) { - case R_ARM_CALL: { - guint32 *code = (guint32*)(gpointer)data; - guint32 ins = *code; - int diff = addr; - - /* bl */ - g_assert (data [3] == 0xeb); - if (diff >= 0 && diff <= 33554431) { - diff >>= 2; - ins = (ins & 0xff000000) | diff; - *code = ins; - } else if (diff <= 0 && diff >= -33554432) { - diff >>= 2; - ins = (ins & 0xff000000) | (diff & ~0xff000000); - *code = ins; - } else { - g_assert_not_reached (); - } - break; - } - case R_ARM_ALU_PC_G0_NC: { - /* Generated by emit_plt () */ - guint8 *code = data; - guint32 val = addr; - - g_assert (val <= 0xffff); - ARM_ADD_REG_IMM (code, ARMREG_IP, ARMREG_PC, 0, 0); - ARM_ADD_REG_IMM (code, ARMREG_IP, ARMREG_IP, (val & 0xFF00) >> 8, 24); - ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, val & 0xFF); - break; - } - default: - g_assert_not_reached (); - } +#if defined(TARGET_AMD64) + /* This should be exactly 16 bytes long */ + *tramp_size = 16; + /* call *(%rip) */ + emit_byte (acfg, '\x41'); + emit_byte (acfg, '\xff'); + emit_byte (acfg, '\x15'); + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4); + /* This should be relative to the start of the trampoline */ + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4 + 19); + emit_zero_bytes (acfg, 5); +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + + /* This should be exactly 28 bytes long */ + *tramp_size = 28; + code = buf; + ARM_PUSH (code, 0x5fff); + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); + /* Load the value from the GOT */ + ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_PC, ARMREG_R1); + /* Branch to it */ + ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC); + ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1); + + g_assert (code - buf == 20); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4 + 8); + emit_symbol_diff (acfg, "got", ".", ((offset + 1) * sizeof (gpointer)) - 4 + 8); #else g_assert_not_reached (); #endif } -static ElfReloc* -resolve_relocations (MonoAotCompile *acfg) +/* + * arch_emit_unbox_trampoline: + * + * Emit code for the unbox trampoline for METHOD used in the full-aot case. + * CALL_TARGET is the symbol pointing to the native code of METHOD. + */ +static void +arch_emit_unbox_trampoline (MonoAotCompile *acfg, MonoMethod *method, MonoGenericSharingContext *gsctx, const char *call_target) { - BinReloc *reloc; - guint8 *data; - gsize end_val, start_val; - ElfReloc *rr; - int i; - gsize vaddr; - - rr = g_new0 (ElfReloc, acfg->num_relocs); - i = 0; - - for (reloc = acfg->relocations; reloc; reloc = reloc->next) { - resolve_reloc (acfg, reloc, &data, &vaddr, &start_val, &end_val); - /* FIXME: little endian */ - if (reloc->reloc_type) { - /* Must be static */ - g_assert (start_val > 0); - do_reloc (acfg, reloc, data, end_val); - } else { - data [0] = end_val; - data [1] = end_val >> 8; - data [2] = end_val >> 16; - data [3] = end_val >> 24; - } - // FIXME: - if (start_val == 0 && reloc->val1 [0] != '.') { - rr [i].r_offset = vaddr; - rr [i].r_info = R_386_RELATIVE; - ++i; - g_assert (i <= acfg->num_relocs); - } - } - return rr; -} +#if defined(TARGET_AMD64) + guint8 buf [32]; + guint8 *code; + int this_reg; -#endif /* USE_ELF_RELA */ + this_reg = mono_arch_get_this_arg_reg (mono_method_signature (method), gsctx, NULL); + code = buf; + amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject)); -static int -bin_writer_emit_writeout (MonoAotCompile *acfg) -{ - char *outfile_name, *tmp_outfile_name; - FILE *file; - ElfHeader header; - ElfProgHeader progh [3]; - ElfSectHeader secth [SECT_NUM]; -#ifdef USE_ELF_RELA - ElfRelocA *relocs; -#else - ElfReloc *relocs; -#endif - ElfStrTable str_table = {NULL, NULL}; - ElfStrTable sh_str_table = {NULL, NULL}; - ElfStrTable dyn_str_table = {NULL, NULL}; - BinSection* all_sections [32]; - BinSection* sections [SECT_NUM]; - ElfSymbol *dynsym; - ElfSymbol *symtab; - ElfDynamic dynamic [14]; - int *hash; - int i, num_sections, file_offset, virt_offset, size, num_symtab; - int num_local_syms; - - g_assert (!acfg->aot_opts.asm_only); + emit_bytes (acfg, buf, code - buf); + /* jump */ + emit_byte (acfg, '\xe9'); + emit_symbol_diff (acfg, call_target, ".", -4); +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; + int this_pos = 0; - if (acfg->aot_opts.outfile) - outfile_name = g_strdup_printf ("%s", acfg->aot_opts.outfile); - else - outfile_name = g_strdup_printf ("%s%s", acfg->image->name, SHARED_EXT); + code = buf; - tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name); + if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret)) + this_pos = 1; - unlink (tmp_outfile_name); - file = fopen (tmp_outfile_name, "w"); - g_assert (file); - - /* Section headers */ - memset (§h, 0, sizeof (secth)); - memset (&dynamic, 0, sizeof (dynamic)); - memset (&header, 0, sizeof (header)); - - for (i = 1; i < SECT_NUM; ++i) { - secth [i].sh_name = str_table_add (&sh_str_table, section_info [i].name); - secth [i].sh_type = section_info [i].type; - secth [i].sh_addralign = section_info [i].align; - secth [i].sh_flags = section_info [i].flags; - secth [i].sh_entsize = section_info [i].esize; - } - secth [SECT_DYNSYM].sh_info = SIZEOF_VOID_P == 4 ? 4 : 2; - secth [SECT_SYMTAB].sh_info = SIZEOF_VOID_P == 4 ? 20 : 17; - secth [SECT_HASH].sh_link = SECT_DYNSYM; - secth [SECT_DYNSYM].sh_link = SECT_DYNSTR; - secth [SECT_REL_DYN].sh_link = SECT_DYNSYM; - secth [SECT_RELA_DYN].sh_link = SECT_DYNSYM; - secth [SECT_DYNAMIC].sh_link = SECT_DYNSTR; - secth [SECT_SYMTAB].sh_link = SECT_STRTAB; - - num_sections = collect_sections (acfg, secth, all_sections, 8); - hash = build_hash (acfg, num_sections, &dyn_str_table); - num_symtab = hash [1]; /* FIXME */ - g_print ("num_sections: %d\n", num_sections); - g_print ("dynsym: %d, dynstr size: %d\n", hash [1], (int)dyn_str_table.data->len); - for (i = 0; i < num_sections; ++i) { - g_print ("section %s, size: %d, %x\n", all_sections [i]->name, all_sections [i]->cur_offset, all_sections [i]->cur_offset); - } + ARM_ADD_REG_IMM8 (code, this_pos, this_pos, sizeof (MonoObject)); - /* Associate the bin sections with the ELF sections */ - memset (sections, 0, sizeof (sections)); - for (i = 0; i < num_sections; ++i) { - BinSection *sect = all_sections [i]; - int j; + emit_bytes (acfg, buf, code - buf); + /* jump to method */ + if (acfg->use_bin_writer) { + guint8 buf [4]; + guint8 *code; - for (j = 0; j < SECT_NUM; ++j) { - if (strcmp (sect->name, section_info [j].name) == 0) { - sect->shidx = j; - break; - } - } + code = buf; + ARM_B (code, 0); - sections [all_sections [i]->shidx] = sect; + img_writer_emit_reloc (acfg->w, R_ARM_JUMP24, call_target, -8); + emit_bytes (acfg, buf, 4); + } else { + fprintf (acfg->fp, "\n\tb %s\n", call_target); } - - /* at this point we know where in the file the first segment sections go */ - dynsym = collect_syms (acfg, hash, &dyn_str_table, NULL, NULL); - num_local_syms = hash [1]; - symtab = collect_syms (acfg, NULL, &str_table, secth, &num_local_syms); - - file_offset = virt_offset = sizeof (header) + sizeof (progh); - secth [SECT_HASH].sh_addr = secth [SECT_HASH].sh_offset = file_offset; - size = sizeof (int) * (2 + hash [0] + hash [1]); - virt_offset = (file_offset += size); - secth [SECT_HASH].sh_size = size; - secth [SECT_DYNSYM].sh_addr = secth [SECT_DYNSYM].sh_offset = file_offset; - size = sizeof (ElfSymbol) * hash [1]; - virt_offset = (file_offset += size); - secth [SECT_DYNSYM].sh_size = size; - secth [SECT_DYNSTR].sh_addr = secth [SECT_DYNSTR].sh_offset = file_offset; - size = dyn_str_table.data->len; - virt_offset = (file_offset += size); - secth [SECT_DYNSTR].sh_size = size; - file_offset += 4-1; - file_offset &= ~(4-1); - secth [SECT_REL_DYN].sh_addr = secth [SECT_REL_DYN].sh_offset = file_offset; -#ifndef USE_ELF_RELA - size = sizeof (ElfReloc) * acfg->num_relocs; -#else - size = 0; -#endif - virt_offset = (file_offset += size); - secth [SECT_REL_DYN].sh_size = size; - secth [SECT_RELA_DYN].sh_addr = secth [SECT_RELA_DYN].sh_offset = file_offset; -#ifdef USE_ELF_RELA - size = sizeof (ElfRelocA) * acfg->num_relocs; #else - size = 0; + g_assert_not_reached (); #endif - virt_offset = (file_offset += size); - secth [SECT_RELA_DYN].sh_size = size; - - file_offset = ALIGN_TO (file_offset, secth [SECT_TEXT].sh_addralign); - virt_offset = file_offset; - secth [SECT_TEXT].sh_addr = secth [SECT_TEXT].sh_offset = file_offset; - if (sections [SECT_TEXT]) { - size = sections [SECT_TEXT]->cur_offset; - secth [SECT_TEXT].sh_size = size; - file_offset += size; - } - - file_offset = ALIGN_TO (file_offset, secth [SECT_DYNAMIC].sh_addralign); - virt_offset = file_offset; - - /* .dynamic, .got.plt, .data, .bss here */ - /* Have to increase the virt offset since these go to a separate segment */ - virt_offset += PAGESIZE; - secth [SECT_DYNAMIC].sh_addr = virt_offset; - secth [SECT_DYNAMIC].sh_offset = file_offset; - size = sizeof (dynamic); - secth [SECT_DYNAMIC].sh_size = size; - file_offset += size; - virt_offset += size; - - file_offset = ALIGN_TO (file_offset, secth [SECT_GOT_PLT].sh_addralign); - virt_offset = ALIGN_TO (virt_offset, secth [SECT_GOT_PLT].sh_addralign); - secth [SECT_GOT_PLT].sh_addr = virt_offset; - secth [SECT_GOT_PLT].sh_offset = file_offset; - size = 12; - secth [SECT_GOT_PLT].sh_size = size; - file_offset += size; - virt_offset += size; - - file_offset = ALIGN_TO (file_offset, secth [SECT_DATA].sh_addralign); - virt_offset = ALIGN_TO (virt_offset, secth [SECT_DATA].sh_addralign); - secth [SECT_DATA].sh_addr = virt_offset; - secth [SECT_DATA].sh_offset = file_offset; - if (sections [SECT_DATA]) { - size = sections [SECT_DATA]->cur_offset; - secth [SECT_DATA].sh_size = size; - file_offset += size; - virt_offset += size; - } +} - file_offset = ALIGN_TO (file_offset, secth [SECT_BSS].sh_addralign); - virt_offset = ALIGN_TO (virt_offset, secth [SECT_BSS].sh_addralign); - secth [SECT_BSS].sh_addr = virt_offset; - secth [SECT_BSS].sh_offset = file_offset; - if (sections [SECT_BSS]) { - size = sections [SECT_BSS]->cur_offset; - secth [SECT_BSS].sh_size = size; - } +/* + * arch_emit_static_rgctx_trampoline: + * + * Emit code for a static rgctx trampoline. OFFSET is the offset of the first of + * two GOT slots which contain the rgctx argument, and the method to jump to. + * TRAMP_SIZE is set to the size of the emitted trampoline. + * These kinds of trampolines cannot be enumerated statically, since there could + * be one trampoline per method instantiation, so we emit the same code for all + * trampolines, and parameterize them using two GOT slots. + */ +static void +arch_emit_static_rgctx_trampoline (MonoAotCompile *acfg, int offset, int *tramp_size) +{ +#if defined(TARGET_AMD64) + /* This should be exactly 13 bytes long */ + *tramp_size = 13; + + /* mov (%rip), %r10 */ + emit_byte (acfg, '\x4d'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x15'); + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4); + + /* jmp *(%rip) */ + emit_byte (acfg, '\xff'); + emit_byte (acfg, '\x25'); + emit_symbol_diff (acfg, "got", ".", ((offset + 1) * sizeof (gpointer)) - 4); +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code; - /* virtual doesn't matter anymore */ - file_offset = ALIGN_TO (file_offset, secth [SECT_DEBUG_FRAME].sh_addralign); - secth [SECT_DEBUG_FRAME].sh_offset = file_offset; - if (sections [SECT_DEBUG_FRAME]) - size = sections [SECT_DEBUG_FRAME]->cur_offset; - else - size = 0; - secth [SECT_DEBUG_FRAME].sh_size = size; - file_offset += size; - - secth [SECT_DEBUG_INFO].sh_offset = file_offset; - if (sections [SECT_DEBUG_INFO]) - size = sections [SECT_DEBUG_INFO]->cur_offset; - else - size = 0; - secth [SECT_DEBUG_INFO].sh_size = size; - file_offset += size; - - secth [SECT_DEBUG_ABBREV].sh_offset = file_offset; - if (sections [SECT_DEBUG_ABBREV]) - size = sections [SECT_DEBUG_ABBREV]->cur_offset; - else - size = 0; - secth [SECT_DEBUG_ABBREV].sh_size = size; - file_offset += size; - - secth [SECT_DEBUG_LINE].sh_offset = file_offset; - if (sections [SECT_DEBUG_LINE]) - size = sections [SECT_DEBUG_LINE]->cur_offset; - else - size = 0; - secth [SECT_DEBUG_LINE].sh_size = size; - file_offset += size; - - secth [SECT_DEBUG_LOC].sh_offset = file_offset; - if (sections [SECT_DEBUG_LOC]) - size = sections [SECT_DEBUG_LOC]->cur_offset; - else - size = 0; - secth [SECT_DEBUG_LOC].sh_size = size; - file_offset += size; - - file_offset = ALIGN_TO (file_offset, secth [SECT_SHSTRTAB].sh_addralign); - secth [SECT_SHSTRTAB].sh_offset = file_offset; - size = sh_str_table.data->len; - secth [SECT_SHSTRTAB].sh_size = size; - file_offset += size; - - file_offset = ALIGN_TO (file_offset, secth [SECT_SYMTAB].sh_addralign); - secth [SECT_SYMTAB].sh_offset = file_offset; - size = sizeof (ElfSymbol) * num_local_syms; - secth [SECT_SYMTAB].sh_size = size; - file_offset += size; - - file_offset = ALIGN_TO (file_offset, secth [SECT_STRTAB].sh_addralign); - secth [SECT_STRTAB].sh_offset = file_offset; - size = str_table.data->len; - secth [SECT_STRTAB].sh_size = size; - file_offset += size; - - file_offset += 4-1; - file_offset &= ~(4-1); - - header.e_ident [EI_MAG0] = ELFMAG0; - header.e_ident [EI_MAG1] = ELFMAG1; - header.e_ident [EI_MAG2] = ELFMAG2; - header.e_ident [EI_MAG3] = ELFMAG3; - header.e_ident [EI_CLASS] = SIZEOF_VOID_P == 4 ? ELFCLASS32 : ELFCLASS64; - header.e_ident [EI_DATA] = ELFDATA2LSB; - header.e_ident [EI_VERSION] = EV_CURRENT; - header.e_ident [EI_OSABI] = ELFOSABI_NONE; - header.e_ident [EI_ABIVERSION] = 0; - for (i = EI_PAD; i < EI_NIDENT; ++i) - header.e_ident [i] = 0; - - header.e_type = ET_DYN; -#if defined(__i386__) - header.e_machine = EM_386; -#elif defined(__x86_64__) - header.e_machine = EM_X86_64; -#elif defined(__arm__) - header.e_machine = EM_ARM; + /* This should be exactly 24 bytes long */ + *tramp_size = 24; + code = buf; + /* Load rgctx value */ + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); + ARM_LDR_REG_REG (code, MONO_ARCH_RGCTX_REG, ARMREG_PC, ARMREG_R1); + /* Load branch addr + branch */ + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 4); + ARM_LDR_REG_REG (code, ARMREG_PC, ARMREG_PC, ARMREG_R1); + + g_assert (code - buf == 16); + + /* Emit it */ + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4 + 8); + emit_symbol_diff (acfg, "got", ".", ((offset + 1) * sizeof (gpointer)) - 4 + 4); #else g_assert_not_reached (); #endif - header.e_version = 1; - - header.e_phoff = sizeof (header); - header.e_ehsize = sizeof (header); - header.e_phentsize = sizeof (ElfProgHeader); - header.e_phnum = 3; - header.e_entry = secth [SECT_TEXT].sh_addr; - header.e_shstrndx = SECT_SHSTRTAB; - header.e_shentsize = sizeof (ElfSectHeader); - header.e_shnum = SECT_NUM; - header.e_shoff = file_offset; - - /* dynamic data */ - i = 0; - dynamic [i].d_tag = DT_HASH; - dynamic [i].d_un.d_val = secth [SECT_HASH].sh_offset; - ++i; - dynamic [i].d_tag = DT_STRTAB; - dynamic [i].d_un.d_val = secth [SECT_DYNSTR].sh_offset; - ++i; - dynamic [i].d_tag = DT_SYMTAB; - dynamic [i].d_un.d_val = secth [SECT_DYNSYM].sh_offset; - ++i; - dynamic [i].d_tag = DT_STRSZ; - dynamic [i].d_un.d_val = dyn_str_table.data->len; - ++i; - dynamic [i].d_tag = DT_SYMENT; - dynamic [i].d_un.d_val = sizeof (ElfSymbol); - ++i; -#ifdef USE_ELF_RELA - dynamic [i].d_tag = DT_RELA; - dynamic [i].d_un.d_val = secth [SECT_RELA_DYN].sh_offset; - ++i; - dynamic [i].d_tag = DT_RELASZ; - dynamic [i].d_un.d_val = secth [SECT_RELA_DYN].sh_size; - ++i; - dynamic [i].d_tag = DT_RELAENT; - dynamic [i].d_un.d_val = sizeof (ElfRelocA); - ++i; -#else - dynamic [i].d_tag = DT_REL; - dynamic [i].d_un.d_val = secth [SECT_REL_DYN].sh_offset; - ++i; - dynamic [i].d_tag = DT_RELSZ; - dynamic [i].d_un.d_val = secth [SECT_REL_DYN].sh_size; - ++i; - dynamic [i].d_tag = DT_RELENT; - dynamic [i].d_un.d_val = sizeof (ElfReloc); - ++i; -#endif - dynamic [i].d_tag = DT_RELCOUNT; - dynamic [i].d_un.d_val = acfg->num_relocs; - ++i; - - /* Program header */ - memset (&progh, 0, sizeof (progh)); - progh [0].p_type = PT_LOAD; - progh [0].p_filesz = progh [0].p_memsz = secth [SECT_DYNAMIC].sh_offset; - progh [0].p_align = 4096; - progh [0].p_flags = 5; - - progh [1].p_type = PT_LOAD; - progh [1].p_offset = secth [SECT_DYNAMIC].sh_offset; - progh [1].p_vaddr = progh [1].p_paddr = secth [SECT_DYNAMIC].sh_addr; - progh [1].p_filesz = secth [SECT_BSS].sh_offset - secth [SECT_DYNAMIC].sh_offset; - progh [1].p_memsz = secth [SECT_BSS].sh_addr + secth [SECT_BSS].sh_size - secth [SECT_DYNAMIC].sh_addr; - progh [1].p_align = 4096; - progh [1].p_flags = 6; - - progh [2].p_type = PT_DYNAMIC; - progh [2].p_offset = secth [SECT_DYNAMIC].sh_offset; - progh [2].p_vaddr = progh [2].p_paddr = secth [SECT_DYNAMIC].sh_addr; - progh [2].p_filesz = progh [2].p_memsz = secth [SECT_DYNAMIC].sh_size; - progh [2].p_align = SIZEOF_VOID_P; - progh [2].p_flags = 6; - - /* Compute the addresses of the bin sections, so relocation can be done */ - for (i = 0; i < SECT_NUM; ++i) { - if (sections [i]) { - sections [i]->file_offset = secth [i].sh_offset; - sections [i]->virt_offset = secth [i].sh_addr; - } - } - - reloc_symbols (acfg, dynsym, secth, &dyn_str_table, TRUE); - reloc_symbols (acfg, symtab, secth, &str_table, FALSE); - relocs = resolve_relocations (acfg); - - fwrite (&header, sizeof (header), 1, file); - fwrite (&progh, sizeof (progh), 1, file); - fwrite (hash, sizeof (int) * (hash [0] + hash [1] + 2), 1, file); - fwrite (dynsym, sizeof (ElfSymbol) * hash [1], 1, file); - fwrite (dyn_str_table.data->str, dyn_str_table.data->len, 1, file); - /* .rel.dyn */ - fseek (file, secth [SECT_REL_DYN].sh_offset, SEEK_SET); - fwrite (relocs, sizeof (ElfReloc), acfg->num_relocs, file); - - /* .rela.dyn */ - fseek (file, secth [SECT_RELA_DYN].sh_offset, SEEK_SET); - fwrite (relocs, secth [SECT_RELA_DYN].sh_size, 1, file); - - /* .text */ - if (sections [SECT_TEXT]) { - fseek (file, secth [SECT_TEXT].sh_offset, SEEK_SET); - fwrite (sections [SECT_TEXT]->data, sections [SECT_TEXT]->cur_offset, 1, file); - } - /* .dynamic */ - fwrite (dynamic, sizeof (dynamic), 1, file); +} - /* .got.plt */ - size = secth [SECT_DYNAMIC].sh_addr; - fwrite (&size, sizeof (size), 1, file); +/* + * arch_emit_imt_thunk: + * + * Emit an IMT thunk usable in full-aot mode. The thunk uses 1 got slot which + * points to an array of pointer pairs. The pairs of the form [key, ptr], where + * key is the IMT key, and ptr holds the address of a memory location holding + * the address to branch to if the IMT arg matches the key. The array is + * terminated by a pair whose key is NULL, and whose ptr is the address of the + * fail_tramp. + * TRAMP_SIZE is set to the size of the emitted trampoline. + */ +static void +arch_emit_imt_thunk (MonoAotCompile *acfg, int offset, int *tramp_size) +{ +#if defined(TARGET_AMD64) + guint8 *buf, *code; + guint8 *labels [3]; - /* .data */ - if (sections [SECT_DATA]) { - fseek (file, secth [SECT_DATA].sh_offset, SEEK_SET); - fwrite (sections [SECT_DATA]->data, sections [SECT_DATA]->cur_offset, 1, file); - } + code = buf = g_malloc (256); - fseek (file, secth [SECT_DEBUG_FRAME].sh_offset, SEEK_SET); - if (sections [SECT_DEBUG_FRAME]) - fwrite (sections [SECT_DEBUG_FRAME]->data, sections [SECT_DEBUG_FRAME]->cur_offset, 1, file); - fseek (file, secth [SECT_DEBUG_INFO].sh_offset, SEEK_SET); - if (sections [SECT_DEBUG_INFO]) - fwrite (sections [SECT_DEBUG_INFO]->data, sections [SECT_DEBUG_INFO]->cur_offset, 1, file); - fseek (file, secth [SECT_DEBUG_ABBREV].sh_offset, SEEK_SET); - if (sections [SECT_DEBUG_ABBREV]) - fwrite (sections [SECT_DEBUG_ABBREV]->data, sections [SECT_DEBUG_ABBREV]->cur_offset, 1, file); - fseek (file, secth [SECT_DEBUG_LINE].sh_offset, SEEK_SET); - if (sections [SECT_DEBUG_LINE]) - fwrite (sections [SECT_DEBUG_LINE]->data, sections [SECT_DEBUG_LINE]->cur_offset, 1, file); - fseek (file, secth [SECT_DEBUG_LINE].sh_offset, SEEK_SET); - if (sections [SECT_DEBUG_LOC]) - fwrite (sections [SECT_DEBUG_LOC]->data, sections [SECT_DEBUG_LOC]->cur_offset, 1, file); - fseek (file, secth [SECT_SHSTRTAB].sh_offset, SEEK_SET); - fwrite (sh_str_table.data->str, sh_str_table.data->len, 1, file); - fseek (file, secth [SECT_SYMTAB].sh_offset, SEEK_SET); - fwrite (symtab, sizeof (ElfSymbol) * num_local_syms, 1, file); - fseek (file, secth [SECT_STRTAB].sh_offset, SEEK_SET); - fwrite (str_table.data->str, str_table.data->len, 1, file); - /*g_print ("file_offset %d vs %d\n", file_offset, ftell (file));*/ - /*g_assert (file_offset >= ftell (file));*/ - fseek (file, file_offset, SEEK_SET); - fwrite (§h, sizeof (secth), 1, file); - fclose (file); - rename (tmp_outfile_name, outfile_name); + /* FIXME: Optimize this, i.e. use binary search etc. */ + /* Maybe move the body into a separate function (slower, but much smaller) */ - g_free (tmp_outfile_name); - g_free (outfile_name); + /* R10 is a free register */ - return 0; -} + labels [0] = code; + amd64_alu_membase_imm (code, X86_CMP, AMD64_R10, 0, 0); + labels [1] = code; + amd64_branch8 (code, X86_CC_Z, FALSE, 0); -#endif /* USE_ELF_WRITER */ + /* Check key */ + amd64_alu_membase_reg (code, X86_CMP, AMD64_R10, 0, MONO_ARCH_IMT_REG); + labels [2] = code; + amd64_branch8 (code, X86_CC_Z, FALSE, 0); -#endif /* USE_BIN_WRITER */ + /* Loop footer */ + amd64_alu_reg_imm (code, X86_ADD, AMD64_R10, 2 * sizeof (gpointer)); + amd64_jump_code (code, labels [0]); -/* ASM WRITER */ + /* Match */ + mono_amd64_patch (labels [2], code); + amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, sizeof (gpointer), 8); + amd64_jump_membase (code, AMD64_R10, 0); -static void -asm_writer_emit_start (MonoAotCompile *acfg) -{ - if (acfg->aot_opts.asm_only) { - if (acfg->aot_opts.outfile) - acfg->tmpfname = g_strdup_printf ("%s", acfg->aot_opts.outfile); - else - acfg->tmpfname = g_strdup_printf ("%s.s", acfg->image->name); - acfg->fp = fopen (acfg->tmpfname, "w+"); - } else { - int i = g_file_open_tmp ("mono_aot_XXXXXX", &acfg->tmpfname, NULL); - acfg->fp = fdopen (i, "w+"); - } - g_assert (acfg->fp); -} + /* No match */ + /* FIXME: */ + mono_amd64_patch (labels [1], code); + x86_breakpoint (code); -static void -asm_writer_emit_unset_mode (MonoAotCompile *acfg) -{ - if (acfg->mode == EMIT_NONE) - return; - fprintf (acfg->fp, "\n"); - acfg->mode = EMIT_NONE; -} + /* mov (%rip), %r10 */ + emit_byte (acfg, '\x4d'); + emit_byte (acfg, '\x8b'); + emit_byte (acfg, '\x15'); + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4); -static void -asm_writer_emit_section_change (MonoAotCompile *acfg, const char *section_name, int subsection_index) -{ - asm_writer_emit_unset_mode (acfg); -#if defined(PLATFORM_WIN32) - fprintf (acfg->fp, ".section %s\n", section_name); -#elif defined(__MACH__) - if (strcmp(section_name, ".bss") == 0) - fprintf (acfg->fp, "%s\n", ".data"); - else - fprintf (acfg->fp, "%s\n", section_name); -#elif defined(sparc) - /* For solaris as, GNU as should accept the same */ - fprintf (acfg->fp, ".section \"%s\"\n", section_name); + emit_bytes (acfg, buf, code - buf); + + *tramp_size = code - buf + 7; +#elif defined(TARGET_ARM) + guint8 buf [128]; + guint8 *code, *code2, *labels [16]; + + code = buf; + + /* The IMT method is in v5 */ + + /* Only IP is available, but we need at least two free registers */ + ARM_PUSH1 (code, ARMREG_R1); + labels [0] = code; + /* Load the parameter from the GOT */ + ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0); + ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP); + + labels [1] = code; + ARM_LDR_IMM (code, ARMREG_R1, ARMREG_IP, 0); + ARM_CMP_REG_REG (code, ARMREG_R1, ARMREG_V5); + labels [2] = code; + ARM_B_COND (code, ARMCOND_EQ, 0); + + /* End-of-loop check */ + ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0); + labels [3] = code; + ARM_B_COND (code, ARMCOND_EQ, 0); + + /* Loop footer */ + ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, sizeof (gpointer) * 2); + labels [4] = code; + ARM_B (code, 0); + arm_patch (labels [4], labels [1]); + + /* Match */ + arm_patch (labels [2], code); + ARM_POP1 (code, ARMREG_R1); + ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 4); + ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0); + + /* No match */ + arm_patch (labels [3], code); + ARM_DBRK (code); + + /* Fixup offset */ + code2 = labels [0]; + ARM_LDR_IMM (code2, ARMREG_IP, ARMREG_PC, (code - (labels [0] + 8))); + + emit_bytes (acfg, buf, code - buf); + emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) + (code - (labels [0] + 8)) - 4); + + *tramp_size = code - buf + 4; #else - if (!strcmp (section_name, ".text") || !strcmp (section_name, ".data") || !strcmp (section_name, ".bss")) { - fprintf (acfg->fp, "%s %d\n", section_name, subsection_index); - } else { - fprintf (acfg->fp, ".section \"%s\"\n", section_name); - fprintf (acfg->fp, ".subsection %d\n", subsection_index); - } + g_assert_not_reached (); #endif } -static void -asm_writer_emit_symbol_type (MonoAotCompile *acfg, const char *name, gboolean func) +/* + * arch_get_cie_program: + * + * Get the unwind bytecode for the DWARF CIE. + */ +static GSList* +arch_get_cie_program (void) { - const char *stype; - - if (func) - stype = "function"; - else - stype = "object"; - - asm_writer_emit_unset_mode (acfg); -#if defined(__MACH__) +#ifdef TARGET_AMD64 + GSList *l = NULL; -#elif defined(sparc) || defined(__arm__) - fprintf (acfg->fp, "\t.type %s,#%s\n", name, stype); -#elif defined(PLATFORM_WIN32) + mono_add_unwind_op_def_cfa (l, (guint8*)NULL, (guint8*)NULL, AMD64_RSP, 8); + mono_add_unwind_op_offset (l, (guint8*)NULL, (guint8*)NULL, AMD64_RIP, -8); -#elif defined(__x86_64__) || defined(__i386__) - fprintf (acfg->fp, "\t.type %s,@%s\n", name, stype); + return l; #else - fprintf (acfg->fp, "\t.type %s,@%s\n", name, stype); + return NULL; #endif } -static void -asm_writer_emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func) +/* END OF ARCH SPECIFIC CODE */ + +static guint32 +mono_get_field_token (MonoClassField *field) { - asm_writer_emit_unset_mode (acfg); -#if (defined(__ppc__) && defined(__MACH__)) || defined(PLATFORM_WIN32) - // mach-o always uses a '_' prefix. - fprintf (acfg->fp, "\t.globl _%s\n", name); -#else - fprintf (acfg->fp, "\t.globl %s\n", name); -#endif + MonoClass *klass = field->parent; + int i; + + for (i = 0; i < klass->field.count; ++i) { + if (field == &klass->fields [i]) + return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i); + } - asm_writer_emit_symbol_type (acfg, name, func); + g_assert_not_reached (); + return 0; } -static void -asm_writer_emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func) +static inline void +encode_value (gint32 value, guint8 *buf, guint8 **endbuf) { - asm_writer_emit_unset_mode (acfg); + guint8 *p = buf; - fprintf (acfg->fp, "\t.local %s\n", name); + //printf ("ENCODE: %d 0x%x.\n", value, value); - asm_writer_emit_symbol_type (acfg, name, func); + /* + * Same encoding as the one used in the metadata, extended to handle values + * greater than 0x1fffffff. + */ + if ((value >= 0) && (value <= 127)) + *p++ = value; + else if ((value >= 0) && (value <= 16383)) { + p [0] = 0x80 | (value >> 8); + p [1] = value & 0xff; + p += 2; + } else if ((value >= 0) && (value <= 0x1fffffff)) { + p [0] = (value >> 24) | 0xc0; + p [1] = (value >> 16) & 0xff; + p [2] = (value >> 8) & 0xff; + p [3] = value & 0xff; + p += 4; + } + else { + p [0] = 0xff; + p [1] = (value >> 24) & 0xff; + p [2] = (value >> 16) & 0xff; + p [3] = (value >> 8) & 0xff; + p [4] = value & 0xff; + p += 5; + } + if (endbuf) + *endbuf = p; } -static void -asm_writer_emit_label (MonoAotCompile *acfg, const char *name) +static guint32 +get_image_index (MonoAotCompile *cfg, MonoImage *image) { - asm_writer_emit_unset_mode (acfg); -#if (defined(__ppc__) && defined(__MACH__)) || defined(PLATFORM_WIN32) - // mach-o always uses a '_' prefix. - fprintf (acfg->fp, "_%s:\n", name); -#else - fprintf (acfg->fp, "%s:\n", name); -#endif + guint32 index; -#if defined(PLATFORM_WIN32) - /* Emit a normal label too */ - fprintf (acfg->fp, "%s:\n", name); -#endif + index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image)); + if (index) + return index - 1; + else { + index = g_hash_table_size (cfg->image_hash); + g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1)); + g_ptr_array_add (cfg->image_table, image); + return index; + } } -static void -asm_writer_emit_string (MonoAotCompile *acfg, const char *value) +static guint32 +find_typespec_for_class (MonoAotCompile *acfg, MonoClass *klass) { - asm_writer_emit_unset_mode (acfg); - fprintf (acfg->fp, "\t%s \"%s\"\n", AS_STRING_DIRECTIVE, value); -} + int i; + MonoClass *k = NULL; -static void -asm_writer_emit_line (MonoAotCompile *acfg) -{ - asm_writer_emit_unset_mode (acfg); - fprintf (acfg->fp, "\n"); -} + /* FIXME: Search referenced images as well */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) { + k = mono_class_get_full (acfg->image, MONO_TOKEN_TYPE_SPEC | (i + 1), NULL); + if (k == klass) + break; + } -static void -asm_writer_emit_alignment (MonoAotCompile *acfg, int size) -{ - asm_writer_emit_unset_mode (acfg); -#if defined(__arm__) - fprintf (acfg->fp, "\t.align %d\n", ilog2 (size)); -#elif defined(__ppc__) && defined(__MACH__) - // the mach-o assembler specifies alignments as powers of 2. - fprintf (acfg->fp, "\t.align %d\t; ilog2\n", ilog2(size)); -#elif defined(__powerpc__) - /* ignore on linux/ppc */ -#else - fprintf (acfg->fp, "\t.align %d\n", size); -#endif + if (i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows) + return MONO_TOKEN_TYPE_SPEC | (i + 1); + else + return 0; } static void -asm_writer_emit_pointer_unaligned (MonoAotCompile *acfg, const char *target) -{ - asm_writer_emit_unset_mode (acfg); - asm_writer_emit_alignment (acfg, sizeof (gpointer)); -#if defined(__x86_64__) - fprintf (acfg->fp, "\t.quad %s\n", target ? target : "0"); -#elif defined(sparc) && SIZEOF_VOID_P == 8 - fprintf (acfg->fp, "\t.xword %s\n", target ? target : "0"); -#else - fprintf (acfg->fp, "\t.long %s\n", target ? target : "0"); -#endif -} - -static void -asm_writer_emit_pointer (MonoAotCompile *acfg, const char *target) -{ - asm_writer_emit_unset_mode (acfg); - asm_writer_emit_alignment (acfg, sizeof (gpointer)); - asm_writer_emit_pointer_unaligned (acfg, target); -} - -static char *byte_to_str; +encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf); +/* + * encode_klass_ref: + * + * Encode a reference to KLASS. We use our home-grown encoding instead of the + * standard metadata encoding. + */ static void -asm_writer_emit_bytes (MonoAotCompile *acfg, const guint8* buf, int size) +encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf) { - int i; - if (acfg->mode != EMIT_BYTE) { - acfg->mode = EMIT_BYTE; - acfg->col_count = 0; - } + guint8 *p = buf; + + if (klass->generic_class) { + guint32 token; + g_assert (klass->type_token); + + /* Find a typespec for a class if possible */ + token = find_typespec_for_class (acfg, klass); + if (token) { + encode_value (token, p, &p); + encode_value (get_image_index (acfg, acfg->image), p, &p); + } else { + MonoClass *gclass = klass->generic_class->container_class; + MonoGenericInst *inst = klass->generic_class->context.class_inst; + int i; - if (byte_to_str == NULL) { - byte_to_str = g_new0 (char, 256 * 8); - for (i = 0; i < 256; ++i) { - sprintf (byte_to_str + (i * 8), ",%d", i); + /* Encode it ourselves */ + /* Marker */ + encode_value (MONO_TOKEN_TYPE_SPEC, p, &p); + encode_value (MONO_TYPE_GENERICINST, p, &p); + encode_klass_ref (acfg, gclass, p, &p); + encode_value (inst->type_argc, p, &p); + for (i = 0; i < inst->type_argc; ++i) + encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); } - } + } else if (klass->type_token) { + g_assert (mono_metadata_token_code (klass->type_token) == MONO_TOKEN_TYPE_DEF); + encode_value (klass->type_token - MONO_TOKEN_TYPE_DEF, p, &p); + encode_value (get_image_index (acfg, klass->image), p, &p); + } else if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) { + MonoGenericContainer *container = mono_type_get_generic_param_owner (&klass->byval_arg); + g_assert (container); + + /* Marker */ + encode_value (MONO_TOKEN_TYPE_SPEC, p, &p); + encode_value (klass->byval_arg.type, p, &p); - for (i = 0; i < size; ++i, ++acfg->col_count) { - if ((acfg->col_count % 32) == 0) - fprintf (acfg->fp, "\n\t.byte %d", buf [i]); + encode_value (mono_type_get_generic_param_num (&klass->byval_arg), p, &p); + + encode_value (container->is_method, p, &p); + if (container->is_method) + encode_method_ref (acfg, container->owner.method, p, &p); else - fputs (byte_to_str + (buf [i] * 8), acfg->fp); + encode_klass_ref (acfg, container->owner.klass, p, &p); + } else { + /* Array class */ + g_assert (klass->rank > 0); + encode_value (MONO_TOKEN_TYPE_DEF, p, &p); + encode_value (get_image_index (acfg, klass->image), p, &p); + encode_value (klass->rank, p, &p); + encode_klass_ref (acfg, klass->element_class, p, &p); } + *endbuf = p; } -static inline void -asm_writer_emit_int16 (MonoAotCompile *acfg, int value) +static void +encode_field_info (MonoAotCompile *cfg, MonoClassField *field, guint8 *buf, guint8 **endbuf) { - if (acfg->mode != EMIT_WORD) { - acfg->mode = EMIT_WORD; - acfg->col_count = 0; - } - if ((acfg->col_count++ % 8) == 0) -#if defined(__MACH__) - fprintf (acfg->fp, "\n\t.short "); -#elif defined(__arm__) - /* FIXME: Use .hword on other archs as well */ - fprintf (acfg->fp, "\n\t.hword "); -#else - fprintf (acfg->fp, "\n\t.word "); -#endif - else - fprintf (acfg->fp, ", "); - fprintf (acfg->fp, "%d", value); -} + guint32 token = mono_get_field_token (field); + guint8 *p = buf; -static inline void -asm_writer_emit_int32 (MonoAotCompile *acfg, int value) -{ - if (acfg->mode != EMIT_LONG) { - acfg->mode = EMIT_LONG; - acfg->col_count = 0; - } - if ((acfg->col_count++ % 8) == 0) - fprintf (acfg->fp, "\n\t.long "); - else - fprintf (acfg->fp, ","); - fprintf (acfg->fp, "%d", value); + encode_klass_ref (cfg, field->parent, p, &p); + g_assert (mono_metadata_token_code (token) == MONO_TOKEN_FIELD_DEF); + encode_value (token - MONO_TOKEN_FIELD_DEF, p, &p); + *endbuf = p; } static void -asm_writer_emit_symbol_diff (MonoAotCompile *acfg, const char *end, const char* start, int offset) +encode_generic_context (MonoAotCompile *acfg, MonoGenericContext *context, guint8 *buf, guint8 **endbuf) { - if (acfg->mode != EMIT_LONG) { - acfg->mode = EMIT_LONG; - acfg->col_count = 0; + guint8 *p = buf; + int i; + MonoGenericInst *inst; + + /* Encode the context */ + inst = context->class_inst; + encode_value (inst ? 1 : 0, p, &p); + if (inst) { + encode_value (inst->type_argc, p, &p); + for (i = 0; i < inst->type_argc; ++i) + encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); + } + inst = context->method_inst; + encode_value (inst ? 1 : 0, p, &p); + if (inst) { + encode_value (inst->type_argc, p, &p); + for (i = 0; i < inst->type_argc; ++i) + encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); } - if ((acfg->col_count++ % 8) == 0) - fprintf (acfg->fp, "\n\t.long "); - else - fprintf (acfg->fp, ","); - if (offset > 0) - fprintf (acfg->fp, "%s - %s + %d", end, start, offset); - else if (offset < 0) - fprintf (acfg->fp, "%s - %s %d", end, start, offset); - else - fprintf (acfg->fp, "%s - %s", end, start); -} -static void -asm_writer_emit_zero_bytes (MonoAotCompile *acfg, int num) -{ - asm_writer_emit_unset_mode (acfg); -#if defined(__MACH__) - fprintf (acfg->fp, "\t.space %d\n", num); -#else - fprintf (acfg->fp, "\t.skip %d\n", num); -#endif + *endbuf = p; } -static int -asm_writer_emit_writeout (MonoAotCompile *acfg) +#define MAX_IMAGE_INDEX 250 + +static void +encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf) { - char *command, *objfile; - char *outfile_name, *tmp_outfile_name; + guint32 image_index = get_image_index (acfg, method->klass->image); + guint32 token = method->token; + MonoJumpInfoToken *ji; + guint8 *p = buf; + char *name; - fclose (acfg->fp); + /* + * The encoding for most methods is as follows: + * - image index encoded as a leb128 + * - token index encoded as a leb128 + * Values of image index >= MONO_AOT_METHODREF_MIN are used to mark additional + * types of method encodings. + */ -#if defined(__x86_64__) -#define AS_OPTIONS "--64" -#elif defined(sparc) && SIZEOF_VOID_P == 8 -#define AS_OPTIONS "-xarch=v9" -#else -#define AS_OPTIONS "" -#endif + g_assert (image_index < MONO_AOT_METHODREF_MIN); - if (acfg->aot_opts.asm_only) { - printf ("Output file: '%s'.\n", acfg->tmpfname); - if (acfg->aot_opts.static_link) - printf ("Linking symbol: '%s'.\n", acfg->static_linking_symbol); - return 0; - } + /* Mark methods which can't use aot trampolines because they need the further + * processing in mono_magic_trampoline () which requires a MonoMethod*. + */ + if ((method->is_generic && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) + encode_value ((MONO_AOT_METHODREF_NO_AOT_TRAMPOLINE << 24), p, &p); - if (acfg->aot_opts.static_link) { - if (acfg->aot_opts.outfile) - objfile = g_strdup_printf ("%s", acfg->aot_opts.outfile); - else - objfile = g_strdup_printf ("%s.o", acfg->image->name); - } else { - objfile = g_strdup_printf ("%s.o", acfg->tmpfname); - } - command = g_strdup_printf ("as %s %s -o %s", AS_OPTIONS, acfg->tmpfname, objfile); - printf ("Executing the native assembler: %s\n", command); - if (system (command) != 0) { - g_free (command); - g_free (objfile); - return 1; + /* + * Some wrapper methods are shared using their signature, encode their + * stringified signature instead. + * FIXME: Optimize disk usage + */ + name = NULL; + if (method->wrapper_type) { + if (method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) { + char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); + name = g_strdup_printf ("(wrapper runtime-invoke):%s (%s)", method->name, tmpsig); + g_free (tmpsig); + } else if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) { + char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); + name = g_strdup_printf ("(wrapper delegate-invoke):%s (%s)", method->name, tmpsig); + g_free (tmpsig); + } else if (method->wrapper_type == MONO_WRAPPER_DELEGATE_BEGIN_INVOKE) { + char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); + name = g_strdup_printf ("(wrapper delegate-begin-invoke):%s (%s)", method->name, tmpsig); + g_free (tmpsig); + } else if (method->wrapper_type == MONO_WRAPPER_DELEGATE_END_INVOKE) { + char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); + name = g_strdup_printf ("(wrapper delegate-end-invoke):%s (%s)", method->name, tmpsig); + g_free (tmpsig); + } } - g_free (command); + if (name) { + encode_value ((MONO_AOT_METHODREF_WRAPPER_NAME << 24), p, &p); + strcpy ((char*)p, name); + p += strlen (name) + 1; + g_free (name); + } else if (method->wrapper_type) { + encode_value ((MONO_AOT_METHODREF_WRAPPER << 24), p, &p); - if (acfg->aot_opts.static_link) { - printf ("Output file: '%s'.\n", objfile); - printf ("Linking symbol: '%s'.\n", acfg->static_linking_symbol); - g_free (objfile); - return 0; - } + encode_value (method->wrapper_type, p, &p); - if (acfg->aot_opts.outfile) - outfile_name = g_strdup_printf ("%s", acfg->aot_opts.outfile); - else - outfile_name = g_strdup_printf ("%s%s", acfg->image->name, SHARED_EXT); + switch (method->wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE: + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_XDOMAIN_INVOKE: { + MonoMethod *m; - tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name); + m = mono_marshal_method_from_wrapper (method); + g_assert (m); + encode_method_ref (acfg, m, p, &p); + break; + } + case MONO_WRAPPER_PROXY_ISINST: + case MONO_WRAPPER_LDFLD: + case MONO_WRAPPER_LDFLDA: + case MONO_WRAPPER_STFLD: + case MONO_WRAPPER_ISINST: { + MonoClass *proxy_class = (MonoClass*)mono_marshal_method_from_wrapper (method); + encode_klass_ref (acfg, proxy_class, p, &p); + break; + } + case MONO_WRAPPER_LDFLD_REMOTE: + case MONO_WRAPPER_STFLD_REMOTE: + break; + case MONO_WRAPPER_ALLOC: { + int alloc_type = mono_gc_get_managed_allocator_type (method); + g_assert (alloc_type != -1); + encode_value (alloc_type, p, &p); + break; + } + case MONO_WRAPPER_STELEMREF: + break; + case MONO_WRAPPER_UNKNOWN: + if (strcmp (method->name, "FastMonitorEnter") == 0) + encode_value (MONO_AOT_WRAPPER_MONO_ENTER, p, &p); + else if (strcmp (method->name, "FastMonitorExit") == 0) + encode_value (MONO_AOT_WRAPPER_MONO_EXIT, p, &p); + else + g_assert_not_reached (); + break; + case MONO_WRAPPER_STATIC_RGCTX_INVOKE: + case MONO_WRAPPER_SYNCHRONIZED: + case MONO_WRAPPER_MANAGED_TO_NATIVE: { + MonoMethod *m; -#if defined(sparc) - command = g_strdup_printf ("ld -shared -G -o %s %s.o", outfile_name, acfg->tmpfname); -#elif defined(__ppc__) && defined(__MACH__) - command = g_strdup_printf ("gcc -dynamiclib -o %s %s.o", outfile_name, acfg->tmpfname); -#elif defined(PLATFORM_WIN32) - command = g_strdup_printf ("gcc -shared --dll -mno-cygwin -o %s %s.o", outfile_name, acfg->tmpfname); -#else - if (acfg->aot_opts.no_dlsym) { + m = mono_marshal_method_from_wrapper (method); + g_assert (m); + g_assert (m != method); + encode_method_ref (acfg, m, p, &p); + break; + } + default: + g_assert_not_reached (); + } + } else if (mono_method_signature (method)->is_inflated) { /* - * Need to link using gcc so our ctor function gets called. + * This is a generic method, find the original token which referenced it and + * encode that. + * Obtain the token from information recorded by the JIT. */ - command = g_strdup_printf ("gcc -shared -o %s %s.o", outfile_name, acfg->tmpfname); - } else { - command = g_strdup_printf ("ld -shared -o %s %s.o", outfile_name, acfg->tmpfname); - } -#endif - printf ("Executing the native linker: %s\n", command); - if (system (command) != 0) { - g_free (tmp_outfile_name); - g_free (outfile_name); - g_free (command); - g_free (objfile); - return 1; - } - - g_free (command); - unlink (objfile); - /*com = g_strdup_printf ("strip --strip-unneeded %s%s", acfg->image->name, SHARED_EXT); - printf ("Stripping the binary: %s\n", com); - system (com); - g_free (com);*/ + ji = g_hash_table_lookup (acfg->token_info_hash, method); + if (ji) { + image_index = get_image_index (acfg, ji->image); + g_assert (image_index < MAX_IMAGE_INDEX); + token = ji->token; - rename (tmp_outfile_name, outfile_name); + encode_value ((MONO_AOT_METHODREF_METHODSPEC << 24), p, &p); + encode_value (image_index, p, &p); + encode_value (token, p, &p); + } else { + MonoMethod *declaring; + MonoGenericContext *context = mono_method_get_context (method); - g_free (tmp_outfile_name); - g_free (outfile_name); - g_free (objfile); + g_assert (method->is_inflated); + declaring = ((MonoMethodInflated*)method)->declaring; - if (acfg->aot_opts.save_temps) - printf ("Retained input file.\n"); - else - unlink (acfg->tmpfname); + /* + * This might be a non-generic method of a generic instance, which + * doesn't have a token since the reference is generated by the JIT + * like Nullable:Box/Unbox, or by generic sharing. + */ - return 0; -} + encode_value ((MONO_AOT_METHODREF_GINST << 24), p, &p); + /* Encode the klass */ + encode_klass_ref (acfg, method->klass, p, &p); + /* Encode the method */ + image_index = get_image_index (acfg, method->klass->image); + g_assert (image_index < MAX_IMAGE_INDEX); + g_assert (declaring->token); + token = declaring->token; + g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); + encode_value (image_index, p, &p); + encode_value (token, p, &p); + encode_generic_context (acfg, context, p, &p); + } + } else if (token == 0) { + /* This might be a method of a constructed type like int[,].Set */ + /* Obtain the token from information recorded by the JIT */ + ji = g_hash_table_lookup (acfg->token_info_hash, method); + if (ji) { + image_index = get_image_index (acfg, ji->image); + g_assert (image_index < MAX_IMAGE_INDEX); + token = ji->token; -/* EMIT FUNCTIONS */ + encode_value ((MONO_AOT_METHODREF_METHODSPEC << 24), p, &p); + encode_value (image_index, p, &p); + encode_value (token, p, &p); + } else { + /* Array methods */ + g_assert (method->klass->rank); -static void emit_start (MonoAotCompile *acfg) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_start (acfg); - else - asm_writer_emit_start (acfg); -#else - asm_writer_emit_start (acfg); -#endif + /* Encode directly */ + encode_value ((MONO_AOT_METHODREF_ARRAY << 24), p, &p); + encode_klass_ref (acfg, method->klass, p, &p); + if (!strcmp (method->name, ".ctor") && mono_method_signature (method)->param_count == method->klass->rank) + encode_value (0, p, &p); + else if (!strcmp (method->name, ".ctor") && mono_method_signature (method)->param_count == method->klass->rank * 2) + encode_value (1, p, &p); + else if (!strcmp (method->name, "Get")) + encode_value (2, p, &p); + else if (!strcmp (method->name, "Address")) + encode_value (3, p, &p); + else if (!strcmp (method->name, "Set")) + encode_value (4, p, &p); + else + g_assert_not_reached (); + } + } else { + g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); + encode_value ((image_index << 24) | mono_metadata_token_index (token), p, &p); + } + *endbuf = p; } -static void emit_section_change (MonoAotCompile *acfg, const char *section_name, int subsection_index) +static gint +compare_patches (gconstpointer a, gconstpointer b) { -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_section_change (acfg, section_name, subsection_index); - else - asm_writer_emit_section_change (acfg, section_name, subsection_index); -#else - asm_writer_emit_section_change (acfg, section_name, subsection_index); -#endif -} + int i, j; -static void emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_global_inner (acfg, name, func); - else - asm_writer_emit_global_inner (acfg, name, func); -#else - asm_writer_emit_global_inner (acfg, name, func); -#endif -} + i = (*(MonoJumpInfo**)a)->ip.i; + j = (*(MonoJumpInfo**)b)->ip.i; -static void G_GNUC_UNUSED emit_local_symbol (MonoAotCompile *acfg, const char *name, const char *end_label, gboolean func) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_local_symbol (acfg, name, end_label, func); + if (i < j) + return -1; else - asm_writer_emit_local_symbol (acfg, name, end_label, func); -#else - asm_writer_emit_local_symbol (acfg, name, end_label, func); -#endif -} - -static void emit_label (MonoAotCompile *acfg, const char *name) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_label (acfg, name); + if (i > j) + return 1; else - asm_writer_emit_label (acfg, name); -#else - asm_writer_emit_label (acfg, name); -#endif + return 0; } -static void emit_bytes (MonoAotCompile *acfg, const guint8* buf, int size) +/* + * is_plt_patch: + * + * Return whenever PATCH_INFO refers to a direct call, and thus requires a + * PLT entry. + */ +static inline gboolean +is_plt_patch (MonoJumpInfo *patch_info) { -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_bytes (acfg, buf, size); - else - asm_writer_emit_bytes (acfg, buf, size); -#else - asm_writer_emit_bytes (acfg, buf, size); -#endif + switch (patch_info->type) { + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_INTERNAL_METHOD: + case MONO_PATCH_INFO_JIT_ICALL_ADDR: + case MONO_PATCH_INFO_ICALL_ADDR: + case MONO_PATCH_INFO_CLASS_INIT: + case MONO_PATCH_INFO_RGCTX_FETCH: + case MONO_PATCH_INFO_GENERIC_CLASS_INIT: + case MONO_PATCH_INFO_MONITOR_ENTER: + case MONO_PATCH_INFO_MONITOR_EXIT: + return TRUE; + default: + return FALSE; + } } -static void emit_string (MonoAotCompile *acfg, const char *value) +static int +get_plt_offset (MonoAotCompile *acfg, MonoJumpInfo *patch_info) { -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_string (acfg, value); - else - asm_writer_emit_string (acfg, value); -#else - asm_writer_emit_string (acfg, value); -#endif -} + int res = -1; -static void emit_line (MonoAotCompile *acfg) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_line (acfg); - else - asm_writer_emit_line (acfg); -#else - asm_writer_emit_line (acfg); -#endif -} + if (is_plt_patch (patch_info)) { + int idx = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_plt_offset, patch_info)); -static void emit_alignment (MonoAotCompile *acfg, int size) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_alignment (acfg, size); - else - asm_writer_emit_alignment (acfg, size); -#else - asm_writer_emit_alignment (acfg, size); -#endif -} + if (patch_info->type == MONO_PATCH_INFO_METHOD && (patch_info->data.method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) { + /* + * Allocate a separate PLT slot for each such patch, since some plt + * entries will refer to the method itself, and some will refer to + * wrapper. + */ + idx = 0; + } -static void emit_pointer_unaligned (MonoAotCompile *acfg, const char *target) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_pointer_unaligned (acfg, target); - else - asm_writer_emit_pointer_unaligned (acfg, target); -#else - asm_writer_emit_pointer_unaligned (acfg, target); -#endif -} + if (idx) { + res = idx; + } else { + MonoJumpInfo *new_ji = mono_patch_info_dup_mp (acfg->mempool, patch_info); -static void emit_pointer (MonoAotCompile *acfg, const char *target) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_pointer (acfg, target); - else - asm_writer_emit_pointer (acfg, target); -#else - asm_writer_emit_pointer (acfg, target); -#endif -} + res = acfg->plt_offset; + g_hash_table_insert (acfg->plt_offset_to_patch, GUINT_TO_POINTER (res), new_ji); + g_hash_table_insert (acfg->patch_to_plt_offset, new_ji, GUINT_TO_POINTER (res)); + acfg->plt_offset ++; + } + } -static void emit_int16 (MonoAotCompile *acfg, int value) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_int16 (acfg, value); - else - asm_writer_emit_int16 (acfg, value); -#else - asm_writer_emit_int16 (acfg, value); -#endif + return res; } -static void emit_int32 (MonoAotCompile *acfg, int value) +/** + * get_got_offset: + * + * Returns the offset of the GOT slot where the runtime object resulting from resolving + * JI could be found if it exists, otherwise allocates a new one. + */ +static guint32 +get_got_offset (MonoAotCompile *acfg, MonoJumpInfo *ji) { -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_int32 (acfg, value); - else - asm_writer_emit_int32 (acfg, value); -#else - asm_writer_emit_int32 (acfg, value); -#endif -} + guint32 got_offset; -static void emit_symbol_diff (MonoAotCompile *acfg, const char *end, const char* start, int offset) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_symbol_diff (acfg, end, start, offset); - else - asm_writer_emit_symbol_diff (acfg, end, start, offset); -#else - asm_writer_emit_symbol_diff (acfg, end, start, offset); -#endif -} + got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_shared_got_offset, ji)); + if (got_offset) + return got_offset - 1; -static void emit_zero_bytes (MonoAotCompile *acfg, int num) -{ -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - bin_writer_emit_zero_bytes (acfg, num); - else - asm_writer_emit_zero_bytes (acfg, num); -#else - asm_writer_emit_zero_bytes (acfg, num); -#endif + got_offset = acfg->got_offset; + acfg->got_offset ++; + + acfg->stats.got_slots ++; + acfg->stats.got_slot_types [ji->type] ++; + + return got_offset; } -static int -emit_writeout (MonoAotCompile *acfg) +static guint32 +get_shared_got_offset (MonoAotCompile *acfg, MonoJumpInfo *ji) { -#ifdef USE_BIN_WRITER - if (acfg->use_bin_writer) - return bin_writer_emit_writeout (acfg); - else - return asm_writer_emit_writeout (acfg); -#else - return asm_writer_emit_writeout (acfg); -#endif + MonoJumpInfo *copy; + guint32 got_offset; + + if (!g_hash_table_lookup (acfg->patch_to_shared_got_offset, ji)) { + got_offset = get_got_offset (acfg, ji); + copy = mono_patch_info_dup_mp (acfg->mempool, ji); + g_hash_table_insert (acfg->patch_to_shared_got_offset, copy, GUINT_TO_POINTER (got_offset + 1)); + g_ptr_array_add (acfg->shared_patches, copy); + } + + return get_got_offset (acfg, ji); } +/* Add a method to the list of methods which need to be emitted */ static void -emit_global (MonoAotCompile *acfg, const char *name, gboolean func) +add_method_with_index (MonoAotCompile *acfg, MonoMethod *method, int index) { - if (acfg->aot_opts.no_dlsym) { - g_ptr_array_add (acfg->globals, g_strdup (name)); - } else { - emit_global_inner (acfg, name, func); + g_assert (method); + if (!g_hash_table_lookup (acfg->method_indexes, method)) { + g_ptr_array_add (acfg->methods, method); + g_hash_table_insert (acfg->method_indexes, method, GUINT_TO_POINTER (index + 1)); + acfg->nmethods = acfg->methods->len + 1; } } -static void -emit_byte (MonoAotCompile *acfg, guint8 val) +static guint32 +get_method_index (MonoAotCompile *acfg, MonoMethod *method) { - emit_bytes (acfg, &val, 1); + int index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); + + g_assert (index); + + return index - 1; } -static void -emit_string_symbol (MonoAotCompile *acfg, const char *name, const char *value) +static int +add_method (MonoAotCompile *acfg, MonoMethod *method) { - emit_section_change (acfg, ".text", 1); - emit_global (acfg, name, FALSE); - emit_label (acfg, name); - emit_string (acfg, value); -} + int index; -/* AOT COMPILER */ + index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); + if (index) + return index - 1; -static guint32 -mono_get_field_token (MonoClassField *field) -{ - MonoClass *klass = field->parent; - int i; + index = acfg->method_index; + add_method_with_index (acfg, method, index); - for (i = 0; i < klass->field.count; ++i) { - if (field == &klass->fields [i]) - return MONO_TOKEN_FIELD_DEF | (klass->field.first + 1 + i); - } + /* FIXME: Fix quadratic behavior */ + acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (index)); - g_assert_not_reached (); - return 0; + acfg->method_index ++; + + return index; } -static inline void -encode_value (gint32 value, guint8 *buf, guint8 **endbuf) +static void +add_extra_method (MonoAotCompile *acfg, MonoMethod *method) { - guint8 *p = buf; - - //printf ("ENCODE: %d 0x%x.\n", value, value); + int index; - /* - * Same encoding as the one used in the metadata, extended to handle values - * greater than 0x1fffffff. - */ - if ((value >= 0) && (value <= 127)) - *p++ = value; - else if ((value >= 0) && (value <= 16383)) { - p [0] = 0x80 | (value >> 8); - p [1] = value & 0xff; - p += 2; - } else if ((value >= 0) && (value <= 0x1fffffff)) { - p [0] = (value >> 24) | 0xc0; - p [1] = (value >> 16) & 0xff; - p [2] = (value >> 8) & 0xff; - p [3] = value & 0xff; - p += 4; - } - else { - p [0] = 0xff; - p [1] = (value >> 24) & 0xff; - p [2] = (value >> 16) & 0xff; - p [3] = (value >> 8) & 0xff; - p [4] = value & 0xff; - p += 5; - } - if (endbuf) - *endbuf = p; + index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); + if (index) + return; + add_method (acfg, method); + g_ptr_array_add (acfg->extra_methods, method); } -static guint32 -get_image_index (MonoAotCompile *cfg, MonoImage *image) +static void +add_jit_icall_wrapper (gpointer key, gpointer value, gpointer user_data) { - guint32 index; + MonoAotCompile *acfg = user_data; + MonoJitICallInfo *callinfo = value; + MonoMethod *wrapper; + char *name; - index = GPOINTER_TO_UINT (g_hash_table_lookup (cfg->image_hash, image)); - if (index) - return index - 1; - else { - index = g_hash_table_size (cfg->image_hash); - g_hash_table_insert (cfg->image_hash, image, GUINT_TO_POINTER (index + 1)); - g_ptr_array_add (cfg->image_table, image); - return index; - } + if (!callinfo->sig) + return; + + name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name); + wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func, check_for_pending_exc); + g_free (name); + + add_method (acfg, wrapper); } -static guint32 -find_typespec_for_class (MonoAotCompile *acfg, MonoClass *klass) +static MonoMethod* +get_runtime_invoke_sig (MonoMethodSignature *sig) { - int i; - MonoClass *k = NULL; - - /* FIXME: Search referenced images as well */ - for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) { - k = mono_class_get_full (acfg->image, MONO_TOKEN_TYPE_SPEC | (i + 1), NULL); - if (k == klass) - break; - } + MonoMethodBuilder *mb; + MonoMethod *m; - if (i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows) - return MONO_TOKEN_TYPE_SPEC | (i + 1); - else - return 0; + mb = mono_mb_new (mono_defaults.object_class, "FOO", MONO_WRAPPER_NONE); + m = mono_mb_create_method (mb, sig, 16); + return mono_marshal_get_runtime_invoke (m, FALSE); } static void -encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf); - -/* - * encode_klass_ref: - * - * Encode a reference to KLASS. We use our home-grown encoding instead of the - * standard metadata encoding. - */ -static void -encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf) +add_wrappers (MonoAotCompile *acfg) { - guint8 *p = buf; - - if (klass->generic_class) { - guint32 token; - g_assert (klass->type_token); + MonoMethod *method, *m; + int i, j, nallocators; + MonoMethodSignature *sig, *csig; + guint32 token; - /* Find a typespec for a class if possible */ - token = find_typespec_for_class (acfg, klass); - if (token) { - encode_value (token, p, &p); - encode_value (get_image_index (acfg, acfg->image), p, &p); - } else { - MonoClass *gclass = klass->generic_class->container_class; - MonoGenericInst *inst = klass->generic_class->context.class_inst; - int i; + /* + * FIXME: Instead of AOTing all the wrappers, it might be better to redesign them + * so there is only one wrapper of a given type, or inlining their contents into their + * callers. + */ - /* Encode it ourselves */ - /* Marker */ - encode_value (MONO_TOKEN_TYPE_SPEC, p, &p); - encode_value (MONO_TYPE_GENERICINST, p, &p); - encode_klass_ref (acfg, gclass, p, &p); - encode_value (inst->type_argc, p, &p); - for (i = 0; i < inst->type_argc; ++i) - encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); - } - } else if (klass->type_token) { - g_assert (mono_metadata_token_code (klass->type_token) == MONO_TOKEN_TYPE_DEF); - encode_value (klass->type_token - MONO_TOKEN_TYPE_DEF, p, &p); - encode_value (get_image_index (acfg, klass->image), p, &p); - } else if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) { - MonoGenericParam *param = klass->byval_arg.data.generic_param; + /* + * FIXME: This depends on the fact that different wrappers have different + * names. + */ - /* Marker */ - encode_value (MONO_TOKEN_TYPE_SPEC, p, &p); - encode_value (klass->byval_arg.type, p, &p); + /* FIXME: Collect these automatically */ - encode_value (param->num, p, &p); - - g_assert (param->owner); - encode_value (param->owner->is_method, p, &p); - if (param->owner->is_method) - encode_method_ref (acfg, param->owner->owner.method, p, &p); - else - encode_klass_ref (acfg, param->owner->owner.klass, p, &p); - } else { - /* Array class */ - g_assert (klass->rank > 0); - encode_value (MONO_TOKEN_TYPE_DEF, p, &p); - encode_value (get_image_index (acfg, klass->image), p, &p); - encode_value (klass->rank, p, &p); - encode_klass_ref (acfg, klass->element_class, p, &p); - } - *endbuf = p; -} + /* Runtime invoke wrappers */ -static void -encode_field_info (MonoAotCompile *cfg, MonoClassField *field, guint8 *buf, guint8 **endbuf) -{ - guint32 token = mono_get_field_token (field); - guint8 *p = buf; + /* void runtime-invoke () [.cctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + csig->ret = &mono_defaults.void_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); - encode_klass_ref (cfg, field->parent, p, &p); - g_assert (mono_metadata_token_code (token) == MONO_TOKEN_FIELD_DEF); - encode_value (token - MONO_TOKEN_FIELD_DEF, p, &p); - *endbuf = p; -} + /* void runtime-invoke () [Finalize] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); -static void -encode_generic_context (MonoAotCompile *acfg, MonoGenericContext *context, guint8 *buf, guint8 **endbuf) -{ - guint8 *p = buf; - int i; - MonoGenericInst *inst; + /* void runtime-invoke (string) [exception ctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); - /* Encode the context */ - inst = context->class_inst; - encode_value (inst ? 1 : 0, p, &p); - if (inst) { - encode_value (inst->type_argc, p, &p); - for (i = 0; i < inst->type_argc; ++i) - encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); - } - inst = context->method_inst; - encode_value (inst ? 1 : 0, p, &p); - if (inst) { - encode_value (inst->type_argc, p, &p); - for (i = 0; i < inst->type_argc; ++i) - encode_klass_ref (acfg, mono_class_from_mono_type (inst->type_argv [i]), p, &p); - } + /* void runtime-invoke (string, string) [exception ctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + csig->params [1] = &mono_defaults.string_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); - *endbuf = p; -} + /* string runtime-invoke () [Exception.ToString ()] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); + csig->hasthis = 1; + csig->ret = &mono_defaults.string_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); -#define MAX_IMAGE_INDEX 250 + /* void runtime-invoke (string, Exception) [exception ctor] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + csig->hasthis = 1; + csig->ret = &mono_defaults.void_class->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + csig->params [1] = &mono_defaults.exception_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); -static void -encode_method_ref (MonoAotCompile *acfg, MonoMethod *method, guint8 *buf, guint8 **endbuf) -{ - guint32 image_index = get_image_index (acfg, method->klass->image); - guint32 token = method->token; - MonoJumpInfoToken *ji; - guint8 *p = buf; + /* Assembly runtime-invoke (string, bool) [DoAssemblyResolve] */ + csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); + csig->hasthis = 1; + csig->ret = &(mono_class_from_name ( + mono_defaults.corlib, "System.Reflection", "Assembly"))->byval_arg; + csig->params [0] = &mono_defaults.string_class->byval_arg; + csig->params [1] = &mono_defaults.boolean_class->byval_arg; + add_method (acfg, get_runtime_invoke_sig (csig)); - g_assert (image_index < MAX_IMAGE_INDEX); + /* runtime-invoke used by finalizers */ + add_method (acfg, mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE)); - /* Mark methods which can't use aot trampolines because they need the further - * processing in mono_magic_trampoline () which requires a MonoMethod*. - */ - if ((method->is_generic && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) || - (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) - encode_value ((252 << 24), p, &p); + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + gboolean skip = FALSE; - if (method->wrapper_type) { - /* Marker */ - encode_value ((253 << 24), p, &p); + method = mono_get_method (acfg->image, token, NULL); - encode_value (method->wrapper_type, p, &p); + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) + skip = TRUE; - switch (method->wrapper_type) { - case MONO_WRAPPER_REMOTING_INVOKE: - case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: - case MONO_WRAPPER_XDOMAIN_INVOKE: { - MonoMethod *m; + if (method->is_generic || method->klass->generic_container) + skip = TRUE; - m = mono_marshal_method_from_wrapper (method); - g_assert (m); - encode_method_ref (acfg, m, p, &p); - break; - } - case MONO_WRAPPER_PROXY_ISINST: - case MONO_WRAPPER_LDFLD: - case MONO_WRAPPER_LDFLDA: - case MONO_WRAPPER_STFLD: - case MONO_WRAPPER_ISINST: { - MonoClass *proxy_class = (MonoClass*)mono_marshal_method_from_wrapper (method); - encode_klass_ref (acfg, proxy_class, p, &p); - break; - } - case MONO_WRAPPER_LDFLD_REMOTE: - case MONO_WRAPPER_STFLD_REMOTE: - break; - case MONO_WRAPPER_ALLOC: { - int alloc_type = mono_gc_get_managed_allocator_type (method); - g_assert (alloc_type != -1); - encode_value (alloc_type, p, &p); - break; + /* Skip methods which can not be handled by get_runtime_invoke () */ + sig = mono_method_signature (method); + if ((sig->ret->type == MONO_TYPE_PTR) || + (sig->ret->type == MONO_TYPE_TYPEDBYREF)) + skip = TRUE; + + for (j = 0; j < sig->param_count; j++) { + if (sig->params [j]->type == MONO_TYPE_TYPEDBYREF) + skip = TRUE; } - case MONO_WRAPPER_STELEMREF: - break; - case MONO_WRAPPER_UNKNOWN: - if (strcmp (method->name, "FastMonitorEnter") == 0) - encode_value (MONO_AOT_WRAPPER_MONO_ENTER, p, &p); - else if (strcmp (method->name, "FastMonitorExit") == 0) - encode_value (MONO_AOT_WRAPPER_MONO_EXIT, p, &p); - else - g_assert_not_reached (); - break; - case MONO_WRAPPER_STATIC_RGCTX_INVOKE: { - MonoMethod *m; - m = mono_marshal_method_from_wrapper (method); - g_assert (m); - encode_method_ref (acfg, m, p, &p); - break; + if (!skip) + add_method (acfg, mono_marshal_get_runtime_invoke (method, FALSE)); + } + + if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) { + MonoMethodDesc *desc; + MonoMethod *orig_method; + + /* JIT icall wrappers */ + /* FIXME: locking */ + g_hash_table_foreach (mono_get_jit_icall_info (), add_jit_icall_wrapper, acfg); + + /* Managed Allocators */ + nallocators = mono_gc_get_managed_allocator_types (); + for (i = 0; i < nallocators; ++i) { + m = mono_gc_get_managed_allocator_by_type (i); + if (m) + add_method (acfg, m); } - default: - g_assert_not_reached (); + + /* stelemref */ + add_method (acfg, mono_marshal_get_stelemref ()); + + /* Monitor Enter/Exit */ + desc = mono_method_desc_new ("Monitor:Enter", FALSE); + orig_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class); + g_assert (orig_method); + mono_method_desc_free (desc); + method = mono_monitor_get_fast_path (orig_method); + if (method) + add_method (acfg, method); + + desc = mono_method_desc_new ("Monitor:Exit", FALSE); + orig_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class); + g_assert (orig_method); + mono_method_desc_free (desc); + method = mono_monitor_get_fast_path (orig_method); + if (method) + add_method (acfg, method); + } + + /* remoting-invoke wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoMethodSignature *sig; + + token = MONO_TOKEN_METHOD_DEF | (i + 1); + method = mono_get_method (acfg->image, token, NULL); + + sig = mono_method_signature (method); + + if (sig->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class)) { + m = mono_marshal_get_remoting_invoke_with_check (method); + + add_method (acfg, m); } - } else if (mono_method_signature (method)->is_inflated) { - /* - * This is a generic method, find the original token which referenced it and - * encode that. - * Obtain the token from information recorded by the JIT. - */ - ji = g_hash_table_lookup (acfg->token_info_hash, method); - if (ji) { - image_index = get_image_index (acfg, ji->image); - g_assert (image_index < MAX_IMAGE_INDEX); - token = ji->token; + } - /* Marker */ - encode_value ((255 << 24), p, &p); - encode_value (image_index, p, &p); - encode_value (token, p, &p); - } else { - MonoMethod *declaring; - MonoGenericContext *context = mono_method_get_context (method); + /* delegate-invoke wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { + MonoClass *klass; + + token = MONO_TOKEN_TYPE_DEF | (i + 1); + klass = mono_class_get (acfg->image, token); - g_assert (method->is_inflated); - declaring = ((MonoMethodInflated*)method)->declaring; + if (klass->delegate && klass != mono_defaults.delegate_class && klass != mono_defaults.multicastdelegate_class && !klass->generic_container) { + method = mono_get_delegate_invoke (klass); - /* - * This might be a non-generic method of a generic instance, which - * doesn't have a token since the reference is generated by the JIT - * like Nullable:Box/Unbox, or by generic sharing. - */ + m = mono_marshal_get_delegate_invoke (method, NULL); - /* Marker */ - encode_value ((254 << 24), p, &p); - /* Encode the klass */ - encode_klass_ref (acfg, method->klass, p, &p); - /* Encode the method */ - image_index = get_image_index (acfg, method->klass->image); - g_assert (image_index < MAX_IMAGE_INDEX); - g_assert (declaring->token); - token = declaring->token; - g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); - encode_value (image_index, p, &p); - encode_value (token, p, &p); - encode_generic_context (acfg, context, p, &p); + add_method (acfg, m); + + method = mono_class_get_method_from_name_flags (klass, "BeginInvoke", -1, 0); + add_method (acfg, mono_marshal_get_delegate_begin_invoke (method)); + + method = mono_class_get_method_from_name_flags (klass, "EndInvoke", -1, 0); + add_method (acfg, mono_marshal_get_delegate_end_invoke (method)); } - } else if (token == 0) { - /* This might be a method of a constructed type like int[,].Set */ - /* Obtain the token from information recorded by the JIT */ - ji = g_hash_table_lookup (acfg->token_info_hash, method); - g_assert (ji); - image_index = get_image_index (acfg, ji->image); - g_assert (image_index < MAX_IMAGE_INDEX); - token = ji->token; + } - /* Marker */ - encode_value ((255 << 24), p, &p); - encode_value (image_index, p, &p); - encode_value (token, p, &p); - } else { - g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD); - encode_value ((image_index << 24) | mono_metadata_token_index (token), p, &p); + /* Synchronized wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + token = MONO_TOKEN_METHOD_DEF | (i + 1); + method = mono_get_method (acfg->image, token, NULL); + + if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) + add_method (acfg, mono_marshal_get_synchronized_wrapper (method)); + } + + /* pinvoke wrappers */ + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + MonoMethod *method; + guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + + method = mono_get_method (acfg->image, token, NULL); + + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) { + add_method (acfg, mono_marshal_get_native_wrapper (method, TRUE, TRUE)); + } } - *endbuf = p; } -static gint -compare_patches (gconstpointer a, gconstpointer b) +static gboolean +has_type_vars (MonoClass *klass) { - int i, j; - - i = (*(MonoJumpInfo**)a)->ip.i; - j = (*(MonoJumpInfo**)b)->ip.i; + if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) + return TRUE; + if (klass->rank) + return has_type_vars (klass->element_class); + if (klass->generic_class) { + MonoGenericContext *context = &klass->generic_class->context; + if (context->class_inst) { + int i; - if (i < j) - return -1; - else - if (i > j) - return 1; - else - return 0; + for (i = 0; i < context->class_inst->type_argc; ++i) + if (has_type_vars (mono_class_from_mono_type (context->class_inst->type_argv [i]))) + return TRUE; + } + } + return FALSE; } -/* - * is_plt_patch: - * - * Return whenever PATCH_INFO refers to a direct call, and thus requires a - * PLT entry. - */ -static inline gboolean -is_plt_patch (MonoJumpInfo *patch_info) +static gboolean +method_has_type_vars (MonoMethod *method) { - switch (patch_info->type) { - case MONO_PATCH_INFO_METHOD: - case MONO_PATCH_INFO_INTERNAL_METHOD: - case MONO_PATCH_INFO_JIT_ICALL_ADDR: - case MONO_PATCH_INFO_ICALL_ADDR: - case MONO_PATCH_INFO_CLASS_INIT: - case MONO_PATCH_INFO_RGCTX_FETCH: - case MONO_PATCH_INFO_GENERIC_CLASS_INIT: - case MONO_PATCH_INFO_MONITOR_ENTER: - case MONO_PATCH_INFO_MONITOR_EXIT: + if (has_type_vars (method->klass)) return TRUE; - default: - return FALSE; + + if (method->is_inflated) { + MonoGenericContext *context = mono_method_get_context (method); + if (context->method_inst) { + int i; + + for (i = 0; i < context->method_inst->type_argc; ++i) + if (has_type_vars (mono_class_from_mono_type (context->method_inst->type_argv [i]))) + return TRUE; + } } + return FALSE; } /* - * is_shared_got_patch: + * add_generic_class: * - * Return whenever PATCH_INFO refers to a patch which needs a shared GOT - * entry. - * Keep it in sync with the version in aot-runtime.c. + * Add all methods of a generic class. */ -static inline gboolean -is_shared_got_patch (MonoJumpInfo *patch_info) +static void +add_generic_class (MonoAotCompile *acfg, MonoClass *klass) { - switch (patch_info->type) { - case MONO_PATCH_INFO_VTABLE: - case MONO_PATCH_INFO_CLASS: - case MONO_PATCH_INFO_IID: - case MONO_PATCH_INFO_ADJUSTED_IID: - case MONO_PATCH_INFO_FIELD: - case MONO_PATCH_INFO_SFLDA: - case MONO_PATCH_INFO_DECLSEC: - case MONO_PATCH_INFO_LDTOKEN: - case MONO_PATCH_INFO_TYPE_FROM_HANDLE: - case MONO_PATCH_INFO_RVA: - case MONO_PATCH_INFO_METHODCONST: - return TRUE; - default: - return FALSE; - } -} - -static int -get_plt_offset (MonoAotCompile *acfg, MonoJumpInfo *patch_info) -{ - int res = -1; + MonoMethod *method; + gpointer iter; - if (is_plt_patch (patch_info)) { - int idx = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_plt_offset, patch_info)); + mono_class_init (klass); - if (patch_info->type == MONO_PATCH_INFO_METHOD && (patch_info->data.method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)) { - /* - * Allocate a separate PLT slot for each such patch, since some plt - * entries will refer to the method itself, and some will refer to - * wrapper. - */ - idx = 0; - } + if (klass->generic_class && klass->generic_class->context.class_inst->is_open) + return; - if (idx) { - res = idx; - } else { - MonoJumpInfo *new_ji = mono_patch_info_dup_mp (acfg->mempool, patch_info); + if (has_type_vars (klass)) + return; - res = acfg->plt_offset; - g_hash_table_insert (acfg->plt_offset_to_patch, GUINT_TO_POINTER (res), new_ji); - g_hash_table_insert (acfg->patch_to_plt_offset, new_ji, GUINT_TO_POINTER (res)); - acfg->plt_offset ++; - } - } + if (!klass->generic_class && !klass->rank) + return; - return res; -} + /* + * Add rgctx wrappers for cctors since those are called by the runtime, so + * there is no methodspec for them. This is needed even for shared classes, + * since rgctx wrappers belong to inflated methods. + */ + method = mono_class_get_cctor (klass); + if (method && mono_method_needs_static_rgctx_invoke (method, FALSE)) + add_extra_method (acfg, mono_marshal_get_static_rgctx_invoke (method)); -/** - * get_got_offset: - * - * Returns the offset of the GOT slot where the runtime object resulting from resolving - * JI could be found if it exists, otherwise allocates a new one. - */ -static guint32 -get_got_offset (MonoAotCompile *acfg, MonoJumpInfo *ji) -{ - guint32 got_offset; + iter = NULL; + while ((method = mono_class_get_methods (klass, &iter))) { + if (mono_method_is_generic_sharable_impl (method, FALSE)) + /* Already added */ + continue; - got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_shared_got_offset, ji)); - if (got_offset) - return got_offset - 1; + if (method->is_generic) + /* FIXME: */ + continue; - got_offset = acfg->got_offset; - acfg->got_offset ++; + /* + * FIXME: Instances which are referenced by these methods are not added, + * for example Array.Resize for List.Add (). + */ + add_extra_method (acfg, method); + } - acfg->stats.got_slots ++; - acfg->stats.got_slot_types [ji->type] ++; + /* + * For ICollection, where T is a vtype, add instances of the helper methods + * in Array, since a T[] could be cast to ICollection. + */ + if (klass->image == mono_defaults.corlib && !strcmp (klass->name_space, "System.Collections.Generic") && + (!strcmp(klass->name, "ICollection`1") || !strcmp (klass->name, "IEnumerable`1") || !strcmp (klass->name, "IList`1") || !strcmp (klass->name, "IEnumerator`1")) && + MONO_TYPE_ISSTRUCT (klass->generic_class->context.class_inst->type_argv [0])) { + MonoClass *tclass = mono_class_from_mono_type (klass->generic_class->context.class_inst->type_argv [0]); + MonoClass *array_class = mono_bounded_array_class_get (tclass, 1, FALSE); + gpointer iter; + char *name_prefix; - return got_offset; -} + if (!strcmp (klass->name, "IEnumerator`1")) + name_prefix = g_strdup_printf ("%s.%s", klass->name_space, "IEnumerable`1"); + else + name_prefix = g_strdup_printf ("%s.%s", klass->name_space, klass->name); -static guint32 -get_shared_got_offset (MonoAotCompile *acfg, MonoJumpInfo *ji) -{ - MonoJumpInfo *copy; - guint32 got_offset; + /* Add the T[]/InternalEnumerator class */ + if (!strcmp (klass->name, "IEnumerable`1") || !strcmp (klass->name, "IEnumerator`1")) { + MonoClass *nclass; - if (!g_hash_table_lookup (acfg->patch_to_shared_got_offset, ji)) { - got_offset = get_got_offset (acfg, ji); - copy = mono_patch_info_dup_mp (acfg->mempool, ji); - g_hash_table_insert (acfg->patch_to_shared_got_offset, copy, GUINT_TO_POINTER (got_offset + 1)); - g_ptr_array_add (acfg->shared_patches, copy); - } + iter = NULL; + while ((nclass = mono_class_get_nested_types (array_class->parent, &iter))) { + if (!strcmp (nclass->name, "InternalEnumerator`1")) + break; + } + g_assert (nclass); + nclass = mono_class_inflate_generic_class (nclass, mono_generic_class_get_context (klass->generic_class)); + add_generic_class (acfg, nclass); + } - return get_got_offset (acfg, ji); -} + iter = NULL; + while ((method = mono_class_get_methods (array_class, &iter))) { + if (strstr (method->name, name_prefix)) + add_extra_method (acfg, method); + } -/* Add a method to the list of methods which need to be emitted */ -static void -add_method_with_index (MonoAotCompile *acfg, MonoMethod *method, int index) -{ - g_assert (method); - if (!g_hash_table_lookup (acfg->method_indexes, method)) { - g_ptr_array_add (acfg->methods, method); - g_hash_table_insert (acfg->method_indexes, method, GUINT_TO_POINTER (index + 1)); - acfg->nmethods = acfg->methods->len + 1; + g_free (name_prefix); } } -static guint32 -get_method_index (MonoAotCompile *acfg, MonoMethod *method) +/* + * add_generic_instances: + * + * Add instances referenced by the METHODSPEC/TYPESPEC table. + */ +static void +add_generic_instances (MonoAotCompile *acfg) { - int index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); - - g_assert (index); + int i; + guint32 token; + MonoMethod *method; + MonoMethodHeader *header; + MonoMethodSignature *sig; + MonoGenericContext *context; - return index - 1; -} + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHODSPEC].rows; ++i) { + token = MONO_TOKEN_METHOD_SPEC | (i + 1); + method = mono_get_method (acfg->image, token, NULL); -static int -add_method (MonoAotCompile *acfg, MonoMethod *method) -{ - int index; + context = mono_method_get_context (method); + if (context && ((context->class_inst && context->class_inst->is_open) || + (context->method_inst && context->method_inst->is_open))) + continue; - index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); - if (index) - return index - 1; + if (method->klass->image != acfg->image) + continue; - index = acfg->method_index; - add_method_with_index (acfg, method, index); + if (mono_method_is_generic_sharable_impl (method, FALSE)) + /* Already added */ + continue; - /* FIXME: Fix quadratic behavior */ - acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (index)); + add_extra_method (acfg, method); + } - acfg->method_index ++; + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) { + MonoClass *klass; - return index; -} + token = MONO_TOKEN_TYPE_SPEC | (i + 1); -static void -add_extra_method (MonoAotCompile *acfg, MonoMethod *method) -{ - int index; + klass = mono_class_get (acfg->image, token); + if (!klass) + continue; - index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method)); - if (index) - return; - add_method (acfg, method); - g_ptr_array_add (acfg->extra_methods, method); -} + add_generic_class (acfg, klass); + } -static void -add_jit_icall_wrapper (gpointer key, gpointer value, gpointer user_data) -{ - MonoAotCompile *acfg = user_data; - MonoJitICallInfo *callinfo = value; - MonoMethod *wrapper; - char *name; + /* Add types of args/locals */ + for (i = 0; i < acfg->methods->len; ++i) { + int j; - if (!callinfo->sig) - return; + method = g_ptr_array_index (acfg->methods, i); - name = g_strdup_printf ("__icall_wrapper_%s", callinfo->name); - wrapper = mono_marshal_get_icall_wrapper (callinfo->sig, name, callinfo->func, check_for_pending_exc); - g_free (name); + sig = mono_method_signature (method); - add_method (acfg, wrapper); -} + if (sig) { + for (j = 0; j < sig->param_count; ++j) + if (sig->params [j]->type == MONO_TYPE_GENERICINST) + add_generic_class (acfg, mono_class_from_mono_type (sig->params [j])); + } -static MonoMethod* -get_runtime_invoke_sig (MonoMethodSignature *sig) -{ - MonoMethodBuilder *mb; - MonoMethod *m; + header = mono_method_get_header (method); - mb = mono_mb_new (mono_defaults.object_class, "FOO", MONO_WRAPPER_NONE); - m = mono_mb_create_method (mb, sig, 16); - return mono_marshal_get_runtime_invoke (m); + if (header) { + for (j = 0; j < header->num_locals; ++j) + if (header->locals [j]->type == MONO_TYPE_GENERICINST) + add_generic_class (acfg, mono_class_from_mono_type (header->locals [j])); + } + } } +/* + * emit_and_reloc_code: + * + * Emit the native code in CODE, handling relocations along the way. If GOT_ONLY + * is true, calls are made through the GOT too. This is used for emitting trampolines + * in full-aot mode, since calls made from trampolines couldn't go through the PLT, + * since trampolines are needed to make PTL work. + */ static void -add_wrappers (MonoAotCompile *acfg) +emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, guint32 code_len, MonoJumpInfo *relocs, gboolean got_only) { - MonoMethod *method, *m; - int i, j, nallocators; - MonoMethodSignature *sig, *csig; - guint32 token; - - /* - * FIXME: Instead of AOTing all the wrappers, it might be better to redesign them - * so there is only one wrapper of a given type, or inlining their contents into their - * callers. - */ + int i, pindex, start_index, method_index; + GPtrArray *patches; + MonoJumpInfo *patch_info; + MonoMethodHeader *header; + gboolean skip, direct_call; + guint32 got_slot; + char direct_call_target [128]; - /* - * FIXME: This depends on the fact that different wrappers have different - * names. - */ + if (method) { + header = mono_method_get_header (method); - /* FIXME: Collect these automatically */ + method_index = get_method_index (acfg, method); + } - /* Runtime invoke wrappers */ + /* Collect and sort relocations */ + patches = g_ptr_array_new (); + for (patch_info = relocs; patch_info; patch_info = patch_info->next) + g_ptr_array_add (patches, patch_info); + g_ptr_array_sort (patches, compare_patches); - /* void runtime-invoke () [.cctor] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); - csig->ret = &mono_defaults.void_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); + start_index = 0; + for (i = 0; i < code_len; i++) { + patch_info = NULL; + for (pindex = start_index; pindex < patches->len; ++pindex) { + patch_info = g_ptr_array_index (patches, pindex); + if (patch_info->ip.i >= i) + break; + } - /* void runtime-invoke () [Finalize] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); - csig->hasthis = 1; - csig->ret = &mono_defaults.void_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); +#ifdef MONO_ARCH_AOT_SUPPORTED + skip = FALSE; + if (patch_info && (patch_info->ip.i == i) && (pindex < patches->len)) { + start_index = pindex; - /* void runtime-invoke (string) [exception ctor] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1); - csig->hasthis = 1; - csig->ret = &mono_defaults.void_class->byval_arg; - csig->params [0] = &mono_defaults.string_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); - - /* void runtime-invoke (string, string) [exception ctor] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); - csig->hasthis = 1; - csig->ret = &mono_defaults.void_class->byval_arg; - csig->params [0] = &mono_defaults.string_class->byval_arg; - csig->params [1] = &mono_defaults.string_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); - - /* string runtime-invoke () [Exception.ToString ()] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 0); - csig->hasthis = 1; - csig->ret = &mono_defaults.string_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); - - /* void runtime-invoke (string, Exception) [exception ctor] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); - csig->hasthis = 1; - csig->ret = &mono_defaults.void_class->byval_arg; - csig->params [0] = &mono_defaults.string_class->byval_arg; - csig->params [1] = &mono_defaults.exception_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); + switch (patch_info->type) { + case MONO_PATCH_INFO_NONE: + break; + case MONO_PATCH_INFO_GOT_OFFSET: { + int code_size; + + arch_emit_got_offset (acfg, code + i, &code_size); + i += code_size - 1; + skip = TRUE; + break; + } + default: { + /* + * If this patch is a call, try emitting a direct call instead of + * through a PLT entry. This is possible if the called method is in + * the same assembly and requires no initialization. + */ + direct_call = FALSE; + if (!got_only && (patch_info->type == MONO_PATCH_INFO_METHOD) && (patch_info->data.method->klass->image == method->klass->image)) { + MonoCompile *callee_cfg = g_hash_table_lookup (acfg->method_to_cfg, patch_info->data.method); + if (callee_cfg) { + gboolean direct_callable = TRUE; - /* Assembly runtime-invoke (string, bool) [DoAssemblyResolve] */ - csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2); - csig->hasthis = 1; - csig->ret = &(mono_class_from_name ( - mono_defaults.corlib, "System.Reflection", "Assembly"))->byval_arg; - csig->params [0] = &mono_defaults.string_class->byval_arg; - csig->params [1] = &mono_defaults.boolean_class->byval_arg; - add_method (acfg, get_runtime_invoke_sig (csig)); + if (direct_callable && !(!callee_cfg->has_got_slots && (callee_cfg->method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT))) + direct_callable = FALSE; + if ((callee_cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) && method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) + // FIXME: Maybe call the wrapper directly ? + direct_callable = FALSE; + if (direct_callable) { + //printf ("DIRECT: %s %s\n", method ? mono_method_full_name (method, TRUE) : "", mono_method_full_name (callee_cfg->method, TRUE)); + direct_call = TRUE; + sprintf (direct_call_target, ".Lm_%x", get_method_index (acfg, callee_cfg->orig_method)); + patch_info->type = MONO_PATCH_INFO_NONE; + acfg->stats.direct_calls ++; + } + } - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - MonoMethod *method; - guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); - gboolean skip = FALSE; + acfg->stats.all_calls ++; + } - method = mono_get_method (acfg->image, token, NULL); + if (!got_only && !direct_call) { + int plt_offset = get_plt_offset (acfg, patch_info); + if (plt_offset != -1) { + /* This patch has a PLT entry, so we must emit a call to the PLT entry */ + direct_call = TRUE; + sprintf (direct_call_target, ".Lp_%d", plt_offset); + + /* Nullify the patch */ + patch_info->type = MONO_PATCH_INFO_NONE; + } + } - if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || - (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || - (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) - skip = TRUE; + if (direct_call) { + int call_size; - if (method->is_generic || method->klass->generic_container) - skip = TRUE; + arch_emit_direct_call (acfg, direct_call_target, &call_size); + i += call_size - 1; + } else { + int code_size; - /* Skip methods which can not be handled by get_runtime_invoke () */ - sig = mono_method_signature (method); - if ((sig->ret->type == MONO_TYPE_PTR) || - (sig->ret->type == MONO_TYPE_TYPEDBYREF)) - skip = TRUE; + got_slot = get_got_offset (acfg, patch_info); - for (j = 0; j < sig->param_count; j++) { - if (sig->params [j]->type == MONO_TYPE_TYPEDBYREF) + arch_emit_got_access (acfg, code + i, got_slot, &code_size); + i += code_size - 1; + } skip = TRUE; + } + } } +#endif /* MONO_ARCH_AOT_SUPPORTED */ - if (!skip) - add_method (acfg, mono_marshal_get_runtime_invoke (method)); - } - - if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) { - MonoMethodDesc *desc; - MonoMethod *orig_method; - - /* JIT icall wrappers */ - /* FIXME: locking */ - g_hash_table_foreach (mono_get_jit_icall_info (), add_jit_icall_wrapper, acfg); - - /* Managed Allocators */ - nallocators = mono_gc_get_managed_allocator_types (); - for (i = 0; i < nallocators; ++i) { - m = mono_gc_get_managed_allocator_by_type (i); - if (m) - add_method (acfg, m); - } - - /* stelemref */ - add_method (acfg, mono_marshal_get_stelemref ()); - - /* Monitor Enter/Exit */ - desc = mono_method_desc_new ("Monitor:Enter", FALSE); - orig_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class); - g_assert (orig_method); - mono_method_desc_free (desc); - method = mono_monitor_get_fast_path (orig_method); - if (method) - add_method (acfg, method); - - desc = mono_method_desc_new ("Monitor:Exit", FALSE); - orig_method = mono_method_desc_search_in_class (desc, mono_defaults.monitor_class); - g_assert (orig_method); - mono_method_desc_free (desc); - method = mono_monitor_get_fast_path (orig_method); - if (method) - add_method (acfg, method); - } - - /* remoting-invoke wrappers */ - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - MonoMethodSignature *sig; - - token = MONO_TOKEN_METHOD_DEF | (i + 1); - method = mono_get_method (acfg->image, token, NULL); - - sig = mono_method_signature (method); - - if (sig->hasthis && (method->klass->marshalbyref || method->klass == mono_defaults.object_class) && - !(method->flags & METHOD_ATTRIBUTE_VIRTUAL)) { - m = mono_marshal_get_remoting_invoke_with_check (method); + if (!skip) { + /* Find next patch */ + patch_info = NULL; + for (pindex = start_index; pindex < patches->len; ++pindex) { + patch_info = g_ptr_array_index (patches, pindex); + if (patch_info->ip.i >= i) + break; + } - add_method (acfg, m); + /* Try to emit multiple bytes at once */ + if (pindex < patches->len && patch_info->ip.i > i) { + emit_bytes (acfg, code + i, patch_info->ip.i - i); + i = patch_info->ip.i - 1; + } else { + emit_bytes (acfg, code + i, 1); + } } } +} - /* delegate-invoke wrappers */ - for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { - MonoClass *klass; - - token = MONO_TOKEN_TYPE_DEF | (i + 1); - klass = mono_class_get (acfg->image, token); +static void +emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg) +{ + MonoMethod *method; + int method_index; + guint8 *code; + char symbol [128]; + int func_alignment = 16; + MonoMethodHeader *header; - if (klass->delegate && klass != mono_defaults.delegate_class && klass != mono_defaults.multicastdelegate_class && !klass->generic_container) { - method = mono_get_delegate_invoke (klass); + method = cfg->orig_method; + code = cfg->native_code; + header = mono_method_get_header (method); - m = mono_marshal_get_delegate_invoke (method, NULL); + method_index = get_method_index (acfg, method); - add_method (acfg, m); + /* Make the labels local */ + sprintf (symbol, ".Lm_%x", method_index); - method = mono_class_get_method_from_name_flags (klass, "BeginInvoke", -1, 0); - add_method (acfg, mono_marshal_get_delegate_begin_invoke (method)); + emit_alignment (acfg, func_alignment); + emit_label (acfg, symbol); - method = mono_class_get_method_from_name_flags (klass, "EndInvoke", -1, 0); - add_method (acfg, mono_marshal_get_delegate_end_invoke (method)); + if (acfg->aot_opts.write_symbols && !acfg->aot_opts.nodebug) { + char *name1, *name2; + int i, j, len; + + name1 = mono_method_full_name (method, TRUE); + len = strlen (name1); + name2 = malloc (len + 1); + j = 0; + for (i = 0; i < len; ++i) { + if (isalnum (name1 [i])) { + name2 [j ++] = name1 [i]; + } else if (name1 [i] == ' ' && name1 [i + 1] == '(' && name1 [i + 2] == ')') { + i += 2; + } else if (name1 [i] == '(' || name1 [i] == ')') { + } else + name2 [j ++] = '_'; } + name2 [j] = '\0'; + sprintf (symbol, ".Lme_%x", method_index); + emit_local_symbol (acfg, name2, symbol, TRUE); + emit_label (acfg, name2); + g_free (name1); + g_free (name2); } - /* Synchronized wrappers */ - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - token = MONO_TOKEN_METHOD_DEF | (i + 1); - method = mono_get_method (acfg->image, token, NULL); - - if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) - add_method (acfg, mono_marshal_get_synchronized_wrapper (method)); - } + if (cfg->verbose_level > 0) + g_print ("Method %s emitted as %s\n", mono_method_full_name (method, TRUE), symbol); -#if 0 - /* static rgctx wrappers */ - /* FIXME: Each wrapper belongs to a given instantiation of a generic method */ - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - token = MONO_TOKEN_METHOD_DEF | (i + 1); - method = mono_get_method (acfg->image, token, NULL); + acfg->stats.code_size += cfg->code_len; - if (((method->flags & METHOD_ATTRIBUTE_STATIC) || - (method->is_inflated && mono_method_get_context (method)->method_inst)) && - mono_class_generic_sharing_enabled (method->klass) && - mono_method_is_generic_sharable_impl (method, FALSE)) { - m = mono_marshal_get_static_rgctx_invoke (method); - add_method (acfg, m); - } - } -#endif + acfg->cfgs [method_index]->got_offset = acfg->got_offset; - /* pinvoke wrappers */ - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - MonoMethod *method; - guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); + emit_and_reloc_code (acfg, method, code, cfg->code_len, cfg->patch_info, FALSE); - method = mono_get_method (acfg->image, token, NULL); + emit_line (acfg); - if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) - add_method (acfg, mono_marshal_get_native_wrapper (method, TRUE, TRUE)); - } + sprintf (symbol, ".Lme_%x", method_index); + emit_label (acfg, symbol); } -static gboolean -has_type_vars (MonoClass *klass) +/** + * encode_patch: + * + * Encode PATCH_INFO into its disk representation. + */ +static void +encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint8 **endbuf) { - if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) - return TRUE; - if (klass->rank) - return has_type_vars (klass->element_class); - if (klass->generic_class) { - MonoGenericContext *context = &klass->generic_class->context; - if (context->class_inst) { - int i; + guint8 *p = buf; - for (i = 0; i < context->class_inst->type_argc; ++i) - if (has_type_vars (mono_class_from_mono_type (context->class_inst->type_argv [i]))) - return TRUE; - } - } - return FALSE; -} - -static gboolean -method_has_type_vars (MonoMethod *method) -{ - if (has_type_vars (method->klass)) - return TRUE; - - if (method->is_inflated) { - MonoGenericContext *context = mono_method_get_context (method); - if (context->method_inst) { - int i; + switch (patch_info->type) { + case MONO_PATCH_INFO_NONE: + break; + case MONO_PATCH_INFO_IMAGE: + encode_value (get_image_index (acfg, patch_info->data.image), p, &p); + break; + case MONO_PATCH_INFO_METHOD_REL: + encode_value ((gint)patch_info->data.offset, p, &p); + break; + case MONO_PATCH_INFO_SWITCH: { + gpointer *table = (gpointer *)patch_info->data.table->table; + int k; - for (i = 0; i < context->method_inst->type_argc; ++i) - if (has_type_vars (mono_class_from_mono_type (context->method_inst->type_argv [i]))) - return TRUE; - } + encode_value (patch_info->data.table->table_size, p, &p); + for (k = 0; k < patch_info->data.table->table_size; k++) + encode_value ((int)(gssize)table [k], p, &p); + break; } - return FALSE; -} - -/* - * add_generic_instances: - * - * Add instances referenced by the METHODSPEC/TYPESPEC table. - */ -static void -add_generic_instances (MonoAotCompile *acfg) -{ - int i; - guint32 token; - MonoMethod *method; - MonoGenericContext *context; - - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHODSPEC].rows; ++i) { - token = MONO_TOKEN_METHOD_SPEC | (i + 1); - method = mono_get_method (acfg->image, token, NULL); + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHOD_JUMP: + case MONO_PATCH_INFO_ICALL_ADDR: + case MONO_PATCH_INFO_METHOD_RGCTX: + encode_method_ref (acfg, patch_info->data.method, p, &p); + break; + case MONO_PATCH_INFO_INTERNAL_METHOD: + case MONO_PATCH_INFO_JIT_ICALL_ADDR: { + guint32 len = strlen (patch_info->data.name); - context = mono_method_get_context (method); - if (context && ((context->class_inst && context->class_inst->is_open) || - (context->method_inst && context->method_inst->is_open))) - continue; + encode_value (len, p, &p); - if (method->klass->image != acfg->image) - continue; + memcpy (p, patch_info->data.name, len); + p += len; + *p++ = '\0'; + break; + } + case MONO_PATCH_INFO_LDSTR: { + guint32 image_index = get_image_index (acfg, patch_info->data.token->image); + guint32 token = patch_info->data.token->token; + g_assert (mono_metadata_token_code (token) == MONO_TOKEN_STRING); + encode_value (image_index, p, &p); + encode_value (patch_info->data.token->token - MONO_TOKEN_STRING, p, &p); + break; + } + case MONO_PATCH_INFO_RVA: + case MONO_PATCH_INFO_DECLSEC: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + encode_value (get_image_index (acfg, patch_info->data.token->image), p, &p); + encode_value (patch_info->data.token->token, p, &p); + encode_value (patch_info->data.token->has_context, p, &p); + if (patch_info->data.token->has_context) + encode_generic_context (acfg, &patch_info->data.token->context, p, &p); + break; + case MONO_PATCH_INFO_EXC_NAME: { + MonoClass *ex_class; - if (mono_method_is_generic_sharable_impl (method, FALSE)) - /* Already added */ - continue; + ex_class = + mono_class_from_name (mono_defaults.exception_class->image, + "System", patch_info->data.target); + g_assert (ex_class); + encode_klass_ref (acfg, ex_class, p, &p); + break; + } + case MONO_PATCH_INFO_R4: + encode_value (*((guint32 *)patch_info->data.target), p, &p); + break; + case MONO_PATCH_INFO_R8: + encode_value (*((guint32 *)patch_info->data.target), p, &p); + encode_value (*(((guint32 *)patch_info->data.target) + 1), p, &p); + break; + case MONO_PATCH_INFO_VTABLE: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_ADJUSTED_IID: + encode_klass_ref (acfg, patch_info->data.klass, p, &p); + break; + case MONO_PATCH_INFO_CLASS_INIT: + case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: + encode_klass_ref (acfg, patch_info->data.klass, p, &p); + break; + case MONO_PATCH_INFO_FIELD: + case MONO_PATCH_INFO_SFLDA: + encode_field_info (acfg, patch_info->data.field, p, &p); + break; + case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: + break; + case MONO_PATCH_INFO_RGCTX_FETCH: { + MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry; - add_extra_method (acfg, method); + encode_method_ref (acfg, entry->method, p, &p); + encode_value (entry->in_mrgctx, p, &p); + encode_value (entry->info_type, p, &p); + encode_value (entry->data->type, p, &p); + encode_patch (acfg, entry->data, p, &p); + break; + } + case MONO_PATCH_INFO_GENERIC_CLASS_INIT: + case MONO_PATCH_INFO_MONITOR_ENTER: + case MONO_PATCH_INFO_MONITOR_EXIT: + break; + default: + g_warning ("unable to handle jump info %d", patch_info->type); + g_assert_not_reached (); } - for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows; ++i) { - MonoClass *klass; - gpointer iter; + *endbuf = p; +} - token = MONO_TOKEN_TYPE_SPEC | (i + 1); +static void +encode_patch_list (MonoAotCompile *acfg, GPtrArray *patches, int n_patches, int first_got_offset, guint8 *buf, guint8 **endbuf) +{ + guint8 *p = buf; + guint32 pindex; + MonoJumpInfo *patch_info; - klass = mono_class_get (acfg->image, token); - if (!klass) - continue; - mono_class_init (klass); + encode_value (n_patches, p, &p); - if (klass->generic_class && klass->generic_class->context.class_inst->is_open) - continue; + if (n_patches) + encode_value (first_got_offset, p, &p); - if (has_type_vars (klass)) - continue; + for (pindex = 0; pindex < patches->len; ++pindex) { + patch_info = g_ptr_array_index (patches, pindex); - if (!klass->generic_class && !klass->rank) + if (patch_info->type == MONO_PATCH_INFO_NONE) + /* Nothing to do */ continue; - /* - * Add rgctx wrappers for cctors since those are called by the runtime, so - * there is no methodspec for them. This is needed even for shared classes, - * since rgctx wrappers belong to inflated methods. - */ - method = mono_class_get_cctor (klass); - if (method) - add_extra_method (acfg, mono_marshal_get_static_rgctx_invoke (method)); - - iter = NULL; - while ((method = mono_class_get_methods (klass, &iter))) { - if (mono_method_is_generic_sharable_impl (method, FALSE)) - /* Already added */ - continue; - - if (method->is_generic) - /* FIXME: */ - continue; - - /* - * FIXME: Instances which are referenced by these methods are not added, - * for example Array.Resize for List.Add (). - */ - add_extra_method (acfg, method); + encode_value (patch_info->type, p, &p); + if (mono_aot_is_shared_got_patch (patch_info)) { + guint32 offset = get_got_offset (acfg, patch_info); + encode_value (offset, p, &p); + } else { + encode_patch (acfg, patch_info, p, &p); } } + + *endbuf = p; } static void -emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, guint32 code_len, MonoJumpInfo *relocs, gboolean got_only) +emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg) { - int i, pindex, start_index, method_index; + MonoMethod *method; + GList *l; + int pindex, buf_size, n_patches; + guint8 *code; + char symbol [128]; GPtrArray *patches; MonoJumpInfo *patch_info; MonoMethodHeader *header; - gboolean skip, direct_call; - guint32 got_slot; - char direct_call_target [128]; + guint32 method_index; + guint8 *p, *buf; + guint32 first_got_offset; - if (method) { - header = mono_method_get_header (method); + method = cfg->orig_method; + code = cfg->native_code; + header = mono_method_get_header (method); - method_index = get_method_index (acfg, method); - } + method_index = get_method_index (acfg, method); - /* Collect and sort relocations */ + /* Make the labels local */ + sprintf (symbol, ".Lm_%x_p", method_index); + + /* Sort relocations */ patches = g_ptr_array_new (); - for (patch_info = relocs; patch_info; patch_info = patch_info->next) + for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) g_ptr_array_add (patches, patch_info); g_ptr_array_sort (patches, compare_patches); - start_index = 0; - for (i = 0; i < code_len; i++) { - patch_info = NULL; - for (pindex = start_index; pindex < patches->len; ++pindex) { - patch_info = g_ptr_array_index (patches, pindex); - if (patch_info->ip.i >= i) - break; - } - -#ifdef MONO_ARCH_AOT_SUPPORTED - skip = FALSE; - if (patch_info && (patch_info->ip.i == i) && (pindex < patches->len)) { - start_index = pindex; + first_got_offset = acfg->cfgs [method_index]->got_offset; - switch (patch_info->type) { - case MONO_PATCH_INFO_NONE: - break; - case MONO_PATCH_INFO_GOT_OFFSET: { - guint32 offset = mono_arch_get_patch_offset (code + i); - emit_bytes (acfg, code + i, offset); - emit_symbol_diff (acfg, "got", ".", offset); + /**********************/ + /* Encode method info */ + /**********************/ - i += offset + 4 - 1; - skip = TRUE; - break; - } - default: { - if (!is_got_patch (patch_info->type)) - break; + buf_size = (patches->len < 1000) ? 40960 : 40960 + (patches->len * 64); + p = buf = g_malloc (buf_size); - /* - * If this patch is a call, try emitting a direct call instead of - * through a PLT entry. This is possible if the called method is in - * the same assembly and requires no initialization. - */ - direct_call = FALSE; - if (!got_only && (patch_info->type == MONO_PATCH_INFO_METHOD) && (patch_info->data.method->klass->image == method->klass->image)) { - MonoCompile *callee_cfg = g_hash_table_lookup (acfg->method_to_cfg, patch_info->data.method); - if (callee_cfg) { - gboolean direct_callable = TRUE; + if (mono_class_get_cctor (method->klass)) + encode_klass_ref (acfg, method->klass, p, &p); + else + /* Not needed when loading the method */ + encode_value (0, p, &p); - if (direct_callable && !(!callee_cfg->has_got_slots && (callee_cfg->method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT))) - direct_callable = FALSE; - if ((callee_cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) && method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED) - // FIXME: Maybe call the wrapper directly ? - direct_callable = FALSE; - if (direct_callable) { - //printf ("DIRECT: %s %s\n", method ? mono_method_full_name (method, TRUE) : "", mono_method_full_name (callee_cfg->method, TRUE)); - direct_call = TRUE; - sprintf (direct_call_target, ".Lm_%x", get_method_index (acfg, callee_cfg->orig_method)); - patch_info->type = MONO_PATCH_INFO_NONE; - acfg->stats.direct_calls ++; - } - } - - acfg->stats.all_calls ++; - } + /* String table */ + if (cfg->opt & MONO_OPT_SHARED) { + encode_value (g_list_length (cfg->ldstr_list), p, &p); + for (l = cfg->ldstr_list; l; l = l->next) { + encode_value ((long)l->data, p, &p); + } + } + else + /* Used only in shared mode */ + g_assert (!cfg->ldstr_list); - if (!got_only && !direct_call) { - int plt_offset = get_plt_offset (acfg, patch_info); - if (plt_offset != -1) { - /* This patch has a PLT entry, so we must emit a call to the PLT entry */ - direct_call = TRUE; - sprintf (direct_call_target, ".Lp_%d", plt_offset); + n_patches = 0; + for (pindex = 0; pindex < patches->len; ++pindex) { + patch_info = g_ptr_array_index (patches, pindex); - /* Nullify the patch */ - patch_info->type = MONO_PATCH_INFO_NONE; - } - } + if ((patch_info->type == MONO_PATCH_INFO_GOT_OFFSET) || + (patch_info->type == MONO_PATCH_INFO_NONE)) { + patch_info->type = MONO_PATCH_INFO_NONE; + /* Nothing to do */ + continue; + } - if (direct_call) { -#if defined(__i386__) || defined(__x86_64__) - g_assert (code [i] == 0xe8); - /* Need to make sure this is exactly 5 bytes long */ - emit_byte (acfg, '\xe8'); - emit_symbol_diff (acfg, direct_call_target, ".", -4); - i += 4; -#elif defined(__arm__) -#ifdef USE_BIN_WRITER - { - guint8 buf [4]; - guint8 *code; - - code = buf; - ARM_BL (code, 0); - - bin_writer_emit_reloc (acfg, R_ARM_CALL, direct_call_target, -8); - emit_bytes (acfg, buf, 4); - } -#else - asm_writer_emit_unset_mode (acfg); - fprintf (acfg->fp, "bl %s\n", direct_call_target); -#endif - i += 4 - 1; -#else - g_assert_not_reached (); -#endif - } else { - got_slot = get_got_offset (acfg, patch_info); + if ((patch_info->type == MONO_PATCH_INFO_IMAGE) && (patch_info->data.image == acfg->image)) { + /* Stored in a GOT slot initialized at module load time */ + patch_info->type = MONO_PATCH_INFO_NONE; + continue; + } - emit_bytes (acfg, code + i, mono_arch_get_patch_offset (code + i)); -#ifdef __x86_64__ - emit_symbol_diff (acfg, "got", ".", (unsigned int) ((got_slot * sizeof (gpointer)) - 4)); -#elif defined(__i386__) - emit_int32 (acfg, (unsigned int) ((got_slot * sizeof (gpointer)))); -#elif defined(__arm__) - emit_symbol_diff (acfg, "got", ".", (unsigned int) ((got_slot * sizeof (gpointer))) - 12); -#else - g_assert_not_reached (); -#endif - - i += mono_arch_get_patch_offset (code + i) + 4 - 1; - } - skip = TRUE; - } - } + if (is_plt_patch (patch_info)) { + /* Calls are made through the PLT */ + patch_info->type = MONO_PATCH_INFO_NONE; + continue; } -#endif /* MONO_ARCH_AOT_SUPPORTED */ - if (!skip) { - /* Find next patch */ - patch_info = NULL; - for (pindex = start_index; pindex < patches->len; ++pindex) { - patch_info = g_ptr_array_index (patches, pindex); - if (patch_info->ip.i >= i) - break; - } + n_patches ++; + } - /* Try to emit multiple bytes at once */ - if (pindex < patches->len && patch_info->ip.i > i) { - emit_bytes (acfg, code + i, patch_info->ip.i - i); - i = patch_info->ip.i - 1; - } else { - emit_bytes (acfg, code + i, 1); - } - } + if (n_patches) + g_assert (cfg->has_got_slots); + + encode_patch_list (acfg, patches, n_patches, first_got_offset, p, &p); + + acfg->stats.info_size += p - buf; + + /* Emit method info */ + + emit_label (acfg, symbol); + + g_assert (p - buf < buf_size); + emit_bytes (acfg, buf, p - buf); + g_free (buf); +} + +static guint32 +get_unwind_info_offset (MonoAotCompile *acfg, guint8 *encoded, guint32 encoded_len) +{ + guint32 cache_index; + guint32 offset; + + /* Reuse the unwind module to canonize and store unwind info entries */ + cache_index = mono_cache_unwind_info (encoded, encoded_len); + + /* Use +/- 1 to distinguish 0s from missing entries */ + offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->unwind_info_offsets, GUINT_TO_POINTER (cache_index + 1))); + if (offset) + return offset - 1; + else { + guint8 buf [16]; + guint8 *p; + + /* + * It would be easier to use assembler symbols, but the caller needs an + * offset now. + */ + offset = acfg->unwind_info_offset; + g_hash_table_insert (acfg->unwind_info_offsets, GUINT_TO_POINTER (cache_index + 1), GUINT_TO_POINTER (offset + 1)); + g_ptr_array_add (acfg->unwind_ops, GUINT_TO_POINTER (cache_index)); + + p = buf; + encode_value (encoded_len, p, &p); + + acfg->unwind_info_offset += encoded_len + (p - buf); + return offset; } } static void -emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg) +emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg) { MonoMethod *method; - int method_index; + int k, buf_size, method_index; + guint32 debug_info_size; guint8 *code; char symbol [128]; - int func_alignment = 16; MonoMethodHeader *header; + guint8 *p, *buf, *debug_info; + MonoJitInfo *jinfo = cfg->jit_info; + guint32 flags; + gboolean use_unwind_ops = FALSE; method = cfg->orig_method; code = cfg->native_code; @@ -3161,1026 +2350,679 @@ emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg) method_index = get_method_index (acfg, method); /* Make the labels local */ - sprintf (symbol, ".Lm_%x", method_index); + sprintf (symbol, ".Le_%x_p", method_index); - emit_alignment (acfg, func_alignment); - emit_label (acfg, symbol); + if (!acfg->aot_opts.nodebug) { + mono_debug_serialize_debug_info (cfg, &debug_info, &debug_info_size); + } else { + debug_info = NULL; + debug_info_size = 0; + } - if (acfg->aot_opts.write_symbols && acfg->use_bin_writer) { - char *full_name; - /* Emit a local symbol into the symbol table */ - full_name = mono_method_full_name (method, TRUE); - sprintf (symbol, ".Lme_%x", method_index); - emit_local_symbol (acfg, full_name, symbol, TRUE); - emit_label (acfg, full_name); - g_free (full_name); + buf_size = header->num_clauses * 256 + debug_info_size + 1024; + p = buf = g_malloc (buf_size); + +#ifdef MONO_ARCH_HAVE_XP_UNWIND + use_unwind_ops = cfg->unwind_ops != NULL; +#endif + + flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0); + + encode_value (jinfo->code_size, p, &p); + encode_value (flags, p, &p); + + if (use_unwind_ops) { + guint32 encoded_len; + guint8 *encoded; + + /* + * This is a duplicate of the data in the .debug_frame section, but that + * section cannot be accessed using the dl interface. + */ + encoded = mono_unwind_ops_encode (cfg->unwind_ops, &encoded_len); + encode_value (get_unwind_info_offset (acfg, encoded, encoded_len), p, &p); + g_free (encoded); + } else { + encode_value (jinfo->used_regs, p, &p); } - if (cfg->verbose_level > 0) - g_print ("Method %s emitted as %s\n", mono_method_full_name (method, TRUE), symbol); + /* Exception table */ + if (header->num_clauses) { + for (k = 0; k < header->num_clauses; ++k) { + MonoJitExceptionInfo *ei = &jinfo->clauses [k]; - acfg->stats.code_size += cfg->code_len; + encode_value (ei->exvar_offset, p, &p); - acfg->cfgs [method_index]->got_offset = acfg->got_offset; + if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) + encode_value ((gint)((guint8*)ei->data.filter - code), p, &p); - emit_and_reloc_code (acfg, method, code, cfg->code_len, cfg->patch_info, FALSE); + encode_value ((gint)((guint8*)ei->try_start - code), p, &p); + encode_value ((gint)((guint8*)ei->try_end - code), p, &p); + encode_value ((gint)((guint8*)ei->handler_start - code), p, &p); + } + } - emit_line (acfg); + if (jinfo->has_generic_jit_info) { + MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (jinfo); + + encode_value (gi->has_this ? 1 : 0, p, &p); + encode_value (gi->this_reg, p, &p); + encode_value (gi->this_offset, p, &p); + + /* + * Need to encode jinfo->method too, since it is not equal to 'method' + * when using generic sharing. + */ + encode_method_ref (acfg, jinfo->method, p, &p); + } + + g_assert (debug_info_size < buf_size); + + encode_value (debug_info_size, p, &p); + if (debug_info_size) { + memcpy (p, debug_info, debug_info_size); + p += debug_info_size; + g_free (debug_info); + } + + acfg->stats.ex_info_size += p - buf; + + /* Emit info */ - sprintf (symbol, ".Lme_%x", method_index); emit_label (acfg, symbol); + + g_assert (p - buf < buf_size); + emit_bytes (acfg, buf, p - buf); + g_free (buf); } -/** - * encode_patch: - * - * Encode PATCH_INFO into its disk representation. - */ static void -encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint8 **endbuf) +emit_klass_info (MonoAotCompile *acfg, guint32 token) { - guint8 *p = buf; + MonoClass *klass = mono_class_get (acfg->image, token); + guint8 *p, *buf; + int i, buf_size; + char symbol [128]; + gboolean no_special_static, cant_encode; + gpointer iter = NULL; - switch (patch_info->type) { - case MONO_PATCH_INFO_NONE: - break; - case MONO_PATCH_INFO_IMAGE: - encode_value (get_image_index (acfg, patch_info->data.image), p, &p); - break; - case MONO_PATCH_INFO_METHOD_REL: - encode_value ((gint)patch_info->data.offset, p, &p); - break; - case MONO_PATCH_INFO_SWITCH: { - gpointer *table = (gpointer *)patch_info->data.table->table; - int k; + buf_size = 10240 + (klass->vtable_size * 16); + p = buf = g_malloc (buf_size); - encode_value (patch_info->data.table->table_size, p, &p); - for (k = 0; k < patch_info->data.table->table_size; k++) - encode_value ((int)(gssize)table [k], p, &p); - break; - } - case MONO_PATCH_INFO_METHODCONST: - case MONO_PATCH_INFO_METHOD: - case MONO_PATCH_INFO_METHOD_JUMP: - case MONO_PATCH_INFO_ICALL_ADDR: - case MONO_PATCH_INFO_METHOD_RGCTX: - encode_method_ref (acfg, patch_info->data.method, p, &p); - break; - case MONO_PATCH_INFO_INTERNAL_METHOD: - case MONO_PATCH_INFO_JIT_ICALL_ADDR: { - guint32 len = strlen (patch_info->data.name); + g_assert (klass); - encode_value (len, p, &p); + mono_class_init (klass); - memcpy (p, patch_info->data.name, len); - p += len; - *p++ = '\0'; - break; - } - case MONO_PATCH_INFO_LDSTR: { - guint32 image_index = get_image_index (acfg, patch_info->data.token->image); - guint32 token = patch_info->data.token->token; - g_assert (mono_metadata_token_code (token) == MONO_TOKEN_STRING); - encode_value (image_index, p, &p); - encode_value (patch_info->data.token->token - MONO_TOKEN_STRING, p, &p); - break; - } - case MONO_PATCH_INFO_RVA: - case MONO_PATCH_INFO_DECLSEC: - case MONO_PATCH_INFO_LDTOKEN: - case MONO_PATCH_INFO_TYPE_FROM_HANDLE: - encode_value (get_image_index (acfg, patch_info->data.token->image), p, &p); - encode_value (patch_info->data.token->token, p, &p); - encode_value (patch_info->data.token->has_context, p, &p); - if (patch_info->data.token->has_context) - encode_generic_context (acfg, &patch_info->data.token->context, p, &p); - break; - case MONO_PATCH_INFO_EXC_NAME: { - MonoClass *ex_class; + mono_class_get_nested_types (klass, &iter); + g_assert (klass->nested_classes_inited); - ex_class = - mono_class_from_name (mono_defaults.exception_class->image, - "System", patch_info->data.target); - g_assert (ex_class); - encode_klass_ref (acfg, ex_class, p, &p); - break; - } - case MONO_PATCH_INFO_R4: - encode_value (*((guint32 *)patch_info->data.target), p, &p); - break; - case MONO_PATCH_INFO_R8: - encode_value (*((guint32 *)patch_info->data.target), p, &p); - encode_value (*(((guint32 *)patch_info->data.target) + 1), p, &p); - break; - case MONO_PATCH_INFO_VTABLE: - case MONO_PATCH_INFO_CLASS: - case MONO_PATCH_INFO_IID: - case MONO_PATCH_INFO_ADJUSTED_IID: - encode_klass_ref (acfg, patch_info->data.klass, p, &p); - break; - case MONO_PATCH_INFO_CLASS_INIT: - case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: - encode_klass_ref (acfg, patch_info->data.klass, p, &p); - break; - case MONO_PATCH_INFO_FIELD: - case MONO_PATCH_INFO_SFLDA: - encode_field_info (acfg, patch_info->data.field, p, &p); - break; - case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG: - break; - case MONO_PATCH_INFO_RGCTX_FETCH: { - MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry; + mono_class_setup_vtable (klass); - encode_method_ref (acfg, entry->method, p, &p); - encode_value (entry->in_mrgctx, p, &p); - encode_value (entry->info_type, p, &p); - encode_value (entry->data->type, p, &p); - encode_patch (acfg, entry->data, p, &p); - break; + /* + * Emit all the information which is required for creating vtables so + * the runtime does not need to create the MonoMethod structures which + * take up a lot of space. + */ + + no_special_static = !mono_class_has_special_static_fields (klass); + + /* Check whenever we have enough info to encode the vtable */ + cant_encode = FALSE; + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm = klass->vtable [i]; + + if (cm && mono_method_signature (cm)->is_inflated && !g_hash_table_lookup (acfg->token_info_hash, cm)) + cant_encode = TRUE; } - case MONO_PATCH_INFO_GENERIC_CLASS_INIT: - case MONO_PATCH_INFO_MONITOR_ENTER: - case MONO_PATCH_INFO_MONITOR_EXIT: - break; - default: - g_warning ("unable to handle jump info %d", patch_info->type); - g_assert_not_reached (); + + if (klass->generic_container || cant_encode) { + encode_value (-1, p, &p); + } else { + encode_value (klass->vtable_size, p, &p); + encode_value ((no_special_static << 7) | (klass->has_static_refs << 6) | (klass->has_references << 5) | ((klass->blittable << 4) | ((klass->ext && klass->ext->nested_classes) ? 1 : 0) << 3) | (klass->has_cctor << 2) | (klass->has_finalize << 1) | klass->ghcimpl, p, &p); + if (klass->has_cctor) + encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p); + if (klass->has_finalize) + encode_method_ref (acfg, mono_class_get_finalizer (klass), p, &p); + + encode_value (klass->instance_size, p, &p); + encode_value (mono_class_data_size (klass), p, &p); + encode_value (klass->packing_size, p, &p); + encode_value (klass->min_align, p, &p); + + for (i = 0; i < klass->vtable_size; ++i) { + MonoMethod *cm = klass->vtable [i]; + + if (cm) + encode_method_ref (acfg, cm, p, &p); + else + encode_value (0, p, &p); + } } - *endbuf = p; + acfg->stats.class_info_size += p - buf; + + /* Emit the info */ + sprintf (symbol, ".LK_I_%x", token - MONO_TOKEN_TYPE_DEF - 1); + emit_label (acfg, symbol); + + g_assert (p - buf < buf_size); + emit_bytes (acfg, buf, p - buf); + g_free (buf); } +/* + * Calls made from AOTed code are routed through a table of jumps similar to the + * ELF PLT (Program Linkage Table). The differences are the following: + * - the ELF PLT entries make an indirect jump though the GOT so they expect the + * GOT pointer to be in EBX. We want to avoid this, so our table contains direct + * jumps. This means the jumps need to be patched when the address of the callee is + * known. Initially the PLT entries jump to code which transfers control to the + * AOT runtime through the first PLT entry. + */ static void -encode_patch_list (MonoAotCompile *acfg, GPtrArray *patches, int n_patches, int first_got_offset, guint8 *buf, guint8 **endbuf) +emit_plt (MonoAotCompile *acfg) { - guint8 *p = buf; - guint32 last_offset, j, pindex; - MonoJumpInfo *patch_info; - - encode_value (n_patches, p, &p); - - if (n_patches) - encode_value (first_got_offset, p, &p); + char symbol [128]; + int i; - /* First encode the type+position table */ - last_offset = 0; - j = 0; - for (pindex = 0; pindex < patches->len; ++pindex) { - guint32 offset; - patch_info = g_ptr_array_index (patches, pindex); - - if (patch_info->type == MONO_PATCH_INFO_NONE) - /* Nothing to do */ - continue; + emit_line (acfg); + sprintf (symbol, "plt"); - j ++; - //printf ("T: %d O: %d.\n", patch_info->type, patch_info->ip.i); - offset = patch_info->ip.i - last_offset; - last_offset = patch_info->ip.i; + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, TRUE); +#ifdef TARGET_X86 + /* This section will be made read-write by the AOT loader */ + emit_alignment (acfg, mono_pagesize ()); +#else + emit_alignment (acfg, 16); +#endif + emit_label (acfg, symbol); - /* Only the type is needed */ - *p = patch_info->type; - p++; - } + for (i = 0; i < acfg->plt_offset; ++i) { + char label [128]; - /* Then encode the other info */ - for (pindex = 0; pindex < patches->len; ++pindex) { - patch_info = g_ptr_array_index (patches, pindex); + sprintf (label, ".Lp_%d", i); + emit_label (acfg, label); - if (is_shared_got_patch (patch_info)) { - guint32 offset = get_got_offset (acfg, patch_info); - encode_value (offset, p, &p); - } else { - encode_patch (acfg, patch_info, p, &p); - } + /* + * The first plt entry is used to transfer code to the AOT loader. + */ + arch_emit_plt_entry (acfg, i); } - *endbuf = p; + sprintf (symbol, "plt_end"); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); } -static void -emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg) +static G_GNUC_UNUSED void +emit_trampoline (MonoAotCompile *acfg, const char *name, guint8 *code, + guint32 code_size, int got_offset, MonoJumpInfo *ji, GSList *unwind_ops) { - MonoMethod *method; - GList *l; - int pindex, buf_size, n_patches; - guint8 *code; - char symbol [128]; - GPtrArray *patches; + char symbol [256]; + guint32 buf_size; MonoJumpInfo *patch_info; - MonoMethodHeader *header; - guint32 method_index; - guint8 *p, *buf; - guint32 first_got_offset; + guint8 *buf, *p; + GPtrArray *patches; - method = cfg->orig_method; - code = cfg->native_code; - header = mono_method_get_header (method); + /* Emit code */ - method_index = get_method_index (acfg, method); + sprintf (symbol, "%s", name); - /* Make the labels local */ - sprintf (symbol, ".Lm_%x_p", method_index); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, TRUE); + emit_alignment (acfg, 16); + emit_label (acfg, symbol); + + sprintf (symbol, ".Lnamed_%s", name); + emit_label (acfg, symbol); + + /* + * The code should access everything through the GOT, so we pass + * TRUE here. + */ + emit_and_reloc_code (acfg, NULL, code, code_size, ji, TRUE); + + /* Emit info */ /* Sort relocations */ patches = g_ptr_array_new (); - for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) + for (patch_info = ji; patch_info; patch_info = patch_info->next) g_ptr_array_add (patches, patch_info); g_ptr_array_sort (patches, compare_patches); - first_got_offset = acfg->cfgs [method_index]->got_offset; - - /**********************/ - /* Encode method info */ - /**********************/ - - buf_size = (patches->len < 1000) ? 40960 : 40960 + (patches->len * 64); - p = buf = g_malloc (buf_size); + buf_size = patches->len * 128 + 128; + buf = g_malloc (buf_size); + p = buf; - if (mono_class_get_cctor (method->klass)) - encode_klass_ref (acfg, method->klass, p, &p); - else - /* Not needed when loading the method */ - encode_value (0, p, &p); + encode_patch_list (acfg, patches, patches->len, got_offset, p, &p); + g_assert (p - buf < buf_size); - /* String table */ - if (cfg->opt & MONO_OPT_SHARED) { - encode_value (g_list_length (cfg->ldstr_list), p, &p); - for (l = cfg->ldstr_list; l; l = l->next) { - encode_value ((long)l->data, p, &p); - } - } - else - /* Used only in shared mode */ - g_assert (!cfg->ldstr_list); + sprintf (symbol, "%s_p", name); - n_patches = 0; - for (pindex = 0; pindex < patches->len; ++pindex) { - patch_info = g_ptr_array_index (patches, pindex); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, FALSE); + emit_label (acfg, symbol); - if ((patch_info->type == MONO_PATCH_INFO_GOT_OFFSET) || - (patch_info->type == MONO_PATCH_INFO_NONE)) { - patch_info->type = MONO_PATCH_INFO_NONE; - /* Nothing to do */ - continue; - } + emit_bytes (acfg, buf, p - buf); - if ((patch_info->type == MONO_PATCH_INFO_IMAGE) && (patch_info->data.image == acfg->image)) { - /* Stored in a GOT slot initialized at module load time */ - patch_info->type = MONO_PATCH_INFO_NONE; - continue; - } + /* Emit debug info */ + if (unwind_ops) { + char symbol2 [256]; - if (is_plt_patch (patch_info)) { - /* Calls are made through the PLT */ - patch_info->type = MONO_PATCH_INFO_NONE; - continue; - } + sprintf (symbol, "%s", name); + sprintf (symbol2, ".Lnamed_%s", name); - n_patches ++; + if (acfg->dwarf) + mono_dwarf_writer_emit_trampoline (acfg->dwarf, symbol, symbol2, NULL, NULL, code_size, unwind_ops); } - - if (n_patches) - g_assert (cfg->has_got_slots); - - encode_patch_list (acfg, patches, n_patches, first_got_offset, p, &p); - - acfg->stats.info_size += p - buf; - - /* Emit method info */ - - emit_label (acfg, symbol); - - g_assert (p - buf < buf_size); - emit_bytes (acfg, buf, p - buf); - g_free (buf); } static void -emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg) +emit_trampolines (MonoAotCompile *acfg) { - MonoMethod *method; - int k, buf_size, method_index; - guint32 debug_info_size; + char symbol [256]; + int i, tramp_got_offset; + MonoAotTrampoline ntype; +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + int tramp_type; + guint32 code_size; + MonoJumpInfo *ji; guint8 *code; - char symbol [128]; - MonoMethodHeader *header; - guint8 *p, *buf, *debug_info; - MonoJitInfo *jinfo = cfg->jit_info; - guint32 flags; - gboolean use_unwind_ops = FALSE; + GSList *unwind_ops; +#endif - method = cfg->orig_method; - code = cfg->native_code; - header = mono_method_get_header (method); + if (!acfg->aot_opts.full_aot) + return; + + g_assert (acfg->image->assembly); - method_index = get_method_index (acfg, method); + /* Currently, we only emit most trampolines into the mscorlib AOT image. */ + if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) { +#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + /* + * Emit the generic trampolines. + * + * We could save some code by treating the generic trampolines as a wrapper + * method, but that approach has its own complexities, so we choose the simpler + * method. + */ + for (tramp_type = 0; tramp_type < MONO_TRAMPOLINE_NUM; ++tramp_type) { + code = mono_arch_create_trampoline_code_full (tramp_type, &code_size, &ji, &unwind_ops, TRUE); - /* Make the labels local */ - sprintf (symbol, ".Le_%x_p", method_index); + /* Emit trampoline code */ - mono_debug_serialize_debug_info (cfg, &debug_info, &debug_info_size); + sprintf (symbol, "generic_trampoline_%d", tramp_type); - buf_size = header->num_clauses * 256 + debug_info_size + 256; - p = buf = g_malloc (buf_size); + emit_trampoline (acfg, symbol, code, code_size, acfg->got_offset, ji, unwind_ops); + } -#if defined(__x86_64__) - use_unwind_ops = cfg->unwind_ops != NULL; + code = mono_arch_get_nullified_class_init_trampoline (&code_size); + emit_trampoline (acfg, "nullified_class_init_trampoline", code, code_size, acfg->got_offset, NULL, NULL); +#if defined(TARGET_AMD64) && defined(MONO_ARCH_MONITOR_OBJECT_REG) + code = mono_arch_create_monitor_enter_trampoline_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "monitor_enter_trampoline", code, code_size, acfg->got_offset, ji, NULL); + code = mono_arch_create_monitor_exit_trampoline_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "monitor_exit_trampoline", code, code_size, acfg->got_offset, ji, NULL); #endif - flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0); + code = mono_arch_create_generic_class_init_trampoline_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "generic_class_init_trampoline", code, code_size, acfg->got_offset, ji, NULL); - encode_value (jinfo->code_size, p, &p); - encode_value (flags, p, &p); + /* Emit the exception related code pieces */ + code = mono_arch_get_restore_context_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "restore_context", code, code_size, acfg->got_offset, ji, NULL); + code = mono_arch_get_call_filter_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "call_filter", code, code_size, acfg->got_offset, ji, NULL); + code = mono_arch_get_throw_exception_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "throw_exception", code, code_size, acfg->got_offset, ji, NULL); + code = mono_arch_get_rethrow_exception_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "rethrow_exception", code, code_size, acfg->got_offset, ji, NULL); + code = mono_arch_get_throw_exception_by_name_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "throw_exception_by_name", code, code_size, acfg->got_offset, ji, NULL); + code = mono_arch_get_throw_corlib_exception_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "throw_corlib_exception", code, code_size, acfg->got_offset, ji, NULL); - if (use_unwind_ops) { - guint32 encoded_len; - guint8 *encoded; +#if defined(TARGET_AMD64) + code = mono_arch_get_throw_pending_exception_full (&code_size, &ji, TRUE); + emit_trampoline (acfg, "throw_pending_exception", code, code_size, acfg->got_offset, ji, NULL); +#endif - /* - * This is a duplicate of the data in the .debug_frame section, but that - * section cannot be accessed using the dl interface. - */ - encoded = mono_unwind_ops_encode (cfg->unwind_ops, &encoded_len); - encode_value (encoded_len, p, &p); - memcpy (p, encoded, encoded_len); - p += encoded_len; - g_free (encoded); - } else { - encode_value (jinfo->used_regs, p, &p); - } +#if defined(TARGET_AMD64) || defined(TARGET_ARM) + for (i = 0; i < 128; ++i) { + int offset; - /* Exception table */ - if (header->num_clauses) { - for (k = 0; k < header->num_clauses; ++k) { - MonoJitExceptionInfo *ei = &jinfo->clauses [k]; + offset = MONO_RGCTX_SLOT_MAKE_RGCTX (i); + code = mono_arch_create_rgctx_lazy_fetch_trampoline_full (offset, &code_size, &ji, TRUE); + sprintf (symbol, "rgctx_fetch_trampoline_%u", offset); + emit_trampoline (acfg, symbol, code, code_size, acfg->got_offset, ji, NULL); - encode_value (ei->exvar_offset, p, &p); + offset = MONO_RGCTX_SLOT_MAKE_MRGCTX (i); + code = mono_arch_create_rgctx_lazy_fetch_trampoline_full (offset, &code_size, &ji, TRUE); + sprintf (symbol, "rgctx_fetch_trampoline_%u", offset); + emit_trampoline (acfg, symbol, code, code_size, acfg->got_offset, ji, NULL); + } +#endif - if (ei->flags == MONO_EXCEPTION_CLAUSE_FILTER) - encode_value ((gint)((guint8*)ei->data.filter - code), p, &p); +#if defined(TARGET_AMD64) || defined(TARGET_ARM) + { + GSList *l; - encode_value ((gint)((guint8*)ei->try_start - code), p, &p); - encode_value ((gint)((guint8*)ei->try_end - code), p, &p); - encode_value ((gint)((guint8*)ei->handler_start - code), p, &p); + /* delegate_invoke_impl trampolines */ + l = mono_arch_get_delegate_invoke_impls (); + while (l) { + MonoAotTrampInfo *info = l->data; + + emit_trampoline (acfg, info->name, info->code, info->code_size, acfg->got_offset, NULL, NULL); + l = l->next; + } } - } +#endif - if (jinfo->has_generic_jit_info) { - MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (jinfo); +#endif /* #ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES */ - encode_value (gi->has_this ? 1 : 0, p, &p); - encode_value (gi->this_reg, p, &p); - encode_value (gi->this_offset, p, &p); + /* Emit trampolines which are numerous */ - /* - * Need to encode jinfo->method too, since it is not equal to 'method' - * when using generic sharing. + /* + * These include the following: + * - specific trampolines + * - static rgctx invoke trampolines + * - imt thunks + * These trampolines have the same code, they are parameterized by GOT + * slots. + * They are defined in this file, in the arch_... routines instead of + * in tramp-.c, since it is easier to do it this way. */ - encode_method_ref (acfg, jinfo->method, p, &p); - } - - g_assert (debug_info_size < buf_size); - - encode_value (debug_info_size, p, &p); - if (debug_info_size) { - memcpy (p, debug_info, debug_info_size); - p += debug_info_size; - g_free (debug_info); - } - acfg->stats.ex_info_size += p - buf; + /* + * When running in aot-only mode, we can't create specific trampolines at + * runtime, so we create a few, and save them in the AOT file. + * Normal trampolines embed their argument as a literal inside the + * trampoline code, we can't do that here, so instead we embed an offset + * which needs to be added to the trampoline address to get the address of + * the GOT slot which contains the argument value. + * The generated trampolines jump to the generic trampolines using another + * GOT slot, which will be setup by the AOT loader to point to the + * generic trampoline code of the given type. + */ - /* Emit info */ + /* + * FIXME: Maybe we should use more specific trampolines (i.e. one class init for + * each class). + */ - emit_label (acfg, symbol); + emit_section_change (acfg, ".text", 0); - g_assert (p - buf < buf_size); - emit_bytes (acfg, buf, p - buf); - g_free (buf); -} + tramp_got_offset = acfg->got_offset; -static void -emit_klass_info (MonoAotCompile *acfg, guint32 token) -{ - MonoClass *klass = mono_class_get (acfg->image, token); - guint8 *p, *buf; - int i, buf_size; - char symbol [128]; - gboolean no_special_static, cant_encode; - gpointer iter = NULL; + for (ntype = 0; ntype < MONO_AOT_TRAMP_NUM; ++ntype) { + switch (ntype) { + case MONO_AOT_TRAMP_SPECIFIC: + sprintf (symbol, "specific_trampolines"); + break; + case MONO_AOT_TRAMP_STATIC_RGCTX: + sprintf (symbol, "static_rgctx_trampolines"); + break; + case MONO_AOT_TRAMP_IMT_THUNK: + sprintf (symbol, "imt_thunks"); + break; + default: + g_assert_not_reached (); + } - buf_size = 10240 + (klass->vtable_size * 16); - p = buf = g_malloc (buf_size); + emit_global (acfg, symbol, TRUE); + emit_alignment (acfg, 16); + emit_label (acfg, symbol); - g_assert (klass); + acfg->trampoline_got_offset_base [ntype] = tramp_got_offset; - mono_class_init (klass); + for (i = 0; i < acfg->num_trampolines [ntype]; ++i) { + int tramp_size = 0; - mono_class_get_nested_types (klass, &iter); - g_assert (klass->nested_classes_inited); + switch (ntype) { + case MONO_AOT_TRAMP_SPECIFIC: + arch_emit_specific_trampoline (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 2; + break; + case MONO_AOT_TRAMP_STATIC_RGCTX: + arch_emit_static_rgctx_trampoline (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 2; + break; + case MONO_AOT_TRAMP_IMT_THUNK: + arch_emit_imt_thunk (acfg, tramp_got_offset, &tramp_size); + tramp_got_offset += 1; + break; + default: + g_assert_not_reached (); + } - mono_class_setup_vtable (klass); + if (!acfg->trampoline_size [ntype]) { + g_assert (tramp_size); + acfg->trampoline_size [ntype] = tramp_size; + } + } + } - /* - * Emit all the information which is required for creating vtables so - * the runtime does not need to create the MonoMethod structures which - * take up a lot of space. - */ + /* Reserve some entries at the end of the GOT for our use */ + acfg->num_trampoline_got_entries = tramp_got_offset - acfg->got_offset; + } - no_special_static = !mono_class_has_special_static_fields (klass); + /* Unbox trampolines */ + for (i = 0; i < acfg->methods->len; ++i) { + MonoMethod *method = g_ptr_array_index (acfg->methods, i); + MonoCompile *cfg; + char call_target [256]; - /* Check whenever we have enough info to encode the vtable */ - cant_encode = FALSE; - for (i = 0; i < klass->vtable_size; ++i) { - MonoMethod *cm = klass->vtable [i]; + cfg = g_hash_table_lookup (acfg->method_to_cfg, method); + if (!cfg || !cfg->orig_method->klass->valuetype || !(method->flags & METHOD_ATTRIBUTE_VIRTUAL)) + continue; - if (cm && mono_method_signature (cm)->is_inflated && !g_hash_table_lookup (acfg->token_info_hash, cm)) - cant_encode = TRUE; - } + if (!method->wrapper_type && !method->is_inflated) { + g_assert (method->token); + sprintf (symbol, "ut_%d", mono_metadata_token_index (method->token) - 1); + } else { + sprintf (symbol, "ut_e_%d", get_method_index (acfg, method)); + } - if (klass->generic_container || cant_encode) { - encode_value (-1, p, &p); - } else { - encode_value (klass->vtable_size, p, &p); - encode_value ((no_special_static << 7) | (klass->has_static_refs << 6) | (klass->has_references << 5) | ((klass->blittable << 4) | (klass->nested_classes ? 1 : 0) << 3) | (klass->has_cctor << 2) | (klass->has_finalize << 1) | klass->ghcimpl, p, &p); - if (klass->has_cctor) - encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p); - if (klass->has_finalize) - encode_method_ref (acfg, mono_class_get_finalizer (klass), p, &p); - - encode_value (klass->instance_size, p, &p); - encode_value (mono_class_data_size (klass), p, &p); - encode_value (klass->packing_size, p, &p); - encode_value (klass->min_align, p, &p); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, TRUE); + emit_label (acfg, symbol); - for (i = 0; i < klass->vtable_size; ++i) { - MonoMethod *cm = klass->vtable [i]; + sprintf (call_target, ".Lm_%x", get_method_index (acfg, cfg->orig_method)); - if (cm) - encode_method_ref (acfg, cm, p, &p); - else - encode_value (0, p, &p); - } + arch_emit_unbox_trampoline (acfg, cfg->orig_method, cfg->generic_sharing_context, call_target); } - acfg->stats.class_info_size += p - buf; - - /* Emit the info */ - sprintf (symbol, ".LK_I_%x", token - MONO_TOKEN_TYPE_DEF - 1); - emit_label (acfg, symbol); + acfg->got_offset += acfg->num_trampoline_got_entries; +} - g_assert (p - buf < buf_size); - emit_bytes (acfg, buf, p - buf); - g_free (buf); +static gboolean +str_begins_with (const char *str1, const char *str2) +{ + int len = strlen (str2); + return strncmp (str1, str2, len) == 0; } -/* - * Calls made from AOTed code are routed through a table of jumps similar to the - * ELF PLT (Program Linkage Table). The differences are the following: - * - the ELF PLT entries make an indirect jump though the GOT so they expect the - * GOT pointer to be in EBX. We want to avoid this, so our table contains direct - * jumps. This means the jumps need to be patched when the address of the callee is - * known. Initially the PLT entries jump to code which transfers control to the - * AOT runtime through the first PLT entry. - */ static void -emit_plt (MonoAotCompile *acfg) +mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) { - char symbol [128]; - int i; - - emit_line (acfg); - sprintf (symbol, "plt"); - - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, TRUE); -#ifdef __i386__ - /* This section will be made read-write by the AOT loader */ - emit_alignment (acfg, PAGESIZE); -#else - emit_alignment (acfg, 16); -#endif - emit_label (acfg, symbol); - - for (i = 0; i < acfg->plt_offset; ++i) { - char label [128]; -#if defined(__arm__) - guint8 buf [256]; - guint8 *code; -#endif - - sprintf (label, ".Lp_%d", i); - emit_label (acfg, label); + gchar **args, **ptr; - /* - * The first plt entry is used to transfer code to the AOT loader. - */ + args = g_strsplit (aot_options ? aot_options : "", ",", -1); + for (ptr = args; ptr && *ptr; ptr ++) { + const char *arg = *ptr; -#if defined(__i386__) - if (i == 0) { - /* It is filled up during loading by the AOT loader. */ - emit_zero_bytes (acfg, 16); + if (str_begins_with (arg, "outfile=")) { + opts->outfile = g_strdup (arg + strlen ("outfile=")); + } else if (str_begins_with (arg, "save-temps")) { + opts->save_temps = TRUE; + } else if (str_begins_with (arg, "keep-temps")) { + opts->save_temps = TRUE; + } else if (str_begins_with (arg, "write-symbols")) { + opts->write_symbols = TRUE; + } else if (str_begins_with (arg, "metadata-only")) { + opts->metadata_only = TRUE; + } else if (str_begins_with (arg, "bind-to-runtime-version")) { + opts->bind_to_runtime_version = TRUE; + } else if (str_begins_with (arg, "full")) { + opts->full_aot = TRUE; + } else if (str_begins_with (arg, "threads=")) { + opts->nthreads = atoi (arg + strlen ("threads=")); + } else if (str_begins_with (arg, "static")) { + opts->static_link = TRUE; + opts->no_dlsym = TRUE; + } else if (str_begins_with (arg, "asmonly")) { + opts->asm_only = TRUE; + } else if (str_begins_with (arg, "asmwriter")) { + opts->asm_writer = TRUE; + } else if (str_begins_with (arg, "nodebug")) { + opts->nodebug = TRUE; + } else if (str_begins_with (arg, "ntrampolines=")) { + opts->ntrampolines = atoi (arg + strlen ("ntrampolines=")); } else { - /* Need to make sure this is 9 bytes long */ - emit_byte (acfg, '\xe9'); - emit_symbol_diff (acfg, "plt", ".", -4); - emit_int32 (acfg, acfg->plt_got_info_offsets [i]); + fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg); + exit (1); } -#elif defined(__x86_64__) - /* - * We can't emit jumps because they are 32 bits only so they can't be patched. - * So we make indirect calls through GOT entries which are patched by the AOT - * loader to point to .Lpd entries. - * An x86_64 plt entry is 10 bytes long, init_plt () depends on this. - */ - /* jmpq *(%rip) */ - emit_byte (acfg, '\xff'); - emit_byte (acfg, '\x25'); - emit_symbol_diff (acfg, "got", ".", ((acfg->plt_got_offset_base + i) * sizeof (gpointer)) -4); - /* Used by mono_aot_get_plt_info_offset */ - emit_int32 (acfg, acfg->plt_got_info_offsets [i]); -#elif defined(__arm__) - /* FIXME: - * - optimize OP_AOTCONST implementation - * - optimize the PLT entries - * - optimize SWITCH AOT implementation - * - implement IMT support - */ - code = buf; -#ifdef USE_BIN_WRITER - /* We only emit 1 relocation since we implement it ourselves anyway */ - bin_writer_emit_reloc (acfg, R_ARM_ALU_PC_G0_NC, "got", ((acfg->plt_got_offset_base + i) * sizeof (gpointer)) - 8); - /* FIXME: A 2 instruction encoding is sufficient in most cases */ - ARM_ADD_REG_IMM (code, ARMREG_IP, ARMREG_PC, 0, 0); - ARM_ADD_REG_IMM (code, ARMREG_IP, ARMREG_IP, 0, 0); - ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0); - emit_bytes (acfg, buf, code - buf); - /* FIXME: Get rid of this */ - emit_symbol_diff (acfg, "got", ".", ((acfg->plt_got_offset_base + i) * sizeof (gpointer))); - /* Used by mono_aot_get_plt_info_offset */ - emit_int32 (acfg, acfg->plt_got_info_offsets [i]); -#else - ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 4); - ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP); - ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, 0); - emit_bytes (acfg, buf, code - buf); - emit_symbol_diff (acfg, "got", ".", ((acfg->plt_got_offset_base + i) * sizeof (gpointer))); - /* Used by mono_aot_get_plt_info_offset */ - emit_int32 (acfg, acfg->plt_got_info_offsets [i]); -#endif - -#else - g_assert_not_reached (); -#endif } - sprintf (symbol, "plt_end"); - emit_global (acfg, symbol, TRUE); - emit_label (acfg, symbol); + g_strfreev (args); } -static G_GNUC_UNUSED void -emit_named_code (MonoAotCompile *acfg, const char *name, guint8 *code, - guint32 code_size, int got_offset, MonoJumpInfo *ji) +static void +add_token_info_hash (gpointer key, gpointer value, gpointer user_data) { - char symbol [256]; - guint32 buf_size; - MonoJumpInfo *patch_info; - guint8 *buf, *p; - GPtrArray *patches; - - /* Emit code */ - - sprintf (symbol, "%s", name); - - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, TRUE); - emit_alignment (acfg, 16); - emit_label (acfg, symbol); - - /* - * The code should access everything through the GOT, so we pass - * TRUE here. - */ - emit_and_reloc_code (acfg, NULL, code, code_size, ji, TRUE); + MonoMethod *method = (MonoMethod*)key; + MonoJumpInfoToken *ji = (MonoJumpInfoToken*)value; + MonoJumpInfoToken *new_ji = g_new0 (MonoJumpInfoToken, 1); + MonoAotCompile *acfg = user_data; - /* Emit info */ + new_ji->image = ji->image; + new_ji->token = ji->token; + g_hash_table_insert (acfg->token_info_hash, method, new_ji); +} - /* Sort relocations */ - patches = g_ptr_array_new (); - for (patch_info = ji; patch_info; patch_info = patch_info->next) - g_ptr_array_add (patches, patch_info); - g_ptr_array_sort (patches, compare_patches); +static gboolean +can_encode_class (MonoAotCompile *acfg, MonoClass *klass) +{ + if (klass->type_token) + return TRUE; + if ((klass->byval_arg.type == MONO_TYPE_VAR) || (klass->byval_arg.type == MONO_TYPE_MVAR)) + return TRUE; + if (klass->rank) + return can_encode_class (acfg, klass->element_class); + return FALSE; +} - buf_size = patches->len * 128 + 128; - buf = g_malloc (buf_size); - p = buf; +static gboolean +can_encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info) +{ + switch (patch_info->type) { + case MONO_PATCH_INFO_METHOD: + case MONO_PATCH_INFO_METHODCONST: { + MonoMethod *method = patch_info->data.method; - encode_patch_list (acfg, patches, patches->len, got_offset, p, &p); - g_assert (p - buf < buf_size); + if (method->wrapper_type) { + switch (method->wrapper_type) { + case MONO_WRAPPER_NONE: + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_XDOMAIN_INVOKE: + case MONO_WRAPPER_STFLD: + case MONO_WRAPPER_LDFLD: + case MONO_WRAPPER_LDFLDA: + case MONO_WRAPPER_LDFLD_REMOTE: + case MONO_WRAPPER_STFLD_REMOTE: + case MONO_WRAPPER_STELEMREF: + case MONO_WRAPPER_ISINST: + case MONO_WRAPPER_PROXY_ISINST: + case MONO_WRAPPER_ALLOC: + case MONO_WRAPPER_REMOTING_INVOKE: + case MONO_WRAPPER_STATIC_RGCTX_INVOKE: + case MONO_WRAPPER_UNKNOWN: + break; + default: + //printf ("Skip (wrapper call): %d -> %s\n", patch_info->type, mono_method_full_name (patch_info->data.method, TRUE)); + return FALSE; + } + } else { + if (!method->token) { + /* The method is part of a constructed type like Int[,].Set (). */ + if (!g_hash_table_lookup (acfg->token_info_hash, method)) { + if (method->klass->rank) + return TRUE; + return FALSE; + } + } + } + break; + } + case MONO_PATCH_INFO_VTABLE: + case MONO_PATCH_INFO_CLASS_INIT: + case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_ADJUSTED_IID: + if (!can_encode_class (acfg, patch_info->data.klass)) { + //printf ("Skip: %s\n", mono_type_full_name (&patch_info->data.klass->byval_arg)); + return FALSE; + } + break; + case MONO_PATCH_INFO_RGCTX_FETCH: { + MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry; - sprintf (symbol, "%s_p", name); + if (!can_encode_patch (acfg, entry->data)) + return FALSE; + break; + } + default: + break; + } - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); - emit_label (acfg, symbol); - - emit_bytes (acfg, buf, p - buf); + return TRUE; } +static void +add_generic_class (MonoAotCompile *acfg, MonoClass *klass); + /* - * When running in aot-only mode, we can't create trampolines at runtime, so we create - * a few, and save them in the AOT file. Normal trampolines embed their argument as a - * literal inside the trampoline code, we can't do that here, so instead we embed an offset - * which needs to be added to the trampoline address to get the address of the GOT slot - * which contains the argument value. - * The generated trampolines jump to the generic trampolines using another GOT slot, which - * will be setup by the AOT loader to point to the generic trampoline code of the given - * type. + * compile_method: + * + * AOT compile a given method. + * This function might be called by multiple threads, so it must be thread-safe. */ static void -emit_trampolines (MonoAotCompile *acfg) +compile_method (MonoAotCompile *acfg, MonoMethod *method) { - char symbol [256]; - int i, offset; -#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES - int tramp_type; - guint32 code_size; - MonoJumpInfo *ji; - guint8 *code; -#endif + MonoCompile *cfg; + MonoJumpInfo *patch_info; + gboolean skip; + int index; + MonoMethod *wrapped; - if (!acfg->aot_opts.full_aot) + if (acfg->aot_opts.metadata_only) return; - - g_assert (acfg->image->assembly); - /* Currently, we only emit most trampolines into the mscorlib AOT image. */ - if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) { -#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES - /* - * Emit the generic trampolines. - * - * We could save some code by treating the generic trampolines as a wrapper - * method, but that approach has its own complexities, so we choose the simpler - * method. - */ - for (tramp_type = 0; tramp_type < MONO_TRAMPOLINE_NUM; ++tramp_type) { - code = mono_arch_create_trampoline_code_full (tramp_type, &code_size, &ji, TRUE); + mono_acfg_lock (acfg); + index = get_method_index (acfg, method); + mono_acfg_unlock (acfg); - /* Emit trampoline code */ + /* fixme: maybe we can also precompile wrapper methods */ + if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || + (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || + (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) { + //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE)); + return; + } - sprintf (symbol, "generic_trampoline_%d", tramp_type); + if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) + return; - emit_named_code (acfg, symbol, code, code_size, acfg->got_offset, ji); - } + wrapped = mono_marshal_method_from_wrapper (method); + if (wrapped && (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && wrapped->is_generic) + // FIXME: The wrapper should be generic too, but it is not + return; - code = mono_arch_get_nullified_class_init_trampoline (&code_size); - emit_named_code (acfg, "nullified_class_init_trampoline", code, code_size, acfg->got_offset, NULL); -#if defined(__x86_64__) && defined(MONO_ARCH_MONITOR_OBJECT_REG) - code = mono_arch_create_monitor_enter_trampoline_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "monitor_enter_trampoline", code, code_size, acfg->got_offset, ji); - code = mono_arch_create_monitor_exit_trampoline_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "monitor_exit_trampoline", code, code_size, acfg->got_offset, ji); -#endif - - /* Emit the exception related code pieces */ - code = mono_arch_get_restore_context_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "restore_context", code, code_size, acfg->got_offset, ji); - code = mono_arch_get_call_filter_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "call_filter", code, code_size, acfg->got_offset, ji); - code = mono_arch_get_throw_exception_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "throw_exception", code, code_size, acfg->got_offset, ji); - code = mono_arch_get_rethrow_exception_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "rethrow_exception", code, code_size, acfg->got_offset, ji); - code = mono_arch_get_throw_exception_by_name_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "throw_exception_by_name", code, code_size, acfg->got_offset, ji); - code = mono_arch_get_throw_corlib_exception_full (&code_size, &ji, TRUE); - emit_named_code (acfg, "throw_corlib_exception", code, code_size, acfg->got_offset, ji); - -#if defined(__x86_64__) || defined(__arm__) - for (i = 0; i < 128; ++i) { - int offset; - - offset = MONO_RGCTX_SLOT_MAKE_RGCTX (i); - code = mono_arch_create_rgctx_lazy_fetch_trampoline_full (offset, &code_size, &ji, TRUE); - sprintf (symbol, "rgctx_fetch_trampoline_%u", offset); - emit_named_code (acfg, symbol, code, code_size, acfg->got_offset, ji); - - offset = MONO_RGCTX_SLOT_MAKE_MRGCTX (i); - code = mono_arch_create_rgctx_lazy_fetch_trampoline_full (offset, &code_size, &ji, TRUE); - sprintf (symbol, "rgctx_fetch_trampoline_%u", offset); - emit_named_code (acfg, symbol, code, code_size, acfg->got_offset, ji); - } -#endif -#endif - - /* - * FIXME: Maybe we should use more specific trampolines (i.e. one class init for - * each class). - */ - - /* Reserve some entries at the end of the GOT for our use */ - acfg->num_trampoline_got_entries = acfg->num_aot_trampolines * 2; - - sprintf (symbol, "trampolines"); - - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, TRUE); - emit_alignment (acfg, 16); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->num_aot_trampolines; ++i) { - offset = acfg->got_offset + (i * 2); - - /* - * The trampolines created here are variations of the specific - * trampolines created in mono_arch_create_specific_trampoline (). The - * differences are: - * - the generic trampoline address is taken from a got slot. - * - the offset of the got slot where the trampoline argument is stored - * is embedded in the instruction stream, and the generic trampoline - * can load the argument by loading the offset, adding it to the - * address of the trampoline to get the address of the got slot, and - * loading the argument from the there. - */ -#if defined(__x86_64__) - /* This should be exactly 16 bytes long */ - /* It should work together with the generic trampoline code in tramp-amd64.c */ - /* call *(%rip) */ - emit_byte (acfg, '\x41'); - emit_byte (acfg, '\xff'); - emit_byte (acfg, '\x15'); - emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4); - /* This should be relative to the start of the trampoline */ - emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4 + 19); - emit_zero_bytes (acfg, 5); -#elif defined(__arm__) - { - guint8 buf [128]; - - /* Generate the trampoline code */ - /* This should be exactly 28 bytes long */ - - code = buf; - ARM_PUSH (code, 0x5fff); - ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 8); - /* Load the value from the GOT */ - ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_PC, ARMREG_R1); - /* Branch to it */ - ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC); - ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1); - - g_assert (code - buf == 20); - - /* Emit it */ - emit_bytes (acfg, buf, code - buf); - emit_symbol_diff (acfg, "got", ".", (offset * sizeof (gpointer)) - 4 + 8); - emit_symbol_diff (acfg, "got", ".", ((offset + 1) * sizeof (gpointer)) - 4 + 8); - } -#else - g_assert_not_reached (); -#endif - } - } - - /* Unbox trampolines */ - - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - MonoMethod *method; - guint32 token = MONO_TOKEN_METHOD_DEF | (i + 1); - MonoCompile *cfg; - char call_target [256]; - - method = mono_get_method (acfg->image, token, NULL); - - cfg = g_hash_table_lookup (acfg->method_to_cfg, method); - if (!cfg || !cfg->orig_method->klass->valuetype || !(method->flags & METHOD_ATTRIBUTE_VIRTUAL)) - continue; - - sprintf (symbol, "unbox_trampoline_%d", i); - - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, TRUE); - emit_label (acfg, symbol); - - sprintf (call_target, ".Lm_%x", get_method_index (acfg, cfg->orig_method)); - -#if defined(__x86_64__) - { - guint8 buf [32]; - int this_reg; - - this_reg = mono_arch_get_this_arg_reg (mono_method_signature (cfg->orig_method), cfg->generic_sharing_context, NULL); - code = buf; - amd64_alu_reg_imm (code, X86_ADD, this_reg, sizeof (MonoObject)); - - emit_bytes (acfg, buf, code - buf); - /* jump */ - emit_byte (acfg, '\xe9'); - emit_symbol_diff (acfg, call_target, ".", -4); - } -#elif defined(__arm__) - { - guint8 buf [128]; - int this_pos = 0; - - code = buf; - - if (MONO_TYPE_ISSTRUCT (mono_method_signature (cfg->orig_method)->ret)) - this_pos = 1; - - ARM_ADD_REG_IMM8 (code, this_pos, this_pos, sizeof (MonoObject)); - - emit_bytes (acfg, buf, code - buf); - /* jump to method */ -#if defined(USE_BIN_WRITER) - /* FIXME: */ - g_assert_not_reached (); -#else - fprintf (acfg->fp, "\n\tb %s\n", call_target); -#endif - } -#else - g_assert_not_reached (); -#endif - } - - acfg->trampoline_got_offset_base = acfg->got_offset; - - acfg->got_offset += acfg->num_trampoline_got_entries; -} - -static gboolean -str_begins_with (const char *str1, const char *str2) -{ - int len = strlen (str2); - return strncmp (str1, str2, len) == 0; -} - -static void -mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts) -{ - gchar **args, **ptr; - - args = g_strsplit (aot_options ? aot_options : "", ",", -1); - for (ptr = args; ptr && *ptr; ptr ++) { - const char *arg = *ptr; - - if (str_begins_with (arg, "outfile=")) { - opts->outfile = g_strdup (arg + strlen ("outfile=")); - } else if (str_begins_with (arg, "save-temps")) { - opts->save_temps = TRUE; - } else if (str_begins_with (arg, "keep-temps")) { - opts->save_temps = TRUE; - } else if (str_begins_with (arg, "write-symbols")) { - opts->write_symbols = TRUE; - } else if (str_begins_with (arg, "metadata-only")) { - opts->metadata_only = TRUE; - } else if (str_begins_with (arg, "bind-to-runtime-version")) { - opts->bind_to_runtime_version = TRUE; - } else if (str_begins_with (arg, "full")) { - opts->full_aot = TRUE; - /* - * The no-dlsym option is only useful on the iphone, and even there, - * do to other limitations of the dynamic linker, it doesn't seem to - * work. So disable it for now so we don't have to support it. - */ - /* - } else if (str_begins_with (arg, "no-dlsym")) { - opts->no_dlsym = TRUE; - */ - } else if (str_begins_with (arg, "threads=")) { - opts->nthreads = atoi (arg + strlen ("threads=")); - } else if (str_begins_with (arg, "static")) { - opts->static_link = TRUE; - opts->no_dlsym = TRUE; - } else if (str_begins_with (arg, "asmonly")) { - opts->asm_only = TRUE; - } else if (str_begins_with (arg, "asmwriter")) { - opts->asm_writer = TRUE; - } else { - fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg); - exit (1); - } - } - - g_strfreev (args); -} - -static void -add_token_info_hash (gpointer key, gpointer value, gpointer user_data) -{ - MonoMethod *method = (MonoMethod*)key; - MonoJumpInfoToken *ji = (MonoJumpInfoToken*)value; - MonoJumpInfoToken *new_ji = g_new0 (MonoJumpInfoToken, 1); - MonoAotCompile *acfg = user_data; - - new_ji->image = ji->image; - new_ji->token = ji->token; - g_hash_table_insert (acfg->token_info_hash, method, new_ji); -} - -static gboolean -can_encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info) -{ - switch (patch_info->type) { - case MONO_PATCH_INFO_METHOD: - case MONO_PATCH_INFO_METHODCONST: - if (patch_info->data.method->wrapper_type) { - switch (patch_info->data.method->wrapper_type) { - case MONO_WRAPPER_NONE: - case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: - case MONO_WRAPPER_XDOMAIN_INVOKE: - case MONO_WRAPPER_STFLD: - case MONO_WRAPPER_LDFLD: - case MONO_WRAPPER_LDFLDA: - case MONO_WRAPPER_LDFLD_REMOTE: - case MONO_WRAPPER_STFLD_REMOTE: - case MONO_WRAPPER_STELEMREF: - case MONO_WRAPPER_ISINST: - case MONO_WRAPPER_PROXY_ISINST: - case MONO_WRAPPER_ALLOC: - case MONO_WRAPPER_REMOTING_INVOKE: - case MONO_WRAPPER_STATIC_RGCTX_INVOKE: - case MONO_WRAPPER_UNKNOWN: - break; - default: - //printf ("Skip (wrapper call): %s %d -> %s\n", mono_method_full_name (method, TRUE), patch_info->type, mono_method_full_name (patch_info->data.method, TRUE)); - return FALSE; - } - } else { - if (!patch_info->data.method->token) { - /* The method is part of a constructed type like Int[,].Set (). */ - if (!g_hash_table_lookup (acfg->token_info_hash, patch_info->data.method)) - return FALSE; - } - } - break; - case MONO_PATCH_INFO_VTABLE: - case MONO_PATCH_INFO_CLASS_INIT: - case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: - case MONO_PATCH_INFO_CLASS: - case MONO_PATCH_INFO_IID: - case MONO_PATCH_INFO_ADJUSTED_IID: - if (!patch_info->data.klass->type_token) - if (!patch_info->data.klass->element_class->type_token && !(patch_info->data.klass->element_class->rank && patch_info->data.klass->element_class->element_class->type_token)) - return FALSE; - break; - case MONO_PATCH_INFO_RGCTX_FETCH: { - MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry; - - if (!can_encode_patch (acfg, entry->data)) - return FALSE; - break; - } - default: - break; - } - - return TRUE; -} - -/* - * compile_method: - * - * AOT compile a given method. - * This function might be called by multiple threads, so it must be thread-safe. - */ -static void -compile_method (MonoAotCompile *acfg, MonoMethod *method) -{ - MonoCompile *cfg; - MonoJumpInfo *patch_info; - gboolean skip; - int index; - MonoMethod *wrapped; - - if (acfg->aot_opts.metadata_only) - return; - - mono_acfg_lock (acfg); - index = get_method_index (acfg, method); - mono_acfg_unlock (acfg); - - /* fixme: maybe we can also precompile wrapper methods */ - if ((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) || - (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || - (method->flags & METHOD_ATTRIBUTE_ABSTRACT)) { - //printf ("Skip (impossible): %s\n", mono_method_full_name (method, TRUE)); - return; - } - - if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) - return; - - wrapped = mono_marshal_method_from_wrapper (method); - if (wrapped && (wrapped->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && wrapped->is_generic) - // FIXME: The wrapper should be generic too, but it is not - return; - - InterlockedIncrement (&acfg->stats.mcount); + InterlockedIncrement (&acfg->stats.mcount); #if 0 if (method->is_generic || method->klass->generic_container) { @@ -4189,8 +3031,7 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) } #endif - if (acfg->aot_opts.full_aot) - mono_use_imt = FALSE; + //acfg->aot_opts.print_skipped_methods = TRUE; /* * Since these methods are the only ones which are compiled with @@ -4210,7 +3051,8 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) } if (cfg->disable_aot) { - //printf ("Skip (other): %s\n", mono_method_full_name (method, TRUE)); + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (disabled): %s\n", mono_method_full_name (method, TRUE)); InterlockedIncrement (&acfg->stats.ocount); mono_destroy_compile (cfg); return; @@ -4241,7 +3083,6 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) switch (patch_info->type) { case MONO_PATCH_INFO_ABS: /* unable to handle this */ - //printf ("Skip (abs addr): %s %d\n", mono_method_full_name (method, TRUE), patch_info->type); skip = TRUE; break; default: @@ -4250,6 +3091,8 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) } if (skip) { + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (abs call): %s\n", mono_method_full_name (method, TRUE)); InterlockedIncrement (&acfg->stats.abscount); mono_destroy_compile (cfg); return; @@ -4268,6 +3111,8 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) } if (skip) { + if (acfg->aot_opts.print_skipped_methods) + printf ("Skip (patches): %s\n", mono_method_full_name (method, TRUE)); acfg->stats.ocount++; mono_destroy_compile (cfg); mono_acfg_unlock (acfg); @@ -4282,8 +3127,15 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) if (m->is_inflated) { if (!(mono_class_generic_sharing_enabled (m->klass) && mono_method_is_generic_sharable_impl (m, FALSE)) && - !method_has_type_vars (m)) - add_extra_method (acfg, m); + !method_has_type_vars (m)) { + if (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { + if (acfg->aot_opts.full_aot) + add_extra_method (acfg, mono_marshal_get_native_wrapper (m, TRUE, TRUE)); + } else { + add_extra_method (acfg, m); + } + } + add_generic_class (acfg, m->klass); } break; } @@ -4395,2163 +3247,1090 @@ compile_method (MonoAotCompile *acfg, MonoMethod *method) g_hash_table_insert (acfg->method_to_cfg, cfg->orig_method, cfg); if (cfg->orig_method->wrapper_type) - g_ptr_array_add (acfg->extra_methods, cfg->orig_method); - - mono_acfg_unlock (acfg); - - InterlockedIncrement (&acfg->stats.ccount); -} - -static void -compile_thread_main (gpointer *user_data) -{ - MonoDomain *domain = user_data [0]; - MonoAotCompile *acfg = user_data [1]; - GPtrArray *methods = user_data [2]; - int i; - - mono_thread_attach (domain); - - for (i = 0; i < methods->len; ++i) - compile_method (acfg, g_ptr_array_index (methods, i)); -} - -static void -load_profile_files (MonoAotCompile *acfg) -{ - FILE *infile; - char *tmp; - int file_index, res, method_index, i; - char ver [256]; - guint32 token; - GList *unordered; - - file_index = 0; - while (TRUE) { - tmp = g_strdup_printf ("%s/.mono/aot-profile-data/%s-%s-%d", g_get_home_dir (), acfg->image->assembly_name, acfg->image->guid, file_index); - - if (!g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) { - g_free (tmp); - break; - } - - infile = fopen (tmp, "r"); - g_assert (infile); - - printf ("Using profile data file '%s'\n", tmp); - g_free (tmp); - - file_index ++; - - res = fscanf (infile, "%32s\n", ver); - if ((res != 1) || strcmp (ver, "#VER:1") != 0) { - printf ("Profile file has wrong version or invalid.\n"); - fclose (infile); - continue; - } - - while (TRUE) { - res = fscanf (infile, "%d\n", &token); - if (res < 1) - break; - - method_index = mono_metadata_token_index (token) - 1; - - if (!g_list_find (acfg->method_order, GUINT_TO_POINTER (method_index))) - acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (method_index)); - } - fclose (infile); - } - - /* Add missing methods */ - unordered = NULL; - for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { - if (!g_list_find (acfg->method_order, GUINT_TO_POINTER (i))) - unordered = g_list_prepend (unordered, GUINT_TO_POINTER (i)); - } - unordered = g_list_reverse (unordered); - if (acfg->method_order) - g_list_last (acfg->method_order)->next = unordered; - else - acfg->method_order = unordered; -} - -/** - * alloc_got_slots: - * - * Collect all patches which have shared GOT entries and alloc entries for them. The - * rest will get entries allocated during emit_code (). - */ -static void -alloc_got_slots (MonoAotCompile *acfg) -{ - int i; - GList *l; - MonoJumpInfo *ji; - - /* Slot 0 is reserved for the address of the current assembly */ - ji = mono_mempool_alloc0 (acfg->mempool, sizeof (MonoAotCompile)); - ji->type = MONO_PATCH_INFO_IMAGE; - ji->data.image = acfg->image; - - get_shared_got_offset (acfg, ji); - - for (l = acfg->method_order; l != NULL; l = l->next) { - i = GPOINTER_TO_UINT (l->data); - - if (acfg->cfgs [i]) { - MonoCompile *cfg = acfg->cfgs [i]; - - for (ji = cfg->patch_info; ji; ji = ji->next) { - if (is_shared_got_patch (ji)) - get_shared_got_offset (acfg, ji); - } - } - } -} - -static void -emit_code (MonoAotCompile *acfg) -{ - int i; - char symbol [256]; - GList *l; - - sprintf (symbol, "methods"); - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, TRUE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - /* - * Emit some padding so the local symbol for the first method doesn't have the - * same address as 'methods'. - */ - emit_zero_bytes (acfg, 16); - - for (l = acfg->method_order; l != NULL; l = l->next) { - i = GPOINTER_TO_UINT (l->data); - - if (acfg->cfgs [i]) - emit_method_code (acfg, acfg->cfgs [i]); - } - - sprintf (symbol, "methods_end"); - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - sprintf (symbol, "method_offsets"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->nmethods; ++i) { - if (acfg->cfgs [i]) { - sprintf (symbol, ".Lm_%x", i); - emit_symbol_diff (acfg, symbol, "methods", 0); - } else { - emit_int32 (acfg, 0xffffffff); - } - } - emit_line (acfg); -} - -static void -emit_info (MonoAotCompile *acfg) -{ - int i; - char symbol [256]; - GList *l; - - /* Emit method info */ - sprintf (symbol, "method_info"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - /* To reduce size of generated assembly code */ - sprintf (symbol, "mi"); - emit_label (acfg, symbol); - - for (l = acfg->method_order; l != NULL; l = l->next) { - i = GPOINTER_TO_UINT (l->data); - - if (acfg->cfgs [i]) - emit_method_info (acfg, acfg->cfgs [i]); - } - - sprintf (symbol, "method_info_offsets"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->nmethods; ++i) { - if (acfg->cfgs [i]) { - sprintf (symbol, ".Lm_%x_p", i); - emit_symbol_diff (acfg, symbol, "mi", 0); - } else { - emit_int32 (acfg, 0); - } - } - emit_line (acfg); -} - -typedef struct HashEntry { - guint32 key, value, index; - struct HashEntry *next; -} HashEntry; - -/* - * emit_extra_methods: - * - * Emit methods which are not in the METHOD table, like wrappers. - */ -static void -emit_extra_methods (MonoAotCompile *acfg) -{ - int i, table_size, buf_size; - char symbol [256]; - guint8 *p, *buf; - guint32 *info_offsets; - guint32 hash; - GPtrArray *table; - HashEntry *entry, *new_entry; - int nmethods; - - info_offsets = g_new0 (guint32, acfg->extra_methods->len); - - buf_size = acfg->extra_methods->len * 256 + 256; - p = buf = g_malloc (buf_size); - - /* Encode method info */ - nmethods = 0; - /* So offsets are > 0 */ - *p = 0; - p++; - for (i = 0; i < acfg->extra_methods->len; ++i) { - MonoMethod *method = g_ptr_array_index (acfg->extra_methods, i); - MonoCompile *cfg = g_hash_table_lookup (acfg->method_to_cfg, method); - - if (!cfg) - continue; - - nmethods ++; - info_offsets [i] = p - buf; - - if (method->wrapper_type) { - char *name; - - // FIXME: Optimize disk usage - if (method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) { - char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); - name = g_strdup_printf ("(wrapper runtime-invoke):%s (%s)", method->name, tmpsig); - g_free (tmpsig); - } else if (method->wrapper_type == MONO_WRAPPER_DELEGATE_INVOKE) { - char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); - name = g_strdup_printf ("(wrapper delegate-invoke):%s (%s)", method->name, tmpsig); - g_free (tmpsig); - } else if (method->wrapper_type == MONO_WRAPPER_DELEGATE_BEGIN_INVOKE) { - char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); - name = g_strdup_printf ("(wrapper delegate-begin-invoke):%s (%s)", method->name, tmpsig); - g_free (tmpsig); - } else if (method->wrapper_type == MONO_WRAPPER_DELEGATE_END_INVOKE) { - char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); - name = g_strdup_printf ("(wrapper delegate-end-invoke):%s (%s)", method->name, tmpsig); - g_free (tmpsig); - } else { - name = mono_method_full_name (cfg->orig_method, TRUE); - } - - encode_value (1, p, &p); - strcpy ((char*)p, name); - p += strlen (name ) + 1; - g_free (name); - } else { - encode_value (0, p, &p); - encode_method_ref (acfg, method, p, &p); - } - - g_assert ((p - buf) < buf_size); - } - - g_assert ((p - buf) < buf_size); - - /* Emit method info */ - sprintf (symbol, "extra_method_info"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - emit_bytes (acfg, buf, p - buf); - - emit_line (acfg); - - /* - * Construct a chained hash table for mapping indexes in extra_method_info to - * method indexes. - */ - table_size = g_spaced_primes_closest ((int)(nmethods * 1.5)); - table = g_ptr_array_sized_new (table_size); - for (i = 0; i < table_size; ++i) - g_ptr_array_add (table, NULL); - for (i = 0; i < acfg->extra_methods->len; ++i) { - MonoMethod *method = g_ptr_array_index (acfg->extra_methods, i); - MonoCompile *cfg = g_hash_table_lookup (acfg->method_to_cfg, method); - guint32 key, value; - - if (!cfg) - continue; - - key = info_offsets [i]; - value = get_method_index (acfg, method); - - if (method->wrapper_type) { - hash = g_str_hash (method->name) % table_size; - } else { - // FIXME: - hash = 0 % table_size; - } - - /* FIXME: Allocate from the mempool */ - new_entry = g_new0 (HashEntry, 1); - new_entry->key = key; - new_entry->value = value; - - entry = g_ptr_array_index (table, hash); - if (entry == NULL) { - new_entry->index = hash; - g_ptr_array_index (table, hash) = new_entry; - } else { - while (entry->next) - entry = entry->next; - - entry->next = new_entry; - new_entry->index = table->len; - g_ptr_array_add (table, new_entry); - } - } - - /* Emit the table */ - sprintf (symbol, "extra_method_table"); - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - g_assert (table_size < 65000); - emit_int32 (acfg, table_size); - g_assert (table->len < 65000); - for (i = 0; i < table->len; ++i) { - HashEntry *entry = g_ptr_array_index (table, i); - - if (entry == NULL) { - emit_int32 (acfg, 0); - emit_int32 (acfg, 0); - emit_int32 (acfg, 0); - } else { - g_assert (entry->key > 0); - emit_int32 (acfg, entry->key); - emit_int32 (acfg, entry->value); - if (entry->next) - emit_int32 (acfg, entry->next->index); - else - emit_int32 (acfg, 0); - } - } - - /* - * Emit a table reverse mapping method indexes to their index in extra_method_info. - * This is used by mono_aot_find_jit_info (). - */ - sprintf (symbol, "extra_method_info_offsets"); - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - emit_int32 (acfg, acfg->extra_methods->len); - for (i = 0; i < acfg->extra_methods->len; ++i) { - MonoMethod *method = g_ptr_array_index (acfg->extra_methods, i); - - emit_int32 (acfg, get_method_index (acfg, method)); - emit_int32 (acfg, info_offsets [i]); - } -} - -static void -emit_method_order (MonoAotCompile *acfg) -{ - int i, index, len; - char symbol [256]; - GList *l; - - sprintf (symbol, "method_order"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - /* First emit an index table */ - index = 0; - len = 0; - for (l = acfg->method_order; l != NULL; l = l->next) { - i = GPOINTER_TO_UINT (l->data); - - if (acfg->cfgs [i]) { - if ((index % 1024) == 0) { - emit_int32 (acfg, i); - } - - index ++; - } - - len ++; - } - emit_int32 (acfg, 0xffffff); - - /* Then emit the whole method order */ - for (l = acfg->method_order; l != NULL; l = l->next) { - i = GPOINTER_TO_UINT (l->data); - - if (acfg->cfgs [i]) { - emit_int32 (acfg, i); - } - } - emit_line (acfg); - - sprintf (symbol, "method_order_end"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_label (acfg, symbol); -} - -static void -emit_exception_info (MonoAotCompile *acfg) -{ - int i; - char symbol [256]; - - sprintf (symbol, "ex_info"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - /* To reduce size of generated assembly */ - sprintf (symbol, "ex"); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->nmethods; ++i) { - if (acfg->cfgs [i]) - emit_exception_debug_info (acfg, acfg->cfgs [i]); - } - - sprintf (symbol, "ex_info_offsets"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->nmethods; ++i) { - if (acfg->cfgs [i]) { - sprintf (symbol, ".Le_%x_p", i); - emit_symbol_diff (acfg, symbol, "ex", 0); - } else { - emit_int32 (acfg, 0); - } - } - emit_line (acfg); -} - -static void -emit_class_info (MonoAotCompile *acfg) -{ - int i; - char symbol [256]; - - sprintf (symbol, "class_info"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) - emit_klass_info (acfg, MONO_TOKEN_TYPE_DEF | (i + 1)); - - sprintf (symbol, "class_info_offsets"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { - sprintf (symbol, ".LK_I_%x", i); - emit_symbol_diff (acfg, symbol, "class_info", 0); - } - emit_line (acfg); -} - -typedef struct ClassNameTableEntry { - guint32 token, index; - struct ClassNameTableEntry *next; -} ClassNameTableEntry; - -static void -emit_class_name_table (MonoAotCompile *acfg) -{ - int i, table_size; - guint32 token, hash; - MonoClass *klass; - GPtrArray *table; - char *full_name; - char symbol [256]; - ClassNameTableEntry *entry, *new_entry; - - /* - * Construct a chained hash table for mapping class names to typedef tokens. - */ - table_size = g_spaced_primes_closest ((int)(acfg->image->tables [MONO_TABLE_TYPEDEF].rows * 1.5)); - table = g_ptr_array_sized_new (table_size); - for (i = 0; i < table_size; ++i) - g_ptr_array_add (table, NULL); - for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { - token = MONO_TOKEN_TYPE_DEF | (i + 1); - klass = mono_class_get (acfg->image, token); - full_name = mono_type_get_name_full (mono_class_get_type (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME); - hash = g_str_hash (full_name) % table_size; - g_free (full_name); - - /* FIXME: Allocate from the mempool */ - new_entry = g_new0 (ClassNameTableEntry, 1); - new_entry->token = token; - - entry = g_ptr_array_index (table, hash); - if (entry == NULL) { - new_entry->index = hash; - g_ptr_array_index (table, hash) = new_entry; - } else { - while (entry->next) - entry = entry->next; - - entry->next = new_entry; - new_entry->index = table->len; - g_ptr_array_add (table, new_entry); - } - } - - /* Emit the table */ - sprintf (symbol, "class_name_table"); - emit_section_change (acfg, ".text", 0); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - /* FIXME: Optimize memory usage */ - g_assert (table_size < 65000); - emit_int16 (acfg, table_size); - g_assert (table->len < 65000); - for (i = 0; i < table->len; ++i) { - ClassNameTableEntry *entry = g_ptr_array_index (table, i); - - if (entry == NULL) { - emit_int16 (acfg, 0); - emit_int16 (acfg, 0); - } else { - emit_int16 (acfg, mono_metadata_token_index (entry->token)); - if (entry->next) - emit_int16 (acfg, entry->next->index); - else - emit_int16 (acfg, 0); - } - } -} - -static void -emit_image_table (MonoAotCompile *acfg) -{ - int i; - char symbol [256]; - - /* - * The image table is small but referenced in a lot of places. - * So we emit it at once, and reference its elements by an index. - */ - - sprintf (symbol, "mono_image_table"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - emit_int32 (acfg, acfg->image_table->len); - for (i = 0; i < acfg->image_table->len; i++) { - MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i); - MonoAssemblyName *aname = &image->assembly->aname; - - /* FIXME: Support multi-module assemblies */ - g_assert (image->assembly->image == image); - - emit_string (acfg, image->assembly_name); - emit_string (acfg, image->guid); - emit_string (acfg, aname->culture ? aname->culture : ""); - emit_string (acfg, (const char*)aname->public_key_token); - - emit_alignment (acfg, 8); - emit_int32 (acfg, aname->flags); - emit_int32 (acfg, aname->major); - emit_int32 (acfg, aname->minor); - emit_int32 (acfg, aname->build); - emit_int32 (acfg, aname->revision); - } -} - -static void -emit_got_info (MonoAotCompile *acfg) -{ - char symbol [256]; - int i, first_plt_got_patch, buf_size; - guint8 *p, *buf; - guint32 *got_info_offsets; - - /* Add the patches needed by the PLT to the GOT */ - acfg->plt_got_offset_base = acfg->got_offset; - first_plt_got_patch = acfg->shared_patches->len; - for (i = 1; i < acfg->plt_offset; ++i) { - MonoJumpInfo *patch_info = g_hash_table_lookup (acfg->plt_offset_to_patch, GUINT_TO_POINTER (i)); - - g_ptr_array_add (acfg->shared_patches, patch_info); - } - - acfg->got_offset += acfg->plt_offset; - - /** - * FIXME: - * - optimize offsets table. - * - reduce number of exported symbols. - * - emit info for a klass only once. - * - determine when a method uses a GOT slot which is guaranteed to be already - * initialized. - * - clean up and document the code. - * - use String.Empty in class libs. - */ - - /* Encode info required to decode shared GOT entries */ - buf_size = acfg->shared_patches->len * 64; - p = buf = mono_mempool_alloc (acfg->mempool, buf_size); - got_info_offsets = mono_mempool_alloc (acfg->mempool, acfg->shared_patches->len * sizeof (guint32)); - acfg->plt_got_info_offsets = mono_mempool_alloc (acfg->mempool, acfg->plt_offset * sizeof (guint32)); - for (i = 0; i < acfg->shared_patches->len; ++i) { - MonoJumpInfo *ji = g_ptr_array_index (acfg->shared_patches, i); - - got_info_offsets [i] = p - buf; - /* No need to encode the patch type for non-PLT patches */ - if (i >= first_plt_got_patch) { - acfg->plt_got_info_offsets [i - first_plt_got_patch + 1] = got_info_offsets [i]; - encode_value (ji->type, p, &p); - } - encode_patch (acfg, ji, p, &p); - } - - g_assert (p - buf <= buf_size); - - acfg->stats.got_info_size = p - buf; - - /* Emit got_info table */ - sprintf (symbol, "got_info"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - emit_bytes (acfg, buf, p - buf); - - /* Emit got_info_offsets table */ - sprintf (symbol, "got_info_offsets"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->shared_patches->len; ++i) - emit_int32 (acfg, got_info_offsets [i]); - - acfg->stats.got_info_offsets_size = acfg->shared_patches->len * 4; -} - -static void -emit_got (MonoAotCompile *acfg) -{ - char symbol [256]; - - /* Don't make GOT global so accesses to it don't need relocations */ - sprintf (symbol, "got"); - emit_section_change (acfg, ".bss", 0); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - if (acfg->got_offset > 0) - emit_zero_bytes (acfg, (int)(acfg->got_offset * sizeof (gpointer))); -} - -static void -emit_globals (MonoAotCompile *acfg) -{ - char *opts_str; - char *build_info; - - emit_string_symbol (acfg, "mono_assembly_guid" , acfg->image->guid); - - emit_string_symbol (acfg, "mono_aot_version", MONO_AOT_FILE_VERSION); - - opts_str = g_strdup_printf ("%d", acfg->opts); - emit_string_symbol (acfg, "mono_aot_opt_flags", opts_str); - g_free (opts_str); - - emit_string_symbol (acfg, "mono_aot_full_aot", acfg->aot_opts.full_aot ? "TRUE" : "FALSE"); - - if (acfg->aot_opts.bind_to_runtime_version) { - build_info = mono_get_runtime_build_info (); - emit_string_symbol (acfg, "mono_runtime_version", build_info); - g_free (build_info); - } else { - emit_string_symbol (acfg, "mono_runtime_version", ""); - } - - /* - * Some platforms like the iphone have no working dlsym (). To work around this, - * we create an ELF ctor function which will be invoked by dlopen, and which - * will call a function in the AOT loader to register the symbols used by the - * image. - * When static linking, we emit a global which will point to the symbol table. - */ - if (acfg->aot_opts.no_dlsym) { - int i; - char symbol [256]; - - if (acfg->aot_opts.static_link) - /* Emit a string holding the assembly name */ - emit_string_symbol (acfg, "mono_aot_assembly_name", acfg->image->assembly->aname.name); - - /* Emit the names */ - for (i = 0; i < acfg->globals->len; ++i) { - char *name = g_ptr_array_index (acfg->globals, i); - - sprintf (symbol, "name_%d", i); - emit_section_change (acfg, ".text", 1); - emit_label (acfg, symbol); - emit_string (acfg, name); - } - - /* Emit the globals table */ - sprintf (symbol, "globals"); - emit_section_change (acfg, ".data", 0); - /* This is not a global, since it is accessed by the init function */ - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - - for (i = 0; i < acfg->globals->len; ++i) { - char *name = g_ptr_array_index (acfg->globals, i); - - sprintf (symbol, "name_%d", i); - emit_pointer (acfg, symbol); - - sprintf (symbol, "%s", name); - emit_pointer (acfg, symbol); - } - /* Null terminate the table */ - emit_pointer (acfg, NULL); - emit_pointer (acfg, NULL); - - if (acfg->aot_opts.static_link) { - char *p; - - /* - * Emit a global symbol which can be passed by an embedding app to - * mono_aot_register_module (). - */ -#if defined(__MACH__) - sprintf (symbol, "_mono_aot_module_%s_info", acfg->image->assembly->aname.name); -#else - sprintf (symbol, "mono_aot_module_%s_info", acfg->image->assembly->aname.name); -#endif - - /* Get rid of characters which cannot occur in symbols */ - p = symbol; - for (p = symbol; *p; ++p) { - if (!(isalnum (*p) || *p == '_')) - *p = '_'; - } - acfg->static_linking_symbol = g_strdup (symbol); - emit_global_inner (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - emit_pointer (acfg, "globals"); - } else { - sprintf (symbol, "init_%s", acfg->image->assembly->aname.name); - emit_section_change (acfg, ".text", 1); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); -#ifdef USE_BIN_WRITER - g_assert_not_reached (); -#else -#ifdef __x86_64__ - fprintf (acfg->fp, "leaq globals(%%rip), %%rdi\n"); - fprintf (acfg->fp, "call mono_aot_register_globals@PLT\n"); - fprintf (acfg->fp, "ret\n"); - fprintf (acfg->fp, ".section .ctors,\"aw\",@progbits\n"); - emit_alignment (acfg, 8); - emit_pointer (acfg, symbol); -#elif defined(__arm__) && defined(__MACH__) - - fprintf (acfg->fp, ".text\n"); - fprintf (acfg->fp, ".align 3\n"); - - fprintf (acfg->fp, "ldr r0, .L5\n"); - fprintf (acfg->fp, ".LPIC0:\n"); - fprintf (acfg->fp, "add r0, pc, r0\n"); - fprintf (acfg->fp, "ldr r0, [r0]\n"); - fprintf (acfg->fp, "b _mono_aot_register_globals@PLT\n"); - fprintf (acfg->fp, ".align 2\n"); - - fprintf (acfg->fp, ".L5:\n"); - fprintf (acfg->fp, ".long globals_ptr-(.LPIC0+8)\n"); - - fprintf (acfg->fp, ".data\n"); - fprintf (acfg->fp, ".align 2\n"); - fprintf (acfg->fp, "globals_ptr:\n"); - fprintf (acfg->fp, ".long globals\n"); - - fprintf (acfg->fp, ".mod_init_func\n"); - fprintf (acfg->fp, ".align 2\n"); - fprintf (acfg->fp, ".long %s@target1\n", symbol); - -#elif defined(__arm__) - /* - * Taken from gcc generated code for: - * static int i; - * void foo () { bar (&i); } - * gcc --shared -fPIC -O2 - */ - fprintf (acfg->fp, "ldr r3, .L5\n"); - fprintf (acfg->fp, "ldr r0, .L5+4\n"); - fprintf (acfg->fp, ".LPIC0:\n"); - fprintf (acfg->fp, "add r3, pc, r3\n"); - fprintf (acfg->fp, "add r0, r3, r0\n"); - fprintf (acfg->fp, "b mono_aot_register_globals(PLT)\n"); - - fprintf (acfg->fp, ".L5:\n"); - fprintf (acfg->fp, ".word _GLOBAL_OFFSET_TABLE_-(.LPIC0+8)\n"); - fprintf (acfg->fp, ".word globals(GOTOFF)\n"); - - fprintf (acfg->fp, ".section .init_array,\"aw\",%%init_array\n"); - fprintf (acfg->fp, ".align 2\n"); - fprintf (acfg->fp, ".word %s(target1)\n", symbol); -#else - g_assert_not_reached (); -#endif -#endif - } - } -} - -/* - * Emit a structure containing all the information not stored elsewhere. - */ -static void -emit_file_info (MonoAotCompile *acfg) -{ - char symbol [128]; + g_ptr_array_add (acfg->extra_methods, cfg->orig_method); - sprintf (symbol, "mono_aot_file_info"); - emit_section_change (acfg, ".data", 0); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); - emit_global (acfg, symbol, FALSE); + mono_acfg_unlock (acfg); - /* The data emitted here must match MonoAotFileInfo in aot-runtime.c. */ - emit_int32 (acfg, acfg->plt_got_offset_base); - emit_int32 (acfg, acfg->trampoline_got_offset_base); - emit_int32 (acfg, acfg->num_aot_trampolines); - emit_int32 (acfg, (int)(acfg->got_offset * sizeof (gpointer))); - emit_int32 (acfg, acfg->plt_offset); - emit_pointer (acfg, "got"); + InterlockedIncrement (&acfg->stats.ccount); } + +static void +compile_thread_main (gpointer *user_data) +{ + MonoDomain *domain = user_data [0]; + MonoAotCompile *acfg = user_data [1]; + GPtrArray *methods = user_data [2]; + int i; -/*****************************************/ -/* Emitting DWARF debug information */ -/*****************************************/ + mono_thread_attach (domain); -static G_GNUC_UNUSED void -emit_uleb128 (MonoAotCompile *acfg, guint32 value) -{ - do { - guint8 b = value & 0x7f; - value >>= 7; - if (value != 0) /* more bytes to come */ - b |= 0x80; - emit_byte (acfg, b); - } while (value); + for (i = 0; i < methods->len; ++i) + compile_method (acfg, g_ptr_array_index (methods, i)); } -static G_GNUC_UNUSED void -emit_sleb128 (MonoAotCompile *acfg, gint64 value) +static void +load_profile_files (MonoAotCompile *acfg) { - gboolean more = 1; - gboolean negative = (value < 0); - guint32 size = 64; - guint8 byte; + FILE *infile; + char *tmp; + int file_index, res, method_index, i; + char ver [256]; + guint32 token; + GList *unordered; - while (more) { - byte = value & 0x7f; - value >>= 7; - /* the following is unnecessary if the - * implementation of >>= uses an arithmetic rather - * than logical shift for a signed left operand - */ - if (negative) - /* sign extend */ - value |= - ((gint64)1 <<(size - 7)); - /* sign bit of byte is second high order bit (0x40) */ - if ((value == 0 && !(byte & 0x40)) || - (value == -1 && (byte & 0x40))) - more = 0; - else - byte |= 0x80; - emit_byte (acfg, byte); - } -} + file_index = 0; + while (TRUE) { + tmp = g_strdup_printf ("%s/.mono/aot-profile-data/%s-%s-%d", g_get_home_dir (), acfg->image->assembly_name, acfg->image->guid, file_index); -static G_GNUC_UNUSED void -encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf) -{ - guint8 *p = buf; + if (!g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) { + g_free (tmp); + break; + } - do { - guint8 b = value & 0x7f; - value >>= 7; - if (value != 0) /* more bytes to come */ - b |= 0x80; - *p ++ = b; - } while (value); + infile = fopen (tmp, "r"); + g_assert (infile); - *endbuf = p; -} + printf ("Using profile data file '%s'\n", tmp); + g_free (tmp); -static G_GNUC_UNUSED void -encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf) -{ - gboolean more = 1; - gboolean negative = (value < 0); - guint32 size = 32; - guint8 byte; - guint8 *p = buf; + file_index ++; - while (more) { - byte = value & 0x7f; - value >>= 7; - /* the following is unnecessary if the - * implementation of >>= uses an arithmetic rather - * than logical shift for a signed left operand - */ - if (negative) - /* sign extend */ - value |= - (1 <<(size - 7)); - /* sign bit of byte is second high order bit (0x40) */ - if ((value == 0 && !(byte & 0x40)) || - (value == -1 && (byte & 0x40))) - more = 0; - else - byte |= 0x80; - *p ++= byte; - } + res = fscanf (infile, "%32s\n", ver); + if ((res != 1) || strcmp (ver, "#VER:1") != 0) { + printf ("Profile file has wrong version or invalid.\n"); + fclose (infile); + continue; + } - *endbuf = p; -} + while (TRUE) { + res = fscanf (infile, "%d\n", &token); + if (res < 1) + break; -static void -emit_dwarf_abbrev (MonoAotCompile *acfg, int code, int tag, gboolean has_child, - int *attrs, int attrs_len) -{ - int i; + method_index = mono_metadata_token_index (token) - 1; - emit_uleb128 (acfg, code); - emit_uleb128 (acfg, tag); - emit_byte (acfg, has_child); + if (!g_list_find (acfg->method_order, GUINT_TO_POINTER (method_index))) + acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (method_index)); + } + fclose (infile); + } - for (i = 0; i < attrs_len; i++) - emit_uleb128 (acfg, attrs [i]); - emit_uleb128 (acfg, 0); - emit_uleb128 (acfg, 0); + /* Add missing methods */ + unordered = NULL; + for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) { + if (!g_list_find (acfg->method_order, GUINT_TO_POINTER (i))) + unordered = g_list_prepend (unordered, GUINT_TO_POINTER (i)); + } + unordered = g_list_reverse (unordered); + if (acfg->method_order) + g_list_last (acfg->method_order)->next = unordered; + else + acfg->method_order = unordered; } +/** + * alloc_got_slots: + * + * Collect all patches which have shared GOT entries and alloc entries for them. The + * rest will get entries allocated during emit_code (). + */ static void -emit_cie (MonoAotCompile *acfg) +alloc_got_slots (MonoAotCompile *acfg) { -#if defined(__x86_64__) || defined(__arm__) - emit_section_change (acfg, ".debug_frame", 0); + int i; + GList *l; + MonoJumpInfo *ji; - emit_alignment (acfg, 8); + /* Slot 0 is reserved for the address of the current assembly */ + ji = mono_mempool_alloc0 (acfg->mempool, sizeof (MonoAotCompile)); + ji->type = MONO_PATCH_INFO_IMAGE; + ji->data.image = acfg->image; - /* Emit a CIE */ - emit_symbol_diff (acfg, ".Lcie0_end", ".", -4); /* length */ - emit_int32 (acfg, 0xffffffff); /* CIE id */ - emit_byte (acfg, 3); /* version */ - emit_string (acfg, ""); /* augmention */ - emit_sleb128 (acfg, 1); /* code alignment factor */ -#ifdef __x86_64__ - emit_sleb128 (acfg, -8); /* data alignment factor */ - emit_uleb128 (acfg, AMD64_RIP); -#elif defined(__arm__) - emit_sleb128 (acfg, -4); /* data alignment factor */ - emit_uleb128 (acfg, mono_hw_reg_to_dwarf_reg (ARMREG_LR)); -#else - g_assert_not_reached (); -#endif + get_shared_got_offset (acfg, ji); -#ifdef __x86_64__ - emit_byte (acfg, DW_CFA_def_cfa); - emit_uleb128 (acfg, mono_hw_reg_to_dwarf_reg (AMD64_RSP)); - emit_uleb128 (acfg, 8); /* offset=8 */ - emit_byte (acfg, DW_CFA_offset | AMD64_RIP); - emit_uleb128 (acfg, 1); /* offset=-8 */ -#elif defined(__arm__) -#else - g_assert_not_reached (); -#endif + for (l = acfg->method_order; l != NULL; l = l->next) { + i = GPOINTER_TO_UINT (l->data); - emit_alignment (acfg, sizeof (gpointer)); - emit_label (acfg, ".Lcie0_end"); -#endif -} + if (acfg->cfgs [i]) { + MonoCompile *cfg = acfg->cfgs [i]; -static void -emit_pointer_value (MonoAotCompile *acfg, gpointer ptr) -{ - gssize val = (gssize)ptr; - emit_bytes (acfg, (guint8*)&val, sizeof (gpointer)); + for (ji = cfg->patch_info; ji; ji = ji->next) { + if (mono_aot_is_shared_got_patch (ji)) + get_shared_got_offset (acfg, ji); + } + } + } } static void -emit_fde (MonoAotCompile *acfg, int fde_index, char *start_symbol, char *end_symbol, - guint8 *code, guint32 code_size, GSList *unwind_ops, gboolean use_cie) +emit_code (MonoAotCompile *acfg) { -#if defined(__x86_64__) || defined(__arm__) - char symbol [128]; - GSList *l; - guint8 *uw_info; - guint32 uw_info_len; - -#ifdef __arm__ - if (!unwind_ops) - /* - * The debugger can unwind without unwind info, but gets confused by empty - * info. - */ - return; -#endif + int i; + char symbol [256]; + GList *l; - emit_section_change (acfg, ".debug_frame", 0); + sprintf (symbol, "methods"); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, TRUE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - sprintf (symbol, ".Lfde%d_end", fde_index); - emit_symbol_diff (acfg, symbol, ".", -4); /* length */ - emit_int32 (acfg, 0); /* CIE_pointer */ - if (start_symbol) { - emit_pointer (acfg, start_symbol); /* initial_location */ - emit_symbol_diff (acfg, end_symbol, start_symbol, 0); /* address_range */ - } else { - emit_pointer_value (acfg, code); - emit_int32 (acfg, code_size); - } -#if SIZEOF_VOID_P == 8 - /* Upper 32 bits of code size */ - emit_int32 (acfg, 0); -#endif + /* + * Emit some padding so the local symbol for the first method doesn't have the + * same address as 'methods'. + */ + emit_zero_bytes (acfg, 16); - l = unwind_ops; -#ifdef __x86_64__ - if (use_cie) - /* Skip the first two ops which are in the CIE */ - l = l->next->next; -#endif + for (l = acfg->method_order; l != NULL; l = l->next) { + i = GPOINTER_TO_UINT (l->data); - /* Convert the list of MonoUnwindOps to the format used by DWARF */ - uw_info = mono_unwind_ops_encode (l, &uw_info_len); - emit_bytes (acfg, uw_info, uw_info_len); - g_free (uw_info); + if (acfg->cfgs [i]) + emit_method_code (acfg, acfg->cfgs [i]); + } - emit_alignment (acfg, sizeof (gpointer)); - sprintf (symbol, ".Lfde%d_end", fde_index); + sprintf (symbol, "methods_end"); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); emit_label (acfg, symbol); -#endif -} -/* Abbrevations */ -#define ABBREV_COMPILE_UNIT 1 -#define ABBREV_SUBPROGRAM 2 -#define ABBREV_PARAM 3 -#define ABBREV_BASE_TYPE 4 -#define ABBREV_STRUCT_TYPE 5 -#define ABBREV_DATA_MEMBER 6 -#define ABBREV_TYPEDEF 7 -#define ABBREV_ENUM_TYPE 8 -#define ABBREV_ENUMERATOR 9 -#define ABBREV_NAMESPACE 10 -#define ABBREV_VARIABLE 11 -#define ABBREV_VARIABLE_LOCLIST 12 - -static int compile_unit_attr [] = { - DW_AT_producer ,DW_FORM_string, - DW_AT_name ,DW_FORM_string, - DW_AT_comp_dir ,DW_FORM_string, - DW_AT_language ,DW_FORM_data1, - DW_AT_low_pc ,DW_FORM_addr, - DW_AT_high_pc ,DW_FORM_addr, - DW_AT_stmt_list ,DW_FORM_data4 -}; - -static int subprogram_attr [] = { - DW_AT_name , DW_FORM_string, - DW_AT_low_pc , DW_FORM_addr, - DW_AT_high_pc , DW_FORM_addr, - DW_AT_frame_base , DW_FORM_block1 -}; + sprintf (symbol, "method_offsets"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); -static int param_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref4, - DW_AT_location, DW_FORM_block1 -}; + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i]) { + sprintf (symbol, ".Lm_%x", i); + emit_symbol_diff (acfg, symbol, "methods", 0); + } else { + emit_int32 (acfg, 0xffffffff); + } + } + emit_line (acfg); +} -static int base_type_attr [] = { - DW_AT_byte_size, DW_FORM_data1, - DW_AT_encoding, DW_FORM_data1, - DW_AT_name, DW_FORM_string -}; +static void +emit_info (MonoAotCompile *acfg) +{ + int i; + char symbol [256]; + GList *l; -static int struct_type_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_byte_size, DW_FORM_udata, -}; + /* Emit method info */ + sprintf (symbol, "method_info"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); -static int data_member_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref4, - DW_AT_data_member_location, DW_FORM_block1 -}; + /* To reduce size of generated assembly code */ + sprintf (symbol, "mi"); + emit_label (acfg, symbol); -static int typedef_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref4 -}; + for (l = acfg->method_order; l != NULL; l = l->next) { + i = GPOINTER_TO_UINT (l->data); -static int enum_type_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_byte_size, DW_FORM_udata, - DW_AT_type, DW_FORM_ref4, -}; + if (acfg->cfgs [i]) + emit_method_info (acfg, acfg->cfgs [i]); + } -static int enumerator_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_const_value, DW_FORM_sdata, -}; + sprintf (symbol, "method_info_offsets"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); -static int namespace_attr [] = { - DW_AT_name, DW_FORM_string, -}; + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i]) { + sprintf (symbol, ".Lm_%x_p", i); + emit_symbol_diff (acfg, symbol, "mi", 0); + } else { + emit_int32 (acfg, 0); + } + } + emit_line (acfg); +} -static int variable_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref4, - DW_AT_location, DW_FORM_block1 -}; +#endif /* #if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) */ -static int variable_loclist_attr [] = { - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref4, - DW_AT_location, DW_FORM_data4 -}; +/* + * mono_aot_str_hash: + * + * Hash function for strings which we use to hash strings for things which are + * saved in the AOT image, since g_str_hash () can change. + */ +guint +mono_aot_str_hash (gconstpointer v1) +{ + /* Same as g_str_hash () in glib */ + char *p = (char *) v1; + guint hash = *p; -typedef struct DwarfBasicType { - const char *die_name, *name; - int type; - int size; - int encoding; -} DwarfBasicType; - -static DwarfBasicType basic_types [] = { - { ".LDIE_I1", "sbyte", MONO_TYPE_I1, 1, DW_ATE_signed }, - { ".LDIE_U1", "byte", MONO_TYPE_U1, 1, DW_ATE_unsigned }, - { ".LDIE_I2", "short", MONO_TYPE_I2, 2, DW_ATE_signed }, - { ".LDIE_U2", "ushort", MONO_TYPE_U2, 2, DW_ATE_unsigned }, - { ".LDIE_I4", "int", MONO_TYPE_I4, 4, DW_ATE_signed }, - { ".LDIE_U4", "uint", MONO_TYPE_U4, 4, DW_ATE_unsigned }, - { ".LDIE_I8", "long", MONO_TYPE_I8, 8, DW_ATE_signed }, - { ".LDIE_U8", "ulong", MONO_TYPE_U8, 8, DW_ATE_unsigned }, - { ".LDIE_R4", "float", MONO_TYPE_R4, 4, DW_ATE_float }, - { ".LDIE_R8", "double", MONO_TYPE_R8, 8, DW_ATE_float }, - { ".LDIE_BOOLEAN", "boolean", MONO_TYPE_BOOLEAN, 1, DW_ATE_boolean }, - { ".LDIE_STRING", "string", MONO_TYPE_STRING, sizeof (gpointer), DW_ATE_address }, - { ".LDIE_OBJECT", "object", MONO_TYPE_OBJECT, sizeof (gpointer), DW_ATE_address }, - { ".LDIE_SZARRAY", "object", MONO_TYPE_SZARRAY, sizeof (gpointer), DW_ATE_address }, -}; + while (*p++) { + if (*p) + hash = (hash << 5) - hash + *p; + } -/* Constants for encoding line number special opcodes */ -#define OPCODE_BASE 13 -#define LINE_BASE -5 -#define LINE_RANGE 14 + return hash; +} -/* Subsections of the .debug_line section */ -#define LINE_SUBSECTION_HEADER 1 -#define LINE_SUBSECTION_INCLUDES 2 -#define LINE_SUBSECTION_FILES 3 -#define LINE_SUBSECTION_DATA 4 -#define LINE_SUBSECTION_END 5 +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +#define mix(a,b,c) { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} +#define final(a,b,c) { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} -static int -emit_line_number_file_name (MonoAotCompile *acfg, const char *name, - gint64 last_mod_time, gint64 file_size) +/* + * mono_aot_method_hash: + * + * Return a hash code for methods which only depends on metadata. + */ +guint32 +mono_aot_method_hash (MonoMethod *method) { - int index; - int dir_index; - char *basename = NULL; - - if (!acfg->file_to_index) - acfg->file_to_index = g_hash_table_new (g_str_hash, g_str_equal); - - index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->file_to_index, name)); - if (index > 0) - return index; + MonoMethodSignature *sig; + MonoClass *klass; + int i; + int hashes_count; + guint32 *hashes_start, *hashes; + guint32 a, b, c; - if (g_path_is_absolute (name)) { - char *dir = g_path_get_dirname (name); + /* Similar to the hash in mono_method_get_imt_slot () */ - if (!acfg->dir_to_index) - acfg->dir_to_index = g_hash_table_new (g_str_hash, g_str_equal); + sig = mono_method_signature (method); - dir_index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->dir_to_index, dir)); - if (dir_index == 0) { - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_INCLUDES); - emit_string (acfg, dir); + hashes_count = sig->param_count + 5; + hashes_start = malloc (hashes_count * sizeof (guint32)); + hashes = hashes_start; - dir_index = ++ acfg->line_number_dir_index; - g_hash_table_insert (acfg->dir_to_index, g_strdup (dir), GUINT_TO_POINTER (dir_index)); - } + /* Some wrappers are assigned to random classes */ + if (!method->wrapper_type || method->wrapper_type == MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK) + klass = method->klass; + else + klass = mono_defaults.object_class; - g_free (dir); + if (!method->wrapper_type) { + char *full_name = mono_type_full_name (&klass->byval_arg); - basename = g_path_get_basename (name); + hashes [0] = mono_aot_str_hash (full_name); + hashes [1] = 0; + g_free (full_name); } else { - dir_index = 0; + hashes [0] = mono_aot_str_hash (klass->name); + hashes [1] = mono_aot_str_hash (klass->name_space); + } + hashes [2] = mono_aot_str_hash (method->name); + hashes [3] = method->wrapper_type; + hashes [4] = mono_metadata_type_hash (sig->ret); + for (i = 0; i < sig->param_count; i++) { + /* This is needed for some reason */ + if (method->wrapper_type && sig->params [i]->type == MONO_TYPE_GENERICINST) + hashes [5 + i] = MONO_TYPE_GENERICINST; + else + hashes [5 + i] = mono_metadata_type_hash (sig->params [i]); + } + + /* Setup internal state */ + a = b = c = 0xdeadbeef + (((guint32)hashes_count)<<2); + + /* Handle most of the hashes */ + while (hashes_count > 3) { + a += hashes [0]; + b += hashes [1]; + c += hashes [2]; + mix (a,b,c); + hashes_count -= 3; + hashes += 3; + } + + /* Handle the last 3 hashes (all the case statements fall through) */ + switch (hashes_count) { + case 3 : c += hashes [2]; + case 2 : b += hashes [1]; + case 1 : a += hashes [0]; + final (a,b,c); + case 0: /* nothing left to add */ + break; } + + free (hashes_start); + + return c; +} +#undef rot +#undef mix +#undef final - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_FILES); +/* + * mono_aot_wrapper_name: + * + * Return a string which uniqely identifies the given wrapper method. + */ +char* +mono_aot_wrapper_name (MonoMethod *method) +{ + char *name, *tmpsig, *klass_desc; - if (basename) - emit_string (acfg, basename); - else - emit_string (acfg, name); - emit_uleb128 (acfg, dir_index); - emit_byte (acfg, 0); - emit_byte (acfg, 0); + tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE); - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_DATA); + switch (method->wrapper_type) { + case MONO_WRAPPER_RUNTIME_INVOKE: + case MONO_WRAPPER_DELEGATE_INVOKE: + case MONO_WRAPPER_DELEGATE_BEGIN_INVOKE: + case MONO_WRAPPER_DELEGATE_END_INVOKE: + /* This is a hack to work around the fact that runtime invoke wrappers get assigned to some random class */ + name = g_strdup_printf ("%s (%s)", method->name, tmpsig); + break; + default: + klass_desc = mono_type_full_name (&method->klass->byval_arg); - if (basename) - g_free (basename); + name = g_strdup_printf ("%s:%s (%s)", klass_desc, method->name, tmpsig); + break; + } - index = ++ acfg->line_number_file_index; - g_hash_table_insert (acfg->file_to_index, g_strdup (name), GUINT_TO_POINTER (index)); + g_free (tmpsig); - return index; + return name; } -static void -emit_base_dwarf_info (MonoAotCompile *acfg) +/* + * mono_aot_tramp_info_create: + * + * Create a MonoAotTrampInfo structure from the arguments. + */ +MonoAotTrampInfo* +mono_aot_tramp_info_create (const char *name, guint8 *code, guint32 code_size) { - char *s, *build_info; - int i; + MonoAotTrampInfo *info = g_new0 (MonoAotTrampInfo, 1); - emit_section_change (acfg, ".debug_abbrev", 0); - emit_dwarf_abbrev (acfg, ABBREV_COMPILE_UNIT, DW_TAG_compile_unit, TRUE, - compile_unit_attr, G_N_ELEMENTS (compile_unit_attr)); - emit_dwarf_abbrev (acfg, ABBREV_SUBPROGRAM, DW_TAG_subprogram, TRUE, - subprogram_attr, G_N_ELEMENTS (subprogram_attr)); - emit_dwarf_abbrev (acfg, ABBREV_PARAM, DW_TAG_formal_parameter, FALSE, - param_attr, G_N_ELEMENTS (param_attr)); - emit_dwarf_abbrev (acfg, ABBREV_BASE_TYPE, DW_TAG_base_type, FALSE, - base_type_attr, G_N_ELEMENTS (base_type_attr)); - emit_dwarf_abbrev (acfg, ABBREV_STRUCT_TYPE, DW_TAG_class_type, TRUE, - struct_type_attr, G_N_ELEMENTS (struct_type_attr)); - emit_dwarf_abbrev (acfg, ABBREV_DATA_MEMBER, DW_TAG_member, FALSE, - data_member_attr, G_N_ELEMENTS (data_member_attr)); - emit_dwarf_abbrev (acfg, ABBREV_TYPEDEF, DW_TAG_typedef, FALSE, - typedef_attr, G_N_ELEMENTS (typedef_attr)); - emit_dwarf_abbrev (acfg, ABBREV_ENUM_TYPE, DW_TAG_enumeration_type, TRUE, - enum_type_attr, G_N_ELEMENTS (enum_type_attr)); - emit_dwarf_abbrev (acfg, ABBREV_ENUMERATOR, DW_TAG_enumerator, FALSE, - enumerator_attr, G_N_ELEMENTS (enumerator_attr)); - emit_dwarf_abbrev (acfg, ABBREV_NAMESPACE, DW_TAG_namespace, TRUE, - namespace_attr, G_N_ELEMENTS (namespace_attr)); - emit_dwarf_abbrev (acfg, ABBREV_VARIABLE, DW_TAG_variable, FALSE, - variable_attr, G_N_ELEMENTS (variable_attr)); - emit_dwarf_abbrev (acfg, ABBREV_VARIABLE_LOCLIST, DW_TAG_variable, FALSE, - variable_loclist_attr, G_N_ELEMENTS (variable_loclist_attr)); - emit_byte (acfg, 0); - - emit_section_change (acfg, ".debug_info", 0); - emit_label (acfg, ".Ldebug_info_start"); - emit_symbol_diff (acfg, ".Ldebug_info_end", ".", -4); /* length */ - emit_int16 (acfg, 0x3); /* DWARF version 3 */ - emit_int32 (acfg, 0); /* .debug_abbrev offset */ - emit_byte (acfg, sizeof (gpointer)); /* address size */ - - /* Emit this into a separate section so it gets placed at the end */ - emit_section_change (acfg, ".debug_info", 1); - emit_int32 (acfg, 0); /* close everything */ - emit_label (acfg, ".Ldebug_info_end"); - emit_section_change (acfg, ".debug_info", 0); - - /* Compilation unit */ - emit_uleb128 (acfg, ABBREV_COMPILE_UNIT); - build_info = mono_get_runtime_build_info (); - s = g_strdup_printf ("Mono AOT Compiler %s", build_info); - emit_string (acfg, s); - g_free (build_info); - g_free (s); - emit_string (acfg, "JITted code"); - emit_string (acfg, ""); - emit_byte (acfg, DW_LANG_C); - emit_pointer_value (acfg, 0); - emit_pointer_value (acfg, 0); - /* offset into .debug_line section */ - emit_symbol_diff (acfg, ".Ldebug_line_start", ".Ldebug_line_section_start", 0); - - /* Base types */ - for (i = 0; i < G_N_ELEMENTS (basic_types); ++i) { - emit_label (acfg, basic_types [i].die_name); - emit_uleb128 (acfg, ABBREV_BASE_TYPE); - emit_byte (acfg, basic_types [i].size); - emit_byte (acfg, basic_types [i].encoding); - emit_string (acfg, basic_types [i].name); - } - - /* debug_loc section */ - emit_section_change (acfg, ".debug_loc", 0); - emit_label (acfg, ".Ldebug_loc_start"); + info->name = (char*)name; + info->code = code; + info->code_size = code_size; - /* Line number info header */ - /* - * GAS seems to emit its own data to the end of the first subsection, so we use - * subsections 1, 2 etc: - * 1 - contains the header - * 2 - contains the file names - * 3 - contains the end of the header + the data - * 4 - the end symbol - */ - emit_section_change (acfg, ".debug_line", 0); - emit_label (acfg, ".Ldebug_line_section_start"); - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_HEADER); - emit_label (acfg, ".Ldebug_line_start"); - emit_symbol_diff (acfg, ".Ldebug_line_end", ".", -4); /* length */ - emit_int16 (acfg, 0x2); /* version */ - emit_symbol_diff (acfg, ".Ldebug_line_header_end", ".", -4); /* header_length */ - emit_byte (acfg, 1); /* minimum_instruction_length */ - emit_byte (acfg, 1); /* default_is_stmt */ - emit_byte (acfg, LINE_BASE); /* line_base */ - emit_byte (acfg, LINE_RANGE); /* line_range */ - emit_byte (acfg, OPCODE_BASE); /* opcode_base */ - emit_byte (acfg, 0); /* standard_opcode_lengths */ - emit_byte (acfg, 1); - emit_byte (acfg, 1); - emit_byte (acfg, 1); - emit_byte (acfg, 1); - emit_byte (acfg, 0); - emit_byte (acfg, 0); - emit_byte (acfg, 0); - emit_byte (acfg, 1); - emit_byte (acfg, 0); - emit_byte (acfg, 0); - emit_byte (acfg, 1); - - /* Includes */ - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_INCLUDES); - - /* End of Includes */ - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_FILES); - emit_byte (acfg, 0); - - /* Files */ - emit_line_number_file_name (acfg, "xdb.il", 0, 0); - - /* End of Files */ - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_DATA); - emit_byte (acfg, 0); - - emit_label (acfg, ".Ldebug_line_header_end"); - - /* Emit this into a separate subsection so it gets placed at the end */ - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_END); - - emit_byte (acfg, 0); - emit_byte (acfg, 1); - emit_byte (acfg, DW_LNE_end_sequence); - - emit_label (acfg, ".Ldebug_line_end"); - - emit_cie (acfg); + return info; } -/* Returns the local symbol pointing to the emitted debug info */ -static char* -emit_class_dwarf_info (MonoAotCompile *acfg, MonoClass *klass) +/* + * mono_is_shared_got_patch: + * + * Return whenever PATCH_INFO refers to a patch which needs a shared GOT + * entry. + */ +gboolean +mono_aot_is_shared_got_patch (MonoJumpInfo *patch_info) { - char *die; - char *full_name; - gpointer iter; - MonoClassField *field; - const char *fdie; - int k; - gboolean emit_namespace = FALSE; + switch (patch_info->type) { + case MONO_PATCH_INFO_VTABLE: + case MONO_PATCH_INFO_CLASS: + case MONO_PATCH_INFO_IID: + case MONO_PATCH_INFO_ADJUSTED_IID: + case MONO_PATCH_INFO_FIELD: + case MONO_PATCH_INFO_SFLDA: + case MONO_PATCH_INFO_DECLSEC: + case MONO_PATCH_INFO_LDTOKEN: + case MONO_PATCH_INFO_TYPE_FROM_HANDLE: + case MONO_PATCH_INFO_RVA: + case MONO_PATCH_INFO_METHODCONST: + case MONO_PATCH_INFO_IMAGE: + return TRUE; + default: + return FALSE; + } +} - // FIXME: Appdomains - if (!acfg->class_to_die) - acfg->class_to_die = g_hash_table_new (NULL, NULL); +#if !defined(DISABLE_AOT) && !defined(DISABLE_JIT) - die = g_hash_table_lookup (acfg->class_to_die, klass); - if (die) - return die; +typedef struct HashEntry { + guint32 key, value, index; + struct HashEntry *next; +} HashEntry; - if (!((klass->byval_arg.type == MONO_TYPE_CLASS) || klass->enumtype)) - return NULL; +/* + * emit_extra_methods: + * + * Emit methods which are not in the METHOD table, like wrappers. + */ +static void +emit_extra_methods (MonoAotCompile *acfg) +{ + int i, table_size, buf_size; + char symbol [256]; + guint8 *p, *buf; + guint32 *info_offsets; + guint32 hash; + GPtrArray *table; + HashEntry *entry, *new_entry; + int nmethods, max_chain_length; + int *chain_lengths; - /* - * FIXME: gdb can't handle namespaces in languages it doesn't know about. - */ - /* - if (klass->name_space && klass->name_space [0] != '\0') - emit_namespace = TRUE; - */ - if (emit_namespace) { - emit_uleb128 (acfg, ABBREV_NAMESPACE); - emit_string (acfg, klass->name_space); - } + info_offsets = g_new0 (guint32, acfg->extra_methods->len); - full_name = g_strdup_printf ("%s%s%s", klass->name_space, klass->name_space ? "." : "", klass->name); + buf_size = acfg->extra_methods->len * 256 + 256; + p = buf = g_malloc (buf_size); - die = g_strdup_printf (".LTDIE_%d", acfg->tdie_index); - emit_label (acfg, die); + /* Encode method info */ + nmethods = 0; + /* So offsets are > 0 */ + *p = 0; + p++; + for (i = 0; i < acfg->extra_methods->len; ++i) { + MonoMethod *method = g_ptr_array_index (acfg->extra_methods, i); + MonoCompile *cfg = g_hash_table_lookup (acfg->method_to_cfg, method); + char *name; - if (klass->enumtype) { - int size = mono_class_value_size (mono_class_from_mono_type (klass->enum_basetype), NULL); + if (!cfg) + continue; - emit_uleb128 (acfg, ABBREV_ENUM_TYPE); - emit_string (acfg, full_name); - emit_uleb128 (acfg, size); - for (k = 0; k < G_N_ELEMENTS (basic_types); ++k) - if (basic_types [k].type == klass->enum_basetype->type) - break; - g_assert (k < G_N_ELEMENTS (basic_types)); - emit_symbol_diff (acfg, basic_types [k].die_name, ".Ldebug_info_start", 0); + nmethods ++; + info_offsets [i] = p - buf; - /* Emit enum values */ - iter = NULL; - while ((field = mono_class_get_fields (klass, &iter))) { - const char *p; - int len; - MonoTypeEnum def_type; - - if (strcmp ("value__", mono_field_get_name (field)) == 0) - continue; - if (mono_field_is_deleted (field)) - continue; - - emit_uleb128 (acfg, ABBREV_ENUMERATOR); - emit_string (acfg, mono_field_get_name (field)); - - p = mono_class_get_field_default_value (field, &def_type); - len = mono_metadata_decode_blob_size (p, &p); - switch (klass->enum_basetype->type) { - case MONO_TYPE_U1: - case MONO_TYPE_I1: - emit_sleb128 (acfg, *p); - break; - case MONO_TYPE_CHAR: - case MONO_TYPE_U2: - case MONO_TYPE_I2: - emit_sleb128 (acfg, read16 (p)); - break; - case MONO_TYPE_U4: - case MONO_TYPE_I4: - emit_sleb128 (acfg, read32 (p)); - break; - case MONO_TYPE_U8: - case MONO_TYPE_I8: - emit_sleb128 (acfg, read64 (p)); + name = NULL; + if (method->wrapper_type) { + /* + * We encode some wrappers using their name, since encoding them + * directly would be difficult. This also avoids creating the wrapper + * methods at runtime, since they are not needed anyway. + */ + switch (method->wrapper_type) { + case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: + case MONO_WRAPPER_SYNCHRONIZED: + /* encode_method_ref () can handle these */ break; default: - g_assert_not_reached (); + name = mono_aot_wrapper_name (method); + break; } } - } else { - emit_uleb128 (acfg, ABBREV_STRUCT_TYPE); - emit_string (acfg, full_name); - emit_uleb128 (acfg, klass->instance_size); - /* Emit fields */ - iter = NULL; - while ((field = mono_class_get_fields (klass, &iter))) { - guint8 buf [128]; - guint8 *p; - - if (field->type->attrs & FIELD_ATTRIBUTE_STATIC) - continue; - - for (k = 0; k < G_N_ELEMENTS (basic_types); ++k) - if (basic_types [k].type == field->type->type) - break; - if (k < G_N_ELEMENTS (basic_types) && field->type->type != MONO_TYPE_SZARRAY && field->type->type != MONO_TYPE_CLASS) { - fdie = basic_types [k].die_name; - - emit_uleb128 (acfg, ABBREV_DATA_MEMBER); - emit_string (acfg, field->name); - emit_symbol_diff (acfg, fdie, ".Ldebug_info_start", 0); - /* location */ - p = buf; - *p ++= DW_OP_plus_uconst; - encode_uleb128 (field->offset, p, &p); - - emit_byte (acfg, p - buf); - emit_bytes (acfg, buf, p - buf); - } + if (name) { + encode_value (1, p, &p); + encode_value (method->wrapper_type, p, &p); + strcpy ((char*)p, name); + p += strlen (name ) + 1; + g_free (name); + } else { + encode_value (0, p, &p); + encode_method_ref (acfg, method, p, &p); } + + g_assert ((p - buf) < buf_size); } - /* Type end */ - emit_uleb128 (acfg, 0x0); + g_assert ((p - buf) < buf_size); + + /* Emit method info */ + sprintf (symbol, "extra_method_info"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + + emit_bytes (acfg, buf, p - buf); + + emit_line (acfg); + + /* + * Construct a chained hash table for mapping indexes in extra_method_info to + * method indexes. + */ + table_size = g_spaced_primes_closest ((int)(nmethods * 1.5)); + table = g_ptr_array_sized_new (table_size); + for (i = 0; i < table_size; ++i) + g_ptr_array_add (table, NULL); + chain_lengths = g_new0 (int, table_size); + max_chain_length = 0; + for (i = 0; i < acfg->extra_methods->len; ++i) { + MonoMethod *method = g_ptr_array_index (acfg->extra_methods, i); + MonoCompile *cfg = g_hash_table_lookup (acfg->method_to_cfg, method); + guint32 key, value; + + if (!cfg) + continue; + + key = info_offsets [i]; + value = get_method_index (acfg, method); - /* Add a typedef, so we can reference the type without a 'struct' in gdb */ - emit_uleb128 (acfg, ABBREV_TYPEDEF); - emit_string (acfg, full_name); - emit_symbol_diff (acfg, die, ".Ldebug_info_start", 0); + hash = mono_aot_method_hash (method) % table_size; - g_free (full_name); - acfg->tdie_index ++; + chain_lengths [hash] ++; + max_chain_length = MAX (max_chain_length, chain_lengths [hash]); + /* FIXME: Allocate from the mempool */ + new_entry = g_new0 (HashEntry, 1); + new_entry->key = key; + new_entry->value = value; - if (emit_namespace) { - /* Namespace end */ - emit_uleb128 (acfg, 0x0); + entry = g_ptr_array_index (table, hash); + if (entry == NULL) { + new_entry->index = hash; + g_ptr_array_index (table, hash) = new_entry; + } else { + while (entry->next) + entry = entry->next; + + entry->next = new_entry; + new_entry->index = table->len; + g_ptr_array_add (table, new_entry); + } } - g_hash_table_insert (acfg->class_to_die, klass, die); - return die; -} + //printf ("MAX: %d\n", max_chain_length); -static void -emit_var_type (MonoAotCompile *acfg, MonoType *t) -{ - MonoClass *klass = mono_class_from_mono_type (t); - int j; - const char *tdie; + /* Emit the table */ + sprintf (symbol, "extra_method_table"); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - for (j = 0; j < G_N_ELEMENTS (basic_types); ++j) - if (basic_types [j].type == t->type) - break; - if (j < G_N_ELEMENTS (basic_types)) - tdie = basic_types [j].die_name; - else { - switch (t->type) { - case MONO_TYPE_CLASS: - case MONO_TYPE_ARRAY: - tdie = ".LDIE_OBJECT"; - break; - case MONO_TYPE_VALUETYPE: - if (klass->enumtype) - tdie = emit_class_dwarf_info (acfg, klass); + g_assert (table_size < 65000); + emit_int32 (acfg, table_size); + g_assert (table->len < 65000); + for (i = 0; i < table->len; ++i) { + HashEntry *entry = g_ptr_array_index (table, i); + + if (entry == NULL) { + emit_int32 (acfg, 0); + emit_int32 (acfg, 0); + emit_int32 (acfg, 0); + } else { + g_assert (entry->key > 0); + emit_int32 (acfg, entry->key); + emit_int32 (acfg, entry->value); + if (entry->next) + emit_int32 (acfg, entry->next->index); else - tdie = ".LDIE_I4"; - break; - default: - tdie = ".LDIE_I4"; - break; + emit_int32 (acfg, 0); } } - if (t->byref) - // FIXME: - tdie = ".LDIE_I4"; - emit_symbol_diff (acfg, tdie, ".Ldebug_info_start", 0); -} -static void -encode_var_location (MonoAotCompile *acfg, MonoInst *ins, guint8 *p, guint8 **endp) -{ - /* location */ - /* FIXME: This needs a location list, since the args can go from reg->stack */ - if (!ins || ins->flags & MONO_INST_IS_DEAD) { - /* gdb treats this as optimized out */ - } else if (ins->opcode == OP_REGVAR) { - *p = DW_OP_reg0 + mono_hw_reg_to_dwarf_reg (ins->dreg); - p ++; - } else if (ins->opcode == OP_REGOFFSET) { - *p ++= DW_OP_breg0 + mono_hw_reg_to_dwarf_reg (ins->inst_basereg); - encode_sleb128 (ins->inst_offset, p, &p); - } else { - // FIXME: - *p ++ = DW_OP_reg0; - } + /* + * Emit a table reverse mapping method indexes to their index in extra_method_info. + * This is used by mono_aot_find_jit_info (). + */ + sprintf (symbol, "extra_method_info_offsets"); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + + emit_int32 (acfg, acfg->extra_methods->len); + for (i = 0; i < acfg->extra_methods->len; ++i) { + MonoMethod *method = g_ptr_array_index (acfg->extra_methods, i); - *endp = p; -} + emit_int32 (acfg, get_method_index (acfg, method)); + emit_int32 (acfg, info_offsets [i]); + } +} static void -emit_loclist (MonoAotCompile *acfg, MonoInst *ins, - guint8 *loclist_begin_addr, guint8 *loclist_end_addr, - guint8 *expr, guint32 expr_len) +emit_method_order (MonoAotCompile *acfg) { - char label [128]; - - emit_section_change (acfg, ".debug_loc", 0); - sprintf (label, ".Lloclist_%d", acfg->loclist_index ++ ); - emit_label (acfg, label); + int i, index, len; + char symbol [256]; + GList *l; - emit_pointer_value (acfg, loclist_begin_addr); - emit_pointer_value (acfg, loclist_end_addr); - emit_byte (acfg, expr_len % 256); - emit_byte (acfg, expr_len / 256); - emit_bytes (acfg, expr, expr_len); + sprintf (symbol, "method_order"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - emit_pointer_value (acfg, NULL); - emit_pointer_value (acfg, NULL); + /* First emit an index table */ + index = 0; + len = 0; + for (l = acfg->method_order; l != NULL; l = l->next) { + i = GPOINTER_TO_UINT (l->data); - emit_section_change (acfg, ".debug_info", 0); - emit_symbol_diff (acfg, label, ".Ldebug_loc_start", 0); -} + if (acfg->cfgs [i]) { + if ((index % 1024) == 0) { + emit_int32 (acfg, i); + } -/* - * MonoDisHelper->tokener doesn't take an IP argument, and we can't add one since - * it is a public header. - */ -static const guint8 *token_handler_ip; + index ++; + } -static char* -token_handler (MonoDisHelper *dh, MonoMethod *method, guint32 token) -{ - char *res, *desc; + len ++; + } + emit_int32 (acfg, 0xffffff); - if (method->wrapper_type) { - gpointer data = mono_method_get_wrapper_data (method, token); + /* Then emit the whole method order */ + for (l = acfg->method_order; l != NULL; l = l->next) { + i = GPOINTER_TO_UINT (l->data); - switch (*token_handler_ip) { - case CEE_ISINST: - case CEE_LDELEMA: - res = g_strdup_printf ("<%s>", ((MonoClass*)data)->name); - break; - case CEE_NEWOBJ: - case CEE_CALL: - desc = mono_method_full_name (data, TRUE); - res = g_strdup_printf ("<%s>", desc); - g_free (desc); - break; - case CEE_CALLI: - desc = mono_signature_get_desc (data, FALSE); - res = g_strdup_printf ("<%s>", desc); - g_free (desc); - break; - default: - res = g_strdup_printf ("<%p>", data); + if (acfg->cfgs [i]) { + emit_int32 (acfg, i); } - } else { - res = g_strdup_printf ("<0x%08x>", token); - } + } + emit_line (acfg); - return res; + sprintf (symbol, "method_order_end"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_label (acfg, symbol); } -/* - * disasm_ins: - * - * Produce a disassembled form of the IL instruction at IP. This is an extension - * of mono_disasm_code_one () which can disasm tokens, handle wrapper methods, and - * CEE_MONO_ opcodes. - */ -static char* -disasm_ins (MonoMethod *method, const guchar *ip, const guint8 **endip) +static void +emit_exception_info (MonoAotCompile *acfg) { - char *dis; - MonoDisHelper dh; - MonoMethodHeader *header = mono_method_get_header (method); - - memset (&dh, 0, sizeof (dh)); - dh.newline = ""; - dh.label_format = "IL_%04x: "; - dh.label_target = "IL_%04x"; - dh.tokener = token_handler; - - token_handler_ip = ip; - if (*ip == MONO_CUSTOM_PREFIX) { - guint32 token; - gpointer data; + int i; + char symbol [256]; - switch (ip [1]) { - case CEE_MONO_ICALL: { - MonoJitICallInfo *info; + sprintf (symbol, "ex_info"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - token = read32 (ip + 2); - data = mono_method_get_wrapper_data (method, token); - info = mono_find_jit_icall_by_addr (data); - g_assert (info); + /* To reduce size of generated assembly */ + sprintf (symbol, "ex"); + emit_label (acfg, symbol); - dis = g_strdup_printf ("IL_%04x: mono_icall <%s>", (int)(ip - header->code), info->name); - ip += 6; - break; - } - case CEE_MONO_CLASSCONST: { - token = read32 (ip + 2); - data = mono_method_get_wrapper_data (method, token); + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i]) + emit_exception_debug_info (acfg, acfg->cfgs [i]); + } - dis = g_strdup_printf ("IL_%04x: mono_classconst <%s>", (int)(ip - header->code), ((MonoClass*)data)->name); - ip += 6; - break; - } - default: - dis = mono_disasm_code_one (&dh, method, ip, &ip); + sprintf (symbol, "ex_info_offsets"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + + for (i = 0; i < acfg->nmethods; ++i) { + if (acfg->cfgs [i]) { + sprintf (symbol, ".Le_%x_p", i); + emit_symbol_diff (acfg, symbol, "ex", 0); + } else { + emit_int32 (acfg, 0); } - } else { - dis = mono_disasm_code_one (&dh, method, ip, &ip); } - token_handler_ip = NULL; - - *endip = ip; - return dis; + emit_line (acfg); } -static gint32 -il_offset_from_address (MonoMethod *method, MonoDebugMethodJitInfo *jit, - guint32 native_offset) +static void +emit_unwind_info (MonoAotCompile *acfg) { int i; + char symbol [128]; - if (!jit->line_numbers) - return -1; - - for (i = jit->num_line_numbers - 1; i >= 0; i--) { - MonoDebugLineNumberEntry lne = jit->line_numbers [i]; - - if (lne.native_offset <= native_offset) - return lne.il_offset; - } - - return -1; -} - -static int max_special_addr_diff = 0; - -static inline void -emit_advance_op (MonoAotCompile *acfg, int line_diff, int addr_diff) -{ - gint64 opcode = 0; + /* + * The unwind info contains a lot of duplicates so we emit each unique + * entry once, and only store the offset from the start of the table in the + * exception info. + */ - if (line_diff == 0) - return; + sprintf (symbol, "unwind_info"); + emit_section_change (acfg, ".text", 1); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + emit_global (acfg, symbol, FALSE); - /* Use a special opcode if possible */ - if (line_diff - LINE_BASE < LINE_RANGE) { - if (max_special_addr_diff == 0) - max_special_addr_diff = (255 - OPCODE_BASE) / LINE_RANGE; + for (i = 0; i < acfg->unwind_ops->len; ++i) { + guint32 index = GPOINTER_TO_UINT (g_ptr_array_index (acfg->unwind_ops, i)); + guint8 *unwind_info; + guint32 unwind_info_len; + guint8 buf [16]; + guint8 *p; - if (addr_diff > max_special_addr_diff && (addr_diff < 2 * max_special_addr_diff)) { - emit_byte (acfg, DW_LNS_const_add_pc); - addr_diff -= max_special_addr_diff; - } + unwind_info = mono_get_cached_unwind_info (index, &unwind_info_len); - opcode = (line_diff - LINE_BASE) + (LINE_RANGE * addr_diff) + OPCODE_BASE; - if (opcode > 255) - opcode = 0; - } + p = buf; + encode_value (unwind_info_len, p, &p); + emit_bytes (acfg, buf, p - buf); + emit_bytes (acfg, unwind_info, unwind_info_len); - if (opcode != 0) { - emit_byte (acfg, opcode); - } else { - emit_byte (acfg, DW_LNS_advance_line); - emit_sleb128 (acfg, line_diff); - emit_byte (acfg, DW_LNS_advance_pc); - emit_sleb128 (acfg, addr_diff); - emit_byte (acfg, DW_LNS_copy); + acfg->stats.unwind_info_size += (p - buf) + unwind_info_len; } } static void -emit_line_number_info (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, - guint32 code_size, MonoDebugMethodJitInfo *debug_info) +emit_class_info (MonoAotCompile *acfg) { - guint32 prev_line = 0; - guint32 prev_native_offset = 0; - int i, file_index, il_offset, prev_il_offset; - gboolean first = TRUE; - MonoDebugSourceLocation *loc; - char *prev_file_name = NULL; - MonoMethodHeader *header = mono_method_get_header (method); - MonoDebugMethodInfo *minfo; - - minfo = mono_debug_lookup_method (method); - - /* FIXME: Avoid quadratic behavior */ - - prev_line = 1; - prev_il_offset = -1; - - for (i = 0; i < code_size; ++i) { - if (!minfo) - continue; + int i; + char symbol [256]; - if (!debug_info->line_numbers) - continue; + sprintf (symbol, "class_info"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - /* - * FIXME: Its hard to optimize this, since the line number info is not - * sorted by il offset or native offset - */ - il_offset = il_offset_from_address (method, debug_info, i); + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) + emit_klass_info (acfg, MONO_TOKEN_TYPE_DEF | (i + 1)); - if (il_offset < 0) - continue; + sprintf (symbol, "class_info_offsets"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - if (il_offset == prev_il_offset) - continue; + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { + sprintf (symbol, ".LK_I_%x", i); + emit_symbol_diff (acfg, symbol, "class_info", 0); + } + emit_line (acfg); +} - prev_il_offset = il_offset; +typedef struct ClassNameTableEntry { + guint32 token, index; + struct ClassNameTableEntry *next; +} ClassNameTableEntry; - loc = mono_debug_symfile_lookup_location (minfo, il_offset); +static void +emit_class_name_table (MonoAotCompile *acfg) +{ + int i, table_size; + guint32 token, hash; + MonoClass *klass; + GPtrArray *table; + char *full_name; + char symbol [256]; + ClassNameTableEntry *entry, *new_entry; - if (loc) { - int line_diff = (gint32)loc->row - (gint32)prev_line; - int addr_diff = i - prev_native_offset; + /* + * Construct a chained hash table for mapping class names to typedef tokens. + */ + table_size = g_spaced_primes_closest ((int)(acfg->image->tables [MONO_TABLE_TYPEDEF].rows * 1.5)); + table = g_ptr_array_sized_new (table_size); + for (i = 0; i < table_size; ++i) + g_ptr_array_add (table, NULL); + for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) { + token = MONO_TOKEN_TYPE_DEF | (i + 1); + klass = mono_class_get (acfg->image, token); + full_name = mono_type_get_name_full (mono_class_get_type (klass), MONO_TYPE_NAME_FORMAT_FULL_NAME); + hash = mono_aot_str_hash (full_name) % table_size; + g_free (full_name); - if (first) { - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_DATA); + /* FIXME: Allocate from the mempool */ + new_entry = g_new0 (ClassNameTableEntry, 1); + new_entry->token = token; - emit_byte (acfg, 0); - emit_byte (acfg, sizeof (gpointer) + 1); - emit_byte (acfg, DW_LNE_set_address); - emit_pointer_value (acfg, code); + entry = g_ptr_array_index (table, hash); + if (entry == NULL) { + new_entry->index = hash; + g_ptr_array_index (table, hash) = new_entry; + } else { + while (entry->next) + entry = entry->next; + + entry->next = new_entry; + new_entry->index = table->len; + g_ptr_array_add (table, new_entry); + } + } - /* - * The prolog+initlocals region does not have a line number, this - * makes them belong to the first line of the method. - */ - emit_byte (acfg, DW_LNS_advance_line); - emit_sleb128 (acfg, (gint32)loc->row - (gint32)prev_line); - prev_line = loc->row; - } + /* Emit the table */ + sprintf (symbol, "class_name_table"); + emit_section_change (acfg, ".text", 0); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - if (loc->row != prev_line) { - if (!prev_file_name || strcmp (loc->source_file, prev_file_name) != 0) { - /* Add an entry to the file table */ - /* FIXME: Avoid duplicates */ - file_index = emit_line_number_file_name (acfg, loc->source_file, 0, 0); - g_free (prev_file_name); - prev_file_name = g_strdup (loc->source_file); - - emit_byte (acfg, DW_LNS_set_file); - emit_uleb128 (acfg, file_index); - emit_byte (acfg, DW_LNS_copy); - } + /* FIXME: Optimize memory usage */ + g_assert (table_size < 65000); + emit_int16 (acfg, table_size); + g_assert (table->len < 65000); + for (i = 0; i < table->len; ++i) { + ClassNameTableEntry *entry = g_ptr_array_index (table, i); - //printf ("X: %p(+0x%x) %d %s:%d(+%d)\n", code + i, addr_diff, loc->il_offset, loc->source_file, loc->row, line_diff); + if (entry == NULL) { + emit_int16 (acfg, 0); + emit_int16 (acfg, 0); + } else { + emit_int16 (acfg, mono_metadata_token_index (entry->token)); + if (entry->next) + emit_int16 (acfg, entry->next->index); + else + emit_int16 (acfg, 0); + } + } +} - emit_advance_op (acfg, line_diff, addr_diff); +static void +emit_image_table (MonoAotCompile *acfg) +{ + int i; + char symbol [256]; - prev_line = loc->row; - prev_native_offset = i; - } + /* + * The image table is small but referenced in a lot of places. + * So we emit it at once, and reference its elements by an index. + */ - first = FALSE; - g_free (loc); - } - } + sprintf (symbol, "mono_image_table"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - g_free (prev_file_name); + emit_int32 (acfg, acfg->image_table->len); + for (i = 0; i < acfg->image_table->len; i++) { + MonoImage *image = (MonoImage*)g_ptr_array_index (acfg->image_table, i); + MonoAssemblyName *aname = &image->assembly->aname; - if (!first) { - emit_byte (acfg, DW_LNS_advance_pc); - emit_sleb128 (acfg, code_size - prev_native_offset); - emit_byte (acfg, DW_LNS_copy); + /* FIXME: Support multi-module assemblies */ + g_assert (image->assembly->image == image); - emit_byte (acfg, 0); - emit_byte (acfg, 1); - emit_byte (acfg, DW_LNE_end_sequence); - } else if (!acfg->image) { - /* No debug info, XDEBUG mode */ - char *name, *dis; - const guint8 *ip = header->code; - int prev_line, prev_native_offset; - int *il_to_line; + emit_string (acfg, image->assembly_name); + emit_string (acfg, image->guid); + emit_string (acfg, aname->culture ? aname->culture : ""); + emit_string (acfg, (const char*)aname->public_key_token); - /* - * Emit the IL code into a temporary file and emit line number info - * referencing that file. - */ + emit_alignment (acfg, 8); + emit_int32 (acfg, aname->flags); + emit_int32 (acfg, aname->major); + emit_int32 (acfg, aname->minor); + emit_int32 (acfg, aname->build); + emit_int32 (acfg, aname->revision); + } +} - name = mono_method_full_name (method, TRUE); - fprintf (acfg->il_file, "// %s\n", name); - acfg->il_file_line_index ++; - g_free (name); +static void +emit_got_info (MonoAotCompile *acfg) +{ + char symbol [256]; + int i, first_plt_got_patch, buf_size; + guint8 *p, *buf; + guint32 *got_info_offsets; - il_to_line = g_new0 (int, header->code_size); + /* Add the patches needed by the PLT to the GOT */ + acfg->plt_got_offset_base = acfg->got_offset; + first_plt_got_patch = acfg->shared_patches->len; + for (i = 1; i < acfg->plt_offset; ++i) { + MonoJumpInfo *patch_info = g_hash_table_lookup (acfg->plt_offset_to_patch, GUINT_TO_POINTER (i)); - emit_section_change (acfg, ".debug_line", LINE_SUBSECTION_DATA); - emit_byte (acfg, 0); - emit_byte (acfg, sizeof (gpointer) + 1); - emit_byte (acfg, DW_LNE_set_address); - emit_pointer_value (acfg, code); + g_ptr_array_add (acfg->shared_patches, patch_info); + } - // FIXME: Optimize this - // FIXME: Decode tokens in the IL info - while (ip < header->code + header->code_size) { - int il_offset = ip - header->code; + acfg->got_offset += acfg->plt_offset; - /* Emit IL */ - acfg->il_file_line_index ++; + /** + * FIXME: + * - optimize offsets table. + * - reduce number of exported symbols. + * - emit info for a klass only once. + * - determine when a method uses a GOT slot which is guaranteed to be already + * initialized. + * - clean up and document the code. + * - use String.Empty in class libs. + */ - dis = disasm_ins (method, ip, &ip); - fprintf (acfg->il_file, "%s\n", dis); - g_free (dis); + /* Encode info required to decode shared GOT entries */ + buf_size = acfg->shared_patches->len * 64; + p = buf = mono_mempool_alloc (acfg->mempool, buf_size); + got_info_offsets = mono_mempool_alloc (acfg->mempool, acfg->shared_patches->len * sizeof (guint32)); + acfg->plt_got_info_offsets = mono_mempool_alloc (acfg->mempool, acfg->plt_offset * sizeof (guint32)); + for (i = 0; i < acfg->shared_patches->len; ++i) { + MonoJumpInfo *ji = g_ptr_array_index (acfg->shared_patches, i); - il_to_line [il_offset] = acfg->il_file_line_index; + got_info_offsets [i] = p - buf; + /* No need to encode the patch type for non-PLT patches */ + if (i >= first_plt_got_patch) { + acfg->plt_got_info_offsets [i - first_plt_got_patch + 1] = got_info_offsets [i]; + encode_value (ji->type, p, &p); } + encode_patch (acfg, ji, p, &p); + } - /* Emit line number info */ - prev_line = 0; - prev_native_offset = 0; - for (i = 0; i < debug_info->num_line_numbers; ++i) { - MonoDebugLineNumberEntry *lne = &debug_info->line_numbers [i]; - int line; + g_assert (p - buf <= buf_size); - if (lne->il_offset >= header->code_size) - continue; - line = il_to_line [lne->il_offset]; - g_assert (line); + acfg->stats.got_info_size = p - buf; - emit_advance_op (acfg, line - prev_line, (gint32)lne->native_offset - prev_native_offset); + /* Emit got_info table */ + sprintf (symbol, "got_info"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - prev_line = line; - prev_native_offset = lne->native_offset; - } + emit_bytes (acfg, buf, p - buf); - emit_byte (acfg, DW_LNS_advance_pc); - emit_sleb128 (acfg, code_size - prev_native_offset); - emit_byte (acfg, DW_LNS_copy); + /* Emit got_info_offsets table */ + sprintf (symbol, "got_info_offsets"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - emit_byte (acfg, 0); - emit_byte (acfg, 1); - emit_byte (acfg, DW_LNE_end_sequence); + for (i = 0; i < acfg->shared_patches->len; ++i) + emit_int32 (acfg, got_info_offsets [i]); - fflush (acfg->il_file); - g_free (il_to_line); - } + acfg->stats.got_info_offsets_size = acfg->shared_patches->len * 4; } static void -emit_method_dwarf_info (MonoAotCompile *acfg, MonoCompile *cfg, MonoMethod *method, char *start_symbol, char *end_symbol, guint8 *code, guint32 code_size, MonoInst **args, MonoInst **locals, GSList *unwind_info, MonoDebugMethodJitInfo *debug_info) +emit_got (MonoAotCompile *acfg) { - char *name; - MonoMethodSignature *sig; - MonoMethodHeader *header; - char **names, **tdies, **local_tdies; - char **local_names; - int *local_indexes; - int i, num_locals; - guint8 buf [128]; - guint8 *p; + char symbol [256]; - emit_section_change (acfg, ".debug_info", 0); + /* Don't make GOT global so accesses to it don't need relocations */ + sprintf (symbol, "got"); + emit_section_change (acfg, ".bss", 0); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + if (acfg->got_offset > 0) + emit_zero_bytes (acfg, (int)(acfg->got_offset * sizeof (gpointer))); - sig = mono_method_signature (method); - header = mono_method_get_header (method); + sprintf (symbol, "mono_aot_got_addr"); + emit_section_change (acfg, ".data", 0); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + emit_pointer (acfg, "got"); +} - /* Parameter types */ - tdies = g_new0 (char *, sig->param_count + sig->hasthis); - for (i = 0; i < sig->param_count + sig->hasthis; ++i) { - MonoType *t; +static void +emit_globals (MonoAotCompile *acfg) +{ + char *opts_str; + char *build_info; - if (i == 0 && sig->hasthis) { - t = &method->klass->this_arg; - } else { - t = sig->params [i - sig->hasthis]; - } + emit_string_symbol (acfg, "mono_assembly_guid" , acfg->image->guid); - emit_class_dwarf_info (acfg, mono_class_from_mono_type (t)); - } + emit_string_symbol (acfg, "mono_aot_version", MONO_AOT_FILE_VERSION); - /* Local types */ - local_tdies = g_new0 (char *, header->num_locals); - for (i = 0; i < header->num_locals; ++i) { - emit_class_dwarf_info (acfg, mono_class_from_mono_type (header->locals [i])); - } + opts_str = g_strdup_printf ("%d", acfg->opts); + emit_string_symbol (acfg, "mono_aot_opt_flags", opts_str); + g_free (opts_str); - /* Subprogram */ - names = g_new0 (char *, sig->param_count); - mono_method_get_param_names (method, (const char **) names); + emit_string_symbol (acfg, "mono_aot_full_aot", acfg->aot_opts.full_aot ? "TRUE" : "FALSE"); - emit_uleb128 (acfg, ABBREV_SUBPROGRAM); - name = mono_method_full_name (method, FALSE); - emit_string (acfg, name); - g_free (name); - if (start_symbol) { - emit_pointer_unaligned (acfg, start_symbol); - emit_pointer_unaligned (acfg, end_symbol); + if (acfg->aot_opts.bind_to_runtime_version) { + build_info = mono_get_runtime_build_info (); + emit_string_symbol (acfg, "mono_runtime_version", build_info); + g_free (build_info); } else { - emit_pointer_value (acfg, code); - emit_pointer_value (acfg, code + code_size); + emit_string_symbol (acfg, "mono_runtime_version", ""); } - /* frame_base */ - emit_byte (acfg, 2); - emit_byte (acfg, DW_OP_breg6); - emit_byte (acfg, 16); - - /* Parameters */ - for (i = 0; i < sig->param_count + sig->hasthis; ++i) { - MonoInst *arg = args ? args [i] : NULL; - MonoType *t; - const char *pname; - char pname_buf [128]; - - if (i == 0 && sig->hasthis) { - t = &mono_defaults.object_class->byval_arg; - pname = "this"; - } else { - t = sig->params [i - sig->hasthis]; - pname = names [i - sig->hasthis]; - } - - emit_uleb128 (acfg, ABBREV_PARAM); - /* name */ - if (pname[0] == '\0') { - sprintf (pname_buf, "param%d", i - sig->hasthis); - pname = pname_buf; - } - emit_string (acfg, pname); - /* type */ - if (!arg || arg->flags & MONO_INST_IS_DEAD) - emit_var_type (acfg, &mono_defaults.int32_class->byval_arg); - else - emit_var_type (acfg, t); - p = buf; - encode_var_location (acfg, arg, p, &p); - emit_byte (acfg, p - buf); - emit_bytes (acfg, buf, p - buf); - } - g_free (names); + /* + * When static linking, we emit a global which will point to the symbol table. + */ + if (acfg->aot_opts.static_link) { + int i; + char symbol [256]; + char *p; - /* Locals */ - num_locals = mono_debug_lookup_locals (method, &local_names, &local_indexes); + /* Emit a string holding the assembly name */ + emit_string_symbol (acfg, "mono_aot_assembly_name", acfg->image->assembly->aname.name); - for (i = 0; i < header->num_locals; ++i) { - MonoInst *ins = locals [i]; - char name_buf [128]; - int j; - MonoMethodVar *vmv; - gboolean need_loclist = FALSE; - - if (code && get_vreg_to_inst (cfg, ins->dreg)) { - vmv = MONO_VARINFO (cfg, cfg->vreg_to_var_num [ins->dreg]); - if (vmv->live_range_start) { - /* This variable has a precise live range */ - need_loclist = TRUE; - } - } + /* Emit the names */ + for (i = 0; i < acfg->globals->len; ++i) { + char *name = g_ptr_array_index (acfg->globals, i); - if (need_loclist) - emit_uleb128 (acfg, ABBREV_VARIABLE_LOCLIST); - else - emit_uleb128 (acfg, ABBREV_VARIABLE); - /* name */ - for (j = 0; j < num_locals; ++j) - if (local_indexes [j] == i) - break; - if (j < num_locals) { - emit_string (acfg, local_names [j]); - } else { - sprintf (name_buf, "V_%d", i); - emit_string (acfg, name_buf); + sprintf (symbol, "name_%d", i); + emit_section_change (acfg, ".text", 1); + emit_label (acfg, symbol); + emit_string (acfg, name); } - /* type */ - if (!ins || ins->flags & MONO_INST_IS_DEAD) - emit_var_type (acfg, &mono_defaults.int32_class->byval_arg); - else - emit_var_type (acfg, header->locals [i]); - p = buf; - encode_var_location (acfg, ins, p, &p); + /* Emit the globals table */ + sprintf (symbol, "globals"); + emit_section_change (acfg, ".data", 0); + /* This is not a global, since it is accessed by the init function */ + emit_alignment (acfg, 8); + emit_label (acfg, symbol); - if (need_loclist) { - if (vmv->live_range_end == 0) - /* FIXME: Uses made in calls are not recorded */ - vmv->live_range_end = code_size; - emit_loclist (acfg, ins, code + vmv->live_range_start, code + vmv->live_range_end, buf, p - buf); - } else { - emit_byte (acfg, p - buf); - emit_bytes (acfg, buf, p - buf); - } - } + for (i = 0; i < acfg->globals->len; ++i) { + char *name = g_ptr_array_index (acfg->globals, i); - g_free (local_names); - g_free (local_indexes); + sprintf (symbol, "name_%d", i); + emit_pointer (acfg, symbol); - /* Subprogram end */ - emit_uleb128 (acfg, 0x0); + sprintf (symbol, "%s", name); + emit_pointer (acfg, symbol); + } + /* Null terminate the table */ + emit_int32 (acfg, 0); + emit_int32 (acfg, 0); - emit_line (acfg); + /* + * Emit a global symbol which can be passed by an embedding app to + * mono_aot_register_module (). + */ +#if defined(__MACH__) + sprintf (symbol, "_mono_aot_module_%s_info", acfg->image->assembly->aname.name); +#else + sprintf (symbol, "mono_aot_module_%s_info", acfg->image->assembly->aname.name); +#endif - /* Emit unwind info */ - emit_fde (acfg, acfg->fde_index, start_symbol, end_symbol, code, code_size, unwind_info, TRUE); - acfg->fde_index ++; + /* Get rid of characters which cannot occur in symbols */ + p = symbol; + for (p = symbol; *p; ++p) { + if (!(isalnum (*p) || *p == '_')) + *p = '_'; + } + acfg->static_linking_symbol = g_strdup (symbol); + emit_global_inner (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + emit_pointer (acfg, "globals"); + } +} - /* Emit line number info */ - if (code && debug_info) - emit_line_number_info (acfg, method, code, code_size, debug_info); +static void +emit_mem_end (MonoAotCompile *acfg) +{ + char symbol [128]; - emit_line (acfg); + sprintf (symbol, "mem_end"); + emit_section_change (acfg, ".text", 1); + emit_global (acfg, symbol, FALSE); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); } +/* + * Emit a structure containing all the information not stored elsewhere. + */ static void -emit_trampoline_dwarf_info (MonoAotCompile *acfg, const char *tramp_name, char *start_symbol, char *end_symbol, guint8 *code, guint32 code_size, GSList *unwind_info) +emit_file_info (MonoAotCompile *acfg) { - emit_section_change (acfg, ".debug_info", 0); - - /* Subprogram */ - emit_uleb128 (acfg, ABBREV_SUBPROGRAM); - emit_string (acfg, tramp_name); - emit_pointer_value (acfg, code); - emit_pointer_value (acfg, code + code_size); - /* frame_base */ - emit_byte (acfg, 2); - emit_byte (acfg, DW_OP_breg6); - emit_byte (acfg, 16); - - /* Subprogram end */ - emit_uleb128 (acfg, 0x0); - - /* Emit unwind info */ - emit_fde (acfg, acfg->fde_index, NULL, NULL, code, code_size, unwind_info, FALSE); - acfg->fde_index ++; + char symbol [128]; + int i; + + sprintf (symbol, "mono_aot_file_info"); + emit_section_change (acfg, ".data", 0); + emit_alignment (acfg, 8); + emit_label (acfg, symbol); + emit_global (acfg, symbol, FALSE); + + /* The data emitted here must match MonoAotFileInfo in aot-runtime.c. */ + emit_int32 (acfg, acfg->plt_got_offset_base); + emit_int32 (acfg, (int)(acfg->got_offset * sizeof (gpointer))); + emit_int32 (acfg, acfg->plt_offset); + + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, acfg->num_trampolines [i]); + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, acfg->trampoline_got_offset_base [i]); + for (i = 0; i < MONO_AOT_TRAMP_NUM; ++i) + emit_int32 (acfg, acfg->trampoline_size [i]); } static void emit_dwarf_info (MonoAotCompile *acfg) { -#if defined(USE_ELF_WRITER) && defined(__x86_64__) +#ifdef EMIT_DWARF_INFO int i; char symbol [128], symbol2 [128]; - emit_base_dwarf_info (acfg); - /* DIEs for methods */ for (i = 0; i < acfg->nmethods; ++i) { MonoCompile *cfg = acfg->cfgs [i]; @@ -6562,83 +4341,16 @@ emit_dwarf_info (MonoAotCompile *acfg) sprintf (symbol, ".Lm_%x", i); sprintf (symbol2, ".Lme_%x", i); - emit_method_dwarf_info (acfg, cfg, cfg->method, symbol, symbol2, NULL, 0, cfg->args, cfg->locals, cfg->unwind_ops, NULL); + mono_dwarf_writer_emit_method (acfg->dwarf, cfg, cfg->method, symbol, symbol2, NULL, 0, cfg->args, cfg->locals, cfg->unwind_ops, NULL); } -#endif /* ELF_WRITER */ +#endif } static void -acfg_free (MonoAotCompile *acfg) -{ - int i; - - for (i = 0; i < acfg->nmethods; ++i) - if (acfg->cfgs [i]) - g_free (acfg->cfgs [i]); - g_free (acfg->cfgs); - g_free (acfg->static_linking_symbol); - g_ptr_array_free (acfg->methods, TRUE); - g_ptr_array_free (acfg->shared_patches, TRUE); - g_ptr_array_free (acfg->image_table, TRUE); - g_ptr_array_free (acfg->globals, TRUE); - g_hash_table_destroy (acfg->method_indexes); - g_hash_table_destroy (acfg->plt_offset_to_patch); - g_hash_table_destroy (acfg->patch_to_plt_offset); - g_hash_table_destroy (acfg->patch_to_shared_got_offset); - g_hash_table_destroy (acfg->method_to_cfg); - g_hash_table_destroy (acfg->token_info_hash); - g_hash_table_destroy (acfg->image_hash); - mono_mempool_destroy (acfg->mempool); - g_free (acfg); -} - -int -mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) -{ - MonoImage *image = ass->image; - char symbol [256]; - int i, res, methods_len; - MonoAotCompile *acfg; - TV_DECLARE (atv); - TV_DECLARE (btv); - - printf ("Mono Ahead of Time compiler - compiling assembly %s\n", image->name); - - acfg = g_new0 (MonoAotCompile, 1); - acfg->methods = g_ptr_array_new (); - acfg->method_indexes = g_hash_table_new (NULL, NULL); - acfg->plt_offset_to_patch = g_hash_table_new (NULL, NULL); - acfg->patch_to_plt_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); - acfg->patch_to_shared_got_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); - acfg->shared_patches = g_ptr_array_new (); - acfg->method_to_cfg = g_hash_table_new (NULL, NULL); - acfg->token_info_hash = g_hash_table_new_full (NULL, NULL, NULL, g_free); - acfg->image_hash = g_hash_table_new (NULL, NULL); - acfg->image_table = g_ptr_array_new (); - acfg->globals = g_ptr_array_new (); - acfg->image = image; - acfg->opts = opts; - acfg->mempool = mono_mempool_new (); - acfg->extra_methods = g_ptr_array_new (); - InitializeCriticalSection (&acfg->mutex); - - memset (&acfg->aot_opts, 0, sizeof (acfg->aot_opts)); - acfg->aot_opts.write_symbols = TRUE; - - mono_aot_parse_options (aot_options, &acfg->aot_opts); - -#ifdef USE_BIN_WRITER - if (!acfg->aot_opts.asm_only && !acfg->aot_opts.asm_writer) - acfg->use_bin_writer = TRUE; -#endif - - load_profile_files (acfg); - - emit_start (acfg); - - acfg->num_aot_trampolines = acfg->aot_opts.full_aot ? 10240 : 0; - - acfg->method_index = 1; +collect_methods (MonoAotCompile *acfg) +{ + int i; + MonoImage *image = acfg->image; /* Collect methods */ for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i) { @@ -6647,6 +4359,11 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) method = mono_get_method (acfg->image, token, NULL); + if (!method) { + printf ("Failed to load method 0x%x from '%s'.\n", token, image->name); + exit (1); + } + /* Load all methods eagerly to skip the slower lazy loading code */ mono_class_setup_methods (method->klass); @@ -6666,15 +4383,12 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) if (acfg->aot_opts.full_aot) add_wrappers (acfg); +} - acfg->cfgs_size = acfg->methods->len + 32; - acfg->cfgs = g_new0 (MonoCompile*, acfg->cfgs_size); - - /* PLT offset 0 is reserved for the PLT trampoline */ - acfg->plt_offset = 1; - - /* Compile methods */ - TV_GETTIME (atv); +static void +compile_methods (MonoAotCompile *acfg) +{ + int i, methods_len; if (acfg->aot_opts.nthreads > 0) { GPtrArray *frag; @@ -6713,7 +4427,7 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) user_data [1] = acfg; user_data [2] = frag; - handle = CreateThread (NULL, 0, (gpointer)compile_thread_main, user_data, 0, NULL); + handle = mono_create_thread (NULL, 0, (gpointer)compile_thread_main, user_data, 0, NULL); g_ptr_array_add (threads, handle); } g_free (methods); @@ -6730,15 +4444,275 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) /* This can new methods to acfg->methods */ compile_method (acfg, g_ptr_array_index (acfg->methods, i)); } +} + +static int +compile_asm (MonoAotCompile *acfg) +{ + char *command, *objfile; + char *outfile_name, *tmp_outfile_name; + +#if defined(TARGET_AMD64) +#define AS_OPTIONS "--64" +#elif defined(sparc) && SIZEOF_VOID_P == 8 +#define AS_OPTIONS "-xarch=v9" +#else +#define AS_OPTIONS "" +#endif + + if (acfg->aot_opts.asm_only) { + printf ("Output file: '%s'.\n", acfg->tmpfname); + if (acfg->aot_opts.static_link) + printf ("Linking symbol: '%s'.\n", acfg->static_linking_symbol); + return 0; + } + + if (acfg->aot_opts.static_link) { + if (acfg->aot_opts.outfile) + objfile = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + objfile = g_strdup_printf ("%s.o", acfg->image->name); + } else { + objfile = g_strdup_printf ("%s.o", acfg->tmpfname); + } + command = g_strdup_printf ("as %s %s -o %s", AS_OPTIONS, acfg->tmpfname, objfile); + printf ("Executing the native assembler: %s\n", command); + if (system (command) != 0) { + g_free (command); + g_free (objfile); + return 1; + } + + g_free (command); + + if (acfg->aot_opts.static_link) { + printf ("Output file: '%s'.\n", objfile); + printf ("Linking symbol: '%s'.\n", acfg->static_linking_symbol); + g_free (objfile); + return 0; + } + + if (acfg->aot_opts.outfile) + outfile_name = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + outfile_name = g_strdup_printf ("%s%s", acfg->image->name, SHARED_EXT); + + tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name); + +#if defined(sparc) + command = g_strdup_printf ("ld -shared -G -o %s %s.o", tmp_outfile_name, acfg->tmpfname); +#elif defined(__ppc__) && defined(__MACH__) + command = g_strdup_printf ("gcc -dynamiclib -o %s %s.o", tmp_outfile_name, acfg->tmpfname); +#elif defined(PLATFORM_WIN32) + command = g_strdup_printf ("gcc -shared --dll -mno-cygwin -o %s %s.o", tmp_outfile_name, acfg->tmpfname); +#else + command = g_strdup_printf ("ld -shared -o %s %s.o", tmp_outfile_name, acfg->tmpfname); +#endif + printf ("Executing the native linker: %s\n", command); + if (system (command) != 0) { + g_free (tmp_outfile_name); + g_free (outfile_name); + g_free (command); + g_free (objfile); + return 1; + } + + g_free (command); + unlink (objfile); + /*com = g_strdup_printf ("strip --strip-unneeded %s%s", acfg->image->name, SHARED_EXT); + printf ("Stripping the binary: %s\n", com); + system (com); + g_free (com);*/ + +#if defined(TARGET_ARM) && !defined(__MACH__) + /* + * gas generates 'mapping symbols' each time code and data is mixed, which + * happens a lot in emit_and_reloc_code (), so we need to get rid of them. + */ + command = g_strdup_printf ("strip --strip-symbol=\\$a --strip-symbol=\\$d %s", tmp_outfile_name); + printf ("Stripping the binary: %s\n", command); + if (system (command) != 0) { + g_free (tmp_outfile_name); + g_free (outfile_name); + g_free (command); + g_free (objfile); + return 1; + } +#endif + + rename (tmp_outfile_name, outfile_name); + + g_free (tmp_outfile_name); + g_free (outfile_name); + g_free (objfile); + + if (acfg->aot_opts.save_temps) + printf ("Retained input file.\n"); + else + unlink (acfg->tmpfname); + + return 0; +} + +static MonoAotCompile* +acfg_create (MonoAssembly *ass, guint32 opts) +{ + MonoImage *image = ass->image; + MonoAotCompile *acfg; + + acfg = g_new0 (MonoAotCompile, 1); + acfg->methods = g_ptr_array_new (); + acfg->method_indexes = g_hash_table_new (NULL, NULL); + acfg->plt_offset_to_patch = g_hash_table_new (NULL, NULL); + acfg->patch_to_plt_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); + acfg->patch_to_shared_got_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal); + acfg->shared_patches = g_ptr_array_new (); + acfg->method_to_cfg = g_hash_table_new (NULL, NULL); + acfg->token_info_hash = g_hash_table_new_full (NULL, NULL, NULL, g_free); + acfg->image_hash = g_hash_table_new (NULL, NULL); + acfg->image_table = g_ptr_array_new (); + acfg->globals = g_ptr_array_new (); + acfg->image = image; + acfg->opts = opts; + acfg->mempool = mono_mempool_new (); + acfg->extra_methods = g_ptr_array_new (); + acfg->unwind_info_offsets = g_hash_table_new (NULL, NULL); + acfg->unwind_ops = g_ptr_array_new (); + InitializeCriticalSection (&acfg->mutex); + + return acfg; +} + +static void +acfg_free (MonoAotCompile *acfg) +{ + int i; + + img_writer_destroy (acfg->w); + for (i = 0; i < acfg->nmethods; ++i) + if (acfg->cfgs [i]) + g_free (acfg->cfgs [i]); + g_free (acfg->cfgs); + g_free (acfg->static_linking_symbol); + g_ptr_array_free (acfg->methods, TRUE); + g_ptr_array_free (acfg->shared_patches, TRUE); + g_ptr_array_free (acfg->image_table, TRUE); + g_ptr_array_free (acfg->globals, TRUE); + g_ptr_array_free (acfg->unwind_ops, TRUE); + g_hash_table_destroy (acfg->method_indexes); + g_hash_table_destroy (acfg->plt_offset_to_patch); + g_hash_table_destroy (acfg->patch_to_plt_offset); + g_hash_table_destroy (acfg->patch_to_shared_got_offset); + g_hash_table_destroy (acfg->method_to_cfg); + g_hash_table_destroy (acfg->token_info_hash); + g_hash_table_destroy (acfg->image_hash); + g_hash_table_destroy (acfg->unwind_info_offsets); + mono_mempool_destroy (acfg->mempool); + g_free (acfg); +} + +int +mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) +{ + MonoImage *image = ass->image; + int res; + MonoAotCompile *acfg; + char *outfile_name, *tmp_outfile_name; + TV_DECLARE (atv); + TV_DECLARE (btv); + + printf ("Mono Ahead of Time compiler - compiling assembly %s\n", image->name); + + acfg = acfg_create (ass, opts); + + memset (&acfg->aot_opts, 0, sizeof (acfg->aot_opts)); + acfg->aot_opts.write_symbols = TRUE; + acfg->aot_opts.ntrampolines = 10240; + + mono_aot_parse_options (aot_options, &acfg->aot_opts); + + //acfg->aot_opts.print_skipped_methods = TRUE; + +#ifndef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES + if (acfg->aot_opts.full_aot) { + printf ("--aot=full is not supported on this platform.\n"); + return 1; + } +#endif + + if (acfg->aot_opts.static_link) + acfg->aot_opts.asm_writer = TRUE; + + if (!acfg->aot_opts.asm_only && !acfg->aot_opts.asm_writer && bin_writer_supported ()) { + if (acfg->aot_opts.outfile) + outfile_name = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + outfile_name = g_strdup_printf ("%s%s", acfg->image->name, SHARED_EXT); + + tmp_outfile_name = g_strdup_printf ("%s.tmp", outfile_name); + + acfg->fp = fopen (tmp_outfile_name, "w"); + g_assert (acfg->fp); + + acfg->w = img_writer_create (acfg->fp, TRUE); + acfg->use_bin_writer = TRUE; + } else { + if (acfg->aot_opts.asm_only) { + if (acfg->aot_opts.outfile) + acfg->tmpfname = g_strdup_printf ("%s", acfg->aot_opts.outfile); + else + acfg->tmpfname = g_strdup_printf ("%s.s", acfg->image->name); + acfg->fp = fopen (acfg->tmpfname, "w+"); + } else { + int i = g_file_open_tmp ("mono_aot_XXXXXX", &acfg->tmpfname, NULL); + acfg->fp = fdopen (i, "w+"); + } + g_assert (acfg->fp); + + acfg->w = img_writer_create (acfg->fp, FALSE); + + tmp_outfile_name = NULL; + outfile_name = NULL; + } + + load_profile_files (acfg); + + if (!acfg->aot_opts.nodebug) + acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL); + + acfg->num_trampolines [MONO_AOT_TRAMP_SPECIFIC] = acfg->aot_opts.full_aot ? acfg->aot_opts.ntrampolines : 0; +#ifdef MONO_ARCH_HAVE_STATIC_RGCTX_TRAMPOLINE + acfg->num_trampolines [MONO_AOT_TRAMP_STATIC_RGCTX] = acfg->aot_opts.full_aot ? 1024 : 0; +#endif + acfg->num_trampolines [MONO_AOT_TRAMP_IMT_THUNK] = acfg->aot_opts.full_aot ? 128 : 0; + + acfg->method_index = 1; + + collect_methods (acfg); + + acfg->cfgs_size = acfg->methods->len + 32; + acfg->cfgs = g_new0 (MonoCompile*, acfg->cfgs_size); + + /* PLT offset 0 is reserved for the PLT trampoline */ + acfg->plt_offset = 1; + + TV_GETTIME (atv); + + compile_methods (acfg); TV_GETTIME (btv); - + acfg->stats.jit_time = TV_ELAPSED (atv, btv); TV_GETTIME (atv); alloc_got_slots (acfg); + img_writer_emit_start (acfg->w); + + if (acfg->dwarf) + mono_dwarf_writer_emit_base_info (acfg->dwarf, arch_get_cie_program ()); + emit_code (acfg); emit_info (acfg); @@ -6755,6 +4729,8 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) emit_exception_info (acfg); + emit_unwind_info (acfg); + emit_class_info (acfg); emit_plt (acfg); @@ -6767,26 +4743,32 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) emit_globals (acfg); - emit_dwarf_info (acfg); + if (acfg->dwarf) + emit_dwarf_info (acfg); - sprintf (symbol, "mem_end"); - emit_section_change (acfg, ".text", 1); - emit_global (acfg, symbol, FALSE); - emit_alignment (acfg, 8); - emit_label (acfg, symbol); + emit_mem_end (acfg); TV_GETTIME (btv); acfg->stats.gen_time = TV_ELAPSED (atv, btv); - printf ("Code: %d Info: %d Ex Info: %d Class Info: %d PLT: %d GOT Info: %d GOT Info Offsets: %d GOT: %d\n", acfg->stats.code_size, acfg->stats.info_size, acfg->stats.ex_info_size, acfg->stats.class_info_size, acfg->plt_offset, acfg->stats.got_info_size, acfg->stats.got_info_offsets_size, (int)(acfg->got_offset * sizeof (gpointer))); + printf ("Code: %d Info: %d Ex Info: %d Unwind Info: %d Class Info: %d PLT: %d GOT Info: %d GOT Info Offsets: %d GOT: %d\n", acfg->stats.code_size, acfg->stats.info_size, acfg->stats.ex_info_size, acfg->stats.unwind_info_size, acfg->stats.class_info_size, acfg->plt_offset, acfg->stats.got_info_size, acfg->stats.got_info_offsets_size, (int)(acfg->got_offset * sizeof (gpointer))); TV_GETTIME (atv); - res = emit_writeout (acfg); + res = img_writer_emit_writeout (acfg->w); if (res != 0) { acfg_free (acfg); return res; } + if (acfg->use_bin_writer) { + rename (tmp_outfile_name, outfile_name); + } else { + res = compile_asm (acfg); + if (res != 0) { + acfg_free (acfg); + return res; + } + } TV_GETTIME (btv); acfg->stats.link_time = TV_ELAPSED (atv, btv); @@ -6801,12 +4783,15 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) printf ("%d methods have other problems (%d%%)\n", acfg->stats.ocount, acfg->stats.mcount ? (acfg->stats.ocount * 100) / acfg->stats.mcount : 100); printf ("Methods without GOT slots: %d (%d%%)\n", acfg->stats.methods_without_got_slots, acfg->stats.mcount ? (acfg->stats.methods_without_got_slots * 100) / acfg->stats.mcount : 100); printf ("Direct calls: %d (%d%%)\n", acfg->stats.direct_calls, acfg->stats.all_calls ? (acfg->stats.direct_calls * 100) / acfg->stats.all_calls : 100); - printf ("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); + /* printf ("GOT slot distribution:\n"); for (i = 0; i < MONO_PATCH_INFO_NONE; ++i) if (acfg->stats.got_slot_types [i]) printf ("\t%s: %d\n", get_patch_name (i), acfg->stats.got_slot_types [i]); + */ + + printf ("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); acfg_free (acfg); @@ -6838,33 +4823,32 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options) end */ -static MonoAotCompile *xdebug_acfg; +static MonoDwarfWriter *xdebug_writer; +static FILE *xdebug_fp; void mono_xdebug_init (void) { - MonoAotCompile *acfg; - - acfg = g_new0 (MonoAotCompile, 1); - acfg->mempool = mono_mempool_new (); - InitializeCriticalSection (&acfg->mutex); - acfg->aot_opts.asm_only = TRUE; - acfg->aot_opts.outfile = g_strdup ("xdb.s"); + FILE *il_file; + MonoImageWriter *w; unlink ("xdb.s"); + xdebug_fp = fopen ("xdb.s", "w"); - emit_start (acfg); + w = img_writer_create (xdebug_fp, FALSE); - xdebug_acfg = acfg; + img_writer_emit_start (w); - /* Emit something so the file has a text segment */ - emit_section_change (acfg, ".text", 0); - emit_string (acfg, ""); + /* This file will contain the IL code for methods which don't have debug info */ + il_file = fopen ("xdb.il", "w"); - emit_base_dwarf_info (acfg); + xdebug_writer = mono_dwarf_writer_create (w, il_file); - /* This file will contain the IL code for methods which don't have debug info */ - acfg->il_file = fopen ("xdb.il", "w"); + /* Emit something so the file has a text segment */ + img_writer_emit_section_change (w, ".text", 0); + img_writer_emit_string (w, ""); + + mono_dwarf_writer_emit_base_info (xdebug_writer, arch_get_cie_program ()); } /* @@ -6872,42 +4856,36 @@ mono_xdebug_init (void) * * Emit debugging info for METHOD into an assembly file which can be assembled * and loaded into gdb to provide debugging info for JITted code. + * LOCKING: Acquires the loader lock. */ void mono_save_xdebug_info (MonoCompile *cfg) { - MonoAotCompile *acfg; - - if (!xdebug_acfg) + if (!xdebug_writer) return; - - acfg = xdebug_acfg; - mono_acfg_lock (acfg); - emit_method_dwarf_info (acfg, cfg, cfg->jit_info->method, NULL, NULL, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->method, mono_domain_get ())); - fflush (acfg->fp); - mono_acfg_unlock (acfg); + mono_loader_lock (); + mono_dwarf_writer_emit_method (xdebug_writer, cfg, cfg->jit_info->method, NULL, NULL, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->method, mono_domain_get ())); + fflush (xdebug_fp); + mono_loader_unlock (); } /* * mono_save_trampoline_xdebug_info: * * Same as mono_save_xdebug_info, but for trampolines. + * LOCKING: Acquires the loader lock. */ void mono_save_trampoline_xdebug_info (const char *tramp_name, guint8 *code, guint32 code_size, GSList *unwind_info) { - MonoAotCompile *acfg; - - if (!xdebug_acfg) + if (!xdebug_writer) return; - acfg = xdebug_acfg; - - mono_acfg_lock (acfg); - emit_trampoline_dwarf_info (acfg, tramp_name, NULL, NULL, code, code_size, unwind_info); - fflush (acfg->fp); - mono_acfg_unlock (acfg); + mono_loader_lock (); + mono_dwarf_writer_emit_trampoline (xdebug_writer, tramp_name, NULL, NULL, code, code_size, unwind_info); + fflush (xdebug_fp); + mono_loader_unlock (); } #else