@@ -41,6 +41,91 @@ typedef struct _optimizer_call_info {
4141 zend_op * opline ;
4242} optimizer_call_info ;
4343
44+ static void zend_delete_call_instructions (zend_op * opline )
45+ {
46+ int call = 0 ;
47+
48+ while (1 ) {
49+ switch (opline -> opcode ) {
50+ case ZEND_INIT_FCALL_BY_NAME :
51+ case ZEND_INIT_NS_FCALL_BY_NAME :
52+ case ZEND_INIT_STATIC_METHOD_CALL :
53+ case ZEND_INIT_METHOD_CALL :
54+ case ZEND_INIT_FCALL :
55+ if (call == 0 ) {
56+ MAKE_NOP (opline );
57+ return ;
58+ }
59+ /* break missing intentionally */
60+ case ZEND_NEW :
61+ case ZEND_INIT_DYNAMIC_CALL :
62+ case ZEND_INIT_USER_CALL :
63+ call -- ;
64+ break ;
65+ case ZEND_DO_FCALL :
66+ case ZEND_DO_ICALL :
67+ case ZEND_DO_UCALL :
68+ case ZEND_DO_FCALL_BY_NAME :
69+ call ++ ;
70+ break ;
71+ case ZEND_SEND_VAL :
72+ case ZEND_SEND_VAR :
73+ case ZEND_SEND_VAR_NO_REF :
74+ case ZEND_SEND_REF :
75+ if (call == 0 ) {
76+ if (opline -> op1_type & (IS_CONST |IS_CV )) {
77+ MAKE_NOP (opline );
78+ } else {
79+ opline -> opcode = ZEND_FREE ;
80+ opline -> extended_value = 0 ;
81+ opline -> result .var = 0 ;
82+ }
83+ }
84+ break ;
85+ }
86+ opline -- ;
87+ }
88+ }
89+
90+ static void zend_try_inline_call (zend_op_array * op_array , zend_op * fcall , zend_op * opline , zend_function * func )
91+ {
92+ if (func -> type == ZEND_USER_FUNCTION
93+ && !(func -> op_array .fn_flags & (ZEND_ACC_ABSTRACT |ZEND_ACC_HAS_TYPE_HINTS ))
94+ && fcall -> extended_value >= func -> op_array .required_num_args
95+ && func -> op_array .opcodes [func -> op_array .num_args ].opcode == ZEND_RETURN ) {
96+
97+ zend_op * ret_opline = func -> op_array .opcodes + func -> op_array .num_args ;
98+
99+ if (ret_opline -> op1_type == IS_CONST ) {
100+
101+ if (fcall -> extended_value < func -> op_array .num_args ) {
102+ /* don't inline funcions with named constants in default arguments */
103+ uint32_t n = fcall -> extended_value ;
104+
105+ do {
106+ if (Z_CONSTANT_P (RT_CONSTANT_EX (& func -> op_array , func -> op_array .opcodes [n ].op2 ))) {
107+ return ;
108+ }
109+ n ++ ;
110+ } while (n < func -> op_array .num_args );
111+ }
112+ if (RETURN_VALUE_USED (opline )) {
113+ zval zv ;
114+
115+ ZVAL_DUP (& zv , RT_CONSTANT_EX (& func -> op_array , ret_opline -> op1 ));
116+ opline -> opcode = ZEND_QM_ASSIGN ;
117+ opline -> op1_type = IS_CONST ;
118+ opline -> op1 .constant = zend_optimizer_add_literal (op_array , & zv );
119+ SET_UNUSED (opline -> op2 );
120+ } else {
121+ MAKE_NOP (opline );
122+ }
123+
124+ zend_delete_call_instructions (opline - 1 );
125+ }
126+ }
127+ }
128+
44129void zend_optimize_func_calls (zend_op_array * op_array , zend_optimizer_ctx * ctx )
45130{
46131 zend_op * opline = op_array -> opcodes ;
@@ -61,12 +146,12 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
61146 case ZEND_INIT_NS_FCALL_BY_NAME :
62147 case ZEND_INIT_STATIC_METHOD_CALL :
63148 case ZEND_INIT_METHOD_CALL :
149+ case ZEND_INIT_FCALL :
64150 call_stack [call ].func = zend_optimizer_get_called_func (
65151 ctx -> script , op_array , opline , 0 );
66152 /* break missing intentionally */
67153 case ZEND_NEW :
68154 case ZEND_INIT_DYNAMIC_CALL :
69- case ZEND_INIT_FCALL :
70155 case ZEND_INIT_USER_CALL :
71156 call_stack [call ].opline = opline ;
72157 call ++ ;
@@ -79,7 +164,9 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
79164 if (call_stack [call ].func && call_stack [call ].opline ) {
80165 zend_op * fcall = call_stack [call ].opline ;
81166
82- if (fcall -> opcode == ZEND_INIT_FCALL_BY_NAME ) {
167+ if (fcall -> opcode == ZEND_INIT_FCALL ) {
168+ /* nothing to do */
169+ } else if (fcall -> opcode == ZEND_INIT_FCALL_BY_NAME ) {
83170 fcall -> opcode = ZEND_INIT_FCALL ;
84171 fcall -> op1 .num = zend_vm_calc_used_stack (fcall -> extended_value , call_stack [call ].func );
85172 Z_CACHE_SLOT (op_array -> literals [fcall -> op2 .constant + 1 ]) = Z_CACHE_SLOT (op_array -> literals [fcall -> op2 .constant ]);
@@ -100,6 +187,10 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
100187 } else {
101188 ZEND_ASSERT (0 );
102189 }
190+
191+ if (ZEND_OPTIMIZER_PASS_16 & ctx -> optimization_level ) {
192+ zend_try_inline_call (op_array , fcall , opline , call_stack [call ].func );
193+ }
103194 }
104195 call_stack [call ].func = NULL ;
105196 call_stack [call ].opline = NULL ;
0 commit comments