Merge pull request #2921 from lewurm/lower-typechecks-later
authormonojenkins <jo.shields+jenkins@xamarin.com>
Thu, 9 Jun 2016 23:05:15 +0000 (00:05 +0100)
committerGitHub <noreply@github.com>
Thu, 9 Jun 2016 23:05:15 +0000 (00:05 +0100)
[mini] lower typechecks later

lower typecheck instructions later in the compilation, so we've a chance to do high-level optimization after `method-to-ir`

mono/mini/method-to-ir.c
mono/mini/mini-codegen.c
mono/mini/mini-ops.h
mono/mini/mini-runtime.c
mono/mini/mini.c
mono/mini/mini.h

index b0ca01954dd0902a91f25a02e4eda756f68fef2d..c7f1e40b9cd027a6bde28ed8c89b82997e7eff64 100644 (file)
@@ -4470,14 +4470,19 @@ icall_is_direct_callable (MonoCompile *cfg, MonoMethod *cmethod)
 
 #define is_complex_isinst(klass) ((klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || mono_class_is_marshalbyref (klass) || (klass->flags & TYPE_ATTRIBUTE_SEALED) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
 
+static MonoInst*
+emit_isinst_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args)
+{
+       MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
+       return mono_emit_method_call (cfg, mono_isinst, args, NULL);
+}
+
 static MonoInst*
 emit_castclass_with_cache (MonoCompile *cfg, MonoClass *klass, MonoInst **args)
 {
-       MonoMethod *mono_castclass;
+       MonoMethod *mono_castclass = mono_marshal_get_castclass_with_cache ();
        MonoInst *res;
 
-       mono_castclass = mono_marshal_get_castclass_with_cache ();
-
        save_cast_details (cfg, klass, args [0]->dreg, TRUE);
        res = mono_emit_method_call (cfg, mono_castclass, args, NULL);
        reset_cast_details (cfg);
@@ -4493,6 +4498,22 @@ get_castclass_cache_idx (MonoCompile *cfg)
        return (cfg->method_index << 16) | cfg->castclass_cache_index;
 }
 
+
+static MonoInst*
+emit_isinst_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass)
+{
+       MonoInst *args [3];
+       int idx;
+
+       args [0] = obj; /* obj */
+       EMIT_NEW_CLASSCONST (cfg, args [1], klass); /* klass */
+
+       idx = get_castclass_cache_idx (cfg); /* inline cache*/
+       args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
+
+       return emit_isinst_with_cache (cfg, klass, args);
+}
+
 static MonoInst*
 emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass)
 {
@@ -4517,45 +4538,16 @@ emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass
  * Returns NULL and set the cfg exception on error.
  */
 static MonoInst*
-handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip, int *inline_costs)
+handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_used)
 {
        MonoBasicBlock *is_null_bb;
        int obj_reg = src->dreg;
        int vtable_reg = alloc_preg (cfg);
-       int context_used;
-       MonoInst *klass_inst = NULL, *res;
+       MonoInst *klass_inst = NULL;
 
        if (src->opcode == OP_PCONST && src->inst_p0 == 0)
                return src;
 
-       context_used = mini_class_check_context_used (cfg, klass);
-
-       if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
-               res = emit_castclass_with_cache_nonshared (cfg, src, klass);
-               (*inline_costs) += 2;
-               return res;
-       } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
-               MonoMethod *mono_castclass;
-               MonoInst *iargs [1];
-               int costs;
-
-               mono_castclass = mono_marshal_get_castclass (klass); 
-               iargs [0] = src;
-                               
-               save_cast_details (cfg, klass, src->dreg, TRUE);
-               costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), 
-                                                          iargs, ip, cfg->real_offset, TRUE);
-               reset_cast_details (cfg);
-               CHECK_CFG_EXCEPTION;
-               g_assert (costs > 0);
-                               
-               cfg->real_offset += 5;
-
-               (*inline_costs) += costs;
-
-               return src;
-       }
-
        if (context_used) {
                MonoInst *args [3];
 
@@ -4620,9 +4612,6 @@ handle_castclass (MonoCompile *cfg, MonoClass *klass, MonoInst *src, guint8 *ip,
        reset_cast_details (cfg);
 
        return src;
-
-exception_exit:
-       return NULL;
 }
 
 /*
@@ -4642,21 +4631,15 @@ handle_isinst (MonoCompile *cfg, MonoClass *klass, MonoInst *src, int context_us
                MonoInst *args [3];
 
                if(mini_class_has_reference_variant_generic_argument (cfg, klass, context_used) || is_complex_isinst (klass)) {
-                       MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
-                       MonoInst *cache_ins;
+                       MonoInst *cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
 
-                       cache_ins = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_CAST_CACHE);
-
-                       /* obj */
-                       args [0] = src;
+                       args [0] = src; /* obj */
 
                        /* klass - it's the second element of the cache entry*/
                        EMIT_NEW_LOAD_MEMBASE (cfg, args [1], OP_LOAD_MEMBASE, alloc_preg (cfg), cache_ins->dreg, sizeof (gpointer));
 
-                       /* cache */
-                       args [2] = cache_ins;
-
-                       return mono_emit_method_call (cfg, mono_isinst, args, NULL);
+                       args [2] = cache_ins; /* cache */
+                       return emit_isinst_with_cache (cfg, klass, args);
                }
 
                klass_inst = emit_get_rgctx_klass (cfg, context_used, klass, MONO_RGCTX_INFO_KLASS);
