Hello again,

Here's the patch again, updated with code to keep track of how many
times a generic virtual method is invoked and to insert it in the thunk
only if a threshold (currently 100) is reached.

Mark
Index: metadata/domain.c
===================================================================
--- metadata/domain.c	(revision 114206)
+++ metadata/domain.c	(working copy)
@@ -1971,6 +1971,10 @@
 		g_hash_table_destroy (domain->method_code_hash);
 		domain->method_code_hash = NULL;
 	}
+	if (domain->generic_virtual_cases) {
+		g_hash_table_destroy (domain->generic_virtual_cases);
+		domain->generic_virtual_cases = NULL;
+	}
 
 	DeleteCriticalSection (&domain->assemblies_lock);
 	DeleteCriticalSection (&domain->jit_code_hash_lock);
Index: metadata/object.c
===================================================================
--- metadata/object.c	(revision 114206)
+++ metadata/object.c	(working copy)
@@ -35,6 +35,7 @@
 #include "mono/metadata/mono-debug-debugger.h"
 #include <mono/metadata/gc-internal.h>
 #include <mono/utils/strenc.h>
+#include <mono/utils/mono-counters.h>
 
 #ifdef HAVE_BOEHM_GC
 #define NEED_TO_ZERO_PTRFREE 1
@@ -1049,8 +1050,8 @@
 	}
 
 	entry = malloc (sizeof (MonoImtBuilderEntry));
-	entry->method = method;
-	entry->vtable_slot = vtable_slot;
+	entry->key = method;
+	entry->value.vtable_slot = vtable_slot;
 	entry->next = imt_builder [imt_slot];
 	if (imt_builder [imt_slot] != NULL) {
 		entry->children = imt_builder [imt_slot]->children + 1;
@@ -1092,7 +1093,7 @@
 	MonoImtBuilderEntry *e1 = *(MonoImtBuilderEntry**) p1;
 	MonoImtBuilderEntry *e2 = *(MonoImtBuilderEntry**) p2;
 	
-	return (e1->method < e2->method) ? -1 : ((e1->method > e2->method) ? 1 : 0);
+	return (e1->key < e2->key) ? -1 : ((e1->key > e2->key) ? 1 : 0);
 }
 
 static int
@@ -1104,8 +1105,8 @@
 		int i;
 		for (i = start; i < end; ++i) {
 			MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1);
-			item->method = sorted_array [i]->method;
-			item->vtable_slot = sorted_array [i]->vtable_slot;
+			item->key = sorted_array [i]->key;
+			item->value = sorted_array [i]->value;
 			item->is_equals = TRUE;
 			if (i < end - 1)
 				item->check_target_idx = out_array->len + 1;
@@ -1117,7 +1118,7 @@
 		int middle = start + count / 2;
 		MonoIMTCheckItem *item = g_new0 (MonoIMTCheckItem, 1);
 
-		item->method = sorted_array [middle]->method;
+		item->key = sorted_array [middle]->key;
 		item->is_equals = FALSE;
 		g_ptr_array_add (out_array, item);
 		imt_emit_ir (sorted_array, start, middle, out_array);
