1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 | https://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@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 /* pass 1 (Simple local optimizations)
23 * - persistent constant substitution (true, false, null, etc)
24 * - constant casting (ADD expects numbers, CONCAT strings, etc)
25 * - constant expression evaluation
26 * - optimize constant conditional JMPs
27 * - pre-evaluate constant function calls
28 */
29
30 #include "Optimizer/zend_optimizer.h"
31 #include "Optimizer/zend_optimizer_internal.h"
32 #include "zend_API.h"
33 #include "zend_constants.h"
34 #include "zend_execute.h"
35 #include "zend_vm.h"
36
replace_by_const_or_qm_assign(zend_op_array * op_array,zend_op * opline,zval * result)37 static void replace_by_const_or_qm_assign(zend_op_array *op_array, zend_op *opline, zval *result) {
38 if (opline->op1_type == IS_CONST) {
39 literal_dtor(&ZEND_OP1_LITERAL(opline));
40 }
41 if (opline->op2_type == IS_CONST) {
42 literal_dtor(&ZEND_OP2_LITERAL(opline));
43 }
44 if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, result)) {
45 MAKE_NOP(opline);
46 } else {
47 opline->opcode = ZEND_QM_ASSIGN;
48 opline->extended_value = 0;
49 SET_UNUSED(opline->op2);
50 zend_optimizer_update_op1_const(op_array, opline, result);
51 }
52 }
53
zend_optimizer_pass1(zend_op_array * op_array,zend_optimizer_ctx * ctx)54 void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
55 {
56 zend_op *opline = op_array->opcodes;
57 zend_op *end = opline + op_array->last;
58 bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
59 (op_array == &ctx->script->main_op_array) : 0;
60 zval result;
61
62 while (opline < end) {
63 switch (opline->opcode) {
64 case ZEND_CONCAT:
65 case ZEND_FAST_CONCAT:
66 if (opline->op1_type == IS_CONST && Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
67 convert_to_string(&ZEND_OP1_LITERAL(opline));
68 }
69 if (opline->op2_type == IS_CONST && Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
70 convert_to_string(&ZEND_OP2_LITERAL(opline));
71 }
72 ZEND_FALLTHROUGH;
73 case ZEND_ADD:
74 case ZEND_SUB:
75 case ZEND_MUL:
76 case ZEND_DIV:
77 case ZEND_POW:
78 case ZEND_MOD:
79 case ZEND_SL:
80 case ZEND_SR:
81 case ZEND_BW_OR:
82 case ZEND_BW_AND:
83 case ZEND_BW_XOR:
84 case ZEND_IS_EQUAL:
85 case ZEND_IS_NOT_EQUAL:
86 case ZEND_IS_SMALLER:
87 case ZEND_IS_SMALLER_OR_EQUAL:
88 case ZEND_IS_IDENTICAL:
89 case ZEND_IS_NOT_IDENTICAL:
90 case ZEND_BOOL_XOR:
91 case ZEND_SPACESHIP:
92 case ZEND_CASE:
93 case ZEND_CASE_STRICT:
94 if (opline->op1_type == IS_CONST && opline->op2_type == IS_CONST &&
95 zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
96 replace_by_const_or_qm_assign(op_array, opline, &result);
97 }
98 break;
99
100 case ZEND_ASSIGN_OP:
101 if (opline->extended_value == ZEND_CONCAT && opline->op2_type == IS_CONST
102 && Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
103 convert_to_string(&ZEND_OP2_LITERAL(opline));
104 }
105 break;
106
107 case ZEND_CAST:
108 if (opline->op1_type == IS_CONST &&
109 zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
110 replace_by_const_or_qm_assign(op_array, opline, &result);
111 }
112 break;
113
114 case ZEND_BW_NOT:
115 case ZEND_BOOL_NOT:
116 if (opline->op1_type == IS_CONST &&
117 zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
118 replace_by_const_or_qm_assign(op_array, opline, &result);
119 }
120 break;
121
122 case ZEND_FETCH_CONSTANT:
123 if (opline->op2_type == IS_CONST &&
124 Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
125 zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__")) {
126 /* substitute __COMPILER_HALT_OFFSET__ constant */
127 zend_execute_data *orig_execute_data = EG(current_execute_data);
128 zend_execute_data fake_execute_data;
129 zval *offset;
130
131 memset(&fake_execute_data, 0, sizeof(zend_execute_data));
132 fake_execute_data.func = (zend_function*)op_array;
133 EG(current_execute_data) = &fake_execute_data;
134 if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
135
136 literal_dtor(&ZEND_OP2_LITERAL(opline));
137 replace_by_const_or_qm_assign(op_array, opline, offset);
138 }
139 EG(current_execute_data) = orig_execute_data;
140 break;
141 }
142
143 if (opline->op2_type == IS_CONST &&
144 Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
145 /* substitute persistent constants */
146 if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &result, 1)) {
147 if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &result)) {
148 break;
149 }
150 }
151 if (Z_TYPE(result) == IS_CONSTANT_AST) {
152 break;
153 }
154 replace_by_const_or_qm_assign(op_array, opline, &result);
155 }
156 break;
157
158 case ZEND_FETCH_CLASS_CONSTANT: {
159 bool is_prototype;
160 const zend_class_constant *cc = zend_fetch_class_const_info(ctx->script, op_array, opline, &is_prototype);
161 if (!cc || is_prototype) {
162 break;
163 }
164 const zval *c = &cc->value;
165 if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
166 zend_ast *ast = Z_ASTVAL_P(c);
167 if (ast->kind != ZEND_AST_CONSTANT
168 || !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &result, 1)
169 || Z_TYPE(result) == IS_CONSTANT_AST) {
170 break;
171 }
172 } else {
173 ZVAL_COPY_OR_DUP(&result, c);
174 }
175 replace_by_const_or_qm_assign(op_array, opline, &result);
176 break;
177 }
178
179 case ZEND_DO_ICALL: {
180 zend_op *send1_opline = opline - 1;
181 zend_op *send2_opline = NULL;
182 zend_op *init_opline = NULL;
183
184 while (send1_opline->opcode == ZEND_NOP) {
185 send1_opline--;
186 }
187 if (send1_opline->opcode != ZEND_SEND_VAL ||
188 send1_opline->op1_type != IS_CONST) {
189 /* don't collect constants after unknown function call */
190 collect_constants = 0;
191 break;
192 }
193 if (send1_opline->op2.num == 2) {
194 send2_opline = send1_opline;
195 send1_opline--;
196 while (send1_opline->opcode == ZEND_NOP) {
197 send1_opline--;
198 }
199 if (send1_opline->opcode != ZEND_SEND_VAL ||
200 send1_opline->op1_type != IS_CONST) {
201 /* don't collect constants after unknown function call */
202 collect_constants = 0;
203 break;
204 }
205 }
206 init_opline = send1_opline - 1;
207 while (init_opline->opcode == ZEND_NOP) {
208 init_opline--;
209 }
210 if (init_opline->opcode != ZEND_INIT_FCALL ||
211 init_opline->op2_type != IS_CONST ||
212 Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
213 /* don't collect constants after unknown function call */
214 collect_constants = 0;
215 break;
216 }
217
218 /* define("name", scalar); */
219 if (zend_string_equals_literal_ci(Z_STR(ZEND_OP2_LITERAL(init_opline)), "define")) {
220
221 if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING && send2_opline) {
222
223 if (collect_constants) {
224 zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline));
225 }
226
227 if (RESULT_UNUSED(opline) &&
228 !zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
229
230 opline->opcode = ZEND_DECLARE_CONST;
231 opline->op1_type = IS_CONST;
232 opline->op2_type = IS_CONST;
233 opline->result_type = IS_UNUSED;
234 opline->op1.constant = send1_opline->op1.constant;
235 opline->op2.constant = send2_opline->op1.constant;
236 opline->result.num = 0;
237
238 literal_dtor(&ZEND_OP2_LITERAL(init_opline));
239 MAKE_NOP(init_opline);
240 MAKE_NOP(send1_opline);
241 MAKE_NOP(send2_opline);
242 }
243 break;
244 }
245 }
246
247 if (!send2_opline && Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING &&
248 zend_optimizer_eval_special_func_call(&result, Z_STR(ZEND_OP2_LITERAL(init_opline)), Z_STR(ZEND_OP1_LITERAL(send1_opline))) == SUCCESS) {
249 literal_dtor(&ZEND_OP2_LITERAL(init_opline));
250 MAKE_NOP(init_opline);
251 literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
252 MAKE_NOP(send1_opline);
253 replace_by_const_or_qm_assign(op_array, opline, &result);
254 break;
255 }
256
257 /* don't collect constants after any other function call */
258 collect_constants = 0;
259 break;
260 }
261 case ZEND_STRLEN:
262 if (opline->op1_type == IS_CONST &&
263 zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
264 replace_by_const_or_qm_assign(op_array, opline, &result);
265 }
266 break;
267 case ZEND_DEFINED:
268 if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &result, 0)) {
269 break;
270 }
271 ZVAL_TRUE(&result);
272 literal_dtor(&ZEND_OP1_LITERAL(opline));
273 replace_by_const_or_qm_assign(op_array, opline, &result);
274 break;
275 case ZEND_DECLARE_CONST:
276 if (collect_constants &&
277 Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
278 Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_CONSTANT_AST) {
279 zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline));
280 }
281 break;
282
283 case ZEND_JMPZ_EX:
284 case ZEND_JMPNZ_EX:
285 /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
286 in case we know it wouldn't jump */
287 if (opline->op1_type == IS_CONST) {
288 if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
289 if (opline->opcode == ZEND_JMPZ_EX) {
290 opline->opcode = ZEND_QM_ASSIGN;
291 zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
292 ZVAL_TRUE(&ZEND_OP1_LITERAL(opline));
293 opline->op2.num = 0;
294 break;
295 }
296 } else {
297 if (opline->opcode == ZEND_JMPNZ_EX) {
298 opline->opcode = ZEND_QM_ASSIGN;
299 zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
300 ZVAL_FALSE(&ZEND_OP1_LITERAL(opline));
301 opline->op2.num = 0;
302 break;
303 }
304 }
305 }
306 collect_constants = 0;
307 break;
308
309 case ZEND_JMPZ:
310 case ZEND_JMPNZ:
311 if (opline->op1_type == IS_CONST) {
312 int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
313
314 if (opline->opcode == ZEND_JMPZ) {
315 should_jmp = !should_jmp;
316 }
317 literal_dtor(&ZEND_OP1_LITERAL(opline));
318 opline->op1_type = IS_UNUSED;
319 if (should_jmp) {
320 opline->opcode = ZEND_JMP;
321 COPY_NODE(opline->op1, opline->op2);
322 opline->op2.num = 0;
323 } else {
324 MAKE_NOP(opline);
325 break;
326 }
327 }
328 collect_constants = 0;
329 break;
330
331 case ZEND_RETURN:
332 case ZEND_RETURN_BY_REF:
333 case ZEND_GENERATOR_RETURN:
334 case ZEND_EXIT:
335 case ZEND_THROW:
336 case ZEND_MATCH_ERROR:
337 case ZEND_CATCH:
338 case ZEND_FAST_CALL:
339 case ZEND_FAST_RET:
340 case ZEND_JMP:
341 case ZEND_FE_RESET_R:
342 case ZEND_FE_RESET_RW:
343 case ZEND_FE_FETCH_R:
344 case ZEND_FE_FETCH_RW:
345 case ZEND_JMP_SET:
346 case ZEND_COALESCE:
347 case ZEND_ASSERT_CHECK:
348 case ZEND_JMP_NULL:
349 case ZEND_VERIFY_NEVER_TYPE:
350 case ZEND_BIND_INIT_STATIC_OR_JMP:
351 case ZEND_JMP_FRAMELESS:
352 collect_constants = 0;
353 break;
354 }
355 opline++;
356 }
357 }
358