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 - zend_optimizer.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 322 523 61.6 %
Date: 2017-10-15 Functions: 18 27 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend OPcache                                                         |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2017 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_cfg.h"
      30             : #include "zend_func_info.h"
      31             : #include "zend_call_graph.h"
      32             : #include "zend_inference.h"
      33             : #include "zend_dump.h"
      34             : 
      35             : #ifndef HAVE_DFA_PASS
      36             : # define HAVE_DFA_PASS 1
      37             : #endif
      38             : 
      39           0 : static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
      40             : {
      41             :         zval_dtor(zvalue);
      42           0 : }
      43             : 
      44           0 : void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
      45             : {
      46             :         zval val;
      47             : 
      48           0 :         if (!ctx->constants) {
      49           0 :                 ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
      50           0 :                 zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
      51             :         }
      52           0 :         ZVAL_DUP(&val, value);
      53           0 :         zend_hash_add(ctx->constants, Z_STR_P(name), &val);
      54           0 : }
      55             : 
      56           0 : int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
      57             : {
      58             :         zval *val;
      59             : 
      60           0 :         if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
      61           0 :                 ZVAL_DUP(value, val);
      62           0 :                 return 1;
      63             :         }
      64           0 :         return 0;
      65             : }
      66             : 
      67           0 : int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
      68             : {
      69           0 :         int i = 0;
      70           0 :         zend_ulong hash_value = zend_string_hash_val(name);
      71             : 
      72           0 :         while (i < op_array->last_var) {
      73           0 :                 if (op_array->vars[i] == name ||
      74           0 :                     (ZSTR_H(op_array->vars[i]) == hash_value &&
      75           0 :                      ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) &&
      76           0 :                      memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) {
      77           0 :                         return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
      78             :                 }
      79           0 :                 i++;
      80             :         }
      81           0 :         i = op_array->last_var;
      82           0 :         op_array->last_var++;
      83           0 :         op_array->vars = erealloc(op_array->vars, op_array->last_var * sizeof(zend_string*));
      84           0 :         op_array->vars[i] = zend_string_dup(name, 0);
      85             : 
      86             :         /* all IS_TMP_VAR and IS_VAR variable numbers have to be adjusted */
      87             :         {
      88           0 :                 zend_op *opline = op_array->opcodes;
      89           0 :                 zend_op *end = opline + op_array->last;
      90           0 :                 while (opline < end) {
      91           0 :                         if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
      92           0 :                                 opline->op1.var += sizeof(zval);
      93             :                         }
      94           0 :                         if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
      95           0 :                                 opline->op2.var += sizeof(zval);
      96             :                         }
      97           0 :                         if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
      98           0 :                                 opline->result.var += sizeof(zval);
      99             :                         }
     100           0 :                         opline++;
     101             :                 }
     102             :         }
     103             : 
     104           0 :         return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
     105             : }
     106             : 
     107         591 : int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
     108             : {
     109         591 :         int i = op_array->last_literal;
     110         591 :         op_array->last_literal++;
     111         591 :         op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
     112         591 :         ZVAL_COPY_VALUE(&op_array->literals[i], zv);
     113         591 :         Z_CACHE_SLOT(op_array->literals[i]) = -1;
     114         591 :         return i;
     115             : }
     116             : 
     117           0 : static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
     118             :         zval zv;
     119           0 :         ZVAL_STR(&zv, str);
     120             :         zend_string_hash_val(str);
     121           0 :         return zend_optimizer_add_literal(op_array, &zv);
     122             : }
     123             : 
     124         481 : int zend_optimizer_is_disabled_func(const char *name, size_t len) {
     125         962 :         zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
     126             : 
     127         962 :         return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
     128         481 :                         fbc->internal_function.handler == ZEND_FN(display_disabled_function));
     129             : }
     130             : 
     131           0 : static inline void drop_leading_backslash(zval *val) {
     132           0 :         if (Z_STRVAL_P(val)[0] == '\\') {
     133           0 :                 zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
     134             :                 zval_dtor(val);
     135           0 :                 ZVAL_STR(val, str);
     136             :         }
     137           0 : }
     138             : 
     139           0 : static inline void alloc_cache_slots_op1(zend_op_array *op_array, zend_op *opline, uint32_t num) {
     140           0 :         Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size;
     141           0 :         op_array->cache_size += num * sizeof(void *);
     142           0 : }
     143           0 : static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) {
     144           0 :         Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size;
     145           0 :         op_array->cache_size += num * sizeof(void *);
     146           0 : }
     147             : 
     148             : #define REQUIRES_STRING(val) do { \
     149             :         if (Z_TYPE_P(val) != IS_STRING) { \
     150             :                 zval_dtor(val); \
     151             :                 return 0; \
     152             :         } \
     153             : } while (0)
     154             : 
     155             : #define TO_STRING_NOWARN(val) do { \
     156             :         if (Z_TYPE_P(val) >= IS_ARRAY) { \
     157             :                 zval_dtor(val); \
     158             :                 return 0; \
     159             :         } \
     160             :         convert_to_string(val); \
     161             : } while (0)
     162             : 
     163         579 : int zend_optimizer_update_op1_const(zend_op_array *op_array,
     164             :                                     zend_op       *opline,
     165             :                                     zval          *val)
     166             : {
     167         579 :         switch (opline->opcode) {
     168             :                 case ZEND_FREE:
     169           0 :                         MAKE_NOP(opline);
     170             :                         zval_dtor(val);
     171           0 :                         return 1;
     172             :                 case ZEND_INIT_STATIC_METHOD_CALL:
     173             :                 case ZEND_CATCH:
     174             :                 case ZEND_FETCH_CONSTANT:
     175             :                 case ZEND_FETCH_CLASS_CONSTANT:
     176             :                 case ZEND_DEFINED:
     177             :                 case ZEND_NEW:
     178           0 :                         REQUIRES_STRING(val);
     179           0 :                         drop_leading_backslash(val);
     180           0 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     181           0 :                         alloc_cache_slots_op1(op_array, opline, 1);
     182           0 :                         zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     183           0 :                         break;
     184             :                 case ZEND_FETCH_STATIC_PROP_R:
     185             :                 case ZEND_FETCH_STATIC_PROP_W:
     186             :                 case ZEND_FETCH_STATIC_PROP_RW:
     187             :                 case ZEND_FETCH_STATIC_PROP_IS:
     188             :                 case ZEND_FETCH_STATIC_PROP_UNSET:
     189             :                 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
     190           0 :                         TO_STRING_NOWARN(val);
     191           0 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     192           0 :                         alloc_cache_slots_op1(op_array, opline, 2);
     193           0 :                         break;
     194             :                 case ZEND_SEND_VAR:
     195           1 :                         opline->opcode = ZEND_SEND_VAL;
     196           1 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     197           1 :                         break;
     198             :                 case ZEND_SEPARATE:
     199             :                 case ZEND_SEND_VAR_NO_REF:
     200             :                 case ZEND_SEND_VAR_NO_REF_EX:
     201           6 :                         zval_ptr_dtor(val);
     202           6 :                         return 0;
     203             :                 case ZEND_VERIFY_RETURN_TYPE:
     204             :                         /* This would require a non-local change.
     205             :                          * zend_optimizer_replace_by_const() supports this. */
     206           0 :                         zval_ptr_dtor(val);
     207           0 :                         return 0;
     208             :                 case ZEND_CONCAT:
     209             :                 case ZEND_FAST_CONCAT:
     210             :                 case ZEND_FETCH_R:
     211             :                 case ZEND_FETCH_W:
     212             :                 case ZEND_FETCH_RW:
     213             :                 case ZEND_FETCH_IS:
     214             :                 case ZEND_FETCH_UNSET:
     215             :                 case ZEND_FETCH_FUNC_ARG:
     216          20 :                         TO_STRING_NOWARN(val);
     217             :                         /* break missing intentionally */
     218             :                 default:
     219         572 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     220             :                         break;
     221             :         }
     222             : 
     223         573 :         ZEND_OP1_TYPE(opline) = IS_CONST;
     224        1146 :         if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
     225          23 :                 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
     226             :         }
     227         573 :         return 1;
     228             : }
     229             : 
     230           5 : int zend_optimizer_update_op2_const(zend_op_array *op_array,
     231             :                                     zend_op       *opline,
     232             :                                     zval          *val)
     233             : {
     234           5 :         switch (opline->opcode) {
     235             :                 case ZEND_ASSIGN_REF:
     236             :                 case ZEND_FAST_CALL:
     237             :                         zval_dtor(val);
     238           1 :                         return 0;
     239             :                 case ZEND_FETCH_CLASS:
     240             :                 case ZEND_INIT_FCALL_BY_NAME:
     241             :                 /*case ZEND_INIT_NS_FCALL_BY_NAME:*/
     242             :                 case ZEND_ADD_INTERFACE:
     243             :                 case ZEND_ADD_TRAIT:
     244             :                 case ZEND_INSTANCEOF:
     245             :                 case ZEND_FETCH_STATIC_PROP_R:
     246             :                 case ZEND_FETCH_STATIC_PROP_W:
     247             :                 case ZEND_FETCH_STATIC_PROP_RW:
     248             :                 case ZEND_FETCH_STATIC_PROP_IS:
     249             :                 case ZEND_FETCH_STATIC_PROP_UNSET:
     250             :                 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
     251             :                 case ZEND_UNSET_STATIC_PROP:
     252             :                 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
     253           0 :                         REQUIRES_STRING(val);
     254           0 :                         drop_leading_backslash(val);
     255           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     256           0 :                         zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     257           0 :                         alloc_cache_slots_op2(op_array, opline, 1);
     258           0 :                         break;
     259             :                 case ZEND_INIT_FCALL:
     260           0 :                         REQUIRES_STRING(val);
     261           0 :                         zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
     262           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     263           0 :                         alloc_cache_slots_op2(op_array, opline, 1);
     264           0 :                         break;
     265             :                 case ZEND_INIT_DYNAMIC_CALL:
     266           0 :                         if (Z_TYPE_P(val) == IS_STRING) {
     267           0 :                                 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
     268             :                                         zval_dtor(val);
     269           0 :                                         return 0;
     270             :                                 }
     271             : 
     272           0 :                                 if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
     273             :                                         /* Dynamic call to various special functions must stay dynamic,
     274             :                                          * otherwise would drop a warning */
     275             :                                         zval_dtor(val);
     276           0 :                                         return 0;
     277             :                                 }
     278             : 
     279           0 :                                 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
     280           0 :                                 drop_leading_backslash(val);
     281           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     282           0 :                                 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     283           0 :                                 alloc_cache_slots_op2(op_array, opline, 1);
     284             :                         } else {
     285           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     286             :                         }
     287           0 :                         break;
     288             :                 case ZEND_INIT_METHOD_CALL:
     289             :                 case ZEND_INIT_STATIC_METHOD_CALL:
     290           0 :                         REQUIRES_STRING(val);
     291           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     292           0 :                         zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     293           0 :                         alloc_cache_slots_op2(op_array, opline, 2);
     294           0 :                         break;
     295             :                 /*case ZEND_FETCH_CLASS_CONSTANT:*/
     296             :                 case ZEND_ASSIGN_OBJ:
     297             :                 case ZEND_FETCH_OBJ_R:
     298             :                 case ZEND_FETCH_OBJ_W:
     299             :                 case ZEND_FETCH_OBJ_RW:
     300             :                 case ZEND_FETCH_OBJ_IS:
     301             :                 case ZEND_FETCH_OBJ_UNSET:
     302             :                 case ZEND_FETCH_OBJ_FUNC_ARG:
     303             :                 case ZEND_UNSET_OBJ:
     304             :                 case ZEND_PRE_INC_OBJ:
     305             :                 case ZEND_PRE_DEC_OBJ:
     306             :                 case ZEND_POST_INC_OBJ:
     307             :                 case ZEND_POST_DEC_OBJ:
     308             :                 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
     309           0 :                         TO_STRING_NOWARN(val);
     310           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     311           0 :                         alloc_cache_slots_op2(op_array, opline, 2);
     312           0 :                         break;
     313             :                 case ZEND_ASSIGN_ADD:
     314             :                 case ZEND_ASSIGN_SUB:
     315             :                 case ZEND_ASSIGN_MUL:
     316             :                 case ZEND_ASSIGN_DIV:
     317             :                 case ZEND_ASSIGN_POW:
     318             :                 case ZEND_ASSIGN_MOD:
     319             :                 case ZEND_ASSIGN_SL:
     320             :                 case ZEND_ASSIGN_SR:
     321             :                 case ZEND_ASSIGN_CONCAT:
     322             :                 case ZEND_ASSIGN_BW_OR:
     323             :                 case ZEND_ASSIGN_BW_AND:
     324             :                 case ZEND_ASSIGN_BW_XOR:
     325           0 :                         if (opline->extended_value == ZEND_ASSIGN_OBJ) {
     326           0 :                                 TO_STRING_NOWARN(val);
     327           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     328           0 :                                 alloc_cache_slots_op2(op_array, opline, 2);
     329             :                         } else {
     330           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     331             :                         }
     332           0 :                         break;
     333             :                 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
     334             :                 case ZEND_ADD_ARRAY_ELEMENT:
     335             :                 case ZEND_INIT_ARRAY:
     336             :                 case ZEND_ASSIGN_DIM:
     337             :                 case ZEND_UNSET_DIM:
     338             :                 case ZEND_FETCH_DIM_R:
     339             :                 case ZEND_FETCH_DIM_W:
     340             :                 case ZEND_FETCH_DIM_RW:
     341             :                 case ZEND_FETCH_DIM_IS:
     342             :                 case ZEND_FETCH_DIM_FUNC_ARG:
     343             :                 case ZEND_FETCH_DIM_UNSET:
     344             :                 case ZEND_FETCH_LIST:
     345           1 :                         if (Z_TYPE_P(val) == IS_STRING) {
     346             :                                 zend_ulong index;
     347           2 :                                 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
     348             :                                         zval_dtor(val);
     349           0 :                                         ZVAL_LONG(val, index);
     350             :                                 }
     351             :                         }
     352           1 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     353           1 :                         break;
     354             :                 case ZEND_ROPE_INIT:
     355             :                 case ZEND_ROPE_ADD:
     356             :                 case ZEND_ROPE_END:
     357             :                 case ZEND_CONCAT:
     358             :                 case ZEND_FAST_CONCAT:
     359           4 :                         TO_STRING_NOWARN(val);
     360             :                         /* break missing intentionally */
     361             :                 default:
     362           3 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     363             :                         break;
     364             :         }
     365             : 
     366           4 :         ZEND_OP2_TYPE(opline) = IS_CONST;
     367           8 :         if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
     368           4 :                 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
     369             :         }
     370           4 :         return 1;
     371             : }
     372             : 
     373         573 : void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
     374             : {
     375         573 :         if (op_array->last_live_range) {
     376         128 :                 int i = 0;
     377         128 :                 int j = 0;
     378             :                 uint32_t *map;
     379             :                 ALLOCA_FLAG(use_heap);
     380             : 
     381         128 :                 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
     382             : 
     383             :                 do {
     384         380 :                         if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
     385         375 :                                 map[i] = j;
     386         375 :                                 if (i != j) {
     387           1 :                                         op_array->live_range[j] = op_array->live_range[i];
     388             :                                 }
     389         375 :                                 j++;
     390             :                         }
     391         380 :                         i++;
     392         380 :                 } while (i < op_array->last_live_range);
     393         128 :                 if (i != j) {
     394           5 :                         if ((op_array->last_live_range = j)) {
     395           1 :                                 zend_op *opline = op_array->opcodes;
     396           1 :                                 zend_op *end = opline + op_array->last;
     397             : 
     398          15 :                                 while (opline != end) {
     399          13 :                                         if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
     400           0 :                                                         opline->extended_value == ZEND_FREE_ON_RETURN) {
     401           0 :                                                 opline->op2.num = map[opline->op2.num];
     402             :                                         }
     403          13 :                                         opline++;
     404             :                                 }
     405             :                         } else {
     406           4 :                                 efree(op_array->live_range);
     407           4 :                                 op_array->live_range = NULL;
     408             :                         }
     409             :                 }
     410         128 :                 free_alloca(map, use_heap);
     411             :         }
     412         573 : }
     413             : 
     414         569 : int zend_optimizer_replace_by_const(zend_op_array *op_array,
     415             :                                     zend_op       *opline,
     416             :                                     zend_uchar     type,
     417             :                                     uint32_t       var,
     418             :                                     zval          *val)
     419             : {
     420         569 :         zend_op *end = op_array->opcodes + op_array->last;
     421             : 
     422        1196 :         while (opline < end) {
     423        1192 :                 if (ZEND_OP1_TYPE(opline) == type &&
     424         565 :                         ZEND_OP1(opline).var == var) {
     425         565 :                         switch (opline->opcode) {
     426             :                                 case ZEND_FETCH_DIM_W:
     427             :                                 case ZEND_FETCH_DIM_RW:
     428             :                                 case ZEND_FETCH_DIM_FUNC_ARG:
     429             :                                 case ZEND_FETCH_DIM_UNSET:
     430             :                                 case ZEND_ASSIGN_DIM:
     431             :                                 case ZEND_SEPARATE:
     432             :                                 case ZEND_RETURN_BY_REF:
     433             :                                         zval_dtor(val);
     434           0 :                                         return 0;
     435             :                                 case ZEND_SEND_VAR:
     436           1 :                                         opline->extended_value = 0;
     437           1 :                                         opline->opcode = ZEND_SEND_VAL;
     438           1 :                                         break;
     439             :                                 case ZEND_SEND_VAR_EX:
     440           0 :                                         opline->extended_value = 0;
     441           0 :                                         opline->opcode = ZEND_SEND_VAL_EX;
     442           0 :                                         break;
     443             :                                 case ZEND_SEND_VAR_NO_REF:
     444             :                                         zval_dtor(val);
     445           0 :                                         return 0;
     446             :                                 case ZEND_SEND_VAR_NO_REF_EX:
     447           0 :                                         opline->opcode = ZEND_SEND_VAL;
     448           0 :                                         break;
     449             :                                 case ZEND_SEND_USER:
     450           0 :                                         opline->opcode = ZEND_SEND_VAL_EX;
     451           0 :                                         break;
     452             :                                 /* In most cases IS_TMP_VAR operand may be used only once.
     453             :                                  * The operands are usually destroyed by the opcode handler.
     454             :                                  * ZEND_CASE and ZEND_FETCH_LIST are exceptions, they keeps operand
     455             :                                  * unchanged, and allows its reuse. these instructions
     456             :                                  * usually terminated by ZEND_FREE that finally kills the value.
     457             :                                  */
     458             :                                 case ZEND_FETCH_LIST: {
     459           1 :                                         zend_op *m = opline;
     460             :                                         do {
     461           8 :                                                 if (m->opcode == ZEND_FETCH_LIST &&
     462           2 :                                                         ZEND_OP1_TYPE(m) == type &&
     463           2 :                                                         ZEND_OP1(m).var == var) {
     464           2 :                                                         zend_optimizer_update_op1_const(op_array, m, val);
     465             :                                                 }
     466           4 :                                                 m++;
     467           4 :                                         } while (m->opcode != ZEND_FREE || ZEND_OP1_TYPE(m) != type || ZEND_OP1(m).var != var);
     468             :                                         ZEND_ASSERT(m->opcode == ZEND_FREE && ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var);
     469           1 :                                         MAKE_NOP(m);
     470           1 :                                         zend_optimizer_remove_live_range(op_array, var);
     471           1 :                                         return 1;
     472             :                                 }
     473             :                                 case ZEND_CASE:
     474             :                                 case ZEND_FREE: {
     475             :                                         zend_op *m, *n;
     476           1 :                                         int brk = op_array->last_live_range;
     477           1 :                                         zend_bool in_switch = 0;
     478           2 :                                         while (brk--) {
     479           2 :                                                 if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
     480           1 :                                                     op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
     481           1 :                                                         in_switch = 1;
     482           1 :                                                         break;
     483             :                                                 }
     484             :                                         }
     485             : 
     486           1 :                                         if (!in_switch) {
     487             :                                                 ZEND_ASSERT(opline->opcode == ZEND_FREE);
     488           0 :                                                 MAKE_NOP(opline);
     489             :                                                 zval_dtor(val);
     490           0 :                                                 return 1;
     491             :                                         }
     492             : 
     493           1 :                                         m = opline;
     494           1 :                                         n = op_array->opcodes + op_array->live_range[brk].end;
     495           3 :                                         if (n->opcode == ZEND_FREE &&
     496           1 :                                             !(n->extended_value & ZEND_FREE_ON_RETURN)) {
     497           1 :                                                 n++;
     498             :                                         } else {
     499           0 :                                                 n = op_array->opcodes + op_array->last;
     500             :                                         }
     501          14 :                                         while (m < n) {
     502          18 :                                                 if (ZEND_OP1_TYPE(m) == type &&
     503           6 :                                                                 ZEND_OP1(m).var == var) {
     504           6 :                                                         if (m->opcode == ZEND_CASE) {
     505             :                                                                 zval old_val;
     506           2 :                                                                 ZVAL_COPY_VALUE(&old_val, val);
     507             :                                                                 zval_copy_ctor(val);
     508           2 :                                                                 zend_optimizer_update_op1_const(op_array, m, val);
     509           2 :                                                                 ZVAL_COPY_VALUE(val, &old_val);
     510           4 :                                                         } else if (m->opcode == ZEND_FREE) {
     511           4 :                                                                 MAKE_NOP(m);
     512             :                                                         } else {
     513             :                                                                 ZEND_ASSERT(0);
     514             :                                                         }
     515             :                                                 }
     516          12 :                                                 m++;
     517             :                                         }
     518             :                                         zval_dtor(val);
     519           1 :                                         zend_optimizer_remove_live_range(op_array, var);
     520           1 :                                         return 1;
     521             :                                 }
     522             :                                 case ZEND_VERIFY_RETURN_TYPE: {
     523           1 :                                         zend_arg_info *ret_info = op_array->arg_info - 1;
     524           4 :                                         if (ret_info->class_name
     525           1 :                                                 || ret_info->type_hint == IS_CALLABLE
     526           2 :                                                 || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
     527           1 :                                                 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
     528             :                                                 zval_dtor(val);
     529           0 :                                                 return 0;
     530             :                                         }
     531           1 :                                         MAKE_NOP(opline);
     532             : 
     533             :                                         /* zend_handle_loops_and_finally may inserts other oplines */
     534             :                                         do {
     535           1 :                                                 ++opline;
     536           1 :                                         } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
     537             :                                         ZEND_ASSERT(ZEND_OP1(opline).var == var);
     538             : 
     539             :                                         break;
     540             :                                   }
     541             :                                 default:
     542             :                                         break;
     543             :                         }
     544         563 :                         if (zend_optimizer_update_op1_const(op_array, opline, val)) {
     545         563 :                                 zend_optimizer_remove_live_range(op_array, var);
     546         563 :                                 return 1;
     547             :                         }
     548           0 :                         return 0;
     549             :                 }
     550             : 
     551          66 :                 if (ZEND_OP2_TYPE(opline) == type &&
     552           4 :                         ZEND_OP2(opline).var == var) {
     553           4 :                         if (zend_optimizer_update_op2_const(op_array, opline, val)) {
     554           4 :                                 zend_optimizer_remove_live_range(op_array, var);
     555           4 :                                 return 1;
     556             :                         }
     557           0 :                         return 0;
     558             :                 }
     559          58 :                 opline++;
     560             :         }
     561             : 
     562           0 :         return 1;
     563             : }
     564             : 
     565         294 : static zend_class_entry *get_class_entry_from_op1(
     566             :                 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
     567         294 :         if (opline->op1_type == IS_CONST) {
     568         283 :                 zval *op1 = CRT_CONSTANT_EX(op_array, opline->op1, rt_constants);
     569         283 :                 if (Z_TYPE_P(op1) == IS_STRING) {
     570         283 :                         zend_string *class_name = Z_STR_P(op1 + 1);
     571             :                         zend_class_entry *ce;
     572         566 :                         if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
     573          27 :                                 return ce;
     574         512 :                         } else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
     575         237 :                                 if (ce->type == ZEND_INTERNAL_CLASS) {
     576         237 :                                         return ce;
     577           0 :                                 } else if (ce->type == ZEND_USER_CLASS &&
     578           0 :                                                    ce->info.user.filename &&
     579           0 :                                                    ce->info.user.filename == op_array->filename) {
     580           0 :                                         return ce;
     581             :                                 }
     582             :                         }
     583             :                 }
     584          41 :         } else if (opline->op1_type == IS_UNUSED && op_array->scope
     585          20 :                         && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
     586          10 :                         && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
     587          10 :                 return op_array->scope;
     588             :         }
     589          20 :         return NULL;
     590             : }
     591             : 
     592        2717 : zend_function *zend_optimizer_get_called_func(
     593             :                 zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
     594             : {
     595             : #define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants)
     596        2717 :         switch (opline->opcode) {
     597             :                 case ZEND_INIT_FCALL:
     598             :                 {
     599        2328 :                         zend_string *function_name = Z_STR_P(GET_OP(op2));
     600             :                         zend_function *func;
     601        4656 :                         if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
     602          62 :                                 return func;
     603        4532 :                         } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
     604        2266 :                                 if (func->type == ZEND_INTERNAL_FUNCTION) {
     605        2266 :                                         return func;
     606           0 :                                 } else if (func->type == ZEND_USER_FUNCTION &&
     607           0 :                                            func->op_array.filename &&
     608           0 :                                            func->op_array.filename == op_array->filename) {
     609           0 :                                         return func;
     610             :                                 }
     611             :                         }
     612           0 :                         break;
     613             :                 }
     614             :                 case ZEND_INIT_FCALL_BY_NAME:
     615             :                 case ZEND_INIT_NS_FCALL_BY_NAME:
     616          46 :                         if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
     617          23 :                                 zval *function_name = GET_OP(op2) + 1;
     618             :                                 zend_function *func;
     619          46 :                                 if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
     620          12 :                                         return func;
     621          22 :                                 } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
     622           0 :                                         if (func->type == ZEND_INTERNAL_FUNCTION) {
     623           0 :                                                 return func;
     624           0 :                                         } else if (func->type == ZEND_USER_FUNCTION &&
     625           0 :                                                    func->op_array.filename &&
     626           0 :                                                    func->op_array.filename == op_array->filename) {
     627           0 :                                                 return func;
     628             :                                         }
     629             :                                 }
     630             :                         }
     631          11 :                         break;
     632             :                 case ZEND_INIT_STATIC_METHOD_CALL:
     633         504 :                         if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
     634         252 :                                 zend_class_entry *ce = get_class_entry_from_op1(
     635         252 :                                         script, op_array, opline, rt_constants);
     636         252 :                                 if (ce) {
     637         238 :                                         zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
     638         476 :                                         return zend_hash_find_ptr(&ce->function_table, func_name);
     639             :                                 }
     640             :                         }
     641          14 :                         break;
     642             :                 case ZEND_INIT_METHOD_CALL:
     643          88 :                         if (opline->op1_type == IS_UNUSED
     644          82 :                                         && opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
     645          18 :                                         && op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
     646           4 :                                 zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
     647             :                                 zend_function *fbc = zend_hash_find_ptr(
     648           8 :                                         &op_array->scope->function_table, method_name);
     649           4 :                                 if (fbc) {
     650           4 :                                         zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
     651           4 :                                         zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
     652           4 :                                         zend_bool same_scope = fbc->common.scope == op_array->scope;
     653           4 :                                         if ((is_private && same_scope)
     654             :                                                         || (is_final && (!is_private || same_scope))) {
     655           2 :                                                 return fbc;
     656             :                                         }
     657             :                                 }
     658             :                         }
     659          70 :                         break;
     660             :                 case ZEND_NEW:
     661             :                 {
     662          42 :                         zend_class_entry *ce = get_class_entry_from_op1(
     663          42 :                                 script, op_array, opline, rt_constants);
     664          42 :                         if (ce && ce->type == ZEND_USER_CLASS) {
     665          13 :                                 return ce->constructor;
     666             :                         }
     667             :                         break;
     668             :                 }
     669             :         }
     670         124 :         return NULL;
     671             : #undef GET_OP
     672             : }
     673             : 
     674        2259 : uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
     675        2259 :         if (zend_string_equals_literal(name, "extract")) {
     676           0 :                 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
     677        2259 :         } else if (zend_string_equals_literal(name, "compact")) {
     678           0 :                 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
     679        2259 :         } else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
     680           0 :                 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
     681        2259 :         } else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
     682           0 :                 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
     683        2259 :         } else if (zend_string_equals_literal(name, "get_defined_vars")) {
     684           0 :                 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
     685        2259 :         } else if (zend_string_equals_literal(name, "assert")) {
     686           2 :                 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
     687        2257 :         } else if (zend_string_equals_literal(name, "func_num_args")) {
     688           0 :                 return ZEND_FUNC_VARARG;
     689        2257 :         } else if (zend_string_equals_literal(name, "func_get_arg")) {
     690           0 :                 return ZEND_FUNC_VARARG;
     691        2257 :         } else if (zend_string_equals_literal(name, "func_get_args")) {
     692           2 :                 return ZEND_FUNC_VARARG;
     693             :         } else {
     694        2255 :                 return 0;
     695             :         }
     696             : }
     697             : 
     698         726 : static void zend_optimize(zend_op_array      *op_array,
     699             :                           zend_optimizer_ctx *ctx)
     700             : {
     701         726 :         if (op_array->type == ZEND_EVAL_CODE) {
     702           0 :                 return;
     703             :         }
     704             : 
     705         726 :         if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
     706           0 :                 zend_dump_op_array(op_array, 0, "before optimizer", NULL);
     707             :         }
     708             : 
     709             :         /* pass 1
     710             :          * - substitute persistent constants (true, false, null, etc)
     711             :          * - perform compile-time evaluation of constant binary and unary operations
     712             :          * - optimize series of ADD_STRING and/or ADD_CHAR
     713             :          * - convert CAST(IS_BOOL,x) into BOOL(x)
     714             :          * - pre-evaluate constant function calls
     715             :          */
     716         726 :         if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
     717         726 :                 zend_optimizer_pass1(op_array, ctx);
     718         726 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
     719           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     720             :                 }
     721             :         }
     722             : 
     723             :         /* pass 2:
     724             :          * - convert non-numeric constants to numeric constants in numeric operators
     725             :          * - optimize constant conditional JMPs
     726             :          */
     727         726 :         if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
     728         726 :                 zend_optimizer_pass2(op_array);
     729         726 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
     730           0 :                         zend_dump_op_array(op_array, 0, "after pass 2", NULL);
     731             :                 }
     732             :         }
     733             : 
     734             :         /* pass 3:
     735             :          * - optimize $i = $i+expr to $i+=expr
     736             :          * - optimize series of JMPs
     737             :          * - change $i++ to ++$i where possible
     738             :          */
     739         726 :         if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
     740         726 :                 zend_optimizer_pass3(op_array);
     741         726 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
     742           0 :                         zend_dump_op_array(op_array, 0, "after pass 3", NULL);
     743             :                 }
     744             :         }
     745             : 
     746             :         /* pass 4:
     747             :          * - INIT_FCALL_BY_NAME -> DO_FCALL
     748             :          */
     749         726 :         if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
     750         726 :                 zend_optimize_func_calls(op_array, ctx);
     751         726 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
     752           0 :                         zend_dump_op_array(op_array, 0, "after pass 4", NULL);
     753             :                 }
     754             :         }
     755             : 
     756             :         /* pass 5:
     757             :          * - CFG optimization
     758             :          */
     759         726 :         if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
     760         726 :                 zend_optimize_cfg(op_array, ctx);
     761         726 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
     762           0 :                         zend_dump_op_array(op_array, 0, "after pass 5", NULL);
     763             :                 }
     764             :         }
     765             : 
     766             : #if HAVE_DFA_PASS
     767             :         /* pass 6:
     768             :          * - DFA optimization
     769             :          */
     770        1452 :         if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
     771         726 :             !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
     772           0 :                 zend_optimize_dfa(op_array, ctx);
     773           0 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
     774           0 :                         zend_dump_op_array(op_array, 0, "after pass 6", NULL);
     775             :                 }
     776             :         }
     777             : #endif
     778             : 
     779             :         /* pass 9:
     780             :          * - Optimize temp variables usage
     781             :          */
     782         726 :         if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
     783         721 :                 zend_optimize_temporary_variables(op_array, ctx);
     784         721 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
     785           0 :                         zend_dump_op_array(op_array, 0, "after pass 9", NULL);
     786             :                 }
     787             :         }
     788             : 
     789             :         /* pass 10:
     790             :          * - remove NOPs
     791             :          */
     792         726 :         if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
     793           0 :                 zend_optimizer_nop_removal(op_array);
     794           0 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
     795           0 :                         zend_dump_op_array(op_array, 0, "after pass 10", NULL);
     796             :                 }
     797             :         }
     798             : 
     799             :         /* pass 11:
     800             :          * - Compact literals table
     801             :          */
     802         726 :         if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
     803         726 :                 zend_optimizer_compact_literals(op_array, ctx);
     804         726 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
     805           0 :                         zend_dump_op_array(op_array, 0, "after pass 11", NULL);
     806             :                 }
     807             :         }
     808             : 
     809         726 :         if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
     810           0 :                 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
     811             :         }
     812             : }
     813             : 
     814        1452 : static void zend_revert_pass_two(zend_op_array *op_array)
     815             : {
     816             :         zend_op *opline, *end;
     817             : 
     818        1452 :         opline = op_array->opcodes;
     819        1452 :         end = opline + op_array->last;
     820       21425 :         while (opline < end) {
     821       18521 :                 if (opline->op1_type == IS_CONST) {
     822        6160 :                         ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
     823             :                 }
     824       18521 :                 if (opline->op2_type == IS_CONST) {
     825        4565 :                         ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
     826             :                 }
     827       18521 :                 opline++;
     828             :         }
     829        1452 : }
     830             : 
     831        1015 : static void zend_redo_pass_two(zend_op_array *op_array)
     832             : {
     833             :         zend_op *opline, *end;
     834             : 
     835        1015 :         opline = op_array->opcodes;
     836        1015 :         end = opline + op_array->last;
     837       14361 :         while (opline < end) {
     838       12331 :                 if (opline->op1_type == IS_CONST) {
     839        4364 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
     840             :                 }
     841       12331 :                 if (opline->op2_type == IS_CONST) {
     842        3199 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
     843             :                 }
     844       12331 :                 ZEND_VM_SET_OPCODE_HANDLER(opline);
     845       12331 :                 opline++;
     846             :         }
     847        1015 : }
     848             : 
     849             : #if HAVE_DFA_PASS
     850         437 : static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
     851             : {
     852             :         zend_op *opline, *end;
     853             : 
     854         437 :         opline = op_array->opcodes;
     855         437 :         end = opline + op_array->last;
     856        4714 :         while (opline < end) {
     857       22747 :                 zend_vm_set_opcode_handler_ex(opline,
     858             :                         opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
     859             :                         opline->op2_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
     860        3840 :                         (opline->opcode == ZEND_PRE_INC ||
     861        3809 :                          opline->opcode == ZEND_PRE_DEC ||
     862        3808 :                          opline->opcode == ZEND_POST_INC ||
     863        3808 :                          opline->opcode == ZEND_POST_DEC) ?
     864          64 :                                 ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
     865        4738 :                                 (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))));
     866        3840 :                 if (opline->op1_type == IS_CONST) {
     867        1141 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
     868             :                 }
     869        3840 :                 if (opline->op2_type == IS_CONST) {
     870         950 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
     871             :                 }
     872        3840 :                 opline++;
     873             :         }
     874         437 : }
     875             : #endif
     876             : 
     877         726 : static void zend_optimize_op_array(zend_op_array      *op_array,
     878             :                                    zend_optimizer_ctx *ctx)
     879             : {
     880             :         /* Revert pass_two() */
     881         726 :         zend_revert_pass_two(op_array);
     882             : 
     883             :         /* Do actual optimizations */
     884         726 :         zend_optimize(op_array, ctx);
     885             : 
     886             :         /* Redo pass_two() */
     887         726 :         zend_redo_pass_two(op_array);
     888         726 : }
     889             : 
     890           0 : static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
     891             : {
     892             :         zend_function *func;
     893             :         zend_op *opline, *end;
     894             : 
     895           0 :         opline = op_array->opcodes;
     896           0 :         end = opline + op_array->last;
     897           0 :         while (opline < end) {
     898           0 :                 if (opline->opcode == ZEND_INIT_FCALL) {
     899           0 :                         func = zend_hash_find_ptr(
     900           0 :                                 &ctx->script->function_table,
     901           0 :                                 Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
     902           0 :                         if (func) {
     903           0 :                                 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
     904             :                         }
     905             :                 }
     906           0 :                 opline++;
     907             :         }
     908           0 : }
     909             : 
     910             : #if HAVE_DFA_PASS
     911         721 : static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
     912             : {
     913         721 :         zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
     914             : 
     915         721 :         if (func_info) {
     916         721 :                 zend_call_info *call_info =func_info->callee_info;
     917             : 
     918        2706 :                 while (call_info) {
     919        1264 :                         zend_op *opline = call_info->caller_init_opline;
     920             : 
     921        1264 :                         if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
     922        2288 :                                 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
     923             :                         }
     924        1264 :                         call_info = call_info->next_callee;
     925             :                 }
     926             :         }
     927         721 : }
     928             : #endif
     929             : 
     930         620 : int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
     931             : {
     932             :         zend_class_entry *ce;
     933             :         zend_op_array *op_array;
     934             :         zend_string *name;
     935             :         zend_optimizer_ctx ctx;
     936             : #if HAVE_DFA_PASS
     937             :         zend_call_graph call_graph;
     938             : #endif
     939             : 
     940         620 :         ctx.arena = zend_arena_create(64 * 1024);
     941         620 :         ctx.script = script;
     942         620 :         ctx.constants = NULL;
     943         620 :         ctx.optimization_level = optimization_level;
     944         620 :         ctx.debug_level = debug_level;
     945             : 
     946         620 :         zend_optimize_op_array(&script->main_op_array, &ctx);
     947             : 
     948         780 :         ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
     949          80 :                 zend_optimize_op_array(op_array, &ctx);
     950             :         } ZEND_HASH_FOREACH_END();
     951             : 
     952         726 :         ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
     953          83 :                 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
     954          26 :                         if (op_array->scope == ce) {
     955          26 :                                 zend_optimize_op_array(op_array, &ctx);
     956           0 :                         } else if (op_array->type == ZEND_USER_FUNCTION) {
     957             :                                 zend_op_array *orig_op_array;
     958           0 :                                 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
     959           0 :                                         HashTable *ht = op_array->static_variables;
     960           0 :                                         *op_array = *orig_op_array;
     961           0 :                                         op_array->static_variables = ht;
     962             :                                 }
     963             :                         }
     964             :                 } ZEND_HASH_FOREACH_END();
     965             :         } ZEND_HASH_FOREACH_END();
     966             : 
     967             : #if HAVE_DFA_PASS
     968        2480 :         if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
     969         620 :             (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
     970         620 :             zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
     971             :                 /* Optimize using call-graph */
     972        1240 :                 void *checkpoint = zend_arena_checkpoint(ctx.arena);
     973             :                 int i;
     974             :                 zend_func_info *func_info;
     975             : 
     976        1346 :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     977         726 :                         zend_revert_pass_two(call_graph.op_arrays[i]);
     978             :                 }
     979             : 
     980        1346 :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     981         726 :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
     982         726 :                         if (func_info) {
     983         726 :                                 func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
     984         726 :                                 if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
     985           5 :                                         zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
     986             :                                 }
     987             :                         }
     988             :                 }
     989             : 
     990        1346 :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     991         726 :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
     992         726 :                         if (func_info) {
     993         726 :                                 zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
     994             :                         }
     995             :                 }
     996             : 
     997             :                 //TODO: perform inner-script inference???
     998        1346 :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     999         726 :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
    1000         726 :                         if (func_info) {
    1001         726 :                                 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
    1002             :                         }
    1003             :                 }
    1004             : 
    1005         620 :                 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
    1006           0 :                         for (i = 0; i < call_graph.op_arrays_count; i++) {
    1007           0 :                                 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
    1008             :                         }
    1009             :                 }
    1010             : 
    1011         620 :                 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
    1012        1338 :                         for (i = 0; i < call_graph.op_arrays_count; i++) {
    1013         721 :                                 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
    1014             :                         }
    1015             :                 }
    1016             : 
    1017        1346 :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
    1018         726 :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
    1019        1163 :                         if (func_info && func_info->ssa.var_info) {
    1020         437 :                                 zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
    1021             :                         } else {
    1022         289 :                                 zend_redo_pass_two(call_graph.op_arrays[i]);
    1023             :                         }
    1024             :                 }
    1025             : 
    1026        1346 :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
    1027         726 :                         ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
    1028             :                 }
    1029             : 
    1030         726 :                 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
    1031          83 :                         ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
    1032          26 :                                 if (op_array->scope != ce) {
    1033             :                                         zend_op_array *orig_op_array;
    1034           0 :                                         if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
    1035           0 :                                                 HashTable *ht = op_array->static_variables;
    1036           0 :                                                 *op_array = *orig_op_array;
    1037           0 :                                                 op_array->static_variables = ht;
    1038             :                                         }
    1039             :                                 }
    1040             :                         } ZEND_HASH_FOREACH_END();
    1041             :                 } ZEND_HASH_FOREACH_END();
    1042             : 
    1043             :                 zend_arena_release(&ctx.arena, checkpoint);
    1044             :         } else
    1045             : #endif
    1046             : 
    1047           0 :         if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
    1048           0 :                 zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
    1049             : 
    1050           0 :                 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
    1051           0 :                         zend_adjust_fcall_stack_size(op_array, &ctx);
    1052             :                 } ZEND_HASH_FOREACH_END();
    1053             : 
    1054           0 :                 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
    1055           0 :                         ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
    1056           0 :                                 if (op_array->scope == ce) {
    1057           0 :                                         zend_adjust_fcall_stack_size(op_array, &ctx);
    1058           0 :                                 } else if (op_array->type == ZEND_USER_FUNCTION) {
    1059             :                                         zend_op_array *orig_op_array;
    1060           0 :                                         if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
    1061           0 :                                                 HashTable *ht = op_array->static_variables;
    1062           0 :                                                 *op_array = *orig_op_array;
    1063           0 :                                                 op_array->static_variables = ht;
    1064             :                                         }
    1065             :                                 }
    1066             :                         } ZEND_HASH_FOREACH_END();
    1067             :                 } ZEND_HASH_FOREACH_END();
    1068             :         }
    1069             : 
    1070         620 :         if (ctx.constants) {
    1071           0 :                 zend_hash_destroy(ctx.constants);
    1072             :         }
    1073         620 :         zend_arena_destroy(ctx.arena);
    1074             : 
    1075         620 :         return 1;
    1076             : }
    1077             : 
    1078         499 : int zend_optimizer_startup(void)
    1079             : {
    1080         499 :         return zend_func_info_startup();
    1081             : }
    1082             : 
    1083       24332 : int zend_optimizer_shutdown(void)
    1084             : {
    1085       24332 :         return zend_func_info_shutdown();
    1086             : }
    1087             : 
    1088             : /*
    1089             :  * Local variables:
    1090             :  * tab-width: 4
    1091             :  * c-basic-offset: 4
    1092             :  * indent-tabs-mode: t
    1093             :  * End:
    1094             :  */

Generated by: LCOV version 1.10

Generated at Sun, 15 Oct 2017 12:26:23 +0000 (8 days ago)

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