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 - optimize_func_calls.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 99 128 77.3 %
Date: 2017-10-15 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend OPcache                                                         |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2017 The PHP Group                                |
       6             :    +----------------------------------------------------------------------+
       7             :    | This source file is subject to version 3.01 of the PHP license,      |
       8             :    | that is bundled with this package in the file LICENSE, and is        |
       9             :    | available through the world-wide-web at the following url:           |
      10             :    | http://www.php.net/license/3_01.txt                                  |
      11             :    | If you did not receive a copy of the PHP license and are unable to   |
      12             :    | obtain it through the world-wide-web, please send a note to          |
      13             :    | license@php.net so we can mail you a copy immediately.               |
      14             :    +----------------------------------------------------------------------+
      15             :    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
      16             :    |          Xinchen Hui <laruence@php.net>                              |
      17             :    +----------------------------------------------------------------------+
      18             : */
      19             : 
      20             : /* pass 4
      21             :  * - optimize INIT_FCALL_BY_NAME to DO_FCALL
      22             :  */
      23             : 
      24             : #include "php.h"
      25             : #include "Optimizer/zend_optimizer.h"
      26             : #include "Optimizer/zend_optimizer_internal.h"
      27             : #include "zend_API.h"
      28             : #include "zend_constants.h"
      29             : #include "zend_execute.h"
      30             : #include "zend_vm.h"
      31             : 
      32             : #define ZEND_OP1_IS_CONST_STRING(opline) \
      33             :         (ZEND_OP1_TYPE(opline) == IS_CONST && \
      34             :         Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
      35             : #define ZEND_OP2_IS_CONST_STRING(opline) \
      36             :         (ZEND_OP2_TYPE(opline) == IS_CONST && \
      37             :         Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
      38             : 
      39             : typedef struct _optimizer_call_info {
      40             :         zend_function *func;
      41             :         zend_op       *opline;
      42             :         zend_bool      try_inline;
      43             : } optimizer_call_info;
      44             : 
      45           5 : static void zend_delete_call_instructions(zend_op *opline)
      46             : {
      47           5 :         int call = 0;
      48             : 
      49             :         while (1) {
      50           6 :                 switch (opline->opcode) {
      51             :                         case ZEND_INIT_FCALL_BY_NAME:
      52             :                         case ZEND_INIT_NS_FCALL_BY_NAME:
      53             :                         case ZEND_INIT_STATIC_METHOD_CALL:
      54             :                         case ZEND_INIT_METHOD_CALL:
      55             :                         case ZEND_INIT_FCALL:
      56           5 :                                 if (call == 0) {
      57           5 :                                         MAKE_NOP(opline);
      58           5 :                                         return;
      59             :                                 }
      60             :                                 /* break missing intentionally */
      61             :                         case ZEND_NEW:
      62             :                         case ZEND_INIT_DYNAMIC_CALL:
      63             :                         case ZEND_INIT_USER_CALL:
      64           0 :                                 call--;
      65           0 :                                 break;
      66             :                         case ZEND_DO_FCALL:
      67             :                         case ZEND_DO_ICALL:
      68             :                         case ZEND_DO_UCALL:
      69             :                         case ZEND_DO_FCALL_BY_NAME:
      70           0 :                                 call++;
      71           0 :                                 break;
      72             :                         case ZEND_SEND_VAL:
      73             :                         case ZEND_SEND_VAR:
      74           1 :                                 if (call == 0) {
      75           1 :                                         if (opline->op1_type == IS_CONST) {
      76           0 :                                                 MAKE_NOP(opline);
      77           1 :                                         } else if (opline->op1_type == IS_CV) {
      78           1 :                                                 opline->opcode = ZEND_CHECK_VAR;
      79           1 :                                                 opline->extended_value = 0;
      80           1 :                                                 opline->result.var = 0;
      81             :                                         } else {
      82           0 :                                                 opline->opcode = ZEND_FREE;
      83           0 :                                                 opline->extended_value = 0;
      84           0 :                                                 opline->result.var = 0;
      85             :                                         }
      86             :                                 }
      87             :                                 break;
      88             :                 }
      89           1 :                 opline--;
      90           1 :         }
      91             : }
      92             : 
      93        1314 : static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
      94             : {
      95        1467 :         if (func->type == ZEND_USER_FUNCTION
      96        1365 :          && !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
      97             :          && fcall->extended_value >= func->op_array.required_num_args
      98         102 :          && func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {
      99             : 
     100           6 :                 zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
     101             : 
     102           6 :                 if (ret_opline->op1_type == IS_CONST) {
     103           6 :                         uint32_t i, num_args = func->op_array.num_args;
     104           6 :                         num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
     105             : 
     106           6 :                         if (fcall->opcode == ZEND_INIT_METHOD_CALL && fcall->op1_type == IS_UNUSED) {
     107             :                                 /* TODO: we can't inlne methods, because $this may be used
     108             :                                  *       not in object context ???
     109             :                                  */
     110           1 :                                 return;
     111             :                         }
     112             : 
     113           5 :                         for (i = 0; i < num_args; i++) {
     114             :                                 /* Don't inline functions with by-reference arguments. This would require
     115             :                                  * correct handling of INDIRECT arguments. */
     116           0 :                                 if (func->op_array.arg_info[i].pass_by_reference) {
     117           0 :                                         return;
     118             :                                 }
     119             :                         }
     120             : 
     121           5 :                         if (fcall->extended_value < func->op_array.num_args) {
     122             :                                 /* don't inline funcions with named constants in default arguments */
     123           0 :                                 i = fcall->extended_value;
     124             : 
     125             :                                 do {
     126           0 :                                         if (Z_CONSTANT_P(RT_CONSTANT(&func->op_array, func->op_array.opcodes[i].op2))) {
     127           0 :                                                 return;
     128             :                                         }
     129           0 :                                         i++;
     130           0 :                                 } while (i < func->op_array.num_args);
     131             :                         }
     132             : 
     133           5 :                         if (RETURN_VALUE_USED(opline)) {
     134             :                                 zval zv;
     135             : 
     136           5 :                                 ZVAL_DUP(&zv, RT_CONSTANT(&func->op_array, ret_opline->op1));
     137           5 :                                 opline->opcode = ZEND_QM_ASSIGN;
     138           5 :                                 opline->op1_type = IS_CONST;
     139           5 :                                 opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
     140           5 :                                 SET_UNUSED(opline->op2);
     141             :                         } else {
     142           0 :                                 MAKE_NOP(opline);
     143             :                         }
     144             : 
     145           5 :                         zend_delete_call_instructions(opline-1);
     146             :                 }
     147             :         }
     148             : }
     149             : 
     150         726 : void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
     151             : {
     152         726 :         zend_op *opline = op_array->opcodes;
     153         726 :         zend_op *end = opline + op_array->last;
     154         726 :         int call = 0;
     155             :         void *checkpoint;
     156             :         optimizer_call_info *call_stack;
     157             : 
     158         726 :         if (op_array->last < 2) {
     159           3 :                 return;
     160             :         }
     161             : 
     162        1446 :         checkpoint = zend_arena_checkpoint(ctx->arena);
     163        1446 :         call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info));
     164       11872 :         while (opline < end) {
     165       10426 :                 switch (opline->opcode) {
     166             :                         case ZEND_INIT_FCALL_BY_NAME:
     167             :                         case ZEND_INIT_NS_FCALL_BY_NAME:
     168             :                         case ZEND_INIT_STATIC_METHOD_CALL:
     169             :                         case ZEND_INIT_METHOD_CALL:
     170             :                         case ZEND_INIT_FCALL:
     171             :                         case ZEND_NEW:
     172        1411 :                                 call_stack[call].func = zend_optimizer_get_called_func(
     173             :                                         ctx->script, op_array, opline, 0);
     174        1411 :                                 call_stack[call].try_inline = opline->opcode != ZEND_NEW;
     175             :                                 /* break missing intentionally */
     176             :                         case ZEND_INIT_DYNAMIC_CALL:
     177             :                         case ZEND_INIT_USER_CALL:
     178        1432 :                                 call_stack[call].opline = opline;
     179        1432 :                                 call++;
     180        1432 :                                 break;
     181             :                         case ZEND_DO_FCALL:
     182             :                         case ZEND_DO_ICALL:
     183             :                         case ZEND_DO_UCALL:
     184             :                         case ZEND_DO_FCALL_BY_NAME:
     185        1432 :                                 call--;
     186        1432 :                                 if (call_stack[call].func && call_stack[call].opline) {
     187        1315 :                                         zend_op *fcall = call_stack[call].opline;
     188             : 
     189        1315 :                                         if (fcall->opcode == ZEND_INIT_FCALL) {
     190             :                                                 /* nothing to do */
     191         131 :                                         } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
     192          11 :                                                 fcall->opcode = ZEND_INIT_FCALL;
     193          22 :                                                 fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
     194          11 :                                                 Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]);
     195          22 :                                                 literal_dtor(&ZEND_OP2_LITERAL(fcall));
     196          11 :                                                 fcall->op2.constant = fcall->op2.constant + 1;
     197          11 :                                                 opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
     198         120 :                                         } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
     199           0 :                                                 fcall->opcode = ZEND_INIT_FCALL;
     200           0 :                                                 fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
     201           0 :                                                 Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]);
     202           0 :                                                 literal_dtor(&op_array->literals[fcall->op2.constant]);
     203           0 :                                                 literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
     204           0 :                                                 fcall->op2.constant = fcall->op2.constant + 1;
     205           0 :                                                 opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
     206         121 :                                         } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
     207         120 :                                                         || fcall->opcode == ZEND_INIT_METHOD_CALL
     208           1 :                                                         || fcall->opcode == ZEND_NEW) {
     209             :                                                 /* We don't have specialized opcodes for this, do nothing */
     210             :                                         } else {
     211             :                                                 ZEND_ASSERT(0);
     212             :                                         }
     213             : 
     214        2630 :                                         if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
     215        1315 :                                          && call_stack[call].try_inline) {
     216        1314 :                                                 zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
     217             :                                         }
     218             :                                 }
     219        1432 :                                 call_stack[call].func = NULL;
     220        1432 :                                 call_stack[call].opline = NULL;
     221        1432 :                                 call_stack[call].try_inline = 0;
     222        1432 :                                 break;
     223             :                         case ZEND_FETCH_FUNC_ARG:
     224             :                         case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
     225             :                         case ZEND_FETCH_OBJ_FUNC_ARG:
     226             :                         case ZEND_FETCH_DIM_FUNC_ARG:
     227           3 :                                 if (call_stack[call - 1].func) {
     228           6 :                                         if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
     229           2 :                                                 opline->extended_value &= ZEND_FETCH_TYPE_MASK;
     230           2 :                                                 if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
     231           2 :                                                         opline->opcode -= 9;
     232             :                                                 } else {
     233           0 :                                                         opline->opcode = ZEND_FETCH_STATIC_PROP_W;
     234             :                                                 }
     235             :                                         } else {
     236           1 :                                                 if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
     237           1 :                                                                 && opline->op2_type == IS_UNUSED) {
     238             :                                                         /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
     239             :                                                          * Performing the replacement would create an invalid opcode. */
     240           0 :                                                         call_stack[call - 1].try_inline = 0;
     241           0 :                                                         break;
     242             :                                                 }
     243             : 
     244           1 :                                                 opline->extended_value &= ZEND_FETCH_TYPE_MASK;
     245           1 :                                                 if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
     246           1 :                                                         opline->opcode -= 12;
     247             :                                                 } else {
     248           0 :                                                         opline->opcode = ZEND_FETCH_STATIC_PROP_R;
     249             :                                                 }
     250             :                                         }
     251             :                                 }
     252           3 :                                 break;
     253             :                         case ZEND_SEND_VAL_EX:
     254         308 :                                 if (call_stack[call - 1].func) {
     255         540 :                                         if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
     256             :                                                 /* We won't convert it into_DO_FCALL to emit error at run-time */
     257           1 :                                                 call_stack[call - 1].opline = NULL;
     258             :                                         } else {
     259         269 :                                                 opline->opcode = ZEND_SEND_VAL;
     260             :                                         }
     261             :                                 }
     262         308 :                                 break;
     263             :                         case ZEND_SEND_VAR_EX:
     264          20 :                                 if (call_stack[call - 1].func) {
     265          26 :                                         if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
     266           3 :                                                 opline->opcode = ZEND_SEND_REF;
     267             :                                         } else {
     268          10 :                                                 opline->opcode = ZEND_SEND_VAR;
     269             :                                         }
     270             :                                 }
     271          20 :                                 break;
     272             :                         case ZEND_SEND_VAR_NO_REF_EX:
     273           8 :                                 if (call_stack[call - 1].func) {
     274          12 :                                         if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
     275           0 :                                                 opline->opcode = ZEND_SEND_VAR_NO_REF;
     276          12 :                                         } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
     277           0 :                                                 opline->opcode = ZEND_SEND_VAL;
     278             :                                         } else {
     279           6 :                                                 opline->opcode = ZEND_SEND_VAR;
     280             :                                         }
     281             :                                 }
     282           8 :                                 break;
     283             :                         case ZEND_SEND_UNPACK:
     284             :                         case ZEND_SEND_USER:
     285             :                         case ZEND_SEND_ARRAY:
     286           1 :                                 call_stack[call - 1].try_inline = 0;
     287             :                                 break;
     288             :                         default:
     289             :                                 break;
     290             :                 }
     291       10426 :                 opline++;
     292             :         }
     293             : 
     294         723 :         zend_arena_release(&ctx->arena, checkpoint);
     295             : }

Generated by: LCOV version 1.10

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

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