From: Dmitry Stogov Date: Thu, 10 Mar 2016 15:31:02 +0000 (+0300) Subject: Remove NOPs after DFA pass. X-Git-Tag: php-7.1.0alpha1~497 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8026da6db0fd7dbfe9c9eac1d1567827dd047e84;p=php Remove NOPs after DFA pass. --- diff --git a/ext/opcache/Optimizer/dfa_pass.c b/ext/opcache/Optimizer/dfa_pass.c index dac4ab2f45..f2a7160cc6 100644 --- a/ext/opcache/Optimizer/dfa_pass.c +++ b/ext/opcache/Optimizer/dfa_pass.c @@ -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); } }