+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];
+
+ if (func == (IMTThunkFunc)mini_llvmonly_initial_imt_thunk)
+ /* Happens when the imt slot contains only a generic virtual method */
+ return NULL;
+ 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];
+}
+
+/*
+ * A version of the imt thunk used for generic virtual methods.
+ * Unlikely a normal imt thunk, its possible that IMT_METHOD is not found
+ * in the search table. The original JIT code had a 'fallback' trampoline it could
+ * call, but we can't do that, so we just return NULL, and the compiled code
+ * will handle it.
+ */
+static gpointer
+mono_llvmonly_generic_virtual_imt_thunk (gpointer *arg, MonoMethod *imt_method)
+{
+ int i = 0;
+
+ while (arg [i] && arg [i] != imt_method)
+ i += 2;
+ if (!arg [i])
+ return NULL;
+
+ return arg [i + 1];
+}
+
+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;
+ gboolean virtual_generic = FALSE;
+
+ /*
+ * 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->is_equals)
+ real_count ++;
+ if (item->has_target_code)
+ virtual_generic = TRUE;
+ }
+
+ /*
+ * 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 (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)
+ continue;
+
+ g_assert (item->key);
+ buf [(index * 2)] = item->key;
+ if (item->has_target_code)
+ buf [(index * 2) + 1] = item->value.target_code;
+ else
+ 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));
+ 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;
+ }
+ if (virtual_generic)
+ res [0] = mono_llvmonly_generic_virtual_imt_thunk;
+ res [1] = buf;
+
+ return res;
+}
+