@@ -1154,13 +1155,14 @@
 	if (imt_builder_entry != NULL) {
 		if (imt_builder_entry->children == 0) {
 			/* No collision, return the vtable slot contents */
-			return vtable->vtable [imt_builder_entry->vtable_slot];
+			return vtable->vtable [imt_builder_entry->value.vtable_slot];
 		} else {
 			/* Collision, build the thunk */
 			GPtrArray *imt_ir = imt_sort_slot_entries (imt_builder_entry);
 			gpointer result;
 			int i;
-			result = imt_thunk_builder (vtable, domain, (MonoIMTCheckItem**)imt_ir->pdata, imt_ir->len);
+			result = imt_thunk_builder (vtable, domain,
+				(MonoIMTCheckItem**)imt_ir->pdata, imt_ir->len, NULL);
 			for (i = 0; i < imt_ir->len; ++i)
 				g_free (g_ptr_array_index (imt_ir, i));
 			g_ptr_array_free (imt_ir, TRUE);
@@ -1294,6 +1296,241 @@
 	mono_domain_unlock (vtable->domain);
 }
 
+
+/*
+ * The first two free list entries both belong to the wait list: The
+ * first entry is the pointer to the head of the list and the second
+ * entry points to the last element.  That way appending and removing
+ * the first element are both O(1) operations.
+ */
+#define NUM_FREE_LISTS		12
+#define FIRST_FREE_LIST_SIZE	64
+#define MAX_WAIT_LENGTH 	50
+#define THUNK_THRESHOLD		100
+
+/*
+ * LOCKING: The domain lock must be held.
+ */
+static void
+init_thunk_free_lists (MonoDomain *domain)
+{
+	if (domain->thunk_free_lists)
+		return;
+	domain->thunk_free_lists = mono_mempool_alloc0 (domain->mp, sizeof (gpointer) * NUM_FREE_LISTS);
+}
+
+static int
+list_index_for_size (int item_size)
+{
+	int i = 2;
+	int size = FIRST_FREE_LIST_SIZE;
+
+	while (item_size > size && i < NUM_FREE_LISTS - 1) {
+		i++;
+		size <<= 1;
+	}
+
+	return i;
+}
+
+/**
+ * mono_method_alloc_generic_virtual_thunk:
+ * @domain: a domain
+ * @size: size in bytes
+ *
+ * Allocs size bytes to be used for the code of a generic virtual
+ * thunk.  It's either allocated from the domain's code manager or
+ * reused from a previously invalidated piece.
+ *
+ * LOCKING: The domain lock must be held.
+ */
+gpointer
+mono_method_alloc_generic_virtual_thunk (MonoDomain *domain, int size)
+{
+	static gboolean inited = FALSE;
+	static int generic_virtual_thunks_size = 0;
+
+	guint32 *p;
+	int i;
+	MonoThunkFreeList **l;
+
+	init_thunk_free_lists (domain);
+
+	size += sizeof (guint32);
+	if (size < sizeof (MonoThunkFreeList))
+		size = sizeof (MonoThunkFreeList);
+
+	i = list_index_for_size (size);
+	for (l = &domain->thunk_free_lists [i]; *l; l = &(*l)->next) {
+		if ((*l)->size >= size) {
+			MonoThunkFreeList *item = *l;
+			*l = item->next;
+			return ((guint32*)item) + 1;
+		}
+	}
+
+	/* no suitable item found - search lists of larger sizes */
+	while (++i < NUM_FREE_LISTS) {
+		MonoThunkFreeList *item = domain->thunk_free_lists [i];
+		if (!item)
+			continue;
+		g_assert (item->size > size);
+		domain->thunk_free_lists [i] = item->next;
+		return ((guint32*)item) + 1;
+	}
+
+	/* still nothing found - allocate it */
+	if (!inited) {
+		mono_counters_register ("Generic virtual thunk bytes",
+				MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &generic_virtual_thunks_size);
+		inited = TRUE;
+	}
+	generic_virtual_thunks_size += size;
+
+	p = mono_code_manager_reserve (domain->code_mp, size);
+	*p = size;
+
+	return p + 1;
+}
+
+/*
+ * LOCKING: The domain lock must be held.
+ */
+static void
+invalidate_generic_virtual_thunk (MonoDomain *domain, gpointer code)
+{
+	guint32 *p = code;
+	MonoThunkFreeList *l = (MonoThunkFreeList*)(p - 1);
+
+	init_thunk_free_lists (domain);
+
+	while (domain->thunk_free_lists [0] && domain->thunk_free_lists [0]->length >= MAX_WAIT_LENGTH) {
+		MonoThunkFreeList *item = domain->thunk_free_lists [0];
+		int length = item->length;
+		int i;
+
+		/* unlink the first item from the wait list */
+		domain->thunk_free_lists [0] = item->next;
+		domain->thunk_free_lists [0]->length = length - 1;
+
+		i = list_index_for_size (item->size);
+
+		g_print ("putting thunk of size %d in list %d\n", item->size, i);
+
+		/* put it in the free list */
+		item->next = domain->thunk_free_lists [i];
+		domain->thunk_free_lists [i] = item;
+	}
+
+	l->next = NULL;
+	if (domain->thunk_free_lists [1]) {
+		domain->thunk_free_lists [1] = domain->thunk_free_lists [1]->next = l;
+		domain->thunk_free_lists [0]->length++;
+	} else {
+		g_assert (!domain->thunk_free_lists [0]);
+
+		domain->thunk_free_lists [0] = domain->thunk_free_lists [1] = l;
+		domain->thunk_free_lists [0]->length = 1;
+	}
+}
+
+typedef struct _GenericVirtualCase {
+	MonoGenericInst *inst;
+	gpointer code;
+	int count;
+	struct _GenericVirtualCase *next;
+} GenericVirtualCase;
+
+/**
+ * mono_method_add_generic_virtual_case:
+ * @domain: a domain
+ * @vtable_slot: pointer to the vtable slot
+ * @method_inst: the method's method_inst
+ * @code: the method's code
+ *
+ * Registers a call via unmanaged code to a generic virtual method
+ * instantiation.  If the number of calls reaches a threshold
+ * (THUNK_THRESHOLD), the method is added to the vtable slot's generic
+ * virtual method thunk.
+ */
+void
+mono_method_add_generic_virtual_case (MonoDomain *domain, gpointer *vtable_slot,
+	MonoGenericInst *method_inst, gpointer code)
+{
+	static gboolean inited = FALSE;
+	static int num_added = 0;
+
+	GenericVirtualCase *gvc, *list;
+	MonoImtBuilderEntry *entries;
+	int i;
+	GPtrArray *sorted;
+
+	mono_domain_lock (domain);
+	if (!domain->generic_virtual_cases)
+		domain->generic_virtual_cases = g_hash_table_new (mono_aligned_addr_hash, NULL);
+
+	/* Check whether the case was already added */
+	gvc = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
+	while (gvc) {
+		if (gvc->inst == method_inst)
+			break;
+		gvc = gvc->next;
+	}
+
+	/* If not found, make a new one */
+	if (!gvc) {
+		gvc = mono_mempool_alloc (domain->mp, sizeof (GenericVirtualCase));
+		gvc->inst = method_inst;
+		gvc->code = code;
+		gvc->count = 0;
+		gvc->next = g_hash_table_lookup (domain->generic_virtual_cases, vtable_slot);
+
+		g_hash_table_insert (domain->generic_virtual_cases, vtable_slot, gvc);
+
+		if (!inited) {
+			mono_counters_register ("Generic virtual cases", MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_added);
+			inited = TRUE;
+		}
+		num_added++;
+	}
+
+	if (++gvc->count < THUNK_THRESHOLD) {
+		mono_domain_unlock (domain);
+		return;
+	}
+
+	entries = NULL;
+	for (list = gvc; list; list = list->next) {
+		MonoImtBuilderEntry *entry = g_new0 (MonoImtBuilderEntry, 1);
+		entry->key = list->inst;
+		entry->value.target_code = list->code;
+		if (entries)
+			entry->children = entries->children + 1;
+		entry->next = entries;
+		entries = entry;
+	}
+
+	sorted = imt_sort_slot_entries (entries);
+
+	if (*vtable_slot != vtable_trampoline)
+		invalidate_generic_virtual_thunk (domain, *vtable_slot);
+
+	*vtable_slot = imt_thunk_builder (NULL, domain, (MonoIMTCheckItem**)sorted->pdata, sorted->len,
+		vtable_trampoline);
+
+	mono_domain_unlock (domain);
+
+	while (entries) {
+		MonoImtBuilderEntry *next = entries->next;
+		g_free (entries);
+		entries = next;
+	}
+
+	for (i = 0; i < sorted->len; ++i)
+		g_free (g_ptr_array_index (sorted, i));
+	g_ptr_array_free (sorted, TRUE);
+}
+
 static MonoVTable *mono_class_create_runtime_vtable (MonoDomain *domain, MonoClass *class);
 
 /**
Index: metadata/class-internals.h
===================================================================
--- metadata/class-internals.h	(revision 114206)
+++ metadata/class-internals.h	(working copy)
@@ -217,7 +217,8 @@
 	MONO_RGCTX_INFO_METHOD,
 	MONO_RGCTX_INFO_GENERIC_METHOD_CODE,
 	MONO_RGCTX_INFO_CLASS_FIELD,
-	MONO_RGCTX_INFO_METHOD_RGCTX
+	MONO_RGCTX_INFO_METHOD_RGCTX,
+	MONO_RGCTX_INFO_METHOD_CONTEXT
 };
 
 typedef struct _MonoRuntimeGenericContextOtherInfoTemplate {
Index: metadata/object-internals.h
===================================================================
--- metadata/object-internals.h	(revision 114206)
+++ metadata/object-internals.h	(working copy)
@@ -1203,19 +1203,24 @@
 
 #define MONO_IMT_SIZE 19
 
+typedef union {
+	int vtable_slot;
+	gpointer target_code;
+} MonoImtItemValue;
+
 typedef struct _MonoImtBuilderEntry {
-	MonoMethod *method;
+	gpointer key;
 	struct _MonoImtBuilderEntry *next;
-	int vtable_slot;
+	MonoImtItemValue value;
 	int children;
 } MonoImtBuilderEntry;
 
 typedef struct _MonoIMTCheckItem MonoIMTCheckItem;
 
 struct _MonoIMTCheckItem {
-	MonoMethod       *method;
+	gpointer          key;
 	int               check_target_idx;
-	int               vtable_slot;
+	MonoImtItemValue  value;
 	guint8           *jmp_code;
 	guint8           *code_target;
 	guint8            is_equals;
@@ -1224,7 +1229,8 @@
 	guint8            short_branch;
 };
 
-typedef gpointer (*MonoImtThunkBuilder) (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count);
+typedef gpointer (*MonoImtThunkBuilder) (MonoVTable *vtable, MonoDomain *domain,
+		MonoIMTCheckItem **imt_entries, int count, gpointer fail_trunk);
 
 void
 mono_install_imt_thunk_builder (MonoImtThunkBuilder func) MONO_INTERNAL;
@@ -1241,6 +1247,13 @@
 guint32
 mono_method_get_imt_slot (MonoMethod *method) MONO_INTERNAL;
 
+void
+mono_method_add_generic_virtual_case (MonoDomain *domain, gpointer *vtable_slot,
+	MonoGenericInst *method_inst, gpointer code) MONO_INTERNAL;
+
+gpointer
+mono_method_alloc_generic_virtual_thunk (MonoDomain *domain, int size) MONO_INTERNAL;
+
 typedef enum {
 	MONO_UNHANLED_POLICY_LEGACY,
 	MONO_UNHANLED_POLICY_CURRENT
Index: metadata/domain-internals.h
===================================================================
--- metadata/domain-internals.h	(revision 114206)
+++ metadata/domain-internals.h	(working copy)
@@ -141,6 +141,12 @@
 	MONO_APPDOMAIN_UNLOADED
 } MonoAppDomainState;
 
+typedef struct _MonoThunkFreeList {
+	guint32 size;
+	struct _MonoThunkFreeList *next;
+	int length;		/* only valid for the wait list */
+} MonoThunkFreeList;
+
 typedef struct _MonoJitCodeHash MonoJitCodeHash;
 
 struct _MonoDomain {
@@ -217,6 +223,9 @@
 	GHashTable	   *method_rgctx_hash;
 	GHashTable     *method_code_hash;
 
+	GHashTable	   *generic_virtual_cases;
+	MonoThunkFreeList **thunk_free_lists;
+
 	/* Information maintained by the JIT engine */
 	gpointer runtime_info;
 };
