Coverage Report

Created: 2026-10-28 06:20

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