@@ -10958,21 +10941,6 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        break;
                }
                case CEE_CASTCLASS:
-                       CHECK_STACK (1);
-                       --sp;
-                       CHECK_OPSIZE (5);
-                       token = read32 (ip + 1);
-                       klass = mini_get_class (method, token, generic_context);
-                       CHECK_TYPELOAD (klass);
-                       if (sp [0]->type != STACK_OBJ)
-                               UNVERIFIED;
-
-                       ins = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
-                       CHECK_CFG_EXCEPTION;
-
-                       *sp ++ = ins;
-                       ip += 5;
-                       break;
                case CEE_ISINST: {
                        CHECK_STACK (1);
                        --sp;
@@ -10982,53 +10950,19 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        CHECK_TYPELOAD (klass);
                        if (sp [0]->type != STACK_OBJ)
                                UNVERIFIED;
-                       context_used = mini_class_check_context_used (cfg, klass);
-
-                       if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
-                               MonoMethod *mono_isinst = mono_marshal_get_isinst_with_cache ();
-                               MonoInst *args [3];
-                               int idx;
-
-                               /* obj */
-                               args [0] = *sp;
-
-                               /* klass */
-                               EMIT_NEW_CLASSCONST (cfg, args [1], klass);
-
-                               /* inline cache*/
-                               idx = get_castclass_cache_idx (cfg);
-                               args [2] = emit_runtime_constant (cfg, MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
-
-                               *sp++ = mono_emit_method_call (cfg, mono_isinst, args, NULL);
-                               ip += 5;
-                               inline_costs += 2;
-                       } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
-                               MonoMethod *mono_isinst;
-                               MonoInst *iargs [1];
-                               int costs;
-
-                               mono_isinst = mono_marshal_get_isinst (klass); 
-                               iargs [0] = sp [0];
 
-                               costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst), 
-                                                                          iargs, ip, cfg->real_offset, TRUE);
-                               CHECK_CFG_EXCEPTION;
-                               g_assert (costs > 0);
-                               
-                               ip += 5;
-                               cfg->real_offset += 5;
+                       MONO_INST_NEW (cfg, ins, *ip == CEE_ISINST ? OP_ISINST : OP_CASTCLASS);
+                       ins->dreg = alloc_preg (cfg);
+                       ins->sreg1 = (*sp)->dreg;
+                       ins->klass = klass;
+                       ins->type = STACK_OBJ;
+                       MONO_ADD_INS (cfg->cbb, ins);
 
-                               *sp++= iargs [0];
+                       CHECK_CFG_EXCEPTION;
+                       *sp++ = ins;
+                       ip += 5;
 
-                               inline_costs += costs;
-                       }
-                       else {
-                               ins = handle_isinst (cfg, klass, *sp, context_used);
-                               CHECK_CFG_EXCEPTION;
-                               *sp ++ = ins;
-                               ip += 5;
-                       }
+                       cfg->flags |= MONO_CFG_HAS_TYPE_CHECK;
                        break;
                }
                case CEE_UNBOX_ANY: {
@@ -11049,8 +10983,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                res = handle_unbox_gsharedvt (cfg, klass, *sp);
                                inline_costs += 2;
                        } else if (generic_class_is_reference_type (cfg, klass)) {
-                               res = handle_castclass (cfg, klass, *sp, ip, &inline_costs);
-                               CHECK_CFG_EXCEPTION;
+                               MONO_INST_NEW (cfg, res, OP_CASTCLASS);
+                               res->dreg = alloc_preg (cfg);
+                               res->sreg1 = (*sp)->dreg;
+                               res->klass = klass;
+                               res->type = STACK_OBJ;
+                               MONO_ADD_INS (cfg->cbb, res);
+                               cfg->flags |= MONO_CFG_HAS_TYPE_CHECK;
                        } else if (mono_class_is_nullable (klass)) {
                                res = handle_unbox_nullable (cfg, *sp, klass, context_used);
                        } else {
@@ -15052,6 +14991,72 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
        g_free (live_range_end_bb);
 }
 
