This works by using the imt entries in the vtable to store 'imt thunks', which in llvm-only mode, are C functions
which receive an info structure and the imt method and return the real method which needs to be called. Thus
interface calls are compiled into two calls: one to the imt thunk, and one to the method address returned by
the thunk.
target, addr);
}
- *vtable_slot = addr;
-
return addr;
}
}
/*
- * mono_resolve_vcall:
+ * resolve_vcall:
*
- * Return the executable code for calling this_obj->vtable [slot].
+ * Return the executable code for calling vt->vtable [slot].
*/
static gpointer
-resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt)
+resolve_vcall (MonoVTable *vt, int slot, MonoMethod *imt_method, gpointer *out_arg, gboolean gsharedvt)
{
- MonoVTable *vt;
MonoMethod *m, *generic_virtual = NULL;
gpointer addr, compiled_method;
gboolean need_unbox_tramp = FALSE;
// FIXME: Optimize this
- if (!this_obj)
- /* The caller will handle it */
- return NULL;
-
- vt = this_obj->vtable;
-
/* Same as in common_call_trampoline () */
/* Avoid loading metadata or creating a generic vtable if possible */
gpointer
mono_resolve_vcall (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
{
- return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, FALSE);
+ g_assert (this_obj);
+
+ return resolve_vcall (this_obj->vtable, slot, imt_method, out_rgctx_arg, FALSE);
}
gpointer
mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int slot, MonoMethod *imt_method, gpointer *out_rgctx_arg)
{
- return resolve_vcall (this_obj, slot, imt_method, out_rgctx_arg, TRUE);
+ g_assert (this_obj);
+
+ return resolve_vcall (this_obj->vtable, 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)
+mono_init_vtable_slot_vt (MonoVTable *vtable, int slot)
{
- gpointer arg;
- gpointer addr = resolve_vcall (this_obj, slot, NULL, &arg, FALSE);
+ gpointer arg = NULL;
+ gpointer addr;
gpointer *ftnptr;
- ftnptr = mono_domain_alloc0 (this_obj->vtable->domain, 2 * sizeof (gpointer));
+ addr = resolve_vcall (vtable, slot, NULL, &arg, FALSE);
+ ftnptr = mono_domain_alloc0 (vtable->domain, 2 * sizeof (gpointer));
ftnptr [0] = addr;
ftnptr [1] = arg;
mono_memory_barrier ();
- this_obj->vtable->vtable [slot] = ftnptr;
+ vtable->vtable [slot] = ftnptr;
return ftnptr;
}
+/*
+ * 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)
+{
+ return mono_init_vtable_slot_vt (this_obj->vtable, slot);
+}
+
/*
* mono_init_delegate:
*
gpointer mono_resolve_vcall_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg);
+gpointer mono_init_vtable_slot_vt (MonoVTable *vtable, int slot);
+
gpointer mono_init_vtable_slot (MonoObject *this_obj, int slot);
void mono_init_delegate (MonoDelegate *del, MonoObject *target, MonoMethod *method);
static int inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
guchar *ip, guint real_offset, gboolean inline_always);
+static MonoInst*
+emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used, MonoInst **sp, MonoInst *imt_arg);
/* helper methods signatures */
static MonoMethodSignature *helper_sig_domain_get;
static MonoMethodSignature *helper_sig_rgctx_lazy_fetch_trampoline;
+static MonoMethodSignature *helper_sig_llvmonly_imt_thunk;
/*
* Instruction metadata
{
helper_sig_domain_get = mono_create_icall_signature ("ptr");
helper_sig_rgctx_lazy_fetch_trampoline = mono_create_icall_signature ("ptr ptr");
+ helper_sig_llvmonly_imt_thunk = mono_create_icall_signature ("ptr ptr ptr");
}
static MONO_NEVER_INLINE void
if (!sig)
sig = mono_method_signature (method);
- if (cfg->llvm_only && (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
- MonoInst *icall_args [16];
- MonoInst *ins;
-
- // FIXME: Optimize this
-
- guint32 imt_slot = mono_method_get_imt_slot (method);
-
- icall_args [0] = this_ins;
- EMIT_NEW_ICONST (cfg, icall_args [1], imt_slot);
- if (imt_arg) {
- icall_args [2] = imt_arg;
- } else {
- EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, method);
- icall_args [2] = ins;
- }
- EMIT_NEW_PCONST (cfg, icall_args [3], NULL);
-
- call_target = mono_emit_jit_icall (cfg, mono_resolve_iface_call, icall_args);
- }
+ if (cfg->llvm_only && (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE))
+ g_assert_not_reached ();
if (rgctx_arg) {
rgctx_reg = mono_alloc_preg (cfg);
}
#endif
- if (cfg->llvm_only && !call_target && virtual_ && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
- // FIXME: Vcall optimizations below
- MonoInst *icall_args [16];
- MonoInst *ins;
- int rgctx_reg;
-
- if (sig->generic_param_count) {
- /*
- * Generic virtual call, pass the concrete method as the imt argument.
- */
- imt_arg = emit_get_rgctx_method (cfg, context_used,
- method, MONO_RGCTX_INFO_METHOD);
- }
-
- // FIXME: Optimize this
-
- int slot = mono_method_get_vtable_index (method);
-
- icall_args [0] = this_ins;
- EMIT_NEW_ICONST (cfg, icall_args [1], slot);
- if (imt_arg) {
- icall_args [2] = imt_arg;
- } else {
- EMIT_NEW_PCONST (cfg, ins, NULL);
- icall_args [2] = ins;
- }
- rgctx_reg = alloc_preg (cfg);
- MONO_EMIT_NEW_PCONST (cfg, rgctx_reg, NULL);
- EMIT_NEW_VARLOADA_VREG (cfg, icall_args [3], rgctx_reg, &mono_defaults.int_class->byval_arg);
- call_target = mono_emit_jit_icall (cfg, mono_resolve_vcall, icall_args);
- }
+ if (cfg->llvm_only && !call_target && virtual_ && (method->flags & METHOD_ATTRIBUTE_VIRTUAL))
+ return emit_llvmonly_virtual_call (cfg, method, sig, 0, args, NULL);
need_unbox_trampoline = method->klass == mono_defaults.object_class || (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE);
MONO_EMIT_NULL_CHECK (cfg, sp [0]->dreg);
+ // FIXME: Pass a vtable to the icalls
+
if (is_iface)
slot = mono_method_get_imt_slot (cmethod);
else
return emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target);
}
+ if (!fsig->generic_param_count && is_iface && !imt_arg && !(cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig))) {
+ /*
+ * A simple interface call
+ *
+ * We make a call through an imt slot to obtain the function descriptor we need to call.
+ * The imt slot contains a function descriptor for a runtime function + arg.
+ * The slot is already initialized when the vtable is created so there is no need
+ * to check it here.
+ */
+ 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;
+ MonoInst *thunk_addr_ins, *thunk_arg_ins, *ftndesc_ins;
+
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, vtable_reg, this_reg, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ offset = ((gint32)slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
+
+ /* Load the imt slot, which contains a function descriptor. */
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, slot_reg, vtable_reg, offset);
+
+ /* Load the address + arg of the imt thunk from the imt slot */
+ EMIT_NEW_LOAD_MEMBASE (cfg, thunk_addr_ins, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0);
+ EMIT_NEW_LOAD_MEMBASE (cfg, thunk_arg_ins, OP_LOAD_MEMBASE, arg_reg, slot_reg, SIZEOF_VOID_P);
+ /*
+ * IMT thunks in llvm-only mode are C functions which take an info argument
+ * plus the imt method and return the ftndesc to call.
+ */
+ icall_args [0] = thunk_arg_ins;
+ EMIT_NEW_AOTCONST (cfg, ins, MONO_PATCH_INFO_METHODCONST, cmethod);
+ icall_args [1] = ins;
+ ftndesc_ins = mono_emit_calli (cfg, helper_sig_llvmonly_imt_thunk, icall_args, thunk_addr_ins, NULL, NULL);
+
+ return emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins);
+ }
+
// FIXME: Optimize this
icall_args [0] = sp [0];
#endif
+static void
+no_gsharedvt_in_wrapper (void)
+{
+ g_assert_not_reached ();
+}
+
static gpointer
mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException **ex)
{
code = mono_jit_compile_method_inner (method, target_domain, opt, ex);
if (!code && mono_llvm_only) {
+ if (method->wrapper_type == MONO_WRAPPER_UNKNOWN) {
+ WrapperInfo *info = mono_marshal_get_wrapper_info (method);
+
+ if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) {
+ /*
+ * These wrappers are only created for signatures which are in the program, but
+ * sometimes we load methods too eagerly and have to create them even if they
+ * will never be called.
+ */
+ return no_gsharedvt_in_wrapper;
+ }
+ }
+
printf ("AOT method not found in llvmonly mode: %s\n", mono_method_full_name (method, 1));
g_assert_not_reached ();
}
}
}
+typedef struct {
+ MonoVTable *vtable;
+ int slot;
+} IMTThunkInfo;
+
+typedef gpointer (*IMTThunkFunc) (gpointer *arg, MonoMethod *imt_method);
+
+/*
+ * mini_llvmonly_initial_imt_thunk:
+ *
+ * This function is called the first time a call is made through an IMT thunk.
+ * It should have the same signature as the mono_llvmonly_imt_thunk_... functions.
+ */
+static gpointer
+mini_llvmonly_initial_imt_thunk (gpointer *arg, MonoMethod *imt_method)
+{
+ IMTThunkInfo *info = (IMTThunkInfo*)arg;
+ gpointer *imt;
+ gpointer *ftndesc;
+ IMTThunkFunc func;
+
+ mono_vtable_build_imt_slot (info->vtable, info->slot);
+
+ imt = (gpointer*)info->vtable;
+ imt -= MONO_IMT_SIZE;
+
+ /* Return what the real IMT thunk returns */
+ ftndesc = imt [info->slot];
+ func = ftndesc [0];
+ return func ((gpointer *)ftndesc [1], imt_method);
+}
+
+/* This is called indirectly through an imt slot. */
+static gpointer
+mono_llvmonly_imt_thunk (gpointer *arg, MonoMethod *imt_method)
+{
+ int i = 0;
+
+ /* arg points to an array created in mono_llvmonly_get_imt_thunk () */
+ while (arg [i] && arg [i] != imt_method)
+ i += 2;
+ g_assert (arg [i]);
+
+ return arg [i + 1];
+}
+
+/* Optimized versions of mono_llvmonly_imt_thunk () for different table sizes */
+static gpointer
+mono_llvmonly_imt_thunk_1 (gpointer *arg, MonoMethod *imt_method)
+{
+ //g_assert (arg [0] == imt_method);
+ return arg [1];
+}
+
+static gpointer
+mono_llvmonly_imt_thunk_2 (gpointer *arg, MonoMethod *imt_method)
+{
+ //g_assert (arg [0] == imt_method || arg [2] == imt_method);
+ if (arg [0] == imt_method)
+ return arg [1];
+ else
+ return arg [3];
+}
+
+static gpointer
+mono_llvmonly_imt_thunk_3 (gpointer *arg, MonoMethod *imt_method)
+{
+ //g_assert (arg [0] == imt_method || arg [2] == imt_method || arg [4] == imt_method);
+ if (arg [0] == imt_method)
+ return arg [1];
+ else if (arg [2] == imt_method)
+ return arg [3];
+ else
+ return arg [5];
+}
+
+static gpointer
+mono_llvmonly_get_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp)
+{
+ gpointer *buf;
+ gpointer *res;
+ int i, index, real_count;
+
+ /*
+ * Create an array which is passed to the imt thunk functions.
+ * The array contains MonoMethod-function descriptor pairs, terminated by a NULL entry.
+ */
+
+ real_count = 0;
+ for (i = 0; i < count; ++i) {
+ MonoIMTCheckItem *item = imt_entries [i];
+
+ if (item->has_target_code)
+ continue;
+
+ if (item->is_equals)
+ real_count ++;
+ }
+
+ /*
+ * Initialize all vtable entries reachable from this imt slot, so the compiled
+ * code doesn't have to check it.
+ */
+ for (i = 0; i < count; ++i) {
+ MonoIMTCheckItem *item = imt_entries [i];
+ int vt_slot;
+
+ if (!item->is_equals || item->has_target_code)
+ continue;
+ vt_slot = item->value.vtable_slot;
+ mono_init_vtable_slot_vt (vtable, vt_slot);
+ }
+
+ /* Save the entries into an array */
+ buf = (void **)mono_domain_alloc (domain, (real_count + 1) * 2 * sizeof (gpointer));
+ index = 0;
+ for (i = 0; i < count; ++i) {
+ MonoIMTCheckItem *item = imt_entries [i];
+
+ if (!item->is_equals || item->has_target_code)
+ continue;
+
+ g_assert (item->key);
+ g_assert (!item->has_target_code);
+ g_assert (vtable->vtable [item->value.vtable_slot]);
+
+ buf [(index * 2)] = item->key;
+ buf [(index * 2) + 1] = vtable->vtable [item->value.vtable_slot];
+ index ++;
+ }
+ buf [(index * 2)] = NULL;
+ buf [(index * 2) + 1] = fail_tramp;
+
+ /*
+ * Return a function descriptor for a C function with 'buf' as its argument.
+ * It will by called by JITted code.
+ */
+ res = (void **)mono_domain_alloc (domain, 2 * sizeof (gpointer));
+ // FIXME: Add more special cases
+ switch (real_count) {
+ case 1:
+ res [0] = mono_llvmonly_imt_thunk_1;
+ break;
+ case 2:
+ res [0] = mono_llvmonly_imt_thunk_2;
+ break;
+ case 3:
+ res [0] = mono_llvmonly_imt_thunk_3;
+ break;
+ default:
+ res [0] = mono_llvmonly_imt_thunk;
+ break;
+ }
+ res [1] = buf;
+
+ return res;
+}
+
MONO_SIG_HANDLER_FUNC (, mono_sigfpe_signal_handler)
{
MonoException *exc = NULL;
int index = slot_index + MONO_IMT_SIZE;
if (mono_llvm_only) {
- /* Not used */
- if (slot_index < 0)
- /* The vtable/imt construction code in object.c depends on this being non-NULL */
- return no_imt_trampoline;
- else
+ if (slot_index < 0) {
+ /* Initialize the IMT thunks to a 'trampoline' so the generated code doesn't have to initialize it */
+ // FIXME: Memory management
+ gpointer *ftndesc = g_malloc (2 * sizeof (gpointer));
+ IMTThunkInfo *info = g_new0 (IMTThunkInfo, 1);
+ info->vtable = vt;
+ info->slot = index;
+ ftndesc [0] = mini_llvmonly_initial_imt_thunk;
+ ftndesc [1] = info;
+ mono_memory_barrier ();
+ return ftndesc;
+ } else {
return NULL;
+ }
}
g_assert (slot_index >= - MONO_IMT_SIZE);
static gboolean
mini_imt_entry_inited (MonoVTable *vt, int imt_slot_index)
{
+ if (mono_llvm_only)
+ return FALSE;
+
gpointer *imt = (gpointer*)vt;
imt -= MONO_IMT_SIZE;
mono_marshal_use_aot_wrappers (TRUE);
}
- if (mono_aot_only)
+ if (mono_llvm_only) {
+ mono_install_imt_thunk_builder (mono_llvmonly_get_imt_thunk);
+ mono_set_always_build_imt_thunks (TRUE);
+ } else if (mono_aot_only) {
mono_install_imt_thunk_builder (mono_aot_get_imt_thunk);
- else
+ } else {
mono_install_imt_thunk_builder (mono_arch_build_imt_thunk);
+ }
/*Init arch tls information only after the metadata side is inited to make sure we see dynamic appdomain tls keys*/
mono_arch_finish_init ();
/* We can only use the AOT compiled code if we don't require further processing */
lookup_aot = !generic_virtual & !variant_iface;
- mono_vtable_build_imt_slot (vt, mono_method_get_imt_slot (imt_method));
+ if (!mono_llvm_only)
+ mono_vtable_build_imt_slot (vt, mono_method_get_imt_slot (imt_method));
if (imt_method->is_inflated && ((MonoMethodInflated*)imt_method)->context.method_inst) {
MonoError error;