* (C) 2002 Ximian, Inc.
*/
+/* Remaining AOT-only work:
+ * - reduce the length of the wrapper names.
+ * - during loading, collect the names into a hashtable to avoid linear
+ * searches.
+ * - aot IMT tables, so we don't have two kinds of aot code.
+ * - optimize the trampolines, generate more code in the arch files.
+ * - make things more consistent with how elf works, for example, use ELF
+ * relocations.
+ */
+
#include "config.h"
#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
+#endif
#include <fcntl.h>
#include <string.h>
#ifndef PLATFORM_WIN32
#include <mono/metadata/assembly.h>
#include <mono/metadata/metadata-internals.h>
#include <mono/metadata/marshal.h>
+#include <mono/metadata/gc-internal.h>
+#include <mono/metadata/method-builder.h>
#include <mono/utils/mono-logger.h>
#include "mono/utils/mono-compiler.h"
#include "mini.h"
+#include "version.h"
#ifndef DISABLE_AOT
#define SHARED_EXT ".so"
#endif
-#if defined(sparc) || defined(__ppc__)
+#if defined(sparc) || defined(__ppc__) || 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_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1)))
#define ROUND_DOWN(VALUE,SIZE) ((VALUE) & ~((SIZE) - 1))
gboolean save_temps;
gboolean write_symbols;
gboolean metadata_only;
+ gboolean bind_to_runtime_version;
+ gboolean full_aot;
+ gboolean no_dlsym;
+ gboolean static_link;
} MonoAotOptions;
typedef struct MonoAotStats {
- int ccount, mcount, lmfcount, abscount, wrappercount, gcount, ocount;
+ int ccount, mcount, lmfcount, abscount, wrappercount, gcount, ocount, genericcount;
int code_size, info_size, ex_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;
typedef struct MonoAotCompile {
MonoImage *image;
+ GPtrArray *methods;
+ GHashTable *method_indexes;
MonoCompile **cfgs;
GHashTable *patch_to_plt_offset;
GHashTable *plt_offset_to_patch;
GPtrArray *shared_patches;
GHashTable *image_hash;
GHashTable *method_to_cfg;
+ GHashTable *token_info_hash;
GPtrArray *image_table;
+ GPtrArray *globals;
GList *method_order;
+ /* Number of trampolines emitted into the AOT file */
+ guint32 num_aot_trampolines;
guint32 got_offset, plt_offset;
+ /* Number of GOT entries reserved for trampolines */
+ guint32 num_trampoline_got_entries;
guint32 *method_got_offsets;
- gboolean *has_got_slots;
MonoAotOptions aot_opts;
guint32 nmethods;
guint32 opts;
MonoMemPool *mempool;
MonoAotStats stats;
+ int method_index;
+ char *static_linking_symbol;
#ifdef USE_BIN_WRITER
BinSymbol *symbols;
BinSection *sections;
#endif
} MonoAotCompile;
-/* Keep in synch with MonoJumpInfoType */
-static const char* patch_types [] = {
- "bb",
- "abs",
- "label",
- "method",
- "method_jump",
- "method_rel",
- "methodconst",
- "internal_method",
- "switch",
- "exc",
- "exc_name",
- "class",
- "image",
- "field",
- "vtable",
- "class_init",
- "sflda",
- "ldstr",
- "ldtoken",
- "type_from_handle",
- "r4",
- "r8",
- "ip",
- "iid",
- "adjusted_iid",
- "bb_ovf",
- "exc_ovf",
- "wrapper",
- "got_offset",
- "declsec",
- "none"
+#ifdef HAVE_ARRAY_ELEM_INIT
+#define MSGSTRFIELD(line) MSGSTRFIELD1(line)
+#define MSGSTRFIELD1(line) str##line
+static const struct msgstr_t {
+#define PATCH_INFO(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)];
+#include "patch-info.h"
+#undef PATCH_INFO
+} opstr = {
+#define PATCH_INFO(a,b) b,
+#include "patch-info.h"
+#undef PATCH_INFO
+};
+static const gint16 opidx [] = {
+#define PATCH_INFO(a,b) [MONO_PATCH_INFO_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
+#include "patch-info.h"
+#undef PATCH_INFO
+};
+
+static const char*
+get_patch_name (int info)
+{
+ return (const char*)&opstr + opidx [info];
+}
+
+#else
+#define PATCH_INFO(a,b) b,
+static const char* const
+patch_types [MONO_PATCH_INFO_NUM + 1] = {
+#include "patch-info.h"
+ NULL
};
+static const char*
+get_patch_name (int info)
+{
+ return patch_types [info];
+}
+
+#endif
+
+static void
+emit_global (MonoAotCompile *acfg, const char *name, gboolean func);
+
static gboolean
is_got_patch (MonoJumpInfoType patch_type)
{
-#ifdef __x86_64__
return TRUE;
-#elif defined(__i386__)
- return TRUE;
-#else
- return FALSE;
-#endif
}
-#if defined(__ppc__) && defined(__MACH__)
-static int
+static G_GNUC_UNUSED int
ilog2(register int value)
{
int count = -1;
while (value) count++, value >>= 1;
return count;
}
-#endif
#ifdef USE_BIN_WRITER
}
static void
-emit_global (MonoAotCompile *acfg, const char *name, gboolean func)
+emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func)
{
BinSymbol *symbol = g_new0 (BinSymbol, 1);
symbol->name = g_strdup (name);
emit_pointer (MonoAotCompile *acfg, const char *target)
{
BinReloc *reloc;
+
+ if (!target)
+ // FIXME:
+ g_assert_not_reached ();
emit_alignment (acfg, sizeof (gpointer));
reloc = g_new0 (BinReloc, 1);
reloc->val1 = g_strdup (target);
return rr;
}
-static void
+static int
emit_writeout (MonoAotCompile *acfg)
{
char *outfile_name, *tmp_outfile_name;
g_free (tmp_outfile_name);
g_free (outfile_name);
+
+ return 0;
}
#endif /* USE_ELF_WRITER */
emit_unset_mode (acfg);
#if defined(PLATFORM_WIN32)
fprintf (acfg->fp, ".section %s\n", section_name);
-#elif defined(sparc)
- /* For solaris as, GNU as should accept the same */
- fprintf (acfg->fp, ".section \"%s\"\n", section_name);
-#elif defined(__ppc__) && defined(__MACH__)
+#elif defined(__MACH__)
/* This needs to be made more precise on mach. */
fprintf (acfg->fp, "%s\n", subsection_index == 0 ? ".text" : ".data");
+#elif defined(sparc) || defined(__arm__)
+ /* For solaris as, GNU as should accept the same */
+ fprintf (acfg->fp, ".section \"%s\"\n", section_name);
#else
fprintf (acfg->fp, "%s %d\n", section_name, subsection_index);
#endif
stype = "object";
emit_unset_mode (acfg);
-#if defined(sparc)
+#if defined(__MACH__)
+
+#elif defined(sparc) || defined(__arm__)
fprintf (acfg->fp, "\t.type %s,#%s\n", name, stype);
#elif defined(PLATFORM_WIN32)
-#elif !(defined(__ppc__) && defined(__MACH__))
- fprintf (acfg->fp, "\t.type %s,@%s\n", name, stype);
#elif defined(__x86_64__) || defined(__i386__)
fprintf (acfg->fp, "\t.type %s,@%s\n", name, stype);
+#else
+ fprintf (acfg->fp, "\t.type %s,@%s\n", name, stype);
#endif
}
static void
-emit_global (MonoAotCompile *acfg, const char *name, gboolean func)
+emit_global_inner (MonoAotCompile *acfg, const char *name, gboolean func)
{
emit_unset_mode (acfg);
#if (defined(__ppc__) && defined(__MACH__)) || defined(PLATFORM_WIN32)
emit_alignment (MonoAotCompile *acfg, int size)
{
emit_unset_mode (acfg);
-#if defined(__ppc__) && defined(__MACH__)
+#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__)
emit_unset_mode (acfg);
emit_alignment (acfg, sizeof (gpointer));
#if defined(__x86_64__)
- fprintf (acfg->fp, "\t.quad %s\n", target);
+ 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);
+ fprintf (acfg->fp, "\t.xword %s\n", target ? target : "0");
#else
- fprintf (acfg->fp, "\t.long %s\n", target);
+ fprintf (acfg->fp, "\t.long %s\n", target ? target : "0");
#endif
}
if ((acfg->col_count % 32) == 0)
fprintf (acfg->fp, "\n\t.byte ");
else
- fprintf (acfg->fp, ", ");
+ fprintf (acfg->fp, ",");
fprintf (acfg->fp, "0x%x", buf [i]);
}
}
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);
if ((acfg->col_count++ % 8) == 0)
fprintf (acfg->fp, "\n\t.long ");
else
- fprintf (acfg->fp, ", ");
+ fprintf (acfg->fp, ",");
fprintf (acfg->fp, "%d", value);
}
if ((acfg->col_count++ % 8) == 0)
fprintf (acfg->fp, "\n\t.long ");
else
- fprintf (acfg->fp, ", ");
- if (offset)
- fprintf (acfg->fp, "%s - %s %c %d", end, start, offset < 0? ' ': '+', offset);
+ 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);
}
emit_zero_bytes (MonoAotCompile *acfg, int num)
{
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
}
-static void
+static int
emit_writeout (MonoAotCompile *acfg)
{
char *command, *objfile;
#else
#define AS_OPTIONS ""
#endif
- command = g_strdup_printf ("as %s %s -o %s.o", AS_OPTIONS, acfg->tmpfname, acfg->tmpfname);
+
+ 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);
- return;
+ 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
#elif defined(PLATFORM_WIN32)
command = g_strdup_printf ("gcc -shared --dll -mno-cygwin -o %s %s.o", outfile_name, acfg->tmpfname);
#else
- command = g_strdup_printf ("ld -shared -o %s %s.o", outfile_name, acfg->tmpfname);
+ if (acfg->aot_opts.no_dlsym) {
+ /*
+ * Need to link using gcc so our ctor function gets called.
+ */
+ 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);
- return;
+ g_free (objfile);
+ return 1;
}
g_free (command);
- objfile = g_strdup_printf ("%s.o", acfg->tmpfname);
unlink (objfile);
- g_free (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 (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;
}
#endif /* ASM_WRITER */
+static void
+emit_global (MonoAotCompile *acfg, const char *name, gboolean func)
+{
+ if (acfg->aot_opts.no_dlsym) {
+ g_ptr_array_add (acfg->globals, g_strdup (name));
+ } else {
+ emit_global_inner (acfg, name, func);
+ }
+}
+
static void
emit_byte (MonoAotCompile *acfg, guint8 val)
{
}
}
+static guint32
+find_typespec_for_class (MonoAotCompile *acfg, MonoClass *klass)
+{
+ 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;
+ }
+
+ if (i < acfg->image->tables [MONO_TABLE_TYPESPEC].rows)
+ return MONO_TOKEN_TYPE_SPEC | (i + 1);
+ else
+ return 0;
+}
+
+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_info (MonoAotCompile *cfg, MonoClass *klass, guint8 *buf, guint8 **endbuf)
+encode_klass_ref (MonoAotCompile *acfg, MonoClass *klass, guint8 *buf, guint8 **endbuf)
{
- if (!klass->type_token) {
+ 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;
+
+ /* 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;
+
+ /* Marker */
+ encode_value (MONO_TOKEN_TYPE_SPEC, p, &p);
+ encode_value (klass->byval_arg.type, p, &p);
+
+ 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);
- g_assert (klass->element_class->type_token);
- encode_value (MONO_TOKEN_TYPE_DEF, buf, &buf);
- encode_value (get_image_index (cfg, klass->image), buf, &buf);
- g_assert (mono_metadata_token_code (klass->element_class->type_token) == MONO_TOKEN_TYPE_DEF);
- encode_value (klass->element_class->type_token - MONO_TOKEN_TYPE_DEF, buf, &buf);
- encode_value (klass->rank, buf, &buf);
- }
- else {
- g_assert (mono_metadata_token_code (klass->type_token) == MONO_TOKEN_TYPE_DEF);
- encode_value (klass->type_token - MONO_TOKEN_TYPE_DEF, buf, &buf);
- encode_value (get_image_index (cfg, klass->image), buf, &buf);
+ 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 = buf;
+ *endbuf = p;
}
static void
encode_field_info (MonoAotCompile *cfg, MonoClassField *field, guint8 *buf, guint8 **endbuf)
{
guint32 token = mono_get_field_token (field);
+ guint8 *p = buf;
- encode_klass_info (cfg, field->parent, buf, &buf);
+ 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, buf, &buf);
- *endbuf = buf;
+ encode_value (token - MONO_TOKEN_FIELD_DEF, p, &p);
+ *endbuf = p;
+}
+
+#if 0
+static guint32
+find_methodspec_for_method (MonoAotCompile *acfg, MonoMethod *method)
+{
+ int i;
+ MonoMethod *m = NULL;
+
+ /* FIXME: Search referenced images as well */
+ for (i = 0; i < acfg->image->tables [MONO_TABLE_METHODSPEC].rows; ++i) {
+ /* Since we don't compile generic methods, the context is empty */
+ m = mono_get_method_full (acfg->image, MONO_TOKEN_METHOD_SPEC | (i + 1), NULL, NULL);
+ if (m == method)
+ break;
+ }
+
+ g_assert (i < acfg->image->tables [MONO_TABLE_METHODSPEC].rows);
+
+ return MONO_TOKEN_METHOD_SPEC | (i + 1);
}
+#endif
+
+#define MAX_IMAGE_INDEX 250
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;
- g_assert (image_index < 256);
- g_assert (mono_metadata_token_table (token) == MONO_TABLE_METHOD);
+ MonoJumpInfoToken *ji;
+ guint8 *p = buf;
+
+ g_assert (image_index < MAX_IMAGE_INDEX);
+
+ if (mono_method_signature (method)->is_inflated) {
+ /*
+ * This is a generic method, find the original token which referenced it and
+ * encode that.
+ */
+ /* This doesn't work for some reason */
+ /*
+ image_index = get_image_index (acfg, acfg->image);
+ g_assert (image_index < 255);
+ token = find_methodspec_for_method (acfg, method);
+ */
+ /* Obtain the token from information recorded by the JIT */
+ ji = g_hash_table_lookup (acfg->token_info_hash, method);
+ if (!ji) {
+ MonoMethod *declaring;
+
+ g_assert (method->is_inflated);
+ declaring = ((MonoMethodInflated*)method)->declaring;
+
+ /*
+ * 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.
+ */
- encode_value ((image_index << 24) + (mono_metadata_token_index (token)), buf, &buf);
- *endbuf = buf;
+ /* 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);
+ } else {
+ 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 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);
+ }
+ *endbuf = p;
}
static gint
return 0;
}
-static int
-get_plt_index (MonoAotCompile *acfg, MonoJumpInfo *patch_info)
+/*
+ * 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)
{
- int res = -1;
- int idx;
-
switch (patch_info->type) {
case MONO_PATCH_INFO_METHOD:
case MONO_PATCH_INFO_WRAPPER:
case MONO_PATCH_INFO_INTERNAL_METHOD:
- case MONO_PATCH_INFO_CLASS_INIT: {
- MonoJumpInfo *new_ji = mono_patch_info_dup_mp (acfg->mempool, patch_info);
- gpointer patch_id = NULL;
+ case MONO_PATCH_INFO_JIT_ICALL_ADDR:
+ case MONO_PATCH_INFO_CLASS_INIT:
+ case MONO_PATCH_INFO_RGCTX_FETCH:
+ case MONO_PATCH_INFO_GENERIC_CLASS_INIT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
- /* First check for an existing patch */
- switch (patch_info->type) {
- case MONO_PATCH_INFO_METHOD:
- patch_id = patch_info->data.method;
- break;
- case MONO_PATCH_INFO_INTERNAL_METHOD:
- patch_id = (gpointer)patch_info->data.name;
- break;
- case MONO_PATCH_INFO_CLASS_INIT:
- patch_id = patch_info->data.klass;
- break;
- case MONO_PATCH_INFO_WRAPPER:
- /* A bit ugly, but works */
- g_assert (patch_info->data.method->wrapper_type < sizeof (MonoMethod));
- patch_id = (gpointer)(((guint8*)patch_info->data.method) + patch_info->data.method->wrapper_type);
- break;
- default:
- g_assert_not_reached ();
- }
+/*
+ * is_shared_got_patch:
+ *
+ * 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.
+ */
+static inline gboolean
+is_shared_got_patch (MonoJumpInfo *patch_info)
+{
+ 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;
+ }
+}
- if (patch_id) {
- idx = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_plt_offset, patch_id));
- if (idx)
- res = idx;
- else
- g_hash_table_insert (acfg->patch_to_plt_offset, patch_id, GUINT_TO_POINTER (acfg->plt_offset));
- }
+static int
+get_plt_offset (MonoAotCompile *acfg, MonoJumpInfo *patch_info)
+{
+ int res = -1;
+
+ if (is_plt_patch (patch_info)) {
+ int idx = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_plt_offset, patch_info));
+
+ if (idx) {
+ res = idx;
+ } else {
+ MonoJumpInfo *new_ji = mono_patch_info_dup_mp (acfg->mempool, patch_info);
- if (res == -1) {
res = acfg->plt_offset;
- g_hash_table_insert (acfg->plt_offset_to_patch, GUINT_TO_POINTER (acfg->plt_offset), new_ji);
+ 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 ++;
}
-
- /* Nullify the patch */
- patch_info->type = MONO_PATCH_INFO_NONE;
-
- return res;
- }
- default:
- return -1;
}
+
+ return res;
}
/**
return get_got_offset (acfg, ji);
}
+/* Add a method to the list of methods which need to be emitted */
static void
-emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg)
+add_method_with_index (MonoAotCompile *acfg, MonoMethod *method, int index)
{
- MonoMethod *method;
- int i, pindex, method_index;
- guint8 *code;
- char *symbol;
- int func_alignment = 16;
- GPtrArray *patches;
- MonoJumpInfo *patch_info;
- MonoMethodHeader *header;
- gboolean skip;
- guint32 got_slot;
+ 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));
+ }
+}
- method = cfg->method;
- code = cfg->native_code;
- header = mono_method_get_header (method);
+static guint32
+get_method_index (MonoAotCompile *acfg, MonoMethod *method)
+{
+ int index = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->method_indexes, method));
+
+ g_assert (index);
- method_index = mono_metadata_token_index (method->token);
+ return index - 1;
+}
- /* Make the labels local */
- symbol = g_strdup_printf (".Lm_%x", method_index);
+static int
+add_method (MonoAotCompile *acfg, MonoMethod *method)
+{
+ int index = acfg->method_index;
- emit_alignment (acfg, func_alignment);
- emit_label (acfg, symbol);
- if (acfg->aot_opts.write_symbols)
- emit_global (acfg, symbol, TRUE);
+ add_method_with_index (acfg, method, index);
- if (cfg->verbose_level > 0)
- g_print ("Method %s emitted as %s\n", mono_method_full_name (method, TRUE), symbol);
+ /* FIXME: Fix quadratic behavior */
+ acfg->method_order = g_list_append (acfg->method_order, GUINT_TO_POINTER (index));
- acfg->stats.code_size += cfg->code_len;
+ acfg->method_index ++;
- /* Collect and sort relocations */
- patches = g_ptr_array_new ();
- 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);
+ return index;
+}
- acfg->method_got_offsets [method_index] = acfg->got_offset;
- for (i = 0; i < cfg->code_len; i++) {
- patch_info = NULL;
- for (pindex = 0; pindex < patches->len; ++pindex) {
- patch_info = g_ptr_array_index (patches, pindex);
- if (patch_info->ip.i == i)
- break;
- }
+static void
+add_jit_icall_wrapper (gpointer key, gpointer value, gpointer user_data)
+{
+ MonoAotCompile *acfg = user_data;
+ MonoJitICallInfo *callinfo = value;
+ MonoMethod *wrapper;
+ char *name;
-#ifdef MONO_ARCH_HAVE_PIC_AOT
+ if (!callinfo->sig)
+ return;
- skip = FALSE;
- if (patch_info && (pindex < patches->len)) {
- switch (patch_info->type) {
- case MONO_PATCH_INFO_LABEL:
- case MONO_PATCH_INFO_BB:
- 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);
+ 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);
- i += offset + 4 - 1;
- skip = TRUE;
- break;
- }
- default: {
- int plt_index;
- char *direct_call_target;
+ add_method (acfg, wrapper);
+}
- if (!is_got_patch (patch_info->type))
- break;
+static MonoMethod*
+get_runtime_invoke_sig (MonoMethodSignature *sig)
+{
+ MonoMethodBuilder *mb;
+ MonoMethod *m;
- /*
- * If this patch is a call, try emitting a direct call instead of
+ 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);
+}
+
+static void
+add_wrappers (MonoAotCompile *acfg)
+{
+ MonoMethod *m;
+ int i, nallocators;
+ MonoMethodSignature *csig;
+
+ /*
+ * 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.
+ */
+
+ /*
+ * FIXME: This depends on the fact that different wrappers have different
+ * names.
+ */
+
+ /* FIXME: Collect these automatically */
+
+ /* Runtime invoke wrappers */
+
+ /* 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));
+
+ /* 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));
+
+ /* 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));
+
+ /* 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));
+
+ 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 (!strcmp (method->name, "Main"))
+ add_method (acfg, mono_marshal_get_runtime_invoke (method));
+ }
+
+ if (strcmp (acfg->image->assembly->aname.name, "mscorlib") == 0) {
+ /* 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 ());
+ }
+
+ /* remoting-invoke wrappers */
+ for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
+ MonoMethod *method;
+ MonoMethodSignature *sig;
+ guint32 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);
+
+ add_method (acfg, m);
+ }
+ }
+}
+
+static void
+emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, guint32 code_len, MonoJumpInfo *relocs, gboolean got_only)
+{
+ int i, pindex, start_index, method_index;
+ GPtrArray *patches;
+ MonoJumpInfo *patch_info;
+ MonoMethodHeader *header;
+ gboolean skip;
+ guint32 got_slot;
+
+ if (method) {
+ header = mono_method_get_header (method);
+
+ method_index = get_method_index (acfg, method);
+ }
+
+ /* 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);
+
+ 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;
+
+ 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);
+
+ i += offset + 4 - 1;
+ skip = TRUE;
+ break;
+ }
+ default: {
+ char *direct_call_target;
+
+ if (!is_got_patch (patch_info->type))
+ break;
+
+ /*
+ * 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_target = NULL;
- if ((patch_info->type == MONO_PATCH_INFO_METHOD) && (patch_info->data.method->klass->image == cfg->method->klass->image)) {
+ 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) {
- guint32 callee_idx = mono_metadata_token_index (callee_cfg->method->token);
- if (!acfg->has_got_slots [callee_idx] && (callee_cfg->method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)) {
+ if (!callee_cfg->has_got_slots && (callee_cfg->method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)) {
//printf ("DIRECT: %s %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (callee_cfg->method, TRUE));
- direct_call_target = g_strdup_printf (".Lm_%x", mono_metadata_token_index (callee_cfg->method->token));
+ direct_call_target = g_strdup_printf (".Lm_%x", get_method_index (acfg, callee_cfg->method));
patch_info->type = MONO_PATCH_INFO_NONE;
acfg->stats.direct_calls ++;
}
acfg->stats.all_calls ++;
}
- if (!direct_call_target) {
- plt_index = get_plt_index (acfg, patch_info);
- if (plt_index != -1) {
+ if (!got_only && !direct_call_target) {
+ 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_target = g_strdup_printf (".Lp_%d", plt_index);
+ direct_call_target = g_strdup_printf (".Lp_%d", plt_offset);
+
+ /* Nullify the patch */
+ patch_info->type = MONO_PATCH_INFO_NONE;
}
}
emit_byte (acfg, '\xe8');
emit_symbol_diff (acfg, direct_call_target, ".", -4);
i += 4;
-#else
+#elif defined(__arm__)
+#ifdef USE_BIN_WRITER
+ /* FIXME: Can't encode this using the current symbol writer functions */
g_assert_not_reached ();
+#else
+ emit_unset_mode (acfg);
+ fprintf (acfg->fp, "bl %s\n", direct_call_target);
+ i += 4 - 1;
+#endif
#endif
+
+ g_free (direct_call_target);
} else {
got_slot = get_got_offset (acfg, patch_info);
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;
}
}
}
-#endif /* MONO_ARCH_HAVE_PIC_AOT */
+#endif /* MONO_ARCH_AOT_SUPPORTED */
if (!skip)
emit_bytes (acfg, code + i, 1);
}
+}
+
+static void
+emit_method_code (MonoAotCompile *acfg, MonoCompile *cfg)
+{
+ MonoMethod *method;
+ int method_index;
+ guint8 *code;
+ char *symbol;
+ int func_alignment = 16;
+ MonoMethodHeader *header;
+
+ method = cfg->orig_method;
+ code = cfg->native_code;
+ header = mono_method_get_header (method);
+
+ method_index = get_method_index (acfg, method);
+
+ /* Make the labels local */
+ symbol = g_strdup_printf (".Lm_%x", method_index);
+
+ emit_alignment (acfg, func_alignment);
+ emit_label (acfg, symbol);
+ if (acfg->aot_opts.write_symbols)
+ emit_global (acfg, symbol, TRUE);
+
+ if (cfg->verbose_level > 0)
+ g_print ("Method %s emitted as %s\n", mono_method_full_name (method, TRUE), symbol);
+ g_free (symbol);
+
+ acfg->stats.code_size += cfg->code_len;
+
+ acfg->method_got_offsets [method_index] = acfg->got_offset;
+
+ emit_and_reloc_code (acfg, method, code, cfg->code_len, cfg->patch_info, FALSE);
+
emit_line (acfg);
}
/**
* encode_patch:
*
- * Encode PATCH_INFO into its disk representation. If SHARED is true, encode some types
- * of patches by allocating a GOT entry for them, and encode the GOT offset instead.
+ * Encode PATCH_INFO into its disk representation.
*/
static void
-encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint8 **endbuf, gboolean shared)
+encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint8 **endbuf)
{
guint8 *p = buf;
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_INTERNAL_METHOD:
+ case MONO_PATCH_INFO_JIT_ICALL_ADDR: {
guint32 len = strlen (patch_info->data.name);
encode_value (len, p, &p);
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);
- /*
- * An optimization would be to emit shared code for ldstr
- * statements followed by a throw.
- */
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:
- if (shared) {
- guint32 offset = get_got_offset (acfg, patch_info);
- encode_value (offset, p, &p);
- } else {
- encode_value (get_image_index (acfg, patch_info->data.token->image), p, &p);
- encode_value (patch_info->data.token->token, p, &p);
- }
+ encode_value (get_image_index (acfg, patch_info->data.token->image), p, &p);
+ encode_value (patch_info->data.token->token, p, &p);
break;
case MONO_PATCH_INFO_EXC_NAME: {
MonoClass *ex_class;
mono_class_from_name (mono_defaults.exception_class->image,
"System", patch_info->data.target);
g_assert (ex_class);
- encode_klass_info (acfg, ex_class, p, &p);
+ encode_klass_ref (acfg, ex_class, p, &p);
break;
}
case MONO_PATCH_INFO_R4:
case MONO_PATCH_INFO_CLASS:
case MONO_PATCH_INFO_IID:
case MONO_PATCH_INFO_ADJUSTED_IID:
- if (shared) {
- guint32 offset = get_got_offset (acfg, patch_info);
- encode_value (offset, p, &p);
- } else {
- encode_klass_info (acfg, patch_info->data.klass, p, &p);
- }
+ encode_klass_ref (acfg, patch_info->data.klass, p, &p);
break;
case MONO_PATCH_INFO_CLASS_INIT:
- encode_klass_info (acfg, patch_info->data.klass, p, &p);
+ 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:
- if (shared) {
- guint32 offset = get_got_offset (acfg, patch_info);
- encode_value (offset, p, &p);
- } else {
- encode_field_info (acfg, patch_info->data.field, p, &p);
- }
+ encode_field_info (acfg, patch_info->data.field, p, &p);
break;
- case MONO_PATCH_INFO_WRAPPER: {
+ case MONO_PATCH_INFO_INTERRUPTION_REQUEST_FLAG:
+ break;
+ case MONO_PATCH_INFO_WRAPPER:
encode_value (patch_info->data.method->wrapper_type, p, &p);
switch (patch_info->data.method->wrapper_type) {
- case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK: {
+ case MONO_WRAPPER_REMOTING_INVOKE:
+ case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
+ case MONO_WRAPPER_XDOMAIN_INVOKE: {
MonoMethod *m;
guint32 image_index;
guint32 token;
case MONO_WRAPPER_LDFLD:
case MONO_WRAPPER_LDFLDA:
case MONO_WRAPPER_STFLD:
- case MONO_WRAPPER_LDFLD_REMOTE:
- case MONO_WRAPPER_STFLD_REMOTE:
case MONO_WRAPPER_ISINST: {
MonoClass *proxy_class = (MonoClass*)mono_marshal_method_from_wrapper (patch_info->data.method);
- encode_klass_info (acfg, proxy_class, p, &p);
+ 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 (patch_info->data.method);
+ g_assert (alloc_type != -1);
+ encode_value (alloc_type, p, &p);
break;
}
case MONO_WRAPPER_STELEMREF:
g_assert_not_reached ();
}
break;
+ case MONO_PATCH_INFO_RGCTX_FETCH: {
+ MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry;
+
+ 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:
+ break;
default:
g_warning ("unable to handle jump info %d", patch_info->type);
g_assert_not_reached ();
*endbuf = p;
}
+static void
+encode_patch_list (MonoAotCompile *acfg, GPtrArray *patches, int n_patches, int first_got_offset, guint8 *buf, guint8 **endbuf)
+{
+ 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);
+
+ /* 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;
+
+ 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;
+
+ /* Only the type is needed */
+ *p = patch_info->type;
+ p++;
+ }
+
+ /* Then encode the other info */
+ for (pindex = 0; pindex < patches->len; ++pindex) {
+ patch_info = g_ptr_array_index (patches, pindex);
+
+ 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);
+ }
+ }
+
+ *endbuf = p;
+}
+
static void
emit_method_info (MonoAotCompile *acfg, MonoCompile *cfg)
{
MonoMethod *method;
GList *l;
- int j, pindex, buf_size, n_patches;
+ int pindex, buf_size, n_patches;
guint8 *code;
char *symbol;
GPtrArray *patches;
MonoJumpInfo *patch_info;
MonoMethodHeader *header;
- guint32 last_offset, method_idx;
+ guint32 method_index;
guint8 *p, *buf;
guint32 first_got_offset;
- method = cfg->method;
+ method = cfg->orig_method;
code = cfg->native_code;
header = mono_method_get_header (method);
- method_idx = mono_metadata_token_index (method->token);
+ method_index = get_method_index (acfg, method);
/* Make the labels local */
- symbol = g_strdup_printf (".Lm_%x_p", method_idx);
+ symbol = g_strdup_printf (".Lm_%x_p", method_index);
/* Sort relocations */
patches = g_ptr_array_new ();
g_ptr_array_add (patches, patch_info);
g_ptr_array_sort (patches, compare_patches);
- first_got_offset = acfg->method_got_offsets [mono_metadata_token_index (cfg->method->token)];
+ first_got_offset = acfg->method_got_offsets [method_index];
/**********************/
/* Encode method info */
p = buf = g_malloc (buf_size);
if (mono_class_get_cctor (method->klass))
- encode_klass_info (acfg, method->klass, p, &p);
+ encode_klass_ref (acfg, method->klass, p, &p);
else
/* Not needed when loading the method */
encode_value (0, p, &p);
for (pindex = 0; pindex < patches->len; ++pindex) {
patch_info = g_ptr_array_index (patches, pindex);
- if ((patch_info->type == MONO_PATCH_INFO_LABEL) ||
- (patch_info->type == MONO_PATCH_INFO_BB) ||
- (patch_info->type == MONO_PATCH_INFO_GOT_OFFSET) ||
+ 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 ((patch_info->type == MONO_PATCH_INFO_METHOD) ||
- (patch_info->type == MONO_PATCH_INFO_INTERNAL_METHOD) ||
- (patch_info->type == MONO_PATCH_INFO_WRAPPER) ||
- (patch_info->type == MONO_PATCH_INFO_CLASS_INIT)) {
+ if (is_plt_patch (patch_info)) {
/* Calls are made through the PLT */
patch_info->type = MONO_PATCH_INFO_NONE;
continue;
}
if (n_patches)
- g_assert (acfg->has_got_slots [method_idx]);
+ g_assert (cfg->has_got_slots);
- encode_value (n_patches, p, &p);
-
- if (n_patches)
- encode_value (first_got_offset, p, &p);
-
- /* 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;
-
- 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;
-
- /* Only the type is needed */
- *p = patch_info->type;
- p++;
- }
-
- /*
- if (n_patches) {
- printf ("%s:\n", mono_method_full_name (cfg->method, TRUE));
- for (pindex = 0; pindex < patches->len; ++pindex) {
- patch_info = g_ptr_array_index (patches, pindex);
- if (patch_info->type != MONO_PATCH_INFO_NONE) {
- printf ("\t%s", patch_types [patch_info->type]);
- if (patch_info->type == MONO_PATCH_INFO_VTABLE)
- printf (": %s\n", patch_info->data.klass->name);
- else
- printf ("\n");
- }
- }
- }
- */
-
- /* Then encode the other info */
- for (pindex = 0; pindex < patches->len; ++pindex) {
- patch_info = g_ptr_array_index (patches, pindex);
-
- encode_patch (acfg, patch_info, p, &p, TRUE);
- }
+ 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_free (symbol);
g_assert (p - buf < buf_size);
emit_bytes (acfg, buf, p - buf);
g_free (buf);
-
- g_free (symbol);
}
static void
emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
{
MonoMethod *method;
- int k, buf_size;
+ int k, buf_size, method_index;
guint32 debug_info_size;
guint8 *code;
char *symbol;
MonoMethodHeader *header;
guint8 *p, *buf, *debug_info;
- method = cfg->method;
+ method = cfg->orig_method;
code = cfg->native_code;
header = mono_method_get_header (method);
+ method_index = get_method_index (acfg, method);
+
/* Make the labels local */
- symbol = g_strdup_printf (".Le_%x_p", mono_metadata_token_index (method->token));
+ symbol = g_strdup_printf (".Le_%x_p", method_index);
+
+ mono_debug_serialize_debug_info (cfg, &debug_info, &debug_info_size);
- buf_size = header->num_clauses * 256 + 128;
+ buf_size = header->num_clauses * 256 + debug_info_size + 128;
p = buf = g_malloc (buf_size);
encode_value (cfg->code_len, p, &p);
}
}
- mono_debug_serialize_debug_info (cfg, &debug_info, &debug_info_size);
+ g_assert (debug_info_size < buf_size);
encode_value (debug_info_size, p, &p);
if (debug_info_size) {
/* Emit info */
emit_label (acfg, symbol);
+ g_free (symbol);
g_assert (p - buf < buf_size);
emit_bytes (acfg, buf, p - buf);
g_free (buf);
-
- g_free (symbol);
}
static void
MonoClass *klass = mono_class_get (acfg->image, token);
guint8 *p, *buf;
int i, buf_size;
- char *label;
- gboolean no_special_static;
+ char *symbol;
+ gboolean no_special_static, cant_encode;
- buf_size = 10240;
+ buf_size = 10240 + (klass->vtable_size * 16);
p = buf = g_malloc (buf_size);
g_assert (klass);
mono_class_init (klass);
+ mono_class_setup_vtable (klass);
+
/*
* Emit all the information which is required for creating vtables so
* the runtime does not need to create the MonoMethod structures which
no_special_static = !mono_class_has_special_static_fields (klass);
- if (1) {
+ /* 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;
+ }
+
+ 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)
acfg->stats.class_info_size += p - buf;
/* Emit the info */
- label = g_strdup_printf (".LK_I_%x", token - MONO_TOKEN_TYPE_DEF - 1);
- emit_label (acfg, label);
+ symbol = g_strdup_printf (".LK_I_%x", token - MONO_TOKEN_TYPE_DEF - 1);
+ emit_label (acfg, symbol);
+ g_free (symbol);
g_assert (p - buf < buf_size);
emit_bytes (acfg, buf, p - buf);
* - 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 transfer control to the
+ * known. Initially the PLT entries jump to code which transfers control to the
* AOT runtime through the first PLT entry.
*/
static void
plt_info_offsets [i] = p - buf;
encode_value (patch_info->type, p, &p);
- encode_patch (acfg, patch_info, p, &p, FALSE);
+ encode_patch (acfg, patch_info, p, &p);
}
emit_line (acfg);
emit_global (acfg, symbol, TRUE);
emit_alignment (acfg, PAGESIZE);
emit_label (acfg, symbol);
+ g_free (symbol);
- /*
- * The first plt entry is used to transfer code to the AOT loader.
- */
- emit_label (acfg, ".Lp_0");
-#if defined(__i386__)
- /* It is filled up during loading by the AOT loader. */
- emit_zero_bytes (acfg, 16);
-#elif defined(__x86_64__)
- /* This should be exactly 16 bytes long */
- /* jmpq *<offset>(%rip) */
- emit_byte (acfg, '\xff');
- emit_byte (acfg, '\x25');
- emit_symbol_diff (acfg, "plt_jump_table", ".", -4);
- emit_zero_bytes (acfg, 10);
-#else
+#if defined(USE_BIN_WRITER) && defined(__arm__)
+ /* FIXME: */
g_assert_not_reached ();
#endif
- for (i = 1; i < acfg->plt_offset; ++i) {
+ for (i = 0; i < acfg->plt_offset; ++i) {
char *label;
label = g_strdup_printf (".Lp_%d", i);
emit_label (acfg, label);
g_free (label);
+
+ /*
+ * The first plt entry is used to transfer code to the AOT loader.
+ */
+
#if defined(__i386__)
- /* Need to make sure this is 5 bytes long */
- emit_byte (acfg, '\xe9');
- label = g_strdup_printf (".Lpd_%d", i);
- emit_symbol_diff (acfg, label, ".", -4);
- g_free (label);
+ if (i == 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, plt_info_offsets [i]);
+ }
#elif defined(__x86_64__)
/*
* We can't emit jumps because they are 32 bits only so they can't be patched.
* point to .Lpd entries. ELF stores these in the GOT too, but we don't, since
* methods with GOT entries can't be called directly.
* We also emit the default PLT code here since the PLT code will not be patched.
- * An x86_64 plt entry is 16 bytes long, init_plt () depends on this.
+ * An x86_64 plt entry is 10 bytes long, init_plt () depends on this.
*/
/* jmpq *<offset>(%rip) */
emit_byte (acfg, '\xff');
emit_byte (acfg, '\x25');
emit_symbol_diff (acfg, "plt_jump_table", ".", (i * sizeof (gpointer)) -4);
- /* mov <plt info offset>, %eax */
- emit_byte (acfg, '\xb8');
+ /* Used by mono_aot_get_plt_info_offset */
emit_int32 (acfg, plt_info_offsets [i]);
- /* jmp .Lp_0 */
- emit_byte (acfg, '\xe9');
- emit_symbol_diff (acfg, ".Lp_0", ".", -4);
+#elif defined(__arm__)
+ /* FIXME:
+ * - optimize OP_AOTCONST implementation
+ * - optimize the PLT entries
+ * - optimize SWITCH AOT implementation
+ * - implement IMT support
+ */
+ emit_unset_mode (acfg);
+ fprintf (acfg->fp, "\tldr ip, [pc, #4]\n");
+ fprintf (acfg->fp, "\tadd ip, pc, ip\n");
+ fprintf (acfg->fp, "\tldr pc, [ip, #0]\n");
+ emit_symbol_diff (acfg, "plt_jump_table", ".", 0);
+ /* Used by mono_aot_get_plt_info_offset */
+ #if defined(__MACH__)
+ fprintf (acfg->fp, "\n\t.long %d\n", plt_info_offsets [i]);
+ #else
+ fprintf (acfg->fp, "\n\t.word %d\n", plt_info_offsets [i]);
+ #endif
+
#else
g_assert_not_reached ();
#endif
}
- symbol = g_strdup_printf ("plt_end");
- emit_global (acfg, symbol, TRUE);
- emit_label (acfg, symbol);
+ g_free (plt_info_offsets);
+
+ symbol = g_strdup_printf ("plt_end");
+ emit_global (acfg, symbol, TRUE);
+ emit_label (acfg, symbol);
+ g_free (symbol);
+
+ /* Emit PLT info */
+ symbol = g_strdup_printf ("plt_info");
+ emit_global (acfg, symbol, FALSE);
+ emit_label (acfg, symbol);
+ g_free (symbol);
+
+ g_assert (p - buf < buf_size);
+ emit_bytes (acfg, buf, p - buf);
+ g_free (buf);
+
+ symbol = g_strdup_printf ("plt_jump_table_addr");
+ emit_section_change (acfg, ".data", 0);
+ emit_global (acfg, symbol, FALSE);
+ emit_alignment (acfg, 8);
+ emit_label (acfg, symbol);
+ emit_pointer (acfg, "plt_jump_table");
+ g_free (symbol);
+
+ symbol = g_strdup_printf ("plt_jump_table_size");
+ emit_section_change (acfg, ".data", 0);
+ emit_global (acfg, symbol, FALSE);
+ emit_alignment (acfg, 8);
+ emit_label (acfg, symbol);
+ emit_symbol_diff (acfg, "plt_jump_table_end", "plt_jump_table", 0);
+ g_free (symbol);
+
+ /* Don't make this a global so accesses don't need relocations */
+ symbol = g_strdup_printf ("plt_jump_table");
+ emit_section_change (acfg, ".bss", 0);
+ emit_label (acfg, symbol);
+ g_free (symbol);
+
+#if defined(__x86_64__) || defined(__arm__)
+ emit_zero_bytes (acfg, (int)(acfg->plt_offset * sizeof (gpointer)));
+#endif
+
+ symbol = g_strdup_printf ("plt_jump_table_end");
+ emit_label (acfg, symbol);
+ g_free (symbol);
+}
+
+static void
+emit_named_code (MonoAotCompile *acfg, const char *name, guint8 *code,
+ guint32 code_size, int got_offset, MonoJumpInfo *ji)
+{
+ char *symbol;
+ guint32 buf_size;
+ MonoJumpInfo *patch_info;
+ guint8 *buf, *p;
+ GPtrArray *patches;
+
+ /* Emit code */
+
+ symbol = g_strdup_printf ("%s", name);
+
+ emit_section_change (acfg, ".text", 0);
+ emit_global (acfg, symbol, TRUE);
+ emit_alignment (acfg, 16);
+ emit_label (acfg, symbol);
+ g_free (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 = ji; patch_info; patch_info = patch_info->next)
+ g_ptr_array_add (patches, patch_info);
+ g_ptr_array_sort (patches, compare_patches);
+
+ buf_size = patches->len * 128 + 128;
+ buf = g_malloc (buf_size);
+ p = buf;
+
+ encode_patch_list (acfg, patches, patches->len, got_offset, p, &p);
+ g_assert (p - buf < buf_size);
+
+ symbol = g_strdup_printf ("%s_p", name);
+
+ emit_section_change (acfg, ".text", 0);
+ emit_global (acfg, symbol, FALSE);
+ emit_label (acfg, symbol);
+ g_free (symbol);
+
+ emit_bytes (acfg, buf, p - buf);
+}
+
+/*
+ * 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.
+ */
+static void
+emit_trampolines (MonoAotCompile *acfg)
+{
+ char *symbol;
+ int tramp_type, i, offset;
+#ifdef MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES
+ guint32 code_size;
+ MonoJumpInfo *ji;
+ guint8 *code;
+#endif
+
+ if (!acfg->aot_opts.full_aot)
+ 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);
+
+ /* Emit trampoline code */
+
+ symbol = g_strdup_printf ("generic_trampoline_%d", tramp_type);
+
+ emit_named_code (acfg, symbol, code, code_size, acfg->got_offset, ji);
+
+ g_free (symbol);
+ }
+
+ 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);
+
+ /* 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);
+#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;
+
+ symbol = g_strdup_printf ("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 *<offset>(%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 */
- /*
- * Emit the default targets for the PLT entries separately since these will not
- * be modified at runtime.
- */
- for (i = 1; i < acfg->plt_offset; ++i) {
- char *label;
+ 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;
- label = g_strdup_printf (".Lpd_%d", i);
- emit_label (acfg, label);
- g_free (label);
+ method = mono_get_method (acfg->image, token, NULL);
- /* Put the offset into the register expected by mono_aot_plt_trampoline */
-#if defined(__i386__)
- /* movl $const, %eax */
- emit_byte (acfg, '\xb8');
- emit_int32 (acfg, plt_info_offsets [i]);
- /* jmp .Lp_0 */
- emit_byte (acfg, '\xe9');
- emit_symbol_diff (acfg, ".Lp_0", ".", -4);
-#elif defined(__x86_64__)
- /* Emitted along with the PLT entries since they will not be patched */
-#else
- g_assert_not_reached ();
-#endif
- }
+ cfg = g_hash_table_lookup (acfg->method_to_cfg, method);
+ if (!cfg || !cfg->orig_method->klass->valuetype || !(method->flags & METHOD_ATTRIBUTE_VIRTUAL))
+ continue;
- /* Emit PLT info */
- symbol = g_strdup_printf ("plt_info");
- emit_global (acfg, symbol, FALSE);
- emit_label (acfg, symbol);
+ symbol = g_strdup_printf ("unbox_trampoline_%d", i);
- g_assert (p - buf < buf_size);
- emit_bytes (acfg, buf, p - buf);
- g_free (buf);
+ emit_section_change (acfg, ".text", 0);
+ emit_global (acfg, symbol, TRUE);
+ emit_label (acfg, symbol);
- symbol = g_strdup_printf ("plt_jump_table_addr");
- emit_section_change (acfg, ".data", 0);
- emit_global (acfg, symbol, FALSE);
- emit_alignment (acfg, 8);
- emit_label (acfg, symbol);
- emit_pointer (acfg, "plt_jump_table");
+ call_target = g_strdup_printf (".Lm_%x", get_method_index (acfg, cfg->orig_method));
- symbol = g_strdup_printf ("plt_jump_table_size");
- emit_section_change (acfg, ".data", 0);
- emit_global (acfg, symbol, FALSE);
- emit_alignment (acfg, 8);
- emit_label (acfg, symbol);
- emit_symbol_diff (acfg, "plt_jump_table_end", "plt_jump_table", 0);
+#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 <method> */
+ emit_byte (acfg, '\xe9');
+ emit_symbol_diff (acfg, call_target, ".", -4);
+ }
+#elif defined(__arm__)
+ {
+ guint8 buf [128];
+ int this_pos = 0;
- /* Don't make this a global so accesses don't need relocations */
- symbol = g_strdup_printf ("plt_jump_table");
- emit_section_change (acfg, ".bss", 0);
- emit_label (acfg, symbol);
+ code = buf;
-#ifdef __x86_64__
- emit_zero_bytes (acfg, (int)(acfg->plt_offset * sizeof (gpointer)));
-#endif
+ if (MONO_TYPE_ISSTRUCT (mono_method_signature (cfg->orig_method)->ret))
+ this_pos = 1;
- symbol = g_strdup_printf ("plt_jump_table_end");
+ 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 ();
+#endif
+ fprintf (acfg->fp, "\n\tb %s\n", call_target);
+ }
+#else
+ g_assert_not_reached ();
+#endif
+ }
+
+ symbol = g_strdup_printf ("trampolines_info");
+
+ emit_section_change (acfg, ".text", 0);
+ emit_global (acfg, symbol, TRUE);
+ emit_alignment (acfg, PAGESIZE);
emit_label (acfg, symbol);
+
+ emit_int32 (acfg, acfg->num_aot_trampolines);
+ emit_int32 (acfg, acfg->got_offset);
}
static gboolean
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, "static")) {
+ opts->static_link = TRUE;
+ opts->no_dlsym = 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 void
-compile_method (MonoAotCompile *acfg, int index)
+compile_method (MonoAotCompile *acfg, MonoMethod *method)
{
MonoCompile *cfg;
- MonoMethod *method;
MonoJumpInfo *patch_info;
gboolean skip;
- guint32 token = MONO_TOKEN_METHOD_DEF | (index + 1);
- guint32 method_idx;
+ int index;
if (acfg->aot_opts.metadata_only)
return;
- method = mono_get_method (acfg->image, token, NULL);
+ index = get_method_index (acfg, method);
- method_idx = mono_metadata_token_index (method->token);
-
/* fixme: maybe we can also precompile wrapper methods */
- if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
- (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
+ 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;
+
acfg->stats.mcount++;
- /* fixme: we need to patch the IP for the LMF in that case */
- if (method->save_lmf) {
- //printf ("Skip (needs lmf): %s\n", mono_method_full_name (method, TRUE));
- acfg->stats.lmfcount++;
+ if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) {
+ /*
+ * FIXME: Enabling this causes virtual-sync.exe to fail, since the trampoline
+ * code can't determine that it needs to insert a sync wrapper in the AOT case.
+ */
return;
}
+ //acfg->opts &= ~MONO_OPT_GSHARED;
+
+ // FIXME: GSHARED is on by default
+#if 1
+ if (TRUE || !(acfg->opts & MONO_OPT_GSHARED)) {
+ if (method->is_generic || method->klass->generic_container) {
+ acfg->stats.genericcount ++;
+ return;
+ }
+ }
+#endif
+
+ if (acfg->aot_opts.full_aot)
+ mono_use_imt = FALSE;
+
/*
* Since these methods are the only ones which are compiled with
* AOT support, and they are not used by runtime startup/shutdown code,
* does not need to support them by creating a fake GOT etc.
*/
cfg = mini_method_compile (method, acfg->opts, mono_get_root_domain (), FALSE, TRUE, 0);
+ if (cfg->exception_type == MONO_EXCEPTION_GENERIC_SHARING_FAILED) {
+ //printf ("F: %s\n", mono_method_full_name (method, TRUE));
+ acfg->stats.genericcount ++;
+ return;
+ }
if (cfg->exception_type != MONO_EXCEPTION_NONE) {
/* Let the exception happen at runtime */
return;
return;
}
- skip = FALSE;
+ /* Nullify patches which need no aot processing */
for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
- if (patch_info->type == 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;
+ switch (patch_info->type) {
+ case MONO_PATCH_INFO_LABEL:
+ case MONO_PATCH_INFO_BB:
+ patch_info->type = MONO_PATCH_INFO_NONE;
+ break;
+ default:
break;
}
}
- if (skip) {
- acfg->stats.abscount++;
- mono_destroy_compile (cfg);
- return;
- }
+ /* Collect method->token associations from the cfg */
+ g_hash_table_foreach (cfg->token_info_hash, add_token_info_hash, acfg);
skip = FALSE;
for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
- if (patch_info->type == MONO_PATCH_INFO_METHOD_JUMP) {
- /*
- * FIXME: We can't handle this because mono_jit_compile_method_inner will try
- * to patch the AOT code when the target of the jump is compiled.
- */
- skip = TRUE;
+ 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:
break;
}
}
if (skip) {
- acfg->stats.ocount++;
+ acfg->stats.abscount++;
mono_destroy_compile (cfg);
return;
}
- /* some wrappers are very common */
+ /* Convert method patches referring to wrapper methods to MONO_PATCH_INFO_WRAPPER */
+ skip = FALSE;
for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
- if (patch_info->type == MONO_PATCH_INFO_METHODCONST) {
- switch (patch_info->data.method->wrapper_type) {
- case MONO_WRAPPER_PROXY_ISINST:
- patch_info->type = MONO_PATCH_INFO_WRAPPER;
- }
- }
-
- if (patch_info->type == MONO_PATCH_INFO_METHOD) {
+ if ((patch_info->type == MONO_PATCH_INFO_METHODCONST) || (patch_info->type == MONO_PATCH_INFO_METHOD)) {
switch (patch_info->data.method->wrapper_type) {
+ case MONO_WRAPPER_NONE:
+ break;
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_STELEMREF:
case MONO_WRAPPER_ISINST:
case MONO_WRAPPER_PROXY_ISINST:
+ case MONO_WRAPPER_ALLOC:
+ case MONO_WRAPPER_REMOTING_INVOKE:
patch_info->type = MONO_PATCH_INFO_WRAPPER;
break;
+ default:
+ /* unable to handle this */
+ //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));
+ skip = TRUE;
+ break;
}
}
}
+ if (skip) {
+ acfg->stats.wrappercount++;
+ mono_destroy_compile (cfg);
+ return;
+ }
+
skip = FALSE;
for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
switch (patch_info->type) {
case MONO_PATCH_INFO_METHOD:
case MONO_PATCH_INFO_METHODCONST:
- if (patch_info->data.method->wrapper_type) {
- /* unable to handle this */
- //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));
- skip = TRUE;
- break;
+ 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))
+ skip = TRUE;
}
- if (!patch_info->data.method->token)
- /*
- * The method is part of a constructed type like Int[,].Set (). It doesn't
- * have a token, and we can't make one, since the parent type is part of
- * assembly which contains the element type, and not the assembly which
- * referenced this type.
+ if (patch_info->data.method->is_inflated && !g_hash_table_lookup (acfg->token_info_hash, patch_info->data.method)) {
+ /*
+ * encode_method_ref () can handle this method if it is not generic
+ * and its class can be encoded.
*/
- skip = TRUE;
+ if (!g_hash_table_lookup (acfg->token_info_hash, patch_info->data.method->klass) || mono_method_get_context (patch_info->data.method)->method_inst) {
+ /* FIXME: Can't encode these */
+ //printf ("Skip (can't encode): %s %d -> %s\n", mono_method_full_name (method, TRUE), patch_info->type, mono_method_full_name (patch_info->data.method, TRUE));
+ skip = TRUE;
+ }
+ }
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)
+ 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))
skip = TRUE;
break;
default:
}
if (skip) {
- acfg->stats.wrappercount++;
+ acfg->stats.ocount++;
mono_destroy_compile (cfg);
return;
}
/* Determine whenever the method has GOT slots */
for (patch_info = cfg->patch_info; patch_info; patch_info = patch_info->next) {
switch (patch_info->type) {
- case MONO_PATCH_INFO_LABEL:
- case MONO_PATCH_INFO_BB:
case MONO_PATCH_INFO_GOT_OFFSET:
case MONO_PATCH_INFO_NONE:
- case MONO_PATCH_INFO_METHOD:
- case MONO_PATCH_INFO_INTERNAL_METHOD:
- case MONO_PATCH_INFO_WRAPPER:
break;
case MONO_PATCH_INFO_IMAGE:
- if (patch_info->data.image == acfg->image)
- /* Stored in GOT slot 0 */
- break;
- /* Fall through */
+ /* The assembly is stored in GOT slot 0 */
+ if (patch_info->data.image != acfg->image)
+ cfg->has_got_slots = TRUE;
+ break;
default:
- acfg->has_got_slots [method_idx] = TRUE;
+ if (!is_plt_patch (patch_info))
+ cfg->has_got_slots = TRUE;
break;
}
}
- if (!acfg->has_got_slots [method_idx])
+ if (!cfg->has_got_slots)
acfg->stats.methods_without_got_slots ++;
/* Make a copy of the patch info which is in the mempool */
acfg->cfgs [index] = cfg;
- g_hash_table_insert (acfg->method_to_cfg, cfg->method, cfg);
+ g_hash_table_insert (acfg->method_to_cfg, cfg->orig_method, cfg);
acfg->stats.ccount++;
}
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))
+ 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 ++;
}
/* 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)))
- acfg->method_order = g_list_append (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;
}
/**
MonoCompile *cfg = acfg->cfgs [i];
for (ji = cfg->patch_info; ji; ji = ji->next) {
- switch (ji->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:
+ if (is_shared_got_patch (ji))
get_shared_got_offset (acfg, ji);
- break;
- default:
- break;
- }
}
}
}
emit_global (acfg, symbol, TRUE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
for (l = acfg->method_order; l != NULL; l = l->next) {
i = GPOINTER_TO_UINT (l->data);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
symbol = g_strdup_printf ("method_offsets");
emit_section_change (acfg, ".text", 1);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
- for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
+ for (i = 0; i < acfg->nmethods; ++i) {
if (acfg->cfgs [i]) {
- symbol = g_strdup_printf (".Lm_%x", i + 1);
+ symbol = g_strdup_printf (".Lm_%x", i);
emit_symbol_diff (acfg, symbol, "methods", 0);
+ g_free (symbol);
} else {
emit_int32 (acfg, 0xffffffff);
}
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
/* To reduce size of generated assembly code */
symbol = g_strdup_printf ("mi");
emit_label (acfg, symbol);
+ g_free (symbol);
for (l = acfg->method_order; l != NULL; l = l->next) {
i = GPOINTER_TO_UINT (l->data);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
- for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
+ for (i = 0; i < acfg->nmethods; ++i) {
if (acfg->cfgs [i]) {
- symbol = g_strdup_printf (".Lm_%x_p", i + 1);
+ symbol = g_strdup_printf (".Lm_%x_p", i);
emit_symbol_diff (acfg, symbol, "mi", 0);
+ g_free (symbol);
} else {
emit_int32 (acfg, 0);
}
emit_line (acfg);
}
+static void
+emit_wrapper_info (MonoAotCompile *acfg)
+{
+ int i, index;
+ char *symbol;
+ char *name;
+
+ /* Emit method info */
+ symbol = g_strdup_printf ("wrapper_info");
+ emit_section_change (acfg, ".text", 1);
+ emit_global (acfg, symbol, FALSE);
+ emit_alignment (acfg, 8);
+ emit_label (acfg, symbol);
+ g_free (symbol);
+
+ if (!acfg->aot_opts.full_aot)
+ return;
+
+ for (i = 0; i < acfg->nmethods; ++i) {
+ MonoCompile *cfg = acfg->cfgs [i];
+
+ if (!cfg || !cfg->orig_method->wrapper_type)
+ continue;
+
+ index = get_method_index (acfg, cfg->orig_method);
+
+ // FIXME: Optimize disk usage and lookup speed
+ name = mono_method_full_name (cfg->orig_method, TRUE);
+ emit_string (acfg, name);
+ emit_alignment (acfg, 4);
+ emit_int32 (acfg, index);
+ }
+
+ emit_byte (acfg, 0);
+
+ emit_line (acfg);
+}
+
static void
emit_method_order (MonoAotCompile *acfg)
{
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
/* First emit an index table */
index = 0;
emit_section_change (acfg, ".text", 1);
emit_global (acfg, symbol, FALSE);
emit_label (acfg, symbol);
+ g_free (symbol);
}
static void
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
- /* To reduce size of generate assembly */
+ /* To reduce size of generated assembly */
symbol = g_strdup_printf ("ex");
emit_label (acfg, symbol);
+ g_free (symbol);
- for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
+ for (i = 0; i < acfg->nmethods; ++i) {
if (acfg->cfgs [i])
emit_exception_debug_info (acfg, acfg->cfgs [i]);
}
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
- for (i = 0; i < acfg->image->tables [MONO_TABLE_METHOD].rows; ++i) {
+ for (i = 0; i < acfg->nmethods; ++i) {
if (acfg->cfgs [i]) {
- symbol = g_strdup_printf (".Le_%x_p", i + 1);
+ symbol = g_strdup_printf (".Le_%x_p", i);
emit_symbol_diff (acfg, symbol, "ex", 0);
+ g_free (symbol);
} else {
emit_int32 (acfg, 0);
}
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i)
emit_klass_info (acfg, MONO_TOKEN_TYPE_DEF | (i + 1));
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
for (i = 0; i < acfg->image->tables [MONO_TABLE_TYPEDEF].rows; ++i) {
symbol = g_strdup_printf (".LK_I_%x", i);
emit_symbol_diff (acfg, symbol, "class_info", 0);
+ g_free (symbol);
}
emit_line (acfg);
}
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_full_name (klass);
+ 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;
-
- //printf ("A: '%s' %d %d\n", full_name, g_str_hash (full_name), hash);
+ g_free (full_name);
/* FIXME: Allocate from the mempool */
new_entry = g_new0 (ClassNameTableEntry, 1);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
/* FIXME: Optimize memory usage */
g_assert (table_size < 65000);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (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);
/* No need to encode the patch type */
got_info_offsets [i] = p - buf;
- encode_patch (acfg, ji, p, &p, FALSE);
+ encode_patch (acfg, ji, p, &p);
}
g_assert (p - buf <= buf_size);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
emit_bytes (acfg, buf, p - buf);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
for (i = 0; i < acfg->shared_patches->len; ++i)
emit_int32 (acfg, got_info_offsets [i]);
emit_section_change (acfg, ".bss", 1);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
- if (acfg->got_offset > 0)
- emit_zero_bytes (acfg, (int)(acfg->got_offset * sizeof (gpointer)));
+ if ((acfg->got_offset + acfg->num_trampoline_got_entries) > 0)
+ emit_zero_bytes (acfg, (int)((acfg->got_offset + acfg->num_trampoline_got_entries) * sizeof (gpointer)));
+ g_free (symbol);
symbol = g_strdup_printf ("got_addr");
emit_section_change (acfg, ".data", 1);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
emit_pointer (acfg, "got");
+ g_free (symbol);
symbol = g_strdup_printf ("got_size");
emit_section_change (acfg, ".data", 1);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
emit_int32 (acfg, (int)(acfg->got_offset * sizeof (gpointer)));
+ g_free (symbol);
}
static void
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)
+ emit_string_symbol (acfg, "mono_runtime_version", FULL_VERSION);
+ 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;
+
+ 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);
+
+ symbol = g_strdup_printf ("name_%d", i);
+ emit_section_change (acfg, ".text", 1);
+ emit_label (acfg, symbol);
+ emit_string (acfg, name);
+ g_free (symbol);
+ }
+
+ /* Emit the globals table */
+ symbol = g_strdup_printf ("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);
+
+ symbol = g_strdup_printf ("name_%d", i);
+ emit_pointer (acfg, symbol);
+ g_free (symbol);
+
+ symbol = g_strdup_printf ("%s", name);
+ emit_pointer (acfg, symbol);
+ g_free (symbol);
+ }
+ /* Null terminate the table */
+ emit_pointer (acfg, NULL);
+ emit_pointer (acfg, NULL);
+
+ if (acfg->aot_opts.static_link) {
+ /*
+ * Emit a global symbol which can be passed by an embedding app to
+ * mono_aot_register_module ().
+ */
+ symbol = g_strdup_printf ("mono_aot_module_%s_info", acfg->image->assembly->aname.name);
+ 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 {
+ symbol = g_strdup_printf ("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__)
+ /*
+ * 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
+ g_free (symbol);
+ }
+ }
+}
+
+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->method_got_offsets);
+ 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
{
MonoImage *image = ass->image;
char *symbol;
- int i;
+ int i, res;
MonoAotCompile *acfg;
- MonoCompile **cfgs;
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 (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 ();
load_profile_files (acfg);
- if (mono_defaults.generic_nullable_class) {
- /*
- * FIXME: Its hard to skip generic methods or methods which use generics.
- */
- printf ("Error: Can't AOT Net 2.0 assemblies.\n");
- return 1;
+ emit_start (acfg);
+
+ acfg->num_aot_trampolines = acfg->aot_opts.full_aot ? 10240 : 0;
+
+ acfg->method_index = 1;
+
+ /* Collect methods */
+ for (i = 0; i < 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 (acfg->aot_opts.full_aot && method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
+ /* Compile the wrapper instead */
+ /* We do this here instead of add_wrappers () because it is easy to do it here */
+ MonoMethod *wrapper = mono_marshal_get_native_wrapper (method, check_for_pending_exc, TRUE);
+ method = wrapper;
+ }
+
+ /* Since we add the normal methods first, their index will be equal to their zero based token index */
+ add_method_with_index (acfg, method, i);
+ acfg->method_index ++;
}
- emit_start (acfg);
+ if (acfg->aot_opts.full_aot)
+ add_wrappers (acfg);
- cfgs = g_new0 (MonoCompile*, image->tables [MONO_TABLE_METHOD].rows + 32);
- acfg->cfgs = cfgs;
- acfg->nmethods = image->tables [MONO_TABLE_METHOD].rows;
- acfg->method_got_offsets = g_new0 (guint32, image->tables [MONO_TABLE_METHOD].rows + 32);
- acfg->has_got_slots = g_new0 (gboolean, image->tables [MONO_TABLE_METHOD].rows + 32);
+ acfg->nmethods = acfg->methods->len + 1;
+ acfg->cfgs = g_new0 (MonoCompile*, acfg->nmethods + 32);
+ acfg->method_got_offsets = g_new0 (guint32, acfg->nmethods + 32);
/* PLT offset 0 is reserved for the PLT trampoline */
acfg->plt_offset = 1;
/* Compile methods */
- for (i = 0; i < image->tables [MONO_TABLE_METHOD].rows; ++i)
- compile_method (acfg, i);
+ for (i = 0; i < acfg->methods->len; ++i) {
+ compile_method (acfg, g_ptr_array_index (acfg->methods, i));
+ }
alloc_got_slots (acfg);
emit_info (acfg);
+ emit_wrapper_info (acfg);
+
emit_method_order (acfg);
+ emit_trampolines (acfg);
+
emit_class_name_table (acfg);
emit_got_info (acfg);
emit_global (acfg, symbol, FALSE);
emit_alignment (acfg, 8);
emit_label (acfg, symbol);
+ g_free (symbol);
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)));
- emit_writeout (acfg);
+ res = emit_writeout (acfg);
+ if (res != 0) {
+ acfg_free (acfg);
+ return res;
+ }
printf ("Compiled %d out of %d methods (%d%%)\n", acfg->stats.ccount, acfg->stats.mcount, acfg->stats.mcount ? (acfg->stats.ccount * 100) / acfg->stats.mcount : 100);
- printf ("%d methods contain absolute addresses (%d%%)\n", acfg->stats.abscount, acfg->stats.mcount ? (acfg->stats.abscount * 100) / acfg->stats.mcount : 100);
- printf ("%d methods contain wrapper references (%d%%)\n", acfg->stats.wrappercount, acfg->stats.mcount ? (acfg->stats.wrappercount * 100) / acfg->stats.mcount : 100);
- printf ("%d methods contain lmf pointers (%d%%)\n", acfg->stats.lmfcount, acfg->stats.mcount ? (acfg->stats.lmfcount * 100) / acfg->stats.mcount : 100);
- printf ("%d methods have other problems (%d%%)\n", acfg->stats.ocount, acfg->stats.mcount ? (acfg->stats.ocount * 100) / acfg->stats.mcount : 100);
+ if (acfg->stats.genericcount)
+ printf ("%d methods are generic (%d%%)\n", acfg->stats.genericcount, acfg->stats.mcount ? (acfg->stats.genericcount * 100) / acfg->stats.mcount : 100);
+ if (acfg->stats.abscount)
+ printf ("%d methods contain absolute addresses (%d%%)\n", acfg->stats.abscount, acfg->stats.mcount ? (acfg->stats.abscount * 100) / acfg->stats.mcount : 100);
+ if (acfg->stats.wrappercount)
+ printf ("%d methods contain wrapper references (%d%%)\n", acfg->stats.wrappercount, acfg->stats.mcount ? (acfg->stats.wrappercount * 100) / acfg->stats.mcount : 100);
+ if (acfg->stats.lmfcount)
+ printf ("%d methods contain lmf pointers (%d%%)\n", acfg->stats.lmfcount, acfg->stats.mcount ? (acfg->stats.lmfcount * 100) / acfg->stats.mcount : 100);
+ if (acfg->stats.ocount)
+ 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 ("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", patch_types [i], acfg->stats.got_slot_types [i]);
+ printf ("\t%s: %d\n", get_patch_name (i), acfg->stats.got_slot_types [i]);
+ acfg_free (acfg);
+
return 0;
}