Index: metadata/generic-sharing.c
===================================================================
--- metadata/generic-sharing.c	(revision 114206)
+++ metadata/generic-sharing.c	(working copy)
@@ -535,7 +535,8 @@
 
 	case MONO_RGCTX_INFO_METHOD:
 	case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
-	case MONO_RGCTX_INFO_METHOD_RGCTX: {
+	case MONO_RGCTX_INFO_METHOD_RGCTX:
+	case MONO_RGCTX_INFO_METHOD_CONTEXT: {
 		MonoMethod *method = data;
 		MonoMethod *inflated_method;
 		MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
@@ -820,6 +821,14 @@
 		return mono_method_lookup_rgctx (mono_class_vtable (domain, method->method.method.klass),
 			method->context.method_inst);
 	}
+	case MONO_RGCTX_INFO_METHOD_CONTEXT: {
+		MonoMethodInflated *method = data;
+
+		g_assert (method->method.method.is_inflated);
+		g_assert (method->context.method_inst);
+
+		return method->context.method_inst;
+	}
 	default:
 		g_assert_not_reached ();
 	}
@@ -921,6 +930,7 @@
 	case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
 	case MONO_RGCTX_INFO_CLASS_FIELD:
 	case MONO_RGCTX_INFO_METHOD_RGCTX:
