Skip to content

Commit edfeab8

Browse files
committed
Added an optimization pass to convert FCALL_BY_NAME into DO_FCALL.
1 parent 7deb3d4 commit edfeab8

File tree

4 files changed

+145
-20
lines changed

4 files changed

+145
-20
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* pass 4
2+
* - optimize INIT_FCALL_BY_NAME to DO_FCALL
3+
*/
4+
5+
typedef struct _optimizer_call_info {
6+
zend_function *func;
7+
zend_op *opline;
8+
} optimizer_call_info;
9+
10+
static void optimize_func_calls(zend_op_array *op_array, zend_persistent_script *script TSRMLS_CC) {
11+
zend_op *opline = op_array->opcodes;
12+
zend_op *end = opline + op_array->last;
13+
int call = 0;
14+
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
15+
optimizer_call_info *call_stack = ecalloc(op_array->nested_calls + 1, sizeof(optimizer_call_info));
16+
#else
17+
int stack_size = 4;
18+
optimizer_call_info *call_stack = ecalloc(stack_size, sizeof(optimizer_call_info));
19+
#endif
20+
21+
while (opline < end) {
22+
switch (opline->opcode) {
23+
case ZEND_INIT_FCALL_BY_NAME:
24+
case ZEND_INIT_NS_FCALL_BY_NAME:
25+
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
26+
zend_function *func;
27+
zval *function_name = &op_array->literals[opline->op2.constant + 1].constant;
28+
if ((zend_hash_quick_find(&script->function_table,
29+
Z_STRVAL_P(function_name), Z_STRLEN_P(function_name) + 1,
30+
Z_HASH_P(function_name), (void **)&func) == SUCCESS)) {
31+
call_stack[call].func = func;
32+
}
33+
}
34+
/* break missing intentionally */
35+
case ZEND_NEW:
36+
case ZEND_INIT_METHOD_CALL:
37+
case ZEND_INIT_STATIC_METHOD_CALL:
38+
call_stack[call].opline = opline;
39+
call++;
40+
#if ZEND_EXTENSION_API_NO < PHP_5_5_X_API_NO
41+
if (call == stack_size) {
42+
stack_size += 4;
43+
call_stack = erealloc(call_stack, sizeof(optimizer_call_info) * stack_size);
44+
memset(call_stack + 4, 0, 4 * sizeof(optimizer_call_info));
45+
}
46+
#endif
47+
break;
48+
case ZEND_DO_FCALL_BY_NAME:
49+
call--;
50+
if (call_stack[call].func && call_stack[call].opline) {
51+
zend_op *fcall = call_stack[call].opline;
52+
53+
opline->opcode = ZEND_DO_FCALL;
54+
ZEND_OP1_TYPE(opline) = IS_CONST;
55+
opline->op1.constant = fcall->op2.constant + 1;
56+
op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
57+
literal_dtor(&ZEND_OP2_LITERAL(fcall));
58+
if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
59+
literal_dtor(&op_array->literals[fcall->op2.constant + 2].constant);
60+
}
61+
MAKE_NOP(fcall);
62+
} else if (opline->extended_value == 0 &&
63+
call_stack[call].opline &&
64+
call_stack[call].opline == ZEND_INIT_FCALL_BY_NAME &&
65+
ZEND_OP2_TYPE(call_stack[call].opline) == IS_CONST) {
66+
67+
zend_op *fcall = call_stack[call].opline;
68+
69+
opline->opcode = ZEND_DO_FCALL;
70+
ZEND_OP1_TYPE(opline) = IS_CONST;
71+
opline->op1.constant = fcall->op2.constant + 1;
72+
op_array->literals[fcall->op2.constant + 1].cache_slot = op_array->literals[fcall->op2.constant].cache_slot;
73+
literal_dtor(&ZEND_OP2_LITERAL(fcall));
74+
MAKE_NOP(fcall);
75+
}
76+
call_stack[call].func = NULL;
77+
call_stack[call].opline = NULL;
78+
break;
79+
case ZEND_FETCH_FUNC_ARG:
80+
case ZEND_FETCH_OBJ_FUNC_ARG:
81+
case ZEND_FETCH_DIM_FUNC_ARG:
82+
if (call_stack[call - 1].func) {
83+
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
84+
opline->extended_value = 0;
85+
opline->opcode -= 9;
86+
} else {
87+
opline->extended_value = 0;
88+
opline->opcode -= 12;
89+
}
90+
}
91+
break;
92+
case ZEND_SEND_VAL:
93+
if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
94+
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call].func, opline->op2.num)) {
95+
/* We won't convert it into_DO_FCALL to emit error at run-time */
96+
call_stack[call].opline = NULL;
97+
} else {
98+
opline->extended_value = ZEND_DO_FCALL;
99+
}
100+
}
101+
break;
102+
case ZEND_SEND_VAR:
103+
if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
104+
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call].func, opline->op2.num)) {
105+
opline->opcode = ZEND_SEND_REF;
106+
}
107+
opline->extended_value = ZEND_DO_FCALL;
108+
}
109+
break;
110+
case ZEND_SEND_VAR_NO_REF:
111+
if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) {
112+
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call].func, opline->op2.num)) {
113+
opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF;
114+
} else if (opline->extended_value) {
115+
opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND;
116+
} else {
117+
opline->opcode = ZEND_SEND_VAR;
118+
opline->extended_value = ZEND_DO_FCALL;
119+
}
120+
}
121+
break;
122+
case ZEND_SEND_REF:
123+
if (opline->extended_value == ZEND_DO_FCALL_BY_NAME && call_stack[call - 1].func) {
124+
/* We won't handle run-time pass by reference */
125+
call_stack[call].opline = NULL;
126+
}
127+
break;
128+
129+
default:
130+
break;
131+
}
132+
opline++;
133+
}
134+
135+
efree(call_stack);
136+
}

