[jit] Delay emulation to allow optimizations to happen on these ops
authorVlad Brezae <brezaevlad@gmail.com>
Mon, 21 Dec 2015 12:36:44 +0000 (14:36 +0200)
committerVlad Brezae <brezaevlad@gmail.com>
Thu, 14 Jan 2016 17:34:46 +0000 (19:34 +0200)
Emulation used to happen at emitting time (method_to_ir). Now it is done after the copy propagation pass, where we propagate immediates and can do strength reduction on some of these ops. Because the call to the native function might imply an inlined wrapper which compiles more IL instructions, we need to run the decomposition pass again.

mono/mini/decompose.c
mono/mini/method-to-ir.c
mono/mini/mini.c
mono/mini/mini.h

index 2b7c98772da877eb3b389256799db9bdf2449cb6..fa59554351b26f19abe420ec1bbed7378a375275 100644 (file)
@@ -519,8 +519,6 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
        }
 
        if (emulate) {
-               MonoJitICallInfo *info = NULL;
-
 #if SIZEOF_REGISTER == 8
                if (decompose_long_opcode (cfg, ins, &repl))
                        emulate = FALSE;
@@ -529,33 +527,8 @@ mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins)
                        emulate = FALSE;
 #endif
 
-               if (emulate)
-                       info = mono_find_jit_opcode_emulation (ins->opcode);
-               if (info) {
-                       MonoInst **args;
-                       MonoInst *call;
-
-                       /* Create dummy MonoInst's for the arguments */
-                       g_assert (!info->sig->hasthis);
-                       g_assert (info->sig->param_count <= MONO_MAX_SRC_REGS);
-
-                       args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * info->sig->param_count);
-                       if (info->sig->param_count > 0) {
-                               int sregs [MONO_MAX_SRC_REGS];
-                               int num_sregs, i;
-                               num_sregs = mono_inst_get_src_registers (ins, sregs);
-                               g_assert (num_sregs == info->sig->param_count);
-                               for (i = 0; i < num_sregs; ++i) {
-                                       MONO_INST_NEW (cfg, args [i], OP_ARG);
-                                       args [i]->dreg = sregs [i];
-                               }
-                       }
-
-                       call = mono_emit_jit_icall_by_info (cfg, info, args);
-                       call->dreg = ins->dreg;
-
-                       NULLIFY_INS (ins);
-               }
+               if (emulate && mono_find_jit_opcode_emulation (ins->opcode))
+                       cfg->has_emulated_ops = TRUE;
        }
 
        if (ins->opcode == OP_NOP) {
@@ -1885,4 +1858,88 @@ mono_decompose_soft_float (MonoCompile *cfg)
 
 #endif
 
+void
+mono_local_emulate_ops (MonoCompile *cfg)
+{
+       MonoBasicBlock *bb;
+       gboolean inlined_wrapper = FALSE;
+
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               MonoInst *ins;
+
+               MONO_BB_FOR_EACH_INS (bb, ins) {
+                       int op_noimm = mono_op_imm_to_op (ins->opcode);
+                       MonoJitICallInfo *info;
+
+                       /*
+                        * Emulation can't handle _IMM ops. If this is an imm opcode we need
+                        * to check whether its non-imm counterpart is emulated and, if so,
+                        * decompose it back to its non-imm counterpart.
+                        */
+                       if (op_noimm != -1)
+                               info = mono_find_jit_opcode_emulation (op_noimm);
+                       else
+                               info = mono_find_jit_opcode_emulation (ins->opcode);
+
+                       if (info) {
+                               MonoInst **args;
+                               MonoInst *call;
+                               MonoBasicBlock *first_bb;
+
+                               /* Create dummy MonoInst's for the arguments */
+                               g_assert (!info->sig->hasthis);
+                               g_assert (info->sig->param_count <= MONO_MAX_SRC_REGS);
+
+                               if (op_noimm != -1)
+                                       mono_decompose_op_imm (cfg, bb, ins);
+
+                               args = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst*) * info->sig->param_count);
+                               if (info->sig->param_count > 0) {
+                                       int sregs [MONO_MAX_SRC_REGS];
+                                       int num_sregs, i;
+                                       num_sregs = mono_inst_get_src_registers (ins, sregs);
+                                       g_assert (num_sregs == info->sig->param_count);
+                                       for (i = 0; i < num_sregs; ++i) {
+                                               MONO_INST_NEW (cfg, args [i], OP_ARG);
+                                               args [i]->dreg = sregs [i];
+                                       }
+                               }
+
+                               /* We emit the call on a separate dummy basic block */
+                               cfg->cbb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
+                               first_bb = cfg->cbb;
+
+                               call = mono_emit_jit_icall_by_info (cfg, info, args);
+                               call->dreg = ins->dreg;
+
+                               /* Replace ins with the emitted code and do the necessary bb linking */
+                               if (cfg->cbb->code || (cfg->cbb != first_bb)) {
+                                       MonoInst *saved_prev = ins->prev;
+
+                                       mono_replace_ins (cfg, bb, ins, &ins->prev, first_bb, cfg->cbb);
+                                       first_bb->code = first_bb->last_ins = NULL;
+                                       first_bb->in_count = first_bb->out_count = 0;
+                                       cfg->cbb = first_bb;
+
+                                       /* ins is hanging, continue scanning the emitted code */
+                                       ins = saved_prev;
+                               } else {
+                                       g_error ("Failed to emit emulation code");
+                               }
+                               inlined_wrapper = TRUE;
+                       }
+               }
+       }
+
+       /*
+        * Avoid rerunning these passes by emitting directly the exception checkpoint
+        * at IR level, instead of inlining the icall wrapper. FIXME
+        */
+       if (inlined_wrapper) {
+               mono_decompose_long_opts (cfg);
+               if (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP))
+                       mono_local_cprop (cfg);
+       }
+}
+
 #endif /* DISABLE_JIT */
