Coverage Report

Created: 2025-12-14 06:10

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