ext/opcache/Optimizer/pass1_5.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* - perform compile-time evaluation of constant binary and unary operations
44
* - optimize series of ADD_STRING and/or ADD_CHAR
55
* - convert CAST(IS_BOOL,x) into BOOL(x)
6-
* - convert INTI_FCALL_BY_NAME, DO_FCALL_BY_NAME into DO_FCALL
76
*/
87

98
if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
@@ -374,23 +373,6 @@ if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
374373
}
375374
break;
376375

377-
case ZEND_INIT_FCALL_BY_NAME:
378-
if (opline->extended_value == 0 /* not method */ &&
379-
ZEND_OP1_TYPE(opline) == IS_UNUSED &&
380-
ZEND_OP2_TYPE(opline) == IS_CONST) {
381-
if ((opline + 1)->opcode == ZEND_DO_FCALL_BY_NAME &&
382-
(opline + 1)->extended_value == 0) {
383-
(opline + 1)->opcode = ZEND_DO_FCALL;
384-
COPY_NODE((opline + 1)->op1, opline->op2);
385-
zend_str_tolower(Z_STRVAL(ZEND_OP1_LITERAL(opline + 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline + 1)));
386-
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
387-
Z_HASH_P(&ZEND_OP1_LITERAL(opline + 1)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline + 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline + 1)) + 1);
388-
op_array->literals[(opline + 1)->op1.constant].cache_slot = op_array->last_cache_slot++;
389-
#endif
390-
MAKE_NOP(opline);
391-
}
392-
}
393-
break;
394376
case ZEND_DO_FCALL:
395377
/* define("name", scalar); */
396378
if (collect_constants &&

ext/opcache/Optimizer/zend_optimizer.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC
113113
#include "Optimizer/block_pass.c"
114114
#include "Optimizer/optimize_temp_vars_5.c"
115115
#include "Optimizer/compact_literals.c"
116+
#include "Optimizer/optimize_func_calls.c"
116117

117118
static void zend_optimize(zend_op_array *op_array,
118119
zend_persistent_script *script,
@@ -128,7 +129,6 @@ static void zend_optimize(zend_op_array *op_array,
128129
* - perform compile-time evaluation of constant binary and unary operations
129130
* - optimize series of ADD_STRING and/or ADD_CHAR
130131
* - convert CAST(IS_BOOL,x) into BOOL(x)
131-
* - convert INTI_FCALL_BY_NAME + DO_FCALL_BY_NAME into DO_FCALL
132132
*/
133133
#include "Optimizer/pass1_5.c"
134134

@@ -146,6 +146,13 @@ static void zend_optimize(zend_op_array *op_array,
146146
*/
147147
#include "Optimizer/pass3.c"
148148

149+
/* pass 4:
150+
* - INIT_FCALL_BY_NAME -> DO_FCALL
151+
*/
152+
if (ZEND_OPTIMIZER_PASS_4 & OPTIMIZATION_LEVEL) {
153+
optimize_func_calls(op_array, script TSRMLS_CC);
154+
}
155+
149156
/* pass 5:
150157
* - CFG optimization
151158
*/

ext/opcache/Optimizer/zend_optimizer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */
2929
#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jumps */
3030
#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */
31-
#define ZEND_OPTIMIZER_PASS_4 (1<<3)
31+
#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
3232
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
3333
#define ZEND_OPTIMIZER_PASS_6 (1<<5)
3434
#define ZEND_OPTIMIZER_PASS_7 (1<<6)

0 commit comments

Comments
 (0)