Add single entry cache to casts of types with variant generic arguments.
authorRodrigo Kumpera <kumpera@gmail.com>
Thu, 7 Oct 2010 18:33:04 +0000 (15:33 -0300)
committerRodrigo Kumpera <kumpera@gmail.com>
Thu, 7 Oct 2010 20:38:57 +0000 (17:38 -0300)
        * jit-icall.c (mono_object_castclass_with_cache): Check
        single entry cache before calling mono_object_isinst.

        * jit-icall.c (mono_object_isinst_with_cache): Check
        single entry cache before calling mono_object_isinst.
        This uses a positive and negative cache as it is used
        for type queries quite often.

        * jit-icalls.h: Export new icalls.

        * mini.c (mini_init): Register new icalls.

        * method-to-ir.c: Call into cache aware version of type
        test ops for types with variant generic arguments.

        This patch replaces direct calls to mono_object_castclass
        mono_object_isisnt to functions that check a cache first.
        The cache is per managed code callsite so the hit ratio
        is very high.

mono/mini/jit-icalls.c
mono/mini/jit-icalls.h
mono/mini/method-to-ir.c
mono/mini/mini.c

index 81c04514b5abe301205e20bdc9d55a4f17ac1b1e..b332d00204f58dceac179bb0d80705a45db42c8a 100644 (file)
@@ -1033,6 +1033,67 @@ mono_object_castclass (MonoObject *obj, MonoClass *klass)
        return NULL;
 }
 
+MonoObject*
+mono_object_castclass_with_cache (MonoObject *obj, MonoClass *klass, gpointer *cache)
+{
+       MonoJitTlsData *jit_tls = NULL;
+       gpointer cached_vtable, obj_vtable;
+
+       if (mini_get_debug_options ()->better_cast_details) {
+               jit_tls = TlsGetValue (mono_jit_tls_id);
+               jit_tls->class_cast_from = NULL;
+       }
+
+       if (!obj)
+               return NULL;
+
+       cached_vtable = *cache;
+       obj_vtable = obj->vtable;
+
+       if (cached_vtable == obj_vtable)
+               return obj;
+
+       if (mono_object_isinst (obj, klass)) {
+               *cache = obj_vtable;
+               return obj;
+       }
+
+       if (mini_get_debug_options ()->better_cast_details) {
+               jit_tls->class_cast_from = obj->vtable->klass;
+               jit_tls->class_cast_to = klass;
+       }
+
+       mono_raise_exception (mono_exception_from_name (mono_defaults.corlib,
+                                       "System", "InvalidCastException"));
+
+       return NULL;
+}
+
+MonoObject*
+mono_object_isinst_with_cache (MonoObject *obj, MonoClass *klass, gpointer *cache)
+{
+       size_t cached_vtable, obj_vtable;
+
+       if (!obj)
+               return NULL;
+
+       cached_vtable = (size_t)*cache;
+       obj_vtable = (size_t)obj->vtable;
+
+       if ((cached_vtable & ~0x1) == obj_vtable) {
+               return (cached_vtable & 0x1) ? NULL : obj;
+       }
+
+       if (mono_object_isinst (obj, klass)) {
+               *cache = (gpointer)obj_vtable;
+               return obj;
+       } else {
+               /*negative cache*/
+               *cache = (gpointer)(obj_vtable | 0x1);
+               return NULL;
+       }
+}
+
 gpointer
 mono_get_native_calli_wrapper (MonoImage *image, MonoMethodSignature *sig, gpointer func)
 {
index 194a5d91c89392cf1c0cab7a6f74045c6ee79ab1..d0c7214a5ff71da737b0ecd9bec73888c05b318c 100644 (file)
@@ -163,5 +163,11 @@ MonoObject* mono_object_castclass (MonoObject *obj, MonoClass *klass) MONO_INTER
 
 gpointer mono_get_native_calli_wrapper (MonoImage *image, MonoMethodSignature *sig, gpointer func) MONO_INTERNAL;
 
+MonoObject*
+mono_object_isinst_with_cache (MonoObject *obj, MonoClass *klass, gpointer *cache);
+
+MonoObject*
+mono_object_castclass_with_cache (MonoObject *obj, MonoClass *klass, gpointer *cache);
+
 #endif /* __MONO_JIT_ICALLS_H__ */
 
index 9e2d96997eaa4602d0c247fea376adde7b6159e3..b8483c0161fa5bfb05d93a53f1a9e8dceb2168e8 100644 (file)
@@ -3290,17 +3290,21 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used)
 
 
 static gboolean
