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