+static void mono_decompose_typecheck (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins)
+{
+       MonoInst *ret, *move, *source;
+       MonoClass *klass = ins->klass;
+       int context_used = mini_class_check_context_used (cfg, klass);
+       int is_isinst = ins->opcode == OP_ISINST;
+       g_assert (is_isinst || ins->opcode == OP_CASTCLASS);
+       source = get_vreg_to_inst (cfg, ins->sreg1);
+       if (!source || source == (MonoInst *) -1)
+               source = mono_compile_create_var_for_vreg (cfg, &mono_defaults.object_class->byval_arg, OP_LOCAL, ins->sreg1);
+       g_assert (source && source != (MonoInst *) -1);
+
+       MonoBasicBlock *first_bb;
+       NEW_BBLOCK (cfg, first_bb);
+       cfg->cbb = first_bb;
+
+       if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
+               if (is_isinst)
+                       ret = emit_isinst_with_cache_nonshared (cfg, source, klass);
+               else
+                       ret = emit_castclass_with_cache_nonshared (cfg, source, klass);
+       } else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
+               MonoInst *iargs [1];
+               int costs;
+
+               iargs [0] = source;
+               if (is_isinst) {
+                       MonoMethod *wrapper = mono_marshal_get_isinst (klass);
+                       costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, 0, 0, TRUE);
+               } else {
+                       MonoMethod *wrapper = mono_marshal_get_castclass (klass);
+                       save_cast_details (cfg, klass, source->dreg, TRUE);
+                       costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), iargs, 0, 0, TRUE);
+                       reset_cast_details (cfg);
+               }
+               g_assert (costs > 0);
+               ret = iargs [0];
+       } else {
+               if (is_isinst)
+                       ret = handle_isinst (cfg, klass, source, context_used);
+               else
+                       ret = handle_castclass (cfg, klass, source, context_used);
+       }
+       EMIT_NEW_UNALU (cfg, move, OP_MOVE, ins->dreg, ret->dreg);
+
+       g_assert (cfg->cbb->code || first_bb->code);
+       MonoInst *prev = ins->prev;
+       mono_replace_ins (cfg, bb, ins, &prev, first_bb, cfg->cbb);
+}
+
+void mono_decompose_typechecks (MonoCompile *cfg)
+{
+       for (MonoBasicBlock *bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *c;
+               MONO_BB_FOR_EACH_INS (bb, c) {
+                       switch (c->opcode) {
+                       case OP_ISINST:
+                       case OP_CASTCLASS:
+                               mono_decompose_typecheck (cfg, bb, c);
+                               break;
+                       }
+               }
+       }
+}
+
+
 /**
  * FIXME:
  * - use 'iadd' instead of 'int_add'
index 9cf772bad0815ae9571b4417e494ba63e5de439b..57a7a38deef9f7845a8aaf94024d58955698e06e 100644 (file)
@@ -535,6 +535,10 @@ mono_print_ins_index_strbuf (int i, MonoInst *ins)
                case OP_GSHAREDVT_ARG_REGOFFSET:
                        g_string_append_printf (sbuf, " + 0x%lx", (long)ins->inst_offset);
                        break;
+               case OP_ISINST:
+               case OP_CASTCLASS:
+                       g_string_append_printf (sbuf, " %s", ins->klass->name);
+                       break;
                default:
                        break;
                }
index 883fc1af323b2eab7427b492160bef2dda2e8411..4c29d4a0e4dd0e9f5b3be528f88faa68f1be86ba 100644 (file)
@@ -706,6 +706,9 @@ MINI_OP(OP_STRLEN, "strlen", IREG, IREG, NONE)
 MINI_OP(OP_NEWARR, "newarr", IREG, IREG, NONE)
 MINI_OP(OP_LDLEN, "ldlen", IREG, IREG, NONE)
 MINI_OP(OP_BOUNDS_CHECK, "bounds_check", NONE, IREG, IREG)
+/* type checks */
+MINI_OP(OP_ISINST, "isinst", IREG, IREG, NONE)
+MINI_OP(OP_CASTCLASS, "castclass", IREG, IREG, NONE)
 /* get adress of element in a 2D array */
 MINI_OP(OP_LDELEMA2D, "getldelema2", NONE, NONE, NONE)
 /* inlined small memcpy with constant length */
