xref: /PHP-7.2/ext/opcache/Optimizer/sccp.c (revision 3f00c936)
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    +----------------------------------------------------------------------+
17 */
18 
19 #include "php.h"
20 #include "zend_type_info.h"
21 #include "ZendAccelerator.h"
22 #include "Optimizer/zend_optimizer_internal.h"
23 #include "Optimizer/zend_call_graph.h"
24 #include "Optimizer/scdf.h"
25 #include "Optimizer/zend_dump.h"
26 #include "ext/standard/php_string.h"
27 
28 /* This implements sparse conditional constant propagation (SCCP) based on the SCDF framework. The
29  * used value lattice is defined as follows:
30  *
31  * BOT < {constant values} < TOP
32  *
33  * TOP indicates an underdefined value, i.e. that we do not yet know the value of variable.
34  * BOT indicates an overdefined value, i.e. that we know the variable to be non-constant.
35  *
36  * All variables are optimistically initialized to TOP, apart from the implicit variables defined
37  * at the start of the first block. Note that variables that MAY_BE_REF are *not* initialized to
38  * BOT. We rely on the fact that any operation resulting in a reference will produce a BOT anyway.
39  * This is better because such operations might never be reached due to the conditional nature of
40  * the algorithm.
41  *
42  * The meet operation for phi functions is defined as follows:
43  * BOT + any = BOT
44  * TOP + any = any
45  * C_i + C_i = C_i (i.e. two equal constants)
46  * C_i + C_j = BOT (i.e. two different constants)
47  *
48  * When evaluating instructions TOP and BOT are handled as follows:
49  * a) If any operand is BOT, the result is BOT. The main exception to this is op1 of ASSIGN, which
50  *    is ignored. However, if the op1 MAY_BE_REF we do have to propagate the BOT.
51  * b) Otherwise, if the instruction can never be evaluated (either in general, or with the
52  *    specific modifiers) the result is BOT.
53  * c) Otherwise, if any operand is TOP, the result is TOP.
54  * d) Otherwise (at this point all operands are known and constant), if we can compute the result
55  *    for these specific constants (without throwing notices or similar) then that is the result.
56  * e) Otherwise the result is BOT.
57  *
58  * It is sometimes possible to determine a result even if one argument is TOP / BOT, e.g. for things
59  * like BOT*0. Right now we don't bother with this -- the only thing that is done is evaluating
60  * TYPE_CHECKS based on the type information.
61  *
62  * Feasible successors for conditional branches are determined as follows:
63  * a) If we don't support the branch type or branch on BOT, all successors are feasible.
64  * b) Otherwise, if we branch on TOP none of the successors are feasible.
65  * c) Otherwise (we branch on a constant), the feasible successors are marked based on the constant
66  *    (usually only one successor will be feasible).
67  */
68 
69 #if 0
70 #define SCP_DEBUG(...) php_printf(__VA_ARGS__)
71 #else
72 #define SCP_DEBUG(...)
73 #endif
74 
75 typedef struct _sccp_ctx {
76 	scdf_ctx scdf;
77 	zend_call_info **call_map;
78 	zval *values;
79 	zval top;
80 	zval bot;
81 } sccp_ctx;
82 
83 #define TOP ((zend_uchar)-1)
84 #define BOT ((zend_uchar)-2)
85 #define IS_TOP(zv) (Z_TYPE_P(zv) == TOP)
86 #define IS_BOT(zv) (Z_TYPE_P(zv) == BOT)
87 
88 #define MAKE_TOP(zv) (Z_TYPE_INFO_P(zv) = TOP)
89 #define MAKE_BOT(zv) (Z_TYPE_INFO_P(zv) = BOT)
90 
value_known(zval * zv)91 static inline zend_bool value_known(zval *zv) {
92 	return !IS_TOP(zv) && !IS_BOT(zv);
93 }
94 
95 /* Sets new value for variable and ensures that it is lower or equal
96  * the previous one in the constant propagation lattice. */
set_value(scdf_ctx * scdf,sccp_ctx * ctx,int var,zval * new)97 static void set_value(scdf_ctx *scdf, sccp_ctx *ctx, int var, zval *new) {
98 	zval *value = &ctx->values[var];
99 	if (IS_BOT(value) || IS_TOP(new)) {
100 		return;
101 	}
102 
103 	if (IS_BOT(new)) {
104 		SCP_DEBUG("Lowering var %d to BOT\n", var);
105 	} else {
106 		SCP_DEBUG("Lowering var %d to %Z\n", var, new);
107 	}
108 
109 	if (IS_TOP(value) || IS_BOT(new)) {
110 		zval_ptr_dtor_nogc(value);
111 		ZVAL_COPY(value, new);
112 		scdf_add_to_worklist(scdf, var);
113 		return;
114 	}
115 
116 #if ZEND_DEBUG
117 	ZEND_ASSERT(zend_is_identical(value, new));
118 #endif
119 }
120 
get_op1_value(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)121 static zval *get_op1_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
122 	if (opline->op1_type == IS_CONST) {
123 		return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op1.constant);
124 	} else if (ssa_op->op1_use != -1) {
125 		return &ctx->values[ssa_op->op1_use];
126 	} else {
127 		return NULL;
128 	}
129 }
130 
get_op2_value(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op)131 static zval *get_op2_value(sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
132 	if (opline->op2_type == IS_CONST) {
133 		return CT_CONSTANT_EX(ctx->scdf.op_array, opline->op2.constant);
134 	} else if (ssa_op->op2_use != -1) {
135 		return &ctx->values[ssa_op->op2_use];
136 	} else {
137 		return NULL;
138 	}
139 }
140 
can_replace_op1(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)141 static zend_bool can_replace_op1(
142 		const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
143 	switch (opline->opcode) {
144 		case ZEND_PRE_INC:
145 		case ZEND_PRE_DEC:
146 		case ZEND_PRE_INC_OBJ:
147 		case ZEND_PRE_DEC_OBJ:
148 		case ZEND_POST_INC:
149 		case ZEND_POST_DEC:
150 		case ZEND_POST_INC_OBJ:
151 		case ZEND_POST_DEC_OBJ:
152 		case ZEND_ASSIGN:
153 		case ZEND_ASSIGN_REF:
154 		case ZEND_ASSIGN_DIM:
155 		case ZEND_ASSIGN_OBJ:
156 		case ZEND_ASSIGN_ADD:
157 		case ZEND_ASSIGN_SUB:
158 		case ZEND_ASSIGN_MUL:
159 		case ZEND_ASSIGN_DIV:
160 		case ZEND_ASSIGN_MOD:
161 		case ZEND_ASSIGN_SL:
162 		case ZEND_ASSIGN_SR:
163 		case ZEND_ASSIGN_CONCAT:
164 		case ZEND_ASSIGN_BW_OR:
165 		case ZEND_ASSIGN_BW_AND:
166 		case ZEND_ASSIGN_BW_XOR:
167 		case ZEND_ASSIGN_POW:
168 		case ZEND_FETCH_DIM_W:
169 		case ZEND_FETCH_DIM_RW:
170 		case ZEND_FETCH_DIM_UNSET:
171 		case ZEND_FETCH_DIM_FUNC_ARG:
172 		case ZEND_FETCH_OBJ_W:
173 		case ZEND_FETCH_OBJ_RW:
174 		case ZEND_FETCH_OBJ_UNSET:
175 		case ZEND_FETCH_OBJ_FUNC_ARG:
176 		case ZEND_UNSET_DIM:
177 		case ZEND_UNSET_OBJ:
178 		case ZEND_SEND_REF:
179 		case ZEND_SEND_VAR_EX:
180 		case ZEND_SEND_UNPACK:
181 		case ZEND_SEND_ARRAY:
182 		case ZEND_SEND_USER:
183 		case ZEND_FE_RESET_RW:
184 			return 0;
185 		/* Do not accept CONST */
186 		case ZEND_VERIFY_ABSTRACT_CLASS:
187 		case ZEND_ADD_INTERFACE:
188 		case ZEND_ADD_TRAIT:
189 		case ZEND_BIND_TRAITS:
190 		case ZEND_ROPE_ADD:
191 		case ZEND_ROPE_END:
192 		case ZEND_BIND_STATIC:
193 		case ZEND_BIND_GLOBAL:
194 		case ZEND_MAKE_REF:
195 		case ZEND_UNSET_CV:
196 		case ZEND_ISSET_ISEMPTY_CV:
197 		case ZEND_INSTANCEOF:
198 			return 0;
199 		case ZEND_INIT_ARRAY:
200 		case ZEND_ADD_ARRAY_ELEMENT:
201 			return !(opline->extended_value & ZEND_ARRAY_ELEMENT_REF);
202 		case ZEND_YIELD:
203 			return !(op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE);
204 		case ZEND_VERIFY_RETURN_TYPE:
205 			// TODO: This would require a non-local change ???
206 			return 0;
207 		default:
208 			if (ssa_op->op1_def != -1) {
209 				ZEND_ASSERT(0);
210 				return 0;
211 			}
212 	}
213 
214 	return 1;
215 }
216 
can_replace_op2(const zend_op_array * op_array,zend_op * opline,zend_ssa_op * ssa_op)217 static zend_bool can_replace_op2(
218 		const zend_op_array *op_array, zend_op *opline, zend_ssa_op *ssa_op) {
219 	switch (opline->opcode) {
220 		/* Do not accept CONST */
221 		case ZEND_DECLARE_INHERITED_CLASS:
222 		case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
223 		case ZEND_DECLARE_ANON_INHERITED_CLASS:
224 		case ZEND_BIND_LEXICAL:
225 		case ZEND_FE_FETCH_R:
226 		case ZEND_FE_FETCH_RW:
227 			return 0;
228 	}
229 	return 1;
230 }
231 
try_replace_op1(sccp_ctx * ctx,zend_op * opline,zend_ssa_op * ssa_op,int var,zval * value)232 static zend_bool try_replace_op1(
233 		sccp_ctx *ctx, zend_op *opline, zend_ssa_op *ssa_op, int var, zval *value) {
234 	if (ssa_op->op1_use == var && can_replace_op1(ctx->scdf.op_array, opline, ssa_op)) {
235 		zval zv;
236 		ZVAL_COPY(&zv, value);
237 		if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
238 			/* Reconstruct SSA */
239 			int num;
240 			zend_basic_block *block;
241 
242 			switch (opline->opcode) {
243 				case ZEND_JMPZ:
244 					if (zend_is_true(&zv)) {
245 						MAKE_NOP(opline);
246 						num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
247 						block = &ctx->scdf.ssa->cfg.blocks[num];
248 						if (block->successors_count == 2) {
249 							if (block->successors[1] != block->successors[0]) {
250 								zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
251 							}
252 							block->successors_count = 1;
253 							block->successors[0] = block->successors[1];
254 						}
255 					} else {
256 						opline->opcode = ZEND_JMP;
257 						COPY_NODE(opline->op1, opline->op2);
258 						num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
259 						block = &ctx->scdf.ssa->cfg.blocks[num];
260 						if (block->successors_count == 2) {
261 							if (block->successors[1] != block->successors[0]) {
262 								zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
263 							}
264 							block->successors_count = 1;
265 						}
266 					}
267 					break;
268 				case ZEND_JMPNZ:
269 					if (zend_is_true(&zv)) {
270 						opline->opcode = ZEND_JMP;
271 						COPY_NODE(opline->op1, opline->op2);
272 						num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
273 						block = &ctx->scdf.ssa->cfg.blocks[num];
274 						if (block->successors_count == 2) {
275 							if (block->successors[1] != block->successors[0]) {
276 								zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
277 							}
278 							block->successors_count = 1;
279 						}
280 					} else {
281 						MAKE_NOP(opline);
282 						num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
283 						block = &ctx->scdf.ssa->cfg.blocks[num];
284 						if (block->successors_count == 2) {
285 							if (block->successors[1] != block->successors[0]) {
286 								zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
287 							}
288 							block->successors_count = 1;
289 							block->successors[0] = block->successors[1];
290 						}
291 					}
292 					break;
293 				case ZEND_JMPZNZ:
294 					if (zend_is_true(&zv)) {
295 						zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
296 						ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
297 						num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
298 						block = &ctx->scdf.ssa->cfg.blocks[num];
299 						if (block->successors_count == 2) {
300 							if (block->successors[1] != block->successors[0]) {
301 								zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
302 							}
303 							block->successors_count = 1;
304 							block->successors[0] = block->successors[1];
305 						}
306 					} else {
307 						zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
308 						ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
309 						num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
310 						block = &ctx->scdf.ssa->cfg.blocks[num];
311 						if (block->successors_count == 2) {
312 							if (block->successors[1] != block->successors[0]) {
313 								zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
314 							}
315 							block->successors_count = 1;
316 						}
317 					}
318 					opline->op1_type = IS_UNUSED;
319 					opline->extended_value = 0;
320 					opline->opcode = ZEND_JMP;
321 					break;
322 				default:
323 					break;
324 			}
325 			return 1;
326 		} else {
327 			// TODO: check the following special cases ???
328 			switch (opline->opcode) {
329 				case ZEND_FETCH_LIST:
330 				case ZEND_CASE:
331 				case ZEND_SWITCH_STRING:
332 				case ZEND_SWITCH_LONG:
333 					if (Z_TYPE(zv) == IS_STRING) {
334 						zend_string_hash_val(Z_STR(zv));
335 					}
336 					opline->op1.constant = zend_optimizer_add_literal(ctx->scdf.op_array, &zv);
337 					opline->op1_type = IS_CONST;
338 					return 1;
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 			zval_ptr_dtor_nogc(&zv);
355 		}
356 	}
357 	return 0;
358 }
359 
zval_to_string_offset(zend_long * result,zval * op)360 static inline int zval_to_string_offset(zend_long *result, zval *op) {
361 	switch (Z_TYPE_P(op)) {
362 		case IS_LONG:
363 			*result = Z_LVAL_P(op);
364 			return SUCCESS;
365 		case IS_STRING:
366 			if (IS_LONG == is_numeric_string(
367 					Z_STRVAL_P(op), Z_STRLEN_P(op), result, NULL, 0)) {
368 				return SUCCESS;
369 			}
370 			return FAILURE;
371 		default:
372 			return FAILURE;
373 	}
374 }
375 
fetch_array_elem(zval ** result,zval * op1,zval * op2)376 static inline int fetch_array_elem(zval **result, zval *op1, zval *op2) {
377 	switch (Z_TYPE_P(op2)) {
378 		case IS_NULL:
379 			*result = zend_hash_find(Z_ARR_P(op1), ZSTR_EMPTY_ALLOC());
380 			return SUCCESS;
381 		case IS_FALSE:
382 			*result = zend_hash_index_find(Z_ARR_P(op1), 0);
383 			return SUCCESS;
384 		case IS_TRUE:
385 			*result = zend_hash_index_find(Z_ARR_P(op1), 1);
386 			return SUCCESS;
387 		case IS_LONG:
388 			*result = zend_hash_index_find(Z_ARR_P(op1), Z_LVAL_P(op2));
389 			return SUCCESS;
390 		case IS_DOUBLE:
391 			*result = zend_hash_index_find(Z_ARR_P(op1), zend_dval_to_lval(Z_DVAL_P(op2)));
392 			return SUCCESS;
393 		case IS_STRING:
394 			*result = zend_symtable_find(Z_ARR_P(op1), Z_STR_P(op2));
395 			return SUCCESS;
396 		default:
397 			return FAILURE;
398 	}
399 }
400 
ct_eval_fetch_dim(zval * result,zval * op1,zval * op2,int support_strings)401 static inline int ct_eval_fetch_dim(zval *result, zval *op1, zval *op2, int support_strings) {
402 	if (Z_TYPE_P(op1) == IS_ARRAY) {
403 		zval *value;
404 		if (fetch_array_elem(&value, op1, op2) == SUCCESS && value) {
405 			ZVAL_COPY(result, value);
406 			return SUCCESS;
407 		}
408 	} else if (support_strings && Z_TYPE_P(op1) == IS_STRING) {
409 		zend_long index;
410 		if (zval_to_string_offset(&index, op2) == FAILURE) {
411 			return FAILURE;
412 		}
413 		if (index >= 0 && index < Z_STRLEN_P(op1)) {
414 			ZVAL_STR(result, zend_string_init(&Z_STRVAL_P(op1)[index], 1, 0));
415 			return SUCCESS;
416 		}
417 	}
418 	return FAILURE;
419 }
420 
ct_eval_isset_dim(zval * result,uint32_t extended_value,zval * op1,zval * op2)421 static inline int ct_eval_isset_dim(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
422 	if (Z_TYPE_P(op1) == IS_ARRAY) {
423 		zval *value;
424 		if (fetch_array_elem(&value, op1, op2) == FAILURE) {
425 			return FAILURE;
426 		}
427 		if (extended_value & ZEND_ISSET) {
428 			ZVAL_BOOL(result, value && Z_TYPE_P(value) != IS_NULL);
429 		} else {
430 			ZEND_ASSERT(extended_value & ZEND_ISEMPTY);
431 			ZVAL_BOOL(result, !value || !zend_is_true(value));
432 		}
433 		return SUCCESS;
434 	} else if (Z_TYPE_P(op1) == IS_STRING) {
435 		// TODO
436 		return FAILURE;
437 	} else {
438 		ZVAL_BOOL(result, extended_value != ZEND_ISSET);
439 		return SUCCESS;
440 	}
441 }
442 
ct_eval_add_array_elem(zval * result,zval * value,zval * key)443 static inline int ct_eval_add_array_elem(zval *result, zval *value, zval *key) {
444 	if (!key) {
445 		if ((value = zend_hash_next_index_insert(Z_ARR_P(result), value))) {
446 			Z_TRY_ADDREF_P(value);
447 			return SUCCESS;
448 		}
449 		return FAILURE;
450 	}
451 
452 	switch (Z_TYPE_P(key)) {
453 		case IS_NULL:
454 			value = zend_hash_update(Z_ARR_P(result), ZSTR_EMPTY_ALLOC(), value);
455 			break;
456 		case IS_FALSE:
457 			value = zend_hash_index_update(Z_ARR_P(result), 0, value);
458 			break;
459 		case IS_TRUE:
460 			value = zend_hash_index_update(Z_ARR_P(result), 1, value);
461 			break;
462 		case IS_LONG:
463 			value = zend_hash_index_update(Z_ARR_P(result), Z_LVAL_P(key), value);
464 			break;
465 		case IS_DOUBLE:
466 			value = zend_hash_index_update(
467 				Z_ARR_P(result), zend_dval_to_lval(Z_DVAL_P(key)), value);
468 			break;
469 		case IS_STRING:
470 			value = zend_symtable_update(Z_ARR_P(result), Z_STR_P(key), value);
471 			break;
472 		default:
473 			return FAILURE;
474 	}
475 
476 	Z_TRY_ADDREF_P(value);
477 	return SUCCESS;
478 }
479 
ct_eval_assign_dim(zval * result,zval * value,zval * key)480 static inline int ct_eval_assign_dim(zval *result, zval *value, zval *key) {
481 	switch (Z_TYPE_P(result)) {
482 		case IS_NULL:
483 		case IS_FALSE:
484 			array_init(result);
485 			/* break missing intentionally */
486 		case IS_ARRAY:
487 			return ct_eval_add_array_elem(result, value, key);
488 		case IS_STRING:
489 			// TODO Before enabling this case, make sure ARRAY_DIM result op is correct
490 #if 0
491 			zend_long index;
492 			zend_string *new_str, *value_str;
493 			if (!key || Z_TYPE_P(value) == IS_ARRAY
494 					|| zval_to_string_offset(&index, key) == FAILURE || index < 0) {
495 				return FAILURE;
496 			}
497 
498 			if (index >= Z_STRLEN_P(result)) {
499 				new_str = zend_string_alloc(index + 1, 0);
500 				memcpy(ZSTR_VAL(new_str), Z_STRVAL_P(result), Z_STRLEN_P(result));
501 				memset(ZSTR_VAL(new_str) + Z_STRLEN_P(result), ' ', index - Z_STRLEN_P(result));
502 				ZSTR_VAL(new_str)[index + 1] = 0;
503 			} else {
504 				new_str = zend_string_init(Z_STRVAL_P(result), Z_STRLEN_P(result), 0);
505 			}
506 
507 			value_str = zval_get_string(value);
508 			ZVAL_STR(result, new_str);
509 			Z_STRVAL_P(result)[index] = ZSTR_VAL(value_str)[0];
510 			zend_string_release(value_str);
511 #endif
512 			return FAILURE;
513 		default:
514 			return FAILURE;
515 	}
516 }
517 
ct_eval_incdec(zval * result,zend_uchar opcode,zval * op1)518 static inline int ct_eval_incdec(zval *result, zend_uchar opcode, zval *op1) {
519 	ZVAL_COPY(result, op1);
520 	if (opcode == ZEND_PRE_INC || opcode == ZEND_POST_INC) {
521 		increment_function(result);
522 	} else {
523 		decrement_function(result);
524 	}
525 	return SUCCESS;
526 }
527 
ct_eval_isset_isempty(zval * result,uint32_t extended_value,zval * op1)528 static inline int ct_eval_isset_isempty(zval *result, uint32_t extended_value, zval *op1) {
529 	if (extended_value & ZEND_ISSET) {
530 		ZVAL_BOOL(result, Z_TYPE_P(op1) != IS_NULL);
531 	} else {
532 		ZEND_ASSERT(extended_value & ZEND_ISEMPTY);
533 		ZVAL_BOOL(result, !zend_is_true(op1));
534 	}
535 	return SUCCESS;
536 }
537 
ct_eval_type_check(zval * result,uint32_t type,zval * op1)538 static inline void ct_eval_type_check(zval *result, uint32_t type, zval *op1) {
539 	if (type == _IS_BOOL) {
540 		ZVAL_BOOL(result, Z_TYPE_P(op1) == IS_TRUE || Z_TYPE_P(op1) == IS_FALSE);
541 	} else {
542 		ZVAL_BOOL(result, Z_TYPE_P(op1) == type);
543 	}
544 }
545 
ct_eval_in_array(zval * result,uint32_t extended_value,zval * op1,zval * op2)546 static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval *op1, zval *op2) {
547 	HashTable *ht;
548 	zend_bool res;
549 
550 	if (Z_TYPE_P(op2) != IS_ARRAY) {
551 		return FAILURE;
552 	}
553 	ht = Z_ARRVAL_P(op2);
554 	if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
555 		res = zend_hash_exists(ht, Z_STR_P(op1));
556 	} else if (extended_value) {
557 		if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
558 			res = zend_hash_index_exists(ht, Z_LVAL_P(op1));
559 		} else {
560 			res = 0;
561 		}
562 	} else if (Z_TYPE_P(op1) <= IS_FALSE) {
563 		res = zend_hash_exists(ht, ZSTR_EMPTY_ALLOC());
564 	} else {
565 		zend_string *key;
566 		zval key_tmp, result_tmp;
567 
568 		res = 0;
569 		ZEND_HASH_FOREACH_STR_KEY(ht, key) {
570 			ZVAL_STR(&key_tmp, key);
571 			compare_function(&result_tmp, op1, &key_tmp);
572 			if (Z_LVAL(result_tmp) == 0) {
573 				res = 1;
574 				break;
575 			}
576 		} ZEND_HASH_FOREACH_END();
577 	}
578 	ZVAL_BOOL(result, res);
579 	return SUCCESS;
580 }
581 
582 /* The functions chosen here are simple to implement and either likely to affect a branch,
583  * or just happened to be commonly used with constant operands in WP (need to test other
584  * applications as well, of course). */
ct_eval_func_call(zval * result,zend_string * name,uint32_t num_args,zval ** args)585 static inline int ct_eval_func_call(
586 		zval *result, zend_string *name, uint32_t num_args, zval **args) {
587 	uint32_t i;
588 	zend_execute_data *execute_data, *prev_execute_data;
589 	zend_function *func;
590 	int overflow;
591 
592 	if (num_args == 0) {
593 		if (zend_string_equals_literal(name, "get_magic_quotes_gpc")
594 				|| zend_string_equals_literal(name, "get_magic_quotes_gpc_runtime")
595 				|| zend_string_equals_literal(name, "php_sapi_name")
596 				|| zend_string_equals_literal(name, "imagetypes")
597 				|| zend_string_equals_literal(name, "phpversion")) {
598 			/* pass */
599 		} else {
600 			return FAILURE;
601 		}
602 	} else if (num_args == 1) {
603 		if (zend_string_equals_literal(name, "chr")) {
604 			zend_long c;
605 			if (Z_TYPE_P(args[0]) != IS_LONG) {
606 				return FAILURE;
607 			}
608 
609 			c = Z_LVAL_P(args[0]) & 0xff;
610 			ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
611 			return SUCCESS;
612 		} else if (zend_string_equals_literal(name, "count")) {
613 			if (Z_TYPE_P(args[0]) != IS_ARRAY) {
614 				return FAILURE;
615 			}
616 
617 			ZVAL_LONG(result, zend_hash_num_elements(Z_ARRVAL_P(args[0])));
618 			return SUCCESS;
619 		} else if (zend_string_equals_literal(name, "ini_get")) {
620 			zend_ini_entry *ini_entry;
621 
622 			if (Z_TYPE_P(args[0]) != IS_STRING) {
623 				return FAILURE;
624 			}
625 
626 			ini_entry = zend_hash_find_ptr(EG(ini_directives), Z_STR_P(args[0]));
627 			if (!ini_entry) {
628 				ZVAL_FALSE(result);
629 			} else if (ini_entry->modifiable != ZEND_INI_SYSTEM) {
630 				return FAILURE;
631 			} else if (ini_entry->value) {
632 				ZVAL_STR_COPY(result, ini_entry->value);
633 			} else {
634 				ZVAL_EMPTY_STRING(result);
635 			}
636 			return SUCCESS;
637 		} else if (zend_string_equals_literal(name, "trim")
638 				|| zend_string_equals_literal(name, "rtrim")
639 				|| zend_string_equals_literal(name, "ltrim")
640 				|| zend_string_equals_literal(name, "str_split")
641 				|| zend_string_equals_literal(name, "preg_quote")
642 				|| zend_string_equals_literal(name, "base64_encode")
643 				|| zend_string_equals_literal(name, "base64_decode")
644 				|| zend_string_equals_literal(name, "urlencode")
645 				|| zend_string_equals_literal(name, "urldecode")
646 				|| zend_string_equals_literal(name, "rawurlencode")
647 				|| zend_string_equals_literal(name, "rawurldecode")
648 				|| zend_string_equals_literal(name, "php_uname")) {
649 			if (Z_TYPE_P(args[0]) != IS_STRING) {
650 				return FAILURE;
651 			}
652 			/* pass */
653 		} else if (zend_string_equals_literal(name, "array_keys")
654 				|| zend_string_equals_literal(name, "array_values")) {
655 			if (Z_TYPE_P(args[0]) != IS_ARRAY) {
656 				return FAILURE;
657 			}
658 			/* pass */
659 		} else if (zend_string_equals_literal(name, "array_flip")) {
660 			zval *entry;
661 
662 			if (Z_TYPE_P(args[0]) != IS_ARRAY) {
663 				return FAILURE;
664 			}
665 			ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
666 				if (Z_TYPE_P(entry) != IS_LONG && Z_TYPE_P(entry) != IS_STRING) {
667 					return FAILURE;
668 				}
669 			} ZEND_HASH_FOREACH_END();
670 			/* pass */
671 		} else if (zend_string_equals_literal(name, "implode")) {
672 			zval *entry;
673 
674 			if (Z_TYPE_P(args[0]) != IS_ARRAY) {
675 				return FAILURE;
676 			}
677 
678 			ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
679 				if (Z_TYPE_P(entry) > IS_STRING) {
680 					return FAILURE;
681 				}
682 			} ZEND_HASH_FOREACH_END();
683 			/* pass */
684 		} else if (zend_string_equals_literal(name, "serialize")) {
685 			/* pass */
686 		} else {
687 			return FAILURE;
688 		}
689 	} else if (num_args == 2) {
690 		if (zend_string_equals_literal(name, "in_array")) {
691 			if (Z_TYPE_P(args[1]) != IS_ARRAY) {
692 				return FAILURE;
693 			}
694 			/* pass */
695 		} else if (zend_string_equals_literal(name, "strpos")) {
696 			if (Z_TYPE_P(args[0]) != IS_STRING
697 					|| Z_TYPE_P(args[1]) != IS_STRING
698 					|| !Z_STRLEN_P(args[1])
699 					|| (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
700 				return FAILURE;
701 			}
702 			/* pass */
703 		} else if (zend_string_equals_literal(name, "str_split")) {
704 			if (Z_TYPE_P(args[0]) != IS_STRING
705 					|| Z_TYPE_P(args[1]) != IS_LONG
706 					|| Z_LVAL_P(args[1]) <= 0) {
707 				return FAILURE;
708 			}
709 			/* pass */
710 		} else if (zend_string_equals_literal(name, "array_key_exists")) {
711 			if (Z_TYPE_P(args[1]) != IS_ARRAY
712 					|| (Z_TYPE_P(args[0]) != IS_LONG
713 						&& Z_TYPE_P(args[0]) != IS_STRING
714 						&& Z_TYPE_P(args[0]) != IS_NULL)) {
715 				return FAILURE;
716 			}
717 			/* pass */
718 		} else if (zend_string_equals_literal(name, "trim")
719 				|| zend_string_equals_literal(name, "rtrim")
720 				|| zend_string_equals_literal(name, "ltrim")
721 				|| zend_string_equals_literal(name, "preg_quote")) {
722 			if (Z_TYPE_P(args[0]) != IS_STRING
723 					|| Z_TYPE_P(args[1]) != IS_STRING) {
724 				return FAILURE;
725 			}
726 			/* pass */
727 		} else if (zend_string_equals_literal(name, "str_repeat")) {
728 			if (Z_TYPE_P(args[0]) != IS_STRING
729 					|| Z_TYPE_P(args[1]) != IS_LONG
730 					|| zend_safe_address(Z_STRLEN_P(args[0]), Z_LVAL_P(args[1]), 0, &overflow) > 64 * 1024
731 					|| overflow) {
732 				return FAILURE;
733 			}
734 			/* pass */
735 		} else if (zend_string_equals_literal(name, "array_merge")
736 				|| zend_string_equals_literal(name, "array_replace")
737 				|| zend_string_equals_literal(name, "array_merge_recursive")
738 				|| zend_string_equals_literal(name, "array_replace_recursive")
739 				|| zend_string_equals_literal(name, "array_diff")
740 				|| zend_string_equals_literal(name, "array_diff_assoc")
741 				|| zend_string_equals_literal(name, "array_diff_key")) {
742 			for (i = 0; i < num_args; i++) {
743 				if (Z_TYPE_P(args[i]) != IS_ARRAY) {
744 					return FAILURE;
745 				}
746 			}
747 			/* pass */
748 		} else if (zend_string_equals_literal(name, "implode")) {
749 			zval *entry;
750 
751 			if ((Z_TYPE_P(args[0]) != IS_STRING || Z_TYPE_P(args[1]) != IS_ARRAY)
752 					&& (Z_TYPE_P(args[0]) != IS_ARRAY || Z_TYPE_P(args[1]) != IS_STRING)) {
753 				return FAILURE;
754 			}
755 
756 			if (Z_TYPE_P(args[0]) == IS_ARRAY) {
757 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[0]), entry) {
758 					if (Z_TYPE_P(entry) > IS_STRING) {
759 						return FAILURE;
760 					}
761 				} ZEND_HASH_FOREACH_END();
762 			} else {
763 				ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args[1]), entry) {
764 					if (Z_TYPE_P(entry) > IS_STRING) {
765 						return FAILURE;
766 					}
767 				} ZEND_HASH_FOREACH_END();
768 			}
769 			/* pass */
770 		} else if (zend_string_equals_literal(name, "version_compare")) {
771 			if (Z_TYPE_P(args[0]) != IS_STRING
772 					|| Z_TYPE_P(args[1]) != IS_STRING) {
773 				return FAILURE;
774 			}
775 			/* pass */
776 		} else if (zend_string_equals_literal(name, "substr")) {
777 			if (Z_TYPE_P(args[0]) != IS_STRING
778 					|| Z_TYPE_P(args[1]) != IS_LONG
779 					|| (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
780 				return FAILURE;
781 			}
782 			/* pass */
783 		} else if (zend_string_equals_literal(name, "pow")) {
784 			if ((Z_TYPE_P(args[0]) != IS_LONG && Z_TYPE_P(args[0]) != IS_DOUBLE)
785 					|| (Z_TYPE_P(args[1]) != IS_LONG && Z_TYPE_P(args[1]) != IS_DOUBLE)) {
786 				return FAILURE;
787 			}
788 			/* pass */
789 		} else {
790 			return FAILURE;
791 		}
792 	} else if (num_args == 3) {
793 		if (zend_string_equals_literal(name, "in_array")) {
794 			if (Z_TYPE_P(args[1]) != IS_ARRAY
795 				|| (Z_TYPE_P(args[2]) != IS_FALSE
796 					&& Z_TYPE_P(args[2]) != IS_TRUE)) {
797 				return FAILURE;
798 			}
799 			/* pass */
800 		} else if (zend_string_equals_literal(name, "array_merge")
801 				|| zend_string_equals_literal(name, "array_replace")
802 				|| zend_string_equals_literal(name, "array_merge_recursive")
803 				|| zend_string_equals_literal(name, "array_replace_recursive")
804 				|| zend_string_equals_literal(name, "array_diff")
805 				|| zend_string_equals_literal(name, "array_diff_assoc")
806 				|| zend_string_equals_literal(name, "array_diff_key")) {
807 			for (i = 0; i < num_args; i++) {
808 				if (Z_TYPE_P(args[i]) != IS_ARRAY) {
809 					return FAILURE;
810 				}
811 			}
812 			/* pass */
813 		} else if (zend_string_equals_literal(name, "version_compare")) {
814 			if (Z_TYPE_P(args[0]) != IS_STRING
815 					|| Z_TYPE_P(args[1]) != IS_STRING
816 					|| Z_TYPE_P(args[2]) != IS_STRING) {
817 				return FAILURE;
818 			}
819 			/* pass */
820 		} else if (zend_string_equals_literal(name, "substr")) {
821 			if (Z_TYPE_P(args[0]) != IS_STRING
822 					|| Z_TYPE_P(args[1]) != IS_LONG
823 					|| Z_TYPE_P(args[2]) != IS_LONG
824 					|| (CG(compiler_options) & ZEND_COMPILE_NO_BUILTIN_STRLEN)) {
825 				return FAILURE;
826 			}
827 			/* pass */
828 		} else {
829 			return FAILURE;
830 		}
831 	} else {
832 		return FAILURE;
833 	}
834 
835 	func = zend_hash_find_ptr(CG(function_table), name);
836 	if (!func || func->type != ZEND_INTERNAL_FUNCTION
837 			|| func->internal_function.handler == ZEND_FN(display_disabled_function)) {
838 		return FAILURE;
839 	}
840 
841 	execute_data = safe_emalloc(num_args, sizeof(zval), ZEND_CALL_FRAME_SLOT * sizeof(zval));
842 	memset(execute_data, 0, sizeof(zend_execute_data));
843 	prev_execute_data = EG(current_execute_data);
844 	EG(current_execute_data) = execute_data;
845 
846 	EX(func) = func;
847 	EX_NUM_ARGS() = num_args;
848 	for (i = 0; i < num_args; i++) {
849 		ZVAL_COPY(EX_VAR_NUM(i), args[i]);
850 	}
851 	func->internal_function.handler(execute_data, result);
852 	for (i = 0; i < num_args; i++) {
853 		zval_ptr_dtor_nogc(EX_VAR_NUM(i));
854 	}
855 	efree(execute_data);
856 	EG(current_execute_data) = prev_execute_data;
857 	return SUCCESS;
858 }
859 
860 #define SET_RESULT(op, zv) do { \
861 	if (ssa_op->op##_def >= 0) { \
862 		set_value(scdf, ctx, ssa_op->op##_def, zv); \
863 	} \
864 } while (0)
865 #define SET_RESULT_BOT(op) SET_RESULT(op, &ctx->bot)
866 #define SET_RESULT_TOP(op) SET_RESULT(op, &ctx->top)
867 
868 #define SKIP_IF_TOP(op) if (IS_TOP(op)) break;
869 
sccp_visit_instr(scdf_ctx * scdf,zend_op * opline,zend_ssa_op * ssa_op)870 static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op) {
871 	sccp_ctx *ctx = (sccp_ctx *) scdf;
872 	zval *op1, *op2, zv; /* zv is a temporary to hold result values */
873 
874 	op1 = get_op1_value(ctx, opline, ssa_op);
875 	op2 = get_op2_value(ctx, opline, ssa_op);
876 
877 	switch (opline->opcode) {
878 		case ZEND_ASSIGN:
879 			/* The value of op1 is irrelevant here, because we are overwriting it
880 			 * -- unless it can be a reference, in which case we propagate a BOT. */
881 			if (IS_BOT(op1) && (ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF)) {
882 				SET_RESULT_BOT(op1);
883 			} else {
884 				SET_RESULT(op1, op2);
885 			}
886 
887 			SET_RESULT(result, op2);
888 			return;
889 		case ZEND_TYPE_CHECK:
890 			/* We may be able to evaluate TYPE_CHECK based on type inference info,
891 			 * even if we don't know the precise value. */
892 			if (!value_known(op1)) {
893 				uint32_t type = ctx->scdf.ssa->var_info[ssa_op->op1_use].type;
894 				uint32_t expected_type = opline->extended_value == _IS_BOOL
895 					? (MAY_BE_TRUE|MAY_BE_FALSE) : (1 << opline->extended_value);
896 				if (!(type & expected_type) && !(type & MAY_BE_UNDEF)) {
897 					ZVAL_FALSE(&zv);
898 					SET_RESULT(result, &zv);
899 					return;
900 				} else if (!(type & ((MAY_BE_ANY|MAY_BE_UNDEF) - expected_type))
901 						   && opline->extended_value != IS_RESOURCE) {
902 					ZVAL_TRUE(&zv);
903 					SET_RESULT(result, &zv);
904 					return;
905 				}
906 			}
907 			break;
908 		case ZEND_ASSIGN_DIM:
909 			/* If $a in $a[$b]=$c is UNDEF, treat it like NULL. There is no warning. */
910 			if ((ctx->scdf.ssa->var_info[ssa_op->op1_use].type & MAY_BE_ANY) == 0) {
911 				op1 = &EG(uninitialized_zval);
912 			}
913 			break;
914 		case ZEND_SEND_VAL:
915 		case ZEND_SEND_VAR:
916 		{
917 			/* If the value of a SEND for an ICALL changes, we need to reconsider the
918 			 * ICALL result value. Otherwise we can ignore the opcode. */
919 			zend_call_info *call;
920 			if (!ctx->call_map) {
921 				return;
922 			}
923 
924 			call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
925 			if (IS_TOP(op1) || !call || call->caller_call_opline->opcode != ZEND_DO_ICALL) {
926 				return;
927 			}
928 
929 			opline = call->caller_call_opline;
930 			ssa_op = &ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes];
931 			break;
932 		}
933 	}
934 
935 	if ((op1 && IS_BOT(op1)) || (op2 && IS_BOT(op2))) {
936 		/* If any operand is BOT, mark the result as BOT right away.
937 		 * Exceptions to this rule are handled above. */
938 		SET_RESULT_BOT(result);
939 		SET_RESULT_BOT(op1);
940 		SET_RESULT_BOT(op2);
941 		return;
942 	}
943 
944 	switch (opline->opcode) {
945 		case ZEND_ADD:
946 		case ZEND_SUB:
947 		case ZEND_MUL:
948 		case ZEND_DIV:
949 		case ZEND_MOD:
950 		case ZEND_POW:
951 		case ZEND_SL:
952 		case ZEND_SR:
953 		case ZEND_CONCAT:
954 		case ZEND_FAST_CONCAT:
955 		case ZEND_IS_EQUAL:
956 		case ZEND_IS_NOT_EQUAL:
957 		case ZEND_IS_SMALLER:
958 		case ZEND_IS_SMALLER_OR_EQUAL:
959 		case ZEND_IS_IDENTICAL:
960 		case ZEND_IS_NOT_IDENTICAL:
961 		case ZEND_BW_OR:
962 		case ZEND_BW_AND:
963 		case ZEND_BW_XOR:
964 		case ZEND_BOOL_XOR:
965 		case ZEND_CASE:
966 			SKIP_IF_TOP(op1);
967 			SKIP_IF_TOP(op2);
968 
969 			if (zend_optimizer_eval_binary_op(&zv, opline->opcode, op1, op2) == SUCCESS) {
970 				SET_RESULT(result, &zv);
971 				zval_ptr_dtor_nogc(&zv);
972 				break;
973 			}
974 			SET_RESULT_BOT(result);
975 			break;
976 		case ZEND_ASSIGN_ADD:
977 		case ZEND_ASSIGN_SUB:
978 		case ZEND_ASSIGN_MUL:
979 		case ZEND_ASSIGN_DIV:
980 		case ZEND_ASSIGN_MOD:
981 		case ZEND_ASSIGN_SL:
982 		case ZEND_ASSIGN_SR:
983 		case ZEND_ASSIGN_CONCAT:
984 		case ZEND_ASSIGN_BW_OR:
985 		case ZEND_ASSIGN_BW_AND:
986 		case ZEND_ASSIGN_BW_XOR:
987 		case ZEND_ASSIGN_POW:
988 			/* Obj/dim compound assign */
989 			if (opline->extended_value) {
990 				SET_RESULT_BOT(op1);
991 				SET_RESULT_BOT(result);
992 				break;
993 			}
994 
995 			SKIP_IF_TOP(op1);
996 			SKIP_IF_TOP(op2);
997 
998 			if (zend_optimizer_eval_binary_op(&zv, zend_compound_assign_to_binary_op(opline->opcode), op1, op2) == SUCCESS) {
999 				SET_RESULT(op1, &zv);
1000 				SET_RESULT(result, &zv);
1001 				zval_ptr_dtor_nogc(&zv);
1002 				break;
1003 			}
1004 			SET_RESULT_BOT(op1);
1005 			SET_RESULT_BOT(result);
1006 			break;
1007 		case ZEND_PRE_INC:
1008 		case ZEND_PRE_DEC:
1009 			SKIP_IF_TOP(op1);
1010 			if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1011 				SET_RESULT(op1, &zv);
1012 				SET_RESULT(result, &zv);
1013 				zval_ptr_dtor_nogc(&zv);
1014 				break;
1015 			}
1016 			SET_RESULT_BOT(op1);
1017 			SET_RESULT_BOT(result);
1018 			break;
1019 		case ZEND_POST_INC:
1020 		case ZEND_POST_DEC:
1021 			SKIP_IF_TOP(op1);
1022 			SET_RESULT(result, op1);
1023 			if (ct_eval_incdec(&zv, opline->opcode, op1) == SUCCESS) {
1024 				SET_RESULT(op1, &zv);
1025 				zval_ptr_dtor_nogc(&zv);
1026 				break;
1027 			}
1028 			SET_RESULT_BOT(op1);
1029 			break;
1030 		case ZEND_BW_NOT:
1031 		case ZEND_BOOL_NOT:
1032 			SKIP_IF_TOP(op1);
1033 			if (zend_optimizer_eval_unary_op(&zv, opline->opcode, op1) == SUCCESS) {
1034 				SET_RESULT(result, &zv);
1035 				zval_ptr_dtor_nogc(&zv);
1036 				break;
1037 			}
1038 			SET_RESULT_BOT(result);
1039 			break;
1040 		case ZEND_CAST:
1041 			SKIP_IF_TOP(op1);
1042 			if (zend_optimizer_eval_cast(&zv, opline->extended_value, op1) == SUCCESS) {
1043 				SET_RESULT(result, &zv);
1044 				zval_ptr_dtor_nogc(&zv);
1045 				break;
1046 			}
1047 			SET_RESULT_BOT(result);
1048 			break;
1049 		case ZEND_BOOL:
1050 		case ZEND_JMPZ_EX:
1051 		case ZEND_JMPNZ_EX:
1052 			SKIP_IF_TOP(op1);
1053 			ZVAL_BOOL(&zv, zend_is_true(op1));
1054 			SET_RESULT(result, &zv);
1055 			break;
1056 		case ZEND_STRLEN:
1057 			SKIP_IF_TOP(op1);
1058 			if (zend_optimizer_eval_strlen(&zv, op1) == SUCCESS) {
1059 				SET_RESULT(result, &zv);
1060 				zval_ptr_dtor_nogc(&zv);
1061 				break;
1062 			}
1063 			SET_RESULT_BOT(result);
1064 			break;
1065 		case ZEND_COUNT:
1066 			SKIP_IF_TOP(op1);
1067 			if (Z_TYPE_P(op1) == IS_ARRAY) {
1068 				ZVAL_LONG(&zv, zend_hash_num_elements(Z_ARRVAL_P(op1)));
1069 				SET_RESULT(result, &zv);
1070 				zval_ptr_dtor_nogc(&zv);
1071 				break;
1072 			}
1073 			SET_RESULT_BOT(result);
1074 			break;
1075 		case ZEND_IN_ARRAY:
1076 			SKIP_IF_TOP(op1);
1077 			SKIP_IF_TOP(op2);
1078 			if (ct_eval_in_array(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1079 				SET_RESULT(result, &zv);
1080 				zval_ptr_dtor_nogc(&zv);
1081 				break;
1082 			}
1083 			SET_RESULT_BOT(result);
1084 			break;
1085 		case ZEND_FETCH_DIM_R:
1086 		case ZEND_FETCH_DIM_IS:
1087 		case ZEND_FETCH_LIST:
1088 			SKIP_IF_TOP(op1);
1089 			SKIP_IF_TOP(op2);
1090 
1091 			if (ct_eval_fetch_dim(&zv, op1, op2, (opline->opcode != ZEND_FETCH_LIST)) == SUCCESS) {
1092 				SET_RESULT(result, &zv);
1093 				zval_ptr_dtor_nogc(&zv);
1094 				break;
1095 			}
1096 			SET_RESULT_BOT(result);
1097 			break;
1098 		case ZEND_ISSET_ISEMPTY_DIM_OBJ:
1099 			SKIP_IF_TOP(op1);
1100 			SKIP_IF_TOP(op2);
1101 
1102 			if (ct_eval_isset_dim(&zv, opline->extended_value, op1, op2) == SUCCESS) {
1103 				SET_RESULT(result, &zv);
1104 				zval_ptr_dtor_nogc(&zv);
1105 				break;
1106 			}
1107 			SET_RESULT_BOT(result);
1108 			break;
1109 		case ZEND_QM_ASSIGN:
1110 		case ZEND_JMP_SET:
1111 		case ZEND_COALESCE:
1112 			SET_RESULT(result, op1);
1113 			break;
1114 		case ZEND_FETCH_CLASS:
1115 			if (!op1) {
1116 				SET_RESULT_BOT(result);
1117 				break;
1118 			}
1119 			SET_RESULT(result, op1);
1120 			break;
1121 		case ZEND_ISSET_ISEMPTY_CV:
1122 			SKIP_IF_TOP(op1);
1123 			if (ct_eval_isset_isempty(&zv, opline->extended_value, op1) == SUCCESS) {
1124 				SET_RESULT(result, &zv);
1125 				zval_ptr_dtor_nogc(&zv);
1126 				break;
1127 			}
1128 			SET_RESULT_BOT(result);
1129 			break;
1130 		case ZEND_TYPE_CHECK:
1131 			SKIP_IF_TOP(op1);
1132 			ct_eval_type_check(&zv, opline->extended_value, op1);
1133 			SET_RESULT(result, &zv);
1134 			zval_ptr_dtor_nogc(&zv);
1135 			break;
1136 		case ZEND_INSTANCEOF:
1137 			SKIP_IF_TOP(op1);
1138 			ZVAL_FALSE(&zv);
1139 			SET_RESULT(result, &zv);
1140 			break;
1141 		case ZEND_ROPE_INIT:
1142 			SKIP_IF_TOP(op2);
1143 			if (zend_optimizer_eval_cast(&zv, IS_STRING, op2) == SUCCESS) {
1144 				SET_RESULT(result, &zv);
1145 				zval_ptr_dtor_nogc(&zv);
1146 				break;
1147 			}
1148 			SET_RESULT_BOT(result);
1149 			break;
1150 		case ZEND_ROPE_ADD:
1151 		case ZEND_ROPE_END:
1152 			// TODO The way this is currently implemented will result in quadratic runtime
1153 			// This is not necessary, the way the algorithm works it's okay to reuse the same
1154 			// string for all SSA vars with some extra checks
1155 			SKIP_IF_TOP(op1);
1156 			SKIP_IF_TOP(op2);
1157 			if (zend_optimizer_eval_binary_op(&zv, ZEND_CONCAT, op1, op2) == SUCCESS) {
1158 				SET_RESULT(result, &zv);
1159 				zval_ptr_dtor_nogc(&zv);
1160 				break;
1161 			}
1162 			SET_RESULT_BOT(result);
1163 			break;
1164 		case ZEND_INIT_ARRAY:
1165 		case ZEND_ADD_ARRAY_ELEMENT:
1166 		{
1167 			zval *result = NULL;
1168 			if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
1169 				SET_RESULT_BOT(result);
1170 				SET_RESULT_BOT(op1);
1171 				break;
1172 			}
1173 
1174 			if (opline->opcode == ZEND_ADD_ARRAY_ELEMENT) {
1175 				result = &ctx->values[ssa_op->result_use];
1176 				if (IS_BOT(result)) {
1177 					SET_RESULT_BOT(result);
1178 					break;
1179 				}
1180 				SKIP_IF_TOP(result);
1181 			}
1182 
1183 			SKIP_IF_TOP(op1);
1184 			if (op2) {
1185 				SKIP_IF_TOP(op2);
1186 			}
1187 
1188 			/* We want to avoid keeping around intermediate arrays for each SSA variable in the
1189 			 * ADD_ARRAY_ELEMENT chain. We do this by only keeping the array on the last opcode
1190 			 * and use a NULL value everywhere else. */
1191 			if (Z_TYPE(ctx->values[ssa_op->result_def]) == IS_NULL) {
1192 				break;
1193 			}
1194 
1195 			if (result) {
1196 				ZVAL_COPY_VALUE(&zv, result);
1197 				ZVAL_NULL(result);
1198 			} else {
1199 				array_init(&zv);
1200 			}
1201 
1202 			if (ct_eval_add_array_elem(&zv, op1, op2) == SUCCESS) {
1203 				SET_RESULT(result, &zv);
1204 				zval_ptr_dtor_nogc(&zv);
1205 				break;
1206 			}
1207 			SET_RESULT_BOT(result);
1208 			zval_ptr_dtor_nogc(&zv);
1209 			break;
1210 		}
1211 		case ZEND_ASSIGN_DIM:
1212 		{
1213 			zval *data = get_op1_value(ctx, opline+1, ssa_op+1);
1214 			if (IS_BOT(data)) {
1215 				SET_RESULT_BOT(op1);
1216 				SET_RESULT_BOT(result);
1217 				break;
1218 			}
1219 
1220 			SKIP_IF_TOP(data);
1221 			SKIP_IF_TOP(op1);
1222 			if (op2) {
1223 				SKIP_IF_TOP(op2);
1224 			}
1225 
1226 			ZVAL_DUP(&zv, op1);
1227 			if (ct_eval_assign_dim(&zv, data, op2) == SUCCESS) {
1228 				SET_RESULT(result, data);
1229 				SET_RESULT(op1, &zv);
1230 				zval_ptr_dtor_nogc(&zv);
1231 				break;
1232 			}
1233 			SET_RESULT_BOT(result);
1234 			SET_RESULT_BOT(op1);
1235 			zval_ptr_dtor_nogc(&zv);
1236 			break;
1237 		}
1238 		case ZEND_DO_ICALL:
1239 		{
1240 			zend_call_info *call;
1241 			zval *name, *args[3] = {NULL};
1242 			int i;
1243 
1244 			if (!ctx->call_map) {
1245 				SET_RESULT_BOT(result);
1246 				break;
1247 			}
1248 
1249 			call = ctx->call_map[opline - ctx->scdf.op_array->opcodes];
1250 			name = CT_CONSTANT_EX(ctx->scdf.op_array, call->caller_init_opline->op2.constant);
1251 
1252 			/* We already know it can't be evaluated, don't bother checking again */
1253 			if (ssa_op->result_def < 0 || IS_BOT(&ctx->values[ssa_op->result_def])) {
1254 				break;
1255 			}
1256 
1257 			/* We're only interested in functions with up to three arguments right now */
1258 			if (call->num_args > 3) {
1259 				SET_RESULT_BOT(result);
1260 				break;
1261 			}
1262 
1263 			for (i = 0; i < call->num_args; i++) {
1264 				zend_op *opline = call->arg_info[i].opline;
1265 				if (opline->opcode != ZEND_SEND_VAL && opline->opcode != ZEND_SEND_VAR) {
1266 					SET_RESULT_BOT(result);
1267 					return;
1268 				}
1269 
1270 				args[i] = get_op1_value(ctx, opline,
1271 					&ctx->scdf.ssa->ops[opline - ctx->scdf.op_array->opcodes]);
1272 				if (args[i]) {
1273 					if (IS_BOT(args[i])) {
1274 						SET_RESULT_BOT(result);
1275 						return;
1276 					} else if (IS_TOP(args[i])) {
1277 						return;
1278 					}
1279 				}
1280 			}
1281 
1282 			/* We didn't get a BOT argument, so value stays the same */
1283 			if (!IS_TOP(&ctx->values[ssa_op->result_def])) {
1284 				break;
1285 			}
1286 
1287 			if (ct_eval_func_call(&zv, Z_STR_P(name), call->num_args, args) == SUCCESS) {
1288 				SET_RESULT(result, &zv);
1289 				zval_ptr_dtor_nogc(&zv);
1290 				break;
1291 			}
1292 
1293 #if 0
1294 			/* sort out | uniq -c | sort -n */
1295 			fprintf(stderr, "%s\n", Z_STRVAL_P(name));
1296 			/*if (args[1]) {
1297 				php_printf("%s %Z %Z\n", Z_STRVAL_P(name), args[0], args[1]);
1298 			} else {
1299 				php_printf("%s %Z\n", Z_STRVAL_P(name), args[0]);
1300 			}*/
1301 #endif
1302 
1303 			SET_RESULT_BOT(result);
1304 			break;
1305 		}
1306 		default:
1307 		{
1308 			/* If we have no explicit implementation return BOT */
1309 			SET_RESULT_BOT(result);
1310 			SET_RESULT_BOT(op1);
1311 			SET_RESULT_BOT(op2);
1312 			break;
1313 		}
1314 	}
1315 }
1316 
1317 /* 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)1318 static void sccp_mark_feasible_successors(
1319 		scdf_ctx *scdf,
1320 		int block_num, zend_basic_block *block,
1321 		zend_op *opline, zend_ssa_op *ssa_op) {
1322 	sccp_ctx *ctx = (sccp_ctx *) scdf;
1323 	zval *op1;
1324 	int s;
1325 
1326 	/* We can't determine the branch target at compile-time for these */
1327 	switch (opline->opcode) {
1328 		case ZEND_ASSERT_CHECK:
1329 		case ZEND_CATCH:
1330 		case ZEND_DECLARE_ANON_CLASS:
1331 		case ZEND_DECLARE_ANON_INHERITED_CLASS:
1332 		case ZEND_FE_FETCH_R:
1333 		case ZEND_FE_FETCH_RW:
1334 			scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1335 			scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1336 			return;
1337 	}
1338 
1339 	op1 = get_op1_value(ctx, opline, ssa_op);
1340 
1341 	/* Branch target can be either one */
1342 	if (!op1 || IS_BOT(op1)) {
1343 		for (s = 0; s < block->successors_count; s++) {
1344 			scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1345 		}
1346 		return;
1347 	}
1348 
1349 	/* Branch target not yet known */
1350 	if (IS_TOP(op1)) {
1351 		return;
1352 	}
1353 
1354 	switch (opline->opcode) {
1355 		case ZEND_JMPZ:
1356 		case ZEND_JMPZNZ:
1357 		case ZEND_JMPZ_EX:
1358 			s = zend_is_true(op1);
1359 			break;
1360 		case ZEND_JMPNZ:
1361 		case ZEND_JMPNZ_EX:
1362 		case ZEND_JMP_SET:
1363 			s = !zend_is_true(op1);
1364 			break;
1365 		case ZEND_COALESCE:
1366 			s = (Z_TYPE_P(op1) == IS_NULL);
1367 			break;
1368 		case ZEND_FE_RESET_R:
1369 		case ZEND_FE_RESET_RW:
1370 			if (Z_TYPE_P(op1) != IS_ARRAY) {
1371 				scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
1372 				scdf_mark_edge_feasible(scdf, block_num, block->successors[1]);
1373 				return;
1374 			}
1375 			s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
1376 			break;
1377 		default:
1378 			for (s = 0; s < block->successors_count; s++) {
1379 				scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1380 			}
1381 			return;
1382 	}
1383 	scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);
1384 }
1385 
join_phi_values(zval * a,zval * b)1386 static void join_phi_values(zval *a, zval *b) {
1387 	if (IS_BOT(a) || IS_TOP(b)) {
1388 		return;
1389 	}
1390 	if (IS_TOP(a)) {
1391 		zval_ptr_dtor_nogc(a);
1392 		ZVAL_COPY(a, b);
1393 		return;
1394 	}
1395 	if (IS_BOT(b) || !zend_is_identical(a, b)) {
1396 		zval_ptr_dtor_nogc(a);
1397 		MAKE_BOT(a);
1398 	}
1399 }
1400 
sccp_visit_phi(scdf_ctx * scdf,zend_ssa_phi * phi)1401 static void sccp_visit_phi(scdf_ctx *scdf, zend_ssa_phi *phi) {
1402 	sccp_ctx *ctx = (sccp_ctx *) scdf;
1403 	zend_ssa *ssa = scdf->ssa;
1404 	ZEND_ASSERT(phi->ssa_var >= 0);
1405 	if (!IS_BOT(&ctx->values[phi->ssa_var])) {
1406 		zend_basic_block *block = &ssa->cfg.blocks[phi->block];
1407 		int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
1408 
1409 		int i;
1410 		zval result;
1411 		MAKE_TOP(&result);
1412 		SCP_DEBUG("Handling PHI(");
1413 		if (phi->pi >= 0) {
1414 			ZEND_ASSERT(phi->sources[0] >= 0);
1415 			if (scdf_is_edge_feasible(scdf, phi->pi, phi->block)) {
1416 				join_phi_values(&result, &ctx->values[phi->sources[0]]);
1417 			}
1418 		} else {
1419 			for (i = 0; i < block->predecessors_count; i++) {
1420 				ZEND_ASSERT(phi->sources[i] >= 0);
1421 				if (scdf_is_edge_feasible(scdf, predecessors[i], phi->block)) {
1422 					SCP_DEBUG("val, ");
1423 					join_phi_values(&result, &ctx->values[phi->sources[i]]);
1424 				} else {
1425 					SCP_DEBUG("--, ");
1426 				}
1427 			}
1428 		}
1429 		SCP_DEBUG(")\n");
1430 
1431 		set_value(scdf, ctx, phi->ssa_var, &result);
1432 		zval_ptr_dtor_nogc(&result);
1433 	}
1434 }
1435 
value_from_type_and_range(sccp_ctx * ctx,int var_num,zval * tmp)1436 static zval *value_from_type_and_range(sccp_ctx *ctx, int var_num, zval *tmp) {
1437 	zend_ssa *ssa = ctx->scdf.ssa;
1438 	zend_ssa_var_info *info = &ssa->var_info[var_num];
1439 
1440 	if (ssa->vars[var_num].var >= ctx->scdf.op_array->last_var) {
1441 		// TODO Non-CVs may cause issues with FREEs
1442 		return NULL;
1443 	}
1444 
1445 	if (info->type & MAY_BE_UNDEF) {
1446 		return NULL;
1447 	}
1448 
1449 	if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_NULL))) {
1450 		ZVAL_NULL(tmp);
1451 		return tmp;
1452 	}
1453 	if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_FALSE))) {
1454 		ZVAL_FALSE(tmp);
1455 		return tmp;
1456 	}
1457 	if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_TRUE))) {
1458 		ZVAL_TRUE(tmp);
1459 		return tmp;
1460 	}
1461 
1462 	if (!(info->type & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_LONG))
1463 			&& info->has_range
1464 			&& !info->range.overflow && !info->range.underflow
1465 			&& info->range.min == info->range.max) {
1466 		ZVAL_LONG(tmp, info->range.min);
1467 		return tmp;
1468 	}
1469 
1470 	return NULL;
1471 }
1472 
1473 /* This will try to replace uses of SSA variables we have determined to be constant. Not all uses
1474  * can be replaced, because some instructions don't accept constant operands or only accept them
1475  * if they have a certain type. */
replace_constant_operands(sccp_ctx * ctx)1476 static int replace_constant_operands(sccp_ctx *ctx) {
1477 	zend_ssa *ssa = ctx->scdf.ssa;
1478 	zend_op_array *op_array = ctx->scdf.op_array;
1479 	int i;
1480 	zval tmp;
1481 	int removed_ops = 0;
1482 
1483 	/* We iterate the variables backwards, so we can eliminate sequences like INIT_ROPE
1484 	 * and INIT_ARRAY. */
1485 	for (i = ssa->vars_count - 1; i >= op_array->last_var; i--) {
1486 		zend_ssa_var *var = &ssa->vars[i];
1487 		zval *value;
1488 		int use;
1489 
1490 		if (value_known(&ctx->values[i])) {
1491 			value = &ctx->values[i];
1492 		} else {
1493 			value = value_from_type_and_range(ctx, i, &tmp);
1494 			if (!value) {
1495 				continue;
1496 			}
1497 		}
1498 
1499 		FOREACH_USE(var, use) {
1500 			zend_op *opline = &op_array->opcodes[use];
1501 			zend_ssa_op *ssa_op = &ssa->ops[use];
1502 			if (try_replace_op1(ctx, opline, ssa_op, i, value)) {
1503 				if (opline->opcode == ZEND_NOP) {
1504 					removed_ops++;
1505 				}
1506 				ZEND_ASSERT(ssa_op->op1_def == -1);
1507 				if (ssa_op->op1_use != ssa_op->op2_use) {
1508 					zend_ssa_unlink_use_chain(ssa, use, ssa_op->op1_use);
1509 				} else {
1510 					ssa_op->op2_use_chain = ssa_op->op1_use_chain;
1511 				}
1512 				ssa_op->op1_use = -1;
1513 				ssa_op->op1_use_chain = -1;
1514 			}
1515 			if (try_replace_op2(ctx, opline, ssa_op, i, value)) {
1516 				ZEND_ASSERT(ssa_op->op2_def == -1);
1517 				if (ssa_op->op2_use != ssa_op->op1_use) {
1518 					zend_ssa_unlink_use_chain(ssa, use, ssa_op->op2_use);
1519 				}
1520 				ssa_op->op2_use = -1;
1521 				ssa_op->op2_use_chain = -1;
1522 			}
1523 		} FOREACH_USE_END();
1524 
1525 		/* This is a basic DCE pass we run after SCCP. It only works on those instructions those result
1526 		 * value(s) were determined by SCCP. It removes dead computational instructions and converts
1527 		 * CV-affecting instructions into CONST ASSIGNs. This basic DCE is performed for multiple reasons:
1528 		 * a) During operand replacement we eliminate FREEs. The corresponding computational instructions
1529 		 *    must be removed to avoid leaks. This way SCCP can run independently of the full DCE pass.
1530 		 * b) The main DCE pass relies on type analysis to determine whether instructions have side-effects
1531 		 *    and can't be DCEd. This means that it will not be able collect all instructions rendered dead
1532 		 *    by SCCP, because they may have potentially side-effecting types, but the actual values are
1533 		 *    not. As such doing DCE here will allow us to eliminate more dead code in combination.
1534 		 * c) The ordinary DCE pass cannot collect dead calls. However SCCP can result in dead calls, which
1535 		 *    we need to collect. */
1536 
1537 		if (var->definition >= 0 && value_known(&ctx->values[i])) {
1538 			zend_op *opline = &op_array->opcodes[var->definition];
1539 			zend_ssa_op *ssa_op = &ssa->ops[var->definition];
1540 			if (opline->opcode == ZEND_ASSIGN) {
1541 				/* Leave assigns to DCE (due to dtor effects) */
1542 				continue;
1543 			}
1544 
1545 			if (ssa_op->result_def == i
1546 					&& ssa_op->op1_def < 0
1547 					&& ssa_op->op2_def < 0
1548 					&& var->use_chain < 0
1549 					&& var->phi_use_chain == NULL) {
1550 				if (opline->opcode == ZEND_DO_ICALL) {
1551 					/* Call instruction -> remove opcodes that are part of the call */
1552 					zend_call_info *call;
1553 					int i;
1554 
1555 					ZEND_ASSERT(ctx->call_map);
1556 					call = ctx->call_map[var->definition];
1557 					ZEND_ASSERT(call);
1558 					ZEND_ASSERT(call->caller_call_opline == opline);
1559 					if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
1560 						zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
1561 					}
1562 					zend_ssa_remove_result_def(ssa, ssa_op);
1563 					zend_ssa_remove_instr(ssa, opline, ssa_op);
1564 					zend_ssa_remove_instr(ssa, call->caller_init_opline,
1565 						&ssa->ops[call->caller_init_opline - op_array->opcodes]);
1566 
1567 					for (i = 0; i < call->num_args; i++) {
1568 						zend_ssa_remove_instr(ssa, call->arg_info[i].opline,
1569 							&ssa->ops[call->arg_info[i].opline - op_array->opcodes]);
1570 					}
1571 					removed_ops = call->num_args + 2;
1572 
1573 					// TODO: remove call_info completely???
1574 					call->callee_func = NULL;
1575 				} else {
1576 					/* Ordinary computational instruction -> remove it */
1577 					if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
1578 						zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
1579 					}
1580 					zend_ssa_remove_result_def(ssa, ssa_op);
1581 					zend_ssa_remove_instr(ssa, opline, ssa_op);
1582 					removed_ops++;
1583 				}
1584 			} else if (ssa_op->op1_def == i &&
1585 					(ssa_op->result_def < 0 ||
1586 					 (ssa->vars[ssa_op->result_def].use_chain < 0 &&
1587 					  ssa->vars[ssa_op->result_def].phi_use_chain == NULL))) {
1588 				/* Compound assign or incdec -> convert to direct ASSIGN */
1589 
1590 				/* Destroy previous op2 */
1591 				if (opline->op2_type == IS_CONST) {
1592 					literal_dtor(&ZEND_OP2_LITERAL(opline));
1593 				} else if (ssa_op->op2_use >= 0) {
1594 					if (ssa_op->op2_use != ssa_op->op1_use) {
1595 						zend_ssa_unlink_use_chain(ssa, var->definition, ssa_op->op2_use);
1596 					}
1597 					ssa_op->op2_use = -1;
1598 					ssa_op->op2_use_chain = -1;
1599 				}
1600 
1601 				/* We checked that result has no uses, mark unused */
1602 				if (ssa_op->result_def >= 0) {
1603 					if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
1604 						zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
1605 					}
1606 					zend_ssa_remove_result_def(ssa, ssa_op);
1607 					opline->result_type = IS_UNUSED;
1608 				}
1609 
1610 				/* Remove OP_DATA opcode */
1611 				if (opline->opcode == ZEND_ASSIGN_DIM) {
1612 					removed_ops++;
1613 					zend_ssa_remove_instr(ssa, opline + 1, ssa_op + 1);
1614 				}
1615 
1616 				/* Convert to ASSIGN */
1617 				opline->opcode = ZEND_ASSIGN;
1618 				opline->op2_type = IS_CONST;
1619 				opline->op2.constant = zend_optimizer_add_literal(op_array, value);
1620 				Z_TRY_ADDREF_P(value);
1621 			}
1622 		}
1623 		if (var->definition_phi
1624 				&& value_known(&ctx->values[i])
1625 				&& var->use_chain < 0
1626 				&& var->phi_use_chain == NULL) {
1627 			zend_ssa_remove_phi(ssa, var->definition_phi);
1628 		}
1629 	}
1630 
1631 	return removed_ops;
1632 }
1633 
sccp_context_init(zend_optimizer_ctx * ctx,sccp_ctx * sccp,zend_ssa * ssa,zend_op_array * op_array,zend_call_info ** call_map)1634 static void sccp_context_init(zend_optimizer_ctx *ctx, sccp_ctx *sccp,
1635 		zend_ssa *ssa, zend_op_array *op_array, zend_call_info **call_map) {
1636 	int i;
1637 	sccp->call_map = call_map;
1638 	sccp->values = zend_arena_alloc(&ctx->arena, sizeof(zval) * ssa->vars_count);
1639 
1640 	MAKE_TOP(&sccp->top);
1641 	MAKE_BOT(&sccp->bot);
1642 
1643 	i = 0;
1644 	for (; i < op_array->last_var; ++i) {
1645 		/* These are all undefined variables, which we have to mark BOT.
1646 		 * Otherwise the undefined variable warning might not be preserved. */
1647 		MAKE_BOT(&sccp->values[i]);
1648 	}
1649 	for (; i < ssa->vars_count; ++i) {
1650 		if (ssa->vars[i].alias) {
1651 			MAKE_BOT(&sccp->values[i]);
1652 		} else {
1653 			MAKE_TOP(&sccp->values[i]);
1654 		}
1655 	}
1656 }
1657 
sccp_context_free(sccp_ctx * sccp)1658 static void sccp_context_free(sccp_ctx *sccp) {
1659 	int i;
1660 	for (i = sccp->scdf.op_array->last_var; i < sccp->scdf.ssa->vars_count; ++i) {
1661 		zval_ptr_dtor_nogc(&sccp->values[i]);
1662 	}
1663 }
1664 
sccp_optimize_op_array(zend_optimizer_ctx * ctx,zend_op_array * op_array,zend_ssa * ssa,zend_call_info ** call_map)1665 int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map)
1666 {
1667 	sccp_ctx sccp;
1668 	int removed_ops = 0;
1669 	void *checkpoint = zend_arena_checkpoint(ctx->arena);
1670 
1671 	sccp_context_init(ctx, &sccp, ssa, op_array, call_map);
1672 
1673 	sccp.scdf.handlers.visit_instr = sccp_visit_instr;
1674 	sccp.scdf.handlers.visit_phi = sccp_visit_phi;
1675 	sccp.scdf.handlers.mark_feasible_successors = sccp_mark_feasible_successors;
1676 
1677 	scdf_init(ctx, &sccp.scdf, op_array, ssa);
1678 	scdf_solve(&sccp.scdf, "SCCP");
1679 
1680 	removed_ops += scdf_remove_unreachable_blocks(&sccp.scdf);
1681 	removed_ops += replace_constant_operands(&sccp);
1682 
1683 	sccp_context_free(&sccp);
1684 	zend_arena_release(&ctx->arena, checkpoint);
1685 
1686 	return removed_ops;
1687 }
1688