php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
sccp.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Zend Engine, SCCP - Sparse Conditional Constant Propagation |
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: Nikita Popov <nikic@php.net> |
16 | Dmitry Stogov <dmitry@php.net> |
17 +----------------------------------------------------------------------+
18*/
19
20#include "zend_API.h"
21#include "zend_exceptions.h"
22#include "zend_ini.h"
23#include "zend_type_info.h"
27#include "Optimizer/scdf.h"
28#include "Optimizer/zend_dump.h"
29
30/* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The
31 * used value lattice is defined as follows:
32 *
33 * BOT < {constant values} < TOP
34 *
35 * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable.
36 * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant.
37 *
38 * All variables are optimistically initialized to TOP, apart from the implicit variables defined
39 * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to
40 * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway.
41 * This is better because such operations might never be reached due to the conditional nature of
42 * the algorithm.
43 *
44 * The meet operation for phi functions is defined as follows:
45 * BOT + any = BOT
46 * TOP + any = any
47 * C_i + C_i = C_i (i.e. two equal constants)
48 * C_i + C_j = BOT (i.e. two different constants)
49 *
50 * When evaluating instructions TOP and BOT are handled as follows:
51 * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which
52 * is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT.
53 * b) Otherwise, if the instruction can never be evaluated (either in general, or with the
54 * specific modifiers) the result is BOT.
55 * c) Otherwise, if any operand is TOP, the result is TOP.
56 * d) Otherwise (at this point all operands are known and constant), if we can compute the result
57 * for these specific constants (without throwing notices or similar) then that is the result.
58 * e) Otherwise the result is BOT.
59 *
60 * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things
61 * like BOT*0. Right now we don't bother with this.
62 *
63 * Feasible successors for conditional branches are determined as follows:
64 * a) If we don't support the branch type or branch on BOT, all successors are feasible.
65 * b) Otherwise, if we branch on TOP none of the successors are feasible.
66 * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant
67 * (usually only one successor will be feasible).
68 *
69 * The original SCCP algorithm is extended with ability to propagate constant array
70 * elements and object properties. The extension is based on a variation of Array
71 * SSA form and its application to Spare Constant Propagation, described at
72 * "Array SSA Form" by Vivek Sarkar, Kathleen Knobe and Stephen Fink in chapter
73 * 16 of the SSA book.
74 */
75
76#define SCP_DEBUG 0
77
85
86#define TOP ((uint8_t)-1)
87#define BOT ((uint8_t)-2)
88#define PARTIAL_ARRAY ((uint8_t)-3)
89#define PARTIAL_OBJECT ((uint8_t)-4)
90#define IS_TOP(zv) (Z_TYPE_P(zv) == TOP)
91#define IS_BOT(zv) (Z_TYPE_P(zv) == BOT)
92#define IS_PARTIAL_ARRAY(zv) (Z_TYPE_P(zv) == PARTIAL_ARRAY)
93#define IS_PARTIAL_OBJECT(zv) (Z_TYPE_P(zv) == PARTIAL_OBJECT)
94
95#define MAKE_PARTIAL_ARRAY(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
96#define MAKE_PARTIAL_OBJECT(zv) (Z_TYPE_INFO_P(zv) = PARTIAL_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
97
98#define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP)
99#define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT)
100
101static void scp_dump_value(zval *zv) {
102 if (IS_TOP(zv)) {
103 fprintf(stderr, " top");
104 } else if (IS_BOT(zv)) {
105 fprintf(stderr, " bot");
106 } else if (Z_TYPE_P(zv) == IS_ARRAY || IS_PARTIAL_ARRAY(zv)) {
107 fprintf(stderr, " %s[", IS_PARTIAL_ARRAY(zv) ? "partial " : "");
109 fprintf(stderr, "]");
110 } else if (IS_PARTIAL_OBJECT(zv)) {
111 fprintf(stderr, " {");
113 fprintf(stderr, "}");
114 } else {
116 }
117}
118
119static void empty_partial_array(zval *zv)
120{
123}
124
125static void dup_partial_array(zval *dst, const zval *src)
126{
128 Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
129}
130
131static void empty_partial_object(zval *zv)
132{
135}
136
137static void dup_partial_object(zval *dst, const zval *src)
138{
140 Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
141}
142
143static inline bool value_known(zval *zv) {
144 return !IS_TOP(zv) && !IS_BOT(zv);
145}
146
147/* Sets new value for variable and ensures that it is lower or equal
148 * the previous one in the constant propagation lattice. */
149static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, const zval *new) {
150 zval *value = &ctx->values[var];
151 if (IS_BOT(value) || IS_TOP(new)) {
152 return;
153 }
154
155#if SCP_DEBUG
156 fprintf(stderr, "Lowering #%d.", var);
157 zend_dump_var(scdf->op_array, IS_CV, scdf->ssa->vars[var].var);
158 fprintf(stderr, " from");
159 scp_dump_value(value);
160 fprintf(stderr, " to");
161 scp_dump_value(new);
162 fprintf(stderr, "\n");
163#endif
164
165 if (IS_TOP(value) || IS_BOT(new)) {
166 zval_ptr_dtor_nogc(value);
167 ZVAL_COPY(value, new);
168 scdf_add_to_worklist(scdf, var);
169 return;
170 }
171
172 /* Always replace PARTIAL_(ARRAY|OBJECT), as new maybe changed by join_partial_(arrays|object) */
173 if (IS_PARTIAL_ARRAY(new) || IS_PARTIAL_OBJECT(new)) {
174 if (Z_TYPE_P(value) != Z_TYPE_P(new)
175 || zend_hash_num_elements(Z_ARR_P(new)) != zend_hash_num_elements(Z_ARR_P(value))) {
176 zval_ptr_dtor_nogc(value);
177 ZVAL_COPY(value, new);
178 scdf_add_to_worklist(scdf, var);
179 }
180 return;
181 }
182
183#if ZEND_DEBUG
185 (Z_TYPE_P(value) == IS_DOUBLE && Z_TYPE_P(new) == IS_DOUBLE && isnan(Z_DVAL_P(value)) && isnan(Z_DVAL_P(new))));
186#endif
187}
188
189static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, const zend_ssa_op *ssa_op) {
190 if (opline->op1_type == IS_CONST) {
191 return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant);
192 } else if (ssa_op->op1_use != -1) {
193 return &ctx->values[ssa_op->op1_use];
194 } else {
195 return NULL;
196 }
197}
198
199static zval *get_op2_value(sccp_ctx *ctx, const zend_op *opline, const zend_ssa_op *ssa_op) {
200 if (opline->op2_type == IS_CONST) {
201 return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant);
202 } else if (ssa_op->op2_use != -1) {
203 return &ctx->values[ssa_op->op2_use];
204 } else {
205 return NULL;
206 }
207}
208
209static bool can_replace_op1(
210 const zend_op_array *op_array, const zend_op *opline, const zend_ssa_op *ssa_op) {
211 switch (opline->opcode) {
212 case ZEND_PRE_INC:
213 case ZEND_PRE_DEC:
214 case ZEND_PRE_INC_OBJ:
215 case ZEND_PRE_DEC_OBJ:
216 case ZEND_POST_INC:
217 case ZEND_POST_DEC:
220 case ZEND_ASSIGN:
221 case ZEND_ASSIGN_REF:
222 case ZEND_ASSIGN_DIM:
223 case ZEND_ASSIGN_OBJ:
225 case ZEND_ASSIGN_OP:
229 case ZEND_FETCH_DIM_W:
233 case ZEND_FETCH_OBJ_W:
238 case ZEND_UNSET_DIM:
239 case ZEND_UNSET_OBJ:
240 case ZEND_SEND_REF:
241 case ZEND_SEND_VAR_EX:
243 case ZEND_SEND_UNPACK:
244 case ZEND_SEND_ARRAY:
245 case ZEND_SEND_USER:
246 case ZEND_FE_RESET_RW:
247 return 0;
248 /* Do not accept CONST */
249 case ZEND_ROPE_ADD:
250 case ZEND_ROPE_END:
251 case ZEND_BIND_STATIC:
253 case ZEND_BIND_GLOBAL:
254 case ZEND_MAKE_REF:
255 case ZEND_UNSET_CV:
257 return 0;
258 case ZEND_INIT_ARRAY:
260 return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
261 case ZEND_YIELD:
262 return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
264 // TODO: This would require a non-local change ???
265 return 0;
266 case ZEND_OP_DATA:
267 return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF &&
268 (opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF;
269 default:
270 if (ssa_op->op1_def != -1) {
272 return 0;
273 }
274 }
275
276 return 1;
277}
278
279static bool can_replace_op2(
280 const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
281 switch (opline->opcode) {
282 /* Do not accept CONST */
285 case ZEND_FE_FETCH_R:
286 case ZEND_FE_FETCH_RW:
287 return 0;
288 }
289 return 1;
290}
291
292static bool try_replace_op1(
293 sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
294 if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) {
295 zval zv;
296 ZVAL_COPY(&zv, value);
297 if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
298 return 1;
299 }
300 zval_ptr_dtor_nogc(&zv);
301 }
302 return 0;
303}
304
305static bool try_replace_op2(
306 sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
307 if (ssa_op->op2_use == var && can_replace_op2(ctx->scdf.op_array, opline, ssa_op)) {
308 zval zv;
309 ZVAL_COPY(&zv, value);
310 if (zend_optimizer_update_op2_const(ctx->scdf.op_array, opline, &zv)) {
311 return 1;
312 }
313 zval_ptr_dtor_nogc(&zv);
314 }
315 return 0;
316}
317
318static inline zend_result ct_eval_binary_op(zval *result, uint8_t binop, zval *op1, zval *op2) {
319 /* TODO: We could implement support for evaluation of + on partial arrays. */
321 return FAILURE;
322 }
323
325}
326
327static inline zend_result ct_eval_bool_cast(zval *result, zval *op) {
328 if (IS_PARTIAL_ARRAY(op)) {
329 if (zend_hash_num_elements(Z_ARRVAL_P(op)) == 0) {
330 /* An empty partial array may be non-empty at runtime, we don't know whether the
331 * result will be true or false. */
332 return FAILURE;
333 }
334
336 return SUCCESS;
337 }
338
340 return SUCCESS;
341}
342
343static inline zend_result zval_to_string_offset(zend_long *result, zval *op) {
344 switch (Z_TYPE_P(op)) {
345 case IS_LONG:
346 *result = Z_LVAL_P(op);
347 return SUCCESS;
348 case IS_STRING:
349 if (IS_LONG == is_numeric_string(
350 Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) {
351 return SUCCESS;
352 }
353 return FAILURE;
354 default:
355 return FAILURE;
356 }
357}
358
359static inline zend_result fetch_array_elem(zval **result, zval *op1, zval *op2) {
360 switch (Z_TYPE_P(op2)) {
361 case IS_NULL:
363 return SUCCESS;
364 case IS_FALSE:
366 return SUCCESS;
367 case IS_TRUE:
369 return SUCCESS;
370 case IS_LONG:
372 return SUCCESS;
373 case IS_DOUBLE: {
374 zend_long lval = zend_dval_to_lval(Z_DVAL_P(op2));
375 if (!zend_is_long_compatible(Z_DVAL_P(op2), lval)) {
376 return FAILURE;
377 }
379 return SUCCESS;
380 }
381 case IS_STRING:
382 *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
383 return SUCCESS;
384 default:
385 return FAILURE;
386 }
387}
388
389static inline zend_result ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
391 zval *value;
392 if (fetch_array_elem(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
394 return SUCCESS;
395 }
396 } else if (support_strings && Z_TYPE_P(op1) == IS_STRING) {
397 zend_long index;
398 if (zval_to_string_offset(&index, op2) == FAILURE) {
399 return FAILURE;
400 }
401 if (index >= 0 && index < Z_STRLEN_P(op1)) {
402 ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
403 return SUCCESS;
404 }
405 }
406 return FAILURE;
407}
408
409/* op1 may be NULL here to indicate an unset value */
410static inline zend_result ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
411 zval zv;
412 if (!(extended_value & ZEND_ISEMPTY)) {
414 return SUCCESS;
415 } else if (!op1) {
417 return SUCCESS;
418 } else if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
420 return SUCCESS;
421 } else {
422 return FAILURE;
423 }
424}
425
426static inline zend_result ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
428 zval *value;
429 if (fetch_array_elem(&value, op1, op2) == FAILURE) {
430 return FAILURE;
431 }
432 if (IS_PARTIAL_ARRAY(op1) && (!value || IS_BOT(value))) {
433 return FAILURE;
434 }
435 return ct_eval_isset_isempty(result, extended_value, value);
436 } else if (Z_TYPE_P(op1) == IS_STRING) {
437 // TODO
438 return FAILURE;
439 } else {
440 ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
441 return SUCCESS;
442 }
443}
444
445static inline zend_result ct_eval_del_array_elem(zval *result, const zval *key) {
447
448 switch (Z_TYPE_P(key)) {
449 case IS_NULL:
451 break;
452 case IS_FALSE:
454 break;
455 case IS_TRUE:
457 break;
458 case IS_LONG:
460 break;
461 case IS_DOUBLE: {
462 zend_long lval = zend_dval_to_lval(Z_DVAL_P(key));
463 if (!zend_is_long_compatible(Z_DVAL_P(key), lval)) {
464 return FAILURE;
465 }
467 break;
468 }
469 case IS_STRING:
470 zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
471 break;
472 default:
473 return FAILURE;
474 }
475
476 return SUCCESS;
477}
478
479static inline zend_result ct_eval_add_array_elem(zval *result, zval *value, const zval *key) {
480 if (!key) {
484 return SUCCESS;
485 }
486 return FAILURE;
487 }
488
489 switch (Z_TYPE_P(key)) {
490 case IS_NULL:
493 break;
494 case IS_FALSE:
497 break;
498 case IS_TRUE:
501 break;
502 case IS_LONG:
505 break;
506 case IS_DOUBLE: {
507 zend_long lval = zend_dval_to_lval(Z_DVAL_P(key));
508 if (!zend_is_long_compatible(Z_DVAL_P(key), lval)) {
509 return FAILURE;
510 }
513 Z_ARR_P(result), lval, value);
514 break;
515 }
516 case IS_STRING:
518 value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
519 break;
520 default:
521 return FAILURE;
522 }
523
525 return SUCCESS;
526}
527
528static inline zend_result ct_eval_add_array_unpack(zval *result, zval *array) {
530 zval *value;
531 if (Z_TYPE_P(array) != IS_ARRAY) {
532 return FAILURE;
533 }
534
537 if (key) {
539 } else {
541 }
542 if (!value) {
543 return FAILURE;
544 }
547 return SUCCESS;
548}
549
550static inline zend_result ct_eval_assign_dim(zval *result, zval *value, const zval *key) {
551 switch (Z_TYPE_P(result)) {
552 case IS_NULL:
553 case IS_FALSE:
556 case IS_ARRAY:
557 case PARTIAL_ARRAY:
558 return ct_eval_add_array_elem(result, value, key);
559 case IS_STRING:
560 // TODO Before enabling this case, make sure ARRAY_DIM result op is correct
561#if 0
562 zend_long index;
563 zend_string *new_str, *value_str;
564 if (!key || Z_TYPE_P(value) == IS_ARRAY
565 || zval_to_string_offset(&index, key) == FAILURE || index < 0) {
566 return FAILURE;
567 }
568
569 if (index >= Z_STRLEN_P(result)) {
570 new_str = zend_string_alloc(index + 1, 0);
572 memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result));
573 ZSTR_VAL(new_str)[index + 1] = 0;
574 } else {
575 new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0);
576 }
577
578 value_str = zval_get_string(value);
579 ZVAL_STR(result, new_str);
580 Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0];
581 zend_string_release_ex(value_str, 0);
582#endif
583 return FAILURE;
584 default:
585 return FAILURE;
586 }
587}
588
589static inline zend_result fetch_obj_prop(zval **result, zval *op1, zval *op2) {
590 switch (Z_TYPE_P(op2)) {
591 case IS_STRING:
592 *result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
593 return SUCCESS;
594 default:
595 return FAILURE;
596 }
597}
598
599static inline zend_result ct_eval_fetch_obj(zval *result, zval *op1, zval *op2) {
600 if (IS_PARTIAL_OBJECT(op1)) {
601 zval *value;
602 if (fetch_obj_prop(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
604 return SUCCESS;
605 }
606 }
607 return FAILURE;
608}
609
610static inline zend_result ct_eval_isset_obj(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
611 if (IS_PARTIAL_OBJECT(op1)) {
612 zval *value;
613 if (fetch_obj_prop(&value, op1, op2) == FAILURE) {
614 return FAILURE;
615 }
616 if (!value || IS_BOT(value)) {
617 return FAILURE;
618 }
619 return ct_eval_isset_isempty(result, extended_value, value);
620 } else {
621 ZVAL_BOOL(result, (extended_value & ZEND_ISEMPTY));
622 return SUCCESS;
623 }
624}
625
626static inline zend_result ct_eval_del_obj_prop(zval *result, const zval *key) {
628
629 switch (Z_TYPE_P(key)) {
630 case IS_STRING:
631 zend_symtable_del(Z_ARR_P(result), Z_STR_P(key));
632 break;
633 default:
634 return FAILURE;
635 }
636
637 return SUCCESS;
638}
639
640static inline zend_result ct_eval_add_obj_prop(zval *result, zval *value, const zval *key) {
641 switch (Z_TYPE_P(key)) {
642 case IS_STRING:
643 value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
644 break;
645 default:
646 return FAILURE;
647 }
648
650 return SUCCESS;
651}
652
653static inline zend_result ct_eval_assign_obj(zval *result, zval *value, const zval *key) {
654 switch (Z_TYPE_P(result)) {
655 case IS_NULL:
656 case IS_FALSE:
657 empty_partial_object(result);
659 case PARTIAL_OBJECT:
660 return ct_eval_add_obj_prop(result, value, key);
661 default:
662 return FAILURE;
663 }
664}
665
666static inline zend_result ct_eval_incdec(zval *result, uint8_t opcode, zval *op1) {
667 /* As of PHP 8.3 with the warning/deprecation notices any type other than int/double/null will emit a diagnostic
668 if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
669 return FAILURE;
670 }
671 */
672 if (Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_DOUBLE && Z_TYPE_P(op1) != IS_NULL) {
673 return FAILURE;
674 }
675
677 if (opcode == ZEND_PRE_INC
678 || opcode == ZEND_POST_INC
679 || opcode == ZEND_PRE_INC_OBJ
680 || opcode == ZEND_POST_INC_OBJ) {
682 } else {
683 /* Decrement on null emits a deprecation notice */
684 if (Z_TYPE_P(op1) == IS_NULL) {
686 return FAILURE;
687 }
689 }
690 return SUCCESS;
691}
692
693static inline void ct_eval_type_check(zval *result, uint32_t type_mask, zval *op1) {
694 uint32_t type = Z_TYPE_P(op1);
695 if (type == PARTIAL_ARRAY) {
696 type = IS_ARRAY;
697 } else if (type == PARTIAL_OBJECT) {
698 type = IS_OBJECT;
699 }
700 ZVAL_BOOL(result, (type_mask >> type) & 1);
701}
702
703static inline zend_result ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
704 HashTable *ht;
705 bool res;
706
707 if (Z_TYPE_P(op2) != IS_ARRAY) {
708 return FAILURE;
709 }
710 ht = Z_ARRVAL_P(op2);
711 if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
712 res = zend_hash_exists(ht, Z_STR_P(op1));
713 } else if (extended_value) {
714 if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
715 res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
716 } else {
717 res = 0;
718 }
719 } else if (Z_TYPE_P(op1) <= IS_FALSE) {
720 res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
721 } else {
723 zval key_tmp;
724
725 res = 0;
727 ZVAL_STR(&key_tmp, key);
728 if (zend_compare(op1, &key_tmp) == 0) {
729 res = 1;
730 break;
731 }
733 }
735 return SUCCESS;
736}
737
738static inline zend_result ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) {
739 zval *value;
740
742 return FAILURE;
743 }
744 if (Z_TYPE_P(op1) != IS_STRING && Z_TYPE_P(op1) != IS_LONG && Z_TYPE_P(op1) != IS_NULL) {
745 return FAILURE;
746 }
747 if (fetch_array_elem(&value, op2, op1) == FAILURE) {
748 return FAILURE;
749 }
750 if (IS_PARTIAL_ARRAY(op2) && (!value || IS_BOT(value))) {
751 return FAILURE;
752 }
753
755 return SUCCESS;
756}
757
758static bool can_ct_eval_func_call(zend_function *func, zend_string *name, uint32_t num_args, zval **args) {
759 /* Precondition: func->type == ZEND_INTERNAL_FUNCTION, this is a global function */
760 /* Functions setting ZEND_ACC_COMPILE_TIME_EVAL (@compile-time-eval) must always produce the same result for the same arguments,
761 * and have no dependence on global state (such as locales). It is okay if they throw
762 * or warn on invalid arguments, as we detect this and will discard the evaluation result. */
763 if (func->common.fn_flags & ZEND_ACC_COMPILE_TIME_EVAL) {
764 /* This has @compile-time-eval in stub info and uses a macro such as ZEND_SUPPORTS_COMPILE_TIME_EVAL_FE */
765 return true;
766 }
767#ifndef ZEND_WIN32
768 /* On Windows this function may be code page dependent. */
769 if (zend_string_equals_literal(name, "dirname")) {
770 return true;
771 }
772#endif
773
774 if (num_args == 2) {
775 if (zend_string_equals_literal(name, "str_repeat")) {
776 /* Avoid creating overly large strings at compile-time. */
777 bool overflow;
778 return Z_TYPE_P(args[0]) == IS_STRING
779 && Z_TYPE_P(args[1]) == IS_LONG
780 && zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) < 64 * 1024
781 && !overflow;
782 }
783 return false;
784 }
785
786 return false;
787}
788
789/* The functions chosen here are simple to implement and either likely to affect a branch,
790 * or just happened to be commonly used with constant operands in WP (need to test other
791 * applications as well, of course). */
792static inline zend_result ct_eval_func_call_ex(
793 zend_op_array *op_array, zval *result, zend_function *func, uint32_t num_args, zval **args) {
794 uint32_t i;
795 zend_string *name = func->common.function_name;
796 if (num_args == 1 && Z_TYPE_P(args[0]) == IS_STRING &&
798 return SUCCESS;
799 }
800
801 if (!can_ct_eval_func_call(func, name, num_args, args)) {
802 return FAILURE;
803 }
804
805 zend_execute_data *prev_execute_data = EG(current_execute_data);
806 zend_execute_data *execute_data, dummy_frame;
807 zend_op dummy_opline;
808
809 /* Add a dummy frame to get the correct strict_types behavior. */
810 memset(&dummy_frame, 0, sizeof(zend_execute_data));
811 memset(&dummy_opline, 0, sizeof(zend_op));
812 dummy_frame.func = (zend_function *) op_array;
813 dummy_frame.opline = &dummy_opline;
814 dummy_opline.opcode = ZEND_DO_FCALL;
815
818 execute_data->prev_execute_data = &dummy_frame;
819 EG(current_execute_data) = execute_data;
820
821 /* Enable suppression and counting of warnings. */
822 ZEND_ASSERT(EG(capture_warnings_during_sccp) == 0);
823 EG(capture_warnings_during_sccp) = 1;
824
825 EX(func) = func;
827 for (i = 0; i < num_args; i++) {
828 ZVAL_COPY(EX_VAR_NUM(i), args[i]);
829 }
831 func->internal_function.handler(execute_data, result);
832 for (i = 0; i < num_args; i++) {
833 zval_ptr_dtor_nogc(EX_VAR_NUM(i));
834 }
835
837 if (EG(exception)) {
840 retval = FAILURE;
841 }
842
843 if (EG(capture_warnings_during_sccp) > 1) {
845 retval = FAILURE;
846 }
847 EG(capture_warnings_during_sccp) = 0;
848
850 EG(current_execute_data) = prev_execute_data;
851 return retval;
852}
853
854static inline zend_result ct_eval_func_call(
855 zend_op_array *op_array, zval *result, zend_string *name, uint32_t num_args, zval **args) {
856 zend_function *func = zend_hash_find_ptr(CG(function_table), name);
857 if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
858 return FAILURE;
859 }
860 return ct_eval_func_call_ex(op_array, result, func, num_args, args);
861}
862
863#define SET_RESULT(op, zv) do { \
864 if (ssa_op->op##_def >= 0) { \
865 set_value(scdf, ctx, ssa_op->op##_def, zv); \
866 } \
867} while (0)
868#define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
869#define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
870
871#define SKIP_IF_TOP(op) if (IS_TOP(op)) return;
872
873static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
874 sccp_ctx *ctx = (sccp_ctx *) scdf;
875 zval *op1, *op2, zv; /* zv is a temporary to hold result values */
876
877 op1 = get_op1_value(ctx, opline, ssa_op);
878 op2 = get_op2_value(ctx, opline, ssa_op);
879
880 switch (opline->opcode) {
881 case ZEND_ASSIGN:
882 /* The value of op1 is irrelevant here, because we are overwriting it
883 * -- unless it can be a reference, in which case we propagate a BOT.
884 * The result is also BOT in this case, because it might be a typed reference. */
885 if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
888 } else {
891 }
892 return;
893 case ZEND_ASSIGN_DIM:
894 {
895 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
896
897 /* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */
898 if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) {
899 op1 = &EG(uninitialized_zval);
900 }
901
902 if (IS_BOT(op1)) {
905 return;
906 }
907
910 if (op2) {
912 }
913
914 if (op2 && IS_BOT(op2)) {
915 /* Update of unknown index */
917 if (ssa_op->op1_def >= 0) {
918 empty_partial_array(&zv);
919 SET_RESULT(op1, &zv);
920 zval_ptr_dtor_nogc(&zv);
921 } else {
923 }
924 return;
925 }
926
927 if (IS_BOT(data)) {
928
931 || Z_TYPE_P(op1) == IS_NULL
932 || Z_TYPE_P(op1) == IS_FALSE
933 || Z_TYPE_P(op1) == IS_ARRAY)
934 && ssa_op->op1_def >= 0) {
935
936 if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
937 empty_partial_array(&zv);
938 } else {
939 dup_partial_array(&zv, op1);
940 }
941
942 if (!op2) {
943 /* We can't add NEXT element into partial array (skip it) */
944 SET_RESULT(op1, &zv);
945 } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
946 SET_RESULT(op1, &zv);
947 } else {
949 }
950
951 zval_ptr_dtor_nogc(&zv);
952 } else {
954 }
955
956 } else {
957
958 if (IS_PARTIAL_ARRAY(op1)) {
959 dup_partial_array(&zv, op1);
960 } else {
961 ZVAL_COPY(&zv, op1);
962 }
963
964 if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
965 /* We can't add NEXT element into partial array (skip it) */
967 SET_RESULT(op1, &zv);
968 } else if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
969 /* Mark array containing partial array as partial */
970 if (IS_PARTIAL_ARRAY(data)) {
972 }
974 SET_RESULT(op1, &zv);
975 } else {
978 }
979
980 zval_ptr_dtor_nogc(&zv);
981 }
982 return;
983 }
984
985 case ZEND_ASSIGN_OBJ:
986 if (ssa_op->op1_def >= 0
988 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
989 zend_ssa_var_info *var_info = &ctx->scdf.ssa->var_info[ssa_op->op1_use];
990
991 /* Don't try to propagate assignments to (potentially) typed properties. We would
992 * need to deal with errors and type conversions first. */
993 // TODO: Distinguish dynamic and declared property assignments here?
994 if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) ||
998 return;
999 }
1000
1001 if (IS_BOT(op1)) {
1004 return;
1005 }
1006
1010
1011 if (IS_BOT(op2)) {
1012 /* Update of unknown property */
1014 empty_partial_object(&zv);
1015 SET_RESULT(op1, &zv);
1016 zval_ptr_dtor_nogc(&zv);
1017 return;
1018 }
1019
1020 if (IS_BOT(data)) {
1023 || Z_TYPE_P(op1) == IS_NULL
1024 || Z_TYPE_P(op1) == IS_FALSE) {
1025
1026 if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
1027 empty_partial_object(&zv);
1028 } else {
1029 dup_partial_object(&zv, op1);
1030 }
1031
1032 if (ct_eval_del_obj_prop(&zv, op2) == SUCCESS) {
1033 SET_RESULT(op1, &zv);
1034 } else {
1036 }
1037 zval_ptr_dtor_nogc(&zv);
1038 } else {
1040 }
1041
1042 } else {
1043
1044 if (IS_PARTIAL_OBJECT(op1)) {
1045 dup_partial_object(&zv, op1);
1046 } else {
1047 ZVAL_COPY(&zv, op1);
1048 }
1049
1050 if (ct_eval_assign_obj(&zv, data, op2) == SUCCESS) {
1052 SET_RESULT(op1, &zv);
1053 } else {
1056 }
1057
1058 zval_ptr_dtor_nogc(&zv);
1059 }
1060 } else {
1063 }
1064 return;
1065
1066 case ZEND_SEND_VAL:
1067 case ZEND_SEND_VAR:
1068 {
1069 /* If the value of a SEND for an ICALL changes, we need to reconsider the
1070 * ICALL result value. Otherwise we can ignore the opcode. */
1072 if (!ctx->call_map) {
1073 return;
1074 }
1075
1076 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1077 if (IS_TOP(op1) || !call || !call->caller_call_opline
1078 || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
1079 return;
1080 }
1081
1082 opline = call->caller_call_opline;
1083 ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
1084 break;
1085 }
1086 case ZEND_INIT_ARRAY:
1088 {
1089 zval *result = NULL;
1090
1091 if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1092 result = &ctx->values[ssa_op->result_use];
1093 if (IS_BOT(result)) {
1096 return;
1097 }
1099 }
1100
1101 if (op1) {
1103 }
1104
1105 if (op2) {
1107 }
1108
1109 /* We want to avoid keeping around intermediate arrays for each SSA variable in the
1110 * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
1111 * and use a NULL value everywhere else. */
1112 if (result && Z_TYPE_P(result) == IS_NULL) {
1114 return;
1115 }
1116
1117 if (op2 && IS_BOT(op2)) {
1118 /* Update of unknown index */
1120 if (ssa_op->result_def >= 0) {
1121 empty_partial_array(&zv);
1122 SET_RESULT(result, &zv);
1123 zval_ptr_dtor_nogc(&zv);
1124 } else {
1126 }
1127 return;
1128 }
1129
1130 if ((op1 && IS_BOT(op1))
1131 || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
1132
1134 if (ssa_op->result_def >= 0) {
1135 if (!result) {
1136 empty_partial_array(&zv);
1137 } else {
1141 }
1142 if (!op2) {
1143 /* We can't add NEXT element into partial array (skip it) */
1144 SET_RESULT(result, &zv);
1145 } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
1146 SET_RESULT(result, &zv);
1147 } else {
1149 }
1150 zval_ptr_dtor_nogc(&zv);
1151 } else {
1152 /* If any operand is BOT, mark the result as BOT right away.
1153 * Exceptions to this rule are handled above. */
1155 }
1156
1157 } else {
1158 if (result) {
1161 } else {
1162 array_init(&zv);
1163 }
1164
1165 if (op1) {
1166 if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
1167 /* We can't add NEXT element into partial array (skip it) */
1168 SET_RESULT(result, &zv);
1169 } else if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
1170 if (IS_PARTIAL_ARRAY(op1)) {
1172 }
1173 SET_RESULT(result, &zv);
1174 } else {
1176 }
1177 } else {
1178 SET_RESULT(result, &zv);
1179 }
1180
1181 zval_ptr_dtor_nogc(&zv);
1182 }
1183 return;
1184 }
1185 case ZEND_ADD_ARRAY_UNPACK: {
1186 zval *result = &ctx->values[ssa_op->result_use];
1187 if (IS_BOT(result) || IS_BOT(op1)) {
1189 return;
1190 }
1193
1194 /* See comment for ADD_ARRAY_ELEMENT. */
1195 if (Z_TYPE_P(result) == IS_NULL) {
1197 return;
1198 }
1201
1202 if (ct_eval_add_array_unpack(&zv, op1) == SUCCESS) {
1203 SET_RESULT(result, &zv);
1204 } else {
1206 }
1207 zval_ptr_dtor_nogc(&zv);
1208 return;
1209 }
1210 case ZEND_NEW:
1211 if (ssa_op->result_def >= 0
1213 empty_partial_object(&zv);
1214 SET_RESULT(result, &zv);
1215 zval_ptr_dtor_nogc(&zv);
1216 } else {
1218 }
1219 return;
1222 /* Handled here because we also need to BOT the OP_DATA operand, while the generic
1223 * code below will not do so. */
1227 opline++;
1228 ssa_op++;
1230 break;
1231 }
1232
1233 if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
1234 /* If any operand is BOT, mark the result as BOT right away.
1235 * Exceptions to this rule are handled above. */
1239 return;
1240 }
1241
1242 switch (opline->opcode) {
1243 case ZEND_ADD:
1244 case ZEND_SUB:
1245 case ZEND_MUL:
1246 case ZEND_DIV:
1247 case ZEND_MOD:
1248 case ZEND_POW:
1249 case ZEND_SL:
1250 case ZEND_SR:
1251 case ZEND_CONCAT:
1252 case ZEND_FAST_CONCAT:
1253 case ZEND_IS_EQUAL:
1254 case ZEND_IS_NOT_EQUAL:
1255 case ZEND_IS_SMALLER:
1257 case ZEND_IS_IDENTICAL:
1259 case ZEND_BW_OR:
1260 case ZEND_BW_AND:
1261 case ZEND_BW_XOR:
1262 case ZEND_BOOL_XOR:
1263 case ZEND_CASE:
1264 case ZEND_CASE_STRICT:
1267
1268 if (ct_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
1269 SET_RESULT(result, &zv);
1270 zval_ptr_dtor_nogc(&zv);
1271 break;
1272 }
1274 break;
1275 case ZEND_ASSIGN_OP:
1276 case ZEND_ASSIGN_DIM_OP:
1277 case ZEND_ASSIGN_OBJ_OP:
1279 if (op1) {
1281 }
1282 if (op2) {
1284 }
1285 if (opline->opcode == ZEND_ASSIGN_OP) {
1286 if (ct_eval_binary_op(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1287 SET_RESULT(op1, &zv);
1288 SET_RESULT(result, &zv);
1289 zval_ptr_dtor_nogc(&zv);
1290 break;
1291 }
1292 } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
1294 && ssa_op->op1_def >= 0 && op2) {
1295 zval tmp;
1296 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1297
1299
1300 if (ct_eval_fetch_dim(&tmp, op1, op2, 0) == SUCCESS) {
1301 if (IS_BOT(data)) {
1302 dup_partial_array(&zv, op1);
1303 ct_eval_del_array_elem(&zv, op2);
1305 SET_RESULT(op1, &zv);
1306 zval_ptr_dtor_nogc(&tmp);
1307 zval_ptr_dtor_nogc(&zv);
1308 break;
1309 }
1310
1311 if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) == FAILURE) {
1314 zval_ptr_dtor_nogc(&tmp);
1315 break;
1316 }
1317
1318 if (IS_PARTIAL_ARRAY(op1)) {
1319 dup_partial_array(&zv, op1);
1320 } else {
1321 ZVAL_COPY(&zv, op1);
1322 }
1323
1324 if (ct_eval_assign_dim(&zv, &tmp, op2) == SUCCESS) {
1325 SET_RESULT(result, &tmp);
1326 SET_RESULT(op1, &zv);
1327 zval_ptr_dtor_nogc(&tmp);
1328 zval_ptr_dtor_nogc(&zv);
1329 break;
1330 }
1331
1332 zval_ptr_dtor_nogc(&tmp);
1333 zval_ptr_dtor_nogc(&zv);
1334 }
1335 }
1336 } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
1337 if (op1 && IS_PARTIAL_OBJECT(op1)
1338 && ssa_op->op1_def >= 0
1339 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1340 zval tmp;
1341 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1342
1344
1345 if (ct_eval_fetch_obj(&tmp, op1, op2) == SUCCESS) {
1346 if (IS_BOT(data)) {
1347 dup_partial_object(&zv, op1);
1348 ct_eval_del_obj_prop(&zv, op2);
1350 SET_RESULT(op1, &zv);
1351 zval_ptr_dtor_nogc(&tmp);
1352 zval_ptr_dtor_nogc(&zv);
1353 break;
1354 }
1355
1356 if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) == FAILURE) {
1359 zval_ptr_dtor_nogc(&tmp);
1360 break;
1361 }
1362
1363 dup_partial_object(&zv, op1);
1364
1365 if (ct_eval_assign_obj(&zv, &tmp, op2) == SUCCESS) {
1366 SET_RESULT(result, &tmp);
1367 SET_RESULT(op1, &zv);
1368 zval_ptr_dtor_nogc(&tmp);
1369 zval_ptr_dtor_nogc(&zv);
1370 break;
1371 }
1372
1373 zval_ptr_dtor_nogc(&tmp);
1374 zval_ptr_dtor_nogc(&zv);
1375 }
1376 }
1377 }
1380 break;
1381 case ZEND_PRE_INC_OBJ:
1382 case ZEND_PRE_DEC_OBJ:
1383 case ZEND_POST_INC_OBJ:
1384 case ZEND_POST_DEC_OBJ:
1385 if (op1) {
1389 && ssa_op->op1_def >= 0
1390 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1391 zval tmp1, tmp2;
1392
1393 if (ct_eval_fetch_obj(&tmp1, op1, op2) == SUCCESS) {
1394 if (ct_eval_incdec(&tmp2, opline->opcode, &tmp1) == SUCCESS) {
1395 dup_partial_object(&zv, op1);
1396 ct_eval_assign_obj(&zv, &tmp2, op2);
1397 if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) {
1398 SET_RESULT(result, &tmp2);
1399 } else {
1401 }
1402 zval_ptr_dtor_nogc(&tmp1);
1403 zval_ptr_dtor_nogc(&tmp2);
1404 SET_RESULT(op1, &zv);
1405 zval_ptr_dtor_nogc(&zv);
1406 break;
1407 }
1408 zval_ptr_dtor_nogc(&tmp1);
1409 }
1410 }
1411 }
1414 break;
1415 case ZEND_PRE_INC:
1416 case ZEND_PRE_DEC:
1418 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1419 SET_RESULT(op1, &zv);
1420 SET_RESULT(result, &zv);
1421 zval_ptr_dtor_nogc(&zv);
1422 break;
1423 }
1426 break;
1427 case ZEND_POST_INC:
1428 case ZEND_POST_DEC:
1431 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1432 SET_RESULT(op1, &zv);
1433 zval_ptr_dtor_nogc(&zv);
1434 break;
1435 }
1437 break;
1438 case ZEND_BW_NOT:
1439 case ZEND_BOOL_NOT:
1441 if (IS_PARTIAL_ARRAY(op1)) {
1443 break;
1444 }
1445 if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
1446 SET_RESULT(result, &zv);
1447 zval_ptr_dtor_nogc(&zv);
1448 break;
1449 }
1451 break;
1452 case ZEND_CAST:
1454 if (IS_PARTIAL_ARRAY(op1)) {
1456 break;
1457 }
1459 SET_RESULT(result, &zv);
1460 zval_ptr_dtor_nogc(&zv);
1461 break;
1462 }
1464 break;
1465 case ZEND_BOOL:
1466 case ZEND_JMPZ_EX:
1467 case ZEND_JMPNZ_EX:
1469 if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
1470 SET_RESULT(result, &zv);
1471 zval_ptr_dtor_nogc(&zv);
1472 break;
1473 }
1475 break;
1476 case ZEND_STRLEN:
1479 SET_RESULT(result, &zv);
1480 zval_ptr_dtor_nogc(&zv);
1481 break;
1482 }
1484 break;
1485 case ZEND_YIELD_FROM:
1486 // tmp = yield from [] -> tmp = null
1488 if (Z_TYPE_P(op1) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(op1)) == 0) {
1489 ZVAL_NULL(&zv);
1490 SET_RESULT(result, &zv);
1491 break;
1492 }
1494 break;
1495 case ZEND_COUNT:
1497 if (Z_TYPE_P(op1) == IS_ARRAY) {
1498 ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
1499 SET_RESULT(result, &zv);
1500 zval_ptr_dtor_nogc(&zv);
1501 break;
1502 }
1504 break;
1505 case ZEND_IN_ARRAY:
1508 if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1509 SET_RESULT(result, &zv);
1510 zval_ptr_dtor_nogc(&zv);
1511 break;
1512 }
1514 break;
1518 if (ct_eval_array_key_exists(&zv, op1, op2) == SUCCESS) {
1519 SET_RESULT(result, &zv);
1520 zval_ptr_dtor_nogc(&zv);
1521 break;
1522 }
1524 break;
1525 case ZEND_FETCH_DIM_R:
1526 case ZEND_FETCH_DIM_IS:
1527 case ZEND_FETCH_LIST_R:
1530
1531 if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) {
1532 SET_RESULT(result, &zv);
1533 zval_ptr_dtor_nogc(&zv);
1534 break;
1535 }
1537 break;
1541
1542 if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1543 SET_RESULT(result, &zv);
1544 zval_ptr_dtor_nogc(&zv);
1545 break;
1546 }
1548 break;
1549 case ZEND_FETCH_OBJ_R:
1550 case ZEND_FETCH_OBJ_IS:
1551 if (op1) {
1554
1555 if (ct_eval_fetch_obj(&zv, op1, op2) == SUCCESS) {
1556 SET_RESULT(result, &zv);
1557 zval_ptr_dtor_nogc(&zv);
1558 break;
1559 }
1560 }
1562 break;
1564 if (op1) {
1567
1568 if (ct_eval_isset_obj(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1569 SET_RESULT(result, &zv);
1570 zval_ptr_dtor_nogc(&zv);
1571 break;
1572 }
1573 }
1575 break;
1576 case ZEND_QM_ASSIGN:
1577 case ZEND_JMP_SET:
1578 case ZEND_COALESCE:
1579 case ZEND_COPY_TMP:
1581 break;
1582 case ZEND_JMP_NULL:
1585 ZVAL_NULL(&zv);
1586 break;
1588 ZVAL_FALSE(&zv);
1589 break;
1591 ZVAL_TRUE(&zv);
1592 break;
1594 }
1595 SET_RESULT(result, &zv);
1596 break;
1597 case ZEND_FETCH_CLASS:
1599 break;
1602 if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
1603 SET_RESULT(result, &zv);
1604 zval_ptr_dtor_nogc(&zv);
1605 break;
1606 }
1608 break;
1609 case ZEND_TYPE_CHECK:
1611 ct_eval_type_check(&zv, opline->extended_value, op1);
1612 SET_RESULT(result, &zv);
1613 zval_ptr_dtor_nogc(&zv);
1614 break;
1615 case ZEND_INSTANCEOF:
1617 ZVAL_FALSE(&zv);
1618 SET_RESULT(result, &zv);
1619 break;
1620 case ZEND_ROPE_INIT:
1622 if (IS_PARTIAL_ARRAY(op2)) {
1624 break;
1625 }
1627 SET_RESULT(result, &zv);
1628 zval_ptr_dtor_nogc(&zv);
1629 break;
1630 }
1632 break;
1633 case ZEND_ROPE_ADD:
1634 case ZEND_ROPE_END:
1635 // TODO The way this is currently implemented will result in quadratic runtime
1636 // This is not necessary, the way the algorithm works it's okay to reuse the same
1637 // string for all SSA vars with some extra checks
1640 if (ct_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
1641 SET_RESULT(result, &zv);
1642 zval_ptr_dtor_nogc(&zv);
1643 break;
1644 }
1646 break;
1647 case ZEND_DO_ICALL:
1648 {
1650 zval *name, *args[3] = {NULL};
1651 int i;
1652
1653 if (!ctx->call_map) {
1655 break;
1656 }
1657
1658 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1659 name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1660
1661 /* We already know it can't be evaluated, don't bother checking again */
1662 if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
1663 break;
1664 }
1665
1666 /* We're only interested in functions with up to three arguments right now.
1667 * Note that named arguments with the argument in declaration order will still work. */
1668 if (call->num_args > 3 || call->send_unpack || call->is_prototype || call->named_args) {
1670 break;
1671 }
1672
1673 for (i = 0; i < call->num_args; i++) {
1674 zend_op *opline = call->arg_info[i].opline;
1675 if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
1677 return;
1678 }
1679
1680 args[i] = get_op1_value(ctx, opline,
1681 &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1682 if (args[i]) {
1683 if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) {
1685 return;
1686 } else if (IS_TOP(args[i])) {
1687 return;
1688 }
1689 }
1690 }
1691
1692 /* We didn't get a BOT argument, so value stays the same */
1693 if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
1694 break;
1695 }
1696
1697 if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1698 SET_RESULT(result, &zv);
1699 zval_ptr_dtor_nogc(&zv);
1700 break;
1701 }
1702
1703#if 0
1704 /* sort out | uniq -c | sort -n */
1705 fprintf(stderr, "%s\n", Z_STRVAL_P(name));
1706 /*if (args[1]) {
1707 php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
1708 } else {
1709 php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
1710 }*/
1711#endif
1712
1714 break;
1715 }
1720 /* We already know it can't be evaluated, don't bother checking again */
1721 if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
1722 break;
1723 }
1724
1725 zval *args[3] = {NULL};
1726 zend_function *func = ZEND_FLF_FUNC(opline);
1727 uint32_t num_args = ZEND_FLF_NUM_ARGS(opline->opcode);
1728
1729 switch (num_args) {
1730 case 3: {
1731 zend_op *op_data = opline + 1;
1732 args[2] = get_op1_value(ctx, op_data, &ctx->scdf.ssa->ops[op_data - ctx->scdf.op_array->opcodes]);
1734 }
1735 case 2:
1736 args[1] = get_op2_value(ctx, opline, &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1738 case 1:
1739 args[0] = get_op1_value(ctx, opline, &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1740 break;
1741 }
1742 for (uint32_t i = 0; i < num_args; i++) {
1743 if (!args[i]) {
1745 return;
1746 } else if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) {
1748 return;
1749 } else if (IS_TOP(args[i])) {
1750 return;
1751 }
1752 }
1753 if (ct_eval_func_call_ex(scdf->op_array, &zv, func, num_args, args) == SUCCESS) {
1754 SET_RESULT(result, &zv);
1755 zval_ptr_dtor_nogc(&zv);
1756 break;
1757 }
1759 break;
1760 }
1761 default:
1762 {
1763 /* If we have no explicit implementation return BOT */
1767 break;
1768 }
1769 }
1770}
1771
1772static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
1773 zend_ssa *ssa = ctx->scdf.ssa;
1774 zend_ssa_var_info *info = &ssa->var_info[var_num];
1775
1776 if (info->type & MAY_BE_UNDEF) {
1777 return NULL;
1778 }
1779
1780 if (!(info->type & MAY_BE_ANY)) {
1781 /* This code must be unreachable. We could replace operands with NULL, but this doesn't
1782 * really make things better. It would be better to later remove this code entirely. */
1783 return NULL;
1784 }
1785
1786 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
1787 if (ssa->vars[var_num].definition >= 0
1788 && ctx->scdf.op_array->opcodes[ssa->vars[var_num].definition].opcode == ZEND_VERIFY_RETURN_TYPE) {
1789 return NULL;
1790 }
1791 ZVAL_NULL(tmp);
1792 return tmp;
1793 }
1794 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
1795 if (ssa->vars[var_num].definition >= 0
1796 && ctx->scdf.op_array->opcodes[ssa->vars[var_num].definition].opcode == ZEND_VERIFY_RETURN_TYPE) {
1797 return NULL;
1798 }
1799 ZVAL_FALSE(tmp);
1800 return tmp;
1801 }
1802 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
1803 if (ssa->vars[var_num].definition >= 0
1804 && ctx->scdf.op_array->opcodes[ssa->vars[var_num].definition].opcode == ZEND_VERIFY_RETURN_TYPE) {
1805 return NULL;
1806 }
1807 ZVAL_TRUE(tmp);
1808 return tmp;
1809 }
1810
1811 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
1812 && info->has_range
1813 && !info->range.overflow && !info->range.underflow
1814 && info->range.min == info->range.max) {
1815 ZVAL_LONG(tmp, info->range.min);
1816 return tmp;
1817 }
1818
1819 return NULL;
1820}
1821
1822
1823/* Returns whether there is a successor */
1824static void sccp_mark_feasible_successors(
1825 scdf_ctx *scdf,
1826 int block_num, zend_basic_block *block,
1827 zend_op *opline, zend_ssa_op *ssa_op) {
1828 sccp_ctx *ctx = (sccp_ctx *) scdf;
1829 zval *op1, zv;
1830 int s;
1831
1832 /* We can't determine the branch target at compile-time for these */
1833 switch (opline->opcode) {
1834 case ZEND_ASSERT_CHECK:
1835 case ZEND_CATCH:
1836 case ZEND_FE_FETCH_R:
1837 case ZEND_FE_FETCH_RW:
1839 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1840 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1841 return;
1842 }
1843
1844 op1 = get_op1_value(ctx, opline, ssa_op);
1845 if (IS_BOT(op1)) {
1846 ZEND_ASSERT(ssa_op->op1_use >= 0);
1847 op1 = value_from_type_and_range(ctx, ssa_op->op1_use, &zv);
1848 }
1849
1850 /* Branch target can be either one */
1851 if (!op1 || IS_BOT(op1)) {
1852 for (s = 0; s < block->successors_count; s++) {
1853 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1854 }
1855 return;
1856 }
1857
1858 /* Branch target not yet known */
1859 if (IS_TOP(op1)) {
1860 return;
1861 }
1862
1863 switch (opline->opcode) {
1864 case ZEND_JMPZ:
1865 case ZEND_JMPZ_EX:
1866 {
1867 if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
1868 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1869 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1870 return;
1871 }
1872 s = Z_TYPE(zv) == IS_TRUE;
1873 break;
1874 }
1875 case ZEND_JMPNZ:
1876 case ZEND_JMPNZ_EX:
1877 case ZEND_JMP_SET:
1878 {
1879 if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
1880 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1881 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1882 return;
1883 }
1884 s = Z_TYPE(zv) == IS_FALSE;
1885 break;
1886 }
1887 case ZEND_COALESCE:
1888 s = (Z_TYPE_P(op1) == IS_NULL);
1889 break;
1890 case ZEND_JMP_NULL:
1891 s = (Z_TYPE_P(op1) != IS_NULL);
1892 break;
1893 case ZEND_FE_RESET_R:
1894 case ZEND_FE_RESET_RW:
1895 /* A non-empty partial array is definitely non-empty, but an
1896 * empty partial array may be non-empty at runtime. */
1897 if (Z_TYPE_P(op1) != IS_ARRAY ||
1898 (IS_PARTIAL_ARRAY(op1) && zend_hash_num_elements(Z_ARR_P(op1)) == 0)) {
1899 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1900 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1901 return;
1902 }
1903 s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
1904 break;
1905 case ZEND_SWITCH_LONG:
1906 case ZEND_SWITCH_STRING:
1907 case ZEND_MATCH:
1908 {
1909 bool strict_comparison = opline->opcode == ZEND_MATCH;
1910 uint8_t type = Z_TYPE_P(op1);
1911 bool correct_type =
1912 (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
1913 || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
1914 || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
1915
1916 if (correct_type) {
1917 zend_op_array *op_array = scdf->op_array;
1918 zend_ssa *ssa = scdf->ssa;
1919 HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
1920 zval *jmp_zv = type == IS_LONG
1921 ? zend_hash_index_find(jmptable, Z_LVAL_P(op1))
1922 : zend_hash_find(jmptable, Z_STR_P(op1));
1923 int target;
1924
1925 if (jmp_zv) {
1926 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))];
1927 } else {
1928 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
1929 }
1930 scdf_mark_edge_feasible(scdf, block_num, target);
1931 return;
1932 } else if (strict_comparison) {
1933 zend_op_array *op_array = scdf->op_array;
1934 zend_ssa *ssa = scdf->ssa;
1935 int target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
1936 scdf_mark_edge_feasible(scdf, block_num, target);
1937 return;
1938 }
1939 s = block->successors_count - 1;
1940 break;
1941 }
1942 default:
1943 for (s = 0; s < block->successors_count; s++) {
1944 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1945 }
1946 return;
1947 }
1948 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1949}
1950
1951static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
1952{
1953 zend_ulong index;
1955 zval *val1, *val2;
1956
1957 ZEND_HASH_FOREACH_KEY_VAL(ht1, index, key, val1) {
1958 if (key) {
1959 val2 = zend_hash_find(ht2, key);
1960 } else {
1961 val2 = zend_hash_index_find(ht2, index);
1962 }
1963 if (val2 && zend_is_identical(val1, val2)) {
1964 if (key) {
1965 val1 = zend_hash_add_new(ret, key, val1);
1966 } else {
1967 val1 = zend_hash_index_add_new(ret, index, val1);
1968 }
1969 Z_TRY_ADDREF_P(val1);
1970 }
1972}
1973
1974static zend_result join_partial_arrays(zval *a, zval *b)
1975{
1976 zval ret;
1977
1978 if ((Z_TYPE_P(a) != IS_ARRAY && !IS_PARTIAL_ARRAY(a))
1979 || (Z_TYPE_P(b) != IS_ARRAY && !IS_PARTIAL_ARRAY(b))) {
1980 return FAILURE;
1981 }
1982
1983 empty_partial_array(&ret);
1984 join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
1985 zval_ptr_dtor_nogc(a);
1987
1988 return SUCCESS;
1989}
1990
1991static zend_result join_partial_objects(zval *a, zval *b)
1992{
1993 zval ret;
1994
1995 if (!IS_PARTIAL_OBJECT(a) || !IS_PARTIAL_OBJECT(b)) {
1996 return FAILURE;
1997 }
1998
1999 empty_partial_object(&ret);
2000 join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
2001 zval_ptr_dtor_nogc(a);
2003
2004 return SUCCESS;
2005}
2006
2007static void join_phi_values(zval *a, zval *b, bool escape) {
2008 if (IS_BOT(a) || IS_TOP(b)) {
2009 return;
2010 }
2011 if (IS_TOP(a)) {
2012 zval_ptr_dtor_nogc(a);
2013 ZVAL_COPY(a, b);
2014 return;
2015 }
2016 if (IS_BOT(b)) {
2017 zval_ptr_dtor_nogc(a);
2018 MAKE_BOT(a);
2019 return;
2020 }
2021 if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) {
2022 if (join_partial_arrays(a, b) == FAILURE) {
2023 zval_ptr_dtor_nogc(a);
2024 MAKE_BOT(a);
2025 }
2026 } else if (IS_PARTIAL_OBJECT(a) || IS_PARTIAL_OBJECT(b)) {
2027 if (escape || join_partial_objects(a, b) == FAILURE) {
2028 zval_ptr_dtor_nogc(a);
2029 MAKE_BOT(a);
2030 }
2031 } else if (!zend_is_identical(a, b)) {
2032 if (join_partial_arrays(a, b) == FAILURE) {
2033 zval_ptr_dtor_nogc(a);
2034 MAKE_BOT(a);
2035 }
2036 }
2037}
2038
2039static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
2040 sccp_ctx *ctx = (sccp_ctx *) scdf;
2041 zend_ssa *ssa = scdf->ssa;
2042 ZEND_ASSERT(phi->ssa_var >= 0);
2043 if (!IS_BOT(&ctx->values[phi->ssa_var])) {
2044 zend_basic_block *block = &ssa->cfg.blocks[phi->block];
2045 int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
2046
2047 int i;
2048 zval result;
2049 MAKE_TOP(&result);
2050#if SCP_DEBUG
2051 fprintf(stderr, "Handling phi(");
2052#endif
2053 if (phi->pi >= 0) {
2054 ZEND_ASSERT(phi->sources[0] >= 0);
2055 if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
2056 join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
2057 }
2058 } else {
2059 for (i = 0; i < block->predecessors_count; i++) {
2060 ZEND_ASSERT(phi->sources[i] >= 0);
2061 if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
2062#if SCP_DEBUG
2063 scp_dump_value(&ctx->values[phi->sources[i]]);
2064 fprintf(stderr, ",");
2065#endif
2066 join_phi_values(&result, &ctx->values[phi->sources[i]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
2067 } else {
2068#if SCP_DEBUG
2069 fprintf(stderr, " --,");
2070#endif
2071 }
2072 }
2073 }
2074#if SCP_DEBUG
2075 fprintf(stderr, ")\n");
2076#endif
2077
2078 set_value(scdf, ctx, phi->ssa_var, &result);
2079 zval_ptr_dtor_nogc(&result);
2080 }
2081}
2082
2083/* Call instruction -> remove opcodes that are part of the call */
2084static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
2085{
2086 zend_ssa *ssa = ctx->scdf.ssa;
2087 zend_op_array *op_array = ctx->scdf.op_array;
2089 int i;
2090
2091 ZEND_ASSERT(ctx->call_map);
2092 call = ctx->call_map[opline - op_array->opcodes];
2094 ZEND_ASSERT(call->caller_call_opline == opline);
2095 zend_ssa_remove_instr(ssa, opline, ssa_op);
2096 zend_ssa_remove_instr(ssa, call->caller_init_opline,
2097 &ssa->ops[call->caller_init_opline - op_array->opcodes]);
2098
2099 for (i = 0; i < call->num_args; i++) {
2100 zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
2101 &ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
2102 }
2103
2104 // TODO: remove call_info completely???
2105 call->callee_func = NULL;
2106
2107 return call->num_args + 2;
2108}
2109
2110/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
2111 * value(s) were determined by SCCP. It removes dead computational instructions and converts
2112 * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
2113 * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
2114 * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
2115 * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
2116 * and can't be DCEd. This means that it will not be able collect all instructions rendered dead
2117 * by SCCP, because they may have potentially side-effecting types, but the actual values are
2118 * not. As such doing DCE here will allow us to eliminate more dead code in combination.
2119 * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
2120 * we need to collect.
2121 * d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects.
2122 */
2123static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
2124{
2125 zend_ssa *ssa = ctx->scdf.ssa;
2126 zend_op_array *op_array = ctx->scdf.op_array;
2127 int removed_ops = 0;
2128
2129 if (var->definition >= 0) {
2130 zend_op *opline = &op_array->opcodes[var->definition];
2131 zend_ssa_op *ssa_op = &ssa->ops[var->definition];
2132
2133 if (ssa_op->result_def == var_num) {
2134 if (opline->opcode == ZEND_ASSIGN) {
2135 /* We can't drop the ASSIGN, but we can remove the result. */
2136 if (var->use_chain < 0 && var->phi_use_chain == NULL) {
2137 opline->result_type = IS_UNUSED;
2138 zend_ssa_remove_result_def(ssa, ssa_op);
2139 }
2140 return 0;
2141 }
2142 if (ssa_op->op1_def >= 0 || ssa_op->op2_def >= 0) {
2143 if (var->use_chain < 0 && var->phi_use_chain == NULL) {
2144 switch (opline->opcode) {
2145 case ZEND_ASSIGN:
2146 case ZEND_ASSIGN_REF:
2147 case ZEND_ASSIGN_DIM:
2148 case ZEND_ASSIGN_OBJ:
2152 case ZEND_ASSIGN_OP:
2153 case ZEND_ASSIGN_DIM_OP:
2154 case ZEND_ASSIGN_OBJ_OP:
2156 case ZEND_PRE_INC:
2157 case ZEND_PRE_DEC:
2158 case ZEND_PRE_INC_OBJ:
2159 case ZEND_PRE_DEC_OBJ:
2160 case ZEND_DO_ICALL:
2161 case ZEND_DO_UCALL:
2163 case ZEND_DO_FCALL:
2165 case ZEND_YIELD:
2166 case ZEND_YIELD_FROM:
2167 case ZEND_ASSERT_CHECK:
2168 opline->result_type = IS_UNUSED;
2169 zend_ssa_remove_result_def(ssa, ssa_op);
2170 break;
2171 default:
2172 break;
2173 }
2174 }
2175 /* we cannot remove instruction that defines other variables */
2176 return 0;
2177 } else if (opline->opcode == ZEND_JMPZ_EX
2178 || opline->opcode == ZEND_JMPNZ_EX
2179 || opline->opcode == ZEND_JMP_SET
2180 || opline->opcode == ZEND_COALESCE
2181 || opline->opcode == ZEND_JMP_NULL
2182 || opline->opcode == ZEND_FE_RESET_R
2183 || opline->opcode == ZEND_FE_RESET_RW
2184 || opline->opcode == ZEND_FE_FETCH_R
2185 || opline->opcode == ZEND_FE_FETCH_RW
2186 || opline->opcode == ZEND_NEW) {
2187 /* we cannot simple remove jump instructions */
2188 return 0;
2189 } else if (var->use_chain >= 0
2190 || var->phi_use_chain != NULL) {
2191 if (value
2192 && (opline->result_type & (IS_VAR|IS_TMP_VAR))
2193 && opline->opcode != ZEND_QM_ASSIGN
2194 && opline->opcode != ZEND_FETCH_CLASS
2195 && opline->opcode != ZEND_ROPE_INIT
2196 && opline->opcode != ZEND_ROPE_ADD
2197 && opline->opcode != ZEND_INIT_ARRAY
2198 && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
2199 && opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
2200 /* Replace with QM_ASSIGN */
2201 uint8_t old_type = opline->result_type;
2202 uint32_t old_var = opline->result.var;
2203
2204 ssa_op->result_def = -1;
2205 if (opline->opcode == ZEND_DO_ICALL) {
2206 removed_ops = remove_call(ctx, opline, ssa_op) - 1;
2207 } else {
2208 bool has_op_data = opline->opcode == ZEND_FRAMELESS_ICALL_3;
2209 zend_ssa_remove_instr(ssa, opline, ssa_op);
2210 removed_ops++;
2211 if (has_op_data) {
2212 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2213 removed_ops++;
2214 }
2215 }
2216 ssa_op->result_def = var_num;
2217 opline->opcode = ZEND_QM_ASSIGN;
2218 opline->result_type = old_type;
2219 opline->result.var = old_var;
2222 }
2223 return 0;
2224 } else if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
2225 && (!value_known(&ctx->values[ssa_op->op2_use])
2226 || IS_PARTIAL_ARRAY(&ctx->values[ssa_op->op2_use])
2227 || IS_PARTIAL_OBJECT(&ctx->values[ssa_op->op2_use]))) {
2228 return 0;
2229 } else if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
2230 && (!value_known(&ctx->values[ssa_op->op1_use])
2231 || IS_PARTIAL_ARRAY(&ctx->values[ssa_op->op1_use])
2232 || IS_PARTIAL_OBJECT(&ctx->values[ssa_op->op1_use]))) {
2233 if (opline->opcode == ZEND_TYPE_CHECK
2234 || opline->opcode == ZEND_BOOL) {
2235 zend_ssa_remove_result_def(ssa, ssa_op);
2236 /* For TYPE_CHECK we may compute the result value without knowing the
2237 * operand, based on type inference information. Make sure the operand is
2238 * freed and leave further cleanup to DCE. */
2239 opline->opcode = ZEND_FREE;
2240 opline->result_type = IS_UNUSED;
2241 removed_ops++;
2242 } else {
2243 return 0;
2244 }
2245 } else {
2246 zend_ssa_remove_result_def(ssa, ssa_op);
2247 if (opline->opcode == ZEND_DO_ICALL) {
2248 removed_ops = remove_call(ctx, opline, ssa_op);
2249 } else {
2250 bool has_op_data = opline->opcode == ZEND_FRAMELESS_ICALL_3;
2251 zend_ssa_remove_instr(ssa, opline, ssa_op);
2252 removed_ops++;
2253 if (has_op_data) {
2254 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2255 removed_ops++;
2256 }
2257 }
2258 }
2259 } else if (ssa_op->op1_def == var_num) {
2260 if (opline->opcode == ZEND_ASSIGN) {
2261 /* Leave assigns to DCE (due to dtor effects) */
2262 return 0;
2263 }
2264
2265 /* Compound assign or incdec -> convert to direct ASSIGN */
2266
2267 if (!value) {
2268 /* In some cases zend_may_throw() may be avoided */
2269 switch (opline->opcode) {
2270 case ZEND_ASSIGN_DIM:
2271 case ZEND_ASSIGN_OBJ:
2272 case ZEND_ASSIGN_OP:
2273 case ZEND_ASSIGN_DIM_OP:
2274 case ZEND_ASSIGN_OBJ_OP:
2276 if ((ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use]))
2277 || ((ssa_op+1)->op1_use >= 0 &&!value_known(&ctx->values[(ssa_op+1)->op1_use]))) {
2278 return 0;
2279 }
2280 break;
2281 case ZEND_PRE_INC_OBJ:
2282 case ZEND_PRE_DEC_OBJ:
2283 case ZEND_POST_INC_OBJ:
2284 case ZEND_POST_DEC_OBJ:
2285 if (ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) {
2286 return 0;
2287 }
2288 break;
2289 case ZEND_INIT_ARRAY:
2291 if (opline->op2_type == IS_UNUSED) {
2292 return 0;
2293 }
2294 /* break missing intentionally */
2295 default:
2296 if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
2297 return 0;
2298 }
2299 break;
2300 }
2301 }
2302
2303 /* Mark result unused, if possible */
2304 if (ssa_op->result_def >= 0) {
2305 if (ssa->vars[ssa_op->result_def].use_chain < 0
2306 && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
2307 zend_ssa_remove_result_def(ssa, ssa_op);
2308 opline->result_type = IS_UNUSED;
2309 } else if (opline->opcode != ZEND_PRE_INC &&
2310 opline->opcode != ZEND_PRE_DEC) {
2311 /* op1_def and result_def are different */
2312 return removed_ops;
2313 }
2314 }
2315
2316 /* Destroy previous op2 */
2317 if (opline->op2_type == IS_CONST) {
2319 } else if (ssa_op->op2_use >= 0) {
2320 if (ssa_op->op2_use != ssa_op->op1_use) {
2321 zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
2322 }
2323 ssa_op->op2_use = -1;
2324 ssa_op->op2_use_chain = -1;
2325 }
2326
2327 /* Remove OP_DATA opcode */
2328 switch (opline->opcode) {
2329 case ZEND_ASSIGN_DIM:
2330 case ZEND_ASSIGN_OBJ:
2331 removed_ops++;
2332 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2333 break;
2334 case ZEND_ASSIGN_DIM_OP:
2335 case ZEND_ASSIGN_OBJ_OP:
2337 removed_ops++;
2338 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2339 break;
2340 default:
2341 break;
2342 }
2343
2344 if (value) {
2345 /* Convert to ASSIGN */
2346 opline->opcode = ZEND_ASSIGN;
2347 opline->op2_type = IS_CONST;
2348 opline->op2.constant = zend_optimizer_add_literal(op_array, value);
2350 } else {
2351 /* Remove dead array or object construction */
2352 removed_ops++;
2353 if (var->use_chain >= 0 || var->phi_use_chain != NULL) {
2354 zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
2355 }
2356 zend_ssa_remove_op1_def(ssa, ssa_op);
2357 zend_ssa_remove_instr(ssa, opline, ssa_op);
2358 }
2359 }
2360 } else if (var->definition_phi
2361 && var->use_chain < 0
2362 && var->phi_use_chain == NULL) {
2364 }
2365 return removed_ops;
2366}
2367
2368/* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
2369 * can be replaced, because some instructions don't accept constant operands or only accept them
2370 * if they have a certain type. */
2371static int replace_constant_operands(sccp_ctx *ctx) {
2372 zend_ssa *ssa = ctx->scdf.ssa;
2373 zend_op_array *op_array = ctx->scdf.op_array;
2374 int i;
2375 zval tmp;
2376 int removed_ops = 0;
2377
2378 /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
2379 * and INIT_ARRAY. */
2380 for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
2381 zend_ssa_var *var = &ssa->vars[i];
2382 zval *value;
2383 int use;
2384
2385 if (IS_PARTIAL_ARRAY(&ctx->values[i])
2386 || IS_PARTIAL_OBJECT(&ctx->values[i])) {
2387 if (!Z_DELREF(ctx->values[i])) {
2389 }
2390 MAKE_BOT(&ctx->values[i]);
2391 if ((var->use_chain < 0 && var->phi_use_chain == NULL) || var->no_val) {
2392 removed_ops += try_remove_definition(ctx, i, var, NULL);
2393 }
2394 continue;
2395 } else if (value_known(&ctx->values[i])) {
2396 value = &ctx->values[i];
2397 } else {
2398 value = value_from_type_and_range(ctx, i, &tmp);
2399 if (!value) {
2400 continue;
2401 }
2402 }
2403
2404 FOREACH_USE(var, use) {
2405 zend_op *opline = &op_array->opcodes[use];
2406 zend_ssa_op *ssa_op = &ssa->ops[use];
2407 if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
2408 if (opline->opcode == ZEND_NOP) {
2409 removed_ops++;
2410 }
2411 ZEND_ASSERT(ssa_op->op1_def == -1);
2412 if (ssa_op->op1_use != ssa_op->op2_use) {
2413 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
2414 } else {
2415 ssa_op->op2_use_chain = ssa_op->op1_use_chain;
2416 }
2417 ssa_op->op1_use = -1;
2418 ssa_op->op1_use_chain = -1;
2419 }
2420 if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
2421 ZEND_ASSERT(ssa_op->op2_def == -1);
2422 if (ssa_op->op2_use != ssa_op->op1_use) {
2423 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
2424 }
2425 ssa_op->op2_use = -1;
2426 ssa_op->op2_use_chain = -1;
2427 }
2428 } FOREACH_USE_END();
2429
2430 if (value_known(&ctx->values[i])) {
2431 removed_ops += try_remove_definition(ctx, i, var, value);
2432 }
2433 }
2434
2435 return removed_ops;
2436}
2437
2438static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
2439 zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
2440 int i;
2441 sccp->call_map = call_map;
2442 sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
2443
2444 MAKE_TOP(&sccp->top);
2445 MAKE_BOT(&sccp->bot);
2446
2447 i = 0;
2448 for (; i < op_array->last_var; ++i) {
2449 /* These are all undefined variables, which we have to mark BOT.
2450 * Otherwise the undefined variable warning might not be preserved. */
2451 MAKE_BOT(&sccp->values[i]);
2452 }
2453 for (; i < ssa->vars_count; ++i) {
2454 if (ssa->vars[i].alias) {
2455 MAKE_BOT(&sccp->values[i]);
2456 } else {
2457 MAKE_TOP(&sccp->values[i]);
2458 }
2459 }
2460}
2461
2462static void sccp_context_free(sccp_ctx *sccp) {
2463 int i;
2464 for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
2465 zval_ptr_dtor_nogc(&sccp->values[i]);
2466 }
2467}
2468
2470{
2471 sccp_ctx sccp;
2472 int removed_ops = 0;
2473 void *checkpoint = zend_arena_checkpoint(ctx->arena);
2474
2475 sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
2476
2477 sccp.scdf.handlers.visit_instr = sccp_visit_instr;
2478 sccp.scdf.handlers.visit_phi = sccp_visit_phi;
2479 sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
2480
2481 scdf_init(ctx, &sccp.scdf, op_array, ssa);
2482 scdf_solve(&sccp.scdf, "SCCP");
2483
2484 if (ctx->debug_level & ZEND_DUMP_SCCP) {
2485 int i, first = 1;
2486
2487 for (i = op_array->last_var; i < ssa->vars_count; i++) {
2488 zval *zv = &sccp.values[i];
2489
2490 if (IS_TOP(zv) || IS_BOT(zv)) {
2491 continue;
2492 }
2493 if (first) {
2494 first = 0;
2495 fprintf(stderr, "\nSCCP Values for \"");
2496 zend_dump_op_array_name(op_array);
2497 fprintf(stderr, "\":\n");
2498 }
2499 fprintf(stderr, " #%d.", i);
2500 zend_dump_var(op_array, IS_CV, ssa->vars[i].var);
2501 fprintf(stderr, " =");
2502 scp_dump_value(zv);
2503 fprintf(stderr, "\n");
2504 }
2505 }
2506
2507 removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
2508 removed_ops += replace_constant_operands(&sccp);
2509
2510 sccp_context_free(&sccp);
2511 zend_arena_release(&ctx->arena, checkpoint);
2512
2513 return removed_ops;
2514}
bool exception
Definition assert.c:30
fprintf($stream, string $format, mixed ... $values)
char s[4]
Definition cdf.c:77
zend_ffi_type * type
Definition ffi.c:3812
zval * zv
Definition ffi.c:3975
zend_string * res
Definition ffi.c:4692
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
HashTable * ht
Definition ffi.c:4838
zend_ffi_type * old_type
Definition ffi.c:3971
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
unsigned char key[REFLECTION_KEY_LEN]
zend_constant * data
#define MAKE_BOT(zv)
Definition sccp.c:99
#define SET_RESULT(op, zv)
Definition sccp.c:863
int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
Definition sccp.c:2469
#define SET_RESULT_BOT(op)
Definition sccp.c:868
#define MAKE_TOP(zv)
Definition sccp.c:98
#define IS_BOT(zv)
Definition sccp.c:91
#define IS_PARTIAL_ARRAY(zv)
Definition sccp.c:92
#define PARTIAL_ARRAY
Definition sccp.c:88
#define SKIP_IF_TOP(op)
Definition sccp.c:871
#define PARTIAL_OBJECT
Definition sccp.c:89
#define MAKE_PARTIAL_ARRAY(zv)
Definition sccp.c:95
#define IS_PARTIAL_OBJECT(zv)
Definition sccp.c:93
struct _sccp_ctx sccp_ctx
#define MAKE_PARTIAL_OBJECT(zv)
Definition sccp.c:96
#define IS_TOP(zv)
Definition sccp.c:90
void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa)
Definition scdf.c:82
void scdf_solve(scdf_ctx *scdf, const char *name)
Definition scdf.c:103
uint32_t scdf_remove_unreachable_blocks(scdf_ctx *scdf)
Definition scdf.c:259
void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to)
Definition scdf.c:54
struct _scdf_ctx scdf_ctx
#define tmp1
zend_call_info ** call_map
Definition sccp.c:80
zval * values
Definition sccp.c:81
scdf_ctx scdf
Definition sccp.c:79
zval top
Definition sccp.c:82
zval bot
Definition sccp.c:83
void(* visit_phi)(struct _scdf_ctx *scdf, zend_ssa_phi *phi)
Definition scdf.h:41
void(* mark_feasible_successors)(struct _scdf_ctx *scdf, int block_num, zend_basic_block *block, zend_op *opline, zend_ssa_op *ssa_op)
Definition scdf.h:43
zend_op_array * op_array
Definition scdf.h:25
void(* visit_instr)(struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op)
Definition scdf.h:39
zend_ssa * ssa
Definition scdf.h:26
struct _scdf_ctx::@015351274142365124137142230002251013147010033176 handlers
uint32_t * map
Definition zend_cfg.h:89
int * predecessors
Definition zend_cfg.h:88
zend_basic_block * blocks
Definition zend_cfg.h:87
uint32_t ce_flags
Definition zend.h:156
zend_function * func
const zend_op * opline
zend_op * opcodes
uint32_t fn_flags
znode_op op1
uint8_t result_type
znode_op op2
znode_op result
uint8_t opcode
uint8_t op1_type
uint32_t extended_value
uint8_t op2_type
int op2_use_chain
Definition zend_ssa.h:90
int op1_use_chain
Definition zend_ssa.h:89
int result_use
Definition zend_ssa.h:85
int result_def
Definition zend_ssa.h:88
int * sources
Definition zend_ssa.h:73
zend_long max
Definition zend_ssa.h:27
zend_long min
Definition zend_ssa.h:26
zend_ssa_range range
Definition zend_ssa.h:131
zend_class_entry * ce
Definition zend_ssa.h:132
unsigned int alias
Definition zend_ssa.h:117
unsigned int escape_state
Definition zend_ssa.h:118
zend_ssa_phi * definition_phi
Definition zend_ssa.h:112
zend_ssa_phi * phi_use_chain
Definition zend_ssa.h:113
int vars_count
Definition zend_ssa.h:137
zend_ssa_var * vars
Definition zend_ssa.h:141
zend_cfg cfg
Definition zend_ssa.h:136
zend_ssa_var_info * var_info
Definition zend_ssa.h:142
zend_ssa_op * ops
Definition zend_ssa.h:140
$obj a
Definition test.php:84
uint32_t var
uint32_t constant
#define array_init(arg)
Definition zend_API.h:537
#define efree(ptr)
Definition zend_alloc.h:155
#define safe_emalloc(nmemb, size, offset)
Definition zend_alloc.h:154
struct _zval_struct zval
struct _zend_basic_block zend_basic_block
uint32_t num_args
zend_string_release_ex(func->internal_function.function_name, 0)
execute_data func
zval * args
struct _zend_op zend_op
#define EX_VAR_NUM(n)
#define ZEND_SHORT_CIRCUITING_CHAIN_EXPR
#define IS_UNUSED
#define IS_CONST
#define ZEND_ACC_HAS_TYPE_HINTS
#define ZEND_ACC_COMPILE_TIME_EVAL
#define ZEND_INTERNAL_FUNCTION
#define IS_VAR
#define ZEND_OFFSET_TO_OPLINE_NUM(op_array, base, offset)
#define EX_NUM_ARGS()
#define ZEND_CALL_FRAME_SLOT
#define ZEND_SHORT_CIRCUITING_CHAIN_EMPTY
#define EX(element)
struct _zend_op_array zend_op_array
#define ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES
#define ZEND_ARRAY_ELEMENT_REF
#define CT_CONSTANT_EX(op_array, num)
#define ZEND_ISEMPTY
#define IS_CV
#define IS_TMP_VAR
#define ZEND_ACC_RETURN_REFERENCE
#define ZEND_SHORT_CIRCUITING_CHAIN_MASK
#define ZEND_SHORT_CIRCUITING_CHAIN_ISSET
void zend_dump_op_array_name(const zend_op_array *op_array)
Definition zend_dump.c:917
void zend_dump_const(const zval *zv)
Definition zend_dump.c:51
ZEND_API void zend_dump_var(const zend_op_array *op_array, uint8_t var_type, uint32_t var_num)
Definition zend_dump.c:140
void zend_dump_ht(HashTable *ht)
Definition zend_dump.c:28
ZEND_API void zend_clear_exception(void)
union _zend_function zend_function
#define ZEND_FLF_NUM_ARGS(opcode)
#define ZEND_FLF_FUNC(opline)
struct _zend_call_info zend_call_info
#define CG(v)
#define EG(v)
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
Definition zend_hash.c:1224
ZEND_API zval *ZEND_FASTCALL zend_hash_index_add_new(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1214
ZEND_API zval *ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:1007
ZEND_API zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
Definition zend_hash.c:1692
ZEND_API zval *ZEND_FASTCALL zend_hash_index_update(HashTable *ht, zend_ulong h, zval *pData)
Definition zend_hash.c:1219
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
Definition zend_hash.c:1808
ZEND_API HashTable *ZEND_FASTCALL zend_array_dup(HashTable *source)
Definition zend_hash.c:2438
ZEND_API zval *ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:997
ZEND_API zend_result ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key)
Definition zend_hash.c:1534
ZEND_API zval *ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key)
Definition zend_hash.c:2668
ZEND_API zval *ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
Definition zend_hash.c:2701
#define zend_new_array(size)
Definition zend_hash.h:338
#define ZEND_HASH_FOREACH_KEY_VAL(ht, _h, _key, _val)
Definition zend_hash.h:1181
#define ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _key, _val)
Definition zend_hash.h:1166
#define ZEND_HASH_MAP_FOREACH_STR_KEY(ht, _key)
Definition zend_hash.h:1346
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
ZEND_API bool zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, const zend_ssa *ssa)
int32_t zend_long
Definition zend_long.h:42
uint32_t zend_ulong
Definition zend_long.h:43
struct _zend_string zend_string
ZEND_API zend_result ZEND_FASTCALL decrement_function(zval *op1)
ZEND_API bool ZEND_FASTCALL zend_is_identical(const zval *op1, const zval *op2)
ZEND_API int ZEND_FASTCALL zend_compare(zval *op1, zval *op2)
ZEND_API bool ZEND_FASTCALL zend_is_true(const zval *op)
ZEND_API zend_result ZEND_FASTCALL increment_function(zval *op1)
bool zend_optimizer_update_op1_const(zend_op_array *op_array, zend_op *opline, zval *val)
zend_result zend_optimizer_eval_strlen(zval *result, const zval *op1)
int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv)
zend_result zend_optimizer_eval_binary_op(zval *result, uint8_t opcode, zval *op1, zval *op2)
zend_result zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1)
zend_result zend_optimizer_eval_special_func_call(zval *result, zend_string *name, zend_string *arg)
zend_result zend_optimizer_eval_unary_op(zval *result, uint8_t opcode, zval *op1)
bool zend_optimizer_update_op2_const(zend_op_array *op_array, zend_op *opline, zval *val)
#define ZEND_DUMP_SCCP
#define literal_dtor(zv)
#define ZEND_OP2_LITERAL(opline)
struct _zend_optimizer_ctx zend_optimizer_ctx
#define ZEND_FALLTHROUGH
#define EXPECTED(condition)
#define ZEND_ASSERT(c)
#define ZEND_UNREACHABLE()
#define EMPTY_SWITCH_DEFAULT_CASE()
void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op)
Definition zend_ssa.c:1281
void zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var)
Definition zend_ssa.c:1202
void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi)
Definition zend_ssa.c:1410
void zend_ssa_rename_var_uses(zend_ssa *ssa, int old, int new, bool update_types)
Definition zend_ssa.c:1595
#define FOREACH_USE_END()
Definition zend_ssa.h:273
@ ESCAPE_STATE_NO_ESCAPE
Definition zend_ssa.h:102
struct _zend_ssa zend_ssa
struct _zend_ssa_var zend_ssa_var
#define FOREACH_USE(var, use)
Definition zend_ssa.h:269
struct _zend_ssa_phi zend_ssa_phi
Definition zend_ssa.h:62
struct _zend_ssa_op zend_ssa_op
struct _zend_ssa_var_info zend_ssa_var_info
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define zend_string_equals_literal(str, literal)
#define ZSTR_EMPTY_ALLOC()
#define MAY_BE_REF
#define MAY_BE_FALSE
#define MAY_BE_UNDEF
#define MAY_BE_NULL
#define MAY_BE_LONG
#define MAY_BE_TRUE
#define MAY_BE_ANY
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define IS_TRUE
Definition zend_types.h:603
#define ZVAL_STR(z, s)
#define ZVAL_FALSE(z)
#define Z_TRY_ADDREF_P(pz)
#define IS_FALSE
Definition zend_types.h:602
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_TRUE(z)
#define ZVAL_NULL(z)
#define ZVAL_LONG(z, l)
#define IS_STRING
Definition zend_types.h:606
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_ARRAY
Definition zend_types.h:607
#define IS_DOUBLE
Definition zend_types.h:605
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
#define IS_NULL
Definition zend_types.h:601
@ FAILURE
Definition zend_types.h:61
#define IS_OBJECT
Definition zend_types.h:608
#define IS_LONG
Definition zend_types.h:604
#define ZVAL_COPY(z, v)
#define Z_ARR(zval)
Definition zend_types.h:983
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define SEPARATE_ARRAY(zv)
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_DVAL_P(zval_p)
Definition zend_types.h:969
#define Z_ARRVAL(zval)
Definition zend_types.h:986
struct _zend_execute_data zend_execute_data
Definition zend_types.h:91
#define Z_ARR_P(zval_p)
Definition zend_types.h:984
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
#define ZVAL_COPY_VALUE(z, v)
#define ZVAL_BOOL(z, b)
#define Z_DELREF(z)
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
call prev_execute_data
zend_string * name
bool result
op2
op1
execute_data
zval * ret
value
zend_execute_data * call
#define ZEND_SL
#define ZEND_SEND_VAR_EX
#define ZEND_IS_IDENTICAL
#define ZEND_YIELD
#define ZEND_SWITCH_LONG
#define ZEND_ASSIGN_DIM_OP
#define ZEND_ASSIGN_STATIC_PROP_REF
#define ZEND_MAKE_REF
#define ZEND_ISSET_ISEMPTY_CV
#define ZEND_PRE_DEC_OBJ
#define ZEND_BOOL_NOT
#define ZEND_FETCH_OBJ_W
#define ZEND_NEW
#define ZEND_FE_FETCH_RW
#define ZEND_VERIFY_RETURN_TYPE
#define ZEND_FETCH_LIST_R
#define ZEND_INCLUDE_OR_EVAL
#define ZEND_FE_RESET_RW
#define ZEND_FE_FETCH_R
#define ZEND_ROPE_INIT
#define ZEND_FRAMELESS_ICALL_0
#define ZEND_FETCH_LIST_W
#define ZEND_ASSERT_CHECK
#define ZEND_SEND_VAL
#define ZEND_OP_DATA
#define ZEND_SWITCH_STRING
#define ZEND_BOOL_XOR
#define ZEND_ASSIGN_DIM
#define ZEND_DECLARE_CLASS_DELAYED
#define ZEND_INIT_ARRAY
#define ZEND_NOP
#define ZEND_JMPZ
#define ZEND_FETCH_OBJ_FUNC_ARG
#define ZEND_PRE_INC_OBJ
#define ZEND_ASSIGN_REF
#define ZEND_ASSIGN_STATIC_PROP_OP
#define ZEND_FETCH_DIM_FUNC_ARG
#define ZEND_JMP_SET
#define ZEND_POW
#define ZEND_SUB
#define ZEND_JMPZ_EX
#define ZEND_IS_SMALLER
#define ZEND_UNSET_CV
#define ZEND_PRE_INC
#define ZEND_FRAMELESS_ICALL_1
#define ZEND_POST_INC
#define ZEND_PRE_DEC
#define ZEND_SEND_ARRAY
#define ZEND_SEND_UNPACK
#define ZEND_BW_XOR
#define ZEND_IS_NOT_EQUAL
#define ZEND_IS_NOT_IDENTICAL
#define ZEND_FETCH_OBJ_IS
#define ZEND_DO_UCALL
#define ZEND_INSTANCEOF
#define ZEND_FETCH_OBJ_UNSET
#define ZEND_ROPE_ADD
#define ZEND_DIV
#define ZEND_SEND_VAR
#define ZEND_POST_DEC_OBJ
#define ZEND_CONCAT
#define ZEND_FAST_CONCAT
#define ZEND_MATCH
#define ZEND_BW_OR
#define ZEND_ISSET_ISEMPTY_PROP_OBJ
#define ZEND_FRAMELESS_ICALL_3
#define ZEND_JMPNZ_EX
#define ZEND_DO_FCALL
#define ZEND_UNSET_DIM
#define ZEND_ADD_ARRAY_ELEMENT
#define ZEND_IN_ARRAY
#define ZEND_ASSIGN_STATIC_PROP
#define ZEND_IS_EQUAL
#define ZEND_IS_SMALLER_OR_EQUAL
#define ZEND_POST_DEC
#define ZEND_UNSET_OBJ
#define ZEND_BOOL
#define ZEND_YIELD_FROM
#define ZEND_STRLEN
#define ZEND_FRAMELESS_ICALL_2
#define ZEND_MUL
#define ZEND_CASE_STRICT
#define ZEND_ROPE_END
#define ZEND_SEND_USER
#define ZEND_COPY_TMP
#define ZEND_BIND_LEXICAL
#define ZEND_FETCH_CLASS
#define ZEND_DO_ICALL
#define ZEND_SEND_FUNC_ARG
#define ZEND_JMP_NULL
#define ZEND_BW_NOT
#define ZEND_FETCH_OBJ_R
#define ZEND_ISSET_ISEMPTY_DIM_OBJ
#define ZEND_BIND_INIT_STATIC_OR_JMP
#define ZEND_BIND_GLOBAL
#define ZEND_BW_AND
#define ZEND_FETCH_DIM_W
#define ZEND_ADD_ARRAY_UNPACK
#define ZEND_ARRAY_KEY_EXISTS
#define ZEND_FREE
#define ZEND_FE_RESET_R
#define ZEND_ASSIGN_OBJ
#define ZEND_ASSIGN_OBJ_OP
#define ZEND_ASSIGN_OBJ_REF
#define ZEND_ASSIGN
#define ZEND_SR
#define ZEND_COUNT
#define ZEND_FETCH_DIM_IS
#define ZEND_TYPE_CHECK
#define ZEND_FETCH_OBJ_RW
#define ZEND_FETCH_DIM_UNSET
#define ZEND_SEND_REF
#define ZEND_POST_INC_OBJ
#define ZEND_FETCH_DIM_R
#define ZEND_DO_FCALL_BY_NAME
#define ZEND_JMPNZ
#define ZEND_CAST
#define ZEND_QM_ASSIGN
#define ZEND_MOD
#define ZEND_CATCH
#define ZEND_ADD
#define ZEND_COALESCE
#define ZEND_ASSIGN_OP
#define ZEND_BIND_STATIC
#define ZEND_FETCH_DIM_RW
#define ZEND_CASE