index 6f4082905c5687bf4679fffe2550815663475880..ee5a2920156b495ca71d1fd702d633ebd315f61d 100644 (file)
@@ -3254,6 +3254,7 @@ register_jit_stats (void)
        mono_counters_register ("JIT/liveness_handle_exception_clauses (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_liveness_handle_exception_clauses);
        mono_counters_register ("JIT/handle_out_of_line_bblock (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_handle_out_of_line_bblock);
        mono_counters_register ("JIT/decompose_long_opts (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_decompose_long_opts);
+       mono_counters_register ("JIT/decompose_typechecks (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_decompose_typechecks);
        mono_counters_register ("JIT/local_cprop (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_local_cprop);
        mono_counters_register ("JIT/local_emulate_ops (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_local_emulate_ops);
        mono_counters_register ("JIT/optimize_branches (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_optimize_branches);
index af779644801d323dcd3ca945e6abe73c0aba5ef4..87d58290d812887d0936302d97e708acad7da54a 100644 (file)
@@ -2896,6 +2896,17 @@ is_open_method (MonoMethod *method)
        return FALSE;
 }
 
+static void mono_insert_nop_in_empty_bb (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               if (bb->code)
+                       continue;
+               MonoInst *nop;
+               MONO_INST_NEW (cfg, nop, OP_NOP);
+               MONO_ADD_INS (bb, nop);
+       }
+}
 static void
 mono_create_gc_safepoint (MonoCompile *cfg, MonoBasicBlock *bblock)
 {
@@ -3499,6 +3510,12 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
        MONO_TIME_TRACK (mono_jit_stats.jit_method_to_ir, i = mono_method_to_ir (cfg, method_to_compile, NULL, NULL, NULL, NULL, 0, FALSE));
        mono_cfg_dump_ir (cfg, "method-to-ir");
 
+       if (cfg->gdump_ctx != NULL) {
+               /* workaround for graph visualization, as it doesn't handle empty basic blocks properly */
+               mono_insert_nop_in_empty_bb (cfg);
+               mono_cfg_dump_ir (cfg, "mono_insert_nop_in_empty_bb");
+       }
+
        if (i < 0) {
                if (try_generic_shared && cfg->exception_type == MONO_EXCEPTION_GENERIC_SHARING_FAILED) {
                        if (compile_aot) {
@@ -3577,6 +3594,15 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
                mono_cfg_dump_ir (cfg, "local_cprop");
        }
 
+       if (cfg->flags & MONO_CFG_HAS_TYPE_CHECK) {
+               MONO_TIME_TRACK (mono_jit_stats.jit_decompose_typechecks, mono_decompose_typechecks (cfg));
+               if (cfg->gdump_ctx != NULL) {
+                       /* workaround for graph visualization, as it doesn't handle empty basic blocks properly */
+                       mono_insert_nop_in_empty_bb (cfg);
+               }
+               mono_cfg_dump_ir (cfg, "decompose_typechecks");
+       }
+
        /*
         * Should be done after cprop which can do strength reduction on
         * some of these ops, after propagating immediates.
index 48729fc2eeeac4601b3a06e59a30d06c120fabf4..7f08014eac22d61b3511cd3172767220980e593a 100644 (file)
@@ -1869,7 +1869,8 @@ typedef enum {
        MONO_CFG_HAS_FPOUT    = 1 << 5, /* there are fp values passed in int registers */
        MONO_CFG_HAS_SPILLUP  = 1 << 6, /* spill var slots are allocated from bottom to top */
        MONO_CFG_HAS_CHECK_THIS  = 1 << 7,
-       MONO_CFG_HAS_ARRAY_ACCESS = 1 << 8
+       MONO_CFG_HAS_ARRAY_ACCESS = 1 << 8,
+       MONO_CFG_HAS_TYPE_CHECK = 1 << 9
 } MonoCompileFlags;
 
 typedef struct {
@@ -1903,6 +1904,7 @@ typedef struct {
        double jit_liveness_handle_exception_clauses;
        double jit_handle_out_of_line_bblock;
        double jit_decompose_long_opts;
+       double jit_decompose_typechecks;
        double jit_local_cprop;
        double jit_local_emulate_ops;
        double jit_optimize_branches;
@@ -2855,6 +2857,7 @@ typedef void  (*MonoUnhandledExceptionFunc)         (MonoObject *exc, gpointer u
 MONO_API void mono_install_unhandled_exception_hook (MonoUnhandledExceptionFunc func, gpointer user_data);
 void          mono_invoke_unhandled_exception_hook  (MonoObject *exc);
 
+void mono_decompose_typechecks (MonoCompile *cfg);
 /* Dominator/SSA methods */
 void        mono_compile_dominator_info         (MonoCompile *cfg, int dom_flags);
 void        mono_compute_natural_loops          (MonoCompile *cfg);