index c608ae16420ebca699c055b08b46bf3dec4fe74b..3122e5e996dffd95c20d924bbbc4f08117513eaa 100644 (file)
@@ -10453,15 +10453,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                        /* Use the immediate opcodes if possible */
                        if (((sp [1]->opcode == OP_ICONST) || (sp [1]->opcode == OP_I8CONST)) && mono_arch_is_inst_imm (sp [1]->opcode == OP_ICONST ? sp [1]->inst_c0 : sp [1]->inst_l)) {
-                               int imm_opcode;
-
-                               imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
-#if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
-                               /* Keep emulated opcodes which are optimized away later */
-                               if ((ins->opcode == OP_IREM_UN || ins->opcode == OP_IDIV_UN_IMM) && (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP)) && sp [1]->opcode == OP_ICONST && mono_is_power_of_two (sp [1]->inst_c0) >= 0) {
-                                       imm_opcode = mono_op_to_op_imm (ins->opcode);
-                               }
-#endif
+                               int imm_opcode = mono_op_to_op_imm_noemul (ins->opcode);
                                if (imm_opcode != -1) {
                                        ins->opcode = imm_opcode;
                                        if (sp [1]->opcode == OP_I8CONST) {
index c35b520f216f83f2bfab5293a639baa86bb48388..7a490f01304c16d85d0ded4c14478ddc8cbc4bd6 100644 (file)
@@ -3708,7 +3708,12 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
        /* Should be done before branch opts */
        if (cfg->opt & (MONO_OPT_CONSPROP | MONO_OPT_COPYPROP))
                mono_local_cprop (cfg);
-
+       /*
+        * Should be done after cprop which can do strength reduction on
+        * some of these ops, after propagating immediates.
+        */
+       if (cfg->has_emulated_ops)
+               mono_local_emulate_ops (cfg);
        if (cfg->opt & MONO_OPT_BRANCH)
                mono_optimize_branches (cfg);
 
index b3cf227be926112a0f7353c9d908b680ca72c169..de3a7404991aa482f9545b3d464429b8ecee0c11 100644 (file)
@@ -1685,6 +1685,7 @@ typedef struct {
        guint            compute_gc_maps : 1;
        guint            soft_breakpoints : 1;
        guint            arch_eh_jit_info : 1;
+       guint            has_emulated_ops : 1;
        guint            has_indirection : 1;
        guint            has_atomic_add_i4 : 1;
        guint            has_atomic_exchange_i4 : 1;
@@ -2582,6 +2583,7 @@ void              mono_decompose_long_opts (MonoCompile *cfg);
 void              mono_decompose_vtype_opts (MonoCompile *cfg);
 void              mono_decompose_array_access_opts (MonoCompile *cfg);
 void              mono_decompose_soft_float (MonoCompile *cfg);
+void              mono_local_emulate_ops (MonoCompile *cfg);
 void              mono_handle_global_vregs (MonoCompile *cfg);
 void              mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts);
 void              mono_allocate_gsharedvt_vars (MonoCompile *cfg);