{
MonoVTable *vt;
MonoMethod *m, *generic_virtual = NULL;
- gpointer *vtable_slot;
gpointer addr, compiled_method;
gboolean need_unbox_tramp = FALSE;
vt = this_obj->vtable;
- vtable_slot = &(vt->vtable [slot]);
-
/* Same as in common_call_trampoline () */
/* Avoid loading metadata or creating a generic vtable if possible */
addr = mono_aot_get_method_from_vt_slot (mono_domain_get (), vt, slot);
- if (addr && !vt->klass->valuetype) {
- if (mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
- *vtable_slot = addr;
-
+ if (addr && !vt->klass->valuetype)
return mono_create_ftnptr (mono_domain_get (), addr);
- }
m = mono_class_get_vtable_entry (vt->klass, slot);
// FIXME: Unify this with mono_resolve_iface_call
- *vtable_slot = addr;
-
if (gsharedvt) {
/*
* The callee uses the gsharedvt calling convention, have to add an out wrapper.
return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, TRUE);
}
+/*
+ * mono_init_vtable_slot:
+ *
+ * Initialize slot SLOT of the vtable of THIS_OBJ.
+ * Return the contents of the vtable slot.
+ */
+gpointer
+mono_init_vtable_slot (MonoObject *this_obj, int slot)
+{
+ gpointer arg;
+ gpointer addr = resolve_vcall (this_obj, slot, NULL, &arg, FALSE);
+ gpointer *ftnptr;
+
+ ftnptr = mono_domain_alloc0 (this_obj->vtable->domain, 2 * sizeof (gpointer));
+ ftnptr [0] = addr;
+ ftnptr [1] = arg;
+ mono_memory_barrier ();
+
+ this_obj->vtable->vtable [slot] = ftnptr;
+
+ return ftnptr;
+}
+
/*
* mono_init_delegate:
*
return NULL;
}
+static MonoInst*
+emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp, MonoInst *imt_arg)
+{
+ MonoInst *icall_args [16];
+ MonoInst *call_target, *ins;
+ int arg_reg;
+ gboolean is_iface = cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE;
+ guint32 slot;
+
+ MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
+
+ if (is_iface)
+ slot = mono_method_get_imt_slot (cmethod);
+ else
+ slot = mono_method_get_vtable_index (cmethod);
+
+ if (!fsig->generic_param_count && !is_iface && !imt_arg && !(cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig))) {
+ /*
+ * The simplest case, a normal virtual call.
+ */
+ int this_reg = sp [0]->dreg;
+ int vtable_reg = alloc_preg (cfg);
+ int slot_reg = alloc_preg (cfg);
+ int addr_reg = alloc_preg (cfg);
+ int arg_reg = alloc_preg (cfg);
+ int offset;
+ MonoBasicBlock *non_null_bb;
+
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ offset = MONO_STRUCT_OFFSET (MonoVTable, vtable) + (slot * SIZEOF_VOID_P);
+
+ /* Load the vtable slot, which contains a function descriptor. */
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
+
+ NEW_BBLOCK (cfg, non_null_bb);
+
+ MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_reg, 0);
+ cfg->cbb->last_ins->flags |= MONO_INST_LIKELY;
+ MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, non_null_bb);
+
+ /* Slow path */
+ // FIXME: Make the wrapper use the preserveall cconv
+ // FIXME: Use one icall per slot for small slot numbers ?
+ icall_args [0] = sp [0];
+ EMIT_NEW_ICONST (cfg, icall_args [1], slot);
+ /* Make the icall return the vtable slot value to save some code space */
+ ins = mono_emit_jit_icall (cfg, mono_init_vtable_slot, icall_args);
+ ins->dreg = slot_reg;
+ MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, non_null_bb);
+
+ /* Fastpath */
+ MONO_START_BB (cfg, non_null_bb);
+ /* Load the address + arg from the vtable slot */
+ EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, SIZEOF_VOID_P);
+
+ return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
+ }
+
+ // FIXME: Optimize this
+
+ icall_args [0] = sp [0];
+ EMIT_NEW_ICONST (cfg, icall_args [1], slot);
+
+ if (fsig->generic_param_count) {
+ /* virtual generic call */
+ g_assert (!imt_arg);
+ /* Same as the virtual generic case above */
+ imt_arg = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD);
+ icall_args [2] = imt_arg;
+ } else if (imt_arg) {
+ icall_args [2] = imt_arg;
+ } else {
+ EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, cmethod);
+ icall_args [2] = ins;
+ }
+
+ // FIXME: For generic virtual calls, avoid computing the rgctx twice
+
+ arg_reg = alloc_preg (cfg);
+ MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
+ EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
+
+ if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
+ /*
+ * We handle virtual calls made from gsharedvt methods here instead
+ * of the gsharedvt block above.
+ */
+ if (is_iface)
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
+ else
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
+ } else {
+ if (is_iface)
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
+ else
+ call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
+ }
+
+ /*
+ * Pass the extra argument even if the callee doesn't receive it, most
+ * calling conventions allow this.
+ */
+ return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
+}
+
static gboolean
is_exception_class (MonoClass *klass)
{
* Virtual calls in llvm-only mode.
*/
if (cfg->llvm_only && virtual_ && cmethod && (cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
- MonoInst *icall_args [16];
- MonoInst *call_target;
- int arg_reg;
- gboolean is_iface = cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE;
-
- MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
-
- // FIXME: Optimize this
-
- guint32 slot;
-
- if (is_iface)
- slot = mono_method_get_imt_slot (cmethod);
- else
- slot = mono_method_get_vtable_index (cmethod);
-
- icall_args [0] = sp [0];
- EMIT_NEW_ICONST (cfg, icall_args [1], slot);
-
- if (fsig->generic_param_count) {
- /* virtual generic call */
- g_assert (!imt_arg);
- /* Same as the virtual generic case above */
- imt_arg = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD);
- icall_args [2] = imt_arg;
- } else if (imt_arg) {
- icall_args [2] = imt_arg;
- } else {
- EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, cmethod);
- icall_args [2] = ins;
- }
-
- // FIXME: For generic virtual calls, avoid computing the rgctx twice
-
- arg_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_PCONST (cfg, arg_reg, NULL);
- EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], arg_reg, &mono_defaults.int_class->byval_arg);
-
- if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
- /*
- * We handle virtual calls made from gsharedvt methods here instead
- * of the gsharedvt block above.
- */
- if (is_iface)
- call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call_gsharedvt, icall_args);
- else
- call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall_gsharedvt, icall_args);
- } else {
- if (is_iface)
- call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
- else
- call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
- }
-
- /*
- * Pass the extra argument even if the callee doesn't receive it, most calling
- * calling conventions allow this.
- */
- ins = emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
+ ins = emit_llvmonly_virtual_call (cfg, cmethod, fsig, context_used, sp, imt_arg);
goto call_end;
}
gboolean vretaddr;
LLVMTypeRef llvm_sig;
gpointer target;
- gboolean is_virtual, calli;
+ gboolean is_virtual, calli, preserveall;
LLVMBuilderRef builder = *builder_ref;
if (call->signature->call_convention != MONO_CALL_DEFAULT)
is_virtual = (ins->opcode == OP_VOIDCALL_MEMBASE || ins->opcode == OP_CALL_MEMBASE || ins->opcode == OP_VCALL_MEMBASE || ins->opcode == OP_LCALL_MEMBASE || ins->opcode == OP_FCALL_MEMBASE || ins->opcode == OP_RCALL_MEMBASE);
calli = !call->fptr_is_patch && (ins->opcode == OP_VOIDCALL_REG || ins->opcode == OP_CALL_REG || ins->opcode == OP_VCALL_REG || ins->opcode == OP_LCALL_REG || ins->opcode == OP_FCALL_REG || ins->opcode == OP_RCALL_REG);
+ /* Unused */
+ preserveall = FALSE;
/* FIXME: Avoid creating duplicate methods */
g_assert (!(call->rgctx_arg_reg && call->imt_arg_reg));
if (!sig->pinvoke && !cfg->llvm_only)
LLVMSetInstructionCallConv (lcall, LLVMMono1CallConv);
+ if (preserveall)
+ mono_llvm_set_call_preserveall_cc (lcall);
if (cinfo->ret.storage == LLVMArgVtypeByRef)
LLVMAddInstrAttribute (lcall, 1 + cinfo->vret_arg_pindex, LLVMStructRetAttribute);
case OP_LCOMPARE_IMM:
case OP_COMPARE_IMM: {
CompRelation rel;
- LLVMValueRef cmp;
+ LLVMValueRef cmp, args [16];
+ gboolean likely = (ins->flags & MONO_INST_LIKELY) != 0;
if (ins->next->opcode == OP_NOP)
break;
} else
cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, rhs, "");
+ if (likely) {
+ args [0] = cmp;
+ args [1] = LLVMConstInt (LLVMInt1Type (), 1, FALSE);
+ cmp = LLVMBuildCall (ctx->builder, LLVMGetNamedFunction (ctx->lmodule, "llvm.expect.i1"), args, 2, "");
+ }
+
if (MONO_IS_COND_BRANCH_OP (ins->next)) {
if (ins->next->inst_true_bb == ins->next->inst_false_bb) {
/*
}
AddFunc2 (module, "llvm.expect.i8", LLVMInt8Type (), LLVMInt8Type (), LLVMInt8Type ());
+ AddFunc2 (module, "llvm.expect.i1", LLVMInt1Type (), LLVMInt1Type (), LLVMInt1Type ());
/* EH intrinsics */
{