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 372 39.2 %
Date: 2016-02-05 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_OP_DATA:
     311           0 :                         if ((opline-1)->opcode != ZEND_ASSIGN_DIM &&
     312           0 :                                 ((opline-1)->extended_value != ZEND_ASSIGN_DIM ||
     313           0 :                                  ((opline-1)->opcode != ZEND_ASSIGN_ADD &&
     314           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_SUB &&
     315           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_MUL &&
     316           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_DIV &&
     317           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_POW &&
     318           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_MOD &&
     319           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_SL &&
     320           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_SR &&
     321           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_CONCAT &&
     322           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_BW_OR &&
     323           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_BW_AND &&
     324           0 :                                  (opline-1)->opcode != ZEND_ASSIGN_BW_XOR))
     325             :                         ) {
     326           0 :                                 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     327           0 :                                 break;
     328             :                         }
     329             :                         /* break missing intentionally */
     330             :                 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
     331             :                 case ZEND_ADD_ARRAY_ELEMENT:
     332             :                 case ZEND_INIT_ARRAY:
     333             :                 case ZEND_ASSIGN_DIM:
     334             :                 case ZEND_UNSET_DIM:
     335             :                 case ZEND_FETCH_DIM_R:
     336             :                 case ZEND_FETCH_DIM_W:
     337             :                 case ZEND_FETCH_DIM_RW:
     338             :                 case ZEND_FETCH_DIM_IS:
     339             :                 case ZEND_FETCH_DIM_FUNC_ARG:
     340             :                 case ZEND_FETCH_DIM_UNSET:
     341             :                 case ZEND_FETCH_LIST:
     342           0 :                         if (Z_TYPE_P(val) == IS_STRING) {
     343             :                                 zend_ulong index;
     344           0 :                                 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
     345             :                                         zval_dtor(val);
     346           0 :                                         ZVAL_LONG(val, index);
     347             :                                 }
     348             :                         }
     349           0 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     350           0 :                         break;
     351             :                 case ZEND_ROPE_INIT:
     352             :                 case ZEND_ROPE_ADD:
     353             :                 case ZEND_ROPE_END:
     354             :                 case ZEND_CONCAT:
     355             :                 case ZEND_FAST_CONCAT:
     356           0 :                         TO_STRING_NOWARN(val);
     357             :                         /* break missing intentionally */
     358             :                 default:
     359           1 :                         opline->op2.constant = zend_optimizer_add_literal(op_array, val);
     360             :                         break;
     361             :         }
     362             : 
     363           1 :         ZEND_OP2_TYPE(opline) = IS_CONST;
     364           2 :         if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
     365           1 :                 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
     366             :         }
     367           1 :         return 1;
     368             : }
     369             : 
     370         424 : void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
     371             : {
     372         424 :         if (op_array->last_live_range) {
     373         113 :                 int i = 0;
     374         113 :                 int j = 0;
     375             :                 uint32_t *map;
     376             :                 ALLOCA_FLAG(use_heap);
     377             : 
     378         113 :                 map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
     379             : 
     380             :                 do {
     381         339 :                         if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
     382         339 :                                 map[i] = j;
     383         339 :                                 if (i != j) {
     384           0 :                                         op_array->live_range[j] = op_array->live_range[i];
     385             :                                 }
     386         339 :                                 j++;
     387             :                         }
     388         339 :                         i++;
     389         339 :                 } while (i < op_array->last_live_range);
     390         113 :                 if (i != j) {
     391           0 :                         zend_op *opline = op_array->opcodes;
     392           0 :                         zend_op *end = opline + op_array->last;
     393             : 
     394           0 :                         op_array->last_live_range = j;
     395           0 :                         while (opline != end) {
     396           0 :                                 if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
     397           0 :                                     opline->extended_value == ZEND_FREE_ON_RETURN) {
     398           0 :                                         opline->op2.num = map[opline->op2.num];
     399             :                                 }
     400           0 :                                 opline++;
     401             :                         }
     402             :                 }
     403         113 :                 free_alloca(map, use_heap);
     404             :         }
     405         424 : }
     406             : 
     407         424 : int zend_optimizer_replace_by_const(zend_op_array *op_array,
     408             :                                     zend_op       *opline,
     409             :                                     zend_uchar     type,
     410             :                                     uint32_t       var,
     411             :                                     zval          *val)
     412             : {
     413         424 :         zend_op *end = op_array->opcodes + op_array->last;
     414             : 
     415         904 :         while (opline < end) {
     416         903 :                 if (ZEND_OP1_TYPE(opline) == type &&
     417         423 :                         ZEND_OP1(opline).var == var) {
     418         423 :                         switch (opline->opcode) {
     419             :                                 case ZEND_FETCH_DIM_W:
     420             :                                 case ZEND_FETCH_DIM_RW:
     421             :                                 case ZEND_FETCH_DIM_FUNC_ARG:
     422             :                                 case ZEND_FETCH_DIM_UNSET:
     423             :                                 case ZEND_ASSIGN_DIM:
     424             :                                 case ZEND_SEPARATE:
     425             :                                 case ZEND_RETURN_BY_REF:
     426             :                                         zval_dtor(val);
     427           0 :                                         return 0;
     428             :                                 case ZEND_SEND_VAR:
     429           0 :                                         opline->extended_value = 0;
     430           0 :                                         opline->opcode = ZEND_SEND_VAL;
     431           0 :                                         break;
     432             :                                 case ZEND_SEND_VAR_EX:
     433           0 :                                         opline->extended_value = 0;
     434           0 :                                         opline->opcode = ZEND_SEND_VAL_EX;
     435           0 :                                         break;
     436             :                                 case ZEND_SEND_VAR_NO_REF:
     437           0 :                                         if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) {
     438           0 :                                                 if (opline->extended_value & ZEND_ARG_SEND_BY_REF) {
     439             :                                                         zval_dtor(val);
     440           0 :                                                         return 0;
     441             :                                                 }
     442           0 :                                                 opline->extended_value = 0;
     443           0 :                                                 opline->opcode = ZEND_SEND_VAL_EX;
     444             :                                         } else {
     445           0 :                                                 opline->extended_value = 0;
     446           0 :                                                 opline->opcode = ZEND_SEND_VAL;
     447             :                                         }
     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 is an exception, that keeps operand unchanged,
     455             :                                  * and allows its reuse. The number of ZEND_CASE instructions
     456             :                                  * usually terminated by ZEND_FREE that finally kills the value.
     457             :                                  */
     458             :                                 case ZEND_FREE:
     459             :                                 case ZEND_CASE: {
     460             :                                         zend_op *m, *n;
     461           0 :                                         int brk = op_array->last_live_range;
     462           0 :                                         zend_bool in_switch = 0;
     463           0 :                                         while (brk--) {
     464           0 :                                                 if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
     465           0 :                                                     op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
     466           0 :                                                         in_switch = 1;
     467           0 :                                                         break;
     468             :                                                 }
     469             :                                         }
     470             : 
     471           0 :                                         if (!in_switch) {
     472             :                                                 ZEND_ASSERT(opline->opcode == ZEND_FREE);
     473           0 :                                                 MAKE_NOP(opline);
     474             :                                                 zval_dtor(val);
     475           0 :                                                 return 1;
     476             :                                         }
     477             : 
     478           0 :                                         m = opline;
     479           0 :                                         n = op_array->opcodes + op_array->live_range[brk].end;
     480           0 :                                         if (n->opcode == ZEND_FREE &&
     481           0 :                                             !(n->extended_value & ZEND_FREE_ON_RETURN)) {
     482           0 :                                                 n++;
     483             :                                         } else {
     484           0 :                                                 n = op_array->opcodes + op_array->last;
     485             :                                         }
     486           0 :                                         while (m < n) {
     487           0 :                                                 if (ZEND_OP1_TYPE(m) == type &&
     488           0 :                                                                 ZEND_OP1(m).var == var) {
     489           0 :                                                         if (m->opcode == ZEND_CASE) {
     490             :                                                                 zval old_val;
     491           0 :                                                                 ZVAL_COPY_VALUE(&old_val, val);
     492             :                                                                 zval_copy_ctor(val);
     493           0 :                                                                 zend_optimizer_update_op1_const(op_array, m, val);
     494           0 :                                                                 ZVAL_COPY_VALUE(val, &old_val);
     495           0 :                                                         } else if (m->opcode == ZEND_FREE) {
     496           0 :                                                                 MAKE_NOP(m);
     497             :                                                         } else {
     498             :                                                                 ZEND_ASSERT(0);
     499             :                                                         }
     500             :                                                 }
     501           0 :                                                 m++;
     502             :                                         }
     503             :                                         zval_dtor(val);
     504           0 :                                         zend_optimizer_remove_live_range(op_array, var);
     505           0 :                                         return 1;
     506             :                                 }
     507             :                                 case ZEND_VERIFY_RETURN_TYPE: {
     508           0 :                                         zend_arg_info *ret_info = op_array->arg_info - 1;
     509             :                                         ZEND_ASSERT((opline + 1)->opcode == ZEND_RETURN || (opline + 1)->opcode == ZEND_RETURN_BY_REF);
     510           0 :                                         if (ret_info->class_name
     511           0 :                                                 || ret_info->type_hint == IS_CALLABLE
     512           0 :                                                 || !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
     513           0 :                                                 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
     514             :                                                 zval_dtor(val);
     515           0 :                                                 return 0;
     516             :                                         }
     517           0 :                                         MAKE_NOP(opline);
     518           0 :                                         opline++;
     519             :                                         break;
     520             :                                   }
     521             :                                 default:
     522             :                                         break;
     523             :                         }
     524         423 :                         if (zend_optimizer_update_op1_const(op_array, opline, val)) {
     525         423 :                                 zend_optimizer_remove_live_range(op_array, var);
     526         423 :                                 return 1;
     527             :                         }
     528           0 :                         return 0;
     529             :                 }
     530             : 
     531          58 :                 if (ZEND_OP2_TYPE(opline) == type &&
     532           1 :                         ZEND_OP2(opline).var == var) {
     533           1 :                         if (zend_optimizer_update_op2_const(op_array, opline, val)) {
     534           1 :                                 zend_optimizer_remove_live_range(op_array, var);
     535           1 :                                 return 1;
     536             :                         }
     537           0 :                         return 0;
     538             :                 }
     539          56 :                 opline++;
     540             :         }
     541             : 
     542           0 :         return 1;
     543             : }
     544             : 
     545         463 : static void zend_optimize(zend_op_array      *op_array,
     546             :                           zend_optimizer_ctx *ctx)
     547             : {
     548         463 :         if (op_array->type == ZEND_EVAL_CODE) {
     549           0 :                 return;
     550             :         }
     551             : 
     552         463 :         if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
     553           0 :                 zend_dump_op_array(op_array, 0, "before optimizer", NULL);
     554             :         }
     555             : 
     556             :         /* pass 1
     557             :          * - substitute persistent constants (true, false, null, etc)
     558             :          * - perform compile-time evaluation of constant binary and unary operations
     559             :          * - optimize series of ADD_STRING and/or ADD_CHAR
     560             :          * - convert CAST(IS_BOOL,x) into BOOL(x)
     561             :          */
     562         463 :         if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
     563         463 :                 zend_optimizer_pass1(op_array, ctx);
     564         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
     565           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     566             :                 }
     567             :         }
     568             : 
     569             :         /* pass 2:
     570             :          * - convert non-numeric constants to numeric constants in numeric operators
     571             :          * - optimize constant conditional JMPs
     572             :          * - optimize static BRKs and CONTs
     573             :          * - pre-evaluate constant function calls
     574             :          */
     575         463 :         if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
     576         463 :                 zend_optimizer_pass2(op_array);
     577         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
     578           0 :                         zend_dump_op_array(op_array, 0, "after pass 2", NULL);
     579             :                 }
     580             :         }
     581             : 
     582             :         /* pass 3:
     583             :          * - optimize $i = $i+expr to $i+=expr
     584             :          * - optimize series of JMPs
     585             :          * - change $i++ to ++$i where possible
     586             :          */
     587         463 :         if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
     588         463 :                 zend_optimizer_pass3(op_array);
     589         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
     590           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     591             :                 }
     592             :         }
     593             : 
     594             :         /* pass 4:
     595             :          * - INIT_FCALL_BY_NAME -> DO_FCALL
     596             :          */
     597         463 :         if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
     598         463 :                 zend_optimize_func_calls(op_array, ctx);
     599         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
     600           0 :                         zend_dump_op_array(op_array, 0, "after pass 1", NULL);
     601             :                 }
     602             :         }
     603             : 
     604             :         /* pass 5:
     605             :          * - CFG optimization
     606             :          */
     607         463 :         if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
     608         463 :                 zend_optimize_cfg(op_array, ctx);
     609         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
     610           0 :                         zend_dump_op_array(op_array, 0, "after pass 5", NULL);
     611             :                 }
     612             :         }
     613             : 
     614             : #if HAVE_DFA_PASS
     615             :         /* pass 6:
     616             :          * - DFA optimization
     617             :          */
     618             :         if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
     619             :             !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
     620             :                 zend_optimize_dfa(op_array, ctx);
     621             :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
     622             :                         zend_dump_op_array(op_array, 0, "after pass 6", NULL);
     623             :                 }
     624             :         }
     625             : #endif
     626             : 
     627             :         /* pass 9:
     628             :          * - Optimize temp variables usage
     629             :          */
     630         463 :         if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
     631         463 :                 zend_optimize_temporary_variables(op_array, ctx);
     632         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
     633           0 :                         zend_dump_op_array(op_array, 0, "after pass 9", NULL);
     634             :                 }
     635             :         }
     636             : 
     637             :         /* pass 10:
     638             :          * - remove NOPs
     639             :          */
     640         463 :         if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
     641           0 :                 zend_optimizer_nop_removal(op_array);
     642           0 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
     643           0 :                         zend_dump_op_array(op_array, 0, "after pass 10", NULL);
     644             :                 }
     645             :         }
     646             : 
     647             :         /* pass 11:
     648             :          * - Compact literals table
     649             :          */
     650         463 :         if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
     651         463 :                 zend_optimizer_compact_literals(op_array, ctx);
     652         463 :                 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
     653           0 :                         zend_dump_op_array(op_array, 0, "after pass 11", NULL);
     654             :                 }
     655             :         }
     656             : 
     657         463 :         if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
     658           0 :                 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
     659             :         }
     660             : }
     661             : 
     662         463 : static void zend_revert_pass_two(zend_op_array *op_array)
     663             : {
     664             :         zend_op *opline, *end;
     665             : 
     666         463 :         opline = op_array->opcodes;
     667         463 :         end = opline + op_array->last;
     668        8288 :         while (opline < end) {
     669        7362 :                 if (opline->op1_type == IS_CONST) {
     670        2545 :                         ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
     671             :                 }
     672        7362 :                 if (opline->op2_type == IS_CONST) {
     673        1724 :                         ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
     674             :                 }
     675        7362 :                 opline++;
     676             :         }
     677         463 : }
     678             : 
     679         463 : static void zend_redo_pass_two(zend_op_array *op_array)
     680             : {
     681             :         zend_op *opline, *end;
     682             : 
     683         463 :         opline = op_array->opcodes;
     684         463 :         end = opline + op_array->last;
     685        6679 :         while (opline < end) {
     686        5753 :                 if (opline->op1_type == IS_CONST) {
     687        2069 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
     688             :                 }
     689        5753 :                 if (opline->op2_type == IS_CONST) {
     690        1423 :                         ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
     691             :                 }
     692        5753 :                 ZEND_VM_SET_OPCODE_HANDLER(opline);
     693        5753 :                 opline++;
     694             :         }
     695         463 : }
     696             : 
     697         463 : static void zend_optimize_op_array(zend_op_array      *op_array,
     698             :                                    zend_optimizer_ctx *ctx)
     699             : {
     700             :         /* Revert pass_two() */
     701         463 :         zend_revert_pass_two(op_array);
     702             : 
     703             :         /* Do actual optimizations */
     704         463 :         zend_optimize(op_array, ctx);
     705             : 
     706             :         /* Redo pass_two() */
     707         463 :         zend_redo_pass_two(op_array);
     708         463 : }
     709             : 
     710         463 : static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
     711             : {
     712             :         zend_function *func;
     713             :         zend_op *opline, *end;
     714             : 
     715         463 :         opline = op_array->opcodes;
     716         463 :         end = opline + op_array->last;
     717        6679 :         while (opline < end) {
     718        5753 :                 if (opline->opcode == ZEND_INIT_FCALL) {
     719        1708 :                         func = zend_hash_find_ptr(
     720         854 :                                 &ctx->script->function_table,
     721         854 :                                 Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
     722         854 :                         if (func) {
     723           0 :                                 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
     724             :                         }
     725             :                 }
     726        5753 :                 opline++;
     727             :         }
     728         463 : }
     729             : 
     730           0 : static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
     731             : {
     732           0 :         zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
     733             : 
     734           0 :         if (func_info) {
     735           0 :                 zend_call_info *call_info =func_info->callee_info;
     736             : 
     737           0 :                 while (call_info) {
     738           0 :                         zend_op *opline = call_info->caller_init_opline;
     739             : 
     740           0 :                         if (opline && call_info->callee_func) {
     741             :                                 ZEND_ASSERT(opline->opcode == ZEND_INIT_FCALL);
     742           0 :                                 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
     743             :                         }
     744           0 :                         call_info = call_info->next_callee;
     745             :                 }
     746             :         }
     747           0 : }
     748             : 
     749         430 : int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
     750             : {
     751             :         zend_class_entry *ce;
     752             :         zend_op_array *op_array;
     753             :         zend_string *name;
     754             :         zend_optimizer_ctx ctx;
     755             :         zend_call_graph call_graph;
     756             : 
     757         430 :         ctx.arena = zend_arena_create(64 * 1024);
     758         430 :         ctx.script = script;
     759         430 :         ctx.constants = NULL;
     760         430 :         ctx.optimization_level = optimization_level;
     761         430 :         ctx.debug_level = debug_level;
     762             : 
     763         430 :         zend_optimize_op_array(&script->main_op_array, &ctx);
     764             : 
     765         478 :         ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
     766          24 :                 zend_optimize_op_array(op_array, &ctx);
     767             :         } ZEND_HASH_FOREACH_END();
     768             : 
     769         458 :         ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
     770          25 :                 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
     771           9 :                         if (op_array->scope == ce) {
     772           9 :                                 zend_optimize_op_array(op_array, &ctx);
     773           0 :                         } else if (op_array->type == ZEND_USER_FUNCTION) {
     774             :                                 zend_op_array *orig_op_array;
     775           0 :                                 if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
     776           0 :                                         HashTable *ht = op_array->static_variables;
     777           0 :                                         *op_array = *orig_op_array;
     778           0 :                                         op_array->static_variables = ht;
     779             :                                 }
     780             :                         }
     781             :                 } ZEND_HASH_FOREACH_END();
     782             :         } ZEND_HASH_FOREACH_END();
     783             : 
     784             : #if HAVE_DFA_PASS
     785             :         if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
     786             :             (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
     787             :             zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
     788             :                 /* Optimize using call-graph */
     789             :                 void *checkpoint = zend_arena_checkpoint(ctx.arena);
     790             :                 int i;
     791             :                 zend_func_info *func_info;
     792             : 
     793             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     794             :                         zend_revert_pass_two(call_graph.op_arrays[i]);
     795             :                 }
     796             : 
     797             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     798             :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
     799             :                         if (func_info) {
     800             :                                 zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
     801             :                         }
     802             :                 }
     803             : 
     804             :                 //TODO: perform inner-script inference???
     805             : 
     806             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     807             :                         func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
     808             :                         if (func_info) {
     809             :                                 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
     810             :                         }
     811             :                 }
     812             : 
     813             :                 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
     814             :                         for (i = 0; i < call_graph.op_arrays_count; i++) {
     815             :                                 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
     816             :                         }
     817             :                 }
     818             : 
     819             :                 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
     820             :                         for (i = 0; i < call_graph.op_arrays_count; i++) {
     821             :                                 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
     822             :                         }
     823             :                 }
     824             : 
     825             :                 for (i = 0; i < call_graph.op_arrays_count; i++) {
     826             :                         zend_redo_pass_two(call_graph.op_arrays[i]);
     827             :                         ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
     828             :                 }
     829             : 
     830             :                 zend_arena_release(&ctx.arena, checkpoint);
     831             :         } else
     832             : #endif
     833             : 
     834         430 :         if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
     835         430 :                 zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
     836             : 
     837         478 :                 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
     838          24 :                         zend_adjust_fcall_stack_size(op_array, &ctx);
     839             :                 } ZEND_HASH_FOREACH_END();
     840             : 
     841         458 :                 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
     842          25 :                         ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
     843           9 :                                 if (op_array->scope == ce) {
     844           9 :                                         zend_adjust_fcall_stack_size(op_array, &ctx);
     845           0 :                                 } else if (op_array->type == ZEND_USER_FUNCTION) {
     846             :                                         zend_op_array *orig_op_array;
     847           0 :                                         if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
     848           0 :                                                 HashTable *ht = op_array->static_variables;
     849           0 :                                                 *op_array = *orig_op_array;
     850           0 :                                                 op_array->static_variables = ht;
     851             :                                         }
     852             :                                 }
     853             :                         } ZEND_HASH_FOREACH_END();
     854             :                 } ZEND_HASH_FOREACH_END();
     855             :         }
     856             : 
     857         430 :         if (ctx.constants) {
     858           0 :                 zend_hash_destroy(ctx.constants);
     859             :         }
     860         430 :         zend_arena_destroy(ctx.arena);
     861             : 
     862         430 :         return 1;
     863             : }
     864             : 
     865         378 : int zend_optimizer_startup(void)
     866             : {
     867         378 :         return zend_func_info_startup();
     868             : }
     869             : 
     870       22430 : int zend_optimizer_shutdown(void)
     871             : {
     872       22430 :         return zend_func_info_shutdown();
     873             : }
     874             : 
     875             : /*
     876             :  * Local variables:
     877             :  * tab-width: 4
     878             :  * c-basic-offset: 4
     879             :  * indent-tabs-mode: t
     880             :  * End:
     881             :  */

Generated by: LCOV version 1.10

Generated at Fri, 05 Feb 2016 08:39:19 +0000 (3 days ago)

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