PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/opcache/Optimizer - dfa_pass.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 232 348 66.7 %
Date: 2019-04-07 Functions: 4 6 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend OPcache                                                         |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2018 The PHP Group                                |
       6             :    +----------------------------------------------------------------------+
       7             :    | This source file is subject to version 3.01 of the PHP license,      |
       8             :    | that is bundled with this package in the file LICENSE, and is        |
       9             :    | available through the world-wide-web at the following url:           |
      10             :    | http://www.php.net/license/3_01.txt                                  |
      11             :    | If you did not receive a copy of the PHP license and are unable to   |
      12             :    | obtain it through the world-wide-web, please send a note to          |
      13             :    | license@php.net so we can mail you a copy immediately.               |
      14             :    +----------------------------------------------------------------------+
      15             :    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
      16             :    +----------------------------------------------------------------------+
      17             : */
      18             : 
      19             : #include "php.h"
      20             : #include "Optimizer/zend_optimizer.h"
      21             : #include "Optimizer/zend_optimizer_internal.h"
      22             : #include "zend_API.h"
      23             : #include "zend_constants.h"
      24             : #include "zend_execute.h"
      25             : #include "zend_vm.h"
      26             : #include "zend_bitset.h"
      27             : #include "zend_cfg.h"
      28             : #include "zend_ssa.h"
      29             : #include "zend_func_info.h"
      30             : #include "zend_call_graph.h"
      31             : #include "zend_inference.h"
      32             : #include "zend_dump.h"
      33             : 
      34         729 : int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, uint32_t *flags)
      35             : {
      36             :         uint32_t build_flags;
      37             : 
      38         729 :         if (op_array->last_try_catch) {
      39             :                 /* TODO: we can't analyze functions with try/catch/finally ??? */
      40          14 :                 return FAILURE;
      41             :         }
      42             : 
      43             :     /* Build SSA */
      44         715 :         memset(ssa, 0, sizeof(zend_ssa));
      45             : 
      46         715 :         if (zend_build_cfg(&ctx->arena, op_array,
      47             :                         ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg, flags) != SUCCESS) {
      48           0 :                 return FAILURE;
      49             :         }
      50             : 
      51         715 :         if (*flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) {
      52             :                 /* TODO: we can't analyze functions with indirect variable access ??? */
      53         275 :                 return FAILURE;
      54             :         }
      55             : 
      56         440 :         if (zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg) != SUCCESS) {
      57           0 :                 return FAILURE;
      58             :         }
      59             : 
      60         440 :         if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
      61           0 :                 zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
      62             :         }
      63             : 
      64             :         /* Compute Dominators Tree */
      65         440 :         if (zend_cfg_compute_dominators_tree(op_array, &ssa->cfg) != SUCCESS) {
      66           0 :                 return FAILURE;
      67             :         }
      68             : 
      69             :         /* Identify reducible and irreducible loops */
      70         440 :         if (zend_cfg_identify_loops(op_array, &ssa->cfg, flags) != SUCCESS) {
      71           0 :                 return FAILURE;
      72             :         }
      73             : 
      74         440 :         if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
      75           0 :                 zend_dump_dominators(op_array, &ssa->cfg);
      76             :         }
      77             : 
      78         440 :         build_flags = 0;
      79         440 :         if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
      80           0 :                 build_flags |= ZEND_SSA_DEBUG_LIVENESS;
      81             :         }
      82         440 :         if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
      83           0 :                 build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
      84             :         }
      85         440 :         if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa, flags) != SUCCESS) {
      86           0 :                 return FAILURE;
      87             :         }
      88             : 
      89         440 :         if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
      90           0 :                 zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
      91             :         }
      92             : 
      93             : 
      94         440 :         if (zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa) != SUCCESS){
      95           0 :                 return FAILURE;
      96             :         }
      97             : 
      98         440 :         if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
      99           0 :                 return FAILURE;
     100             :         }
     101             : 
     102         440 :         if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
     103           0 :                 return FAILURE;
     104             :         }
     105             : 
     106         440 :         if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa) != SUCCESS) {
     107           0 :                 return FAILURE;
     108             :         }
     109             : 
     110         440 :         if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
     111           0 :                 zend_dump_ssa_variables(op_array, ssa, 0);
     112             :         }
     113             : 
     114         440 :         return SUCCESS;
     115             : }
     116             : 
     117           8 : static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa)
     118             : {
     119           8 :         zend_basic_block *blocks = ssa->cfg.blocks;
     120           8 :         zend_basic_block *end = blocks + ssa->cfg.blocks_count;
     121             :         zend_basic_block *b;
     122             :         zend_func_info *func_info;
     123             :         int j;
     124             :         uint32_t i;
     125           8 :         uint32_t target = 0;
     126             :         uint32_t *shiftlist;
     127             :         ALLOCA_FLAG(use_heap);
     128             : 
     129           8 :         shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
     130           8 :         memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
     131          72 :         for (b = blocks; b < end; b++) {
     132          64 :                 if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
     133             :                         uint32_t end;
     134          64 :                         if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
     135             :                                 /* Only keep the FREE for the loop var */
     136           0 :                                 ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
     137             :                                                 || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
     138           0 :                                 b->len = 1;
     139             :                         }
     140             : 
     141          64 :                         end = b->start + b->len;
     142          64 :                         i = b->start;
     143          64 :                         b->start = target;
     144         623 :                         while (i < end) {
     145         495 :                                 shiftlist[i] = i - target;
     146         495 :                                 if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) ||
     147             :                                    /* Keep NOP to support ZEND_VM_SMART_BRANCH. Using "target-1" instead of
     148             :                                     * "i-1" here to check the last non-NOP instruction. */
     149          13 :                                    (target > 0 &&
     150          26 :                                     i + 1 < op_array->last &&
     151          25 :                                     (op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
     152          13 :                                      op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
     153           1 :                                     zend_is_smart_branch(op_array->opcodes + target - 1))) {
     154         482 :                                         if (i != target) {
     155         344 :                                                 op_array->opcodes[target] = op_array->opcodes[i];
     156         344 :                                                 ssa->ops[target] = ssa->ops[i];
     157             :                                         }
     158         482 :                                         target++;
     159             :                                 }
     160         495 :                                 i++;
     161             :                         }
     162          64 :                         if (target != end && b->len != 0) {
     163             :                                 zend_op *opline;
     164             :                                 zend_op *new_opline;
     165             : 
     166          57 :                                 b->len = target - b->start;
     167          57 :                                 opline = op_array->opcodes + end - 1;
     168          57 :                                 if (opline->opcode == ZEND_NOP) {
     169           1 :                                         continue;
     170             :                                 }
     171             : 
     172          56 :                                 new_opline = op_array->opcodes + target - 1;
     173          56 :                                 switch (new_opline->opcode) {
     174          14 :                                         case ZEND_JMP:
     175             :                                         case ZEND_FAST_CALL:
     176          14 :                                                 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
     177          14 :                                                 break;
     178           0 :                                         case ZEND_JMPZNZ:
     179           0 :                                                 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
     180             :                                                 /* break missing intentionally */
     181          21 :                                         case ZEND_JMPZ:
     182             :                                         case ZEND_JMPNZ:
     183             :                                         case ZEND_JMPZ_EX:
     184             :                                         case ZEND_JMPNZ_EX:
     185             :                                         case ZEND_FE_RESET_R:
     186             :                                         case ZEND_FE_RESET_RW:
     187             :                                         case ZEND_JMP_SET:
     188             :                                         case ZEND_COALESCE:
     189             :                                         case ZEND_ASSERT_CHECK:
     190          21 :                                                 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
     191          21 :                                                 break;
     192           0 :                                         case ZEND_CATCH:
     193           0 :                                                 if (!opline->result.num) {
     194           0 :                                                         new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
     195             :                                                 }
     196           0 :                                                 break;
     197           0 :                                         case ZEND_DECLARE_ANON_CLASS:
     198             :                                         case ZEND_DECLARE_ANON_INHERITED_CLASS:
     199             :                                         case ZEND_FE_FETCH_R:
     200             :                                         case ZEND_FE_FETCH_RW:
     201           0 :                                                 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
     202           0 :                                                 break;
     203             :                                 }
     204             :                         }
     205             :                 }
     206             :         }
     207             : 
     208           8 :         if (target != op_array->last) {
     209             :                 /* reset rest opcodes */
     210          21 :                 for (i = target; i < op_array->last; i++) {
     211          13 :                         MAKE_NOP(op_array->opcodes + i);
     212             :                 }
     213             : 
     214             :                 /* update SSA variables */
     215         370 :                 for (j = 0; j < ssa->vars_count; j++) {
     216         362 :                         if (ssa->vars[j].definition >= 0) {
     217         259 :                                 ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
     218             :                         }
     219         362 :                         if (ssa->vars[j].use_chain >= 0) {
     220         255 :                                 ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
     221             :                         }
     222             :                 }
     223         503 :                 for (i = 0; i < op_array->last; i++) {
     224         495 :                         if (ssa->ops[i].op1_use_chain >= 0) {
     225          45 :                                 ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
     226             :                         }
     227         495 :                         if (ssa->ops[i].op2_use_chain >= 0) {
     228          18 :                                 ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
     229             :                         }
     230         495 :                         if (ssa->ops[i].res_use_chain >= 0) {
     231           0 :                                 ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
     232             :                         }
     233             :                 }
     234             : 
     235             :                 /* update branch targets */
     236          72 :                 for (b = blocks; b < end; b++) {
     237          64 :                         if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
     238          63 :                                 zend_op *opline = op_array->opcodes + b->start + b->len - 1;
     239             : 
     240          63 :                                 switch (opline->opcode) {
     241          14 :                                         case ZEND_JMP:
     242             :                                         case ZEND_FAST_CALL:
     243          14 :                                                 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
     244          14 :                                                 break;
     245           0 :                                         case ZEND_JMPZNZ:
     246           0 :                                                 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)]);
     247             :                                                 /* break missing intentionally */
     248          26 :                                         case ZEND_JMPZ:
     249             :                                         case ZEND_JMPNZ:
     250             :                                         case ZEND_JMPZ_EX:
     251             :                                         case ZEND_JMPNZ_EX:
     252             :                                         case ZEND_FE_RESET_R:
     253             :                                         case ZEND_FE_RESET_RW:
     254             :                                         case ZEND_JMP_SET:
     255             :                                         case ZEND_COALESCE:
     256             :                                         case ZEND_ASSERT_CHECK:
     257          26 :                                                 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
     258          26 :                                                 break;
     259           2 :                                         case ZEND_DECLARE_ANON_CLASS:
     260             :                                         case ZEND_DECLARE_ANON_INHERITED_CLASS:
     261             :                                         case ZEND_FE_FETCH_R:
     262             :                                         case ZEND_FE_FETCH_RW:
     263             :                                         case ZEND_CATCH:
     264           2 :                                                 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)]);
     265           2 :                                                 break;
     266             :                                 }
     267             :                         }
     268             :                 }
     269             : 
     270             :                 /* update brk/cont array */
     271          34 :                 for (j = 0; j < op_array->last_live_range; j++) {
     272          26 :                         op_array->live_range[j].start -= shiftlist[op_array->live_range[j].start];
     273          26 :                         op_array->live_range[j].end   -= shiftlist[op_array->live_range[j].end];
     274             :                 }
     275             : 
     276             :                 /* update try/catch array */
     277           8 :                 for (j = 0; j < op_array->last_try_catch; j++) {
     278           0 :                         op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
     279           0 :                         op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
     280           0 :                         if (op_array->try_catch_array[j].finally_op) {
     281           0 :                                 op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
     282           0 :                                 op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
     283             :                         }
     284             :                 }
     285             : 
     286             :                 /* update early binding list */
     287           8 :                 if (op_array->early_binding != (uint32_t)-1) {
     288           0 :                         uint32_t *opline_num = &op_array->early_binding;
     289             : 
     290             :                         do {
     291           0 :                                 *opline_num -= shiftlist[*opline_num];
     292           0 :                                 opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
     293           0 :                         } while (*opline_num != (uint32_t)-1);
     294             :                 }
     295             : 
     296             :                 /* update call graph */
     297           8 :                 func_info = ZEND_FUNC_INFO(op_array);
     298           8 :                 if (func_info) {
     299           8 :                         zend_call_info *call_info = func_info->callee_info;
     300          64 :                         while (call_info) {
     301          96 :                                 call_info->caller_init_opline -=
     302          48 :                                         shiftlist[call_info->caller_init_opline - op_array->opcodes];
     303          96 :                                 call_info->caller_call_opline -=
     304          48 :                                         shiftlist[call_info->caller_call_opline - op_array->opcodes];
     305          48 :                                 call_info = call_info->next_callee;
     306             :                         }
     307             :                 }
     308             : 
     309           8 :                 op_array->last = target;
     310             :         }
     311           8 :         free_alloca(shiftlist, use_heap);
     312           8 : }
     313             : 
     314           0 : static inline zend_bool can_elide_return_type_check(
     315             :                 zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) {
     316           0 :         zend_arg_info *info = &op_array->arg_info[-1];
     317           0 :         zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
     318           0 :         zend_ssa_var_info *def_info = &ssa->var_info[ssa_op->op1_def];
     319             : 
     320           0 :         if (use_info->type & MAY_BE_REF) {
     321           0 :                 return 0;
     322             :         }
     323             : 
     324             :         /* A type is possible that is not in the allowed types */
     325           0 :         if ((use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~(def_info->type & MAY_BE_ANY)) {
     326           0 :                 return 0;
     327             :         }
     328             : 
     329           0 :         if (info->type_hint == IS_CALLABLE) {
     330           0 :                 return 0;
     331             :         }
     332             : 
     333           0 :         if (info->class_name) {
     334           0 :                 if (!use_info->ce || !def_info->ce || !instanceof_function(use_info->ce, def_info->ce)) {
     335           0 :                         return 0;
     336             :                 }
     337             :         }
     338             : 
     339           0 :         return 1;
     340             : }
     341             : 
     342          28 : static zend_bool opline_supports_assign_contraction(
     343             :                 zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
     344          28 :         if (opline->opcode == ZEND_NEW) {
     345             :                 /* see Zend/tests/generators/aborted_yield_during_new.phpt */
     346           3 :                 return 0;
     347             :         }
     348             : 
     349          25 :         if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
     350          13 :                         || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
     351             :                 /* Function calls may dtor the return value after it has already been written -- allow
     352             :                  * direct assignment only for types where a double-dtor does not matter. */
     353          12 :                 uint32_t type = ssa->var_info[src_var].type;
     354          12 :                 uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
     355          12 :                 return !((type & MAY_BE_ANY) & ~simple);
     356             :         }
     357             : 
     358          13 :         if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
     359             :                 /* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++
     360             :                  * eliding the temporary variable would thus yield an incorrect result. */
     361           0 :                 return opline->op1_type != IS_CV || opline->op1.var != cv_var;
     362             :         }
     363             : 
     364          13 :         if (opline->opcode == ZEND_INIT_ARRAY) {
     365             :                 /* INIT_ARRAY initializes the result array before reading key/value. */
     366           0 :                 return (opline->op1_type != IS_CV || opline->op1.var != cv_var)
     367           0 :                         && (opline->op2_type != IS_CV || opline->op2.var != cv_var);
     368             :         }
     369             : 
     370          13 :         if (opline->opcode == ZEND_CAST
     371           0 :                         && (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
     372             :                 /* CAST to array/object may initialize the result to an empty array/object before
     373             :                  * reading the expression. */
     374           0 :                 return opline->op1_type != IS_CV || opline->op1.var != cv_var;
     375             :         }
     376             : 
     377          13 :         return 1;
     378             : }
     379             : 
     380         729 : void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
     381             : {
     382         729 :         if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
     383           0 :                 zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
     384             :         }
     385             : 
     386         729 :         if (ssa->var_info) {
     387             :                 int op_1;
     388             :                 int v;
     389         440 :                 int remove_nops = 0;
     390             :                 zend_op *opline;
     391             :                 zval tmp;
     392             : 
     393        1739 :                 for (v = op_array->last_var; v < ssa->vars_count; v++) {
     394             : 
     395        1299 :                         op_1 = ssa->vars[v].definition;
     396             : 
     397        1299 :                         if (op_1 < 0) {
     398          75 :                                 continue;
     399             :                         }
     400             : 
     401        1224 :                         opline = op_array->opcodes + op_1;
     402             : 
     403             :                         /* Convert LONG constants to DOUBLE */
     404        1224 :                         if (ssa->var_info[v].use_as_double) {
     405           0 :                                 if (opline->opcode == ZEND_ASSIGN
     406           0 :                                  && opline->op2_type == IS_CONST
     407           0 :                                  && ssa->ops[op_1].op1_def == v
     408           0 :                                  && !RETURN_VALUE_USED(opline)
     409           0 :                                 ) {
     410             : 
     411             : // op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
     412             : 
     413           0 :                                         zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
     414           0 :                                         ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
     415           0 :                                         ZVAL_DOUBLE(&tmp, zval_get_double(zv));
     416           0 :                                         opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
     417             : 
     418           0 :                                 } else if (opline->opcode == ZEND_QM_ASSIGN
     419           0 :                                  && opline->op1_type == IS_CONST
     420             :                                 ) {
     421             : 
     422             : // op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
     423             : 
     424           0 :                                         zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
     425           0 :                                         ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
     426           0 :                                         ZVAL_DOUBLE(&tmp, zval_get_double(zv));
     427           0 :                                         opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
     428             :                                 }
     429             : 
     430             :                         } else {
     431        1224 :                                 if (opline->opcode == ZEND_ADD
     432        1216 :                                  || opline->opcode == ZEND_SUB
     433        1215 :                                  || opline->opcode == ZEND_MUL
     434        1215 :                                  || opline->opcode == ZEND_IS_EQUAL
     435        1209 :                                  || opline->opcode == ZEND_IS_NOT_EQUAL
     436        1204 :                                  || opline->opcode == ZEND_IS_SMALLER
     437        1194 :                                  || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
     438             :                                 ) {
     439             : 
     440          30 :                                         if (opline->op1_type == IS_CONST
     441           4 :                                          && opline->op2_type != IS_CONST
     442           4 :                                          && (OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
     443           0 :                                          && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_LONG
     444           0 :                                         ) {
     445             : 
     446             : // op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
     447             : 
     448           0 :                                                 zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
     449           0 :                                                 ZVAL_DOUBLE(&tmp, zval_get_double(zv));
     450           0 :                                                 opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
     451             : 
     452          30 :                                         } else if (opline->op1_type != IS_CONST
     453          26 :                                          && opline->op2_type == IS_CONST
     454          18 :                                          && (OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
     455           0 :                                          && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
     456             :                                         ) {
     457             : 
     458             : // op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
     459             : 
     460           0 :                                                 zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
     461           0 :                                                 ZVAL_DOUBLE(&tmp, zval_get_double(zv));
     462           0 :                                                 opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
     463             :                                         }
     464             :                                 }
     465             :                         }
     466             : 
     467        1224 :                         if (ssa->vars[v].var >= op_array->last_var) {
     468             :                                 /* skip TMP and VAR */
     469         874 :                                 continue;
     470             :                         }
     471             : 
     472         350 :                         if (opline->opcode == ZEND_ASSIGN
     473         210 :                          && ssa->ops[op_1].op1_def == v
     474         210 :                          && !RETURN_VALUE_USED(opline)
     475         189 :                         ) {
     476         189 :                                 int orig_var = ssa->ops[op_1].op1_use;
     477             : 
     478         189 :                                 if (orig_var >= 0
     479         189 :                                  && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
     480             :                                 ) {
     481             : 
     482          60 :                                         int src_var = ssa->ops[op_1].op2_use;
     483             : 
     484          60 :                                         if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
     485          36 :                                          && src_var >= 0
     486          36 :                                          && !(ssa->var_info[src_var].type & MAY_BE_REF)
     487          34 :                                          && ssa->vars[src_var].definition >= 0
     488          34 :                                          && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
     489          34 :                                          && ssa->ops[ssa->vars[src_var].definition].result_use < 0
     490          28 :                                          && ssa->vars[src_var].use_chain == op_1
     491          28 :                                          && ssa->ops[op_1].op2_use_chain < 0
     492          28 :                                          && !ssa->vars[src_var].phi_use_chain
     493          28 :                                          && !ssa->vars[src_var].sym_use_chain
     494          56 :                                          && opline_supports_assign_contraction(
     495          28 :                                                  ssa, &op_array->opcodes[ssa->vars[src_var].definition],
     496             :                                                  src_var, opline->op1.var)
     497          13 :                                         ) {
     498             : 
     499          13 :                                                 int op_2 = ssa->vars[src_var].definition;
     500             : 
     501             : // op_2: #src_var.T = OP ...                                     => #v.CV = OP ...
     502             : // op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T    NOP
     503             : 
     504          13 :                                                 if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
     505             :                                                         /* Reconstruct SSA */
     506          13 :                                                         ssa->vars[v].definition = op_2;
     507          13 :                                                         ssa->ops[op_2].result_def = v;
     508             : 
     509          13 :                                                         ssa->vars[src_var].definition = -1;
     510          13 :                                                         ssa->vars[src_var].use_chain = -1;
     511             : 
     512          13 :                                                         ssa->ops[op_1].op1_use = -1;
     513          13 :                                                         ssa->ops[op_1].op2_use = -1;
     514          13 :                                                         ssa->ops[op_1].op1_def = -1;
     515          13 :                                                         ssa->ops[op_1].op1_use_chain = -1;
     516             : 
     517             :                                                         /* Update opcodes */
     518          13 :                                                         op_array->opcodes[op_2].result_type = opline->op1_type;
     519          13 :                                                         op_array->opcodes[op_2].result.var = opline->op1.var;
     520          13 :                                                         MAKE_NOP(opline);
     521          13 :                                                         remove_nops = 1;
     522             :                                                 }
     523          47 :                                         } else if (opline->op2_type == IS_CONST
     524          23 :                                          || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
     525          23 :                                              && ssa->ops[op_1].op2_use >= 0
     526          23 :                                              && ssa->ops[op_1].op2_def < 0)
     527             :                                         ) {
     528             : 
     529             : // op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
     530             : 
     531          47 :                                                 if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
     532             :                                                         /* Reconstruct SSA */
     533          47 :                                                         ssa->ops[op_1].result_def = v;
     534          47 :                                                         ssa->ops[op_1].op1_def = -1;
     535          47 :                                                         ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
     536          47 :                                                         ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
     537          47 :                                                         ssa->ops[op_1].op2_use = -1;
     538          47 :                                                         ssa->ops[op_1].op2_use_chain = -1;
     539             : 
     540             :                                                         /* Update opcode */
     541          47 :                                                         opline->result_type = opline->op1_type;
     542          47 :                                                         opline->result.var = opline->op1.var;
     543          47 :                                                         opline->op1_type = opline->op2_type;
     544          47 :                                                         opline->op1.var = opline->op2.var;
     545          47 :                                                         opline->op2_type = IS_UNUSED;
     546          47 :                                                         opline->op2.var = 0;
     547          47 :                                                         opline->opcode = ZEND_QM_ASSIGN;
     548             :                                                 }
     549             :                                         }
     550             :                                 }
     551             : 
     552         161 :                         } else if (opline->opcode == ZEND_ASSIGN_ADD
     553           4 :                          && opline->extended_value == 0
     554           4 :                          && ssa->ops[op_1].op1_def == v
     555           4 :                          && opline->op2_type == IS_CONST
     556           2 :                          && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
     557           1 :                          && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
     558           0 :                          && ssa->ops[op_1].op1_use >= 0
     559           0 :                          && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
     560             : 
     561             : // op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV
     562             : 
     563           0 :                                 opline->opcode = ZEND_PRE_INC;
     564           0 :                                 SET_UNUSED(opline->op2);
     565             : 
     566         161 :                         } else if (opline->opcode == ZEND_ASSIGN_SUB
     567           1 :                          && opline->extended_value == 0
     568           1 :                          && ssa->ops[op_1].op1_def == v
     569           1 :                          && opline->op2_type == IS_CONST
     570           2 :                          && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
     571           1 :                          && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
     572           0 :                          && ssa->ops[op_1].op1_use >= 0
     573           0 :                          && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
     574             : 
     575             : // op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
     576             : 
     577           0 :                                 opline->opcode = ZEND_PRE_DEC;
     578           0 :                                 SET_UNUSED(opline->op2);
     579             : 
     580         161 :                         } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
     581           0 :                          && ssa->ops[op_1].op1_def == v
     582           0 :                          && ssa->ops[op_1].op1_use >= 0
     583           0 :                          && ssa->ops[op_1].op1_use_chain == -1
     584           0 :                          && ssa->vars[v].use_chain >= 0
     585           0 :                          && can_elide_return_type_check(op_array, ssa, &ssa->ops[op_1])) {
     586             : 
     587             : // op_1: VERIFY_RETURN_TYPE #orig_var.CV [T] -> #v.CV [T] => NOP
     588             : 
     589           0 :                                 int orig_var = ssa->ops[op_1].op1_use;
     590           0 :                                 if (zend_ssa_unlink_use_chain(ssa, op_1, orig_var)) {
     591             : 
     592           0 :                                         int ret = ssa->vars[v].use_chain;
     593             : 
     594           0 :                                         ssa->ops[ret].op1_use = orig_var;
     595           0 :                                         ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain;
     596           0 :                                         ssa->vars[orig_var].use_chain = ret;
     597             : 
     598           0 :                                         ssa->vars[v].definition = -1;
     599           0 :                                         ssa->vars[v].use_chain = -1;
     600             : 
     601           0 :                                         ssa->ops[op_1].op1_def = -1;
     602           0 :                                         ssa->ops[op_1].op1_use = -1;
     603             : 
     604           0 :                                         MAKE_NOP(opline);
     605           0 :                                         remove_nops = 1;
     606             :                                 }
     607             : 
     608         161 :                         } else if (ssa->ops[op_1].op1_def == v
     609         106 :                          && !RETURN_VALUE_USED(opline)
     610          64 :                          && ssa->ops[op_1].op1_use >= 0
     611          64 :                          && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
     612          40 :                          && (opline->opcode == ZEND_ASSIGN_ADD
     613          36 :                           || opline->opcode == ZEND_ASSIGN_SUB
     614          36 :                           || opline->opcode == ZEND_ASSIGN_MUL
     615          36 :                           || opline->opcode == ZEND_ASSIGN_DIV
     616          36 :                           || opline->opcode == ZEND_ASSIGN_MOD
     617          36 :                           || opline->opcode == ZEND_ASSIGN_SL
     618          36 :                           || opline->opcode == ZEND_ASSIGN_SR
     619          36 :                           || opline->opcode == ZEND_ASSIGN_BW_OR
     620          36 :                           || opline->opcode == ZEND_ASSIGN_BW_AND
     621          36 :                           || opline->opcode == ZEND_ASSIGN_BW_XOR)
     622           4 :                          && opline->extended_value == 0) {
     623             : 
     624             : // op_1: ASSIGN_ADD #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?
     625             : 
     626             :                                 /* Reconstruct SSA */
     627           4 :                                 ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
     628           4 :                                 ssa->ops[op_1].op1_def = -1;
     629             : 
     630             :                                 /* Update opcode */
     631           4 :                                 opline->opcode -= (ZEND_ASSIGN_ADD - ZEND_ADD);
     632           4 :                                 opline->result_type = opline->op1_type;
     633           4 :                                 opline->result.var = opline->op1.var;
     634             : 
     635             :                         }
     636             :                 }
     637             : 
     638         440 :                 if (remove_nops) {
     639           8 :                         zend_ssa_remove_nops(op_array, ssa);
     640             :                 }
     641             :         }
     642             : 
     643         729 :         if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
     644           0 :                 zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
     645             :         }
     646         729 : }
     647             : 
     648           0 : void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
     649             : {
     650           0 :         void *checkpoint = zend_arena_checkpoint(ctx->arena);
     651           0 :         uint32_t flags = 0;
     652             :         zend_ssa ssa;
     653             : 
     654           0 :         if (zend_dfa_analyze_op_array(op_array, ctx, &ssa, &flags) != SUCCESS) {
     655           0 :                 zend_arena_release(&ctx->arena, checkpoint);
     656           0 :                 return;
     657             :         }
     658             : 
     659           0 :         zend_dfa_optimize_op_array(op_array, ctx, &ssa);
     660             : 
     661             :         /* Destroy SSA */
     662           0 :         zend_arena_release(&ctx->arena, checkpoint);
     663             : }
     664             : 
     665             : /*
     666             :  * Local variables:
     667             :  * tab-width: 4
     668             :  * c-basic-offset: 4
     669             :  * indent-tabs-mode: t
     670             :  * End:
     671             :  */

Generated by: LCOV version 1.10

Generated at Sun, 07 Apr 2019 20:56:51 +0000 (11 days ago)

Copyright © 2005-2019 The PHP Group
All rights reserved.