From 11aca2f9396b0dc068e4aadca7aefb95162f27b1 Mon Sep 17 00:00:00 2001 From: Vlad Brezae Date: Thu, 17 Jan 2019 16:57:15 +0200 Subject: [PATCH] [interp] Improve performance of generic virtual calls Different instantiations of generic virtual methods occupy the same slot in the vtable. We probe them using a single linked list, by the same logic of jit's imt trampolines. --- mono/mini/interp/interp.c | 64 +++++++++++++++++++++++++++++++++++++++++--- mono/mini/interp/transform.c | 4 +-- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/mono/mini/interp/interp.c b/mono/mini/interp/interp.c index eb57c6938b3..ce4ef7db873 100644 --- a/mono/mini/interp/interp.c +++ b/mono/mini/interp/interp.c @@ -505,6 +505,37 @@ get_virtual_method (InterpMethod *imethod, MonoObject *obj) return virtual_imethod; } +typedef struct { + InterpMethod *imethod; + InterpMethod *target_imethod; +} InterpVTableEntry; + +/* domain lock must be held */ +static GSList* +append_imethod (MonoDomain *domain, GSList *list, InterpMethod *imethod, InterpMethod *target_imethod) +{ + GSList *ret; + InterpVTableEntry *entry; + + entry = (InterpVTableEntry*) mono_mempool_alloc (domain->mp, sizeof (InterpVTableEntry)); + entry->imethod = imethod; + entry->target_imethod = target_imethod; + ret = g_slist_append_mempool (domain->mp, list, entry); + + return ret; +} + +static InterpMethod* +get_target_imethod (GSList *list, InterpMethod *imethod) +{ + while (list != NULL) { + InterpVTableEntry *entry = (InterpVTableEntry*) list->data; + if (entry->imethod == imethod) + return entry->target_imethod; + list = list->next; + } + return NULL; +} static InterpMethod* get_virtual_method_fast (MonoObject *obj, InterpMethod *imethod, int slot) @@ -512,6 +543,7 @@ get_virtual_method_fast (MonoObject *obj, InterpMethod *imethod, int slot) gpointer *table = obj->vtable->interp_vtable; if (!table) { + /* Lazily allocate method table */ mono_domain_lock (obj->vtable->domain); table = obj->vtable->interp_vtable; if (!table) { @@ -521,9 +553,35 @@ get_virtual_method_fast (MonoObject *obj, InterpMethod *imethod, int slot) mono_domain_unlock (obj->vtable->domain); } - if (!table [slot]) - table [slot] = get_virtual_method (imethod, obj); - return (InterpMethod*)table[slot]; + if (!table [slot]) { + InterpMethod *target_imethod = get_virtual_method (imethod, obj); + /* Lazily initialize the method table slot */ + mono_domain_lock (obj->vtable->domain); + if (!table [slot]) { + if (imethod->method->is_inflated) + table [slot] = append_imethod (obj->vtable->domain, NULL, imethod, target_imethod); + else + table [slot] = (gpointer) ((gsize)target_imethod | 0x1); + } + mono_domain_unlock (obj->vtable->domain); + } + + if ((gsize)table [slot] & 0x1) { + /* Non generic virtual call. Only one method in slot */ + return (InterpMethod*) ((gsize)table [slot] & ~0x1); + } else { + /* Virtual generic or interface call. Multiple methods in slot */ + InterpMethod *target_imethod = get_target_imethod ((GSList*)table [slot], imethod); + + if (!target_imethod) { + target_imethod = get_virtual_method (imethod, obj); + mono_domain_lock (obj->vtable->domain); + if (!get_target_imethod ((GSList*)table [slot], imethod)) + table [slot] = append_imethod (obj->vtable->domain, (GSList*)table [slot], imethod, target_imethod); + mono_domain_unlock (obj->vtable->domain); + } + return target_imethod; + } } static void inline diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c index 5cf838bcd72..8719b67d4f1 100644 --- a/mono/mini/interp/transform.c +++ b/mono/mini/interp/transform.c @@ -1802,7 +1802,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target #endif if (calli) ADD_CODE(td, native ? ((op != -1) ? MINT_CALLI_NAT_FAST : MINT_CALLI_NAT) : MINT_CALLI); - else if (is_virtual && !mono_class_is_interface (target_method->klass) && !target_method->is_inflated && !mono_class_is_marshalbyref (target_method->klass)) + else if (is_virtual && !mono_class_is_interface (target_method->klass) && !mono_class_is_marshalbyref (target_method->klass)) ADD_CODE(td, is_void ? MINT_VCALLVIRT_FAST : MINT_CALLVIRT_FAST); else if (is_virtual) ADD_CODE(td, is_void ? MINT_VCALLVIRT : MINT_CALLVIRT); @@ -1818,7 +1818,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target return_val_if_nok (error, FALSE); if (csignature->call_convention == MONO_CALL_VARARG) ADD_CODE(td, get_data_item_index (td, (void *)csignature)); - else if (is_virtual && !mono_class_is_interface (target_method->klass) && !target_method->is_inflated && !mono_class_is_marshalbyref (target_method->klass)) + else if (is_virtual && !mono_class_is_interface (target_method->klass) && !mono_class_is_marshalbyref (target_method->klass)) ADD_CODE(td, mono_method_get_vtable_slot (target_method)); } } -- 2.11.4.GIT