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