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