+	case MONO_RGCTX_INFO_METHOD_CONTEXT:
 		return data1 == data2;
 	default:
 		g_assert_not_reached ();
Index: mini/method-to-ir.c
===================================================================
--- mini/method-to-ir.c	(revision 114206)
+++ mini/method-to-ir.c	(working copy)
@@ -2372,6 +2372,10 @@
 		} else {
 			slot_reg = vtable_reg;
 			call->inst.inst_offset = G_STRUCT_OFFSET (MonoVTable, vtable) + (method->slot * SIZEOF_VOID_P);
+			if (imt_arg) {
+				g_assert (mono_method_signature (method)->generic_param_count);
+				emit_imt_argument (cfg, call, imt_arg);
+			}
 		}
 
 		call->inst.sreg1 = slot_reg;
@@ -5943,28 +5947,47 @@
 				/* Prevent inlining of methods that contain indirect calls */
 				INLINE_FAILURE;
 
-				this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
-				NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
-				MONO_ADD_INS (bblock, store);
- 
-				/* FIXME: This should be a managed pointer */
-				this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+#if MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK
+				if (!(cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
+					g_assert (!imt_arg);
+					if (context_used) {
+						imt_arg = emit_get_rgctx_method (cfg, context_used,
+							cmethod, MONO_RGCTX_INFO_METHOD_CONTEXT);
 
-				EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
-				if (context_used) {
-					iargs [1] = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
-					EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
-					addr = mono_emit_jit_icall (cfg,
-						mono_helper_compile_generic_method, iargs);
-				} else {
-					EMIT_NEW_METHODCONST (cfg, iargs [1], cmethod);
-					EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
-					addr = mono_emit_jit_icall (cfg, mono_helper_compile_generic_method, iargs);
+					} else {
+						g_assert (cmethod->is_inflated);
+						EMIT_NEW_PCONST (cfg, imt_arg,
+							((MonoMethodInflated*)cmethod)->context.method_inst);
+					}
+					ins = mono_emit_method_call_full (cfg, cmethod, fsig, sp, sp [0], imt_arg);
+				} else
+#endif
+				{
+					this_temp = mono_compile_create_var (cfg, type_from_stack_type (sp [0]), OP_LOCAL);
+					NEW_TEMPSTORE (cfg, store, this_temp->inst_c0, sp [0]);
+					MONO_ADD_INS (bblock, store);
+
+					/* FIXME: This should be a managed pointer */
+					this_arg_temp = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+
+					EMIT_NEW_TEMPLOAD (cfg, iargs [0], this_temp->inst_c0);
+					if (context_used) {
+						iargs [1] = emit_get_rgctx_method (cfg, context_used,
+							cmethod, MONO_RGCTX_INFO_METHOD);
+						EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
+						addr = mono_emit_jit_icall (cfg,
+								mono_helper_compile_generic_method, iargs);
+					} else {
+						EMIT_NEW_METHODCONST (cfg, iargs [1], cmethod);
+						EMIT_NEW_TEMPLOADA (cfg, iargs [2], this_arg_temp->inst_c0);
+						addr = mono_emit_jit_icall (cfg, mono_helper_compile_generic_method, iargs);
+					}
+
+					EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
+
+					ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr);
 				}
 
-				EMIT_NEW_TEMPLOAD (cfg, sp [0], this_arg_temp->inst_c0);
-
-				ins = (MonoInst*)mono_emit_calli (cfg, fsig, sp, addr);
 				if (!MONO_TYPE_IS_VOID (fsig->ret))
 					*sp++ = ins;
 
Index: mini/mini.h
===================================================================
--- mini/mini.h	(revision 114206)
+++ mini/mini.h	(working copy)
@@ -1513,7 +1513,7 @@
 void        mono_arch_emit_imt_argument         (MonoCompile *cfg, MonoCallInst *call, MonoInst *imt_arg) MONO_INTERNAL;
 MonoMethod* mono_arch_find_imt_method           (gpointer *regs, guint8 *code) MONO_INTERNAL;
 MonoVTable* mono_arch_find_static_call_vtable   (gpointer *regs, guint8 *code) MONO_INTERNAL;
-gpointer    mono_arch_build_imt_thunk           (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count) MONO_INTERNAL;
+gpointer    mono_arch_build_imt_thunk           (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp) MONO_INTERNAL;
 void    mono_arch_notify_pending_exc            (void) MONO_INTERNAL;
 void    mono_arch_fixup_jinfo                   (MonoCompile *cfg) MONO_INTERNAL;
 
Index: mini/mini-amd64.c
===================================================================
--- mini/mini-amd64.c	(revision 114206)
+++ mini/mini-amd64.c	(working copy)
@@ -6254,19 +6254,22 @@
  * LOCKING: called with the domain lock held
  */
 gpointer
-mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+	gpointer fail_tramp)
 {
 	int i;
 	int size = 0;
 	guint8 *code, *start;
 	gboolean vtable_is_32bit = ((gsize)(vtable) == (gsize)(int)(gsize)(vtable));
 
+	g_assert (!fail_tramp);
+
 	for (i = 0; i < count; ++i) {
 		MonoIMTCheckItem *item = imt_entries [i];
 		if (item->is_equals) {
 			if (item->check_target_idx) {
 				if (!item->compare_done) {
-					if (amd64_is_imm32 (item->method))
+					if (amd64_is_imm32 (item->key))
 						item->chunk_size += CMP_SIZE;
 					else
 						item->chunk_size += MOV_REG_IMM_SIZE + CMP_REG_REG_SIZE;
@@ -6287,7 +6290,7 @@
 				 */
 			}
 		} else {
-			if (amd64_is_imm32 (item->method))
+			if (amd64_is_imm32 (item->key))
 				item->chunk_size += CMP_SIZE;
 			else
 				item->chunk_size += MOV_REG_IMM_SIZE + CMP_REG_REG_SIZE;
@@ -6304,31 +6307,31 @@
 		if (item->is_equals) {
 			if (item->check_target_idx) {
 				if (!item->compare_done) {
-					if (amd64_is_imm32 (item->method))
-						amd64_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)(gssize)item->method);
+					if (amd64_is_imm32 (item->key))
+						amd64_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)(gssize)item->key);
 					else {
-						amd64_mov_reg_imm (code, AMD64_R10, item->method);
+						amd64_mov_reg_imm (code, AMD64_R10, item->key);
 						amd64_alu_reg_reg (code, X86_CMP, MONO_ARCH_IMT_REG, AMD64_R10);
 					}
 				}
 				item->jmp_code = code;
 				amd64_branch8 (code, X86_CC_NE, 0, FALSE);
 				/* See the comment below about R10 */
-				amd64_mov_reg_imm (code, AMD64_R10, & (vtable->vtable [item->vtable_slot]));
+				amd64_mov_reg_imm (code, AMD64_R10, & (vtable->vtable [item->value.vtable_slot]));
 				amd64_jump_membase (code, AMD64_R10, 0);
 			} else {
 				/* enable the commented code to assert on wrong method */
 #if 0
-				if (amd64_is_imm32 (item->method))
-					amd64_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)(gssize)item->method);
+				if (amd64_is_imm32 (item->key))
+					amd64_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)(gssize)item->key);
 				else {
-					amd64_mov_reg_imm (code, AMD64_R10, item->method);
+					amd64_mov_reg_imm (code, AMD64_R10, item->key);
 					amd64_alu_reg_reg (code, X86_CMP, MONO_ARCH_IMT_REG, AMD64_R10);
 				}
 				item->jmp_code = code;
 				amd64_branch8 (code, X86_CC_NE, 0, FALSE);
 				/* See the comment below about R10 */
