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