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 - block_pass.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 687 1009 68.1 %
Date: 2018-07-27 Functions: 9 9 100.0 %
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: Andi Gutmans <andi@zend.com>                                |
      16             :    |          Zeev Suraski <zeev@zend.com>                                |
      17             :    |          Stanislav Malyshev <stas@zend.com>                          |
      18             :    |          Dmitry Stogov <dmitry@zend.com>                             |
      19             :    +----------------------------------------------------------------------+
      20             : */
      21             : 
      22             : #include "php.h"
      23             : #include "Optimizer/zend_optimizer.h"
      24             : #include "Optimizer/zend_optimizer_internal.h"
      25             : #include "zend_API.h"
      26             : #include "zend_constants.h"
      27             : #include "zend_execute.h"
      28             : #include "zend_vm.h"
      29             : #include "zend_bitset.h"
      30             : #include "zend_cfg.h"
      31             : #include "zend_dump.h"
      32             : 
      33             : /* Checks if a constant (like "true") may be replaced by its value */
      34          70 : int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
      35             : {
      36             :         zend_constant *c;
      37             :         char *lookup_name;
      38          70 :         int retval = 1;
      39             :         ALLOCA_FLAG(use_heap);
      40             : 
      41         140 :         if ((c = zend_hash_find_ptr(EG(zend_constants), name)) == NULL) {
      42          53 :                 lookup_name = do_alloca(ZSTR_LEN(name) + 1, use_heap);
      43          53 :                 memcpy(lookup_name, ZSTR_VAL(name), ZSTR_LEN(name) + 1);
      44          53 :                 zend_str_tolower(lookup_name, ZSTR_LEN(name));
      45             : 
      46         106 :                 if ((c = zend_hash_str_find_ptr(EG(zend_constants), lookup_name, ZSTR_LEN(name))) != NULL) {
      47           0 :                         if (!(c->flags & CONST_CT_SUBST) || (c->flags & CONST_CS)) {
      48           0 :                                 retval = 0;
      49             :                         }
      50             :                 } else {
      51          53 :                         retval = 0;
      52             :                 }
      53          53 :                 free_alloca(lookup_name, use_heap);
      54             :         }
      55             : 
      56          70 :         if (retval) {
      57          17 :                 if (c->flags & CONST_PERSISTENT) {
      58           1 :                         ZVAL_COPY_VALUE(result, &c->value);
      59           1 :                         if (copy) {
      60             :                                 zval_copy_ctor(result);
      61             :                         }
      62             :                 } else {
      63          16 :                         retval = 0;
      64             :                 }
      65             :         }
      66             : 
      67          70 :         return retval;
      68             : }
      69             : 
      70             : /* CFG back references management */
      71             : 
      72             : #define DEL_SOURCE(from, to)
      73             : #define ADD_SOURCE(from, to)
      74             : 
      75             : /* Data dependencies macros */
      76             : 
      77             : #define VAR_NUM_EX(op) VAR_NUM((op).var)
      78             : 
      79             : #define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
      80             : #define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
      81             : 
      82             : #define convert_to_string_safe(v) \
      83             :         if (Z_TYPE_P((v)) == IS_NULL) { \
      84             :                 ZVAL_STRINGL((v), "", 0); \
      85             :         } else { \
      86             :                 convert_to_string((v)); \
      87             :         }
      88             : 
      89       10410 : static void strip_leading_nops(zend_op_array *op_array, zend_basic_block *b)
      90             : {
      91       10410 :         zend_op *opcodes = op_array->opcodes;
      92             : 
      93       22882 :         while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP) {
      94             :             /* check if NOP breaks incorrect smart branch */
      95        2062 :                 if (b->len == 2
      96         441 :                  && (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
      97         424 :                   || op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
      98          27 :                  && (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
      99          24 :                  && b->start > 0
     100          24 :                  && zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
     101           0 :                         break;
     102             :                 }
     103        2062 :                 b->start++;
     104        2062 :                 b->len--;
     105             :         }
     106       10410 : }
     107             : 
     108        5205 : static void strip_nops(zend_op_array *op_array, zend_basic_block *b)
     109             : {
     110             :         uint32_t i, j;
     111             : 
     112        5205 :         strip_leading_nops(op_array, b);
     113        5205 :         if (b->len == 0) {
     114           7 :                 return;
     115             :         }
     116             : 
     117             :         /* strip the inside NOPs */
     118        5198 :         i = j = b->start + 1;
     119       30515 :         while (i < b->start + b->len) {
     120       20119 :                 if (op_array->opcodes[i].opcode != ZEND_NOP) {
     121       19793 :                         if (i != j) {
     122         632 :                                 op_array->opcodes[j] = op_array->opcodes[i];
     123             :                         }
     124       19793 :                         j++;
     125             :                 }
     126       20119 :                 if (i + 1 < b->start + b->len
     127       16718 :                  && (op_array->opcodes[i+1].opcode == ZEND_JMPZ
     128       16339 :                   || op_array->opcodes[i+1].opcode == ZEND_JMPNZ)
     129         528 :                  && op_array->opcodes[i+1].op1_type & (IS_CV|IS_CONST)
     130          72 :                  && zend_is_smart_branch(op_array->opcodes + j - 1)) {
     131             :                         /* don't remove NOP, that splits incorrect smart branch */
     132           0 :                         j++;
     133             :                 }
     134       20119 :                 i++;
     135             :         }
     136        5198 :         b->len = j - b->start;
     137       10722 :         while (j < i) {
     138         326 :                 MAKE_NOP(op_array->opcodes + j);
     139         326 :                 j++;
     140             :         }
     141             : }
     142             : 
     143        5205 : static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource)
     144             : {
     145             :         zend_op *opline, *src;
     146        5205 :         zend_op *end, *last_op = NULL;
     147             : 
     148             :         /* remove leading NOPs */
     149        5205 :         strip_leading_nops(op_array, block);
     150             : 
     151        5205 :         opline = op_array->opcodes + block->start;
     152        5205 :         end = opline + block->len;
     153       35736 :         while (opline < end) {
     154             :                 /* Constant Propagation: strip X = QM_ASSIGN(const) */
     155       30294 :                 if ((opline->op1_type & (IS_TMP_VAR|IS_VAR)) &&
     156        4968 :                     opline->opcode != ZEND_FREE) {
     157        4942 :                         src = VAR_SOURCE(opline->op1);
     158        9823 :                         if (src &&
     159        4894 :                             src->opcode == ZEND_QM_ASSIGN &&
     160          13 :                             src->op1_type == IS_CONST
     161             :                         ) {
     162          13 :                                 znode_op op1 = opline->op1;
     163          13 :                                 if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
     164           0 :                                         COPY_NODE(opline->result, opline->op1);
     165           0 :                                         COPY_NODE(opline->op1, src->op1);
     166           0 :                                         VAR_SOURCE(op1) = NULL;
     167           0 :                                         MAKE_NOP(src);
     168          13 :                                 } else if (opline->opcode != ZEND_FETCH_LIST && opline->opcode != ZEND_CASE) {
     169          10 :                                         zval c = ZEND_OP1_LITERAL(src);
     170             :                                         zval_copy_ctor(&c);
     171          10 :                                         if (zend_optimizer_update_op1_const(op_array, opline, &c)) {
     172           4 :                                                 zend_optimizer_remove_live_range(op_array, op1.var);
     173           4 :                                                 VAR_SOURCE(op1) = NULL;
     174           8 :                                                 literal_dtor(&ZEND_OP1_LITERAL(src));
     175           4 :                                                 MAKE_NOP(src);
     176             :                                         }
     177             :                                 }
     178             :                         }
     179             :                 }
     180             : 
     181             :                 /* Constant Propagation: strip X = QM_ASSIGN(const) */
     182       25326 :                 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
     183        1050 :                         src = VAR_SOURCE(opline->op2);
     184        2080 :                         if (src &&
     185        1031 :                             src->opcode == ZEND_QM_ASSIGN &&
     186           1 :                             src->op1_type == IS_CONST) {
     187             : 
     188           1 :                                 znode_op op2 = opline->op2;
     189           1 :                                 zval c = ZEND_OP1_LITERAL(src);
     190             : 
     191             :                                 zval_copy_ctor(&c);
     192           1 :                                 if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
     193           0 :                                         zend_optimizer_remove_live_range(op_array, op2.var);
     194           0 :                                         VAR_SOURCE(op2) = NULL;
     195           0 :                                         literal_dtor(&ZEND_OP1_LITERAL(src));
     196           0 :                                         MAKE_NOP(src);
     197             :                                 }
     198             :                         }
     199             :                 }
     200             : 
     201       25326 :                 if (opline->opcode == ZEND_ECHO) {
     202         866 :                         if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
     203         225 :                                 src = VAR_SOURCE(opline->op1);
     204         447 :                                 if (src &&
     205         222 :                                     src->opcode == ZEND_CAST &&
     206           0 :                                     src->extended_value == IS_STRING) {
     207             :                                         /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
     208           0 :                                         VAR_SOURCE(opline->op1) = NULL;
     209           0 :                                         COPY_NODE(opline->op1, src->op1);
     210           0 :                                         MAKE_NOP(src);
     211             :                                 }
     212             :                         }
     213             : 
     214         866 :                         if (opline->op1_type == IS_CONST) {
     215         627 :                                 if (last_op && last_op->opcode == ZEND_ECHO &&
     216           2 :                                     last_op->op1_type == IS_CONST &&
     217           3 :                                     Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE &&
     218           2 :                                     Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) {
     219             :                                         /* compress consecutive ECHO's.
     220             :                                          * Float to string conversion may be affected by current
     221             :                                          * locale setting.
     222             :                                          */
     223             :                                         int l, old_len;
     224             : 
     225           2 :                                         if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
     226           0 :                                                 convert_to_string_safe(&ZEND_OP1_LITERAL(opline));
     227             :                                         }
     228           2 :                                         if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
     229           0 :                                                 convert_to_string_safe(&ZEND_OP1_LITERAL(last_op));
     230             :                                         }
     231           1 :                                         old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
     232           1 :                                         l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
     233           1 :                                         if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
     234           0 :                                                 zend_string *tmp = zend_string_alloc(l, 0);
     235           0 :                                                 memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
     236           0 :                                                 Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
     237             :                                         } else {
     238           2 :                                                 Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
     239             :                                         }
     240           1 :                                         Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
     241           1 :                                         memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
     242           1 :                                         Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
     243           1 :                                         zval_dtor(&ZEND_OP1_LITERAL(opline));
     244           1 :                                         ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
     245           1 :                                         ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
     246           1 :                                         MAKE_NOP(last_op);
     247             :                                 }
     248         626 :                                 last_op = opline;
     249             :                         } else {
     250         240 :                                 last_op = NULL;
     251             :                         }
     252             :                 } else {
     253       24460 :                         last_op = NULL;
     254             :                 }
     255             : 
     256       25326 :                 switch (opline->opcode) {
     257             : 
     258          26 :                         case ZEND_FREE:
     259          26 :                                 if (opline->op1_type == IS_TMP_VAR) {
     260          16 :                                         src = VAR_SOURCE(opline->op1);
     261          29 :                                         if (src &&
     262          26 :                                             (src->opcode == ZEND_BOOL || src->opcode == ZEND_BOOL_NOT)) {
     263             :                                                 /* T = BOOL(X), FREE(T) => T = BOOL(X) */
     264             :                                                 /* The remaining BOOL is removed by a separate optimization */
     265           0 :                                                 VAR_SOURCE(opline->op1) = NULL;
     266           0 :                                                 MAKE_NOP(opline);
     267             :                                         }
     268          10 :                                 } else if (opline->op1_type == IS_VAR) {
     269          10 :                                         src = VAR_SOURCE(opline->op1);
     270             :                                         /* V = OP, FREE(V) => OP. NOP */
     271          17 :                                         if (src &&
     272          14 :                                             src->opcode != ZEND_FETCH_R &&
     273          14 :                                             src->opcode != ZEND_FETCH_STATIC_PROP_R &&
     274          12 :                                             src->opcode != ZEND_FETCH_DIM_R &&
     275          10 :                                             src->opcode != ZEND_FETCH_OBJ_R &&
     276           5 :                                             src->opcode != ZEND_NEW) {
     277           0 :                                                 if (opline->extended_value & ZEND_FREE_ON_RETURN) {
     278             :                                                         /* mark as removed (empty live range) */
     279           0 :                                                         op_array->live_range[opline->op2.num].var = (uint32_t)-1;
     280             :                                                 }
     281           0 :                                                 ZEND_RESULT_TYPE(src) = IS_UNUSED;
     282           0 :                                                 MAKE_NOP(opline);
     283             :                                         }
     284             :                                 }
     285          26 :                                 break;
     286             : 
     287             : #if 0
     288             :                 /* pre-evaluate functions:
     289             :                    constant(x)
     290             :                    defined(x)
     291             :                    function_exists(x)
     292             :                    extension_loaded(x)
     293             :                    BAD: interacts badly with Accelerator
     294             :                 */
     295             :                 if((ZEND_OP1_TYPE(opline) & IS_VAR) &&
     296             :                    VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
     297             :                    VAR_SOURCE(opline->op1)->extended_value == 1) {
     298             :                         zend_op *fcall = VAR_SOURCE(opline->op1);
     299             :                         zend_op *sv = fcall-1;
     300             :                         if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
     301             :                            ZEND_OP1_TYPE(sv) == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
     302             :                            Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
     303             :                            ) {
     304             :                                 zval *arg = &OPLINE_OP1_LITERAL(sv);
     305             :                                 char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
     306             :                                 int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
     307             :                                 if(flen == sizeof("defined")-1 && zend_binary_strcasecmp(fname, flen, "defined", sizeof("defined")-1) == 0) {
     308             :                                         zval c;
     309             :                                         if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 0 ELS_CC) != 0) {
     310             :                                                 literal_dtor(arg);
     311             :                                                 MAKE_NOP(sv);
     312             :                                                 MAKE_NOP(fcall);
     313             :                                                 LITERAL_BOOL(opline->op1, 1);
     314             :                                                 ZEND_OP1_TYPE(opline) = IS_CONST;
     315             :                                         }
     316             :                                 } else if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
     317             :                                                   (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
     318             :                                                   ) {
     319             :                                         zend_function *function;
     320             :                                         if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
     321             :                                                 literal_dtor(arg);
     322             :                                                 MAKE_NOP(sv);
     323             :                                                 MAKE_NOP(fcall);
     324             :                                                 LITERAL_BOOL(opline->op1, 1);
     325             :                                                 ZEND_OP1_TYPE(opline) = IS_CONST;
     326             :                                         }
     327             :                                 } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
     328             :                                         zval c;
     329             :                                         if(zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, 1 ELS_CC) != 0) {
     330             :                                                 literal_dtor(arg);
     331             :                                                 MAKE_NOP(sv);
     332             :                                                 MAKE_NOP(fcall);
     333             :                                                 ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
     334             :                                                 /* no copy ctor - get already copied it */
     335             :                                                 ZEND_OP1_TYPE(opline) = IS_CONST;
     336             :                                         }
     337             :                                 } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
     338             :                                         if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
     339             :                                                 literal_dtor(arg);
     340             :                                                 MAKE_NOP(sv);
     341             :                                                 MAKE_NOP(fcall);
     342             :                                                 LITERAL_BOOL(opline->op1, 1);
     343             :                                                 ZEND_OP1_TYPE(opline) = IS_CONST;
     344             :                                         }
     345             :                                 }
     346             :                         }
     347             :                 }
     348             : #endif
     349             : 
     350          21 :                         case ZEND_FETCH_LIST:
     351          21 :                                 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
     352             :                                         /* LIST variable will be deleted later by FREE */
     353           9 :                                         Tsource[VAR_NUM(opline->op1.var)] = NULL;
     354             :                                 }
     355          21 :                                 break;
     356             : 
     357          63 :                         case ZEND_CASE:
     358          63 :                                 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
     359             :                                         /* CASE variable will be deleted later by FREE, so we can't optimize it */
     360           0 :                                         Tsource[VAR_NUM(opline->op1.var)] = NULL;
     361           0 :                                         break;
     362             :                                 }
     363         117 :                                 if (opline->op1_type == IS_CONST &&
     364          54 :                                     opline->op2_type == IS_CONST) {
     365          54 :                                         break;
     366             :                                 }
     367             :                                 /* break missing intentionally */
     368             : 
     369             :                         case ZEND_IS_EQUAL:
     370             :                         case ZEND_IS_NOT_EQUAL:
     371         141 :                                 if (opline->op1_type == IS_CONST &&
     372           0 :                                     opline->op2_type == IS_CONST) {
     373           0 :                                         goto optimize_constant_binary_op;
     374             :                                 }
     375             :                         /* IS_EQ(TRUE, X)      => BOOL(X)
     376             :                          * IS_EQ(FALSE, X)     => BOOL_NOT(X)
     377             :                          * IS_NOT_EQ(TRUE, X)  => BOOL_NOT(X)
     378             :                          * IS_NOT_EQ(FALSE, X) => BOOL(X)
     379             :                          * CASE(TRUE, X)       => BOOL(X)
     380             :                          * CASE(FALSE, X)      => BOOL_NOT(X)
     381             :                          */
     382         141 :                                 if (opline->op1_type == IS_CONST &&
     383           0 :                                         (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
     384           0 :                                          Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
     385             :                                         /* Optimization of comparison with "null" is not safe,
     386             :                                          * because ("0" == null) is not equal to !("0")
     387             :                                          */
     388           0 :                                         opline->opcode =
     389           0 :                                                 ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
     390             :                                                 ZEND_BOOL : ZEND_BOOL_NOT;
     391           0 :                                         COPY_NODE(opline->op1, opline->op2);
     392           0 :                                         SET_UNUSED(opline->op2);
     393           0 :                                         goto optimize_bool;
     394         243 :                                 } else if (opline->op2_type == IS_CONST &&
     395         306 :                                            (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
     396         204 :                                             Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
     397             :                                         /* Optimization of comparison with "null" is not safe,
     398             :                                          * because ("0" == null) is not equal to !("0")
     399             :                                          */
     400           0 :                                         opline->opcode =
     401           0 :                                                 ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
     402             :                                                 ZEND_BOOL : ZEND_BOOL_NOT;
     403           0 :                                         SET_UNUSED(opline->op2);
     404           0 :                                         goto optimize_bool;
     405             :                                 }
     406         141 :                                 break;
     407             : 
     408             :                         case ZEND_BOOL:
     409             :                         case ZEND_BOOL_NOT:
     410         136 :                         optimize_bool:
     411         155 :                                 if (opline->op1_type == IS_CONST) {
     412           2 :                                         goto optimize_const_unary_op;
     413             :                                 }
     414         189 :                                 if (opline->op1_type == IS_TMP_VAR &&
     415          36 :                                     !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
     416          27 :                                         src = VAR_SOURCE(opline->op1);
     417          27 :                                         if (src) {
     418          27 :                                                 switch (src->opcode) {
     419          19 :                                                         case ZEND_BOOL_NOT:
     420             :                                                                 /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
     421          19 :                                                                 VAR_SOURCE(opline->op1) = NULL;
     422          19 :                                                                 COPY_NODE(opline->op1, src->op1);
     423          19 :                                                                 opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
     424          19 :                                                                 MAKE_NOP(src);
     425          19 :                                                                 goto optimize_bool;
     426           0 :                                                         case ZEND_BOOL:
     427             :                                                                 /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
     428           0 :                                                                 VAR_SOURCE(opline->op1) = NULL;
     429           0 :                                                                 COPY_NODE(opline->op1, src->op1);
     430           0 :                                                                 MAKE_NOP(src);
     431           0 :                                                                 goto optimize_bool;
     432           2 :                                                         case ZEND_IS_EQUAL:
     433           2 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     434           0 :                                                                         src->opcode = ZEND_IS_NOT_EQUAL;
     435             :                                                                 }
     436           2 :                                                                 COPY_NODE(src->result, opline->result);
     437           2 :                                                                 SET_VAR_SOURCE(src);
     438           2 :                                                                 MAKE_NOP(opline);
     439           2 :                                                                 break;
     440           2 :                                                         case ZEND_IS_NOT_EQUAL:
     441           2 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     442           0 :                                                                         src->opcode = ZEND_IS_EQUAL;
     443             :                                                                 }
     444           2 :                                                                 COPY_NODE(src->result, opline->result);
     445           2 :                                                                 SET_VAR_SOURCE(src);
     446           2 :                                                                 MAKE_NOP(opline);
     447           2 :                                                                 break;
     448           0 :                                                         case ZEND_IS_IDENTICAL:
     449           0 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     450           0 :                                                                         src->opcode = ZEND_IS_NOT_IDENTICAL;
     451             :                                                                 }
     452           0 :                                                                 COPY_NODE(src->result, opline->result);
     453           0 :                                                                 SET_VAR_SOURCE(src);
     454           0 :                                                                 MAKE_NOP(opline);
     455           0 :                                                                 break;
     456           0 :                                                         case ZEND_IS_NOT_IDENTICAL:
     457           0 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     458           0 :                                                                         src->opcode = ZEND_IS_IDENTICAL;
     459             :                                                                 }
     460           0 :                                                                 COPY_NODE(src->result, opline->result);
     461           0 :                                                                 SET_VAR_SOURCE(src);
     462           0 :                                                                 MAKE_NOP(opline);
     463           0 :                                                                 break;
     464           2 :                                                         case ZEND_IS_SMALLER:
     465           2 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     466             :                                                                         zend_uchar tmp_type;
     467             :                                                                         uint32_t tmp;
     468             : 
     469           0 :                                                                         src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
     470           0 :                                                                         tmp_type = src->op1_type;
     471           0 :                                                                         src->op1_type = src->op2_type;
     472           0 :                                                                         src->op2_type = tmp_type;
     473           0 :                                                                         tmp = src->op1.num;
     474           0 :                                                                         src->op1.num = src->op2.num;
     475           0 :                                                                         src->op2.num = tmp;
     476             :                                                                 }
     477           2 :                                                                 COPY_NODE(src->result, opline->result);
     478           2 :                                                                 SET_VAR_SOURCE(src);
     479           2 :                                                                 MAKE_NOP(opline);
     480           2 :                                                                 break;
     481           0 :                                                         case ZEND_IS_SMALLER_OR_EQUAL:
     482           0 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     483             :                                                                         zend_uchar tmp_type;
     484             :                                                                         uint32_t tmp;
     485             : 
     486           0 :                                                                         src->opcode = ZEND_IS_SMALLER;
     487           0 :                                                                         tmp_type = src->op1_type;
     488           0 :                                                                         src->op1_type = src->op2_type;
     489           0 :                                                                         src->op2_type = tmp_type;
     490           0 :                                                                         tmp = src->op1.num;
     491           0 :                                                                         src->op1.num = src->op2.num;
     492           0 :                                                                         src->op2.num = tmp;
     493             :                                                                 }
     494           0 :                                                                 COPY_NODE(src->result, opline->result);
     495           0 :                                                                 SET_VAR_SOURCE(src);
     496           0 :                                                                 MAKE_NOP(opline);
     497           0 :                                                                 break;
     498           2 :                                                         case ZEND_ISSET_ISEMPTY_VAR:
     499             :                                                         case ZEND_ISSET_ISEMPTY_DIM_OBJ:
     500             :                                                         case ZEND_ISSET_ISEMPTY_PROP_OBJ:
     501             :                                                         case ZEND_ISSET_ISEMPTY_STATIC_PROP:
     502             :                                                         case ZEND_INSTANCEOF:
     503             :                                                         case ZEND_TYPE_CHECK:
     504             :                                                         case ZEND_DEFINED:
     505           2 :                                                                 if (opline->opcode == ZEND_BOOL_NOT) {
     506           1 :                                                                         break;
     507             :                                                                 }
     508           1 :                                                                 COPY_NODE(src->result, opline->result);
     509           1 :                                                                 SET_VAR_SOURCE(src);
     510           1 :                                                                 MAKE_NOP(opline);
     511           1 :                                                                 break;
     512             :                                                 }
     513             :                                         }
     514             :                                 }
     515         134 :                                 break;
     516             : 
     517             :                         case ZEND_JMPZ:
     518             :                         case ZEND_JMPNZ:
     519             :                         case ZEND_JMPZ_EX:
     520             :                         case ZEND_JMPNZ_EX:
     521             :                         case ZEND_JMPZNZ:
     522         940 :                         optimize_jmpznz:
     523        1407 :                                 if (opline->op1_type == IS_TMP_VAR &&
     524         486 :                                     (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
     525          28 :                                      (opline->result_type == opline->op1_type &&
     526           9 :                                       opline->result.var == opline->op1.var))) {
     527         457 :                                         src = VAR_SOURCE(opline->op1);
     528         457 :                                         if (src) {
     529         528 :                                                 if (src->opcode == ZEND_BOOL_NOT &&
     530         142 :                                                     opline->opcode != ZEND_JMPZ_EX &&
     531          71 :                                                     opline->opcode != ZEND_JMPNZ_EX) {
     532          71 :                                                         VAR_SOURCE(opline->op1) = NULL;
     533          71 :                                                         COPY_NODE(opline->op1, src->op1);
     534          71 :                                                         if (opline->opcode == ZEND_JMPZ) {
     535             :                                                                 /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
     536          62 :                                                                 opline->opcode = ZEND_JMPNZ;
     537           9 :                                                         } else if (opline->opcode == ZEND_JMPNZ) {
     538             :                                                                 /* T = BOOL_NOT(X) + JMPNZ(T) -> NOP, JMPZ(X) */
     539           6 :                                                                 opline->opcode = ZEND_JMPZ;
     540             : #if 0
     541             :                                                         } else if (opline->opcode == ZEND_JMPZ_EX) {
     542             :                                                                 /* T = BOOL_NOT(X) + JMPZ_EX(T) -> NOP, JMPNZ_EX(X) */
     543             :                                                                 opline->opcode = ZEND_JMPNZ_EX;
     544             :                                                         } else if (opline->opcode == ZEND_JMPNZ_EX) {
     545             :                                                                 /* T = BOOL_NOT(X) + JMPNZ_EX(T) -> NOP, JMPZ_EX(X) */
     546             :                                                                 opline->opcode = ZEND_JMPZ;
     547             : #endif
     548             :                                                         } else {
     549             :                                                                 /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */
     550             :                                                                 uint32_t tmp;
     551             : 
     552           3 :                                                                 ZEND_ASSERT(opline->opcode == ZEND_JMPZNZ);
     553           3 :                                                                 tmp = block->successors[0];
     554           3 :                                                                 block->successors[0] = block->successors[1];
     555           3 :                                                                 block->successors[1] = tmp;
     556             :                                                         }
     557          71 :                                                         MAKE_NOP(src);
     558          71 :                                                         goto optimize_jmpznz;
     559         748 :                                                 } else if (src->opcode == ZEND_BOOL ||
     560         362 :                                                            src->opcode == ZEND_QM_ASSIGN) {
     561          24 :                                                         VAR_SOURCE(opline->op1) = NULL;
     562          24 :                                                         COPY_NODE(opline->op1, src->op1);
     563          24 :                                                         MAKE_NOP(src);
     564          24 :                                                         goto optimize_jmpznz;
     565             :                                                 }
     566             :                                         }
     567             :                                 }
     568         845 :                                 break;
     569             : 
     570         429 :                         case ZEND_CONCAT:
     571             :                         case ZEND_FAST_CONCAT:
     572         576 :                                 if (opline->op1_type == IS_CONST &&
     573         147 :                                     opline->op2_type == IS_CONST) {
     574           0 :                                         goto optimize_constant_binary_op;
     575             :                                 }
     576             : 
     577         645 :                                 if (opline->op2_type == IS_CONST &&
     578         216 :                                     opline->op1_type == IS_TMP_VAR) {
     579             : 
     580          99 :                                         src = VAR_SOURCE(opline->op1);
     581         198 :                                     if (src &&
     582         144 :                                             (src->opcode == ZEND_CONCAT ||
     583         135 :                                              src->opcode == ZEND_FAST_CONCAT) &&
     584          90 :                                             src->op2_type == IS_CONST) {
     585             :                                                 /* compress consecutive CONCATs */
     586             :                                                 int l, old_len;
     587             : 
     588           0 :                                                 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
     589           0 :                                                         convert_to_string_safe(&ZEND_OP2_LITERAL(opline));
     590             :                                                 }
     591           0 :                                                 if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
     592           0 :                                                         convert_to_string_safe(&ZEND_OP2_LITERAL(src));
     593             :                                                 }
     594             : 
     595           0 :                                                 VAR_SOURCE(opline->op1) = NULL;
     596           0 :                                                 COPY_NODE(opline->op1, src->op1);
     597           0 :                                                 old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
     598           0 :                                                 l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
     599           0 :                                                 if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
     600           0 :                                                         zend_string *tmp = zend_string_alloc(l, 0);
     601           0 :                                                         memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
     602           0 :                                                         Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
     603             :                                                 } else {
     604           0 :                                                         Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
     605             :                                                 }
     606           0 :                                                 Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
     607           0 :                                                 memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
     608           0 :                                                 Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
     609           0 :                                                 zend_string_release(Z_STR(ZEND_OP2_LITERAL(opline)));
     610           0 :                                                 ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
     611           0 :                                                 ZVAL_NULL(&ZEND_OP2_LITERAL(src));
     612           0 :                                                 MAKE_NOP(src);
     613             :                                         }
     614             :                                 }
     615             : 
     616         429 :                                 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
     617         210 :                                         src = VAR_SOURCE(opline->op1);
     618         420 :                                         if (src &&
     619         210 :                                             src->opcode == ZEND_CAST &&
     620           0 :                                             src->extended_value == IS_STRING) {
     621             :                                                 /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
     622           0 :                                                 VAR_SOURCE(opline->op1) = NULL;
     623           0 :                                                 COPY_NODE(opline->op1, src->op1);
     624           0 :                                                 MAKE_NOP(src);
     625             :                                         }
     626             :                     }
     627         429 :                                 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
     628         171 :                                         src = VAR_SOURCE(opline->op2);
     629         342 :                                         if (src &&
     630         171 :                                             src->opcode == ZEND_CAST &&
     631           0 :                                             src->extended_value == IS_STRING) {
     632             :                                                 /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
     633           0 :                                                 zend_op *src = VAR_SOURCE(opline->op2);
     634           0 :                                                 VAR_SOURCE(opline->op2) = NULL;
     635           0 :                                                 COPY_NODE(opline->op2, src->op1);
     636           0 :                                                 MAKE_NOP(src);
     637             :                                         }
     638             :                                 }
     639         576 :                                 if (opline->op1_type == IS_CONST &&
     640         441 :                                     Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
     641         147 :                                     Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
     642             :                                         /* convert CONCAT('', X) => CAST(STRING, X) */
     643           0 :                                         literal_dtor(&ZEND_OP1_LITERAL(opline));
     644           0 :                                         opline->opcode = ZEND_CAST;
     645           0 :                                         opline->extended_value = IS_STRING;
     646           0 :                                         COPY_NODE(opline->op1, opline->op2);
     647           0 :                                         opline->op2_type = IS_UNUSED;
     648           0 :                                         opline->op2.var = 0;
     649         645 :                                 } else if (opline->op2_type == IS_CONST &&
     650         648 :                                    Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
     651         216 :                                    Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
     652             :                                         /* convert CONCAT(X, '') => CAST(STRING, X) */
     653           0 :                                         literal_dtor(&ZEND_OP2_LITERAL(opline));
     654           0 :                                         opline->opcode = ZEND_CAST;
     655           0 :                                         opline->extended_value = IS_STRING;
     656           0 :                                         opline->op2_type = IS_UNUSED;
     657           0 :                                         opline->op2.var = 0;
     658         724 :                                 } else if (opline->opcode == ZEND_CONCAT &&
     659         484 :                                            (opline->op1_type == IS_CONST ||
     660         306 :                                             (opline->op1_type == IS_TMP_VAR &&
     661         234 :                                              VAR_SOURCE(opline->op1) &&
     662         216 :                                              (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
     663         192 :                                               VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
     664         183 :                                               VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
     665         223 :                                               VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
     666         251 :                                            (opline->op2_type == IS_CONST ||
     667         146 :                                             (opline->op2_type == IS_TMP_VAR &&
     668          56 :                                              VAR_SOURCE(opline->op2) &&
     669          56 :                                              (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
     670          56 :                                               VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
     671          35 :                                               VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
     672           7 :                                               VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
     673          37 :                                         opline->opcode = ZEND_FAST_CONCAT;
     674             :                                 }
     675         429 :                                 break;
     676             : 
     677         204 :                         case ZEND_ADD:
     678             :                         case ZEND_SUB:
     679             :                         case ZEND_MUL:
     680             :                         case ZEND_DIV:
     681             :                         case ZEND_MOD:
     682             :                         case ZEND_SL:
     683             :                         case ZEND_SR:
     684             :                         case ZEND_IS_SMALLER:
     685             :                         case ZEND_IS_SMALLER_OR_EQUAL:
     686             :                         case ZEND_IS_IDENTICAL:
     687             :                         case ZEND_IS_NOT_IDENTICAL:
     688             :                         case ZEND_BOOL_XOR:
     689             :                         case ZEND_BW_OR:
     690             :                         case ZEND_BW_AND:
     691             :                         case ZEND_BW_XOR:
     692         237 :                                 if (opline->op1_type == IS_CONST &&
     693          33 :                                     opline->op2_type == IS_CONST) {
     694             :                                         /* evaluate constant expressions */
     695             :                                         binary_op_type binary_op;
     696             :                                         zval result;
     697             :                                         int er;
     698             : 
     699           0 : optimize_constant_binary_op:
     700           0 :                                         binary_op = get_binary_op(opline->opcode);
     701           0 :                             if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) &&
     702           0 :                                 zval_get_long(&ZEND_OP2_LITERAL(opline)) == 0) {
     703           0 :                                                 SET_VAR_SOURCE(opline);
     704           0 :                                 opline++;
     705           0 :                                                 continue;
     706           0 :                             } else if ((opline->opcode == ZEND_SL || opline->opcode == ZEND_SR) &&
     707           0 :                                 zval_get_long(&ZEND_OP2_LITERAL(opline)) < 0) {
     708           0 :                                                 SET_VAR_SOURCE(opline);
     709           0 :                                 opline++;
     710           0 :                                                 continue;
     711           0 :                                         } else if (zend_binary_op_produces_numeric_string_error(opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline))) {
     712           0 :                                                 SET_VAR_SOURCE(opline);
     713           0 :                                 opline++;
     714           0 :                                                 continue;
     715             :                                         }
     716           0 :                                         er = EG(error_reporting);
     717           0 :                                         EG(error_reporting) = 0;
     718           0 :                                         if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
     719           0 :                                                 literal_dtor(&ZEND_OP1_LITERAL(opline));
     720           0 :                                                 literal_dtor(&ZEND_OP2_LITERAL(opline));
     721           0 :                                                 opline->opcode = ZEND_QM_ASSIGN;
     722           0 :                                                 SET_UNUSED(opline->op2);
     723           0 :                                                 zend_optimizer_update_op1_const(op_array, opline, &result);
     724             :                                         }
     725           0 :                                         EG(error_reporting) = er;
     726             :                                 }
     727         204 :                                 break;
     728             : 
     729           9 :                         case ZEND_BW_NOT:
     730           9 :                                 if (opline->op1_type == IS_CONST) {
     731             :                                         /* evaluate constant unary ops */
     732             :                                         unary_op_type unary_op;
     733             :                                         zval result;
     734             : 
     735           0 : optimize_const_unary_op:
     736           2 :                                         unary_op = get_unary_op(opline->opcode);
     737           2 :                                         if (unary_op) {
     738           0 :                                                 unary_op(&result, &ZEND_OP1_LITERAL(opline));
     739           0 :                                                 literal_dtor(&ZEND_OP1_LITERAL(opline));
     740             :                                         } else {
     741             :                                                 /* BOOL */
     742           2 :                                                 ZVAL_COPY_VALUE(&result, &ZEND_OP1_LITERAL(opline));
     743           2 :                                                 convert_to_boolean(&result);
     744           2 :                                                 ZVAL_NULL(&ZEND_OP1_LITERAL(opline));
     745             :                                         }
     746           2 :                                         opline->opcode = ZEND_QM_ASSIGN;
     747           2 :                                         zend_optimizer_update_op1_const(op_array, opline, &result);
     748             :                                 }
     749          11 :                                 break;
     750             : 
     751        2503 :                         case ZEND_RETURN:
     752             :                         case ZEND_EXIT:
     753        2503 :                                 if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
     754         159 :                                         src = VAR_SOURCE(opline->op1);
     755         159 :                                         if (src && src->opcode == ZEND_QM_ASSIGN) {
     756           0 :                                                 zend_op *op = src + 1;
     757           0 :                                                 zend_bool optimize = 1;
     758             : 
     759           0 :                                                 while (op < opline) {
     760           0 :                                                         if ((op->op1_type == opline->op1_type
     761           0 :                                                           && op->op1.var == opline->op1.var)
     762           0 :                                                          || (op->op2_type == opline->op1_type
     763           0 :                                                           && op->op2.var == opline->op1.var)) {
     764           0 :                                                                 optimize = 0;
     765           0 :                                                                 break;
     766             :                                                         }
     767           0 :                                                         op++;
     768             :                                                 }
     769             : 
     770           0 :                                                 if (optimize) {
     771             :                                                         /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
     772           0 :                                                         VAR_SOURCE(opline->op1) = NULL;
     773           0 :                                                         COPY_NODE(opline->op1, src->op1);
     774           0 :                                                         MAKE_NOP(src);
     775             :                                                 }
     776             :                                         }
     777             :                                 }
     778        2503 :                                 break;
     779             : 
     780          59 :                         case ZEND_QM_ASSIGN:
     781          62 :                                 if (opline->op1_type == opline->result_type &&
     782           3 :                                     opline->op1.var == opline->result.var) {
     783             :                                         /* strip T = QM_ASSIGN(T) */
     784           0 :                                         MAKE_NOP(opline);
     785             :                                 }
     786          59 :                                 break;
     787             :                 }
     788             : 
     789             :                 /* get variable source */
     790       25326 :                 if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
     791        6237 :                         SET_VAR_SOURCE(opline);
     792             :                 }
     793       25326 :                 opline++;
     794             :         }
     795        5205 : }
     796             : 
     797             : /* Rebuild plain (optimized) op_array from CFG */
     798         729 : static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
     799             : {
     800         729 :         zend_basic_block *blocks = cfg->blocks;
     801         729 :         zend_basic_block *end = blocks + cfg->blocks_count;
     802             :         zend_basic_block *b;
     803             :         zend_op *new_opcodes;
     804             :         zend_op *opline;
     805         729 :         uint32_t len = 0;
     806             :         int n;
     807             : 
     808        3069 :         for (b = blocks; b < end; b++) {
     809        2340 :                 if (b->len == 0) {
     810         621 :                         continue;
     811             :                 }
     812        1719 :                 if (b->flags & ZEND_BB_REACHABLE) {
     813        1593 :                         opline = op_array->opcodes + b->start + b->len - 1;
     814        1593 :                         if (opline->opcode == ZEND_JMP) {
     815          46 :                                 zend_basic_block *next = b + 1;
     816             : 
     817          95 :                                 while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
     818           3 :                                         next++;
     819             :                                 }
     820          46 :                                 if (next < end && next == blocks + b->successors[0]) {
     821             :                                         /* JMP to the next block - strip it */
     822           0 :                                         MAKE_NOP(opline);
     823           0 :                                         b->len--;
     824             :                                 }
     825        1547 :                         } else if (b->len == 1 && opline->opcode == ZEND_NOP) {
     826             :                                 /* skip empty block */
     827           0 :                                 b->len--;
     828             :                         }
     829        1593 :                         len += b->len;
     830             :                 } else {
     831             :                         /* this block will not be used, delete all constants there */
     832         126 :                         zend_op *op = op_array->opcodes + b->start;
     833         126 :                         zend_op *end = op + b->len;
     834         265 :                         for (; op < end; op++) {
     835         139 :                                 if (ZEND_OP1_TYPE(op) == IS_CONST) {
     836         170 :                                         literal_dtor(&ZEND_OP1_LITERAL(op));
     837             :                                 }
     838         139 :                                 if (ZEND_OP2_TYPE(op) == IS_CONST) {
     839           0 :                                         literal_dtor(&ZEND_OP2_LITERAL(op));
     840             :                                 }
     841             :                         }
     842             :                 }
     843             :         }
     844             : 
     845         729 :         new_opcodes = emalloc(len * sizeof(zend_op));
     846         729 :         opline = new_opcodes;
     847             : 
     848             :         /* Copy code of reachable blocks into a single buffer */
     849        3069 :         for (b = blocks; b < end; b++) {
     850        2340 :                 if (b->flags & ZEND_BB_REACHABLE) {
     851        1595 :                         memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
     852        1595 :                         b->start = opline - new_opcodes;
     853        1595 :                         opline += b->len;
     854             :                 }
     855             :         }
     856             : 
     857             :         /* adjust jump targets */
     858         729 :         efree(op_array->opcodes);
     859         729 :         op_array->opcodes = new_opcodes;
     860         729 :         op_array->last = len;
     861             : 
     862        3069 :         for (b = blocks; b < end; b++) {
     863        2340 :                 if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
     864         747 :                         continue;
     865             :                 }
     866        1593 :                 opline = op_array->opcodes + b->start + b->len - 1;
     867        1593 :                 switch (opline->opcode) {
     868          47 :                         case ZEND_FAST_CALL:
     869             :                         case ZEND_JMP:
     870          47 :                                 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
     871          47 :                                 break;
     872           7 :                         case ZEND_JMPZNZ:
     873           7 :                                 opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[1]].start);
     874             :                                 /* break missing intentionally */
     875         268 :                         case ZEND_JMPZ:
     876             :                         case ZEND_JMPNZ:
     877             :                         case ZEND_JMPZ_EX:
     878             :                         case ZEND_JMPNZ_EX:
     879             :                         case ZEND_FE_RESET_R:
     880             :                         case ZEND_FE_RESET_RW:
     881             :                         case ZEND_JMP_SET:
     882             :                         case ZEND_COALESCE:
     883             :                         case ZEND_ASSERT_CHECK:
     884         268 :                                 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
     885         268 :                                 break;
     886           0 :                         case ZEND_CATCH:
     887           0 :                                 if (!opline->result.var) {
     888           0 :                                         opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
     889             :                                 }
     890           0 :                                 break;
     891           6 :                         case ZEND_DECLARE_ANON_CLASS:
     892             :                         case ZEND_DECLARE_ANON_INHERITED_CLASS:
     893             :                         case ZEND_FE_FETCH_R:
     894             :                         case ZEND_FE_FETCH_RW:
     895           6 :                                 opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
     896           6 :                                 break;
     897             :                 }
     898             :         }
     899             : 
     900             :         /* adjust exception jump targets & remove unused try_catch_array entries */
     901         729 :         if (op_array->last_try_catch) {
     902             :                 int i, j;
     903             :                 uint32_t *map;
     904             :                 ALLOCA_FLAG(use_heap);
     905             : 
     906          15 :                 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
     907          35 :                 for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
     908          20 :                         if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
     909          17 :                                 map[i] = j;
     910          17 :                                 op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
     911          17 :                                 if (op_array->try_catch_array[i].catch_op) {
     912          16 :                                         op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
     913             :                                 } else {
     914           1 :                                         op_array->try_catch_array[j].catch_op =  0;
     915             :                                 }
     916          17 :                                 if (op_array->try_catch_array[i].finally_op) {
     917           1 :                                         op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
     918             :                                 } else {
     919          16 :                                         op_array->try_catch_array[j].finally_op =  0;
     920             :                                 }
     921          17 :                                 if (!op_array->try_catch_array[i].finally_end) {
     922          16 :                                         op_array->try_catch_array[j].finally_end = 0;
     923             :                                 } else {
     924           1 :                                         op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
     925             :                                 }
     926          17 :                                 j++;
     927             :                         }
     928             :                 }
     929          15 :                 if (i != j) {
     930           3 :                         op_array->last_try_catch = j;
     931           3 :                         if (j == 0) {
     932           1 :                                 efree(op_array->try_catch_array);
     933           1 :                                 op_array->try_catch_array = NULL;
     934             :                         }
     935             : 
     936           3 :                         if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
     937           0 :                                 zend_op *opline = new_opcodes;
     938           0 :                                 zend_op *end = opline + len;
     939           0 :                                 while (opline < end) {
     940           0 :                                         if (opline->opcode == ZEND_FAST_RET &&
     941           0 :                                             opline->op2.num != (uint32_t)-1 &&
     942           0 :                                             opline->op2.num < (uint32_t)j) {
     943           0 :                                                 opline->op2.num = map[opline->op2.num];
     944             :                                         }
     945           0 :                                         opline++;
     946             :                                 }
     947             :                         }
     948             :                 }
     949          15 :                 free_alloca(map, use_heap);
     950             :         }
     951             : 
     952             :         /* adjust loop jump targets & remove unused live range entries */
     953         729 :         if (op_array->last_live_range) {
     954             :                 int i, j;
     955             :                 uint32_t *map;
     956             :                 ALLOCA_FLAG(use_heap);
     957             : 
     958          93 :                 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
     959             : 
     960         317 :                 for (i = 0, j = 0; i < op_array->last_live_range; i++) {
     961         224 :                         if (op_array->live_range[i].var == (uint32_t)-1) {
     962             :                                 /* this live range already removed */
     963           0 :                                 continue;
     964             :                         }
     965         224 :                         if (!(blocks[cfg->map[op_array->live_range[i].start]].flags & ZEND_BB_REACHABLE)) {
     966          15 :                                 ZEND_ASSERT(!(blocks[cfg->map[op_array->live_range[i].end]].flags & ZEND_BB_REACHABLE));
     967             :                         } else {
     968         209 :                                 uint32_t start_op = blocks[cfg->map[op_array->live_range[i].start]].start;
     969         209 :                                 uint32_t end_op = blocks[cfg->map[op_array->live_range[i].end]].start;
     970             : 
     971         209 :                                 if (start_op == end_op) {
     972             :                                         /* skip empty live range */
     973           0 :                                         continue;
     974             :                                 }
     975         209 :                                 op_array->live_range[i].start = start_op;
     976         209 :                                 op_array->live_range[i].end = end_op;
     977         209 :                                 map[i] = j;
     978         209 :                                 if (i != j) {
     979          45 :                                         op_array->live_range[j]  = op_array->live_range[i];
     980             :                                 }
     981         209 :                                 j++;
     982             :                         }
     983             :                 }
     984             : 
     985          93 :                 if (i != j) {
     986          15 :                         if ((op_array->last_live_range = j)) {
     987          15 :                                 zend_op *opline = new_opcodes;
     988          15 :                                 zend_op *end = opline + len;
     989         645 :                                 while (opline != end) {
     990         615 :                                         if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
     991           0 :                                                         opline->extended_value == ZEND_FREE_ON_RETURN) {
     992           0 :                                                 ZEND_ASSERT(opline->op2.num < (uint32_t) i);
     993           0 :                                                 opline->op2.num = map[opline->op2.num];
     994             :                                         }
     995         615 :                                         opline++;
     996             :                                 }
     997             :                         } else {
     998           0 :                                 efree(op_array->live_range);
     999           0 :                                 op_array->live_range = NULL;
    1000             :                         }
    1001             :                 }
    1002          93 :                 free_alloca(map, use_heap);
    1003             :         }
    1004             : 
    1005             :         /* adjust early binding list */
    1006         729 :         if (op_array->early_binding != (uint32_t)-1) {
    1007           5 :                 uint32_t *opline_num = &op_array->early_binding;
    1008             :                 zend_op *end;
    1009             : 
    1010           5 :                 opline = op_array->opcodes;
    1011           5 :                 end = opline + op_array->last;
    1012         250 :                 while (opline < end) {
    1013         240 :                         if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
    1014           5 :                                 *opline_num = opline - op_array->opcodes;
    1015           5 :                                 opline_num = &ZEND_RESULT(opline).opline_num;
    1016             :                         }
    1017         240 :                         ++opline;
    1018             :                 }
    1019           5 :                 *opline_num = -1;
    1020             :         }
    1021             : 
    1022             :         /* rebuild map (just for printing) */
    1023         729 :         memset(cfg->map, -1, sizeof(int) * op_array->last);
    1024        3069 :         for (n = 0; n < cfg->blocks_count; n++) {
    1025        2340 :                 if (cfg->blocks[n].flags & ZEND_BB_REACHABLE) {
    1026        1595 :                         cfg->map[cfg->blocks[n].start] = n;
    1027             :                 }
    1028             :         }
    1029         729 : }
    1030             : 
    1031        5205 : static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, zend_cfg *cfg, zend_uchar *same_t)
    1032             : {
    1033             :         /* last_op is the last opcode of the current block */
    1034        5205 :         zend_basic_block *blocks = cfg->blocks;
    1035             :         zend_op *last_op;
    1036             : 
    1037        5205 :         if (block->len == 0) {
    1038           7 :                 return;
    1039             :         }
    1040             : 
    1041        5198 :         last_op = op_array->opcodes + block->start + block->len - 1;
    1042        5198 :         switch (last_op->opcode) {
    1043         409 :                 case ZEND_JMP:
    1044             :                         {
    1045         409 :                                 zend_basic_block *target_block = blocks + block->successors[0];
    1046         409 :                                 zend_op *target = op_array->opcodes + target_block->start;
    1047         409 :                                 int next = (block - blocks) + 1;
    1048             : 
    1049        1170 :                                 while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
    1050             :                                         /* find used one */
    1051         352 :                                         next++;
    1052             :                                 }
    1053             : 
    1054             :                                 /* JMP(next) -> NOP */
    1055         409 :                                 if (block->successors[0] == next) {
    1056         247 :                                         MAKE_NOP(last_op);
    1057         247 :                                         block->len--;
    1058         247 :                                         break;
    1059             :                                 }
    1060             : 
    1061         162 :                                 if (target->opcode == ZEND_JMP &&
    1062           0 :                                         block->successors[0] != target_block->successors[0] &&
    1063           0 :                                         !(target_block->flags & ZEND_BB_PROTECTED)) {
    1064             :                                         /* JMP L, L: JMP L1 -> JMP L1 */
    1065           0 :                                         *last_op = *target;
    1066             :                                         DEL_SOURCE(block, block->successors[0]);
    1067           0 :                                         block->successors[0] = target_block->successors[0];
    1068             :                                         ADD_SOURCE(block, block->successors[0]);
    1069         162 :                                 } else if (target->opcode == ZEND_JMPZNZ &&
    1070           0 :                                            !(target_block->flags & ZEND_BB_PROTECTED)) {
    1071             :                                         /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
    1072           0 :                                         *last_op = *target;
    1073           0 :                                         if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
    1074           0 :                                                 zval zv = ZEND_OP1_LITERAL(last_op);
    1075             :                                                 zval_copy_ctor(&zv);
    1076           0 :                                                 last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
    1077             :                                         }
    1078             :                                         DEL_SOURCE(block, block->successors[0]);
    1079           0 :                                         block->successors[0] = target_block->successors[0];
    1080           0 :                                         block->successors[1] = target_block->successors[1];
    1081             :                                         ADD_SOURCE(block, block->successors[0]);
    1082             :                                         ADD_SOURCE(block, block->successors[1]);
    1083         315 :                                 } else if ((target->opcode == ZEND_RETURN ||
    1084         306 :                                             target->opcode == ZEND_RETURN_BY_REF ||
    1085         162 :                                             target->opcode == ZEND_EXIT) &&
    1086           9 :                                            !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
    1087             :                                         /* JMP L, L: RETURN to immediate RETURN */
    1088           9 :                                         *last_op = *target;
    1089           9 :                                         if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
    1090           9 :                                                 zval zv = ZEND_OP1_LITERAL(last_op);
    1091             :                                                 zval_copy_ctor(&zv);
    1092           9 :                                                 last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
    1093             :                                         }
    1094             :                                         DEL_SOURCE(block, block->successors[0]);
    1095           9 :                                         block->successors[0] = -1;
    1096             : #if 0
    1097             :                                 /* Temporarily disabled - see bug #0025274 */
    1098             :                                 } else if (0&& block->op1_to != block &&
    1099             :                                    block->op1_to != blocks &&
    1100             :                                                    op_array->last_try_catch == 0 &&
    1101             :                                            target->opcode != ZEND_FREE) {
    1102             :                                     /* Block Reordering (saves one JMP on each "for" loop iteration)
    1103             :                                      * It is disabled for some cases (ZEND_FREE)
    1104             :                                      * which may break register allocation.
    1105             :                      */
    1106             :                                         zend_bool can_reorder = 0;
    1107             :                                         zend_block_source *cs = block->op1_to->sources;
    1108             : 
    1109             :                                         /* the "target" block doesn't had any followed block */
    1110             :                                         while(cs) {
    1111             :                                                 if (cs->from->follow_to == block->op1_to) {
    1112             :                                                         can_reorder = 0;
    1113             :                                                         break;
    1114             :                                                 }
    1115             :                                                 cs = cs->next;
    1116             :                                         }
    1117             :                                         if (can_reorder) {
    1118             :                                                 next = block->op1_to;
    1119             :                                                 /* the "target" block is not followed by current "block" */
    1120             :                                                 while (next->follow_to != NULL) {
    1121             :                                                         if (next->follow_to == block) {
    1122             :                                                                 can_reorder = 0;
    1123             :                                                                 break;
    1124             :                                                         }
    1125             :                                                         next = next->follow_to;
    1126             :                                                 }
    1127             :                                                 if (can_reorder) {
    1128             :                                                         zend_basic_block *prev = blocks;
    1129             : 
    1130             :                                                         while (prev->next != block->op1_to) {
    1131             :                                                                 prev = prev->next;
    1132             :                                                         }
    1133             :                                                         prev->next = next->next;
    1134             :                                                         next->next = block->next;
    1135             :                                                         block->next = block->op1_to;
    1136             : 
    1137             :                                                         block->follow_to = block->op1_to;
    1138             :                                                         block->op1_to = NULL;
    1139             :                                                         MAKE_NOP(last_op);
    1140             :                                                         block->len--;
    1141             :                                                         if(block->len == 0) {
    1142             :                                                                 /* this block is nothing but NOP now */
    1143             :                                                                 delete_code_block(block, ctx);
    1144             :                                                         }
    1145             :                                                         break;
    1146             :                                                 }
    1147             :                                         }
    1148             : #endif
    1149             :                                 }
    1150             :                         }
    1151         162 :                         break;
    1152             : 
    1153         800 :                 case ZEND_JMPZ:
    1154             :                 case ZEND_JMPNZ:
    1155             :                         /* constant conditional JMPs */
    1156         800 :                         if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
    1157          24 :                                 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
    1158             : 
    1159          24 :                                 if (last_op->opcode == ZEND_JMPZ) {
    1160          17 :                                         should_jmp = !should_jmp;
    1161             :                                 }
    1162          48 :                                 literal_dtor(&ZEND_OP1_LITERAL(last_op));
    1163          24 :                                 ZEND_OP1_TYPE(last_op) = IS_UNUSED;
    1164          24 :                                 if (should_jmp) {
    1165             :                                         /* JMPNZ(true) -> JMP */
    1166          24 :                                         last_op->opcode = ZEND_JMP;
    1167             :                                         DEL_SOURCE(block, block->successors[1]);
    1168          24 :                                         block->successors[1] = -1;
    1169             :                                 } else {
    1170             :                                         /* JMPNZ(false) -> NOP */
    1171           0 :                                         MAKE_NOP(last_op);
    1172             :                                         DEL_SOURCE(block, block->successors[0]);
    1173           0 :                                         block->successors[0] = block->successors[1];
    1174           0 :                                         block->successors[1] = -1;
    1175             :                                 }
    1176          24 :                                 break;
    1177             :                         }
    1178             : 
    1179         776 :                         if (block->successors[0] == block->successors[1]) {
    1180             :                                 /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
    1181             : 
    1182           5 :                                 if (last_op->op1_type == IS_CV) {
    1183           1 :                                         last_op->opcode = ZEND_CHECK_VAR;
    1184           1 :                                         last_op->op2.num = 0;
    1185           4 :                                 } else if (last_op->op1_type & (IS_VAR|IS_TMP_VAR)) {
    1186           4 :                                         last_op->opcode = ZEND_FREE;
    1187           4 :                                         last_op->op2.num = 0;
    1188             :                                 } else {
    1189           0 :                                         MAKE_NOP(last_op);
    1190             :                                 }
    1191           5 :                                 block->successors[1] = -1;
    1192           5 :                                 break;
    1193             :                         }
    1194             : 
    1195             :                         if (1) {
    1196         771 :                                 zend_uchar same_type = ZEND_OP1_TYPE(last_op);
    1197         771 :                                 uint32_t same_var = VAR_NUM_EX(last_op->op1);
    1198             :                                 zend_op *target;
    1199             :                                 zend_op *target_end;
    1200         771 :                                 zend_basic_block *target_block = blocks + block->successors[0];
    1201             : 
    1202         771 : next_target:
    1203         771 :                                 target = op_array->opcodes + target_block->start;
    1204         771 :                                 target_end = target + target_block->len;
    1205        1542 :                                 while (target < target_end && target->opcode == ZEND_NOP) {
    1206           0 :                                         target++;
    1207             :                                 }
    1208             : 
    1209             :                                 /* next block is only NOP's */
    1210         771 :                                 if (target == target_end) {
    1211           0 :                                         target_block = blocks + target_block->successors[0];
    1212           0 :                                         goto next_target;
    1213         825 :                                 } else if (target->opcode == INV_COND(last_op->opcode) &&
    1214             :                                         /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
    1215         108 :                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1216          54 :                                    same_type == ZEND_OP1_TYPE(target) &&
    1217           0 :                                    same_var == VAR_NUM_EX(target->op1) &&
    1218           0 :                                    !(target_block->flags & ZEND_BB_PROTECTED)
    1219             :                                    ) {
    1220             :                                         DEL_SOURCE(block, block->successors[0]);
    1221           0 :                                         block->successors[0] = target_block->successors[1];
    1222             :                                         ADD_SOURCE(block, block->successors[0]);
    1223         771 :                                 } else if (target->opcode == INV_COND_EX(last_op->opcode) &&
    1224           0 :                                                         (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1225           0 :                                                 same_type == ZEND_OP1_TYPE(target) &&
    1226           0 :                                                 same_var == VAR_NUM_EX(target->op1) &&
    1227           0 :                                                         !(target_block->flags & ZEND_BB_PROTECTED)) {
    1228             :                                         /* JMPZ(X, L), L: T = JMPNZ_EX(X, L2) -> T = JMPZ_EX(X, L+1) */
    1229           0 :                                         last_op->opcode += 3;
    1230           0 :                                         COPY_NODE(last_op->result, target->result);
    1231             :                                         DEL_SOURCE(block, block->successors[0]);
    1232           0 :                                         block->successors[0] = target_block->successors[1];
    1233             :                                         ADD_SOURCE(block, block->successors[0]);
    1234         785 :                                 } else if (target->opcode == last_op->opcode &&
    1235          28 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1236          28 :                                                    same_type == ZEND_OP1_TYPE(target) &&
    1237          19 :                                                    same_var == VAR_NUM_EX(target->op1) &&
    1238           5 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1239             :                                         /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
    1240             :                                         DEL_SOURCE(block, block->successors[0]);
    1241           5 :                                         block->successors[0] = target_block->successors[0];
    1242             :                                         ADD_SOURCE(block, block->successors[0]);
    1243         782 :                                 } else if (target->opcode == ZEND_JMP &&
    1244          16 :                                            !(target_block->flags & ZEND_BB_PROTECTED)) {
    1245             :                                         /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
    1246             :                                         DEL_SOURCE(block, block->successors[0]);
    1247          16 :                                         block->successors[0] = target_block->successors[0];
    1248             :                                         ADD_SOURCE(block, block->successors[0]);
    1249         750 :                                 } else if (target->opcode == ZEND_JMPZNZ &&
    1250           0 :                                            (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1251           0 :                                            same_type == ZEND_OP1_TYPE(target) &&
    1252           0 :                                            same_var == VAR_NUM_EX(target->op1) &&
    1253           0 :                                            !(target_block->flags & ZEND_BB_PROTECTED)) {
    1254             :                                         /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */
    1255             :                                         DEL_SOURCE(block, block->successors[0]);
    1256           0 :                                         if (last_op->opcode == ZEND_JMPZ) {
    1257           0 :                                                 block->successors[0] = target_block->successors[0];
    1258             :                                         } else {
    1259           0 :                                                 block->successors[0] = target_block->successors[1];
    1260             :                                         }
    1261             :                                         ADD_SOURCE(block, block->successors[0]);
    1262             :                                 }
    1263             :                         }
    1264             : 
    1265         771 :                         if (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ) {
    1266             :                                 zend_op *target;
    1267             :                                 zend_op *target_end;
    1268             :                                 zend_basic_block *target_block;
    1269             : 
    1270             :                                 while (1) {
    1271         771 :                                         target_block = blocks + block->successors[1];
    1272         771 :                                         target = op_array->opcodes + target_block->start;
    1273         771 :                                         target_end = op_array->opcodes + target_block->start + 1;
    1274        1542 :                                         while (target < target_end && target->opcode == ZEND_NOP) {
    1275           0 :                                                 target++;
    1276             :                                         }
    1277             : 
    1278             :                                         /* next block is only NOP's */
    1279         771 :                                         if (target == target_end && !(target_block->flags & ZEND_BB_PROTECTED)) {
    1280             :                                                 DEL_SOURCE(block, block->successors[1]);
    1281           0 :                                                 block->successors[1] = target_block->successors[0];
    1282             :                                                 ADD_SOURCE(block, block->successors[1]);
    1283             :                                         } else {
    1284             :                                                 break;
    1285             :                                         }
    1286             :                                 }
    1287             :                                 /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
    1288         774 :                                 if (target->opcode == ZEND_JMP &&
    1289           3 :                                         !(target_block->flags & ZEND_BB_PROTECTED)) {
    1290             :                                         DEL_SOURCE(block, block->successors[1]);
    1291           3 :                                         if (last_op->opcode == ZEND_JMPZ) {
    1292           3 :                                                 block->successors[1] = target_block->successors[0];
    1293             :                                                 ADD_SOURCE(block, block->successors[1]);
    1294             :                                         } else {
    1295           0 :                                                 block->successors[1] = block->successors[0];
    1296           0 :                                                 block->successors[0] = target_block->successors[0];
    1297             :                                                 ADD_SOURCE(block, block->successors[0]);
    1298             :                                         }
    1299           3 :                                         last_op->opcode = ZEND_JMPZNZ;
    1300             :                                 }
    1301             :                         }
    1302         771 :                         break;
    1303             : 
    1304          16 :                 case ZEND_JMPNZ_EX:
    1305             :                 case ZEND_JMPZ_EX:
    1306             :                         /* constant conditional JMPs */
    1307          16 :                         if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
    1308           0 :                                 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op));
    1309             : 
    1310           0 :                                 if (last_op->opcode == ZEND_JMPZ_EX) {
    1311           0 :                                         should_jmp = !should_jmp;
    1312             :                                 }
    1313           0 :                                 if (!should_jmp) {
    1314             :                                         /* T = JMPZ_EX(true,L)   -> T = QM_ASSIGN(true)
    1315             :                                          * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false)
    1316             :                                          */
    1317           0 :                                         last_op->opcode = ZEND_QM_ASSIGN;
    1318           0 :                                         SET_UNUSED(last_op->op2);
    1319             :                                         DEL_SOURCE(block, block->successors[0]);
    1320           0 :                                         block->successors[0] = block->successors[1];
    1321           0 :                                         block->successors[1] = -1;
    1322             :                                 }
    1323           0 :                                 break;
    1324             :                         }
    1325             : 
    1326             :                         if (1) {
    1327             :                                 zend_op *target, *target_end;
    1328             :                                 zend_basic_block *target_block;
    1329          16 :                                 int var_num = op_array->last_var + op_array->T;
    1330             : 
    1331          16 :                                 if (var_num <= 0) {
    1332           0 :                                         return;
    1333             :                                 }
    1334          16 :                                 memset(same_t, 0, var_num);
    1335          16 :                                 same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op);
    1336          16 :                                 same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op);
    1337          16 :                                 target_block = blocks + block->successors[0];
    1338          16 : next_target_ex:
    1339          16 :                                 target = op_array->opcodes + target_block->start;
    1340          16 :                                 target_end = target + target_block->len;
    1341          32 :                                 while (target < target_end && target->opcode == ZEND_NOP) {
    1342           0 :                                         target++;
    1343             :                                 }
    1344             :                                 /* next block is only NOP's */
    1345          16 :                                 if (target == target_end) {
    1346           0 :                                         target_block = blocks + target_block->successors[0];
    1347           0 :                                         goto next_target_ex;
    1348          16 :                                 } else if (target->opcode == last_op->opcode-3 &&
    1349           0 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1350           0 :                                                    (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
    1351           0 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1352             :                                         /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
    1353             :                                         DEL_SOURCE(block, block->successors[0]);
    1354           0 :                                         block->successors[0] = target_block->successors[0];
    1355             :                                         ADD_SOURCE(block, block->successors[0]);
    1356          20 :                                 } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
    1357           8 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1358           8 :                                                    (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
    1359           4 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1360             :                                         /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
    1361             :                                         DEL_SOURCE(block, block->successors[0]);
    1362           4 :                                         block->successors[0] = target_block->successors[1];
    1363             :                                         ADD_SOURCE(block, block->successors[0]);
    1364          12 :                                 } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
    1365           0 :                                                (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1366           0 :                                                    (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
    1367           0 :                                                    (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
    1368           0 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1369             :                                         /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */
    1370             :                                         DEL_SOURCE(block, block->successors[0]);
    1371           0 :                                         block->successors[0] = target_block->successors[1];
    1372             :                                         ADD_SOURCE(block, block->successors[0]);
    1373          12 :                                 } else if (target->opcode == last_op->opcode &&
    1374           0 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1375           0 :                                                    (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
    1376           0 :                                                    (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 &&
    1377           0 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1378             :                                         /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
    1379             :                                         DEL_SOURCE(block, block->successors[0]);
    1380           0 :                                         block->successors[0] = target_block->successors[0];
    1381             :                                         ADD_SOURCE(block, block->successors[0]);
    1382          12 :                                 } else if (target->opcode == ZEND_JMP &&
    1383           0 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1384             :                                         /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
    1385             :                                         DEL_SOURCE(block, block->successors[0]);
    1386           0 :                                         block->successors[0] = target_block->successors[0];
    1387             :                                         ADD_SOURCE(block, block->successors[0]);
    1388          15 :                                 } else if (target->opcode == ZEND_JMPZNZ &&
    1389           6 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1390           6 :                                                    (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 &&
    1391           3 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1392             :                                         /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */
    1393             :                                         DEL_SOURCE(block, block->successors[0]);
    1394           3 :                                         if (last_op->opcode == ZEND_JMPZ_EX) {
    1395           3 :                                                 block->successors[0] = target_block->successors[0];
    1396             :                                         } else {
    1397           0 :                                                 block->successors[0] = target_block->successors[1];
    1398             :                                         }
    1399             :                                         ADD_SOURCE(block, block->successors[0]);
    1400             :                                 }
    1401             :                         }
    1402          16 :                         break;
    1403             : 
    1404          29 :                 case ZEND_JMPZNZ: {
    1405          29 :                         int next = (block - blocks) + 1;
    1406             : 
    1407         108 :                         while (next < cfg->blocks_count && !(blocks[next].flags & ZEND_BB_REACHABLE)) {
    1408             :                                 /* find first accessed one */
    1409          50 :                                 next++;
    1410             :                         }
    1411             : 
    1412          29 :                         if (ZEND_OP1_TYPE(last_op) == IS_CONST) {
    1413           0 :                                 if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) {
    1414             :                                         /* JMPZNZ(false,L1,L2) -> JMP(L1) */
    1415           0 :                                         literal_dtor(&ZEND_OP1_LITERAL(last_op));
    1416           0 :                                         last_op->opcode = ZEND_JMP;
    1417           0 :                                         SET_UNUSED(last_op->op1);
    1418           0 :                                         SET_UNUSED(last_op->op2);
    1419             :                                         DEL_SOURCE(block, block->successors[1]);
    1420           0 :                                         block->successors[1] = -1;
    1421             :                                 } else {
    1422             :                                         /* JMPZNZ(true,L1,L2) -> JMP(L2) */
    1423           0 :                                         literal_dtor(&ZEND_OP1_LITERAL(last_op));
    1424           0 :                                         last_op->opcode = ZEND_JMP;
    1425           0 :                                         SET_UNUSED(last_op->op1);
    1426           0 :                                         SET_UNUSED(last_op->op2);
    1427             :                                         DEL_SOURCE(block, block->successors[0]);
    1428           0 :                                         block->successors[0] = block->successors[1];
    1429           0 :                                         block->successors[1] = -1;
    1430             :                                 }
    1431          29 :                         } else if (block->successors[0] == block->successors[1]) {
    1432             :                                 /* both goto the same one - it's JMP */
    1433           5 :                                 if (!(last_op->op1_type & (IS_VAR|IS_TMP_VAR))) {
    1434             :                                         /* JMPZNZ(?,L,L) -> JMP(L) */
    1435           0 :                                         last_op->opcode = ZEND_JMP;
    1436           0 :                                         SET_UNUSED(last_op->op1);
    1437           0 :                                         SET_UNUSED(last_op->op2);
    1438           0 :                                         block->successors[1] = -1;
    1439             :                                 }
    1440          24 :                         } else if (block->successors[0] == next) {
    1441             :                                 /* jumping to next on Z - can follow to it and jump only on NZ */
    1442             :                                 /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */
    1443           3 :                                 last_op->opcode = ZEND_JMPNZ;
    1444           3 :                                 block->successors[0] = block->successors[1];
    1445           3 :                                 block->successors[1] = next;
    1446             :                                 /* no need to add source */
    1447          21 :                         } else if (block->successors[1] == next) {
    1448             :                                 /* jumping to next on NZ - can follow to it and jump only on Z */
    1449             :                                 /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */
    1450           9 :                                 last_op->opcode = ZEND_JMPZ;
    1451             :                                 /* no need to add source */
    1452             :                         }
    1453             : 
    1454          29 :                         if (last_op->opcode == ZEND_JMPZNZ) {
    1455          17 :                                 zend_uchar same_type = ZEND_OP1_TYPE(last_op);
    1456          17 :                                 zend_uchar same_var = VAR_NUM_EX(last_op->op1);
    1457             :                                 zend_op *target;
    1458             :                                 zend_op *target_end;
    1459          17 :                                 zend_basic_block *target_block = blocks + block->successors[0];
    1460             : 
    1461          17 : next_target_znz:
    1462          17 :                                 target = op_array->opcodes + target_block->start;
    1463          17 :                                 target_end = target + target_block->len;
    1464          34 :                                 while (target < target_end && target->opcode == ZEND_NOP) {
    1465           0 :                                         target++;
    1466             :                                 }
    1467             :                                 /* next block is only NOP's */
    1468          17 :                                 if (target == target_end) {
    1469           0 :                                         target_block = blocks + target_block->successors[0];
    1470           0 :                                         goto next_target_znz;
    1471          17 :                                 } else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
    1472           0 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1473           0 :                                                    same_type == ZEND_OP1_TYPE(target) &&
    1474           0 :                                                    same_var == VAR_NUM_EX(target->op1) &&
    1475           0 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1476             :                                     /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
    1477             :                                         DEL_SOURCE(block, block->successors[0]);
    1478           0 :                                         block->successors[0] = target_block->successors[0];
    1479             :                                         ADD_SOURCE(block, block->successors[0]);
    1480          17 :                                 } else if (target->opcode == ZEND_JMPNZ &&
    1481           0 :                                                    (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) &&
    1482           0 :                                                    same_type == ZEND_OP1_TYPE(target) &&
    1483           0 :                                                    same_var == VAR_NUM_EX(target->op1) &&
    1484           0 :                                                    !(target_block->flags & ZEND_BB_PROTECTED)) {
    1485             :                     /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
    1486             :                                         DEL_SOURCE(block, block->successors[0]);
    1487           0 :                                         block->successors[0] = target_block->successors[1];
    1488             :                                         ADD_SOURCE(block, block->successors[0]);
    1489          17 :                                 } else if (target->opcode == ZEND_JMP &&
    1490           0 :                                                !(target_block->flags & ZEND_BB_PROTECTED)) {
    1491             :                     /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */
    1492             :                                         DEL_SOURCE(block, block->successors[0]);
    1493           0 :                                         block->successors[0] = target_block->successors[0];
    1494             :                                         ADD_SOURCE(block, block->successors[0]);
    1495             :                                 }
    1496             :                         }
    1497          29 :                         break;
    1498             :                 }
    1499             :         }
    1500             : }
    1501             : 
    1502             : /* Global data dependencies */
    1503             : 
    1504             : /* Find a set of variables which are used outside of the block where they are
    1505             :  * defined. We won't apply some optimization patterns for such variables. */
    1506        2916 : static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
    1507             : {
    1508             :         int n;
    1509             :         zend_basic_block *block, *next_block;
    1510             :         uint32_t var_num;
    1511             :         uint32_t bitset_len;
    1512             :         zend_bitset usage;
    1513             :         zend_bitset defined_here;
    1514             :         void *checkpoint;
    1515             :         zend_op *opline, *end;
    1516             : 
    1517             : 
    1518        2916 :         if (op_array->T == 0) {
    1519             :                 /* shortcut - if no Ts, nothing to do */
    1520         192 :                 return;
    1521             :         }
    1522             : 
    1523        5448 :         checkpoint = zend_arena_checkpoint(ctx->arena);
    1524        2724 :         bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
    1525        5448 :         defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
    1526             : 
    1527        2724 :         zend_bitset_clear(defined_here, bitset_len);
    1528        9092 :         for (n = 1; n < cfg->blocks_count; n++) {
    1529        6368 :                 block = cfg->blocks + n;
    1530             : 
    1531        6368 :                 if (!(block->flags & ZEND_BB_REACHABLE)) {
    1532        2484 :                         continue;
    1533             :                 }
    1534             : 
    1535        3884 :                 opline = op_array->opcodes + block->start;
    1536        3884 :                 end = opline + block->len;
    1537        6820 :                 if (!(block->flags & ZEND_BB_FOLLOW) ||
    1538        2936 :                     (block->flags & ZEND_BB_TARGET)) {
    1539             :                         /* Skip continuation of "extended" BB */
    1540        1388 :                         zend_bitset_clear(defined_here, bitset_len);
    1541             :                 }
    1542             : 
    1543       22288 :                 while (opline<end) {
    1544       14520 :                         if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
    1545        3765 :                                 var_num = VAR_NUM(opline->op1.var);
    1546        3765 :                                 if (!zend_bitset_in(defined_here, var_num)) {
    1547         316 :                                         zend_bitset_incl(used_ext, var_num);
    1548             :                                 }
    1549             :                         }
    1550       14520 :                         if (opline->op2_type == IS_VAR) {
    1551         649 :                                 var_num = VAR_NUM(opline->op2.var);
    1552        1298 :                                 if (opline->opcode == ZEND_FE_FETCH_R ||
    1553         649 :                                     opline->opcode == ZEND_FE_FETCH_RW) {
    1554             :                                         /* these opcode use the op2 as result */
    1555           0 :                                         zend_bitset_incl(defined_here, var_num);
    1556         649 :                                 } else if (!zend_bitset_in(defined_here, var_num)) {
    1557          13 :                                         zend_bitset_incl(used_ext, var_num);
    1558             :                                 }
    1559       13871 :                         } else if (opline->op2_type == IS_TMP_VAR) {
    1560         292 :                                 var_num = VAR_NUM(opline->op2.var);
    1561         292 :                                 if (!zend_bitset_in(defined_here, var_num)) {
    1562          46 :                                         zend_bitset_incl(used_ext, var_num);
    1563             :                                 }
    1564             :                         }
    1565             : 
    1566       14520 :                         if (opline->result_type == IS_VAR) {
    1567        2317 :                                 var_num = VAR_NUM(opline->result.var);
    1568        2317 :                                 zend_bitset_incl(defined_here, var_num);
    1569       12203 :                         } else if (opline->result_type == IS_TMP_VAR) {
    1570        2451 :                                 var_num = VAR_NUM(opline->result.var);
    1571        2451 :                                 switch (opline->opcode) {
    1572         552 :                                         case ZEND_ADD_ARRAY_ELEMENT:
    1573             :                                         case ZEND_ROPE_ADD:
    1574             :                                                 /* these opcodes use the result as argument */
    1575         552 :                                                 if (!zend_bitset_in(defined_here, var_num)) {
    1576         275 :                                                         zend_bitset_incl(used_ext, var_num);
    1577             :                                                 }
    1578         552 :                                                 break;
    1579        1899 :                                         default :
    1580        1899 :                                                 zend_bitset_incl(defined_here, var_num);
    1581             :                                 }
    1582        9752 :                         }
    1583       14520 :                         opline++;
    1584             :                 }
    1585             :         }
    1586             : 
    1587        2724 :         if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
    1588           0 :                 int printed = 0;
    1589             :                 uint32_t i;
    1590             : 
    1591           0 :                 for (i = op_array->last_var; i< op_array->T; i++) {
    1592           0 :                         if (zend_bitset_in(used_ext, i)) {
    1593           0 :                                 if (!printed) {
    1594           0 :                                         fprintf(stderr, "NON-LOCAL-VARS: %d", i);
    1595           0 :                                         printed = 1;
    1596             :                                 } else {
    1597           0 :                                         fprintf(stderr, ", %d", i);
    1598             :                                 }
    1599             :                         }
    1600             :                 }
    1601           0 :                 if (printed) {
    1602           0 :                         fprintf(stderr, "\n");
    1603             :                 }
    1604             :         }
    1605             : 
    1606        2724 :         usage = defined_here;
    1607        2724 :         next_block = NULL;
    1608       14540 :         for (n = cfg->blocks_count; n > 0;) {
    1609        9092 :                 block = cfg->blocks + (--n);
    1610             : 
    1611        9092 :                 if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
    1612        2490 :                         continue;
    1613             :                 }
    1614             : 
    1615        6602 :                 end = op_array->opcodes + block->start;
    1616        6602 :                 opline = end + block->len - 1;
    1617       10480 :                 if (!next_block ||
    1618        6808 :                     !(next_block->flags & ZEND_BB_FOLLOW) ||
    1619        2930 :                     (next_block->flags & ZEND_BB_TARGET)) {
    1620             :                         /* Skip continuation of "extended" BB */
    1621        4112 :                         zend_bitset_copy(usage, used_ext, bitset_len);
    1622        2490 :                 } else if (block->successors[1] != -1) {
    1623        1024 :                         zend_bitset_union(usage, used_ext, bitset_len);
    1624             :                 }
    1625        6602 :                 next_block = block;
    1626             : 
    1627       48398 :                 while (opline >= end) {
    1628             :                         /* usage checks */
    1629       35194 :                         if (opline->result_type == IS_VAR) {
    1630        5144 :                                 if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
    1631          12 :                                         switch (opline->opcode) {
    1632           0 :                                                 case ZEND_ASSIGN_ADD:
    1633             :                                                 case ZEND_ASSIGN_SUB:
    1634             :                                                 case ZEND_ASSIGN_MUL:
    1635             :                                                 case ZEND_ASSIGN_DIV:
    1636             :                                                 case ZEND_ASSIGN_POW:
    1637             :                                                 case ZEND_ASSIGN_MOD:
    1638             :                                                 case ZEND_ASSIGN_SL:
    1639             :                                                 case ZEND_ASSIGN_SR:
    1640             :                                                 case ZEND_ASSIGN_CONCAT:
    1641             :                                                 case ZEND_ASSIGN_BW_OR:
    1642             :                                                 case ZEND_ASSIGN_BW_AND:
    1643             :                                                 case ZEND_ASSIGN_BW_XOR:
    1644             :                                                 case ZEND_PRE_INC:
    1645             :                                                 case ZEND_PRE_DEC:
    1646             :                                                 case ZEND_ASSIGN:
    1647             :                                                 case ZEND_ASSIGN_REF:
    1648             :                                                 case ZEND_DO_FCALL:
    1649             :                                                 case ZEND_DO_ICALL:
    1650             :                                                 case ZEND_DO_UCALL:
    1651             :                                                 case ZEND_DO_FCALL_BY_NAME:
    1652           0 :                                                         opline->result_type = IS_UNUSED;
    1653           0 :                                                         break;
    1654             :                                         }
    1655             :                                 } else {
    1656        5132 :                                         zend_bitset_excl(usage, VAR_NUM(opline->result.var));
    1657             :                                 }
    1658       30050 :                         } else if (opline->result_type == IS_TMP_VAR) {
    1659        3159 :                                 if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
    1660          59 :                                         switch (opline->opcode) {
    1661           0 :                                                 case ZEND_POST_INC:
    1662             :                                                 case ZEND_POST_DEC:
    1663           0 :                                                         opline->opcode -= 2;
    1664           0 :                                                         opline->result_type = IS_UNUSED;
    1665           0 :                                                         break;
    1666           4 :                                                 case ZEND_QM_ASSIGN:
    1667             :                                                 case ZEND_BOOL:
    1668             :                                                 case ZEND_BOOL_NOT:
    1669           4 :                                                         if (ZEND_OP1_TYPE(opline) == IS_CV) {
    1670           0 :                                                                 opline->opcode = ZEND_CHECK_VAR;
    1671           0 :                                                                 SET_UNUSED(opline->result);
    1672           4 :                                                         } else if (ZEND_OP1_TYPE(opline) & (IS_TMP_VAR|IS_VAR)) {
    1673           1 :                                                                 opline->opcode = ZEND_FREE;
    1674           1 :                                                                 SET_UNUSED(opline->result);
    1675             :                                                         } else {
    1676           3 :                                                                 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
    1677           6 :                                                                         literal_dtor(&ZEND_OP1_LITERAL(opline));
    1678             :                                                                 }
    1679           3 :                                                                 MAKE_NOP(opline);
    1680             :                                                         }
    1681           4 :                                                         break;
    1682          55 :                                                 case ZEND_JMPZ_EX:
    1683             :                                                 case ZEND_JMPNZ_EX:
    1684          55 :                                                         opline->opcode -= 3;
    1685          55 :                                                         SET_UNUSED(opline->result);
    1686          55 :                                                         break;
    1687           0 :                                                 case ZEND_ADD_ARRAY_ELEMENT:
    1688             :                                                 case ZEND_ROPE_ADD:
    1689           0 :                                                         zend_bitset_incl(usage, VAR_NUM(opline->result.var));
    1690           0 :                                                         break;
    1691             :                                         }
    1692             :                                 } else {
    1693        3100 :                                         switch (opline->opcode) {
    1694         552 :                                                 case ZEND_ADD_ARRAY_ELEMENT:
    1695             :                                                 case ZEND_ROPE_ADD:
    1696         552 :                                                         break;
    1697        2548 :                                                 default:
    1698        2548 :                                                         zend_bitset_excl(usage, VAR_NUM(opline->result.var));
    1699        2548 :                                                         break;
    1700             :                                         }
    1701       26891 :                                 }
    1702             :                         }
    1703             : 
    1704       35194 :                         if (opline->op2_type == IS_VAR) {
    1705        1020 :                                 switch (opline->opcode) {
    1706           0 :                                         case ZEND_FE_FETCH_R:
    1707             :                                         case ZEND_FE_FETCH_RW:
    1708           0 :                                                 zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
    1709           0 :                                                 break;
    1710        1020 :                                         default:
    1711        1020 :                                                 zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
    1712        1020 :                                                 break;
    1713             :                                 }
    1714       34174 :                         } else if (opline->op2_type == IS_TMP_VAR) {
    1715         375 :                                 zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
    1716             :                         }
    1717             : 
    1718       35194 :                         if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
    1719        6555 :                                 zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
    1720             :                         }
    1721             : 
    1722       35194 :                         opline--;
    1723             :                 }
    1724             :         }
    1725             : 
    1726        2724 :         zend_arena_release(&ctx->arena, checkpoint);
    1727             : }
    1728             : 
    1729        2187 : static void zend_merge_blocks(zend_op_array *op_array, zend_cfg *cfg)
    1730             : {
    1731             :         int i;
    1732             :         zend_basic_block *b, *bb;
    1733        2187 :         zend_basic_block *prev = NULL;
    1734             : 
    1735        9207 :         for (i = 0; i < cfg->blocks_count; i++) {
    1736        7020 :                 b = cfg->blocks + i;
    1737        7020 :                 if (b->flags & ZEND_BB_REACHABLE) {
    1738        7461 :                         if ((b->flags & ZEND_BB_FOLLOW) &&
    1739        3271 :                             !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
    1740         896 :                             prev &&
    1741        1173 :                             prev->successors[0] == i && prev->successors[1] == -1)
    1742         277 :                         {
    1743         277 :                                 zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
    1744         277 :                                 if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
    1745           0 :                                         MAKE_NOP(last_op);
    1746             :                                 }
    1747             : 
    1748         661 :                                 for (bb = prev + 1; bb != b; bb++) {
    1749         384 :                                         zend_op *op = op_array->opcodes + bb->start;
    1750         384 :                                         zend_op *end = op + bb->len;
    1751        1320 :                                         while (op < end) {
    1752         552 :                                                 if (ZEND_OP1_TYPE(op) == IS_CONST) {
    1753         678 :                                                         literal_dtor(&ZEND_OP1_LITERAL(op));
    1754             :                                                 }
    1755         552 :                                                 if (ZEND_OP2_TYPE(op) == IS_CONST) {
    1756         122 :                                                         literal_dtor(&ZEND_OP2_LITERAL(op));
    1757             :                                                 }
    1758         552 :                                                 MAKE_NOP(op);
    1759         552 :                                                 op++;
    1760             :                                         }
    1761             :                                         /* make block empty */
    1762         384 :                                         bb->len = 0;
    1763             :                                 }
    1764             : 
    1765             :                                 /* re-link */
    1766         277 :                                 prev->flags |= (b->flags & ZEND_BB_EXIT);
    1767         277 :                                 prev->len = b->start + b->len - prev->start;
    1768         277 :                                 prev->successors[0] = b->successors[0];
    1769         277 :                                 prev->successors[1] = b->successors[1];
    1770             : 
    1771             :                                 /* unlink & make block empty and unreachable */
    1772         277 :                                 b->flags = 0;
    1773         277 :                                 b->len = 0;
    1774         277 :                                 b->successors[0] = -1;
    1775         277 :                                 b->successors[1] = -1;
    1776             :                         } else {
    1777        4809 :                                 prev = b;
    1778             :                         }
    1779             :                 }
    1780             :         }
    1781        2187 : }
    1782             : 
    1783             : #define PASSES 3
    1784             : 
    1785         729 : void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
    1786             : {
    1787             :         zend_cfg cfg;
    1788             :         zend_basic_block *blocks, *end, *b;
    1789             :         int pass;
    1790             :         uint32_t bitset_len;
    1791             :         zend_bitset usage;
    1792             :         void *checkpoint;
    1793             :         zend_op **Tsource;
    1794             :         zend_uchar *same_t;
    1795             : 
    1796             :     /* Build CFG */
    1797        1458 :         checkpoint = zend_arena_checkpoint(ctx->arena);
    1798         729 :         if (zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_SPLIT_AT_LIVE_RANGES, &cfg, NULL) != SUCCESS) {
    1799           0 :                 zend_arena_release(&ctx->arena, checkpoint);
    1800           0 :                 return;
    1801             :         }
    1802             : 
    1803         729 :         if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) {
    1804           0 :                 zend_arena_release(&ctx->arena, checkpoint);
    1805           0 :                 return;
    1806             :         }
    1807             : 
    1808         729 :         if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
    1809           0 :                 zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
    1810             :         }
    1811             : 
    1812         729 :         if (op_array->last_var || op_array->T) {
    1813         688 :                 bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
    1814        1376 :                 Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
    1815        1376 :                 same_t = zend_arena_alloc(&ctx->arena, op_array->last_var + op_array->T);
    1816        1376 :                 usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
    1817             :         } else {
    1818          41 :                 bitset_len = 0;
    1819          41 :                 Tsource = NULL;
    1820          41 :                 same_t = NULL;
    1821          41 :                 usage = NULL;
    1822             :         }
    1823         729 :         blocks = cfg.blocks;
    1824         729 :         end = blocks + cfg.blocks_count;
    1825        2916 :         for (pass = 0; pass < PASSES; pass++) {
    1826             :                 /* Compute data dependencies */
    1827        2187 :                 zend_bitset_clear(usage, bitset_len);
    1828        2187 :                 zend_t_usage(&cfg, op_array, usage, ctx);
    1829             : 
    1830             :                 /* optimize each basic block separately */
    1831        9207 :                 for (b = blocks; b < end; b++) {
    1832        7020 :                         if (!(b->flags & ZEND_BB_REACHABLE)) {
    1833        1815 :                                 continue;
    1834             :                         }
    1835             :                         /* we track data dependencies only insight a single basic block */
    1836        7442 :                         if (!(b->flags & ZEND_BB_FOLLOW) ||
    1837        2237 :                             (b->flags & ZEND_BB_TARGET)) {
    1838             :                                 /* Skip continuation of "extended" BB */
    1839        3308 :                                 memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
    1840             :                         }
    1841        5205 :                         zend_optimize_block(b, op_array, usage, &cfg, Tsource);
    1842             :                 }
    1843             : 
    1844             :                 /* Eliminate NOPs */
    1845        9207 :                 for (b = blocks; b < end; b++) {
    1846        7020 :                         if (b->flags & ZEND_BB_REACHABLE) {
    1847        5205 :                                 strip_nops(op_array, b);
    1848             :                         }
    1849             :                 }
    1850             : 
    1851             :                 /* Jump optimization for each block */
    1852        9207 :                 for (b = blocks; b < end; b++) {
    1853        7020 :                         if (b->flags & ZEND_BB_REACHABLE) {
    1854        5205 :                                 zend_jmp_optimization(b, op_array, &cfg, same_t);
    1855             :                         }
    1856             :                 }
    1857             : 
    1858             :                 /* Eliminate unreachable basic blocks */
    1859        2187 :                 zend_cfg_remark_reachable_blocks(op_array, &cfg);
    1860             : 
    1861             :                 /* Merge Blocks */
    1862        2187 :                 zend_merge_blocks(op_array, &cfg);
    1863             :         }
    1864             : 
    1865         729 :         zend_bitset_clear(usage, bitset_len);
    1866         729 :         zend_t_usage(&cfg, op_array, usage, ctx);
    1867         729 :         assemble_code_blocks(&cfg, op_array);
    1868             : 
    1869         729 :         if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
    1870           0 :                 zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
    1871             :         }
    1872             : 
    1873             :         /* Destroy CFG */
    1874         729 :         zend_arena_release(&ctx->arena, checkpoint);
    1875             : }

Generated by: LCOV version 1.10

Generated at Sat, 28 Jul 2018 01:57:16 +0000 (23 days ago)

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