-				amd64_mov_reg_imm (code, AMD64_R10, & (vtable->vtable [item->vtable_slot]));
+				amd64_mov_reg_imm (code, AMD64_R10, & (vtable->vtable [item->value.vtable_slot]));
 				amd64_jump_membase (code, AMD64_R10, 0);
 				amd64_patch (item->jmp_code, code);
 				amd64_breakpoint (code);
@@ -6339,15 +6342,15 @@
 				   to be preserved for calls which
 				   require a runtime generic context,
 				   but interface calls don't. */
-				amd64_mov_reg_imm (code, AMD64_R10, & (vtable->vtable [item->vtable_slot]));
+				amd64_mov_reg_imm (code, AMD64_R10, & (vtable->vtable [item->value.vtable_slot]));
 				amd64_jump_membase (code, AMD64_R10, 0);
 #endif
 			}
 		} else {
-			if (amd64_is_imm32 (item->method))
-				amd64_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)(gssize)item->method);
+			if (amd64_is_imm32 (item->key))
+				amd64_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)(gssize)item->key);
 			else {
-				amd64_mov_reg_imm (code, AMD64_R10, item->method);
+				amd64_mov_reg_imm (code, AMD64_R10, item->key);
 				amd64_alu_reg_reg (code, X86_CMP, MONO_ARCH_IMT_REG, AMD64_R10);
 			}
 			item->jmp_code = code;
Index: mini/mini-x86.c
===================================================================
--- mini/mini-x86.c	(revision 114206)
+++ mini/mini-x86.c	(working copy)
@@ -21,6 +21,7 @@
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/mono-debug.h>
 #include <mono/utils/mono-math.h>
+#include <mono/utils/mono-counters.h>
 
 #include "trace.h"
 #include "mini-x86.h"
@@ -4467,7 +4468,8 @@
  * LOCKING: called with the domain lock held
  */
 gpointer
-mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+	gpointer fail_tramp)
 {
 	int i;
 	int size = 0;
@@ -4481,10 +4483,14 @@
 					item->chunk_size += CMP_SIZE;
 				item->chunk_size += BR_SMALL_SIZE + JUMP_IMM_SIZE;
 			} else {
-				item->chunk_size += JUMP_IMM_SIZE;
+				if (fail_tramp) {
+					item->chunk_size += CMP_SIZE + BR_SMALL_SIZE + JUMP_IMM_SIZE * 2;
+				} else {
+					item->chunk_size += JUMP_IMM_SIZE;
 #if ENABLE_WRONG_METHOD_CHECK
-				item->chunk_size += CMP_SIZE + BR_SMALL_SIZE + 1;
+					item->chunk_size += CMP_SIZE + BR_SMALL_SIZE + 1;
 #endif
+				}
 			}
 		} else {
 			item->chunk_size += CMP_SIZE + BR_LARGE_SIZE;
@@ -4492,7 +4498,10 @@
 		}
 		size += item->chunk_size;
 	}
