2008-10-18 Zoltan Varga <vargaz@gmail.com>
+ * method-to-ir.c (mono_method_to_ir2): Change a !compile_aot assert to
+ disable_aot.
+
+ * mini.c (mono_patch_info_equal): Compare the generic context as well.
+
+ * mini.h: Bump aot file format version.
+
+ * aot-compiler.c aot-runtime.c: Generalize the wrapper handling code so the
+ AOT file can contain native code for methods which are not in the METHOD
+ table. Generate code for non-sharable generic instances of generic methods
+ found in the METHODSPEC table.
+
* method-to-ir.c (mono_method_to_ir2): Remove the aot restriction when
encoding generic type handles.
GHashTable *image_hash;
GHashTable *method_to_cfg;
GHashTable *token_info_hash;
+ GPtrArray *extra_methods;
+ GHashTable *extra_methods_hash;
GPtrArray *image_table;
GPtrArray *globals;
GList *method_order;
#endif
}
+/*
+ * add_methodspecs:
+ *
+ * Add methods referenced by the METHODSPEC table.
+ */
+static void
+add_methodspecs (MonoAotCompile *acfg)
+{
+ int i;
+ guint32 token;
+ MonoMethod *method;
+ MonoGenericContext *context;
+
+ /* FIXME: Only add instantiations with vtype arguments */
+ 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);
+
+ 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;
+
+ if (method->klass->image != acfg->image)
+ continue;
+
+ if (mono_method_is_generic_sharable_impl (method, FALSE))
+ /* Already added */
+ continue;
+
+ add_method (acfg, method);
+
+ g_ptr_array_add (acfg->extra_methods, method);
+ g_hash_table_insert (acfg->extra_methods_hash, method, method);
+ }
+}
+
static void
emit_and_reloc_code (MonoAotCompile *acfg, MonoMethod *method, guint8 *code, guint32 code_len, MonoJumpInfo *relocs, gboolean got_only)
{
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) {
- 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));
+ gboolean direct_callable = TRUE;
+
+ if (direct_callable && !(!callee_cfg->has_got_slots && (callee_cfg->method->klass->flags & TYPE_ATTRIBUTE_BEFORE_FIELD_INIT)))
+ direct_callable = FALSE;
+ /*
+ * FIXME: mono_aot_find_jit_info () needs to construct the
+ * MonoMethod belonging to a piece of code, and it can't
+ * do it for generic instances.
+ */
+ if (direct_callable && callee_cfg->method->is_inflated && g_hash_table_lookup (acfg->extra_methods_hash, callee_cfg->method))
+ 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_target = g_strdup_printf (".Lm_%x", get_method_index (acfg, callee_cfg->orig_method));
patch_info->type = MONO_PATCH_INFO_NONE;
acfg->stats.direct_calls ++;
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);
+ g_hash_table_insert (acfg->extra_methods_hash, cfg->orig_method, cfg->orig_method);
+ }
+
acfg->stats.ccount++;
}
}
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_wrapper_info (MonoAotCompile *acfg)
+emit_extra_methods (MonoAotCompile *acfg)
{
- int i, index;
+ int i, table_size, buf_size;
char *symbol;
- char *name;
+ guint8 *p, *buf;
+ guint32 *info_offsets;
+ guint32 hash;
+ GPtrArray *table;
+ HashEntry *entry, *new_entry;
+ int nmethods;
+
+ info_offsets = g_new0 (guint32, acfg->nmethods);
+
+ buf_size = acfg->extra_methods->len * 256 + 256;
+ p = buf = g_malloc (buf_size);
+
+ /* Encode method info */
+ nmethods = 0;
+ /* So offsets are > 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 {
+ 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 */
- symbol = g_strdup_printf ("wrapper_info");
+ symbol = g_strdup_printf ("extra_method_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;
+ emit_bytes (acfg, buf, p - buf);
- for (i = 0; i < acfg->nmethods; ++i) {
- MonoCompile *cfg = acfg->cfgs [i];
- MonoMethod *method;
+ 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 || !cfg->orig_method->wrapper_type)
+ if (!cfg)
continue;
- method = cfg->orig_method;
- index = get_method_index (acfg, method);
+ key = info_offsets [i];
+ value = get_method_index (acfg, method);
+ // FIXME:
+ hash = 0 % table_size;
+
+ /* FIXME: Allocate from the mempool */
+ new_entry = g_new0 (HashEntry, 1);
+ new_entry->key = key;
+ new_entry->value = value;
- // FIXME: Optimize disk usage and lookup speed
- 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);
+ entry = g_ptr_array_index (table, hash);
+ if (entry == NULL) {
+ new_entry->index = hash;
+ g_ptr_array_index (table, hash) = new_entry;
} else {
- name = mono_method_full_name (cfg->orig_method, TRUE);
+ while (entry->next)
+ entry = entry->next;
+
+ entry->next = new_entry;
+ new_entry->index = table->len;
+ g_ptr_array_add (table, new_entry);
}
- emit_string (acfg, name);
- emit_alignment (acfg, 4);
- emit_int32 (acfg, index);
}
- emit_byte (acfg, 0);
+ /* Emit the table */
+ symbol = g_strdup_printf ("extra_method_table");
+ emit_section_change (acfg, ".text", 0);
+ emit_global (acfg, symbol, FALSE);
+ emit_alignment (acfg, 8);
+ emit_label (acfg, symbol);
+ g_free (symbol);
- emit_line (acfg);
+ 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);
+ }
+ }
}
static void
acfg->image = image;
acfg->opts = opts;
acfg->mempool = mono_mempool_new ();
+ acfg->extra_methods = g_ptr_array_new ();
+ acfg->extra_methods_hash = g_hash_table_new (NULL, NULL);
mono_aot_parse_options (aot_options, &acfg->aot_opts);
acfg->method_index ++;
}
+ add_methodspecs (acfg);
+
if (acfg->aot_opts.full_aot)
add_wrappers (acfg);
emit_info (acfg);
- emit_wrapper_info (acfg);
+ emit_extra_methods (acfg);
emit_method_order (acfg);
gpointer *got;
guint32 got_size;
GHashTable *name_cache;
- GHashTable *wrappers;
- /* Maps wrapper names to their method index */
- GHashTable *wrappers_by_name;
- /* Maps wrapper methods to their code */
- GHashTable *wrappers_to_code;
+ GHashTable *extra_methods;
+ /* Maps methods to their code */
+ GHashTable *method_to_code;
MonoAssemblyName *image_names;
char **image_guids;
MonoAssembly *assembly;
guint32 *class_info_offsets;
guint32 *methods_loaded;
guint16 *class_name_table;
- guint8 *wrapper_info;
+ guint32 *extra_method_table;
+ guint8 *extra_method_info;
guint8 *trampolines;
guint32 num_trampolines, first_trampoline_got_offset, trampoline_index;
gpointer *globals;
amodule->got [0] = assembly->image;
amodule->globals = globals;
amodule->sofile = sofile;
+ amodule->method_to_code = g_hash_table_new (mono_aligned_addr_hash, NULL);
sscanf (opt_flags, "%d", &amodule->opts);
find_symbol (sofile, globals, "class_info", (gpointer*)&amodule->class_info);
find_symbol (sofile, globals, "class_info_offsets", (gpointer*)&amodule->class_info_offsets);
find_symbol (sofile, globals, "class_name_table", (gpointer *)&amodule->class_name_table);
+ find_symbol (sofile, globals, "extra_method_table", (gpointer *)&amodule->extra_method_table);
+ find_symbol (sofile, globals, "extra_method_info", (gpointer *)&amodule->extra_method_info);
find_symbol (sofile, globals, "got_info", (gpointer*)&amodule->got_info);
find_symbol (sofile, globals, "got_info_offsets", (gpointer*)&amodule->got_info_offsets);
- find_symbol (sofile, globals, "wrapper_info", (gpointer*)&amodule->wrapper_info);
find_symbol (sofile, globals, "trampolines", (gpointer*)&amodule->trampolines);
find_symbol (sofile, globals, "mem_end", (gpointer*)&amodule->mem_end);
method_index = table [pos];
- /* Might be a wrapper */
- if (aot_module->wrappers) {
+ /* Might be a wrapper/extra method */
+ if (aot_module->extra_methods) {
mono_aot_lock ();
- method = g_hash_table_lookup (aot_module->wrappers, GUINT_TO_POINTER (method_index));
+ method = g_hash_table_lookup (aot_module->extra_methods, GUINT_TO_POINTER (method_index));
mono_aot_unlock ();
} else {
method = NULL;
}
if (!method) {
+ /*
+ * This only works for methods which are in the METHOD table, it can't
+ * handle generic instances/wrappers. This is only a problem for direct
+ * calls, since those bypass mono_get_aot_method (), so we can't add the
+ * method to aot_module->extra_methods.
+ */
+ g_assert (method_index < image->tables [MONO_TABLE_METHOD].rows);
token = mono_metadata_make_token (MONO_TABLE_METHOD, method_index + 1);
method = mono_get_method (image, token, NULL);
}
mono_domain_unlock (domain);
}
+/*
+ * find_extra_method:
+ *
+ * Try finding METHOD in the extra_method table in the AOT image.
+ * Return its method index, or 0xffffff if not found.
+ */
+static guint32
+find_extra_method (MonoDomain *domain, MonoMethod *method)
+{
+ MonoClass *klass = method->klass;
+ MonoAotModule *aot_module = klass->image->aot_module;
+ guint32 table_size, entry_size, hash;
+ guint32 *table, *entry;
+ char *full_name = NULL;
+
+ if (!aot_module)
+ return 0xffffff;
+
+ table_size = aot_module->extra_method_table [0];
+ table = aot_module->extra_method_table + 1;
+ entry_size = 3;
+
+ if (method->wrapper_type) {
+ /* FIXME: This is a hack to work around the fact that runtime invoke wrappers get assigned to some random class */
+ if (method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) {
+ char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
+ full_name = g_strdup_printf ("(wrapper runtime-invoke):%s (%s)", method->name, tmpsig);
+ g_free (tmpsig);
+ } else {
+ full_name = mono_method_full_name (method, TRUE);
+ }
+ }
+
+ // FIXME:
+ hash = 0 % table_size;
+
+ entry = &table [hash * entry_size];
+
+ if (entry [0] != 0) {
+ while (TRUE) {
+ guint32 key = entry [0];
+ guint32 value = entry [1];
+ guint32 next = entry [entry_size - 1];
+ MonoMethod *m;
+ guint8 *p;
+ int is_wrapper;
+
+ // FIXME: Avoid fully decoding the method ref
+ p = aot_module->extra_method_info + key;
+ is_wrapper = decode_value (p, &p);
+ if (method->wrapper_type && is_wrapper) {
+ if (!strcmp (full_name, (char*)p))
+ return value;
+ } else {
+ m = decode_method_ref_2 (aot_module, p, &p);
+ if (m == method)
+ return value;
+ }
+
+ if (next != 0) {
+ entry = &table [next * entry_size];
+ } else {
+ break;
+ }
+ }
+ }
+
+ return 0xffffff;
+}
+
gpointer
mono_aot_get_method (MonoDomain *domain, MonoMethod *method)
{
MonoClass *klass = method->klass;
- guint32 method_index = mono_metadata_token_index (method->token) - 1;
+ guint32 method_index;
guint8 *code, *info;
MonoAotModule *aot_module = klass->image->aot_module;
if (aot_module->out_of_date)
return NULL;
- if (method->is_inflated) {
- if (!mono_method_is_generic_sharable_impl (method, FALSE))
- return NULL;
+ /* Find method index */
+ if (method->is_inflated && mono_method_is_generic_sharable_impl (method, FALSE)) {
method = mono_method_get_declaring_generic_method (method);
- }
-
- if (!method->token) {
- char *full_name;
- char *p;
-
- if (!method->wrapper_type || !aot_module->wrapper_info)
- return NULL;
-
+ method_index = mono_metadata_token_index (method->token) - 1;
+ } else if (method->is_inflated || !method->token) {
+ /* This hash table is used to avoid the slower search in the extra_method_table in the AOT image */
mono_aot_lock ();
-
- /* Avoid doing a hash table lookup using strings if possible */
- if (!aot_module->wrappers_to_code)
- aot_module->wrappers_to_code = g_hash_table_new (mono_aligned_addr_hash, NULL);
- code = g_hash_table_lookup (aot_module->wrappers_to_code, method);
- if (code) {
- mono_aot_unlock ();
- return code;
- }
-
- /* Try to find the wrapper among the wrapper info */
- /* FIXME: This is a hack to work around the fact that runtime invoke wrappers get assigned to some random class */
- if (method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE) {
- char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
- full_name = g_strdup_printf ("(wrapper runtime-invoke):%s (%s)", method->name, tmpsig);
- g_free (tmpsig);
- } else {
- full_name = mono_method_full_name (method, TRUE);
- }
-
- /* Create a hash table to avoid repeated linear searches */
- if (!aot_module->wrappers_by_name) {
- aot_module->wrappers_by_name = g_hash_table_new (g_str_hash, g_str_equal);
- p = (char*)aot_module->wrapper_info;
- while (*p) {
- char *end;
-
- end = p + strlen (p) + 1;
- end = ALIGN_PTR_TO (end, 4);
- method_index = *(guint32*)end;
- end += 4;
- /* The + 1 is used to distinguish a 0 index from a not-found one */
- g_hash_table_insert (aot_module->wrappers_by_name, p, GUINT_TO_POINTER (method_index + 1));
- p = end;
- }
- }
- method_index = GPOINTER_TO_UINT (g_hash_table_lookup (aot_module->wrappers_by_name, full_name));
+ code = g_hash_table_lookup (aot_module->method_to_code, method);
mono_aot_unlock ();
+ if (code)
+ return code;
- g_free (full_name);
- if (!method_index)
+ method_index = find_extra_method (domain, method);
+ if (method_index == 0xffffff)
return NULL;
- method_index --;
-
/* Needed by find_jit_info */
mono_aot_lock ();
- if (!aot_module->wrappers)
- aot_module->wrappers = g_hash_table_new (NULL, NULL);
- g_hash_table_insert (aot_module->wrappers, GUINT_TO_POINTER (method_index), method);
+ if (!aot_module->extra_methods)
+ aot_module->extra_methods = g_hash_table_new (NULL, NULL);
+ g_hash_table_insert (aot_module->extra_methods, GUINT_TO_POINTER (method_index), method);
mono_aot_unlock ();
+ } else {
+ /* Common case */
+ method_index = mono_metadata_token_index (method->token) - 1;
}
if (aot_module->code_offsets [method_index] == 0xffffffff) {
init_plt (aot_module);
if (method->wrapper_type)
- g_hash_table_insert (aot_module->wrappers_to_code, method, code);
+ g_hash_table_insert (aot_module->method_to_code, method, code);
mono_aot_unlock ();
cmethod, MONO_RGCTX_INFO_METHOD_CONTEXT);
} else {
- g_assert (!cfg->compile_aot);
+ // FIXME:
+ cfg->disable_aot = TRUE;
g_assert (cmethod->is_inflated);
EMIT_NEW_PCONST (cfg, imt_arg,
((MonoMethodInflated*)cmethod)->context.method_inst);
case MONO_PATCH_INFO_LDTOKEN:
case MONO_PATCH_INFO_DECLSEC:
if ((ji1->data.token->image != ji2->data.token->image) ||
- (ji1->data.token->token != ji2->data.token->token))
+ (ji1->data.token->token != ji2->data.token->token) ||
+ (ji1->data.token->has_context != ji2->data.token->has_context) ||
+ (ji1->data.token->context.class_inst != ji2->data.token->context.class_inst) ||
+ (ji1->data.token->context.method_inst != ji2->data.token->context.method_inst))
return 0;
break;
default:
#define MONO_FAKE_VTABLE_METHOD ((MonoMethod*)GINT_TO_POINTER(-2))
/* Version number of the AOT file format */
-#define MONO_AOT_FILE_VERSION "40"
+#define MONO_AOT_FILE_VERSION "41"
/* Per-domain information maintained by the JIT */
typedef struct