Coverage Report

Created: 2026-09-14 06:25

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