-	code = mono_code_manager_reserve (domain->code_mp, size);
+	if (fail_tramp)
+		code = mono_method_alloc_generic_virtual_thunk (domain, size);
+	else
+		code = mono_code_manager_reserve (domain->code_mp, size);
 	start = code;
 	for (i = 0; i < count; ++i) {
 		MonoIMTCheckItem *item = imt_entries [i];
@@ -4500,26 +4509,39 @@
 		if (item->is_equals) {
 			if (item->check_target_idx) {
 				if (!item->compare_done)
-					x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->method);
+					x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->key);
 				item->jmp_code = code;
 				x86_branch8 (code, X86_CC_NE, 0, FALSE);
-				x86_jump_mem (code, & (vtable->vtable [item->vtable_slot]));
+				if (fail_tramp)
+					x86_jump_code (code, item->value.target_code);
+				else
+					x86_jump_mem (code, & (vtable->vtable [item->value.vtable_slot]));
 			} else {
-				/* enable the commented code to assert on wrong method */
+				if (fail_tramp) {
+					x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->key);
+					item->jmp_code = code;
+					x86_branch8 (code, X86_CC_NE, 0, FALSE);
+					x86_jump_code (code, item->value.target_code);
+					x86_patch (item->jmp_code, code);
+					x86_jump_code (code, fail_tramp);
+					item->jmp_code = NULL;
+				} else {
+					/* enable the commented code to assert on wrong method */
 #if ENABLE_WRONG_METHOD_CHECK
-				x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->method);
-				item->jmp_code = code;
-				x86_branch8 (code, X86_CC_NE, 0, FALSE);
+					x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->key);
+					item->jmp_code = code;
+					x86_branch8 (code, X86_CC_NE, 0, FALSE);
 #endif
-				x86_jump_mem (code, & (vtable->vtable [item->vtable_slot]));
+					x86_jump_mem (code, & (vtable->vtable [item->value.vtable_slot]));
 #if ENABLE_WRONG_METHOD_CHECK
-				x86_patch (item->jmp_code, code);
-				x86_breakpoint (code);
-				item->jmp_code = NULL;
+					x86_patch (item->jmp_code, code);
+					x86_breakpoint (code);
+					item->jmp_code = NULL;
 #endif
+				}
 			}
 		} else {
-			x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->method);
+			x86_alu_reg_imm (code, X86_CMP, MONO_ARCH_IMT_REG, (guint32)item->key);
 			item->jmp_code = code;
 			if (x86_is_imm8 (imt_branch_distance (imt_entries, i, item->check_target_idx)))
 				x86_branch8 (code, X86_CC_GE, 0, FALSE);
@@ -4536,8 +4558,9 @@
 			}
 		}
 	}
-		
-	mono_stats.imt_thunks_size += code - start;
+
+	if (!fail_tramp)
+		mono_stats.imt_thunks_size += code - start;
 	g_assert (code - start <= size);
 	return start;
 }
Index: mini/mini-x86.h
===================================================================
--- mini/mini-x86.h	(revision 114206)
+++ mini/mini-x86.h	(working copy)
@@ -285,6 +285,7 @@
 #define MONO_ARCH_COMMON_VTABLE_TRAMPOLINE 1
 #define MONO_ARCH_RGCTX_REG X86_EDX
 #define MONO_ARCH_ENABLE_NORMALIZE_OPCODES 1
+#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
 
 #define MONO_ARCH_HAVE_CMOV_OPS 1
 
Index: mini/mini-trampolines.c
===================================================================
--- mini/mini-trampolines.c	(revision 114206)
+++ mini/mini-trampolines.c	(working copy)
@@ -106,6 +106,7 @@
 	gpointer *vtable_slot;
 	gboolean generic_shared = FALSE;
 	MonoMethod *declaring = NULL;
+	MonoGenericInst *generic_virtual_method_inst = NULL;
 	int context_used;
 
 #if MONO_ARCH_COMMON_VTABLE_TRAMPOLINE
@@ -158,7 +159,27 @@
 	}
 #endif
 
