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