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"
24 #include "Optimizer/zend_optimizer_internal.h"
25 #include "Optimizer/zend_call_graph.h"
26 #include "Optimizer/zend_inference.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
78 typedef struct _sccp_ctx {
79 scdf_ctx scdf;
80 zend_call_info **call_map;
81 zval *values;
82 zval top;
83 zval bot;
84 } sccp_ctx;
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
scp_dump_value(zval * zv)101 static 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 " : "");
108 zend_dump_ht(Z_ARRVAL_P(zv));
109 fprintf(stderr, "]");
110 } else if (IS_PARTIAL_OBJECT(zv)) {
111 fprintf(stderr, " {");
112 zend_dump_ht(Z_ARRVAL_P(zv));
113 fprintf(stderr, "}");
114 } else {
115 zend_dump_const(zv);
116 }
117 }
118
empty_partial_array(zval * zv)119 static void empty_partial_array(zval *zv)
120 {
121 MAKE_PARTIAL_ARRAY(zv);
122 Z_ARR_P(zv) = zend_new_array(8);
123 }
124
dup_partial_array(zval * dst,const zval * src)125 static void dup_partial_array(zval *dst, const zval *src)
126 {
127 MAKE_PARTIAL_ARRAY(dst);
128 Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
129 }
130
empty_partial_object(zval * zv)131 static void empty_partial_object(zval *zv)
132 {
133 MAKE_PARTIAL_OBJECT(zv);
134 Z_ARR_P(zv) = zend_new_array(8);
135 }
136
dup_partial_object(zval * dst,const zval * src)137 static void dup_partial_object(zval *dst, const zval *src)
138 {
139 MAKE_PARTIAL_OBJECT(dst);
140 Z_ARR_P(dst) = zend_array_dup(Z_ARR_P(src));
141 }
142
value_known(zval * zv)143 static 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. */
set_value(scdf_ctx * scdf,sccp_ctx * ctx,int var,const zval * new)149 static 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
184 ZEND_ASSERT(zend_is_identical(value, new) ||
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
get_op1_value(sccp_ctx * ctx,zend_op * opline,const zend_ssa_op * ssa_op)189 static 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
get_op2_value(sccp_ctx * ctx,const zend_op * opline,const zend_ssa_op * ssa_op)199 static 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
can_replace_op1(const zend_op_array * op_array,const zend_op * opline,const zend_ssa_op * ssa_op)209 static 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:
218 case ZEND_POST_INC_OBJ:
219 case ZEND_POST_DEC_OBJ:
220 case ZEND_ASSIGN:
221 case ZEND_ASSIGN_REF:
222 case ZEND_ASSIGN_DIM:
223 case ZEND_ASSIGN_OBJ:
224 case ZEND_ASSIGN_OBJ_REF:
225 case ZEND_ASSIGN_OP:
226 case ZEND_ASSIGN_DIM_OP:
227 case ZEND_ASSIGN_OBJ_OP:
228 case ZEND_ASSIGN_STATIC_PROP_OP:
229 case ZEND_FETCH_DIM_W:
230 case ZEND_FETCH_DIM_RW:
231 case ZEND_FETCH_DIM_UNSET:
232 case ZEND_FETCH_DIM_FUNC_ARG:
233 case ZEND_FETCH_OBJ_W:
234 case ZEND_FETCH_OBJ_RW:
235 case ZEND_FETCH_OBJ_UNSET:
236 case ZEND_FETCH_OBJ_FUNC_ARG:
237 case ZEND_FETCH_LIST_W:
238 case ZEND_UNSET_DIM:
239 case ZEND_UNSET_OBJ:
240 case ZEND_SEND_REF:
241 case ZEND_SEND_VAR_EX:
242 case ZEND_SEND_FUNC_ARG:
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:
252 case ZEND_BIND_INIT_STATIC_OR_JMP:
253 case ZEND_BIND_GLOBAL:
254 case ZEND_MAKE_REF:
255 case ZEND_UNSET_CV:
256 case ZEND_ISSET_ISEMPTY_CV:
257 return 0;
258 case ZEND_INIT_ARRAY:
259 case ZEND_ADD_ARRAY_ELEMENT:
260 return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
261 case ZEND_YIELD:
262 return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
263 case ZEND_VERIFY_RETURN_TYPE:
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) {
271 ZEND_UNREACHABLE();
272 return 0;
273 }
274 }
275
276 return 1;
277 }
278
can_replace_op2(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)279 static 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 */
283 case ZEND_DECLARE_CLASS_DELAYED:
284 case ZEND_BIND_LEXICAL:
285 case ZEND_FE_FETCH_R:
286 case ZEND_FE_FETCH_RW:
287 return 0;
288 }
289 return 1;
290 }
291
try_replace_op1(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)292 static 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
try_replace_op2(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)305 static 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
ct_eval_binary_op(zval * result,uint8_t binop,zval * op1,zval * op2)318 static 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. */
320 if (IS_PARTIAL_ARRAY(op1) || IS_PARTIAL_ARRAY(op2)) {
321 return FAILURE;
322 }
323
324 return zend_optimizer_eval_binary_op(result, binop, op1, op2);
325 }
326
ct_eval_bool_cast(zval * result,zval * op)327 static 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
335 ZVAL_TRUE(result);
336 return SUCCESS;
337 }
338
339 ZVAL_BOOL(result, zend_is_true(op));
340 return SUCCESS;
341 }
342
zval_to_string_offset(zend_long * result,zval * op)343 static 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
fetch_array_elem(zval ** result,zval * op1,zval * op2)359 static inline zend_result fetch_array_elem(zval **result, zval *op1, zval *op2) {
360 switch (Z_TYPE_P(op2)) {
361 case IS_NULL:
362 *result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC());
363 return SUCCESS;
364 case IS_FALSE:
365 *result = zend_hash_index_find(Z_ARR_P(op1), 0);
366 return SUCCESS;
367 case IS_TRUE:
368 *result = zend_hash_index_find(Z_ARR_P(op1), 1);
369 return SUCCESS;
370 case IS_LONG:
371 *result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2));
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 }
378 *result = zend_hash_index_find(Z_ARR_P(op1), lval);
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
ct_eval_fetch_dim(zval * result,zval * op1,zval * op2,int support_strings)389 static inline zend_result ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
390 if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
391 zval *value;
392 if (fetch_array_elem(&value, op1, op2) == SUCCESS && value && !IS_BOT(value)) {
393 ZVAL_COPY(result, 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 */
ct_eval_isset_isempty(zval * result,uint32_t extended_value,zval * op1)410 static inline zend_result ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
411 zval zv;
412 if (!(extended_value & ZEND_ISEMPTY)) {
413 ZVAL_BOOL(result, op1 && Z_TYPE_P(op1) != IS_NULL);
414 return SUCCESS;
415 } else if (!op1) {
416 ZVAL_TRUE(result);
417 return SUCCESS;
418 } else if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
419 ZVAL_BOOL(result, Z_TYPE(zv) == IS_FALSE);
420 return SUCCESS;
421 } else {
422 return FAILURE;
423 }
424 }
425
ct_eval_isset_dim(zval * result,uint32_t extended_value,zval * op1,zval * op2)426 static inline zend_result ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
427 if (Z_TYPE_P(op1) == IS_ARRAY || IS_PARTIAL_ARRAY(op1)) {
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
ct_eval_del_array_elem(zval * result,const zval * key)445 static inline zend_result ct_eval_del_array_elem(zval *result, const zval *key) {
446 ZEND_ASSERT(IS_PARTIAL_ARRAY(result));
447
448 switch (Z_TYPE_P(key)) {
449 case IS_NULL:
450 zend_hash_del(Z_ARR_P(result), ZSTR_EMPTY_ALLOC());
451 break;
452 case IS_FALSE:
453 zend_hash_index_del(Z_ARR_P(result), 0);
454 break;
455 case IS_TRUE:
456 zend_hash_index_del(Z_ARR_P(result), 1);
457 break;
458 case IS_LONG:
459 zend_hash_index_del(Z_ARR_P(result), Z_LVAL_P(key));
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 }
466 zend_hash_index_del(Z_ARR_P(result), lval);
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
ct_eval_add_array_elem(zval * result,zval * value,const zval * key)479 static inline zend_result ct_eval_add_array_elem(zval *result, zval *value, const zval *key) {
480 if (!key) {
481 SEPARATE_ARRAY(result);
482 if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) {
483 Z_TRY_ADDREF_P(value);
484 return SUCCESS;
485 }
486 return FAILURE;
487 }
488
489 switch (Z_TYPE_P(key)) {
490 case IS_NULL:
491 SEPARATE_ARRAY(result);
492 value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value);
493 break;
494 case IS_FALSE:
495 SEPARATE_ARRAY(result);
496 value = zend_hash_index_update(Z_ARR_P(result), 0, value);
497 break;
498 case IS_TRUE:
499 SEPARATE_ARRAY(result);
500 value = zend_hash_index_update(Z_ARR_P(result), 1, value);
501 break;
502 case IS_LONG:
503 SEPARATE_ARRAY(result);
504 value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value);
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 }
511 SEPARATE_ARRAY(result);
512 value = zend_hash_index_update(
513 Z_ARR_P(result), lval, value);
514 break;
515 }
516 case IS_STRING:
517 SEPARATE_ARRAY(result);
518 value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
519 break;
520 default:
521 return FAILURE;
522 }
523
524 Z_TRY_ADDREF_P(value);
525 return SUCCESS;
526 }
527
ct_eval_add_array_unpack(zval * result,zval * array)528 static inline zend_result ct_eval_add_array_unpack(zval *result, zval *array) {
529 zend_string *key;
530 zval *value;
531 if (Z_TYPE_P(array) != IS_ARRAY) {
532 return FAILURE;
533 }
534
535 SEPARATE_ARRAY(result);
536 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(array), key, value) {
537 if (key) {
538 value = zend_hash_update(Z_ARR_P(result), key, value);
539 } else {
540 value = zend_hash_next_index_insert(Z_ARR_P(result), value);
541 }
542 if (!value) {
543 return FAILURE;
544 }
545 Z_TRY_ADDREF_P(value);
546 } ZEND_HASH_FOREACH_END();
547 return SUCCESS;
548 }
549
ct_eval_assign_dim(zval * result,zval * value,const zval * key)550 static 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:
554 array_init(result);
555 ZEND_FALLTHROUGH;
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);
571 memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result));
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
fetch_obj_prop(zval ** result,zval * op1,zval * op2)589 static 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
ct_eval_fetch_obj(zval * result,zval * op1,zval * op2)599 static 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)) {
603 ZVAL_COPY(result, value);
604 return SUCCESS;
605 }
606 }
607 return FAILURE;
608 }
609
ct_eval_isset_obj(zval * result,uint32_t extended_value,zval * op1,zval * op2)610 static 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
ct_eval_del_obj_prop(zval * result,const zval * key)626 static inline zend_result ct_eval_del_obj_prop(zval *result, const zval *key) {
627 ZEND_ASSERT(IS_PARTIAL_OBJECT(result));
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
ct_eval_add_obj_prop(zval * result,zval * value,const zval * key)640 static 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
649 Z_TRY_ADDREF_P(value);
650 return SUCCESS;
651 }
652
ct_eval_assign_obj(zval * result,zval * value,const zval * key)653 static 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);
658 ZEND_FALLTHROUGH;
659 case PARTIAL_OBJECT:
660 return ct_eval_add_obj_prop(result, value, key);
661 default:
662 return FAILURE;
663 }
664 }
665
ct_eval_incdec(zval * result,uint8_t opcode,zval * op1)666 static 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
676 ZVAL_COPY(result, op1);
677 if (opcode == ZEND_PRE_INC
678 || opcode == ZEND_POST_INC
679 || opcode == ZEND_PRE_INC_OBJ
680 || opcode == ZEND_POST_INC_OBJ) {
681 increment_function(result);
682 } else {
683 /* Decrement on null emits a deprecation notice */
684 if (Z_TYPE_P(op1) == IS_NULL) {
685 zval_ptr_dtor(result);
686 return FAILURE;
687 }
688 decrement_function(result);
689 }
690 return SUCCESS;
691 }
692
ct_eval_type_check(zval * result,uint32_t type_mask,zval * op1)693 static 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
ct_eval_in_array(zval * result,uint32_t extended_value,zval * op1,zval * op2)703 static 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 {
722 zend_string *key;
723 zval key_tmp;
724
725 res = 0;
726 ZEND_HASH_MAP_FOREACH_STR_KEY(ht, key) {
727 ZVAL_STR(&key_tmp, key);
728 if (zend_compare(op1, &key_tmp) == 0) {
729 res = 1;
730 break;
731 }
732 } ZEND_HASH_FOREACH_END();
733 }
734 ZVAL_BOOL(result, res);
735 return SUCCESS;
736 }
737
ct_eval_array_key_exists(zval * result,zval * op1,zval * op2)738 static inline zend_result ct_eval_array_key_exists(zval *result, zval *op1, zval *op2) {
739 zval *value;
740
741 if (Z_TYPE_P(op2) != IS_ARRAY && !IS_PARTIAL_ARRAY(op2)) {
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
754 ZVAL_BOOL(result, value != NULL);
755 return SUCCESS;
756 }
757
can_ct_eval_func_call(zend_function * func,zend_string * name,uint32_t num_args,zval ** args)758 static 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). */
ct_eval_func_call(zend_op_array * op_array,zval * result,zend_string * name,uint32_t num_args,zval ** args)792 static inline zend_result ct_eval_func_call(
793 zend_op_array *op_array, zval *result, zend_string *name, uint32_t num_args, zval **args) {
794 uint32_t i;
795 zend_function *func = zend_hash_find_ptr(CG(function_table), name);
796 if (!func || func->type != ZEND_INTERNAL_FUNCTION) {
797 return FAILURE;
798 }
799
800 if (num_args == 1 && Z_TYPE_P(args[0]) == IS_STRING &&
801 zend_optimizer_eval_special_func_call(result, name, Z_STR_P(args[0])) == SUCCESS) {
802 return SUCCESS;
803 }
804
805 if (!can_ct_eval_func_call(func, name, num_args, args)) {
806 return FAILURE;
807 }
808
809 zend_execute_data *prev_execute_data = EG(current_execute_data);
810 zend_execute_data *execute_data, dummy_frame;
811 zend_op dummy_opline;
812
813 /* Add a dummy frame to get the correct strict_types behavior. */
814 memset(&dummy_frame, 0, sizeof(zend_execute_data));
815 memset(&dummy_opline, 0, sizeof(zend_op));
816 dummy_frame.func = (zend_function *) op_array;
817 dummy_frame.opline = &dummy_opline;
818 dummy_opline.opcode = ZEND_DO_FCALL;
819
820 execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
821 memset(execute_data, 0, sizeof(zend_execute_data));
822 execute_data->prev_execute_data = &dummy_frame;
823 EG(current_execute_data) = execute_data;
824
825 /* Enable suppression and counting of warnings. */
826 ZEND_ASSERT(EG(capture_warnings_during_sccp) == 0);
827 EG(capture_warnings_during_sccp) = 1;
828
829 EX(func) = func;
830 EX_NUM_ARGS() = num_args;
831 for (i = 0; i < num_args; i++) {
832 ZVAL_COPY(EX_VAR_NUM(i), args[i]);
833 }
834 ZVAL_NULL(result);
835 func->internal_function.handler(execute_data, result);
836 for (i = 0; i < num_args; i++) {
837 zval_ptr_dtor_nogc(EX_VAR_NUM(i));
838 }
839
840 zend_result retval = SUCCESS;
841 if (EG(exception)) {
842 zval_ptr_dtor(result);
843 zend_clear_exception();
844 retval = FAILURE;
845 }
846
847 if (EG(capture_warnings_during_sccp) > 1) {
848 zval_ptr_dtor(result);
849 retval = FAILURE;
850 }
851 EG(capture_warnings_during_sccp) = 0;
852
853 efree(execute_data);
854 EG(current_execute_data) = prev_execute_data;
855 return retval;
856 }
857
858 #define SET_RESULT(op, zv) do { \
859 if (ssa_op->op##_def >= 0) { \
860 set_value(scdf, ctx, ssa_op->op##_def, zv); \
861 } \
862 } while (0)
863 #define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
864 #define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
865
866 #define SKIP_IF_TOP(op) if (IS_TOP(op)) return;
867
sccp_visit_instr(scdf_ctx * scdf,zend_op * opline,zend_ssa_op * ssa_op)868 static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
869 sccp_ctx *ctx = (sccp_ctx *) scdf;
870 zval *op1, *op2, zv; /* zv is a temporary to hold result values */
871
872 op1 = get_op1_value(ctx, opline, ssa_op);
873 op2 = get_op2_value(ctx, opline, ssa_op);
874
875 switch (opline->opcode) {
876 case ZEND_ASSIGN:
877 /* The value of op1 is irrelevant here, because we are overwriting it
878 * -- unless it can be a reference, in which case we propagate a BOT.
879 * The result is also BOT in this case, because it might be a typed reference. */
880 if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
881 SET_RESULT_BOT(op1);
882 SET_RESULT_BOT(result);
883 } else {
884 SET_RESULT(op1, op2);
885 SET_RESULT(result, op2);
886 }
887 return;
888 case ZEND_ASSIGN_DIM:
889 {
890 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
891
892 /* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */
893 if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) {
894 op1 = &EG(uninitialized_zval);
895 }
896
897 if (IS_BOT(op1)) {
898 SET_RESULT_BOT(result);
899 SET_RESULT_BOT(op1);
900 return;
901 }
902
903 SKIP_IF_TOP(op1);
904 SKIP_IF_TOP(data);
905 if (op2) {
906 SKIP_IF_TOP(op2);
907 }
908
909 if (op2 && IS_BOT(op2)) {
910 /* Update of unknown index */
911 SET_RESULT_BOT(result);
912 if (ssa_op->op1_def >= 0) {
913 empty_partial_array(&zv);
914 SET_RESULT(op1, &zv);
915 zval_ptr_dtor_nogc(&zv);
916 } else {
917 SET_RESULT_BOT(op1);
918 }
919 return;
920 }
921
922 if (IS_BOT(data)) {
923
924 SET_RESULT_BOT(result);
925 if ((IS_PARTIAL_ARRAY(op1)
926 || Z_TYPE_P(op1) == IS_NULL
927 || Z_TYPE_P(op1) == IS_FALSE
928 || Z_TYPE_P(op1) == IS_ARRAY)
929 && ssa_op->op1_def >= 0) {
930
931 if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
932 empty_partial_array(&zv);
933 } else {
934 dup_partial_array(&zv, op1);
935 }
936
937 if (!op2) {
938 /* We can't add NEXT element into partial array (skip it) */
939 SET_RESULT(op1, &zv);
940 } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
941 SET_RESULT(op1, &zv);
942 } else {
943 SET_RESULT_BOT(op1);
944 }
945
946 zval_ptr_dtor_nogc(&zv);
947 } else {
948 SET_RESULT_BOT(op1);
949 }
950
951 } else {
952
953 if (IS_PARTIAL_ARRAY(op1)) {
954 dup_partial_array(&zv, op1);
955 } else {
956 ZVAL_COPY(&zv, op1);
957 }
958
959 if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
960 /* We can't add NEXT element into partial array (skip it) */
961 SET_RESULT(result, data);
962 SET_RESULT(op1, &zv);
963 } else if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
964 /* Mark array containing partial array as partial */
965 if (IS_PARTIAL_ARRAY(data)) {
966 MAKE_PARTIAL_ARRAY(&zv);
967 }
968 SET_RESULT(result, data);
969 SET_RESULT(op1, &zv);
970 } else {
971 SET_RESULT_BOT(result);
972 SET_RESULT_BOT(op1);
973 }
974
975 zval_ptr_dtor_nogc(&zv);
976 }
977 return;
978 }
979
980 case ZEND_ASSIGN_OBJ:
981 if (ssa_op->op1_def >= 0
982 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
983 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
984 zend_ssa_var_info *var_info = &ctx->scdf.ssa->var_info[ssa_op->op1_use];
985
986 /* Don't try to propagate assignments to (potentially) typed properties. We would
987 * need to deal with errors and type conversions first. */
988 // TODO: Distinguish dynamic and declared property assignments here?
989 if (!var_info->ce || (var_info->ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) ||
990 !(var_info->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) {
991 SET_RESULT_BOT(result);
992 SET_RESULT_BOT(op1);
993 return;
994 }
995
996 if (IS_BOT(op1)) {
997 SET_RESULT_BOT(result);
998 SET_RESULT_BOT(op1);
999 return;
1000 }
1001
1002 SKIP_IF_TOP(op1);
1003 SKIP_IF_TOP(data);
1004 SKIP_IF_TOP(op2);
1005
1006 if (IS_BOT(op2)) {
1007 /* Update of unknown property */
1008 SET_RESULT_BOT(result);
1009 empty_partial_object(&zv);
1010 SET_RESULT(op1, &zv);
1011 zval_ptr_dtor_nogc(&zv);
1012 return;
1013 }
1014
1015 if (IS_BOT(data)) {
1016 SET_RESULT_BOT(result);
1017 if (IS_PARTIAL_OBJECT(op1)
1018 || Z_TYPE_P(op1) == IS_NULL
1019 || Z_TYPE_P(op1) == IS_FALSE) {
1020
1021 if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) {
1022 empty_partial_object(&zv);
1023 } else {
1024 dup_partial_object(&zv, op1);
1025 }
1026
1027 if (ct_eval_del_obj_prop(&zv, op2) == SUCCESS) {
1028 SET_RESULT(op1, &zv);
1029 } else {
1030 SET_RESULT_BOT(op1);
1031 }
1032 zval_ptr_dtor_nogc(&zv);
1033 } else {
1034 SET_RESULT_BOT(op1);
1035 }
1036
1037 } else {
1038
1039 if (IS_PARTIAL_OBJECT(op1)) {
1040 dup_partial_object(&zv, op1);
1041 } else {
1042 ZVAL_COPY(&zv, op1);
1043 }
1044
1045 if (ct_eval_assign_obj(&zv, data, op2) == SUCCESS) {
1046 SET_RESULT(result, data);
1047 SET_RESULT(op1, &zv);
1048 } else {
1049 SET_RESULT_BOT(result);
1050 SET_RESULT_BOT(op1);
1051 }
1052
1053 zval_ptr_dtor_nogc(&zv);
1054 }
1055 } else {
1056 SET_RESULT_BOT(result);
1057 SET_RESULT_BOT(op1);
1058 }
1059 return;
1060
1061 case ZEND_SEND_VAL:
1062 case ZEND_SEND_VAR:
1063 {
1064 /* If the value of a SEND for an ICALL changes, we need to reconsider the
1065 * ICALL result value. Otherwise we can ignore the opcode. */
1066 zend_call_info *call;
1067 if (!ctx->call_map) {
1068 return;
1069 }
1070
1071 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1072 if (IS_TOP(op1) || !call || !call->caller_call_opline
1073 || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
1074 return;
1075 }
1076
1077 opline = call->caller_call_opline;
1078 ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
1079 break;
1080 }
1081 case ZEND_INIT_ARRAY:
1082 case ZEND_ADD_ARRAY_ELEMENT:
1083 {
1084 zval *result = NULL;
1085
1086 if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1087 result = &ctx->values[ssa_op->result_use];
1088 if (IS_BOT(result)) {
1089 SET_RESULT_BOT(result);
1090 SET_RESULT_BOT(op1);
1091 return;
1092 }
1093 SKIP_IF_TOP(result);
1094 }
1095
1096 if (op1) {
1097 SKIP_IF_TOP(op1);
1098 }
1099
1100 if (op2) {
1101 SKIP_IF_TOP(op2);
1102 }
1103
1104 /* We want to avoid keeping around intermediate arrays for each SSA variable in the
1105 * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
1106 * and use a NULL value everywhere else. */
1107 if (result && Z_TYPE_P(result) == IS_NULL) {
1108 SET_RESULT_BOT(result);
1109 return;
1110 }
1111
1112 if (op2 && IS_BOT(op2)) {
1113 /* Update of unknown index */
1114 SET_RESULT_BOT(op1);
1115 if (ssa_op->result_def >= 0) {
1116 empty_partial_array(&zv);
1117 SET_RESULT(result, &zv);
1118 zval_ptr_dtor_nogc(&zv);
1119 } else {
1120 SET_RESULT_BOT(result);
1121 }
1122 return;
1123 }
1124
1125 if ((op1 && IS_BOT(op1))
1126 || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF)) {
1127
1128 SET_RESULT_BOT(op1);
1129 if (ssa_op->result_def >= 0) {
1130 if (!result) {
1131 empty_partial_array(&zv);
1132 } else {
1133 MAKE_PARTIAL_ARRAY(result);
1134 ZVAL_COPY_VALUE(&zv, result);
1135 ZVAL_NULL(result);
1136 }
1137 if (!op2) {
1138 /* We can't add NEXT element into partial array (skip it) */
1139 SET_RESULT(result, &zv);
1140 } else if (ct_eval_del_array_elem(&zv, op2) == SUCCESS) {
1141 SET_RESULT(result, &zv);
1142 } else {
1143 SET_RESULT_BOT(result);
1144 }
1145 zval_ptr_dtor_nogc(&zv);
1146 } else {
1147 /* If any operand is BOT, mark the result as BOT right away.
1148 * Exceptions to this rule are handled above. */
1149 SET_RESULT_BOT(result);
1150 }
1151
1152 } else {
1153 if (result) {
1154 ZVAL_COPY_VALUE(&zv, result);
1155 ZVAL_NULL(result);
1156 } else {
1157 array_init(&zv);
1158 }
1159
1160 if (op1) {
1161 if (!op2 && IS_PARTIAL_ARRAY(&zv)) {
1162 /* We can't add NEXT element into partial array (skip it) */
1163 SET_RESULT(result, &zv);
1164 } else if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
1165 if (IS_PARTIAL_ARRAY(op1)) {
1166 MAKE_PARTIAL_ARRAY(&zv);
1167 }
1168 SET_RESULT(result, &zv);
1169 } else {
1170 SET_RESULT_BOT(result);
1171 }
1172 } else {
1173 SET_RESULT(result, &zv);
1174 }
1175
1176 zval_ptr_dtor_nogc(&zv);
1177 }
1178 return;
1179 }
1180 case ZEND_ADD_ARRAY_UNPACK: {
1181 zval *result = &ctx->values[ssa_op->result_use];
1182 if (IS_BOT(result) || IS_BOT(op1)) {
1183 SET_RESULT_BOT(result);
1184 return;
1185 }
1186 SKIP_IF_TOP(result);
1187 SKIP_IF_TOP(op1);
1188
1189 /* See comment for ADD_ARRAY_ELEMENT. */
1190 if (Z_TYPE_P(result) == IS_NULL) {
1191 SET_RESULT_BOT(result);
1192 return;
1193 }
1194 ZVAL_COPY_VALUE(&zv, result);
1195 ZVAL_NULL(result);
1196
1197 if (ct_eval_add_array_unpack(&zv, op1) == SUCCESS) {
1198 SET_RESULT(result, &zv);
1199 } else {
1200 SET_RESULT_BOT(result);
1201 }
1202 zval_ptr_dtor_nogc(&zv);
1203 return;
1204 }
1205 case ZEND_NEW:
1206 if (ssa_op->result_def >= 0
1207 && ctx->scdf.ssa->vars[ssa_op->result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1208 empty_partial_object(&zv);
1209 SET_RESULT(result, &zv);
1210 zval_ptr_dtor_nogc(&zv);
1211 } else {
1212 SET_RESULT_BOT(result);
1213 }
1214 return;
1215 case ZEND_ASSIGN_STATIC_PROP_REF:
1216 case ZEND_ASSIGN_OBJ_REF:
1217 /* Handled here because we also need to BOT the OP_DATA operand, while the generic
1218 * code below will not do so. */
1219 SET_RESULT_BOT(result);
1220 SET_RESULT_BOT(op1);
1221 SET_RESULT_BOT(op2);
1222 opline++;
1223 ssa_op++;
1224 SET_RESULT_BOT(op1);
1225 break;
1226 }
1227
1228 if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
1229 /* If any operand is BOT, mark the result as BOT right away.
1230 * Exceptions to this rule are handled above. */
1231 SET_RESULT_BOT(result);
1232 SET_RESULT_BOT(op1);
1233 SET_RESULT_BOT(op2);
1234 return;
1235 }
1236
1237 switch (opline->opcode) {
1238 case ZEND_ADD:
1239 case ZEND_SUB:
1240 case ZEND_MUL:
1241 case ZEND_DIV:
1242 case ZEND_MOD:
1243 case ZEND_POW:
1244 case ZEND_SL:
1245 case ZEND_SR:
1246 case ZEND_CONCAT:
1247 case ZEND_FAST_CONCAT:
1248 case ZEND_IS_EQUAL:
1249 case ZEND_IS_NOT_EQUAL:
1250 case ZEND_IS_SMALLER:
1251 case ZEND_IS_SMALLER_OR_EQUAL:
1252 case ZEND_IS_IDENTICAL:
1253 case ZEND_IS_NOT_IDENTICAL:
1254 case ZEND_BW_OR:
1255 case ZEND_BW_AND:
1256 case ZEND_BW_XOR:
1257 case ZEND_BOOL_XOR:
1258 case ZEND_CASE:
1259 case ZEND_CASE_STRICT:
1260 SKIP_IF_TOP(op1);
1261 SKIP_IF_TOP(op2);
1262
1263 if (ct_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
1264 SET_RESULT(result, &zv);
1265 zval_ptr_dtor_nogc(&zv);
1266 break;
1267 }
1268 SET_RESULT_BOT(result);
1269 break;
1270 case ZEND_ASSIGN_OP:
1271 case ZEND_ASSIGN_DIM_OP:
1272 case ZEND_ASSIGN_OBJ_OP:
1273 case ZEND_ASSIGN_STATIC_PROP_OP:
1274 if (op1) {
1275 SKIP_IF_TOP(op1);
1276 }
1277 if (op2) {
1278 SKIP_IF_TOP(op2);
1279 }
1280 if (opline->opcode == ZEND_ASSIGN_OP) {
1281 if (ct_eval_binary_op(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1282 SET_RESULT(op1, &zv);
1283 SET_RESULT(result, &zv);
1284 zval_ptr_dtor_nogc(&zv);
1285 break;
1286 }
1287 } else if (opline->opcode == ZEND_ASSIGN_DIM_OP) {
1288 if ((IS_PARTIAL_ARRAY(op1) || Z_TYPE_P(op1) == IS_ARRAY)
1289 && ssa_op->op1_def >= 0 && op2) {
1290 zval tmp;
1291 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1292
1293 SKIP_IF_TOP(data);
1294
1295 if (ct_eval_fetch_dim(&tmp, op1, op2, 0) == SUCCESS) {
1296 if (IS_BOT(data)) {
1297 dup_partial_array(&zv, op1);
1298 ct_eval_del_array_elem(&zv, op2);
1299 SET_RESULT_BOT(result);
1300 SET_RESULT(op1, &zv);
1301 zval_ptr_dtor_nogc(&tmp);
1302 zval_ptr_dtor_nogc(&zv);
1303 break;
1304 }
1305
1306 if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) == FAILURE) {
1307 SET_RESULT_BOT(result);
1308 SET_RESULT_BOT(op1);
1309 zval_ptr_dtor_nogc(&tmp);
1310 break;
1311 }
1312
1313 if (IS_PARTIAL_ARRAY(op1)) {
1314 dup_partial_array(&zv, op1);
1315 } else {
1316 ZVAL_COPY(&zv, op1);
1317 }
1318
1319 if (ct_eval_assign_dim(&zv, &tmp, op2) == SUCCESS) {
1320 SET_RESULT(result, &tmp);
1321 SET_RESULT(op1, &zv);
1322 zval_ptr_dtor_nogc(&tmp);
1323 zval_ptr_dtor_nogc(&zv);
1324 break;
1325 }
1326
1327 zval_ptr_dtor_nogc(&tmp);
1328 zval_ptr_dtor_nogc(&zv);
1329 }
1330 }
1331 } else if (opline->opcode == ZEND_ASSIGN_OBJ_OP) {
1332 if (op1 && IS_PARTIAL_OBJECT(op1)
1333 && ssa_op->op1_def >= 0
1334 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1335 zval tmp;
1336 zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1337
1338 SKIP_IF_TOP(data);
1339
1340 if (ct_eval_fetch_obj(&tmp, op1, op2) == SUCCESS) {
1341 if (IS_BOT(data)) {
1342 dup_partial_object(&zv, op1);
1343 ct_eval_del_obj_prop(&zv, op2);
1344 SET_RESULT_BOT(result);
1345 SET_RESULT(op1, &zv);
1346 zval_ptr_dtor_nogc(&tmp);
1347 zval_ptr_dtor_nogc(&zv);
1348 break;
1349 }
1350
1351 if (ct_eval_binary_op(&tmp, opline->extended_value, &tmp, data) == FAILURE) {
1352 SET_RESULT_BOT(result);
1353 SET_RESULT_BOT(op1);
1354 zval_ptr_dtor_nogc(&tmp);
1355 break;
1356 }
1357
1358 dup_partial_object(&zv, op1);
1359
1360 if (ct_eval_assign_obj(&zv, &tmp, op2) == SUCCESS) {
1361 SET_RESULT(result, &tmp);
1362 SET_RESULT(op1, &zv);
1363 zval_ptr_dtor_nogc(&tmp);
1364 zval_ptr_dtor_nogc(&zv);
1365 break;
1366 }
1367
1368 zval_ptr_dtor_nogc(&tmp);
1369 zval_ptr_dtor_nogc(&zv);
1370 }
1371 }
1372 }
1373 SET_RESULT_BOT(result);
1374 SET_RESULT_BOT(op1);
1375 break;
1376 case ZEND_PRE_INC_OBJ:
1377 case ZEND_PRE_DEC_OBJ:
1378 case ZEND_POST_INC_OBJ:
1379 case ZEND_POST_DEC_OBJ:
1380 if (op1) {
1381 SKIP_IF_TOP(op1);
1382 SKIP_IF_TOP(op2);
1383 if (IS_PARTIAL_OBJECT(op1)
1384 && ssa_op->op1_def >= 0
1385 && ctx->scdf.ssa->vars[ssa_op->op1_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
1386 zval tmp1, tmp2;
1387
1388 if (ct_eval_fetch_obj(&tmp1, op1, op2) == SUCCESS) {
1389 if (ct_eval_incdec(&tmp2, opline->opcode, &tmp1) == SUCCESS) {
1390 dup_partial_object(&zv, op1);
1391 ct_eval_assign_obj(&zv, &tmp2, op2);
1392 if (opline->opcode == ZEND_PRE_INC_OBJ || opline->opcode == ZEND_PRE_DEC_OBJ) {
1393 SET_RESULT(result, &tmp2);
1394 } else {
1395 SET_RESULT(result, &tmp1);
1396 }
1397 zval_ptr_dtor_nogc(&tmp1);
1398 zval_ptr_dtor_nogc(&tmp2);
1399 SET_RESULT(op1, &zv);
1400 zval_ptr_dtor_nogc(&zv);
1401 break;
1402 }
1403 zval_ptr_dtor_nogc(&tmp1);
1404 }
1405 }
1406 }
1407 SET_RESULT_BOT(op1);
1408 SET_RESULT_BOT(result);
1409 break;
1410 case ZEND_PRE_INC:
1411 case ZEND_PRE_DEC:
1412 SKIP_IF_TOP(op1);
1413 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1414 SET_RESULT(op1, &zv);
1415 SET_RESULT(result, &zv);
1416 zval_ptr_dtor_nogc(&zv);
1417 break;
1418 }
1419 SET_RESULT_BOT(op1);
1420 SET_RESULT_BOT(result);
1421 break;
1422 case ZEND_POST_INC:
1423 case ZEND_POST_DEC:
1424 SKIP_IF_TOP(op1);
1425 SET_RESULT(result, op1);
1426 if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1427 SET_RESULT(op1, &zv);
1428 zval_ptr_dtor_nogc(&zv);
1429 break;
1430 }
1431 SET_RESULT_BOT(op1);
1432 break;
1433 case ZEND_BW_NOT:
1434 case ZEND_BOOL_NOT:
1435 SKIP_IF_TOP(op1);
1436 if (IS_PARTIAL_ARRAY(op1)) {
1437 SET_RESULT_BOT(result);
1438 break;
1439 }
1440 if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
1441 SET_RESULT(result, &zv);
1442 zval_ptr_dtor_nogc(&zv);
1443 break;
1444 }
1445 SET_RESULT_BOT(result);
1446 break;
1447 case ZEND_CAST:
1448 SKIP_IF_TOP(op1);
1449 if (IS_PARTIAL_ARRAY(op1)) {
1450 SET_RESULT_BOT(result);
1451 break;
1452 }
1453 if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) {
1454 SET_RESULT(result, &zv);
1455 zval_ptr_dtor_nogc(&zv);
1456 break;
1457 }
1458 SET_RESULT_BOT(result);
1459 break;
1460 case ZEND_BOOL:
1461 case ZEND_JMPZ_EX:
1462 case ZEND_JMPNZ_EX:
1463 SKIP_IF_TOP(op1);
1464 if (ct_eval_bool_cast(&zv, op1) == SUCCESS) {
1465 SET_RESULT(result, &zv);
1466 zval_ptr_dtor_nogc(&zv);
1467 break;
1468 }
1469 SET_RESULT_BOT(result);
1470 break;
1471 case ZEND_STRLEN:
1472 SKIP_IF_TOP(op1);
1473 if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) {
1474 SET_RESULT(result, &zv);
1475 zval_ptr_dtor_nogc(&zv);
1476 break;
1477 }
1478 SET_RESULT_BOT(result);
1479 break;
1480 case ZEND_YIELD_FROM:
1481 // tmp = yield from [] -> tmp = null
1482 SKIP_IF_TOP(op1);
1483 if (Z_TYPE_P(op1) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(op1)) == 0) {
1484 ZVAL_NULL(&zv);
1485 SET_RESULT(result, &zv);
1486 break;
1487 }
1488 SET_RESULT_BOT(result);
1489 break;
1490 case ZEND_COUNT:
1491 SKIP_IF_TOP(op1);
1492 if (Z_TYPE_P(op1) == IS_ARRAY) {
1493 ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
1494 SET_RESULT(result, &zv);
1495 zval_ptr_dtor_nogc(&zv);
1496 break;
1497 }
1498 SET_RESULT_BOT(result);
1499 break;
1500 case ZEND_IN_ARRAY:
1501 SKIP_IF_TOP(op1);
1502 SKIP_IF_TOP(op2);
1503 if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1504 SET_RESULT(result, &zv);
1505 zval_ptr_dtor_nogc(&zv);
1506 break;
1507 }
1508 SET_RESULT_BOT(result);
1509 break;
1510 case ZEND_ARRAY_KEY_EXISTS:
1511 SKIP_IF_TOP(op1);
1512 SKIP_IF_TOP(op2);
1513 if (ct_eval_array_key_exists(&zv, op1, op2) == SUCCESS) {
1514 SET_RESULT(result, &zv);
1515 zval_ptr_dtor_nogc(&zv);
1516 break;
1517 }
1518 SET_RESULT_BOT(result);
1519 break;
1520 case ZEND_FETCH_DIM_R:
1521 case ZEND_FETCH_DIM_IS:
1522 case ZEND_FETCH_LIST_R:
1523 SKIP_IF_TOP(op1);
1524 SKIP_IF_TOP(op2);
1525
1526 if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST_R)) == SUCCESS) {
1527 SET_RESULT(result, &zv);
1528 zval_ptr_dtor_nogc(&zv);
1529 break;
1530 }
1531 SET_RESULT_BOT(result);
1532 break;
1533 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
1534 SKIP_IF_TOP(op1);
1535 SKIP_IF_TOP(op2);
1536
1537 if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1538 SET_RESULT(result, &zv);
1539 zval_ptr_dtor_nogc(&zv);
1540 break;
1541 }
1542 SET_RESULT_BOT(result);
1543 break;
1544 case ZEND_FETCH_OBJ_R:
1545 case ZEND_FETCH_OBJ_IS:
1546 if (op1) {
1547 SKIP_IF_TOP(op1);
1548 SKIP_IF_TOP(op2);
1549
1550 if (ct_eval_fetch_obj(&zv, op1, op2) == SUCCESS) {
1551 SET_RESULT(result, &zv);
1552 zval_ptr_dtor_nogc(&zv);
1553 break;
1554 }
1555 }
1556 SET_RESULT_BOT(result);
1557 break;
1558 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
1559 if (op1) {
1560 SKIP_IF_TOP(op1);
1561 SKIP_IF_TOP(op2);
1562
1563 if (ct_eval_isset_obj(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1564 SET_RESULT(result, &zv);
1565 zval_ptr_dtor_nogc(&zv);
1566 break;
1567 }
1568 }
1569 SET_RESULT_BOT(result);
1570 break;
1571 case ZEND_QM_ASSIGN:
1572 case ZEND_JMP_SET:
1573 case ZEND_COALESCE:
1574 case ZEND_COPY_TMP:
1575 SET_RESULT(result, op1);
1576 break;
1577 case ZEND_JMP_NULL:
1578 switch (opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK) {
1579 case ZEND_SHORT_CIRCUITING_CHAIN_EXPR:
1580 ZVAL_NULL(&zv);
1581 break;
1582 case ZEND_SHORT_CIRCUITING_CHAIN_ISSET:
1583 ZVAL_FALSE(&zv);
1584 break;
1585 case ZEND_SHORT_CIRCUITING_CHAIN_EMPTY:
1586 ZVAL_TRUE(&zv);
1587 break;
1588 EMPTY_SWITCH_DEFAULT_CASE()
1589 }
1590 SET_RESULT(result, &zv);
1591 break;
1592 case ZEND_FETCH_CLASS:
1593 SET_RESULT(result, op2);
1594 break;
1595 case ZEND_ISSET_ISEMPTY_CV:
1596 SKIP_IF_TOP(op1);
1597 if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
1598 SET_RESULT(result, &zv);
1599 zval_ptr_dtor_nogc(&zv);
1600 break;
1601 }
1602 SET_RESULT_BOT(result);
1603 break;
1604 case ZEND_TYPE_CHECK:
1605 SKIP_IF_TOP(op1);
1606 ct_eval_type_check(&zv, opline->extended_value, op1);
1607 SET_RESULT(result, &zv);
1608 zval_ptr_dtor_nogc(&zv);
1609 break;
1610 case ZEND_INSTANCEOF:
1611 SKIP_IF_TOP(op1);
1612 ZVAL_FALSE(&zv);
1613 SET_RESULT(result, &zv);
1614 break;
1615 case ZEND_ROPE_INIT:
1616 SKIP_IF_TOP(op2);
1617 if (IS_PARTIAL_ARRAY(op2)) {
1618 SET_RESULT_BOT(result);
1619 break;
1620 }
1621 if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) {
1622 SET_RESULT(result, &zv);
1623 zval_ptr_dtor_nogc(&zv);
1624 break;
1625 }
1626 SET_RESULT_BOT(result);
1627 break;
1628 case ZEND_ROPE_ADD:
1629 case ZEND_ROPE_END:
1630 // TODO The way this is currently implemented will result in quadratic runtime
1631 // This is not necessary, the way the algorithm works it's okay to reuse the same
1632 // string for all SSA vars with some extra checks
1633 SKIP_IF_TOP(op1);
1634 SKIP_IF_TOP(op2);
1635 if (ct_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
1636 SET_RESULT(result, &zv);
1637 zval_ptr_dtor_nogc(&zv);
1638 break;
1639 }
1640 SET_RESULT_BOT(result);
1641 break;
1642 case ZEND_DO_ICALL:
1643 {
1644 zend_call_info *call;
1645 zval *name, *args[3] = {NULL};
1646 int i;
1647
1648 if (!ctx->call_map) {
1649 SET_RESULT_BOT(result);
1650 break;
1651 }
1652
1653 call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1654 name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1655
1656 /* We already know it can't be evaluated, don't bother checking again */
1657 if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
1658 break;
1659 }
1660
1661 /* We're only interested in functions with up to three arguments right now.
1662 * Note that named arguments with the argument in declaration order will still work. */
1663 if (call->num_args > 3 || call->send_unpack || call->is_prototype || call->named_args) {
1664 SET_RESULT_BOT(result);
1665 break;
1666 }
1667
1668 for (i = 0; i < call->num_args; i++) {
1669 zend_op *opline = call->arg_info[i].opline;
1670 if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
1671 SET_RESULT_BOT(result);
1672 return;
1673 }
1674
1675 args[i] = get_op1_value(ctx, opline,
1676 &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1677 if (args[i]) {
1678 if (IS_BOT(args[i]) || IS_PARTIAL_ARRAY(args[i])) {
1679 SET_RESULT_BOT(result);
1680 return;
1681 } else if (IS_TOP(args[i])) {
1682 return;
1683 }
1684 }
1685 }
1686
1687 /* We didn't get a BOT argument, so value stays the same */
1688 if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
1689 break;
1690 }
1691
1692 if (ct_eval_func_call(scdf->op_array, &zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1693 SET_RESULT(result, &zv);
1694 zval_ptr_dtor_nogc(&zv);
1695 break;
1696 }
1697
1698 #if 0
1699 /* sort out | uniq -c | sort -n */
1700 fprintf(stderr, "%s\n", Z_STRVAL_P(name));
1701 /*if (args[1]) {
1702 php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
1703 } else {
1704 php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
1705 }*/
1706 #endif
1707
1708 SET_RESULT_BOT(result);
1709 break;
1710 }
1711 default:
1712 {
1713 /* If we have no explicit implementation return BOT */
1714 SET_RESULT_BOT(result);
1715 SET_RESULT_BOT(op1);
1716 SET_RESULT_BOT(op2);
1717 break;
1718 }
1719 }
1720 }
1721
value_from_type_and_range(sccp_ctx * ctx,int var_num,zval * tmp)1722 static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
1723 zend_ssa *ssa = ctx->scdf.ssa;
1724 zend_ssa_var_info *info = &ssa->var_info[var_num];
1725
1726 if (info->type & MAY_BE_UNDEF) {
1727 return NULL;
1728 }
1729
1730 if (!(info->type & MAY_BE_ANY)) {
1731 /* This code must be unreachable. We could replace operands with NULL, but this doesn't
1732 * really make things better. It would be better to later remove this code entirely. */
1733 return NULL;
1734 }
1735
1736 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
1737 if (ssa->vars[var_num].definition >= 0
1738 && ctx->scdf.op_array->opcodes[ssa->vars[var_num].definition].opcode == ZEND_VERIFY_RETURN_TYPE) {
1739 return NULL;
1740 }
1741 ZVAL_NULL(tmp);
1742 return tmp;
1743 }
1744 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
1745 if (ssa->vars[var_num].definition >= 0
1746 && ctx->scdf.op_array->opcodes[ssa->vars[var_num].definition].opcode == ZEND_VERIFY_RETURN_TYPE) {
1747 return NULL;
1748 }
1749 ZVAL_FALSE(tmp);
1750 return tmp;
1751 }
1752 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
1753 if (ssa->vars[var_num].definition >= 0
1754 && ctx->scdf.op_array->opcodes[ssa->vars[var_num].definition].opcode == ZEND_VERIFY_RETURN_TYPE) {
1755 return NULL;
1756 }
1757 ZVAL_TRUE(tmp);
1758 return tmp;
1759 }
1760
1761 if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
1762 && info->has_range
1763 && !info->range.overflow && !info->range.underflow
1764 && info->range.min == info->range.max) {
1765 ZVAL_LONG(tmp, info->range.min);
1766 return tmp;
1767 }
1768
1769 return NULL;
1770 }
1771
1772
1773 /* Returns whether there is a successor */
sccp_mark_feasible_successors(scdf_ctx * scdf,int block_num,zend_basic_block * block,zend_op * opline,zend_ssa_op * ssa_op)1774 static void sccp_mark_feasible_successors(
1775 scdf_ctx *scdf,
1776 int block_num, zend_basic_block *block,
1777 zend_op *opline, zend_ssa_op *ssa_op) {
1778 sccp_ctx *ctx = (sccp_ctx *) scdf;
1779 zval *op1, zv;
1780 int s;
1781
1782 /* We can't determine the branch target at compile-time for these */
1783 switch (opline->opcode) {
1784 case ZEND_ASSERT_CHECK:
1785 case ZEND_CATCH:
1786 case ZEND_FE_FETCH_R:
1787 case ZEND_FE_FETCH_RW:
1788 case ZEND_BIND_INIT_STATIC_OR_JMP:
1789 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1790 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1791 return;
1792 }
1793
1794 op1 = get_op1_value(ctx, opline, ssa_op);
1795 if (IS_BOT(op1)) {
1796 ZEND_ASSERT(ssa_op->op1_use >= 0);
1797 op1 = value_from_type_and_range(ctx, ssa_op->op1_use, &zv);
1798 }
1799
1800 /* Branch target can be either one */
1801 if (!op1 || IS_BOT(op1)) {
1802 for (s = 0; s < block->successors_count; s++) {
1803 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1804 }
1805 return;
1806 }
1807
1808 /* Branch target not yet known */
1809 if (IS_TOP(op1)) {
1810 return;
1811 }
1812
1813 switch (opline->opcode) {
1814 case ZEND_JMPZ:
1815 case ZEND_JMPZ_EX:
1816 {
1817 if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
1818 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1819 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1820 return;
1821 }
1822 s = Z_TYPE(zv) == IS_TRUE;
1823 break;
1824 }
1825 case ZEND_JMPNZ:
1826 case ZEND_JMPNZ_EX:
1827 case ZEND_JMP_SET:
1828 {
1829 if (ct_eval_bool_cast(&zv, op1) == FAILURE) {
1830 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1831 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1832 return;
1833 }
1834 s = Z_TYPE(zv) == IS_FALSE;
1835 break;
1836 }
1837 case ZEND_COALESCE:
1838 s = (Z_TYPE_P(op1) == IS_NULL);
1839 break;
1840 case ZEND_JMP_NULL:
1841 s = (Z_TYPE_P(op1) != IS_NULL);
1842 break;
1843 case ZEND_FE_RESET_R:
1844 case ZEND_FE_RESET_RW:
1845 /* A non-empty partial array is definitely non-empty, but an
1846 * empty partial array may be non-empty at runtime. */
1847 if (Z_TYPE_P(op1) != IS_ARRAY ||
1848 (IS_PARTIAL_ARRAY(op1) && zend_hash_num_elements(Z_ARR_P(op1)) == 0)) {
1849 scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1850 scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1851 return;
1852 }
1853 s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
1854 break;
1855 case ZEND_SWITCH_LONG:
1856 case ZEND_SWITCH_STRING:
1857 case ZEND_MATCH:
1858 {
1859 bool strict_comparison = opline->opcode == ZEND_MATCH;
1860 uint8_t type = Z_TYPE_P(op1);
1861 bool correct_type =
1862 (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
1863 || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
1864 || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
1865
1866 if (correct_type) {
1867 zend_op_array *op_array = scdf->op_array;
1868 zend_ssa *ssa = scdf->ssa;
1869 HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
1870 zval *jmp_zv = type == IS_LONG
1871 ? zend_hash_index_find(jmptable, Z_LVAL_P(op1))
1872 : zend_hash_find(jmptable, Z_STR_P(op1));
1873 int target;
1874
1875 if (jmp_zv) {
1876 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))];
1877 } else {
1878 target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
1879 }
1880 scdf_mark_edge_feasible(scdf, block_num, target);
1881 return;
1882 } else if (strict_comparison) {
1883 zend_op_array *op_array = scdf->op_array;
1884 zend_ssa *ssa = scdf->ssa;
1885 int target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
1886 scdf_mark_edge_feasible(scdf, block_num, target);
1887 return;
1888 }
1889 s = block->successors_count - 1;
1890 break;
1891 }
1892 default:
1893 for (s = 0; s < block->successors_count; s++) {
1894 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1895 }
1896 return;
1897 }
1898 scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1899 }
1900
join_hash_tables(HashTable * ret,HashTable * ht1,HashTable * ht2)1901 static void join_hash_tables(HashTable *ret, HashTable *ht1, HashTable *ht2)
1902 {
1903 zend_ulong index;
1904 zend_string *key;
1905 zval *val1, *val2;
1906
1907 ZEND_HASH_FOREACH_KEY_VAL(ht1, index, key, val1) {
1908 if (key) {
1909 val2 = zend_hash_find(ht2, key);
1910 } else {
1911 val2 = zend_hash_index_find(ht2, index);
1912 }
1913 if (val2 && zend_is_identical(val1, val2)) {
1914 if (key) {
1915 val1 = zend_hash_add_new(ret, key, val1);
1916 } else {
1917 val1 = zend_hash_index_add_new(ret, index, val1);
1918 }
1919 Z_TRY_ADDREF_P(val1);
1920 }
1921 } ZEND_HASH_FOREACH_END();
1922 }
1923
join_partial_arrays(zval * a,zval * b)1924 static zend_result join_partial_arrays(zval *a, zval *b)
1925 {
1926 zval ret;
1927
1928 if ((Z_TYPE_P(a) != IS_ARRAY && !IS_PARTIAL_ARRAY(a))
1929 || (Z_TYPE_P(b) != IS_ARRAY && !IS_PARTIAL_ARRAY(b))) {
1930 return FAILURE;
1931 }
1932
1933 empty_partial_array(&ret);
1934 join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
1935 zval_ptr_dtor_nogc(a);
1936 ZVAL_COPY_VALUE(a, &ret);
1937
1938 return SUCCESS;
1939 }
1940
join_partial_objects(zval * a,zval * b)1941 static zend_result join_partial_objects(zval *a, zval *b)
1942 {
1943 zval ret;
1944
1945 if (!IS_PARTIAL_OBJECT(a) || !IS_PARTIAL_OBJECT(b)) {
1946 return FAILURE;
1947 }
1948
1949 empty_partial_object(&ret);
1950 join_hash_tables(Z_ARRVAL(ret), Z_ARRVAL_P(a), Z_ARRVAL_P(b));
1951 zval_ptr_dtor_nogc(a);
1952 ZVAL_COPY_VALUE(a, &ret);
1953
1954 return SUCCESS;
1955 }
1956
join_phi_values(zval * a,zval * b,bool escape)1957 static void join_phi_values(zval *a, zval *b, bool escape) {
1958 if (IS_BOT(a) || IS_TOP(b)) {
1959 return;
1960 }
1961 if (IS_TOP(a)) {
1962 zval_ptr_dtor_nogc(a);
1963 ZVAL_COPY(a, b);
1964 return;
1965 }
1966 if (IS_BOT(b)) {
1967 zval_ptr_dtor_nogc(a);
1968 MAKE_BOT(a);
1969 return;
1970 }
1971 if (IS_PARTIAL_ARRAY(a) || IS_PARTIAL_ARRAY(b)) {
1972 if (join_partial_arrays(a, b) == FAILURE) {
1973 zval_ptr_dtor_nogc(a);
1974 MAKE_BOT(a);
1975 }
1976 } else if (IS_PARTIAL_OBJECT(a) || IS_PARTIAL_OBJECT(b)) {
1977 if (escape || join_partial_objects(a, b) == FAILURE) {
1978 zval_ptr_dtor_nogc(a);
1979 MAKE_BOT(a);
1980 }
1981 } else if (!zend_is_identical(a, b)) {
1982 if (join_partial_arrays(a, b) == FAILURE) {
1983 zval_ptr_dtor_nogc(a);
1984 MAKE_BOT(a);
1985 }
1986 }
1987 }
1988
sccp_visit_phi(scdf_ctx * scdf,zend_ssa_phi * phi)1989 static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
1990 sccp_ctx *ctx = (sccp_ctx *) scdf;
1991 zend_ssa *ssa = scdf->ssa;
1992 ZEND_ASSERT(phi->ssa_var >= 0);
1993 if (!IS_BOT(&ctx->values[phi->ssa_var])) {
1994 zend_basic_block *block = &ssa->cfg.blocks[phi->block];
1995 int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
1996
1997 int i;
1998 zval result;
1999 MAKE_TOP(&result);
2000 #if SCP_DEBUG
2001 fprintf(stderr, "Handling phi(");
2002 #endif
2003 if (phi->pi >= 0) {
2004 ZEND_ASSERT(phi->sources[0] >= 0);
2005 if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
2006 join_phi_values(&result, &ctx->values[phi->sources[0]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
2007 }
2008 } else {
2009 for (i = 0; i < block->predecessors_count; i++) {
2010 ZEND_ASSERT(phi->sources[i] >= 0);
2011 if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
2012 #if SCP_DEBUG
2013 scp_dump_value(&ctx->values[phi->sources[i]]);
2014 fprintf(stderr, ",");
2015 #endif
2016 join_phi_values(&result, &ctx->values[phi->sources[i]], ssa->vars[phi->ssa_var].escape_state != ESCAPE_STATE_NO_ESCAPE);
2017 } else {
2018 #if SCP_DEBUG
2019 fprintf(stderr, " --,");
2020 #endif
2021 }
2022 }
2023 }
2024 #if SCP_DEBUG
2025 fprintf(stderr, ")\n");
2026 #endif
2027
2028 set_value(scdf, ctx, phi->ssa_var, &result);
2029 zval_ptr_dtor_nogc(&result);
2030 }
2031 }
2032
2033 /* Call instruction -> remove opcodes that are part of the call */
remove_call(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)2034 static int remove_call(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op)
2035 {
2036 zend_ssa *ssa = ctx->scdf.ssa;
2037 zend_op_array *op_array = ctx->scdf.op_array;
2038 zend_call_info *call;
2039 int i;
2040
2041 ZEND_ASSERT(ctx->call_map);
2042 call = ctx->call_map[opline - op_array->opcodes];
2043 ZEND_ASSERT(call);
2044 ZEND_ASSERT(call->caller_call_opline == opline);
2045 zend_ssa_remove_instr(ssa, opline, ssa_op);
2046 zend_ssa_remove_instr(ssa, call->caller_init_opline,
2047 &ssa->ops[call->caller_init_opline - op_array->opcodes]);
2048
2049 for (i = 0; i < call->num_args; i++) {
2050 zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
2051 &ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
2052 }
2053
2054 // TODO: remove call_info completely???
2055 call->callee_func = NULL;
2056
2057 return call->num_args + 2;
2058 }
2059
2060 /* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
2061 * value(s) were determined by SCCP. It removes dead computational instructions and converts
2062 * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
2063 * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
2064 * must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
2065 * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
2066 * and can't be DCEd. This means that it will not be able collect all instructions rendered dead
2067 * by SCCP, because they may have potentially side-effecting types, but the actual values are
2068 * not. As such doing DCE here will allow us to eliminate more dead code in combination.
2069 * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
2070 * we need to collect.
2071 * d) The ordinary DCE pass cannot collect construction of dead non-escaping arrays and objects.
2072 */
try_remove_definition(sccp_ctx * ctx,int var_num,zend_ssa_var * var,zval * value)2073 static int try_remove_definition(sccp_ctx *ctx, int var_num, zend_ssa_var *var, zval *value)
2074 {
2075 zend_ssa *ssa = ctx->scdf.ssa;
2076 zend_op_array *op_array = ctx->scdf.op_array;
2077 int removed_ops = 0;
2078
2079 if (var->definition >= 0) {
2080 zend_op *opline = &op_array->opcodes[var->definition];
2081 zend_ssa_op *ssa_op = &ssa->ops[var->definition];
2082
2083 if (ssa_op->result_def == var_num) {
2084 if (opline->opcode == ZEND_ASSIGN) {
2085 /* We can't drop the ASSIGN, but we can remove the result. */
2086 if (var->use_chain < 0 && var->phi_use_chain == NULL) {
2087 opline->result_type = IS_UNUSED;
2088 zend_ssa_remove_result_def(ssa, ssa_op);
2089 }
2090 return 0;
2091 }
2092 if (ssa_op->op1_def >= 0 || ssa_op->op2_def >= 0) {
2093 if (var->use_chain < 0 && var->phi_use_chain == NULL) {
2094 switch (opline->opcode) {
2095 case ZEND_ASSIGN:
2096 case ZEND_ASSIGN_REF:
2097 case ZEND_ASSIGN_DIM:
2098 case ZEND_ASSIGN_OBJ:
2099 case ZEND_ASSIGN_OBJ_REF:
2100 case ZEND_ASSIGN_STATIC_PROP:
2101 case ZEND_ASSIGN_STATIC_PROP_REF:
2102 case ZEND_ASSIGN_OP:
2103 case ZEND_ASSIGN_DIM_OP:
2104 case ZEND_ASSIGN_OBJ_OP:
2105 case ZEND_ASSIGN_STATIC_PROP_OP:
2106 case ZEND_PRE_INC:
2107 case ZEND_PRE_DEC:
2108 case ZEND_PRE_INC_OBJ:
2109 case ZEND_PRE_DEC_OBJ:
2110 case ZEND_DO_ICALL:
2111 case ZEND_DO_UCALL:
2112 case ZEND_DO_FCALL_BY_NAME:
2113 case ZEND_DO_FCALL:
2114 case ZEND_INCLUDE_OR_EVAL:
2115 case ZEND_YIELD:
2116 case ZEND_YIELD_FROM:
2117 case ZEND_ASSERT_CHECK:
2118 opline->result_type = IS_UNUSED;
2119 zend_ssa_remove_result_def(ssa, ssa_op);
2120 break;
2121 default:
2122 break;
2123 }
2124 }
2125 /* we cannot remove instruction that defines other variables */
2126 return 0;
2127 } else if (opline->opcode == ZEND_JMPZ_EX
2128 || opline->opcode == ZEND_JMPNZ_EX
2129 || opline->opcode == ZEND_JMP_SET
2130 || opline->opcode == ZEND_COALESCE
2131 || opline->opcode == ZEND_JMP_NULL
2132 || opline->opcode == ZEND_FE_RESET_R
2133 || opline->opcode == ZEND_FE_RESET_RW
2134 || opline->opcode == ZEND_FE_FETCH_R
2135 || opline->opcode == ZEND_FE_FETCH_RW
2136 || opline->opcode == ZEND_NEW) {
2137 /* we cannot simple remove jump instructions */
2138 return 0;
2139 } else if (var->use_chain >= 0
2140 || var->phi_use_chain != NULL) {
2141 if (value
2142 && (opline->result_type & (IS_VAR|IS_TMP_VAR))
2143 && opline->opcode != ZEND_QM_ASSIGN
2144 && opline->opcode != ZEND_FETCH_CLASS
2145 && opline->opcode != ZEND_ROPE_INIT
2146 && opline->opcode != ZEND_ROPE_ADD
2147 && opline->opcode != ZEND_INIT_ARRAY
2148 && opline->opcode != ZEND_ADD_ARRAY_ELEMENT
2149 && opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
2150 /* Replace with QM_ASSIGN */
2151 uint8_t old_type = opline->result_type;
2152 uint32_t old_var = opline->result.var;
2153
2154 ssa_op->result_def = -1;
2155 if (opline->opcode == ZEND_DO_ICALL) {
2156 removed_ops = remove_call(ctx, opline, ssa_op) - 1;
2157 } else {
2158 zend_ssa_remove_instr(ssa, opline, ssa_op);
2159 }
2160 ssa_op->result_def = var_num;
2161 opline->opcode = ZEND_QM_ASSIGN;
2162 opline->result_type = old_type;
2163 opline->result.var = old_var;
2164 Z_TRY_ADDREF_P(value);
2165 zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, value);
2166 }
2167 return 0;
2168 } else if ((opline->op2_type & (IS_VAR|IS_TMP_VAR))
2169 && (!value_known(&ctx->values[ssa_op->op2_use])
2170 || IS_PARTIAL_ARRAY(&ctx->values[ssa_op->op2_use])
2171 || IS_PARTIAL_OBJECT(&ctx->values[ssa_op->op2_use]))) {
2172 return 0;
2173 } else if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
2174 && (!value_known(&ctx->values[ssa_op->op1_use])
2175 || IS_PARTIAL_ARRAY(&ctx->values[ssa_op->op1_use])
2176 || IS_PARTIAL_OBJECT(&ctx->values[ssa_op->op1_use]))) {
2177 if (opline->opcode == ZEND_TYPE_CHECK
2178 || opline->opcode == ZEND_BOOL) {
2179 zend_ssa_remove_result_def(ssa, ssa_op);
2180 /* For TYPE_CHECK we may compute the result value without knowing the
2181 * operand, based on type inference information. Make sure the operand is
2182 * freed and leave further cleanup to DCE. */
2183 opline->opcode = ZEND_FREE;
2184 opline->result_type = IS_UNUSED;
2185 removed_ops++;
2186 } else {
2187 return 0;
2188 }
2189 } else {
2190 zend_ssa_remove_result_def(ssa, ssa_op);
2191 if (opline->opcode == ZEND_DO_ICALL) {
2192 removed_ops = remove_call(ctx, opline, ssa_op);
2193 } else {
2194 zend_ssa_remove_instr(ssa, opline, ssa_op);
2195 removed_ops++;
2196 }
2197 }
2198 } else if (ssa_op->op1_def == var_num) {
2199 if (opline->opcode == ZEND_ASSIGN) {
2200 /* Leave assigns to DCE (due to dtor effects) */
2201 return 0;
2202 }
2203
2204 /* Compound assign or incdec -> convert to direct ASSIGN */
2205
2206 if (!value) {
2207 /* In some cases zend_may_throw() may be avoided */
2208 switch (opline->opcode) {
2209 case ZEND_ASSIGN_DIM:
2210 case ZEND_ASSIGN_OBJ:
2211 case ZEND_ASSIGN_OP:
2212 case ZEND_ASSIGN_DIM_OP:
2213 case ZEND_ASSIGN_OBJ_OP:
2214 case ZEND_ASSIGN_STATIC_PROP_OP:
2215 if ((ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use]))
2216 || ((ssa_op+1)->op1_use >= 0 &&!value_known(&ctx->values[(ssa_op+1)->op1_use]))) {
2217 return 0;
2218 }
2219 break;
2220 case ZEND_PRE_INC_OBJ:
2221 case ZEND_PRE_DEC_OBJ:
2222 case ZEND_POST_INC_OBJ:
2223 case ZEND_POST_DEC_OBJ:
2224 if (ssa_op->op2_use >= 0 && !value_known(&ctx->values[ssa_op->op2_use])) {
2225 return 0;
2226 }
2227 break;
2228 case ZEND_INIT_ARRAY:
2229 case ZEND_ADD_ARRAY_ELEMENT:
2230 if (opline->op2_type == IS_UNUSED) {
2231 return 0;
2232 }
2233 /* break missing intentionally */
2234 default:
2235 if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
2236 return 0;
2237 }
2238 break;
2239 }
2240 }
2241
2242 /* Mark result unused, if possible */
2243 if (ssa_op->result_def >= 0) {
2244 if (ssa->vars[ssa_op->result_def].use_chain < 0
2245 && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
2246 zend_ssa_remove_result_def(ssa, ssa_op);
2247 opline->result_type = IS_UNUSED;
2248 } else if (opline->opcode != ZEND_PRE_INC &&
2249 opline->opcode != ZEND_PRE_DEC) {
2250 /* op1_def and result_def are different */
2251 return removed_ops;
2252 }
2253 }
2254
2255 /* Destroy previous op2 */
2256 if (opline->op2_type == IS_CONST) {
2257 literal_dtor(&ZEND_OP2_LITERAL(opline));
2258 } else if (ssa_op->op2_use >= 0) {
2259 if (ssa_op->op2_use != ssa_op->op1_use) {
2260 zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
2261 }
2262 ssa_op->op2_use = -1;
2263 ssa_op->op2_use_chain = -1;
2264 }
2265
2266 /* Remove OP_DATA opcode */
2267 switch (opline->opcode) {
2268 case ZEND_ASSIGN_DIM:
2269 case ZEND_ASSIGN_OBJ:
2270 removed_ops++;
2271 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2272 break;
2273 case ZEND_ASSIGN_DIM_OP:
2274 case ZEND_ASSIGN_OBJ_OP:
2275 case ZEND_ASSIGN_STATIC_PROP_OP:
2276 removed_ops++;
2277 zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
2278 break;
2279 default:
2280 break;
2281 }
2282
2283 if (value) {
2284 /* Convert to ASSIGN */
2285 opline->opcode = ZEND_ASSIGN;
2286 opline->op2_type = IS_CONST;
2287 opline->op2.constant = zend_optimizer_add_literal(op_array, value);
2288 Z_TRY_ADDREF_P(value);
2289 } else {
2290 /* Remove dead array or object construction */
2291 removed_ops++;
2292 if (var->use_chain >= 0 || var->phi_use_chain != NULL) {
2293 zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
2294 }
2295 zend_ssa_remove_op1_def(ssa, ssa_op);
2296 zend_ssa_remove_instr(ssa, opline, ssa_op);
2297 }
2298 }
2299 } else if (var->definition_phi
2300 && var->use_chain < 0
2301 && var->phi_use_chain == NULL) {
2302 zend_ssa_remove_phi(ssa, var->definition_phi);
2303 }
2304 return removed_ops;
2305 }
2306
2307 /* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
2308 * can be replaced, because some instructions don't accept constant operands or only accept them
2309 * if they have a certain type. */
replace_constant_operands(sccp_ctx * ctx)2310 static int replace_constant_operands(sccp_ctx *ctx) {
2311 zend_ssa *ssa = ctx->scdf.ssa;
2312 zend_op_array *op_array = ctx->scdf.op_array;
2313 int i;
2314 zval tmp;
2315 int removed_ops = 0;
2316
2317 /* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
2318 * and INIT_ARRAY. */
2319 for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
2320 zend_ssa_var *var = &ssa->vars[i];
2321 zval *value;
2322 int use;
2323
2324 if (IS_PARTIAL_ARRAY(&ctx->values[i])
2325 || IS_PARTIAL_OBJECT(&ctx->values[i])) {
2326 if (!Z_DELREF(ctx->values[i])) {
2327 zend_array_destroy(Z_ARR(ctx->values[i]));
2328 }
2329 MAKE_BOT(&ctx->values[i]);
2330 if ((var->use_chain < 0 && var->phi_use_chain == NULL) || var->no_val) {
2331 removed_ops += try_remove_definition(ctx, i, var, NULL);
2332 }
2333 continue;
2334 } else if (value_known(&ctx->values[i])) {
2335 value = &ctx->values[i];
2336 } else {
2337 value = value_from_type_and_range(ctx, i, &tmp);
2338 if (!value) {
2339 continue;
2340 }
2341 }
2342
2343 FOREACH_USE(var, use) {
2344 zend_op *opline = &op_array->opcodes[use];
2345 zend_ssa_op *ssa_op = &ssa->ops[use];
2346 if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
2347 if (opline->opcode == ZEND_NOP) {
2348 removed_ops++;
2349 }
2350 ZEND_ASSERT(ssa_op->op1_def == -1);
2351 if (ssa_op->op1_use != ssa_op->op2_use) {
2352 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
2353 } else {
2354 ssa_op->op2_use_chain = ssa_op->op1_use_chain;
2355 }
2356 ssa_op->op1_use = -1;
2357 ssa_op->op1_use_chain = -1;
2358 }
2359 if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
2360 ZEND_ASSERT(ssa_op->op2_def == -1);
2361 if (ssa_op->op2_use != ssa_op->op1_use) {
2362 zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
2363 }
2364 ssa_op->op2_use = -1;
2365 ssa_op->op2_use_chain = -1;
2366 }
2367 } FOREACH_USE_END();
2368
2369 if (value_known(&ctx->values[i])) {
2370 removed_ops += try_remove_definition(ctx, i, var, value);
2371 }
2372 }
2373
2374 return removed_ops;
2375 }
2376
sccp_context_init(zend_optimizer_ctx * ctx,sccp_ctx * sccp,zend_ssa * ssa,zend_op_array * op_array,zend_call_info ** call_map)2377 static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
2378 zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
2379 int i;
2380 sccp->call_map = call_map;
2381 sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
2382
2383 MAKE_TOP(&sccp->top);
2384 MAKE_BOT(&sccp->bot);
2385
2386 i = 0;
2387 for (; i < op_array->last_var; ++i) {
2388 /* These are all undefined variables, which we have to mark BOT.
2389 * Otherwise the undefined variable warning might not be preserved. */
2390 MAKE_BOT(&sccp->values[i]);
2391 }
2392 for (; i < ssa->vars_count; ++i) {
2393 if (ssa->vars[i].alias) {
2394 MAKE_BOT(&sccp->values[i]);
2395 } else {
2396 MAKE_TOP(&sccp->values[i]);
2397 }
2398 }
2399 }
2400
sccp_context_free(sccp_ctx * sccp)2401 static void sccp_context_free(sccp_ctx *sccp) {
2402 int i;
2403 for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
2404 zval_ptr_dtor_nogc(&sccp->values[i]);
2405 }
2406 }
2407
sccp_optimize_op_array(zend_optimizer_ctx * ctx,zend_op_array * op_array,zend_ssa * ssa,zend_call_info ** call_map)2408 int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
2409 {
2410 sccp_ctx sccp;
2411 int removed_ops = 0;
2412 void *checkpoint = zend_arena_checkpoint(ctx->arena);
2413
2414 sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
2415
2416 sccp.scdf.handlers.visit_instr = sccp_visit_instr;
2417 sccp.scdf.handlers.visit_phi = sccp_visit_phi;
2418 sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
2419
2420 scdf_init(ctx, &sccp.scdf, op_array, ssa);
2421 scdf_solve(&sccp.scdf, "SCCP");
2422
2423 if (ctx->debug_level & ZEND_DUMP_SCCP) {
2424 int i, first = 1;
2425
2426 for (i = op_array->last_var; i < ssa->vars_count; i++) {
2427 zval *zv = &sccp.values[i];
2428
2429 if (IS_TOP(zv) || IS_BOT(zv)) {
2430 continue;
2431 }
2432 if (first) {
2433 first = 0;
2434 fprintf(stderr, "\nSCCP Values for \"");
2435 zend_dump_op_array_name(op_array);
2436 fprintf(stderr, "\":\n");
2437 }
2438 fprintf(stderr, " #%d.", i);
2439 zend_dump_var(op_array, IS_CV, ssa->vars[i].var);
2440 fprintf(stderr, " =");
2441 scp_dump_value(zv);
2442 fprintf(stderr, "\n");
2443 }
2444 }
2445
2446 removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
2447 removed_ops += replace_constant_operands(&sccp);
2448
2449 sccp_context_free(&sccp);
2450 zend_arena_release(&ctx->arena, checkpoint);
2451
2452 return removed_ops;
2453 }
2454