Coverage Report

Created: 2025-07-23 06:33

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