-	if ((context_used = mono_method_check_context_used (m))) {
+	if (m->is_generic) {
+		MonoGenericContext context = { NULL, NULL };
+		MonoMethod *declaring;
+
+		if (m->is_inflated)
+			declaring = mono_method_get_declaring_generic_method (m);
+		else
+			declaring = m;
+
+		if (m->klass->generic_class)
+			context.class_inst = m->klass->generic_class->context.class_inst;
+		else
+			g_assert (!m->klass->generic_container);
+
+		generic_virtual_method_inst = (MonoGenericInst*)mono_arch_find_imt_method ((gpointer*)regs, code);
+		context.method_inst = generic_virtual_method_inst;
+
+		m = mono_class_inflate_generic_method (declaring, &context);
+		/* FIXME: only do this if the method is sharable */
+		m = mono_marshal_get_static_rgctx_invoke (m);
+	} else if ((context_used = mono_method_check_context_used (m))) {
 		MonoClass *klass = NULL;
 		MonoMethod *actual_method = NULL;
 		MonoVTable *vt = NULL;
@@ -264,6 +285,16 @@
 
 	mono_debugger_trampoline_compiled (m, addr);
 
+	if (generic_virtual_method_inst) {
+		vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
+		g_assert (vtable_slot);
+
+		mono_method_add_generic_virtual_case (mono_domain_get (), vtable_slot,
+			generic_virtual_method_inst, addr);
+
+		return addr;
+	}
+
 	/* the method was jumped to */
 	if (!code) {
 		MonoDomain *domain = mono_domain_get ();
Index: mini/mini-arm.c
===================================================================
--- mini/mini-arm.c	(revision 114206)
+++ mini/mini-arm.c	(working copy)
@@ -4359,16 +4359,19 @@
 }
 
 gpointer
-mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+	gpointer fail_tramp)
 {
 	int size, i, extra_space = 0;
 	arminstr_t *code, *start, *vtable_target = NULL;
 	size = BASE_SIZE;
 
+	g_assert (!fail_tramp);
+
 	for (i = 0; i < count; ++i) {
 		MonoIMTCheckItem *item = imt_entries [i];
 		if (item->is_equals) {
-			g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->vtable_slot])));
+			g_assert (arm_is_imm12 (DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot])));
 
 			if (item->check_target_idx) {
 				if (!item->compare_done)
@@ -4393,7 +4396,7 @@
 	printf ("building IMT thunk for class %s %s entries %d code size %d code at %p end %p vtable %p\n", vtable->klass->name_space, vtable->klass->name, count, size, start, ((guint8*)start) + size, vtable);
 	for (i = 0; i < count; ++i) {
 		MonoIMTCheckItem *item = imt_entries [i];
-		printf ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i, item->method, item->method->name, &vtable->vtable [item->vtable_slot], item->is_equals, item->chunk_size);
+		printf ("method %d (%p) %s vtable slot %p is_equals %d chunk size %d\n", i, item->key, item->key->name, &vtable->vtable [item->value.vtable_slot], item->is_equals, item->chunk_size);
 	}
 #endif
 
@@ -4422,7 +4425,7 @@
 				ARM_B_COND (code, ARMCOND_NE, 0);
 
 				ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
-				ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
+				ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]));
 			} else {
 				/*Enable the commented code to assert on wrong method*/
 #if ENABLE_WRONG_METHOD_CHECK
@@ -4432,7 +4435,7 @@
 				ARM_B_COND (code, ARMCOND_NE, 1);
 #endif
 				ARM_POP2 (code, ARMREG_R0, ARMREG_R1);
-				ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->vtable_slot]));
+				ARM_LDR_IMM (code, ARMREG_PC, ARMREG_IP, DISTANCE (vtable, &vtable->vtable[item->value.vtable_slot]));
 
 #if ENABLE_WRONG_METHOD_CHECK
 				ARM_DBRK (code);
@@ -4440,7 +4443,7 @@
 			}
 
 			if (imt_method)