-mini_class_has_reference_variant_generic_argument (MonoClass *klass)
+mini_class_has_reference_variant_generic_argument (MonoClass *klass, int context_used)
 {
        int i;
        MonoGenericContainer *container;
        MonoGenericInst *ginst;
 
-       if (!klass->generic_class)
+       if (klass->generic_class) {
+               container = klass->generic_class->container_class->generic_container;
+               ginst = klass->generic_class->context.class_inst;
+       } else if (klass->generic_container && context_used) {
+               container = klass->generic_container;
+               ginst = container->context.class_inst;
+       } else {
                return FALSE;
-
-       container = klass->generic_class->container_class->generic_container;
-       ginst = klass->generic_class->context.class_inst;
+       }
 
        for (i = 0; i < container->type_argc; ++i) {
                MonoType *type;
@@ -3309,12 +3313,15 @@ mini_class_has_reference_variant_generic_argument (MonoClass *klass)
                type = ginst->type_argv [i];
                if (MONO_TYPE_IS_REFERENCE (type))
                        return TRUE;
+
+               if (context_used && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
+                       return TRUE;
        }
        return FALSE;
 }
 
 // FIXME: This doesn't work yet (class libs tests fail?)
-#define is_complex_isinst(klass) (TRUE || (klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || klass->marshalbyref || (klass->flags & TYPE_ATTRIBUTE_SEALED) || mini_class_has_reference_variant_generic_argument (klass) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
+#define is_complex_isinst(klass) (TRUE || (klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || klass->marshalbyref || (klass->flags & TYPE_ATTRIBUTE_SEALED) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
 
 /*
  * Returns NULL and set the cfg exception on error.
@@ -7872,8 +7879,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (cfg->generic_sharing_context)
                                context_used = mono_class_check_context_used (klass);
 
-                       if (!context_used && mini_class_has_reference_variant_generic_argument (klass)) {
-                               MonoInst *args [2];
+                       if (!context_used && mini_class_has_reference_variant_generic_argument (klass, context_used)) {
+                               MonoInst *args [3];
 
                                /* obj */
                                args [0] = *sp;
@@ -7881,7 +7888,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                /* klass */
                                EMIT_NEW_CLASSCONST (cfg, args [1], klass);
 
-                               ins = mono_emit_jit_icall (cfg, mono_object_castclass, args);
+                               /* inline cache*/
+                               /*FIXME AOT support*/
+                               EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
+
+
+                               ins = mono_emit_jit_icall (cfg, mono_object_castclass_with_cache, args);
                                *sp ++ = ins;
                                ip += 5;
                                inline_costs += 2;
@@ -7926,8 +7938,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (cfg->generic_sharing_context)
                                context_used = mono_class_check_context_used (klass);
 
-                       if (!context_used && mini_class_has_reference_variant_generic_argument (klass)) {
-                               MonoInst *args [2];
+                       if (!context_used && mini_class_has_reference_variant_generic_argument (klass, context_used)) {
+                               MonoInst *args [3];
 
                                /* obj */
                                args [0] = *sp;
@@ -7935,7 +7947,11 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                /* klass */
                                EMIT_NEW_CLASSCONST (cfg, args [1], klass);
 
-                               *sp = mono_emit_jit_icall (cfg, mono_object_isinst, args);
+                               /* inline cache*/
+                               /*FIXME AOT support*/
+                               EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
+
+                               *sp = mono_emit_jit_icall (cfg, mono_object_isinst_with_cache, args);
                                sp++;
                                ip += 5;
                                inline_costs += 2;
@@ -7983,7 +7999,25 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                        if (generic_class_is_reference_type (cfg, klass)) {
                                /* CASTCLASS FIXME kill this huge slice of duplicated code*/
-                               if (!context_used && (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
+                               if (!context_used && mini_class_has_reference_variant_generic_argument (klass, context_used)) {
+                                       MonoInst *args [3];
+
+                                       /* obj */
+                                       args [0] = *sp;
+
+                                       /* klass */
+                                       EMIT_NEW_CLASSCONST (cfg, args [1], klass);
+
+                                       /* inline cache*/
+                                       /*FIXME AOT support*/
+                                       EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
+
+
+                                       ins = mono_emit_jit_icall (cfg, mono_object_castclass_with_cache, args);
+                                       *sp ++ = ins;
+                                       ip += 5;
+                                       inline_costs += 2;
+                               } else if (!context_used && (klass->marshalbyref || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
                                        MonoMethod *mono_castclass;
                                        MonoInst *iargs [1];
                                        int costs;
index ca0a49430406db09b07b76c308d060cbd278c738..a5f41323ff06ed7d45cc48dc536afcea2dd625d0 100644 (file)
@@ -6164,6 +6164,9 @@ mini_init (const char *filename, const char *runtime_version)
 
        register_icall (mono_gc_wbarrier_value_copy_bitmap, "mono_gc_wbarrier_value_copy_bitmap", "void ptr ptr int int", FALSE);
 
+       register_icall (mono_object_castclass_with_cache, "mono_object_castclass_with_cache", "object object ptr ptr", FALSE);
+       register_icall (mono_object_isinst_with_cache, "mono_object_isinst_with_cache", "object object ptr ptr", FALSE);
+
 #endif
 
        mono_generic_sharing_init ();