static GHashTable *class_init_hash_addr = NULL;
static GHashTable *delegate_trampoline_hash_addr = NULL;
+static GHashTable *rgctx_lazy_fetch_trampoline_hash = NULL;
+static GHashTable *rgctx_lazy_fetch_trampoline_hash_addr = NULL;
#define mono_trampolines_lock() EnterCriticalSection (&trampolines_mutex)
#define mono_trampolines_unlock() LeaveCriticalSection (&trampolines_mutex)
static CRITICAL_SECTION trampolines_mutex;
-static MonoGenericSharingContext*
-get_generic_context (guint8 *code)
+static gpointer
+get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
{
- MonoJitInfo *jit_info = mono_jit_info_table_find (mono_domain_get (), (char*)code);
-
- g_assert (jit_info);
-
- return mono_jit_info_get_generic_sharing_context (jit_info);
+ if (mono_aot_only)
+ return mono_aot_get_unbox_trampoline (m);
+ else
+ return mono_arch_get_unbox_trampoline (gsctx, m, addr);
}
#ifdef MONO_ARCH_HAVE_IMT
static gpointer*
mono_convert_imt_slot_to_vtable_slot (gpointer* slot, gpointer *regs, guint8 *code, MonoMethod *method, MonoMethod **impl_method)
{
- MonoGenericSharingContext *gsctx = get_generic_context (code);
+ MonoGenericSharingContext *gsctx = mono_get_generic_context_from_code (code);
MonoObject *this_argument = mono_arch_find_this_argument (regs, method, gsctx);
MonoVTable *vt = this_argument->vtable;
int displacement = slot - ((gpointer*)vt);
gpointer *vtable_slot;
gboolean generic_shared = FALSE;
MonoMethod *declaring = NULL;
+ int context_used;
#if MONO_ARCH_COMMON_VTABLE_TRAMPOLINE
if (m == MONO_FAKE_VTABLE_METHOD) {
mono_class_setup_vtable (vt->klass);
m = vt->klass->vtable [displacement];
- if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
- m = mono_marshal_get_synchronized_wrapper (m);
/*g_print ("%s with disp %d: %s at %p\n", vt->klass->name, displacement, m->name, code);*/
} else {
/* We got here from an interface method: redirect to IMT handling */
}
#endif
- if (mono_method_check_context_used (m)) {
+ if ((context_used = mono_method_check_context_used (m))) {
MonoClass *klass = NULL;
MonoMethod *actual_method = NULL;
MonoVTable *vt = NULL;
+ MonoGenericInst *method_inst = NULL;
vtable_slot = NULL;
generic_shared = TRUE;
g_assert (code);
+ if (m->is_inflated && mono_method_get_context (m)->method_inst) {
+#ifdef MONO_ARCH_RGCTX_REG
+ MonoMethodRuntimeGenericContext *mrgctx = (MonoMethodRuntimeGenericContext*)mono_arch_find_static_call_vtable ((gpointer*)regs, code);
- if (m->flags & METHOD_ATTRIBUTE_STATIC) {
+ klass = mrgctx->class_vtable->klass;
+ method_inst = mrgctx->method_inst;
+#else
+ g_assert_not_reached ();
+#endif
+ } else if (m->flags & METHOD_ATTRIBUTE_STATIC) {
#ifdef MONO_ARCH_RGCTX_REG
MonoVTable *vtable = mono_arch_find_static_call_vtable ((gpointer*)regs, code);
} else {
#ifdef MONO_ARCH_HAVE_IMT
MonoObject *this_argument = mono_arch_find_this_argument ((gpointer*)regs, m,
- get_generic_context (code));
+ mono_get_generic_context_from_code (code));
vt = this_argument->vtable;
vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
g_assert (displacement > 0);
actual_method = vt->klass->vtable [displacement];
- } else {
- int i;
+ }
+
+ if (method_inst) {
+ MonoGenericContext context = { NULL, NULL };
if (m->is_inflated)
declaring = mono_method_get_declaring_generic_method (m);
else
declaring = m;
- if (klass->generic_class && !klass->methods) {
- /* Avoid calling setup_methods () if possible */
- actual_method = mono_class_inflate_generic_method_full (declaring, klass, mono_class_get_context (klass));
- } else {
- mono_class_setup_methods (klass);
- for (i = 0; i < klass->method.count; ++i) {
- actual_method = klass->methods [i];
- if (actual_method->is_inflated) {
- if (mono_method_get_declaring_generic_method (actual_method) == declaring)
- break;
- }
- }
- }
+ if (klass->generic_class)
+ context.class_inst = klass->generic_class->context.class_inst;
+ else if (klass->generic_container)
+ context.class_inst = klass->generic_container->context.class_inst;
+ context.method_inst = method_inst;
- g_assert (mono_method_get_declaring_generic_method (actual_method) == declaring);
+ actual_method = mono_class_inflate_generic_method (declaring, &context);
+ } else {
+ actual_method = mono_class_get_method_generic (klass, m);
}
- g_assert (actual_method);
+ g_assert (klass);
+ g_assert (actual_method->klass == klass);
+
+ if (actual_method->is_inflated)
+ declaring = mono_method_get_declaring_generic_method (actual_method);
+ else
+ declaring = NULL;
+
m = actual_method;
}
+ if (m->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) {
+ MonoJitInfo *ji;
+
+ if (code)
+ ji = mono_jit_info_table_find (mono_domain_get (), (char*)code);
+ else
+ ji = NULL;
+
+ /* Avoid recursion */
+ if (!(ji && ji->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED))
+ m = mono_marshal_get_synchronized_wrapper (m);
+ }
+
addr = mono_compile_method (m);
g_assert (addr);
mono_debugger_trampoline_compiled (m, addr);
/* the method was jumped to */
- if (!code)
+ if (!code) {
+ MonoDomain *domain = mono_domain_get ();
+
+ /* Patch the got entries pointing to this method */
+ /*
+ * We do this here instead of in mono_codegen () to cover the case when m
+ * was loaded from an aot image.
+ */
+ if (jit_domain_info (domain)->jump_target_got_slot_hash) {
+ GSList *list, *tmp;
+
+ mono_domain_lock (domain);
+ list = g_hash_table_lookup (jit_domain_info (domain)->jump_target_got_slot_hash, m);
+ if (list) {
+ for (tmp = list; tmp; tmp = tmp->next) {
+ gpointer *got_slot = tmp->data;
+ *got_slot = addr;
+ }
+ g_hash_table_remove (jit_domain_info (domain)->jump_target_got_slot_hash, m);
+ g_slist_free (list);
+ }
+ mono_domain_unlock (domain);
+ }
+
return addr;
+ }
vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
if (vtable_slot) {
if (m->klass->valuetype)
- addr = mono_arch_get_unbox_trampoline (m, addr);
+ addr = get_unbox_trampoline (mono_get_generic_context_from_code (code), m, addr);
g_assert (*vtable_slot);
*vtable_slot = mono_get_addr_from_ftnptr (addr);
}
}
- else if (!generic_shared || mono_domain_lookup_shared_generic (mono_domain_get (), declaring)) {
+ else if (!generic_shared || (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
+ mono_domain_lookup_shared_generic (mono_domain_get (), declaring)) {
guint8 *plt_entry = mono_aot_get_plt_entry (code);
+ if (generic_shared)
+ g_assert (mono_method_is_generic_sharable_impl (m, FALSE));
+
/* Patch calling code */
if (plt_entry) {
mono_arch_patch_plt_entry (plt_entry, addr);
if (!method)
method = mono_get_method (image, token, NULL);
if (method->klass->valuetype)
- addr = mono_arch_get_unbox_trampoline (method, addr);
+ addr = get_unbox_trampoline (mono_get_generic_context_from_code (code), method, addr);
}
} else {
/* This is a normal call through a PLT entry */
mono_aot_plt_trampoline (gssize *regs, guint8 *code, guint8 *aot_module,
guint8* tramp)
{
-#ifdef MONO_ARCH_AOT_PLT_OFFSET_REG
- guint32 plt_info_offset = regs [MONO_ARCH_AOT_PLT_OFFSET_REG];
-#else
- guint32 plt_info_offset = -1;
-#endif
+ guint32 plt_info_offset = mono_aot_get_plt_info_offset (regs, code);
return mono_aot_plt_resolve (aot_module, plt_info_offset, code);
}
void
mono_generic_class_init_trampoline (gssize *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
{
- //g_print ("generic class init for class %s.%s\n", vtable->klass->name_space, vtable->klass->name);
+ g_assert (!vtable->initialized);
mono_runtime_class_init (vtable);
-
- //g_print ("done initing generic\n");
}
static gpointer
mono_rgctx_lazy_fetch_trampoline (gssize *regs, guint8 *code, gpointer data, guint8 *tramp)
{
+#ifdef MONO_ARCH_VTABLE_REG
static gboolean inited = FALSE;
static int num_lookups = 0;
-
- guint32 slot = mono_arch_get_rgctx_lazy_fetch_offset ((gpointer*)regs);
+ guint32 slot = GPOINTER_TO_UINT (data);
+ gpointer arg = (gpointer)(gssize)regs [MONO_ARCH_VTABLE_REG];
guint32 index = MONO_RGCTX_SLOT_INDEX (slot);
gboolean mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
num_lookups++;
if (mrgctx)
- return mono_method_fill_runtime_generic_context (data, index);
+ return mono_method_fill_runtime_generic_context (arg, index);
else
- return mono_class_fill_runtime_generic_context (data, index);
+ return mono_class_fill_runtime_generic_context (arg, index);
+#else
+ g_assert_not_reached ();
+#endif
}
#ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
* mono_delegate_trampoline:
*
* This trampoline handles calls made to Delegate:Invoke ().
+ * This is called once the first time a delegate is invoked, so it must be fast.
*/
gpointer
-mono_delegate_trampoline (gssize *regs, guint8 *code, MonoClass *klass, guint8* tramp)
+mono_delegate_trampoline (gssize *regs, guint8 *code, gpointer *tramp_data, guint8* tramp)
{
MonoDomain *domain = mono_domain_get ();
MonoDelegate *delegate;
MonoJitInfo *ji;
- MonoMethod *invoke, *m;
+ MonoMethod *m;
MonoMethod *method = NULL;
gboolean multicast, callvirt;
-
- invoke = mono_get_delegate_invoke (klass);
- g_assert (invoke);
+ MonoMethod *invoke = tramp_data [0];
+ guint8 *impl_this = tramp_data [1];
+ guint8 *impl_nothis = tramp_data [2];
/* Obtain the delegate object according to the calling convention */
- delegate = mono_arch_get_this_arg_from_call (get_generic_context (code), mono_method_signature (invoke), regs, code);
+ /*
+ * Avoid calling mono_get_generic_context_from_code () now since it is expensive,
+ * get_this_arg_from_call will call it if needed.
+ */
+ delegate = mono_arch_get_this_arg_from_call (NULL, mono_method_signature (invoke), regs, code);
if (!delegate->method_ptr && delegate->method) {
/* The delegate was initialized by mini_delegate_ctor */
method = mono_marshal_get_remoting_invoke (method);
else if (mono_method_signature (method)->hasthis && method->klass->valuetype)
method = mono_marshal_get_unbox_wrapper (method);
+ } else if (delegate->method) {
+ method = delegate->method;
} else {
ji = mono_jit_info_table_find (domain, mono_get_addr_from_ftnptr (delegate->method_ptr));
if (ji)
}
callvirt = !delegate->target && method && mono_method_signature (method)->hasthis;
+ if (method && method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
+ method = mono_marshal_get_synchronized_wrapper (method);
+
/*
* If the called address is a trampoline, replace it with the compiled method so
* further calls don't have to go through the trampoline.
*/
if (method && !callvirt) {
- delegate->method_ptr = mono_compile_method (method);
- mono_debugger_trampoline_compiled (method, delegate->method_ptr);
+ /* Avoid the overhead of looking up an already compiled method if possible */
+ if (delegate->method_code && *delegate->method_code) {
+ delegate->method_ptr = *delegate->method_code;
+ } else {
+ delegate->method_ptr = mono_compile_method (method);
+ if (delegate->method_code)
+ *delegate->method_code = delegate->method_ptr;
+ mono_debugger_trampoline_compiled (method, delegate->method_ptr);
+ }
}
multicast = ((MonoMulticastDelegate*)delegate)->prev != NULL;
if (!multicast && !callvirt) {
- code = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), delegate->target != NULL);
+ code = delegate->target ? impl_this : impl_nothis;
if (code) {
delegate->invoke_impl = code;
mono_get_trampoline_func (MonoTrampolineType tramp_type)
{
switch (tramp_type) {
- case MONO_TRAMPOLINE_GENERIC:
+ case MONO_TRAMPOLINE_JIT:
case MONO_TRAMPOLINE_JUMP:
return mono_magic_trampoline;
case MONO_TRAMPOLINE_CLASS_INIT:
{
InitializeCriticalSection (&trampolines_mutex);
- mono_trampoline_code [MONO_TRAMPOLINE_GENERIC] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC);
+ if (mono_aot_only)
+ return;
+
+ mono_trampoline_code [MONO_TRAMPOLINE_JIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JIT);
mono_trampoline_code [MONO_TRAMPOLINE_JUMP] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_JUMP);
mono_trampoline_code [MONO_TRAMPOLINE_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
mono_trampoline_code [MONO_TRAMPOLINE_GENERIC_CLASS_INIT] = mono_arch_create_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
guint8 *
mono_get_trampoline_code (MonoTrampolineType tramp_type)
{
+ g_assert (mono_trampoline_code [tramp_type]);
+
return mono_trampoline_code [tramp_type];
}
+gpointer
+mono_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
+{
+ if (mono_aot_only)
+ return mono_aot_create_specific_trampoline (mono_defaults.corlib, arg1, tramp_type, domain, code_len);
+ else
+ return mono_arch_create_specific_trampoline (arg1, tramp_type, domain, code_len);
+}
+
gpointer
mono_create_class_init_trampoline (MonoVTable *vtable)
{
if (ptr)
return ptr;
- code = mono_arch_create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain, NULL);
+ code = mono_create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain, NULL);
ptr = mono_create_ftnptr (vtable->domain, code);
}
gpointer
-mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method,
- gboolean add_sync_wrapper)
+mono_create_generic_class_init_trampoline (void)
+{
+#ifdef MONO_ARCH_VTABLE_REG
+ static gpointer code;
+
+ mono_trampolines_lock ();
+
+ if (!code)
+ code = mono_arch_create_generic_class_init_trampoline ();
+
+ mono_trampolines_unlock ();
+
+ return code;
+#else
+ g_assert_not_reached ();
+#endif
+}
+
+gpointer
+mono_create_jump_trampoline (MonoDomain *domain, MonoMethod *method, gboolean add_sync_wrapper)
{
MonoJitInfo *ji;
gpointer code;
guint32 code_size = 0;
- if (add_sync_wrapper && method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
- return mono_create_jump_trampoline (domain, mono_marshal_get_synchronized_wrapper (method), FALSE);
-
code = mono_jit_find_compiled_method (domain, method);
if (code)
return code;
if (code)
return code;
- code = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
+ code = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
g_assert (code_size);
mono_domain_lock (domain);
{
gpointer tramp;
+ if (mono_aot_only) {
+ /* Avoid creating trampolines if possible */
+ gpointer code = mono_jit_find_compiled_method (domain, method);
+
+ if (code)
+ return code;
+ }
+
mono_domain_lock (domain);
tramp = g_hash_table_lookup (domain->jit_trampoline_hash, method);
mono_domain_unlock (domain);
if (tramp)
return tramp;
- if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED)
- return mono_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method));
-
- tramp = mono_arch_create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, domain, NULL);
+ tramp = mono_create_specific_trampoline (method, MONO_TRAMPOLINE_JIT, domain, NULL);
mono_domain_lock (domain);
g_hash_table_insert (domain->jit_trampoline_hash, method, tramp);
buf += sizeof (gpointer);
*(guint32*)(gpointer)buf = token;
- tramp = mono_arch_create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain, NULL);
+ tramp = mono_create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain, NULL);
mono_jit_stats.method_trampolines++;
MonoDomain *domain = mono_domain_get ();
gpointer ptr;
guint32 code_size = 0;
+ gpointer *tramp_data;
+ MonoMethod *invoke;
mono_domain_lock (domain);
ptr = g_hash_table_lookup (domain->delegate_trampoline_hash, klass);
if (ptr)
return ptr;
- ptr = mono_arch_create_specific_trampoline (klass, MONO_TRAMPOLINE_DELEGATE, mono_domain_get (), &code_size);
+ // Precompute the delegate invoke impl and pass it to the delegate trampoline
+ invoke = mono_get_delegate_invoke (klass);
+ g_assert (invoke);
+
+ mono_domain_lock (domain );
+ tramp_data = mono_mempool_alloc (domain->mp, sizeof (gpointer) * 3);
+ mono_domain_unlock (domain);
+ tramp_data [0] = invoke;
+ if (mono_aot_only) {
+ tramp_data [1] = NULL;
+ tramp_data [2] = NULL;
+ } else {
+ tramp_data [1] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), TRUE);
+ tramp_data [2] = mono_arch_get_delegate_invoke_impl (mono_method_signature (invoke), FALSE);
+ }
+
+ ptr = mono_create_specific_trampoline (tramp_data, MONO_TRAMPOLINE_DELEGATE, mono_domain_get (), &code_size);
g_assert (code_size);
/* store trampoline address */
#endif
}
+gpointer
+mono_create_rgctx_lazy_fetch_trampoline (guint32 offset)
+{
+ static gboolean inited = FALSE;
+ static int num_trampolines = 0;
+
+ gpointer tramp, ptr;
+
+ if (mono_aot_only)
+ return mono_aot_get_lazy_fetch_trampoline (offset);
+
+ mono_trampolines_lock ();
+ if (rgctx_lazy_fetch_trampoline_hash)
+ tramp = g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset));
+ else
+ tramp = NULL;
+ mono_trampolines_unlock ();
+ if (tramp)
+ return tramp;
+
+ tramp = mono_arch_create_rgctx_lazy_fetch_trampoline (offset);
+ ptr = mono_create_ftnptr (mono_get_root_domain (), tramp);
+
+ mono_trampolines_lock ();
+ if (!rgctx_lazy_fetch_trampoline_hash) {
+ rgctx_lazy_fetch_trampoline_hash = g_hash_table_new (NULL, NULL);
+ rgctx_lazy_fetch_trampoline_hash_addr = g_hash_table_new (NULL, NULL);
+ }
+ g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset), ptr);
+ g_assert (offset != -1);
+ g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash_addr, ptr, GUINT_TO_POINTER (offset + 1));
+ mono_trampolines_unlock ();
+
+ if (!inited) {
+ mono_counters_register ("RGCTX num lazy fetch trampolines",
+ MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_trampolines);
+ inited = TRUE;
+ }
+ num_trampolines++;
+
+ return ptr;
+}
+
MonoVTable*
mono_find_class_init_trampoline_by_addr (gconstpointer addr)
{
mono_trampolines_unlock ();
return res;
}
+
+guint32
+mono_find_rgctx_lazy_fetch_trampoline_by_addr (gconstpointer addr)
+{
+ int offset;
+
+ mono_trampolines_lock ();
+ if (rgctx_lazy_fetch_trampoline_hash_addr) {
+ /* We store the real offset + 1 so we can detect when the lookup fails */
+ offset = GPOINTER_TO_INT (g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash_addr, addr));
+ if (offset)
+ offset -= 1;
+ else
+ offset = -1;
+ } else {
+ offset = -1;
+ }
+ mono_trampolines_unlock ();
+ return offset;
+}