From: monojenkins Date: Thu, 9 Jun 2016 23:05:15 +0000 (+0100) Subject: Merge pull request #2921 from lewurm/lower-typechecks-later X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=cadf391beab4c0d0c4aada7acb8fcb7c2e702e87;hp=59ec6fd3d68cc0e7c8085b95a3b436d4c9a3cef4;p=mono.git Merge pull request #2921 from lewurm/lower-typechecks-later [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` --- diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index b0ca01954dd..c7f1e40b9cd 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -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' diff --git a/mono/mini/mini-codegen.c b/mono/mini/mini-codegen.c index 9cf772bad08..57a7a38deef 100644 --- a/mono/mini/mini-codegen.c +++ b/mono/mini/mini-codegen.c @@ -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; } diff --git a/mono/mini/mini-ops.h b/mono/mini/mini-ops.h index 883fc1af323..4c29d4a0e4d 100644 --- a/mono/mini/mini-ops.h +++ b/mono/mini/mini-ops.h @@ -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 */ diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c index 6f4082905c5..ee5a2920156 100644 --- a/mono/mini/mini-runtime.c +++ b/mono/mini/mini-runtime.c @@ -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); diff --git a/mono/mini/mini.c b/mono/mini/mini.c index af779644801..87d58290d81 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -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. diff --git a/mono/mini/mini.h b/mono/mini/mini.h index 48729fc2eee..7f08014eac2 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -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);