Coverage Report

Created: 2026-04-01 06:49

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