This optimization is activated in the ldvirtftn + delegate case, and is deactivated in the case where the architecture does not support it (mono_arch_get_delegate_virtual_invoke_impl return NULL)
{
MonoInst *ptr;
int dreg;
- MonoDelegateTrampInfo *trampoline;
+ gpointer trampoline;
MonoInst *obj, *method_ins, *tramp_ins;
MonoDomain *domain;
guint8 **code_slot;
+
+ if (virtual) {
+ MonoMethod *invoke = mono_get_delegate_invoke (klass);
+ g_assert (invoke);
+
+ if (!mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), context_used ? NULL : method))
+ return NULL;
+ }
obj = handle_alloc (cfg, klass, FALSE, 0);
if (!obj)
/* Set method field */
method_ins = emit_get_rgctx_method (cfg, context_used, method, MONO_RGCTX_INFO_METHOD);
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method), method_ins->dreg);
+
/*
* To avoid looking up the compiled code belonging to the target method
* in mono_delegate_trampoline (), we allocate a per-domain memory slot to
MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_code), code_slot_ins->dreg);
}
- /* Set invoke_impl field */
if (cfg->compile_aot) {
MonoDelegateClassMethodPair *del_tramp;
del_tramp->virtual = virtual;
EMIT_NEW_AOTCONST (cfg, tramp_ins, MONO_PATCH_INFO_DELEGATE_TRAMPOLINE, del_tramp);
} else {
- trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
+ if (virtual)
+ trampoline = mono_create_delegate_virtual_trampoline (cfg->domain, klass, context_used ? NULL : method);
+ else
+ trampoline = mono_create_delegate_trampoline_info (cfg->domain, klass, context_used ? NULL : method);
EMIT_NEW_PCONST (cfg, tramp_ins, trampoline);
}
- dreg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
+ /* Set invoke_impl field */
+ if (virtual) {
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), tramp_ins->dreg);
+ } else {
+ dreg = alloc_preg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, invoke_impl));
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, invoke_impl), dreg);
- dreg = alloc_preg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
+ dreg = alloc_preg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, tramp_ins->dreg, MONO_STRUCT_OFFSET (MonoDelegateTrampInfo, method_ptr));
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, obj->dreg, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr), dreg);
+ }
/* All the checks which are in mono_delegate_ctor () are done by the delegate trampoline */
ensure_method_is_allowed_to_call_method (cfg, method, cmethod, bblock, ip);
}
+ /*
+ * Optimize the common case of ldvirtftn+delegate creation
+ */
+ if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ip > header->code && ip [-1] == CEE_DUP)) {
+ MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (ip + 7), NULL, generic_context);
+ if (ctor_method && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
+ MonoInst *target_ins, *handle_ins;
+ MonoMethod *invoke;
+ int invoke_context_used;
+
+ invoke = mono_get_delegate_invoke (ctor_method->klass);
+ if (!invoke || !mono_method_signature (invoke))
+ LOAD_ERROR;
+
+ invoke_context_used = mini_method_check_context_used (cfg, invoke);
+
+ target_ins = sp [-1];
+
+ if (mono_security_core_clr_enabled ())
+ ensure_method_is_allowed_to_call_method (cfg, method, ctor_method, bblock, ip);
+
+#if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE)
+ /* FIXME: SGEN support */
+ if (invoke_context_used == 0) {
+ ip += 6;
+ if (cfg->verbose_level > 3)
+ g_print ("converting (in B%d: stack: %d) %s", bblock->block_num, (int)(sp - stack_start), mono_disasm_code_one (NULL, method, ip, NULL));
+ if ((handle_ins = handle_delegate_ctor (cfg, ctor_method->klass, target_ins, cmethod, context_used, TRUE))) {
+ sp -= 2;
+ *sp = handle_ins;
+ CHECK_CFG_EXCEPTION;
+ ip += 5;
+ sp ++;
+ break;
+ }
+ ip -= 6;
+ }
+#endif
+ }
+ }
+
--sp;
args [0] = *sp;
return start;
}
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ guint8 *code, *start;
+ int size = 20;
+
+ start = code = mono_global_codeman_reserve (size);
+
+ /* Replace the this argument with the target */
+ amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
+ amd64_mov_reg_membase (code, AMD64_ARG_REG1, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, target), 8);
+
+ if (load_imt_reg) {
+ /* Load the IMT reg */
+ amd64_mov_reg_membase (code, MONO_ARCH_IMT_REG, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, method), 8);
+ }
+
+ /* Load the vtable */
+ amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoObject, vtable), 8);
+ amd64_jump_membase (code, AMD64_RAX, offset);
+
+ return start;
+}
+
void
mono_arch_finish_init (void)
{
return NULL;
}
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ return NULL;
+}
+
gpointer
mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
{
return NULL;
}
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ return NULL;
+}
+
MonoInst*
mono_arch_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
return NULL;
}
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ return NULL;
+}
+
gpointer
mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
{
return start;
}
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ return NULL;
+}
+
gpointer
mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
{
/*========================= End of Function ========================*/
+/*------------------------------------------------------------------*/
+/* */
+/* Name - mono_arch_get_delegate_virtual_invoke_impl. */
+/* */
+/* Function - */
+/* */
+/*------------------------------------------------------------------*/
+
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ return NULL;
+}
+
+/*========================= End of Function ========================*/
+
/*------------------------------------------------------------------*/
/* */
/* Name - mono_arch_build_imt_thunk. */
#endif
}
+gpointer
+mono_create_delegate_virtual_trampoline (MonoDomain *domain, MonoClass *klass, MonoMethod *method)
+{
+#ifdef MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
+ MonoMethod *invoke = mono_get_delegate_invoke (klass);
+ g_assert (invoke);
+
+ return mono_get_delegate_virtual_invoke_impl (mono_method_signature (invoke), method);
+#else
+ return NULL;
+#endif
+}
+
gpointer
mono_create_rgctx_lazy_fetch_trampoline (guint32 offset)
{
return start;
}
+gpointer
+mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg)
+{
+ guint8 *code, *start;
+ int size = 24;
+
+ /*
+ * The stack contains:
+ * <delegate>
+ * <return addr>
+ */
+ start = code = mono_global_codeman_reserve (size);
+
+ /* Replace the this argument with the target */
+ x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
+ x86_mov_reg_membase (code, X86_ECX, X86_EAX, MONO_STRUCT_OFFSET (MonoDelegate, target), 4);
+ x86_mov_membase_reg (code, X86_ESP, 4, X86_ECX, 4);
+
+ if (load_imt_reg) {
+ /* Load the IMT reg */
+ x86_mov_reg_membase (code, MONO_ARCH_IMT_REG, X86_EAX, MONO_STRUCT_OFFSET (MonoDelegate, method), 4);
+ }
+
+ /* Load the vtable */
+ x86_mov_reg_membase (code, X86_EAX, X86_ECX, MONO_STRUCT_OFFSET (MonoObject, vtable), 4);
+ x86_jump_membase (code, X86_EAX, offset);
+
+ return start;
+}
+
mgreg_t
mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
{
case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE: {
MonoDelegateClassMethodPair *del_tramp = patch_info->data.del_tramp;
- target = mono_create_delegate_trampoline_info (domain, del_tramp->klass, del_tramp->method);
+ if (del_tramp->virtual)
+ target = mono_create_delegate_virtual_trampoline (domain, del_tramp->klass, del_tramp->method);
+ else
+ target = mono_create_delegate_trampoline_info (domain, del_tramp->klass, del_tramp->method);
break;
}
case MONO_PATCH_INFO_SFLDA: {
return vtable_trampolines [index];
}
+gpointer
+mono_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method)
+{
+ gboolean is_virtual_generic, is_interface, load_imt_reg;
+ int offset, idx;
+
+ static guint8 **cache = NULL;
+ static int cache_size = 0;
+
+ if (!method)
+ return NULL;
+
+ /* FIXME Support more cases */
+ if (mono_aot_only)
+ return NULL;
+
+ if (MONO_TYPE_ISSTRUCT (sig->ret))
+ return NULL;
+
+ is_virtual_generic = method->is_inflated && mono_method_get_declaring_generic_method (method)->is_generic;
+ is_interface = method->klass->flags & TYPE_ATTRIBUTE_INTERFACE ? TRUE : FALSE;
+ load_imt_reg = is_virtual_generic || is_interface;
+
+ if (is_interface && !is_virtual_generic)
+ offset = ((gint32)mono_method_get_imt_slot (method) - MONO_IMT_SIZE) * SIZEOF_VOID_P;
+ else
+ offset = G_STRUCT_OFFSET (MonoVTable, vtable) + ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
+
+ idx = (offset / SIZEOF_VOID_P + MONO_IMT_SIZE) * 2 + (load_imt_reg ? 1 : 0);
+ g_assert (idx >= 0);
+
+ /* Resize the cache to idx + 1 */
+ if (cache_size < idx + 1) {
+ mono_jit_lock ();
+ if (cache_size < idx + 1) {
+ guint8 **new_cache;
+ int new_cache_size = idx + 1;
+
+ new_cache = g_new0 (guint8*, new_cache_size);
+ if (cache)
+ memcpy (new_cache, cache, cache_size * sizeof (guint8*));
+ g_free (cache);
+
+ mono_memory_barrier ();
+ cache = new_cache;
+ cache_size = new_cache_size;
+ }
+ mono_jit_unlock ();
+ }
+
+ if (cache [idx])
+ return cache [idx];
+
+ return cache [idx] = mono_arch_get_delegate_virtual_invoke_impl (sig, method, offset, load_imt_reg);
+}
+
static gpointer
mini_get_imt_trampoline (int slot_index)
{
gpointer mono_create_jit_trampoline_in_domain (MonoDomain *domain, MonoMethod *method) MONO_LLVM_INTERNAL;
gpointer mono_create_delegate_trampoline (MonoDomain *domain, MonoClass *klass) MONO_INTERNAL;
MonoDelegateTrampInfo* mono_create_delegate_trampoline_info (MonoDomain *domain, MonoClass *klass, MonoMethod *method) MONO_INTERNAL;
+gpointer mono_create_delegate_virtual_trampoline (MonoDomain *domain, MonoClass *klass, MonoMethod *method) MONO_INTERNAL;
gpointer mono_create_rgctx_lazy_fetch_trampoline (guint32 offset) MONO_INTERNAL;
gpointer mono_create_monitor_enter_trampoline (void) MONO_INTERNAL;
gpointer mono_create_monitor_exit_trampoline (void) MONO_INTERNAL;
void mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts) MONO_INTERNAL;
void mono_if_conversion (MonoCompile *cfg) MONO_INTERNAL;
+/* virtual function delegate */
+gpointer mono_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method) MONO_INTERNAL;
+
/* methods that must be provided by the arch-specific port */
void mono_arch_init (void) MONO_INTERNAL;
void mono_arch_finish_init (void) MONO_INTERNAL;
int mono_arch_get_this_arg_reg (guint8 *code) MONO_INTERNAL;
gpointer mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code) MONO_INTERNAL;
gpointer mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gboolean has_target) MONO_INTERNAL;
+gpointer mono_arch_get_delegate_virtual_invoke_impl (MonoMethodSignature *sig, MonoMethod *method, int offset, gboolean load_imt_reg) MONO_INTERNAL;
gpointer mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) MONO_INTERNAL;
void mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg) MONO_INTERNAL;
MonoMethod* mono_arch_find_imt_method (mgreg_t *regs, guint8 *code) MONO_INTERNAL;