]> granicus.if.org Git - php/commitdiff
Remove NOPs after DFA pass.
authorDmitry Stogov <dmitry@zend.com>
Thu, 10 Mar 2016 15:31:02 +0000 (18:31 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 10 Mar 2016 15:31:02 +0000 (18:31 +0300)
ext/opcache/Optimizer/dfa_pass.c

index dac4ab2f45d7a38163902cd3b974e1aed3db15ec..f2a7160cc68026016105d77307e35e605d8b81b8 100644 (file)
@@ -27,6 +27,7 @@
 #include "zend_cfg.h"
 #include "zend_ssa.h"
 #include "zend_func_info.h"
+#include "zend_call_graph.h"
 #include "zend_inference.h"
 #include "zend_dump.h"
 
@@ -112,6 +113,204 @@ int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx,
        return SUCCESS;
 }
 
+static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
+{
+       zend_basic_block *blocks = ssa->cfg.blocks;
+       zend_basic_block *end = blocks + ssa->cfg.blocks_count;
+       zend_basic_block *b;
+       zend_func_info *func_info;
+       int j;
+       uint32_t i;
+       uint32_t target = 0;
+       uint32_t *shiftlist;
+       ALLOCA_FLAG(use_heap);
+
+       shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
+       memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
+       for (b = blocks; b < end; b++) {
+               if (b->flags & ZEND_BB_REACHABLE) {
+                       i = b->start;
+                       b->start = target;
+                       while (i <= b->end) {
+                               if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) ||
+                                  /*keep NOP to support ZEND_VM_SMART_BRANCH */
+                                  (i > 0 &&
+                                   i + 1 < op_array->last &&
+                                   (op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
+                                    op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
+                                   (op_array->opcodes[i-1].opcode == ZEND_IS_IDENTICAL ||
+                                    op_array->opcodes[i-1].opcode == ZEND_IS_NOT_IDENTICAL ||
+                                    op_array->opcodes[i-1].opcode == ZEND_IS_EQUAL ||
+                                    op_array->opcodes[i-1].opcode == ZEND_IS_NOT_EQUAL ||
+                                    op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER ||
+                                    op_array->opcodes[i-1].opcode == ZEND_IS_SMALLER_OR_EQUAL ||
+                                    op_array->opcodes[i-1].opcode == ZEND_CASE ||
+                                    op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_VAR ||
+                                    op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_STATIC_PROP ||
+                                    op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ ||
+                                    op_array->opcodes[i-1].opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ ||
+                                    op_array->opcodes[i-1].opcode == ZEND_INSTANCEOF ||
+                                    op_array->opcodes[i-1].opcode == ZEND_TYPE_CHECK ||
+                                    op_array->opcodes[i-1].opcode == ZEND_DEFINED))) {
+                                       if (i != target) {
+                                               op_array->opcodes[target] = op_array->opcodes[i];
+                                               ssa->ops[target] = ssa->ops[i];
+                                               shiftlist[i] = i - target;
+                                       }
+                                       target++;
+                               }
+                               i++;
+                       }
+                       if (b->end != target - 1) {
+                               zend_op *opline;
+                               zend_op *new_opline;
+
+                               opline = op_array->opcodes + b->end;
+                               b->end = target - 1;
+                               new_opline = op_array->opcodes + b->end;
+                               switch (new_opline->opcode) {
+                                       case ZEND_JMP:
+                                       case ZEND_FAST_CALL:
+                                               ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
+                                               break;
+                                       case ZEND_JMPZNZ:
+                                               new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+                                               /* break missing intentionally */
+                                       case ZEND_JMPZ:
+                                       case ZEND_JMPNZ:
+                                       case ZEND_JMPZ_EX:
+                                       case ZEND_JMPNZ_EX:
+                                       case ZEND_FE_RESET_R:
+                                       case ZEND_FE_RESET_RW:
+                                       case ZEND_NEW:
+                                       case ZEND_JMP_SET:
+                                       case ZEND_COALESCE:
+                                       case ZEND_ASSERT_CHECK:
+                                               ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
+                                               break;
+                                       case ZEND_CATCH:
+                                               if (!opline->result.num) {
+                                                       new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+                                               }
+                                               break;
+                                       case ZEND_DECLARE_ANON_CLASS:
+                                       case ZEND_DECLARE_ANON_INHERITED_CLASS:
+                                       case ZEND_FE_FETCH_R:
+                                       case ZEND_FE_FETCH_RW:
+                                               new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
+                                               break;
+                               }
+                       }
+               }
+       }
+
+       if (target != op_array->last) {
+               /* reset rest opcodes */
+               for (i = target; i < op_array->last; i++) {
+                       MAKE_NOP(op_array->opcodes + i);
+               }
+
+               /* update SSA variables */
+               for (j = 0; j < ssa->vars_count; j++) {
+                       if (ssa->vars[j].definition >= 0) {
+                               ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
+                       }
+                       if (ssa->vars[j].use_chain >= 0) {
+                               ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
+                       }
+               }
+               for (i = 0; i < op_array->last; i++) {
+                       if (ssa->ops[i].op1_use_chain >= 0) {
+                               ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
+                       }
+                       if (ssa->ops[i].op2_use_chain >= 0) {
+                               ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
+                       }
+                       if (ssa->ops[i].res_use_chain >= 0) {
+                               ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
+                       }
+               }
+
+               /* update branch targets */
+               for (b = blocks; b < end; b++) {
+                       if (b->flags & ZEND_BB_REACHABLE) {
+                               zend_op *opline = op_array->opcodes + b->end;
+
+                               switch (opline->opcode) {
+                                       case ZEND_JMP:
+                                       case ZEND_FAST_CALL:
+                                               ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
+                                               break;
+                                       case ZEND_JMPZNZ:
+                                               opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+                                               /* break missing intentionally */
+                                       case ZEND_JMPZ:
+                                       case ZEND_JMPNZ:
+                                       case ZEND_JMPZ_EX:
+                                       case ZEND_JMPNZ_EX:
+                                       case ZEND_FE_RESET_R:
+                                       case ZEND_FE_RESET_RW:
+                                       case ZEND_NEW:
+                                       case ZEND_JMP_SET:
+                                       case ZEND_COALESCE:
+                                       case ZEND_ASSERT_CHECK:
+                                               ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
+                                               break;
+                                       case ZEND_DECLARE_ANON_CLASS:
+                                       case ZEND_DECLARE_ANON_INHERITED_CLASS:
+                                       case ZEND_FE_FETCH_R:
+                                       case ZEND_FE_FETCH_RW:
+                                       case ZEND_CATCH:
+                                               opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
+                                               break;
+                               }
+                       }
+               }
+
+               /* update brk/cont array */
+               for (j = 0; j < op_array->last_live_range; j++) {
+                       op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
+                       op_array->live_range[j].end   -= shiftlist[op_array->live_range[j].end];
+               }
+
+               /* update try/catch array */
+               for (j = 0; j < op_array->last_try_catch; j++) {
+                       op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
+                       op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
+                       if (op_array->try_catch_array[j].finally_op) {
+                               op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
+                               op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
+                       }
+               }
+
+               /* update early binding list */
+               if (op_array->early_binding != (uint32_t)-1) {
+                       uint32_t *opline_num = &op_array->early_binding;
+
+                       do {
+                               *opline_num -= shiftlist[*opline_num];
+                               opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
+                       } while (*opline_num != (uint32_t)-1);
+               }
+
+               /* update call graph */
+               func_info = ZEND_FUNC_INFO(op_array);
+               if (func_info) {
+                       zend_call_info *call_info = func_info->callee_info;
+                       while (call_info) {
+                               call_info->caller_init_opline -=
+                                       shiftlist[call_info->caller_init_opline - op_array->opcodes];
+                               call_info->caller_call_opline -=
+                                       shiftlist[call_info->caller_call_opline - op_array->opcodes];
+                               call_info = call_info->next_callee;
+                       }
+               }
+
+               op_array->last = target;
+       }
+       free_alloca(shiftlist, use_heap);
+}
+
 void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
 {
        if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
@@ -207,7 +406,7 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
                        }
                }
                if (remove_nops) {
-                       // TODO: remove nop???
+                       zend_ssa_remove_nops(op_array, ssa);
                }
        }