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 #include "Optimizer/zend_optimizer.h"
23 #include "Optimizer/zend_optimizer_internal.h"
24 #include "zend_API.h"
25 #include "zend_constants.h"
26 #include "zend_execute.h"
27 #include "zend_vm.h"
28 #include "zend_cfg.h"
29 #include "zend_func_info.h"
30 #include "zend_call_graph.h"
31 #include "zend_inference.h"
32 #include "zend_dump.h"
33
34 #ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES
35 # define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32
36 #endif
37
38 struct {
39 zend_optimizer_pass_t pass[ZEND_OPTIMIZER_MAX_REGISTERED_PASSES];
40 int last;
41 } zend_optimizer_registered_passes = {{NULL}, 0};
42
zend_optimizer_zval_dtor_wrapper(zval * zvalue)43 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
44 {
45 zval_ptr_dtor_nogc(zvalue);
46 }
47
zend_optimizer_collect_constant(zend_optimizer_ctx * ctx,zval * name,zval * value)48 void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
49 {
50 zval val;
51
52 if (!ctx->constants) {
53 ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
54 zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
55 }
56 ZVAL_COPY(&val, value);
57 zend_hash_add(ctx->constants, Z_STR_P(name), &val);
58 }
59
zend_optimizer_eval_binary_op(zval * result,zend_uchar opcode,zval * op1,zval * op2)60 int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2) /* {{{ */
61 {
62 if (zend_binary_op_produces_error(opcode, op1, op2)) {
63 return FAILURE;
64 }
65
66 binary_op_type binary_op = get_binary_op(opcode);
67 return binary_op(result, op1, op2);
68 }
69 /* }}} */
70
zend_optimizer_eval_unary_op(zval * result,zend_uchar opcode,zval * op1)71 int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1) /* {{{ */
72 {
73 unary_op_type unary_op = get_unary_op(opcode);
74
75 if (unary_op) {
76 if (zend_unary_op_produces_error(opcode, op1)) {
77 return FAILURE;
78 }
79 return unary_op(result, op1);
80 } else { /* ZEND_BOOL */
81 ZVAL_BOOL(result, zend_is_true(op1));
82 return SUCCESS;
83 }
84 }
85 /* }}} */
86
zend_optimizer_eval_cast(zval * result,uint32_t type,zval * op1)87 int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1) /* {{{ */
88 {
89 switch (type) {
90 case IS_NULL:
91 ZVAL_NULL(result);
92 return SUCCESS;
93 case _IS_BOOL:
94 ZVAL_BOOL(result, zval_is_true(op1));
95 return SUCCESS;
96 case IS_LONG:
97 ZVAL_LONG(result, zval_get_long(op1));
98 return SUCCESS;
99 case IS_DOUBLE:
100 ZVAL_DOUBLE(result, zval_get_double(op1));
101 return SUCCESS;
102 case IS_STRING:
103 /* Conversion from double to string takes into account run-time
104 'precision' setting and cannot be evaluated at compile-time */
105 if (Z_TYPE_P(op1) != IS_ARRAY && Z_TYPE_P(op1) != IS_DOUBLE) {
106 ZVAL_STR(result, zval_get_string(op1));
107 return SUCCESS;
108 }
109 break;
110 case IS_ARRAY:
111 ZVAL_COPY(result, op1);
112 convert_to_array(result);
113 return SUCCESS;
114 }
115 return FAILURE;
116 }
117 /* }}} */
118
zend_optimizer_eval_strlen(zval * result,zval * op1)119 int zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */
120 {
121 if (Z_TYPE_P(op1) != IS_STRING) {
122 return FAILURE;
123 }
124 ZVAL_LONG(result, Z_STRLEN_P(op1));
125 return SUCCESS;
126 }
127 /* }}} */
128
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)129 int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
130 {
131 zval *val;
132
133 if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
134 ZVAL_COPY(value, val);
135 return 1;
136 }
137 return 0;
138 }
139
zend_optimizer_add_literal(zend_op_array * op_array,zval * zv)140 int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
141 {
142 int i = op_array->last_literal;
143 op_array->last_literal++;
144 op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
145 ZVAL_COPY_VALUE(&op_array->literals[i], zv);
146 Z_EXTRA(op_array->literals[i]) = 0;
147 return i;
148 }
149
zend_optimizer_add_literal_string(zend_op_array * op_array,zend_string * str)150 static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
151 zval zv;
152 ZVAL_STR(&zv, str);
153 zend_string_hash_val(str);
154 return zend_optimizer_add_literal(op_array, &zv);
155 }
156
drop_leading_backslash(zval * val)157 static inline void drop_leading_backslash(zval *val) {
158 if (Z_STRVAL_P(val)[0] == '\\') {
159 zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
160 zval_ptr_dtor_nogc(val);
161 ZVAL_STR(val, str);
162 }
163 }
164
alloc_cache_slots(zend_op_array * op_array,uint32_t num)165 static inline uint32_t alloc_cache_slots(zend_op_array *op_array, uint32_t num) {
166 uint32_t ret = op_array->cache_size;
167 op_array->cache_size += num * sizeof(void *);
168 return ret;
169 }
170
171 #define REQUIRES_STRING(val) do { \
172 if (Z_TYPE_P(val) != IS_STRING) { \
173 return 0; \
174 } \
175 } while (0)
176
177 #define TO_STRING_NOWARN(val) do { \
178 if (Z_TYPE_P(val) >= IS_ARRAY) { \
179 return 0; \
180 } \
181 convert_to_string(val); \
182 } while (0)
183
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)184 int zend_optimizer_update_op1_const(zend_op_array *op_array,
185 zend_op *opline,
186 zval *val)
187 {
188 switch (opline->opcode) {
189 case ZEND_OP_DATA:
190 switch ((opline-1)->opcode) {
191 case ZEND_ASSIGN_OBJ_REF:
192 case ZEND_ASSIGN_STATIC_PROP_REF:
193 return 0;
194 }
195 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
196 break;
197 case ZEND_FREE:
198 case ZEND_CHECK_VAR:
199 MAKE_NOP(opline);
200 zval_ptr_dtor_nogc(val);
201 return 1;
202 case ZEND_SEND_VAR_EX:
203 case ZEND_SEND_FUNC_ARG:
204 case ZEND_FETCH_DIM_W:
205 case ZEND_FETCH_DIM_RW:
206 case ZEND_FETCH_DIM_FUNC_ARG:
207 case ZEND_FETCH_DIM_UNSET:
208 case ZEND_FETCH_LIST_W:
209 case ZEND_ASSIGN_DIM:
210 case ZEND_RETURN_BY_REF:
211 case ZEND_INSTANCEOF:
212 case ZEND_MAKE_REF:
213 return 0;
214 case ZEND_CATCH:
215 REQUIRES_STRING(val);
216 drop_leading_backslash(val);
217 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
218 opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_LAST_CATCH);
219 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
220 break;
221 case ZEND_DEFINED:
222 REQUIRES_STRING(val);
223 drop_leading_backslash(val);
224 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
225 opline->extended_value = alloc_cache_slots(op_array, 1);
226 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
227 break;
228 case ZEND_NEW:
229 REQUIRES_STRING(val);
230 drop_leading_backslash(val);
231 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
232 opline->op2.num = alloc_cache_slots(op_array, 1);
233 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
234 break;
235 case ZEND_INIT_STATIC_METHOD_CALL:
236 REQUIRES_STRING(val);
237 drop_leading_backslash(val);
238 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
239 if (opline->op2_type != IS_CONST) {
240 opline->result.num = alloc_cache_slots(op_array, 1);
241 }
242 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
243 break;
244 case ZEND_FETCH_CLASS_CONSTANT:
245 REQUIRES_STRING(val);
246 drop_leading_backslash(val);
247 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
248 if (opline->op2_type != IS_CONST) {
249 opline->extended_value = alloc_cache_slots(op_array, 1);
250 }
251 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
252 break;
253 case ZEND_ASSIGN_OP:
254 case ZEND_ASSIGN_DIM_OP:
255 case ZEND_ASSIGN_OBJ_OP:
256 break;
257 case ZEND_ASSIGN_STATIC_PROP_OP:
258 case ZEND_ASSIGN_STATIC_PROP:
259 case ZEND_ASSIGN_STATIC_PROP_REF:
260 case ZEND_FETCH_STATIC_PROP_R:
261 case ZEND_FETCH_STATIC_PROP_W:
262 case ZEND_FETCH_STATIC_PROP_RW:
263 case ZEND_FETCH_STATIC_PROP_IS:
264 case ZEND_FETCH_STATIC_PROP_UNSET:
265 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
266 case ZEND_UNSET_STATIC_PROP:
267 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
268 case ZEND_PRE_INC_STATIC_PROP:
269 case ZEND_PRE_DEC_STATIC_PROP:
270 case ZEND_POST_INC_STATIC_PROP:
271 case ZEND_POST_DEC_STATIC_PROP:
272 TO_STRING_NOWARN(val);
273 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
274 if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) {
275 op_array->cache_size += sizeof(void *);
276 } else {
277 opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
278 }
279 break;
280 case ZEND_SEND_VAR:
281 opline->opcode = ZEND_SEND_VAL;
282 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
283 break;
284 case ZEND_CASE:
285 opline->opcode = ZEND_IS_EQUAL;
286 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
287 break;
288 case ZEND_CASE_STRICT:
289 opline->opcode = ZEND_IS_IDENTICAL;
290 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
291 break;
292 case ZEND_SEPARATE:
293 case ZEND_SEND_VAR_NO_REF:
294 case ZEND_SEND_VAR_NO_REF_EX:
295 return 0;
296 case ZEND_VERIFY_RETURN_TYPE:
297 /* This would require a non-local change.
298 * zend_optimizer_replace_by_const() supports this. */
299 return 0;
300 case ZEND_COPY_TMP:
301 case ZEND_FETCH_CLASS_NAME:
302 return 0;
303 case ZEND_ECHO:
304 {
305 zval zv;
306 if (Z_TYPE_P(val) != IS_STRING && zend_optimizer_eval_cast(&zv, IS_STRING, val) == SUCCESS) {
307 zval_ptr_dtor_nogc(val);
308 val = &zv;
309 }
310 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
311 if (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0) {
312 MAKE_NOP(opline);
313 }
314 /* TODO: In a subsequent pass, *after* this step and compacting nops, combine consecutive ZEND_ECHOs using the block information from ssa->cfg */
315 /* (e.g. for ext/opcache/tests/opt/sccp_010.phpt) */
316 break;
317 }
318 case ZEND_CONCAT:
319 case ZEND_FAST_CONCAT:
320 case ZEND_FETCH_R:
321 case ZEND_FETCH_W:
322 case ZEND_FETCH_RW:
323 case ZEND_FETCH_IS:
324 case ZEND_FETCH_UNSET:
325 case ZEND_FETCH_FUNC_ARG:
326 case ZEND_ISSET_ISEMPTY_VAR:
327 case ZEND_UNSET_VAR:
328 TO_STRING_NOWARN(val);
329 if (opline->opcode == ZEND_CONCAT && opline->op2_type == IS_CONST) {
330 opline->opcode = ZEND_FAST_CONCAT;
331 }
332 ZEND_FALLTHROUGH;
333 default:
334 opline->op1.constant = zend_optimizer_add_literal(op_array, val);
335 break;
336 }
337
338 opline->op1_type = IS_CONST;
339 if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
340 zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
341 }
342 return 1;
343 }
344
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)345 int zend_optimizer_update_op2_const(zend_op_array *op_array,
346 zend_op *opline,
347 zval *val)
348 {
349 zval tmp;
350
351 switch (opline->opcode) {
352 case ZEND_ASSIGN_REF:
353 case ZEND_FAST_CALL:
354 return 0;
355 case ZEND_FETCH_CLASS:
356 if ((opline + 1)->opcode == ZEND_INSTANCEOF &&
357 (opline + 1)->op2.var == opline->result.var) {
358 return 0;
359 }
360 ZEND_FALLTHROUGH;
361 case ZEND_INSTANCEOF:
362 REQUIRES_STRING(val);
363 drop_leading_backslash(val);
364 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
365 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
366 opline->extended_value = alloc_cache_slots(op_array, 1);
367 break;
368 case ZEND_INIT_FCALL_BY_NAME:
369 REQUIRES_STRING(val);
370 drop_leading_backslash(val);
371 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
372 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
373 opline->result.num = alloc_cache_slots(op_array, 1);
374 break;
375 case ZEND_ASSIGN_STATIC_PROP:
376 case ZEND_ASSIGN_STATIC_PROP_REF:
377 case ZEND_FETCH_STATIC_PROP_R:
378 case ZEND_FETCH_STATIC_PROP_W:
379 case ZEND_FETCH_STATIC_PROP_RW:
380 case ZEND_FETCH_STATIC_PROP_IS:
381 case ZEND_FETCH_STATIC_PROP_UNSET:
382 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
383 case ZEND_UNSET_STATIC_PROP:
384 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
385 case ZEND_PRE_INC_STATIC_PROP:
386 case ZEND_PRE_DEC_STATIC_PROP:
387 case ZEND_POST_INC_STATIC_PROP:
388 case ZEND_POST_DEC_STATIC_PROP:
389 case ZEND_ASSIGN_STATIC_PROP_OP:
390 REQUIRES_STRING(val);
391 drop_leading_backslash(val);
392 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
393 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
394 if (opline->op1_type != IS_CONST) {
395 opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS));
396 }
397 break;
398 case ZEND_INIT_FCALL:
399 REQUIRES_STRING(val);
400 if (Z_REFCOUNT_P(val) == 1) {
401 zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
402 } else {
403 ZVAL_STR(&tmp, zend_string_tolower(Z_STR_P(val)));
404 zval_ptr_dtor_nogc(val);
405 val = &tmp;
406 }
407 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
408 opline->result.num = alloc_cache_slots(op_array, 1);
409 break;
410 case ZEND_INIT_DYNAMIC_CALL:
411 if (Z_TYPE_P(val) == IS_STRING) {
412 if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
413 return 0;
414 }
415
416 if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
417 /* Dynamic call to various special functions must stay dynamic,
418 * otherwise would drop a warning */
419 return 0;
420 }
421
422 opline->opcode = ZEND_INIT_FCALL_BY_NAME;
423 drop_leading_backslash(val);
424 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
425 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
426 opline->result.num = alloc_cache_slots(op_array, 1);
427 } else {
428 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
429 }
430 break;
431 case ZEND_INIT_METHOD_CALL:
432 REQUIRES_STRING(val);
433 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
434 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
435 opline->result.num = alloc_cache_slots(op_array, 2);
436 break;
437 case ZEND_INIT_STATIC_METHOD_CALL:
438 REQUIRES_STRING(val);
439 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
440 zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
441 if (opline->op1_type != IS_CONST) {
442 opline->result.num = alloc_cache_slots(op_array, 2);
443 }
444 break;
445 case ZEND_ASSIGN_OBJ:
446 case ZEND_ASSIGN_OBJ_REF:
447 case ZEND_FETCH_OBJ_R:
448 case ZEND_FETCH_OBJ_W:
449 case ZEND_FETCH_OBJ_RW:
450 case ZEND_FETCH_OBJ_IS:
451 case ZEND_FETCH_OBJ_UNSET:
452 case ZEND_FETCH_OBJ_FUNC_ARG:
453 case ZEND_UNSET_OBJ:
454 case ZEND_PRE_INC_OBJ:
455 case ZEND_PRE_DEC_OBJ:
456 case ZEND_POST_INC_OBJ:
457 case ZEND_POST_DEC_OBJ:
458 TO_STRING_NOWARN(val);
459 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
460 opline->extended_value = alloc_cache_slots(op_array, 3);
461 break;
462 case ZEND_ASSIGN_OBJ_OP:
463 TO_STRING_NOWARN(val);
464 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
465 ZEND_ASSERT((opline + 1)->opcode == ZEND_OP_DATA);
466 (opline + 1)->extended_value = alloc_cache_slots(op_array, 3);
467 break;
468 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
469 TO_STRING_NOWARN(val);
470 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
471 opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY);
472 break;
473 case ZEND_ASSIGN_DIM_OP:
474 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
475 case ZEND_ASSIGN_DIM:
476 case ZEND_UNSET_DIM:
477 case ZEND_FETCH_DIM_R:
478 case ZEND_FETCH_DIM_W:
479 case ZEND_FETCH_DIM_RW:
480 case ZEND_FETCH_DIM_IS:
481 case ZEND_FETCH_DIM_FUNC_ARG:
482 case ZEND_FETCH_DIM_UNSET:
483 case ZEND_FETCH_LIST_R:
484 case ZEND_FETCH_LIST_W:
485 if (Z_TYPE_P(val) == IS_STRING) {
486 zend_ulong index;
487
488 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
489 ZVAL_LONG(&tmp, index);
490 opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
491 zend_string_hash_val(Z_STR_P(val));
492 zend_optimizer_add_literal(op_array, val);
493 Z_EXTRA(op_array->literals[opline->op2.constant]) = ZEND_EXTRA_VALUE;
494 break;
495 }
496 }
497 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
498 break;
499 case ZEND_ADD_ARRAY_ELEMENT:
500 case ZEND_INIT_ARRAY:
501 if (Z_TYPE_P(val) == IS_STRING) {
502 zend_ulong index;
503 if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
504 zval_ptr_dtor_nogc(val);
505 ZVAL_LONG(val, index);
506 }
507 }
508 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
509 break;
510 case ZEND_ROPE_INIT:
511 case ZEND_ROPE_ADD:
512 case ZEND_ROPE_END:
513 case ZEND_CONCAT:
514 case ZEND_FAST_CONCAT:
515 TO_STRING_NOWARN(val);
516 if (opline->opcode == ZEND_CONCAT && opline->op1_type == IS_CONST) {
517 opline->opcode = ZEND_FAST_CONCAT;
518 }
519 ZEND_FALLTHROUGH;
520 default:
521 opline->op2.constant = zend_optimizer_add_literal(op_array, val);
522 break;
523 }
524
525 opline->op2_type = IS_CONST;
526 if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
527 zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
528 }
529 return 1;
530 }
531
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)532 int zend_optimizer_replace_by_const(zend_op_array *op_array,
533 zend_op *opline,
534 zend_uchar type,
535 uint32_t var,
536 zval *val)
537 {
538 zend_op *end = op_array->opcodes + op_array->last;
539
540 while (opline < end) {
541 if (opline->op1_type == type &&
542 opline->op1.var == var) {
543 switch (opline->opcode) {
544 case ZEND_FETCH_DIM_W:
545 case ZEND_FETCH_DIM_RW:
546 case ZEND_FETCH_DIM_FUNC_ARG:
547 case ZEND_FETCH_DIM_UNSET:
548 case ZEND_FETCH_LIST_W:
549 case ZEND_ASSIGN_DIM:
550 case ZEND_SEPARATE:
551 case ZEND_RETURN_BY_REF:
552 return 0;
553 case ZEND_SEND_VAR:
554 opline->extended_value = 0;
555 opline->opcode = ZEND_SEND_VAL;
556 break;
557 case ZEND_SEND_VAR_EX:
558 case ZEND_SEND_FUNC_ARG:
559 opline->extended_value = 0;
560 opline->opcode = ZEND_SEND_VAL_EX;
561 break;
562 case ZEND_SEND_VAR_NO_REF:
563 return 0;
564 case ZEND_SEND_VAR_NO_REF_EX:
565 opline->opcode = ZEND_SEND_VAL;
566 break;
567 case ZEND_SEND_USER:
568 opline->opcode = ZEND_SEND_VAL_EX;
569 break;
570 /* In most cases IS_TMP_VAR operand may be used only once.
571 * The operands are usually destroyed by the opcode handler.
572 * However, there are some exception which keep the operand alive. In that case
573 * we want to try to replace all uses of the temporary.
574 */
575 case ZEND_FETCH_LIST_R:
576 case ZEND_CASE:
577 case ZEND_CASE_STRICT:
578 case ZEND_SWITCH_LONG:
579 case ZEND_SWITCH_STRING:
580 case ZEND_MATCH:
581 case ZEND_JMP_NULL: {
582 zend_op *end = op_array->opcodes + op_array->last;
583 while (opline < end) {
584 if (opline->op1_type == type && opline->op1.var == var) {
585 /* If this opcode doesn't keep the operand alive, we're done. Check
586 * this early, because op replacement may modify the opline. */
587 bool is_last = opline->opcode != ZEND_FETCH_LIST_R
588 && opline->opcode != ZEND_CASE
589 && opline->opcode != ZEND_CASE_STRICT
590 && opline->opcode != ZEND_SWITCH_LONG
591 && opline->opcode != ZEND_SWITCH_STRING
592 && opline->opcode != ZEND_MATCH
593 && opline->opcode != ZEND_JMP_NULL
594 && (opline->opcode != ZEND_FREE
595 || opline->extended_value != ZEND_FREE_ON_RETURN);
596
597 Z_TRY_ADDREF_P(val);
598 if (!zend_optimizer_update_op1_const(op_array, opline, val)) {
599 zval_ptr_dtor(val);
600 return 0;
601 }
602 if (is_last) {
603 break;
604 }
605 }
606 opline++;
607 }
608 zval_ptr_dtor_nogc(val);
609 return 1;
610 }
611 case ZEND_VERIFY_RETURN_TYPE: {
612 zend_arg_info *ret_info = op_array->arg_info - 1;
613 if (!ZEND_TYPE_CONTAINS_CODE(ret_info->type, Z_TYPE_P(val))
614 || (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
615 return 0;
616 }
617 MAKE_NOP(opline);
618
619 /* zend_handle_loops_and_finally may inserts other oplines */
620 do {
621 ++opline;
622 } while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
623 ZEND_ASSERT(opline->op1.var == var);
624
625 break;
626 }
627 default:
628 break;
629 }
630 return zend_optimizer_update_op1_const(op_array, opline, val);
631 }
632
633 if (opline->op2_type == type &&
634 opline->op2.var == var) {
635 return zend_optimizer_update_op2_const(op_array, opline, val);
636 }
637 opline++;
638 }
639
640 return 1;
641 }
642
643 /* Update jump offsets after a jump was migrated to another opline */
zend_optimizer_migrate_jump(zend_op_array * op_array,zend_op * new_opline,zend_op * opline)644 void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline) {
645 switch (new_opline->opcode) {
646 case ZEND_JMP:
647 case ZEND_FAST_CALL:
648 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op1, ZEND_OP1_JMP_ADDR(opline));
649 break;
650 case ZEND_JMPZNZ:
651 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
652 ZEND_FALLTHROUGH;
653 case ZEND_JMPZ:
654 case ZEND_JMPNZ:
655 case ZEND_JMPZ_EX:
656 case ZEND_JMPNZ_EX:
657 case ZEND_FE_RESET_R:
658 case ZEND_FE_RESET_RW:
659 case ZEND_JMP_SET:
660 case ZEND_COALESCE:
661 case ZEND_ASSERT_CHECK:
662 case ZEND_JMP_NULL:
663 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
664 break;
665 case ZEND_FE_FETCH_R:
666 case ZEND_FE_FETCH_RW:
667 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
668 break;
669 case ZEND_CATCH:
670 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
671 ZEND_SET_OP_JMP_ADDR(new_opline, new_opline->op2, ZEND_OP2_JMP_ADDR(opline));
672 }
673 break;
674 case ZEND_SWITCH_LONG:
675 case ZEND_SWITCH_STRING:
676 case ZEND_MATCH:
677 {
678 HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
679 zval *zv;
680 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
681 Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
682 } ZEND_HASH_FOREACH_END();
683 new_opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, new_opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
684 break;
685 }
686 }
687 }
688
689 /* Shift jump offsets based on shiftlist */
zend_optimizer_shift_jump(zend_op_array * op_array,zend_op * opline,uint32_t * shiftlist)690 void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist) {
691 switch (opline->opcode) {
692 case ZEND_JMP:
693 case ZEND_FAST_CALL:
694 ZEND_SET_OP_JMP_ADDR(opline, opline->op1, ZEND_OP1_JMP_ADDR(opline) - shiftlist[ZEND_OP1_JMP_ADDR(opline) - op_array->opcodes]);
695 break;
696 case ZEND_JMPZNZ:
697 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
698 ZEND_FALLTHROUGH;
699 case ZEND_JMPZ:
700 case ZEND_JMPNZ:
701 case ZEND_JMPZ_EX:
702 case ZEND_JMPNZ_EX:
703 case ZEND_FE_RESET_R:
704 case ZEND_FE_RESET_RW:
705 case ZEND_JMP_SET:
706 case ZEND_COALESCE:
707 case ZEND_ASSERT_CHECK:
708 case ZEND_JMP_NULL:
709 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
710 break;
711 case ZEND_CATCH:
712 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
713 ZEND_SET_OP_JMP_ADDR(opline, opline->op2, ZEND_OP2_JMP_ADDR(opline) - shiftlist[ZEND_OP2_JMP_ADDR(opline) - op_array->opcodes]);
714 }
715 break;
716 case ZEND_FE_FETCH_R:
717 case ZEND_FE_FETCH_RW:
718 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
719 break;
720 case ZEND_SWITCH_LONG:
721 case ZEND_SWITCH_STRING:
722 case ZEND_MATCH:
723 {
724 HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
725 zval *zv;
726 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
727 Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))]);
728 } ZEND_HASH_FOREACH_END();
729 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) - shiftlist[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
730 break;
731 }
732 }
733 }
734
zend_optimizer_get_class_entry(const zend_script * script,zend_string * lcname)735 zend_class_entry *zend_optimizer_get_class_entry(const zend_script *script, zend_string *lcname) {
736 zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
737 if (ce) {
738 return ce;
739 }
740
741 ce = zend_hash_find_ptr(CG(class_table), lcname);
742 if (ce && ce->type == ZEND_INTERNAL_CLASS) {
743 return ce;
744 }
745
746 return NULL;
747 }
748
get_class_entry_from_op1(zend_script * script,zend_op_array * op_array,zend_op * opline)749 static zend_class_entry *get_class_entry_from_op1(
750 zend_script *script, zend_op_array *op_array, zend_op *opline) {
751 if (opline->op1_type == IS_CONST) {
752 zval *op1 = CRT_CONSTANT(opline->op1);
753 if (Z_TYPE_P(op1) == IS_STRING) {
754 return zend_optimizer_get_class_entry(script, Z_STR_P(op1 + 1));
755 }
756 } else if (opline->op1_type == IS_UNUSED && op_array->scope
757 && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
758 && (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
759 return op_array->scope;
760 }
761 return NULL;
762 }
763
zend_optimizer_get_called_func(zend_script * script,zend_op_array * op_array,zend_op * opline,bool * is_prototype)764 zend_function *zend_optimizer_get_called_func(
765 zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype)
766 {
767 *is_prototype = 0;
768 switch (opline->opcode) {
769 case ZEND_INIT_FCALL:
770 {
771 zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2));
772 zend_function *func;
773 if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
774 return func;
775 } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
776 if (func->type == ZEND_INTERNAL_FUNCTION) {
777 return func;
778 } else if (func->type == ZEND_USER_FUNCTION &&
779 func->op_array.filename &&
780 func->op_array.filename == op_array->filename) {
781 return func;
782 }
783 }
784 break;
785 }
786 case ZEND_INIT_FCALL_BY_NAME:
787 case ZEND_INIT_NS_FCALL_BY_NAME:
788 if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
789 zval *function_name = CRT_CONSTANT(opline->op2) + 1;
790 zend_function *func;
791 if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
792 return func;
793 } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
794 if (func->type == ZEND_INTERNAL_FUNCTION) {
795 return func;
796 } else if (func->type == ZEND_USER_FUNCTION &&
797 func->op_array.filename &&
798 func->op_array.filename == op_array->filename) {
799 return func;
800 }
801 }
802 }
803 break;
804 case ZEND_INIT_STATIC_METHOD_CALL:
805 if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) {
806 zend_class_entry *ce = get_class_entry_from_op1(
807 script, op_array, opline);
808 if (ce) {
809 zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
810 zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name);
811 if (fbc) {
812 bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0;
813 bool same_scope = fbc->common.scope == op_array->scope;
814 if (is_public || same_scope) {
815 return fbc;
816 }
817 }
818 }
819 }
820 break;
821 case ZEND_INIT_METHOD_CALL:
822 if (opline->op1_type == IS_UNUSED
823 && opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING
824 && op_array->scope
825 && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)
826 && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
827 zend_string *method_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1);
828 zend_function *fbc = zend_hash_find_ptr(
829 &op_array->scope->function_table, method_name);
830 if (fbc) {
831 bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
832 bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
833 bool same_scope = fbc->common.scope == op_array->scope;
834 if (is_private) {
835 /* Only use private method if in the same scope. We can't even use it
836 * as a prototype, as it may be overridden with changed signature. */
837 return same_scope ? fbc : NULL;
838 }
839 /* If the method is non-final, it may be overridden,
840 * but only with a compatible method signature. */
841 *is_prototype = !is_final;
842 return fbc;
843 }
844 }
845 break;
846 case ZEND_NEW:
847 {
848 zend_class_entry *ce = get_class_entry_from_op1(
849 script, op_array, opline);
850 if (ce && ce->type == ZEND_USER_CLASS) {
851 return ce->constructor;
852 }
853 break;
854 }
855 }
856 return NULL;
857 }
858
zend_optimizer_classify_function(zend_string * name,uint32_t num_args)859 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
860 if (zend_string_equals_literal(name, "extract")) {
861 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
862 } else if (zend_string_equals_literal(name, "compact")) {
863 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
864 } else if (zend_string_equals_literal(name, "get_defined_vars")) {
865 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
866 } else if (zend_string_equals_literal(name, "db2_execute")) {
867 return ZEND_FUNC_INDIRECT_VAR_ACCESS;
868 } else if (zend_string_equals_literal(name, "func_num_args")) {
869 return ZEND_FUNC_VARARG;
870 } else if (zend_string_equals_literal(name, "func_get_arg")) {
871 return ZEND_FUNC_VARARG;
872 } else if (zend_string_equals_literal(name, "func_get_args")) {
873 return ZEND_FUNC_VARARG;
874 } else {
875 return 0;
876 }
877 }
878
zend_optimizer_get_loop_var_def(const zend_op_array * op_array,zend_op * free_opline)879 zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline) {
880 uint32_t var = free_opline->op1.var;
881 ZEND_ASSERT(zend_optimizer_is_loop_var_free(free_opline));
882
883 while (--free_opline >= op_array->opcodes) {
884 if ((free_opline->result_type & (IS_TMP_VAR|IS_VAR)) && free_opline->result.var == var) {
885 return free_opline;
886 }
887 }
888 return NULL;
889 }
890
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)891 static void zend_optimize(zend_op_array *op_array,
892 zend_optimizer_ctx *ctx)
893 {
894 if (op_array->type == ZEND_EVAL_CODE) {
895 return;
896 }
897
898 if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
899 zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL);
900 }
901
902 /* pass 1 (Simple local optimizations)
903 * - persistent constant substitution (true, false, null, etc)
904 * - constant casting (ADD expects numbers, CONCAT strings, etc)
905 * - constant expression evaluation
906 * - optimize constant conditional JMPs
907 * - pre-evaluate constant function calls
908 * - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
909 */
910 if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
911 zend_optimizer_pass1(op_array, ctx);
912 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
913 zend_dump_op_array(op_array, 0, "after pass 1", NULL);
914 }
915 }
916
917 /* pass 3: (Jump optimization)
918 * - optimize series of JMPs
919 */
920 if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
921 zend_optimizer_pass3(op_array, ctx);
922 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
923 zend_dump_op_array(op_array, 0, "after pass 3", NULL);
924 }
925 }
926
927 /* pass 4:
928 * - INIT_FCALL_BY_NAME -> DO_FCALL
929 */
930 if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
931 zend_optimize_func_calls(op_array, ctx);
932 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
933 zend_dump_op_array(op_array, 0, "after pass 4", NULL);
934 }
935 }
936
937 /* pass 5:
938 * - CFG optimization
939 */
940 if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
941 zend_optimize_cfg(op_array, ctx);
942 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
943 zend_dump_op_array(op_array, 0, "after pass 5", NULL);
944 }
945 }
946
947 /* pass 6:
948 * - DFA optimization
949 */
950 if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
951 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
952 zend_optimize_dfa(op_array, ctx);
953 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
954 zend_dump_op_array(op_array, 0, "after pass 6", NULL);
955 }
956 }
957
958 /* pass 9:
959 * - Optimize temp variables usage
960 */
961 if ((ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) &&
962 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
963 zend_optimize_temporary_variables(op_array, ctx);
964 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
965 zend_dump_op_array(op_array, 0, "after pass 9", NULL);
966 }
967 }
968
969 /* pass 10:
970 * - remove NOPs
971 */
972 if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
973 zend_optimizer_nop_removal(op_array, ctx);
974 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
975 zend_dump_op_array(op_array, 0, "after pass 10", NULL);
976 }
977 }
978
979 /* pass 11:
980 * - Compact literals table
981 */
982 if ((ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) &&
983 (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
984 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
985 zend_optimizer_compact_literals(op_array, ctx);
986 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
987 zend_dump_op_array(op_array, 0, "after pass 11", NULL);
988 }
989 }
990
991 if ((ZEND_OPTIMIZER_PASS_13 & ctx->optimization_level) &&
992 (!(ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) ||
993 !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level))) {
994 zend_optimizer_compact_vars(op_array);
995 if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_13) {
996 zend_dump_op_array(op_array, 0, "after pass 13", NULL);
997 }
998 }
999
1000 if (ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level) {
1001 return;
1002 }
1003
1004 if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
1005 zend_dump_op_array(op_array, 0, "after optimizer", NULL);
1006 }
1007 }
1008
zend_revert_pass_two(zend_op_array * op_array)1009 static void zend_revert_pass_two(zend_op_array *op_array)
1010 {
1011 zend_op *opline, *end;
1012
1013 ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0);
1014
1015 opline = op_array->opcodes;
1016 end = opline + op_array->last;
1017 while (opline < end) {
1018 if (opline->op1_type == IS_CONST) {
1019 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op1);
1020 }
1021 if (opline->op2_type == IS_CONST) {
1022 ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline, opline->op2);
1023 }
1024 /* reset smart branch flags IS_SMART_BRANCH_JMP[N]Z */
1025 opline->result_type &= (IS_TMP_VAR|IS_VAR|IS_CV|IS_CONST);
1026 opline++;
1027 }
1028 #if !ZEND_USE_ABS_CONST_ADDR
1029 if (op_array->literals) {
1030 zval *literals = emalloc(sizeof(zval) * op_array->last_literal);
1031 memcpy(literals, op_array->literals, sizeof(zval) * op_array->last_literal);
1032 op_array->literals = literals;
1033 }
1034 #endif
1035
1036 op_array->fn_flags &= ~ZEND_ACC_DONE_PASS_TWO;
1037 }
1038
zend_redo_pass_two(zend_op_array * op_array)1039 static void zend_redo_pass_two(zend_op_array *op_array)
1040 {
1041 zend_op *opline, *end;
1042 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1043 zend_op *old_opcodes = op_array->opcodes;
1044 #endif
1045
1046 ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0);
1047
1048 #if !ZEND_USE_ABS_CONST_ADDR
1049 if (op_array->last_literal) {
1050 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1051 ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1052 sizeof(zval) * op_array->last_literal);
1053 memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1054 op_array->literals, sizeof(zval) * op_array->last_literal);
1055 efree(op_array->literals);
1056 op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1057 } else {
1058 if (op_array->literals) {
1059 efree(op_array->literals);
1060 }
1061 op_array->literals = NULL;
1062 }
1063 #endif
1064
1065 opline = op_array->opcodes;
1066 end = opline + op_array->last;
1067 while (opline < end) {
1068 if (opline->op1_type == IS_CONST) {
1069 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1070 }
1071 if (opline->op2_type == IS_CONST) {
1072 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1073 }
1074 /* fix jumps to point to new array */
1075 switch (opline->opcode) {
1076 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1077 case ZEND_JMP:
1078 case ZEND_FAST_CALL:
1079 opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
1080 break;
1081 case ZEND_JMPZNZ:
1082 /* relative extended_value don't have to be changed */
1083 /* break omitted intentionally */
1084 case ZEND_JMPZ:
1085 case ZEND_JMPNZ:
1086 case ZEND_JMPZ_EX:
1087 case ZEND_JMPNZ_EX:
1088 case ZEND_JMP_SET:
1089 case ZEND_COALESCE:
1090 case ZEND_FE_RESET_R:
1091 case ZEND_FE_RESET_RW:
1092 case ZEND_ASSERT_CHECK:
1093 case ZEND_JMP_NULL:
1094 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1095 break;
1096 case ZEND_CATCH:
1097 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1098 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1099 }
1100 break;
1101 case ZEND_FE_FETCH_R:
1102 case ZEND_FE_FETCH_RW:
1103 case ZEND_SWITCH_LONG:
1104 case ZEND_SWITCH_STRING:
1105 case ZEND_MATCH:
1106 /* relative extended_value don't have to be changed */
1107 break;
1108 #endif
1109 case ZEND_IS_IDENTICAL:
1110 case ZEND_IS_NOT_IDENTICAL:
1111 case ZEND_IS_EQUAL:
1112 case ZEND_IS_NOT_EQUAL:
1113 case ZEND_IS_SMALLER:
1114 case ZEND_IS_SMALLER_OR_EQUAL:
1115 case ZEND_CASE:
1116 case ZEND_CASE_STRICT:
1117 case ZEND_ISSET_ISEMPTY_CV:
1118 case ZEND_ISSET_ISEMPTY_VAR:
1119 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
1120 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
1121 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
1122 case ZEND_INSTANCEOF:
1123 case ZEND_TYPE_CHECK:
1124 case ZEND_DEFINED:
1125 case ZEND_IN_ARRAY:
1126 case ZEND_ARRAY_KEY_EXISTS:
1127 if (opline->result_type & IS_TMP_VAR) {
1128 /* reinitialize result_type of smart branch instructions */
1129 if (opline + 1 < end) {
1130 if ((opline+1)->opcode == ZEND_JMPZ
1131 && (opline+1)->op1_type == IS_TMP_VAR
1132 && (opline+1)->op1.var == opline->result.var) {
1133 opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR;
1134 } else if ((opline+1)->opcode == ZEND_JMPNZ
1135 && (opline+1)->op1_type == IS_TMP_VAR
1136 && (opline+1)->op1.var == opline->result.var) {
1137 opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR;
1138 }
1139 }
1140 }
1141 break;
1142 }
1143 ZEND_VM_SET_OPCODE_HANDLER(opline);
1144 opline++;
1145 }
1146
1147 op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
1148 }
1149
zend_redo_pass_two_ex(zend_op_array * op_array,zend_ssa * ssa)1150 static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
1151 {
1152 zend_op *opline, *end;
1153 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1154 zend_op *old_opcodes = op_array->opcodes;
1155 #endif
1156
1157 ZEND_ASSERT((op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) == 0);
1158
1159 #if !ZEND_USE_ABS_CONST_ADDR
1160 if (op_array->last_literal) {
1161 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1162 ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1163 sizeof(zval) * op_array->last_literal);
1164 memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1165 op_array->literals, sizeof(zval) * op_array->last_literal);
1166 efree(op_array->literals);
1167 op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1168 } else {
1169 if (op_array->literals) {
1170 efree(op_array->literals);
1171 }
1172 op_array->literals = NULL;
1173 }
1174 #endif
1175
1176 opline = op_array->opcodes;
1177 end = opline + op_array->last;
1178 while (opline < end) {
1179 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
1180 uint32_t op1_info = opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
1181 uint32_t op2_info = opline->op1_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY));
1182 uint32_t res_info =
1183 (opline->opcode == ZEND_PRE_INC ||
1184 opline->opcode == ZEND_PRE_DEC ||
1185 opline->opcode == ZEND_POST_INC ||
1186 opline->opcode == ZEND_POST_DEC) ?
1187 ((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
1188 (opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)));
1189
1190 if (opline->op1_type == IS_CONST) {
1191 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1192 }
1193 if (opline->op2_type == IS_CONST) {
1194 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1195 }
1196
1197 /* fix jumps to point to new array */
1198 switch (opline->opcode) {
1199 #if ZEND_USE_ABS_JMP_ADDR && !ZEND_USE_ABS_CONST_ADDR
1200 case ZEND_JMP:
1201 case ZEND_FAST_CALL:
1202 opline->op1.jmp_addr = &op_array->opcodes[opline->op1.jmp_addr - old_opcodes];
1203 break;
1204 case ZEND_JMPZNZ:
1205 /* relative extended_value don't have to be changed */
1206 /* break omitted intentionally */
1207 case ZEND_JMPZ:
1208 case ZEND_JMPNZ:
1209 case ZEND_JMPZ_EX:
1210 case ZEND_JMPNZ_EX:
1211 case ZEND_JMP_SET:
1212 case ZEND_COALESCE:
1213 case ZEND_FE_RESET_R:
1214 case ZEND_FE_RESET_RW:
1215 case ZEND_ASSERT_CHECK:
1216 case ZEND_JMP_NULL:
1217 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1218 break;
1219 case ZEND_CATCH:
1220 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1221 opline->op2.jmp_addr = &op_array->opcodes[opline->op2.jmp_addr - old_opcodes];
1222 }
1223 break;
1224 case ZEND_FE_FETCH_R:
1225 case ZEND_FE_FETCH_RW:
1226 case ZEND_SWITCH_LONG:
1227 case ZEND_SWITCH_STRING:
1228 case ZEND_MATCH:
1229 /* relative extended_value don't have to be changed */
1230 break;
1231 #endif
1232 case ZEND_IS_IDENTICAL:
1233 case ZEND_IS_NOT_IDENTICAL:
1234 case ZEND_IS_EQUAL:
1235 case ZEND_IS_NOT_EQUAL:
1236 case ZEND_IS_SMALLER:
1237 case ZEND_IS_SMALLER_OR_EQUAL:
1238 case ZEND_CASE:
1239 case ZEND_CASE_STRICT:
1240 case ZEND_ISSET_ISEMPTY_CV:
1241 case ZEND_ISSET_ISEMPTY_VAR:
1242 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
1243 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
1244 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
1245 case ZEND_INSTANCEOF:
1246 case ZEND_TYPE_CHECK:
1247 case ZEND_DEFINED:
1248 case ZEND_IN_ARRAY:
1249 case ZEND_ARRAY_KEY_EXISTS:
1250 if (opline->result_type & IS_TMP_VAR) {
1251 /* reinitialize result_type of smart branch instructions */
1252 if (opline + 1 < end) {
1253 if ((opline+1)->opcode == ZEND_JMPZ
1254 && (opline+1)->op1_type == IS_TMP_VAR
1255 && (opline+1)->op1.var == opline->result.var) {
1256 opline->result_type = IS_SMART_BRANCH_JMPZ | IS_TMP_VAR;
1257 } else if ((opline+1)->opcode == ZEND_JMPNZ
1258 && (opline+1)->op1_type == IS_TMP_VAR
1259 && (opline+1)->op1.var == opline->result.var) {
1260 opline->result_type = IS_SMART_BRANCH_JMPNZ | IS_TMP_VAR;
1261 }
1262 }
1263 }
1264 break;
1265 }
1266 zend_vm_set_opcode_handler_ex(opline, op1_info, op2_info, res_info);
1267 opline++;
1268 }
1269
1270 op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
1271 }
1272
zend_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx)1273 static void zend_optimize_op_array(zend_op_array *op_array,
1274 zend_optimizer_ctx *ctx)
1275 {
1276 /* Revert pass_two() */
1277 zend_revert_pass_two(op_array);
1278
1279 /* Do actual optimizations */
1280 zend_optimize(op_array, ctx);
1281
1282 /* Redo pass_two() */
1283 zend_redo_pass_two(op_array);
1284
1285 if (op_array->live_range) {
1286 zend_recalc_live_ranges(op_array, NULL);
1287 }
1288 }
1289
zend_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)1290 static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1291 {
1292 zend_function *func;
1293 zend_op *opline, *end;
1294
1295 opline = op_array->opcodes;
1296 end = opline + op_array->last;
1297 while (opline < end) {
1298 if (opline->opcode == ZEND_INIT_FCALL) {
1299 func = zend_hash_find_ptr(
1300 &ctx->script->function_table,
1301 Z_STR_P(RT_CONSTANT(opline, opline->op2)));
1302 if (func) {
1303 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
1304 }
1305 }
1306 opline++;
1307 }
1308 }
1309
zend_adjust_fcall_stack_size_graph(zend_op_array * op_array)1310 static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
1311 {
1312 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
1313
1314 if (func_info) {
1315 zend_call_info *call_info =func_info->callee_info;
1316
1317 while (call_info) {
1318 zend_op *opline = call_info->caller_init_opline;
1319
1320 if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
1321 ZEND_ASSERT(!call_info->is_prototype);
1322 opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
1323 }
1324 call_info = call_info->next_callee;
1325 }
1326 }
1327 }
1328
needs_live_range(zend_op_array * op_array,zend_op * def_opline)1329 static bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) {
1330 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
1331 zend_ssa_op *ssa_op = &func_info->ssa.ops[def_opline - op_array->opcodes];
1332 int ssa_var = ssa_op->result_def;
1333 if (ssa_var < 0) {
1334 /* Be conservative. */
1335 return 1;
1336 }
1337
1338 /* If the variable is used by a PHI, this may be the assignment of the final branch of a
1339 * ternary/etc structure. While this is where the live range starts, the value from the other
1340 * branch may also be used. As such, use the type of the PHI node for the following check. */
1341 if (func_info->ssa.vars[ssa_var].phi_use_chain) {
1342 ssa_var = func_info->ssa.vars[ssa_var].phi_use_chain->ssa_var;
1343 }
1344
1345 uint32_t type = func_info->ssa.var_info[ssa_var].type;
1346 return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
1347 }
1348
zend_foreach_op_array_helper(zend_op_array * op_array,zend_op_array_func_t func,void * context)1349 static void zend_foreach_op_array_helper(
1350 zend_op_array *op_array, zend_op_array_func_t func, void *context) {
1351 func(op_array, context);
1352 for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
1353 zend_foreach_op_array_helper(op_array->dynamic_func_defs[i], func, context);
1354 }
1355 }
1356
zend_foreach_op_array(zend_script * script,zend_op_array_func_t func,void * context)1357 void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
1358 {
1359 zend_class_entry *ce;
1360 zend_string *key;
1361 zend_op_array *op_array;
1362
1363 zend_foreach_op_array_helper(&script->main_op_array, func, context);
1364
1365 ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1366 zend_foreach_op_array_helper(op_array, func, context);
1367 } ZEND_HASH_FOREACH_END();
1368
1369 ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1370 if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1371 continue;
1372 }
1373 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
1374 if (op_array->scope == ce
1375 && op_array->type == ZEND_USER_FUNCTION
1376 && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
1377 zend_foreach_op_array_helper(op_array, func, context);
1378 }
1379 } ZEND_HASH_FOREACH_END();
1380 } ZEND_HASH_FOREACH_END();
1381 }
1382
step_optimize_op_array(zend_op_array * op_array,void * context)1383 static void step_optimize_op_array(zend_op_array *op_array, void *context) {
1384 zend_optimize_op_array(op_array, (zend_optimizer_ctx *) context);
1385 }
1386
step_adjust_fcall_stack_size(zend_op_array * op_array,void * context)1387 static void step_adjust_fcall_stack_size(zend_op_array *op_array, void *context) {
1388 zend_adjust_fcall_stack_size(op_array, (zend_optimizer_ctx *) context);
1389 }
1390
step_dump_after_optimizer(zend_op_array * op_array,void * context)1391 static void step_dump_after_optimizer(zend_op_array *op_array, void *context) {
1392 zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "after optimizer", NULL);
1393 }
1394
zend_optimizer_call_registered_passes(zend_script * script,void * ctx)1395 static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx) {
1396 for (int i = 0; i < zend_optimizer_registered_passes.last; i++) {
1397 if (!zend_optimizer_registered_passes.pass[i]) {
1398 continue;
1399 }
1400
1401 zend_optimizer_registered_passes.pass[i](script, ctx);
1402 }
1403 }
1404
zend_optimize_script(zend_script * script,zend_long optimization_level,zend_long debug_level)1405 ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
1406 {
1407 zend_class_entry *ce;
1408 zend_string *key;
1409 zend_op_array *op_array;
1410 zend_string *name;
1411 zend_optimizer_ctx ctx;
1412 zend_call_graph call_graph;
1413
1414 ctx.arena = zend_arena_create(64 * 1024);
1415 ctx.script = script;
1416 ctx.constants = NULL;
1417 ctx.optimization_level = optimization_level;
1418 ctx.debug_level = debug_level;
1419
1420 if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
1421 (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
1422 zend_build_call_graph(&ctx.arena, script, &call_graph) == SUCCESS) {
1423 /* Optimize using call-graph */
1424 int i;
1425 zend_func_info *func_info;
1426
1427 for (i = 0; i < call_graph.op_arrays_count; i++) {
1428 zend_revert_pass_two(call_graph.op_arrays[i]);
1429 zend_optimize(call_graph.op_arrays[i], &ctx);
1430 }
1431
1432 zend_analyze_call_graph(&ctx.arena, script, &call_graph);
1433
1434 for (i = 0; i < call_graph.op_arrays_count; i++) {
1435 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1436 if (func_info) {
1437 func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
1438 if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1439 zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
1440 }
1441 }
1442 }
1443
1444 for (i = 0; i < call_graph.op_arrays_count; i++) {
1445 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1446 if (func_info) {
1447 if (zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa) == SUCCESS) {
1448 func_info->flags = func_info->ssa.cfg.flags;
1449 } else {
1450 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1451 }
1452 }
1453 }
1454
1455 //TODO: perform inner-script inference???
1456 for (i = 0; i < call_graph.op_arrays_count; i++) {
1457 func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1458 if (func_info) {
1459 zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, func_info->call_map);
1460 }
1461 }
1462
1463 if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
1464 for (i = 0; i < call_graph.op_arrays_count; i++) {
1465 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
1466 }
1467 }
1468
1469 if (ZEND_OPTIMIZER_PASS_9 & optimization_level) {
1470 for (i = 0; i < call_graph.op_arrays_count; i++) {
1471 zend_optimize_temporary_variables(call_graph.op_arrays[i], &ctx);
1472 if (debug_level & ZEND_DUMP_AFTER_PASS_9) {
1473 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 9", NULL);
1474 }
1475 }
1476 }
1477
1478 if (ZEND_OPTIMIZER_PASS_11 & optimization_level) {
1479 for (i = 0; i < call_graph.op_arrays_count; i++) {
1480 zend_optimizer_compact_literals(call_graph.op_arrays[i], &ctx);
1481 if (debug_level & ZEND_DUMP_AFTER_PASS_11) {
1482 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 11", NULL);
1483 }
1484 }
1485 }
1486
1487 if (ZEND_OPTIMIZER_PASS_13 & optimization_level) {
1488 for (i = 0; i < call_graph.op_arrays_count; i++) {
1489 zend_optimizer_compact_vars(call_graph.op_arrays[i]);
1490 if (debug_level & ZEND_DUMP_AFTER_PASS_13) {
1491 zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 13", NULL);
1492 }
1493 }
1494 }
1495
1496 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1497 for (i = 0; i < call_graph.op_arrays_count; i++) {
1498 zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
1499 }
1500 }
1501
1502 for (i = 0; i < call_graph.op_arrays_count; i++) {
1503 op_array = call_graph.op_arrays[i];
1504 func_info = ZEND_FUNC_INFO(op_array);
1505 if (func_info && func_info->ssa.var_info) {
1506 zend_redo_pass_two_ex(op_array, &func_info->ssa);
1507 if (op_array->live_range) {
1508 zend_recalc_live_ranges(op_array, needs_live_range);
1509 }
1510 } else {
1511 zend_redo_pass_two(op_array);
1512 if (op_array->live_range) {
1513 zend_recalc_live_ranges(op_array, NULL);
1514 }
1515 }
1516 }
1517
1518 for (i = 0; i < call_graph.op_arrays_count; i++) {
1519 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1520 }
1521 } else {
1522 zend_foreach_op_array(script, step_optimize_op_array, &ctx);
1523
1524 if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1525 zend_foreach_op_array(script, step_adjust_fcall_stack_size, &ctx);
1526 }
1527 }
1528
1529 ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1530 if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1531 continue;
1532 }
1533 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1534 if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
1535 zend_op_array *orig_op_array =
1536 zend_hash_find_ptr(&op_array->scope->function_table, name);
1537
1538 ZEND_ASSERT(orig_op_array != NULL);
1539 if (orig_op_array != op_array) {
1540 uint32_t fn_flags = op_array->fn_flags;
1541 zend_function *prototype = op_array->prototype;
1542 HashTable *ht = op_array->static_variables;
1543
1544 *op_array = *orig_op_array;
1545 op_array->fn_flags = fn_flags;
1546 op_array->prototype = prototype;
1547 op_array->static_variables = ht;
1548 }
1549 }
1550 } ZEND_HASH_FOREACH_END();
1551 } ZEND_HASH_FOREACH_END();
1552
1553 zend_optimizer_call_registered_passes(script, &ctx);
1554
1555 if ((debug_level & ZEND_DUMP_AFTER_OPTIMIZER) &&
1556 (ZEND_OPTIMIZER_PASS_7 & optimization_level)) {
1557 zend_foreach_op_array(script, step_dump_after_optimizer, NULL);
1558 }
1559
1560 if (ctx.constants) {
1561 zend_hash_destroy(ctx.constants);
1562 }
1563 zend_arena_destroy(ctx.arena);
1564
1565 return 1;
1566 }
1567
zend_optimizer_register_pass(zend_optimizer_pass_t pass)1568 ZEND_API int zend_optimizer_register_pass(zend_optimizer_pass_t pass)
1569 {
1570 if (!pass) {
1571 return -1;
1572 }
1573
1574 if (zend_optimizer_registered_passes.last == ZEND_OPTIMIZER_MAX_REGISTERED_PASSES) {
1575 return -1;
1576 }
1577
1578 zend_optimizer_registered_passes.pass[
1579 zend_optimizer_registered_passes.last++] = pass;
1580
1581 return zend_optimizer_registered_passes.last;
1582 }
1583
zend_optimizer_unregister_pass(int idx)1584 ZEND_API void zend_optimizer_unregister_pass(int idx)
1585 {
1586 zend_optimizer_registered_passes.pass[idx-1] = NULL;
1587 }
1588
zend_optimizer_startup(void)1589 int zend_optimizer_startup(void)
1590 {
1591 return zend_func_info_startup();
1592 }
1593
zend_optimizer_shutdown(void)1594 int zend_optimizer_shutdown(void)
1595 {
1596 return zend_func_info_shutdown();
1597 }
1598