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