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: 146 358 40.8 %
Date: 2016-06-25 Functions: 14 23 60.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend OPcache                                                         |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2016 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_dump.h"
      33             : 
      34             : #ifndef HAVE_DFA_PASS
      35             : # define HAVE_DFA_PASS 0
      36             : #endif
      37             : 
      38           0 : static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
      39             : {
      40             :         zval_dtor(zvalue);
      41           0 : }
      42             : 
      43           0 : void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
      44             : {
      45             :         zval val;
      46             : 
      47           0 :         if (!ctx->constants) {
      48           0 :                 ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
      49           0 :                 zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
      50             :         }
      51           0 :         ZVAL_DUP(&val, value);
      52           0 :         zend_hash_add(ctx->constants, Z_STR_P(name), &val);
      53           0 : }
      54             : 
      55           0 : int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
      56             : {
      57             :         zval *val;
      58             : 
      59           0 :         if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
      60           0 :                 ZVAL_DUP(value, val);
      61           0 :                 return 1;
      62             :         }
      63           0 :         return 0;
      64             : }
      65             : 
      66           0 : int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
      67             : {
      68           0 :         int i = 0;
      69           0 :         zend_ulong hash_value = zend_string_hash_val(name);
      70             : 
      71           0 :         while (i < op_array->last_var) {
      72           0 :                 if (op_array->vars[i] == name ||
      73           0 :                     (ZSTR_H(op_array->vars[i]) == hash_value &&
      74           0 :                      ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) &&
      75           0 :                      memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) {
      76           0 :                         return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
      77             :                 }
      78           0 :                 i++;
      79             :         }
      80           0 :         i = op_array->last_var;
      81           0 :         op_array->last_var++;
      82           0 :         op_array->vars = erealloc(op_array->vars, op_array->last_var * sizeof(zend_string*));
      83           0 :         op_array->vars[i] = zend_string_dup(name, 0);
      84             : 
      85             :         /* all IS_TMP_VAR and IS_VAR variable numbers have to be adjusted */
      86             :         {
      87           0 :                 zend_op *opline = op_array->opcodes;
      88           0 :                 zend_op *end = opline + op_array->last;
      89           0 :                 while (opline < end) {
      90           0 :                         if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
      91           0 :                                 opline->op1.var += sizeof(zval);
      92             :                         }
      93           0 :                         if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
      94           0 :                                 opline->op2.var += sizeof(zval);
      95             :                         }
      96           0 :                         if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
      97           0 :                                 opline->result.var += sizeof(zval);
      98             :                         }
      99           0 :                         opline++;
     100             :                 }
     101             :         }
     102             : 
     103           0 :         return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
     104             : }
     105             : 
     106         426 : int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
     107             : {
     108         426 :         int i = op_array->last_literal;
     109         426 :         op_array->last_literal++;
     110         426 :         op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
     111         426 :         ZVAL_COPY_VALUE(&op_array->literals[i], zv);
     112         426 :         Z_CACHE_SLOT(op_array->literals[i]) = -1;
     113         426 :         return i;
     114             : }
     115             : 
     116           0 : static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
     117             :         zval zv;
     118           0 :         ZVAL_STR(&zv, str);
     119             :         zend_string_hash_val(str);
     120           0 :         return zend_optimizer_add_literal(op_array, &zv);
     121             : }
     122             : 
     123         189 : int zend_optimizer_is_disabled_func(const char *name, size_t len) {
     124         378 :         zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
     125             : 
     126         378 :         return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
     127         189 :                         fbc->internal_function.handler == ZEND_FN(display_disabled_function));
     128             : }
     129             : 
     130           0 : static inline void drop_leading_backslash(zval *val) {
     131           0 :         if (Z_STRVAL_P(val)[0] == '\\') {
     132           0 :                 zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
     133             :                 zval_dtor(val);
     134           0 :                 ZVAL_STR(val, str);
     135             :         }
     136           0 : }
     137             : 
     138           0 : static inline void alloc_cache_slots_op1(zend_op_array *op_array, zend_op *opline, uint32_t num) {
     139           0 :         Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size;
     140           0 :         op_array->cache_size += num * sizeof(void *);
     141           0 : }
     142           0 : static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) {
     143           0 :         Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size;
     144           0 :         op_array->cache_size += num * sizeof(void *);
     145           0 : }
     146             : 
     147             : #define REQUIRES_STRING(val) do { \
     148             :         if (Z_TYPE_P(val) != IS_STRING) { \
     149             :                 zval_dtor(val); \
     150             :                 return 0; \
     151             :         } \
     152             : } while (0)
     153             : 
     154             : #define TO_STRING_NOWARN(val) do { \
     155             :         if (Z_TYPE_P(val) >= IS_ARRAY) { \
     156             :                 zval_dtor(val); \
     157             :                 return 0; \
     158             :         } \
     159             :         convert_to_string(val); \
     160             : } while (0)
     161             : 
     162         423 : int zend_optimizer_update_op1_const(zend_op_array *op_array,
     163             :                                     zend_op       *opline,
     164             :                                     zval          *val)
     165             : {
     166         423 :         switch (opline->opcode) {
     167             :                 case ZEND_FREE:
     168           0 :                         MAKE_NOP(opline);
     169             :                         zval_dtor(val);
     170           0 :                         return 1;
     171             :                 case ZEND_INIT_STATIC_METHOD_CALL:
     172             :                 case ZEND_CATCH:
     173             :                 case ZEND_FETCH_CONSTANT:
     174             :                 case ZEND_FETCH_CLASS_CONSTANT:
     175             :                 case ZEND_DEFINED:
     176             :                 case ZEND_NEW:
     177           0 :                         REQUIRES_STRING(val);
     178           0 :                         drop_leading_backslash(val);
     179           0 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     180           0 :                         alloc_cache_slots_op1(op_array, opline, 1);
     181           0 :                         zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     182           0 :                         break;
     183             :                 case ZEND_FETCH_STATIC_PROP_R:
     184             :                 case ZEND_FETCH_STATIC_PROP_W:
     185             :                 case ZEND_FETCH_STATIC_PROP_RW:
     186             :                 case ZEND_FETCH_STATIC_PROP_IS:
     187             :                 case ZEND_FETCH_STATIC_PROP_UNSET:
     188             :                 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
     189           0 :                         TO_STRING_NOWARN(val);
     190           0 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     191           0 :                         alloc_cache_slots_op1(op_array, opline, 2);
     192           0 :                         break;
     193             :                 case ZEND_CONCAT:
     194             :                 case ZEND_FAST_CONCAT:
     195             :                 case ZEND_FETCH_R:
     196             :                 case ZEND_FETCH_W:
     197             :                 case ZEND_FETCH_RW:
     198             :                 case ZEND_FETCH_IS:
     199             :                 case ZEND_FETCH_UNSET:
     200             :                 case ZEND_FETCH_FUNC_ARG:
     201          14 :                         TO_STRING_NOWARN(val);
     202             :                         /* break missing intentionally */
     203             :                 default:
     204         423 :                         opline->op1.constant = zend_optimizer_add_literal(op_array, val);
     205             :                         break;
     206             :         }
     207             : 
     208         423 :         ZEND_OP1_TYPE(opline) = IS_CONST;
     209         846 :         if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
     210          14 :                 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
     211             :         }
     212         423 :         return 1;
     213             : }
     214             : 
     215           1 : int zend_optimizer_update_op2_const(zend_op_array *op_array,
     216             :                                     zend_op       *opline,
     217             :                                     zval          *val)
     218             : {
     219           1 :         switch (opline->opcode) {
     220             :                 case ZEND_ASSIGN_REF:
     221             :                         zval_dtor(val);
     222           0 :                         return 0;
     223             :                 case ZEND_FETCH_CLASS:
     224             :                 case ZEND_INIT_FCALL_BY_NAME:
     225             :                 /*case ZEND_INIT_NS_FCALL_BY_NAME:*/
     226             :                 case ZEND_ADD_INTERFACE:
     227             :                 case ZEND_ADD_TRAIT:
     228             :                 case ZEND_INSTANCEOF:
     229             :                 case ZEND_FETCH_STATIC_PROP_R:
     230             :                 case ZEND_FETCH_STATIC_PROP_W:
     231             :                 case ZEND_FETCH_STATIC_PROP_RW:
     232             :                 case ZEND_FETCH_STATIC_PROP_IS:
     233             :                 case ZEND_FETCH_STATIC_PROP_UNSET:
     234             :                 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
     235             :                 case ZEND_UNSET_STATIC_PROP:
     236             :                 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
     237           0 :                         REQUIRES_STRING(val);
     238           0 :                         drop_leading_backslash(val);
     239           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     240           0 :                         zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     241           0 :                         alloc_cache_slots_op2(op_array, opline, 1);
     242           0 :                         break;
     243             :                 case ZEND_INIT_FCALL:
     244           0 :                         REQUIRES_STRING(val);
     245           0 :                         zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
     246           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     247           0 :                         alloc_cache_slots_op2(op_array, opline, 1);
     248           0 :                         break;
     249             :                 case ZEND_INIT_DYNAMIC_CALL:
     250           0 :                         if (Z_TYPE_P(val) == IS_STRING) {
     251           0 :                                 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
     252             :                                         zval_dtor(val);
     253           0 :                                         return 0;
     254             :                                 }
     255             : 
     256           0 :                                 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
     257           0 :                                 drop_leading_backslash(val);
     258           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     259           0 :                                 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     260           0 :                                 alloc_cache_slots_op2(op_array, opline, 1);
     261             :                         } else {
     262           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     263             :                         }
     264           0 :                         break;
     265             :                 case ZEND_INIT_METHOD_CALL:
     266             :                 case ZEND_INIT_STATIC_METHOD_CALL:
     267           0 :                         REQUIRES_STRING(val);
     268           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     269           0 :                         zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
     270           0 :                         alloc_cache_slots_op2(op_array, opline, 2);
     271           0 :                         break;
     272             :                 /*case ZEND_FETCH_CLASS_CONSTANT:*/
     273             :                 case ZEND_ASSIGN_OBJ:
     274             :                 case ZEND_FETCH_OBJ_R:
     275             :                 case ZEND_FETCH_OBJ_W:
     276             :                 case ZEND_FETCH_OBJ_RW:
     277             :                 case ZEND_FETCH_OBJ_IS:
     278             :                 case ZEND_FETCH_OBJ_UNSET:
     279             :                 case ZEND_FETCH_OBJ_FUNC_ARG:
     280             :                 case ZEND_UNSET_OBJ:
     281             :                 case ZEND_PRE_INC_OBJ:
     282             :                 case ZEND_PRE_DEC_OBJ:
     283             :                 case ZEND_POST_INC_OBJ:
     284             :                 case ZEND_POST_DEC_OBJ:
     285             :                 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
     286           0 :                         TO_STRING_NOWARN(val);
     287           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     288           0 :                         alloc_cache_slots_op2(op_array, opline, 2);
     289           0 :                         break;
     290             :                 case ZEND_ASSIGN_ADD:
     291             :                 case ZEND_ASSIGN_SUB:
     292             :                 case ZEND_ASSIGN_MUL:
     293             :                 case ZEND_ASSIGN_DIV:
     294             :                 case ZEND_ASSIGN_POW:
     295             :                 case ZEND_ASSIGN_MOD:
     296             :                 case ZEND_ASSIGN_SL:
     297             :                 case ZEND_ASSIGN_SR:
     298             :                 case ZEND_ASSIGN_CONCAT:
     299             :                 case ZEND_ASSIGN_BW_OR:
     300             :                 case ZEND_ASSIGN_BW_AND:
     301             :                 case ZEND_ASSIGN_BW_XOR:
     302           0 :                         if (opline->extended_value == ZEND_ASSIGN_OBJ) {
     303           0 :                                 TO_STRING_NOWARN(val);
     304           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     305           0 :                                 alloc_cache_slots_op2(op_array, opline, 2);
     306             :                         } else {
     307           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     308             :                         }
     309           0 :                         break;
     310             :                 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
     311             :                 case ZEND_ADD_ARRAY_ELEMENT:
     312             :                 case ZEND_INIT_ARRAY:
     313             :                 case ZEND_ASSIGN_DIM:
     314             :                 case ZEND_UNSET_DIM:
     315             :                 case ZEND_FETCH_DIM_R:
     316             :                 case ZEND_FETCH_DIM_W:
     317             :                 case ZEND_FETCH_DIM_RW:
     318             :                 case ZEND_FETCH_DIM_IS:
     319             :                 case ZEND_FETCH_DIM_FUNC_ARG:
     320             :                 case ZEND_FETCH_DIM_UNSET:
     321             :                 case ZEND_FETCH_LIST:
     322           0 :                         if (Z_TYPE_P(val) == IS_STRING) {
     323             :                                 zend_ulong index;
     324           0 :                                 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
     325             :                                         zval_dtor(val);
     326           0 :                                         ZVAL_LONG(val, index);
     327             :                                 }
     328             :                         }
     329           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     330           0 :                         break;
     331             :                 case ZEND_ROPE_INIT:
     332             :                 case ZEND_ROPE_ADD:
     333             :                 case ZEND_ROPE_END:
     334             :                 case ZEND_CONCAT:
     335             :                 case ZEND_FAST_CONCAT:
     336           0 :                         TO_STRING_NOWARN(val);
     337             :                         /* break missing intentionally */
     338             :                 default:
     339           1 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     340             :                         break;
     341             :         }
     342             : 
     343           1 :         ZEND_OP2_TYPE(opline) = IS_CONST;
     344           2 :         if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
     345           1 :                 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
     346             :         }
     347           1 :         return 1;
     348             : }
     349             : 
     350         424 : void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
     351             : {
     352         424 :         if (op_array->last_live_range) {
     353         113 :                 int i = 0;
     354         113 :                 int j = 0;
     355             :                 uint32_t *map;
     356             :                 ALLOCA_FLAG(use_heap);
     357             : 
     358         113 :                 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
     359             : 
     360             :                 do {
     361         339 :                         if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
     362         339 :                                 map[i] = j;
     363         339 :                                 if (i != j) {
     364           0 :                                         op_array->live_range[j] = op_array->live_range[i];
     365             :                                 }
     366         339 :                                 j++;
     367             :                         }
     368         339 :                         i++;
     369         339 :                 } while (i < op_array->last_live_range);
     370         113 :                 if (i != j) {
     371           0 :                         if ((op_array->last_live_range = j)) {
     372           0 :                                 zend_op *opline = op_array->opcodes;
     373           0 :                                 zend_op *end = opline + op_array->last;
     374             : 
     375           0 :                                 while (opline != end) {
     376           0 :                                         if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
     377           0 :                                                         opline->extended_value == ZEND_FREE_ON_RETURN) {
     378           0 :                                                 opline->op2.num = map[opline->op2.num];
     379             :                                         }
     380           0 :                                         opline++;
     381             :                                 }
     382             :                         } else {
     383           0 :                                 efree(op_array->live_range);
     384           0 :                                 op_array->live_range = NULL;
     385             :                         }
     386             :                 }
     387         113 :                 free_alloca(map, use_heap);
     388             :         }
     389         424 : }
     390             : 
     391         424 : int zend_optimizer_replace_by_const(zend_op_array *op_array,
     392             :                                     zend_op       *opline,
     393             :                                     zend_uchar     type,
     394             :                                     uint32_t       var,
     395             :                                     zval          *val)
     396             : {
     397         424 :         zend_op *end = op_array->opcodes + op_array->last;
     398             : 
     399         904 :         while (opline < end) {
     400         903 :                 if (ZEND_OP1_TYPE(opline) == type &&
     401         423 :                         ZEND_OP1(opline).var == var) {
     402         423 :                         switch (opline->opcode) {
     403             :                                 case ZEND_FETCH_DIM_W:
     404             :                                 case ZEND_FETCH_DIM_RW:
     405             :                                 case ZEND_FETCH_DIM_FUNC_ARG:
     406             :                                 case ZEND_FETCH_DIM_UNSET:
     407             :                                 case ZEND_ASSIGN_DIM:
     408             :                                 case ZEND_SEPARATE:
     409             :                                 case ZEND_RETURN_BY_REF:
     410             :                                         zval_dtor(val);
     411           0 :                                         return 0;
     412             :                                 case ZEND_SEND_VAR:
     413           0 :                                         opline->extended_value = 0;
     414           0 :                                         opline->opcode = ZEND_SEND_VAL;
     415           0 :                                         break;
     416             :                                 case ZEND_SEND_VAR_EX:
     417           0 :                                         opline->extended_value = 0;
     418           0 :                                         opline->opcode = ZEND_SEND_VAL_EX;
     419           0 :                                         break;
     420             :                                 case ZEND_SEND_VAR_NO_REF:
     421           0 :                                         if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
     422           0 :                                                 if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
     423             :                                                         zval_dtor(val);
     424           0 :                                                         return 0;
     425             :                                                 }
     426           0 :                                                 opline->extended_value = 0;
     427           0 :                                                 opline->opcode = ZEND_SEND_VAL_EX;
     428             :                                         } else {
     429           0 :                                                 opline->extended_value = 0;
     430           0 :                                                 opline->opcode = ZEND_SEND_VAL;
     431             :                                         }
     432           0 :                                         break;
     433             :                                 case ZEND_SEND_USER:
     434           0 :                                         opline->opcode = ZEND_SEND_VAL_EX;
     435           0 :                                         break;
     436             :                                 /* In most cases IS_TMP_VAR operand may be used only once.
     437             :                                  * The operands are usually destroyed by the opcode handler.
     438             :                                  * ZEND_CASE is an exception, that keeps operand unchanged,
     439             :                                  * and allows its reuse. The number of ZEND_CASE instructions
     440             :                                  * usually terminated by ZEND_FREE that finally kills the value.
     441             :                                  */
     442             :                                 case ZEND_FREE:
     443             :                                 case ZEND_CASE: {
     444             :                                         zend_op *m, *n;
     445           0 :                                         int brk = op_array->last_live_range;
     446           0 :                                         zend_bool in_switch = 0;
     447           0 :                                         while (brk--) {
     448           0 :                                                 if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
     449           0 :                                                     op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
     450           0 :                                                         in_switch = 1;
     451           0 :                                                         break;
     452             :                                                 }
     453             :                                         }
     454             : 
     455           0 :                                         if (!in_switch) {
     456             :                                                 ZEND_ASSERT(opline->opcode == ZEND_FREE);
     457           0 :                                                 MAKE_NOP(opline);
     458             :                                                 zval_dtor(val);
     459           0 :                                                 return 1;
     460             :                                         }
     461             : 
     462           0 :                                         m = opline;
     463           0 :                                         n = op_array->opcodes + op_array->live_range[brk].end;
     464           0 :                                         if (n->opcode == ZEND_FREE &&
     465           0 :                                             !(n->extended_value & ZEND_FREE_ON_RETURN)) {
     466           0 :                                                 n++;
     467             :                                         } else {
     468           0 :                                                 n = op_array->opcodes + op_array->last;
     469             :                                         }
     470           0 :                                         while (m < n) {
     471           0 :                                                 if (ZEND_OP1_TYPE(m) == type &&
     472           0 :                                                                 ZEND_OP1(m).var == var) {
     473           0 :                                                         if (m->opcode == ZEND_CASE) {
     474             :                                                                 zval old_val;
     475           0 :                                                                 ZVAL_COPY_VALUE(&old_val, val);
     476             :                                                                 zval_copy_ctor(val);
     477           0 :                                                                 zend_optimizer_update_op1_const(op_array, m, val);
     478           0 :                                                                 ZVAL_COPY_VALUE(val, &old_val);
     479           0 :                                                         } else if (m->opcode == ZEND_FREE) {
     480           0 :                                                                 MAKE_NOP(m);
     481             :                                                         } else {
     482             :                                                                 ZEND_ASSERT(0);
     483             :                                                         }
     484             :                                                 }
     485           0 :                                                 m++;
     486             :                                         }
     487             :                                         zval_dtor(val);
     488           0 :                                         zend_optimizer_remove_live_range(op_array, var);
     489           0 :                                         return 1;
     490             :                                 }
     491             :                                 case ZEND_VERIFY_RETURN_TYPE: {
     492           0 :                                         zend_arg_info *ret_info = op_array->arg_info - 1;
     493             :                                         ZEND_ASSERT((opline + 1)->opcode == ZEND_RETURN || (opline + 1)->opcode == ZEND_RETURN_BY_REF);
     494           0 :                                         if (ret_info->class_name
     495           0 :                                                 || ret_info->type_hint == IS_CALLABLE
     496           0 :                                                 || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
     497           0 :                                                 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
     498             :                                                 zval_dtor(val);
     499           0 :                                                 return 0;
     500             :                                         }
     501           0 :                                         MAKE_NOP(opline);
     502           0 :                                         opline++;
     503             :                                         break;
     504             :                                   }
     505             :                                 default:
     506             :                                         break;
     507             :                         }
     508         423 :                         if (zend_optimizer_update_op1_const(op_array, opline, val)) {
     509         423 :                                 zend_optimizer_remove_live_range(op_array, var);
     510         423 :                                 return 1;
     511             :                         }
     512           0 :                         return 0;
     513             :                 }
     514             : 
     515          58 :                 if (ZEND_OP2_TYPE(opline) == type &&
     516           1 :                         ZEND_OP2(opline).var == var) {
     517           1 :                         if (zend_optimizer_update_op2_const(op_array, opline, val)) {
     518           1 :                                 zend_optimizer_remove_live_range(op_array, var);
     519           1 :                                 return 1;
     520             :                         }
     521           0 :                         return 0;
     522             :                 }
     523          56 :                 opline++;
     524             :         }
     525             : 
     526           0 :         return 1;
     527             : }
     528             : 
     529         463 : static void zend_optimize(zend_op_array      *op_array,
     530             :                           zend_optimizer_ctx *ctx)
     531             : {
     532         463 :         if (op_array->type == ZEND_EVAL_CODE) {
     533           0 :                 return;
     534             :         }
     535             : 
     536         463 :         if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
     537           0 :                 zend_dump_op_array(op_array, 0, "before optimizer", NULL);
     538             :         }
     539             : 
     540             :         /* pass 1
     541             :          * - substitute persistent constants (true, false, null, etc)
     542             :          * - perform compile-time evaluation of constant binary and unary operations
     543             :          * - optimize series of ADD_STRING and/or ADD_CHAR
     544             :          * - convert CAST(IS_BOOL,x) into BOOL(x)
     545             :          */
     546         463 :         if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
     547         463 :                 zend_optimizer_pass1(op_array, ctx);
     548         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
     549           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     550             :                 }
     551             :         }
     552             : 
     553             :         /* pass 2:
     554             :          * - convert non-numeric constants to numeric constants in numeric operators
     555             :          * - optimize constant conditional JMPs
     556             :          * - optimize static BRKs and CONTs
     557             :          * - pre-evaluate constant function calls
     558             :          */
     559         463 :         if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
     560         463 :                 zend_optimizer_pass2(op_array);
     561         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
     562           0 :                         zend_dump_op_array(op_array, 0, "after pass 2", NULL);
     563             :                 }
     564             :         }
     565             : 
     566             :         /* pass 3:
     567             :          * - optimize $i = $i+expr to $i+=expr
     568             :          * - optimize series of JMPs
     569             :          * - change $i++ to ++$i where possible
     570             :          */
     571         463 :         if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
     572         463 :                 zend_optimizer_pass3(op_array);
     573         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
     574           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     575             :                 }
     576             :         }
     577             : 
     578             :         /* pass 4:
     579             :          * - INIT_FCALL_BY_NAME -> DO_FCALL
     580             :          */
     581         463 :         if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
     582         463 :                 zend_optimize_func_calls(op_array, ctx);
     583         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
     584           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     585             :                 }
     586             :         }
     587             : 
     588             :         /* pass 5:
     589             :          * - CFG optimization
     590             :          */
     591         463 :         if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
     592         463 :                 zend_optimize_cfg(op_array, ctx);
     593         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
     594           0 :                         zend_dump_op_array(op_array, 0, "after pass 5", NULL);
     595             :                 }
     596             :         }
     597             : 
     598             : #if HAVE_DFA_PASS
     599             :         /* pass 6:
     600             :          * - DFA optimization
     601             :          */
     602             :         if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
     603             :             !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
     604             :                 zend_optimize_dfa(op_array, ctx);
     605             :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
     606             :                         zend_dump_op_array(op_array, 0, "after pass 6", NULL);
     607             :                 }
     608             :         }
     609             : #endif
     610             : 
     611             :         /* pass 9:
     612             :          * - Optimize temp variables usage
     613             :          */
     614         463 :         if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
     615         463 :                 zend_optimize_temporary_variables(op_array, ctx);
     616         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
     617           0 :                         zend_dump_op_array(op_array, 0, "after pass 9", NULL);
     618             :                 }
     619             :         }
     620             : 
     621             :         /* pass 10:
     622             :          * - remove NOPs
     623             :          */
     624         463 :         if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
     625           0 :                 zend_optimizer_nop_removal(op_array);
     626           0 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
     627           0 :                         zend_dump_op_array(op_array, 0, "after pass 10", NULL);
     628             :                 }
     629             :         }
     630             : 
     631             :         /* pass 11:
     632             :          * - Compact literals table
     633             :          */
     634         463 :         if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
     635         463 :                 zend_optimizer_compact_literals(op_array, ctx);
     636         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
     637           0 :                         zend_dump_op_array(op_array, 0, "after pass 11", NULL);
     638             :                 }
     639             :         }
     640             : 
     641         463 :         if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
     642           0 :                 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
     643             :         }
     644             : }
     645             : 
     646         463 : static void zend_revert_pass_two(zend_op_array *op_array)
     647             : {
     648             :         zend_op *opline, *end;
     649             : 
     650         463 :         opline = op_array->opcodes;
     651         463 :         end = opline + op_array->last;
     652        8288 :         while (opline < end) {
     653        7362 :                 if (opline->op1_type == IS_CONST) {
     654        2545 :                         ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
     655             :                 }
     656        7362 :                 if (opline->op2_type == IS_CONST) {
     657        1724 :                         ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
     658             :                 }
     659        7362 :                 opline++;
     660             :         }
     661         463 : }
     662             : 
     663         463 : static void zend_redo_pass_two(zend_op_array *op_array)
     664             : {
     665             :         zend_op *opline, *end;
     666             : 
     667         463 :         opline = op_array->opcodes;
     668         463 :         end = opline + op_array->last;
     669        6679 :         while (opline < end) {
     670        5753 :                 if (opline->op1_type == IS_CONST) {
     671        2069 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
     672             :                 }
     673        5753 :                 if (opline->op2_type == IS_CONST) {
     674        1423 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
     675             :                 }
     676        5753 :                 ZEND_VM_SET_OPCODE_HANDLER(opline);
     677        5753 :                 opline++;
     678             :         }
     679         463 : }
     680             : 
     681         463 : static void zend_optimize_op_array(zend_op_array      *op_array,
     682             :                                    zend_optimizer_ctx *ctx)
     683             : {
     684             :         /* Revert pass_two() */
     685         463 :         zend_revert_pass_two(op_array);
     686             : 
     687             :         /* Do actual optimizations */
     688         463 :         zend_optimize(op_array, ctx);
     689             : 
     690             :         /* Redo pass_two() */
     691         463 :         zend_redo_pass_two(op_array);
     692         463 : }
     693             : 
     694         463 : static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
     695             : {
     696             :         zend_function *func;
     697             :         zend_op *opline, *end;
     698             : 
     699         463 :         opline = op_array->opcodes;
     700         463 :         end = opline + op_array->last;
     701        6679 :         while (opline < end) {
     702        5753 :                 if (opline->opcode == ZEND_INIT_FCALL) {
     703        1708 :                         func = zend_hash_find_ptr(
     704         854 :                                 &ctx->script->function_table,
     705         854 :                                 Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
     706         854 :                         if (func) {
     707           0 :                                 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
     708             :                         }
     709             :                 }
     710        5753 :                 opline++;
     711             :         }
     712         463 : }
     713             : 
     714           0 : static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
     715             : {
     716           0 :         zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
     717             : 
     718           0 :         if (func_info) {
     719           0 :                 zend_call_info *call_info =func_info->callee_info;
     720             : 
     721           0 :                 while (call_info) {
     722           0 :                         zend_op *opline = call_info->caller_init_opline;
     723             : 
     724           0 :                         if (opline && call_info->callee_func) {
     725             :                                 ZEND_ASSERT(opline->opcode == ZEND_INIT_FCALL);
     726           0 :                                 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
     727             :                         }
     728           0 :                         call_info = call_info->next_callee;
     729             :                 }
     730             :         }
     731           0 : }
     732             : 
     733         430 : int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
     734             : {
     735             :         zend_class_entry *ce;
     736             :         zend_op_array *op_array;
     737             :         zend_string *name;
     738             :         zend_optimizer_ctx ctx;
     739             :         zend_call_graph call_graph;
     740             : 
     741         430 :         ctx.arena = zend_arena_create(64 * 1024);
     742         430 :         ctx.script = script;
     743         430 :         ctx.constants = NULL;
     744         430 :         ctx.optimization_level = optimization_level;
     745         430 :         ctx.debug_level = debug_level;
     746             : 
     747         430 :         zend_optimize_op_array(&script->main_op_array, &ctx);
     748             : 
     749         478 :         ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
     750          24 :                 zend_optimize_op_array(op_array, &ctx);
     751             :         } ZEND_HASH_FOREACH_END();
     752             : 
     753         458 :         ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
     754          25 :                 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
     755           9 :                         if (op_array->scope == ce) {
     756           9 :                                 zend_optimize_op_array(op_array, &ctx);
     757           0 :                         } else if (op_array->type == ZEND_USER_FUNCTION) {
     758             :                                 zend_op_array *orig_op_array;
     759           0 :                                 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
     760           0 :                                         HashTable *ht = op_array->static_variables;
     761           0 :                                         *op_array = *orig_op_array;
     762           0 :                                         op_array->static_variables = ht;
     763             :                                 }
     764             :                         }
     765             :                 } ZEND_HASH_FOREACH_END();
     766             :         } ZEND_HASH_FOREACH_END();
     767             : 
     768             : #if HAVE_DFA_PASS
     769             :         if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
     770             :             (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
     771             :             zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
     772             :                 /* Optimize using call-graph */
     773             :                 void *checkpoint = zend_arena_checkpoint(ctx.arena);
     774             :                 int i;
     775             :                 zend_func_info *func_info;
     776             : 
     777             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     778             :                         zend_revert_pass_two(call_graph.op_arrays[i]);
     779             :                 }
     780             : 
     781             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     782             :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
     783             :                         if (func_info) {
     784             :                                 zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
     785             :                         }
     786             :                 }
     787             : 
     788             :                 //TODO: perform inner-script inference???
     789             : 
     790             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     791             :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
     792             :                         if (func_info) {
     793             :                                 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
     794             :                         }
     795             :                 }
     796             : 
     797             :                 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
     798             :                         for (i = 0; i < call_graph.op_arrays_count; i++) {
     799             :                                 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
     800             :                         }
     801             :                 }
     802             : 
     803             :                 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
     804             :                         for (i = 0; i < call_graph.op_arrays_count; i++) {
     805             :                                 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
     806             :                         }
     807             :                 }
     808             : 
     809             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     810             :                         zend_redo_pass_two(call_graph.op_arrays[i]);
     811             :                         ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
     812             :                 }
     813             : 
     814             :                 zend_arena_release(&ctx.arena, checkpoint);
     815             :         } else
     816             : #endif
     817             : 
     818         430 :         if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
     819         430 :                 zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
     820             : 
     821         478 :                 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
     822          24 :                         zend_adjust_fcall_stack_size(op_array, &ctx);
     823             :                 } ZEND_HASH_FOREACH_END();
     824             : 
     825         458 :                 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
     826          25 :                         ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
     827           9 :                                 if (op_array->scope == ce) {
     828           9 :                                         zend_adjust_fcall_stack_size(op_array, &ctx);
     829           0 :                                 } else if (op_array->type == ZEND_USER_FUNCTION) {
     830             :                                         zend_op_array *orig_op_array;
     831           0 :                                         if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
     832           0 :                                                 HashTable *ht = op_array->static_variables;
     833           0 :                                                 *op_array = *orig_op_array;
     834           0 :                                                 op_array->static_variables = ht;
     835             :                                         }
     836             :                                 }
     837             :                         } ZEND_HASH_FOREACH_END();
     838             :                 } ZEND_HASH_FOREACH_END();
     839             :         }
     840             : 
     841         430 :         if (ctx.constants) {
     842           0 :                 zend_hash_destroy(ctx.constants);
     843             :         }
     844         430 :         zend_arena_destroy(ctx.arena);
     845             : 
     846         430 :         return 1;
     847             : }
     848             : 
     849         378 : int zend_optimizer_startup(void)
     850             : {
     851         378 :         return zend_func_info_startup();
     852             : }
     853             : 
     854       22453 : int zend_optimizer_shutdown(void)
     855             : {
     856       22453 :         return zend_func_info_shutdown();
     857             : }
     858             : 
     859             : /*
     860             :  * Local variables:
     861             :  * tab-width: 4
     862             :  * c-basic-offset: 4
     863             :  * indent-tabs-mode: t
     864             :  * End:
     865             :  */

Generated by: LCOV version 1.10

Generated at Sat, 25 Jun 2016 07:09:00 +0000 (2 days ago)

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