-				code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->method);
+				code = arm_emit_value_and_patch_ldr (code, imt_method, (guint32)item->key);
 
 			/*must emit after unconditional branch*/
 			if (vtable_target) {
Index: mini/mini-ia64.c
===================================================================
--- mini/mini-ia64.c	(revision 114206)
+++ mini/mini-ia64.c	(working copy)
@@ -4958,13 +4958,16 @@
  * LOCKING: called with the domain lock held
  */
 gpointer
-mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+	gpointer fail_tramp)
 {
 	int i;
 	int size = 0;
 	guint8 *start, *buf;
 	Ia64CodegenState code;
 
+	g_assert (!fail_tramp);
+
 	size = count * 256;
 	buf = g_malloc0 (size);
 	ia64_codegen_init (code, buf);
@@ -4978,13 +4981,13 @@
 		if (item->is_equals) {
 			if (item->check_target_idx) {
 				if (!item->compare_done) {
-					ia64_movl (code, GP_SCRATCH_REG, item->method);
+					ia64_movl (code, GP_SCRATCH_REG, item->key);
 					ia64_cmp_eq (code, 6, 7, IA64_R9, GP_SCRATCH_REG);
 				}
 				item->jmp_code = (guint8*)code.buf + code.nins;
 				ia64_br_cond_pred (code, 7, 0);
 
-				ia64_movl (code, GP_SCRATCH_REG, &(vtable->vtable [item->vtable_slot]));
+				ia64_movl (code, GP_SCRATCH_REG, &(vtable->vtable [item->value.vtable_slot]));
 				ia64_ld8 (code, GP_SCRATCH_REG, GP_SCRATCH_REG);
 				ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG);
 				ia64_br_cond_reg (code, IA64_B6);
@@ -4993,7 +4996,7 @@
 #if ENABLE_WRONG_METHOD_CHECK
 				g_assert_not_reached ();
 #endif
-				ia64_movl (code, GP_SCRATCH_REG, &(vtable->vtable [item->vtable_slot]));
+				ia64_movl (code, GP_SCRATCH_REG, &(vtable->vtable [item->value.vtable_slot]));
 				ia64_ld8 (code, GP_SCRATCH_REG, GP_SCRATCH_REG);
 				ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG);
 				ia64_br_cond_reg (code, IA64_B6);
@@ -5002,7 +5005,7 @@
 #endif
 			}
 		} else {
-			ia64_movl (code, GP_SCRATCH_REG, item->method);
+			ia64_movl (code, GP_SCRATCH_REG, item->key);
 			ia64_cmp_geu (code, 6, 7, IA64_R9, GP_SCRATCH_REG);
 			item->jmp_code = (guint8*)code.buf + code.nins;
 			ia64_br_cond_pred (code, 6, 0);
Index: mini/mini-sparc.c
===================================================================
--- mini/mini-sparc.c	(revision 114206)
+++ mini/mini-sparc.c	(working copy)
@@ -2576,12 +2576,15 @@
  * LOCKING: called with the domain lock held
  */
 gpointer
-mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+	gpointer fail_tramp)
 {
 	int i;
 	int size = 0;
 	guint32 *code, *start;
 
+	g_assert (!fail_tramp);
+
 	for (i = 0; i < count; ++i) {
 		MonoIMTCheckItem *item = imt_entries [i];
 		if (item->is_equals) {
@@ -2610,13 +2613,13 @@
 		if (item->is_equals) {
 			if (item->check_target_idx) {
 				if (!item->compare_done) {
-					sparc_set (code, (guint32)item->method, sparc_g5);
+					sparc_set (code, (guint32)item->key, sparc_g5);
 					sparc_cmp (code, MONO_ARCH_IMT_REG, sparc_g5);
 				}
 				item->jmp_code = (guint8*)code;
 				sparc_branch (code, 0, sparc_bne, 0);
 				sparc_nop (code);
-				sparc_set (code, ((guint32)(&(vtable->vtable [item->vtable_slot]))), sparc_g5);
+				sparc_set (code, ((guint32)(&(vtable->vtable [item->value.vtable_slot]))), sparc_g5);
 				sparc_ld (code, sparc_g5, 0, sparc_g5);
 				sparc_jmpl (code, sparc_g5, sparc_g0, sparc_g0);
 				sparc_nop (code);
@@ -2625,7 +2628,7 @@
 #if ENABLE_WRONG_METHOD_CHECK
 				g_assert_not_reached ();
 #endif
-				sparc_set (code, ((guint32)(&(vtable->vtable [item->vtable_slot]))), sparc_g5);
+				sparc_set (code, ((guint32)(&(vtable->vtable [item->value.vtable_slot]))), sparc_g5);
 				sparc_ld (code, sparc_g5, 0, sparc_g5);
 				sparc_jmpl (code, sparc_g5, sparc_g0, sparc_g0);
 				sparc_nop (code);
@@ -2634,7 +2637,7 @@
 #endif
 			}
 		} else {
-			sparc_set (code, (guint32)item->method, sparc_g5);
+			sparc_set (code, (guint32)item->key, sparc_g5);
 			sparc_cmp (code, MONO_ARCH_IMT_REG, sparc_g5);
 			item->jmp_code = (guint8*)code;
 			sparc_branch (code, 0, sparc_beu, 0);
Index: mini/tramp-x86.c
===================================================================
--- mini/tramp-x86.c	(revision 114206)
+++ mini/tramp-x86.c	(working copy)
@@ -94,7 +94,7 @@
 	} else {
 		printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
 				code [4], code [5], code [6]);
-		g_assert_not_reached ();
+		//g_assert_not_reached ();
 	}
 }
 
Index: mini/mini-ppc.c
===================================================================
--- mini/mini-ppc.c	(revision 114206)
+++ mini/mini-ppc.c	(working copy)
@@ -4671,12 +4671,15 @@
  * LOCKING: called with the domain lock held
  */
 gpointer
-mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count)
+mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count,
+	gpointer fail_tramp)
 {
 	int i;
 	int size = 0;
 	guint8 *code, *start;
 
+	g_assert (!fail_tramp);
+
 	for (i = 0; i < count; ++i) {
 		MonoIMTCheckItem *item = imt_entries [i];
 		if (item->is_equals) {
@@ -4707,23 +4710,23 @@
 		if (item->is_equals) {
 			if (item->check_target_idx) {
 				if (!item->compare_done) {
-					ppc_load (code, ppc_r0, (guint32)item->method);
+					ppc_load (code, ppc_r0, (guint32)item->key);
 					ppc_cmpl (code, 0, 0, MONO_ARCH_IMT_REG, ppc_r0);
 				}
 				item->jmp_code = code;
 				ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
-				ppc_lwz (code, ppc_r0, (sizeof (gpointer) * item->vtable_slot), ppc_r11);
+				ppc_lwz (code, ppc_r0, (sizeof (gpointer) * item->value.vtable_slot), ppc_r11);
 				ppc_mtctr (code, ppc_r0);
 				ppc_bcctr (code, PPC_BR_ALWAYS, 0);
 			} else {
 				/* enable the commented code to assert on wrong method */
 #if ENABLE_WRONG_METHOD_CHECK
-				ppc_load (code, ppc_r0, (guint32)item->method);
+				ppc_load (code, ppc_r0, (guint32)item->key);
 				ppc_cmpl (code, 0, 0, MONO_ARCH_IMT_REG, ppc_r0);
 				item->jmp_code = code;
 				ppc_bc (code, PPC_BR_FALSE, PPC_BR_EQ, 0);
 #endif
-				ppc_lwz (code, ppc_r0, (sizeof (gpointer) * item->vtable_slot), ppc_r11);
+				ppc_lwz (code, ppc_r0, (sizeof (gpointer) * item->value.vtable_slot), ppc_r11);
 				ppc_mtctr (code, ppc_r0);
 				ppc_bcctr (code, PPC_BR_ALWAYS, 0);
 #if ENABLE_WRONG_METHOD_CHECK
@@ -4733,7 +4736,7 @@
 #endif
 			}
 		} else {
-			ppc_load (code, ppc_r0, (guint32)item->method);
+			ppc_load (code, ppc_r0, (guint32)item->key);
 			ppc_cmpl (code, 0, 0, MONO_ARCH_IMT_REG, ppc_r0);
 			item->jmp_code = code;
 			ppc_bc (code, PPC_BR_FALSE, PPC_BR_LT, 0);
_______________________________________________
Mono-devel-list mailing list
[email protected]
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to