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