Coverage Report

Created: 2026-04-01 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/Optimizer/dfa_pass.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache                                                         |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) The PHP Group                                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 3.01 of the PHP license,      |
8
   | that is bundled with this package in the file LICENSE, and is        |
9
   | available through the world-wide-web at the following url:           |
10
   | https://www.php.net/license/3_01.txt                                 |
11
   | If you did not receive a copy of the PHP license and are unable to   |
12
   | obtain it through the world-wide-web, please send a note to          |
13
   | license@php.net so we can mail you a copy immediately.               |
14
   +----------------------------------------------------------------------+
15
   | Authors: Dmitry Stogov <dmitry@php.net>                              |
16
   +----------------------------------------------------------------------+
17
*/
18
19
#include "Optimizer/zend_optimizer.h"
20
#include "Optimizer/zend_optimizer_internal.h"
21
#include "zend_API.h"
22
#include "zend_constants.h"
23
#include "zend_execute.h"
24
#include "zend_vm.h"
25
#include "zend_bitset.h"
26
#include "zend_cfg.h"
27
#include "zend_ssa.h"
28
#include "zend_func_info.h"
29
#include "zend_call_graph.h"
30
#include "zend_inference.h"
31
#include "zend_dump.h"
32
33
#ifndef ZEND_DEBUG_DFA
34
# define ZEND_DEBUG_DFA ZEND_DEBUG
35
#endif
36
37
#if ZEND_DEBUG_DFA
38
# include "ssa_integrity.c"
39
#endif
40
41
zend_result zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa)
42
89.0k
{
43
89.0k
  uint32_t build_flags;
44
45
89.0k
  if (op_array->last_try_catch) {
46
    /* TODO: we can't analyze functions with try/catch/finally ??? */
47
19.7k
    return FAILURE;
48
19.7k
  }
49
50
    /* Build SSA */
51
69.2k
  memset(ssa, 0, sizeof(zend_ssa));
52
53
69.2k
  zend_build_cfg(&ctx->arena, op_array, ZEND_CFG_NO_ENTRY_PREDECESSORS, &ssa->cfg);
54
55
69.2k
  if ((ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
56
    /* TODO: we can't analyze functions with indirect variable access ??? */
57
2.79k
    return FAILURE;
58
2.79k
  }
59
60
66.4k
  zend_cfg_build_predecessors(&ctx->arena, &ssa->cfg);
61
62
66.4k
  if (ctx->debug_level & ZEND_DUMP_DFA_CFG) {
63
0
    zend_dump_op_array(op_array, ZEND_DUMP_CFG, "dfa cfg", &ssa->cfg);
64
0
  }
65
66
  /* Compute Dominators Tree */
67
66.4k
  zend_cfg_compute_dominators_tree(op_array, &ssa->cfg);
68
69
  /* Identify reducible and irreducible loops */
70
66.4k
  zend_cfg_identify_loops(op_array, &ssa->cfg);
71
72
66.4k
  if (ctx->debug_level & ZEND_DUMP_DFA_DOMINATORS) {
73
0
    zend_dump_dominators(op_array, &ssa->cfg);
74
0
  }
75
76
66.4k
  build_flags = 0;
77
66.4k
  if (ctx->debug_level & ZEND_DUMP_DFA_LIVENESS) {
78
0
    build_flags |= ZEND_SSA_DEBUG_LIVENESS;
79
0
  }
80
66.4k
  if (ctx->debug_level & ZEND_DUMP_DFA_PHI) {
81
0
    build_flags |= ZEND_SSA_DEBUG_PHI_PLACEMENT;
82
0
  }
83
66.4k
  if (zend_build_ssa(&ctx->arena, ctx->script, op_array, build_flags, ssa) == FAILURE) {
84
0
    return FAILURE;
85
0
  }
86
87
66.4k
  if (ctx->debug_level & ZEND_DUMP_DFA_SSA) {
88
0
    zend_dump_op_array(op_array, ZEND_DUMP_SSA, "dfa ssa", ssa);
89
0
  }
90
91
92
66.4k
  zend_ssa_compute_use_def_chains(&ctx->arena, op_array, ssa);
93
94
66.4k
  zend_ssa_find_false_dependencies(op_array, ssa);
95
96
66.4k
  zend_ssa_find_sccs(op_array, ssa);
97
98
66.4k
  if (zend_ssa_inference(&ctx->arena, op_array, ctx->script, ssa, ctx->optimization_level) == FAILURE) {
99
0
    return FAILURE;
100
0
  }
101
102
66.4k
  if (zend_ssa_escape_analysis(ctx->script, op_array, ssa) == FAILURE) {
103
0
    return FAILURE;
104
0
  }
105
106
66.4k
  if (ctx->debug_level & ZEND_DUMP_DFA_SSA_VARS) {
107
0
    zend_dump_ssa_variables(op_array, ssa, 0);
108
0
  }
109
110
66.4k
  return SUCCESS;
111
66.4k
}
112
113
static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx)
114
6.94k
{
115
6.94k
  zend_basic_block *blocks = ssa->cfg.blocks;
116
6.94k
  zend_basic_block *blocks_end = blocks + ssa->cfg.blocks_count;
117
6.94k
  zend_basic_block *b;
118
6.94k
  zend_func_info *func_info;
119
6.94k
  int j;
120
6.94k
  uint32_t i = 0;
121
6.94k
  uint32_t target = 0;
122
6.94k
  uint32_t *shiftlist;
123
6.94k
  ALLOCA_FLAG(use_heap);
124
125
6.94k
  shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
126
6.94k
  memset(shiftlist, 0, sizeof(uint32_t) * op_array->last);
127
  /* remove empty callee_info */
128
6.94k
  func_info = ZEND_FUNC_INFO(op_array);
129
6.94k
  if (func_info) {
130
6.94k
    zend_call_info **call_info = &func_info->callee_info;
131
15.0k
    while ((*call_info)) {
132
8.09k
      if ((*call_info)->caller_init_opline->opcode == ZEND_NOP) {
133
64
        *call_info = (*call_info)->next_callee;
134
8.02k
      } else {
135
8.02k
        call_info = &(*call_info)->next_callee;
136
8.02k
      }
137
8.09k
    }
138
6.94k
  }
139
140
60.6k
  for (b = blocks; b < blocks_end; b++) {
141
53.7k
    if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
142
48.1k
      if (b->len) {
143
47.8k
        uint32_t new_start, old_end;
144
61.2k
        while (i < b->start) {
145
13.4k
          shiftlist[i] = i - target;
146
13.4k
          i++;
147
13.4k
        }
148
149
47.8k
        if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
150
          /* Only keep the FREE for the loop var */
151
10
          ZEND_ASSERT(op_array->opcodes[b->start].opcode == ZEND_FREE
152
10
              || op_array->opcodes[b->start].opcode == ZEND_FE_FREE);
153
10
          b->len = 1;
154
10
        }
155
156
47.8k
        new_start = target;
157
47.8k
        old_end = b->start + b->len;
158
458k
        while (i < old_end) {
159
410k
          shiftlist[i] = i - target;
160
410k
          if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP)) {
161
388k
            if (i != target) {
162
234k
              op_array->opcodes[target] = op_array->opcodes[i];
163
234k
              ssa->ops[target] = ssa->ops[i];
164
234k
              ssa->cfg.map[target] = b - blocks;
165
234k
            }
166
388k
            target++;
167
388k
          }
168
410k
          i++;
169
410k
        }
170
47.8k
        b->start = new_start;
171
47.8k
        if (target != old_end) {
172
29.1k
          zend_op *opline;
173
29.1k
          zend_op *new_opline;
174
175
29.1k
          b->len = target - b->start;
176
29.1k
          opline = op_array->opcodes + old_end - 1;
177
29.1k
          if (opline->opcode == ZEND_NOP) {
178
162
            continue;
179
162
          }
180
181
29.0k
          new_opline = op_array->opcodes + target - 1;
182
29.0k
          zend_optimizer_migrate_jump(op_array, new_opline, opline);
183
29.0k
        }
184
47.8k
      } else {
185
362
        b->start = target;
186
362
      }
187
48.1k
    } else {
188
5.52k
      b->start = target;
189
5.52k
      b->len = 0;
190
5.52k
    }
191
53.7k
  }
192
193
6.94k
  if (target != op_array->last) {
194
    /* reset rest opcodes */
195
42.4k
    for (i = target; i < op_array->last; i++) {
196
35.5k
      MAKE_NOP(op_array->opcodes + i);
197
35.5k
    }
198
199
    /* update SSA variables */
200
381k
    for (j = 0; j < ssa->vars_count; j++) {
201
374k
      if (ssa->vars[j].definition >= 0) {
202
264k
        ssa->vars[j].definition -= shiftlist[ssa->vars[j].definition];
203
264k
      }
204
374k
      if (ssa->vars[j].use_chain >= 0) {
205
272k
        ssa->vars[j].use_chain -= shiftlist[ssa->vars[j].use_chain];
206
272k
      }
207
374k
    }
208
431k
    for (i = 0; i < op_array->last; i++) {
209
424k
      if (ssa->ops[i].op1_use_chain >= 0) {
210
18.5k
        ssa->ops[i].op1_use_chain -= shiftlist[ssa->ops[i].op1_use_chain];
211
18.5k
      }
212
424k
      if (ssa->ops[i].op2_use_chain >= 0) {
213
24.4k
        ssa->ops[i].op2_use_chain -= shiftlist[ssa->ops[i].op2_use_chain];
214
24.4k
      }
215
424k
      if (ssa->ops[i].res_use_chain >= 0) {
216
0
        ssa->ops[i].res_use_chain -= shiftlist[ssa->ops[i].res_use_chain];
217
0
      }
218
424k
    }
219
220
    /* update branch targets */
221
60.6k
    for (b = blocks; b < blocks_end; b++) {
222
53.7k
      if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
223
47.8k
        zend_op *opline = op_array->opcodes + b->start + b->len - 1;
224
47.8k
        zend_optimizer_shift_jump(op_array, opline, shiftlist);
225
47.8k
      }
226
53.7k
    }
227
228
    /* update try/catch array */
229
6.94k
    for (uint32_t j = 0; j < op_array->last_try_catch; j++) {
230
0
      op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
231
0
      op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
232
0
      if (op_array->try_catch_array[j].finally_op) {
233
0
        op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
234
0
        op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
235
0
      }
236
0
    }
237
238
    /* update call graph */
239
6.94k
    if (func_info) {
240
6.94k
      zend_call_info *call_info = func_info->callee_info;
241
14.9k
      while (call_info) {
242
8.02k
        call_info->caller_init_opline -=
243
8.02k
          shiftlist[call_info->caller_init_opline - op_array->opcodes];
244
8.02k
        if (call_info->caller_call_opline) {
245
8.02k
          call_info->caller_call_opline -=
246
8.02k
            shiftlist[call_info->caller_call_opline - op_array->opcodes];
247
8.02k
        }
248
8.02k
        call_info = call_info->next_callee;
249
8.02k
      }
250
6.94k
    }
251
252
6.94k
    op_array->last = target;
253
6.94k
  }
254
6.94k
  free_alloca(shiftlist, use_heap);
255
6.94k
}
256
257
305
static bool safe_instanceof(const zend_class_entry *ce1, const zend_class_entry *ce2) {
258
305
  if (ce1 == ce2) {
259
183
    return true;
260
183
  }
261
122
  if (!(ce1->ce_flags & ZEND_ACC_LINKED)) {
262
    /* This case could be generalized, similarly to unlinked_instanceof */
263
16
    return false;
264
16
  }
265
106
  return instanceof_function(ce1, ce2);
266
122
}
267
268
static inline bool can_elide_list_type(
269
  const zend_script *script, const zend_op_array *op_array,
270
  const zend_ssa_var_info *use_info, const zend_type type)
271
408
{
272
408
  const zend_type *single_type;
273
  /* For intersection: result==false is failure, default is success.
274
   * For union: result==true is success, default is failure. */
275
408
  bool is_intersection = ZEND_TYPE_IS_INTERSECTION(type);
276
816
  ZEND_TYPE_FOREACH(type, single_type) {
277
816
    if (ZEND_TYPE_HAS_LIST(*single_type)) {
278
48
      ZEND_ASSERT(!is_intersection);
279
48
      return can_elide_list_type(script, op_array, use_info, *single_type);
280
48
    }
281
360
    if (ZEND_TYPE_HAS_NAME(*single_type)) {
282
360
      zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type));
283
360
      const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname);
284
360
      zend_string_release(lcname);
285
360
      bool result = ce && safe_instanceof(use_info->ce, ce);
286
360
      if (result == !is_intersection) {
287
269
        return result;
288
269
      }
289
360
    }
290
360
  } ZEND_TYPE_FOREACH_END();
291
91
  return is_intersection;
292
408
}
293
294
static inline bool can_elide_return_type_check(
295
2.60k
    const zend_script *script, zend_op_array *op_array, zend_ssa *ssa, zend_ssa_op *ssa_op) {
296
2.60k
  zend_arg_info *arg_info = &op_array->arg_info[-1];
297
2.60k
  zend_ssa_var_info *use_info = &ssa->var_info[ssa_op->op1_use];
298
2.60k
  uint32_t use_type = use_info->type & (MAY_BE_ANY|MAY_BE_UNDEF);
299
2.60k
  if (use_type & MAY_BE_REF) {
300
0
    return false;
301
0
  }
302
303
2.60k
  if (use_type & MAY_BE_UNDEF) {
304
66
    use_type &= ~MAY_BE_UNDEF;
305
66
    use_type |= MAY_BE_NULL;
306
66
  }
307
308
2.60k
  uint32_t disallowed_types = use_type & ~ZEND_TYPE_PURE_MASK(arg_info->type);
309
2.60k
  if (!disallowed_types) {
310
    /* Only contains allowed types. */
311
658
    return true;
312
658
  }
313
314
1.94k
  if (disallowed_types == MAY_BE_OBJECT && use_info->ce && ZEND_TYPE_IS_COMPLEX(arg_info->type)) {
315
360
    return can_elide_list_type(script, op_array, use_info, arg_info->type);
316
360
  }
317
318
1.58k
  return false;
319
1.94k
}
320
321
static bool opline_supports_assign_contraction(
322
5.24k
    zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int src_var, uint32_t cv_var) {
323
5.24k
  if (opline->opcode == ZEND_NEW) {
324
    /* see Zend/tests/generators/aborted_yield_during_new.phpt */
325
992
    return false;
326
992
  }
327
328
  /* Frameless calls override the return value, but the return value may overlap with the arguments. */
329
4.24k
  switch (opline->opcode) {
330
0
    case ZEND_FRAMELESS_ICALL_3:
331
0
      if ((opline + 1)->op1_type == IS_CV && (opline + 1)->op1.var == cv_var) return false;
332
0
      ZEND_FALLTHROUGH;
333
0
    case ZEND_FRAMELESS_ICALL_2:
334
0
      if (opline->op2_type == IS_CV && opline->op2.var == cv_var) return false;
335
0
      ZEND_FALLTHROUGH;
336
0
    case ZEND_FRAMELESS_ICALL_1:
337
0
      if (opline->op1_type == IS_CV && opline->op1.var == cv_var) return false;
338
0
      return true;
339
4.24k
  }
340
341
4.24k
  if (opline->opcode == ZEND_DO_ICALL || opline->opcode == ZEND_DO_UCALL
342
4.23k
      || opline->opcode == ZEND_DO_FCALL || opline->opcode == ZEND_DO_FCALL_BY_NAME) {
343
    /* Function calls may dtor the return value after it has already been written -- allow
344
     * direct assignment only for types where a double-dtor does not matter. */
345
568
    uint32_t type = ssa->var_info[src_var].type;
346
568
    uint32_t simple = MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE;
347
568
    return !((type & MAY_BE_ANY) & ~simple);
348
568
  }
349
350
3.68k
  if (opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) {
351
    /* POST_INC/DEC write the result variable before performing the inc/dec. For $i = $i++
352
     * eliding the temporary variable would thus yield an incorrect result. */
353
13
    return opline->op1_type != IS_CV || opline->op1.var != cv_var;
354
13
  }
355
356
3.66k
  if (opline->opcode == ZEND_INIT_ARRAY) {
357
    /* INIT_ARRAY initializes the result array before reading key/value. */
358
54
    return (opline->op1_type != IS_CV || opline->op1.var != cv_var)
359
50
      && (opline->op2_type != IS_CV || opline->op2.var != cv_var);
360
54
  }
361
362
3.61k
  if (opline->opcode == ZEND_CAST
363
70
      && (opline->extended_value == IS_ARRAY || opline->extended_value == IS_OBJECT)) {
364
    /* CAST to array/object may initialize the result to an empty array/object before
365
     * reading the expression. */
366
14
    return opline->op1_type != IS_CV || opline->op1.var != cv_var;
367
14
  }
368
369
3.60k
  if ((opline->opcode == ZEND_ASSIGN_OP
370
3.59k
    || opline->opcode == ZEND_ASSIGN_OBJ
371
3.59k
    || opline->opcode == ZEND_ASSIGN_DIM
372
3.58k
    || opline->opcode == ZEND_ASSIGN_OBJ_OP
373
3.58k
    || opline->opcode == ZEND_ASSIGN_DIM_OP)
374
19
   && opline->op1_type == IS_CV
375
19
   && opline->op1.var == cv_var
376
8
   && zend_may_throw(opline, &ssa->ops[ssa->vars[src_var].definition], op_array, ssa)) {
377
6
    return false;
378
6
  }
379
380
3.59k
  return true;
381
3.60k
}
382
383
static bool variable_defined_or_used_in_range(zend_ssa *ssa, int var, int start, int end)
384
3.68k
{
385
3.71k
  while (start < end) {
386
32
    const zend_ssa_op *ssa_op = &ssa->ops[start];
387
32
    if ((ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].var == var) ||
388
32
      (ssa_op->op2_def >= 0 && ssa->vars[ssa_op->op2_def].var == var) ||
389
32
      (ssa_op->result_def >= 0 && ssa->vars[ssa_op->result_def].var == var) ||
390
32
      (ssa_op->op1_use >= 0 && ssa->vars[ssa_op->op1_use].var == var) ||
391
32
      (ssa_op->op2_use >= 0 && ssa->vars[ssa_op->op2_use].var == var) ||
392
32
      (ssa_op->result_use >= 0 && ssa->vars[ssa_op->result_use].var == var)
393
32
    ) {
394
0
      return true;
395
0
    }
396
32
    start++;
397
32
  }
398
3.68k
  return false;
399
3.68k
}
400
401
static uint32_t zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
402
66.4k
{
403
66.4k
  const zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
404
66.4k
  uint32_t removed_ops = 0;
405
406
66.4k
  if (func_info->callee_info) {
407
30.9k
    const zend_call_info *call_info = func_info->callee_info;
408
409
74.6k
    do {
410
74.6k
      zend_op *op = call_info->caller_init_opline;
411
412
74.6k
      if ((op->opcode == ZEND_FRAMELESS_ICALL_2
413
74.6k
        || (op->opcode == ZEND_FRAMELESS_ICALL_3 && (op + 1)->op1_type == IS_CONST))
414
0
       && call_info->callee_func
415
0
       && zend_string_equals_literal_ci(call_info->callee_func->common.function_name, "in_array")) {
416
0
        bool strict = false;
417
0
        bool has_opdata = op->opcode == ZEND_FRAMELESS_ICALL_3;
418
0
        ZEND_ASSERT(!call_info->is_prototype);
419
420
0
        if (has_opdata) {
421
0
          if (zend_is_true(CT_CONSTANT_EX(op_array, (op + 1)->op1.constant))) {
422
0
            strict = true;
423
0
          }
424
0
        }
425
426
0
        if (op->op2_type == IS_CONST
427
0
         && Z_TYPE_P(CT_CONSTANT_EX(op_array, op->op2.constant)) == IS_ARRAY) {
428
0
          bool ok = true;
429
430
0
          const HashTable *src = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, op->op2.constant));
431
0
          HashTable *dst;
432
0
          zval *val, tmp;
433
0
          zend_ulong idx;
434
435
0
          ZVAL_TRUE(&tmp);
436
0
          dst = zend_new_array(zend_hash_num_elements(src));
437
0
          if (strict) {
438
0
            ZEND_HASH_FOREACH_VAL(src, val) {
439
0
              if (Z_TYPE_P(val) == IS_STRING) {
440
0
                zend_hash_add(dst, Z_STR_P(val), &tmp);
441
0
              } else if (Z_TYPE_P(val) == IS_LONG) {
442
0
                zend_hash_index_add(dst, Z_LVAL_P(val), &tmp);
443
0
              } else {
444
0
                zend_array_destroy(dst);
445
0
                ok = false;
446
0
                break;
447
0
              }
448
0
            } ZEND_HASH_FOREACH_END();
449
0
          } else {
450
0
            ZEND_HASH_FOREACH_VAL(src, val) {
451
0
              if (Z_TYPE_P(val) != IS_STRING || ZEND_HANDLE_NUMERIC(Z_STR_P(val), idx)) {
452
0
                zend_array_destroy(dst);
453
0
                ok = false;
454
0
                break;
455
0
              }
456
0
              zend_hash_add(dst, Z_STR_P(val), &tmp);
457
0
            } ZEND_HASH_FOREACH_END();
458
0
          }
459
460
0
          if (ok) {
461
0
            ZVAL_ARR(&tmp, dst);
462
463
            /* Update opcode */
464
0
            op->opcode = ZEND_IN_ARRAY;
465
0
            op->extended_value = strict;
466
0
            op->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
467
0
            if (has_opdata) {
468
0
              MAKE_NOP(op + 1);
469
0
              removed_ops++;
470
0
            }
471
0
          }
472
0
        }
473
0
      }
474
74.6k
      call_info = call_info->next_callee;
475
74.6k
    } while (call_info);
476
30.9k
  }
477
478
66.4k
  return removed_ops;
479
66.4k
}
480
481
static zend_always_inline void take_successor_0(zend_ssa *ssa, uint32_t block_num, zend_basic_block *block)
482
1.60k
{
483
1.60k
  if (block->successors_count == 2) {
484
331
    if (block->successors[1] != block->successors[0]) {
485
49
      zend_ssa_remove_predecessor(ssa, block_num, block->successors[1]);
486
49
    }
487
331
    block->successors_count = 1;
488
331
  }
489
1.60k
}
490
491
static zend_always_inline void take_successor_1(zend_ssa *ssa, uint32_t block_num, zend_basic_block *block)
492
764
{
493
764
  if (block->successors_count == 2) {
494
420
    if (block->successors[1] != block->successors[0]) {
495
323
      zend_ssa_remove_predecessor(ssa, block_num, block->successors[0]);
496
323
      block->successors[0] = block->successors[1];
497
323
    }
498
420
    block->successors_count = 1;
499
420
  }
500
764
}
501
502
static zend_always_inline void take_successor_ex(zend_ssa *ssa, uint32_t block_num, zend_basic_block *block, int target_block)
503
22
{
504
74
  for (uint32_t i = 0; i < block->successors_count; i++) {
505
52
    if (block->successors[i] != target_block) {
506
30
      zend_ssa_remove_predecessor(ssa, block_num, block->successors[i]);
507
30
    }
508
52
  }
509
22
  block->successors[0] = target_block;
510
22
  block->successors_count = 1;
511
22
}
512
513
static void compress_block(zend_op_array *op_array, zend_basic_block *block)
514
249k
{
515
255k
  while (block->len > 0) {
516
253k
    zend_op *opline = &op_array->opcodes[block->start + block->len - 1];
517
518
253k
    if (opline->opcode == ZEND_NOP) {
519
5.90k
      block->len--;
520
247k
    } else {
521
247k
      break;
522
247k
    }
523
253k
  }
524
249k
}
525
526
1.73k
static void replace_predecessor(zend_ssa *ssa, int block_id, int old_pred, int new_pred) {
527
1.73k
  zend_basic_block *block = &ssa->cfg.blocks[block_id];
528
1.73k
  int *predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
529
1.73k
  zend_ssa_phi *phi;
530
531
1.73k
  int old_pred_idx = -1;
532
1.73k
  int new_pred_idx = -1;
533
4.22k
  for (uint32_t i = 0; i < block->predecessors_count; i++) {
534
2.49k
    if (predecessors[i] == old_pred) {
535
1.73k
      old_pred_idx = i;
536
1.73k
    }
537
2.49k
    if (predecessors[i] == new_pred) {
538
422
      new_pred_idx = i;
539
422
    }
540
2.49k
  }
541
542
1.73k
  ZEND_ASSERT(old_pred_idx != -1);
543
1.73k
  if (new_pred_idx == -1) {
544
    /* If the new predecessor doesn't exist yet, simply rewire the old one */
545
1.31k
    predecessors[old_pred_idx] = new_pred;
546
1.31k
  } else {
547
    /* Otherwise, rewiring the old predecessor would make the new predecessor appear
548
     * twice, which violates our CFG invariants. Remove the old predecessor instead. */
549
422
    memmove(
550
422
      predecessors + old_pred_idx,
551
422
      predecessors + old_pred_idx + 1,
552
422
      sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
553
422
    );
554
555
    /* Also remove the corresponding phi node entries */
556
446
    for (phi = ssa->blocks[block_id].phis; phi; phi = phi->next) {
557
24
      if (phi->pi >= 0) {
558
8
        if (phi->pi == old_pred || phi->pi == new_pred) {
559
8
          zend_ssa_rename_var_uses(
560
8
            ssa, phi->ssa_var, phi->sources[0], /* update_types */ 0);
561
8
          zend_ssa_remove_phi(ssa, phi);
562
8
        }
563
16
      } else {
564
16
        memmove(
565
16
          phi->sources + old_pred_idx,
566
16
          phi->sources + old_pred_idx + 1,
567
16
          sizeof(int) * (block->predecessors_count - old_pred_idx - 1)
568
16
        );
569
16
      }
570
24
    }
571
572
422
    block->predecessors_count--;
573
422
  }
574
1.73k
}
575
576
static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa, int from, int to, int new_to)
577
1.73k
{
578
1.73k
  zend_basic_block *src = &ssa->cfg.blocks[from];
579
1.73k
  zend_basic_block *old = &ssa->cfg.blocks[to];
580
1.73k
  zend_basic_block *dst = &ssa->cfg.blocks[new_to];
581
1.73k
  zend_op *opline;
582
583
4.54k
  for (uint32_t i = 0; i < src->successors_count; i++) {
584
2.81k
    if (src->successors[i] == to) {
585
1.77k
      src->successors[i] = new_to;
586
1.77k
    }
587
2.81k
  }
588
589
1.73k
  if (src->len > 0) {
590
1.53k
    opline = op_array->opcodes + src->start + src->len - 1;
591
1.53k
    switch (opline->opcode) {
592
7
      case ZEND_JMP:
593
7
      case ZEND_FAST_CALL:
594
7
        ZEND_ASSERT(ZEND_OP1_JMP_ADDR(opline) == op_array->opcodes + old->start);
595
7
        ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + dst->start);
596
7
        break;
597
419
      case ZEND_JMPZ:
598
597
      case ZEND_JMPNZ:
599
813
      case ZEND_JMPZ_EX:
600
837
      case ZEND_JMPNZ_EX:
601
837
      case ZEND_FE_RESET_R:
602
837
      case ZEND_FE_RESET_RW:
603
938
      case ZEND_JMP_SET:
604
1.13k
      case ZEND_COALESCE:
605
1.13k
      case ZEND_ASSERT_CHECK:
606
1.13k
      case ZEND_JMP_NULL:
607
1.13k
      case ZEND_BIND_INIT_STATIC_OR_JMP:
608
1.13k
      case ZEND_JMP_FRAMELESS:
609
1.13k
        if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) {
610
309
          ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start);
611
309
        }
612
1.13k
        break;
613
0
      case ZEND_CATCH:
614
0
        if (!(opline->extended_value & ZEND_LAST_CATCH)) {
615
0
          if (ZEND_OP2_JMP_ADDR(opline) == op_array->opcodes + old->start) {
616
0
            ZEND_SET_OP_JMP_ADDR(opline, opline->op2, op_array->opcodes + dst->start);
617
0
          }
618
0
        }
619
0
        break;
620
6
      case ZEND_FE_FETCH_R:
621
6
      case ZEND_FE_FETCH_RW:
622
6
        if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
623
0
          opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
624
0
        }
625
6
        break;
626
0
      case ZEND_SWITCH_LONG:
627
0
      case ZEND_SWITCH_STRING:
628
6
      case ZEND_MATCH:
629
6
        {
630
6
          HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
631
6
          zval *zv;
632
30
          ZEND_HASH_FOREACH_VAL(jumptable, zv) {
633
30
            if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)) == old->start) {
634
0
              Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
635
0
            }
636
30
          } ZEND_HASH_FOREACH_END();
637
6
          if (ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value) == old->start) {
638
6
            opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, dst->start);
639
6
          }
640
6
          break;
641
0
        }
642
1.53k
    }
643
1.53k
  }
644
645
1.73k
  replace_predecessor(ssa, new_to, to, from);
646
1.73k
}
647
648
static void zend_ssa_unlink_block(zend_op_array *op_array, zend_ssa *ssa, zend_basic_block *block, uint32_t block_num)
649
2.14k
{
650
2.14k
  if (block->predecessors_count == 1 && ssa->blocks[block_num].phis == NULL) {
651
1.73k
    int *predecessors;
652
1.73k
    zend_basic_block *fe_fetch_block = NULL;
653
654
1.73k
    ZEND_ASSERT(block->successors_count == 1);
655
1.73k
    predecessors = &ssa->cfg.predecessors[block->predecessor_offset];
656
1.73k
    if (block->predecessors_count == 1 && (block->flags & ZEND_BB_FOLLOW)) {
657
1.52k
      zend_basic_block *pred_block = &ssa->cfg.blocks[predecessors[0]];
658
659
1.52k
      if (pred_block->len > 0 && (pred_block->flags & ZEND_BB_REACHABLE)) {
660
1.32k
        if ((op_array->opcodes[pred_block->start + pred_block->len - 1].opcode == ZEND_FE_FETCH_R
661
1.31k
         || op_array->opcodes[pred_block->start + pred_block->len - 1].opcode == ZEND_FE_FETCH_RW)
662
6
          && op_array->opcodes[pred_block->start + pred_block->len - 1].op2_type == IS_CV) {
663
6
          fe_fetch_block = pred_block;
664
6
          }
665
1.32k
      }
666
1.52k
    }
667
3.46k
    for (uint32_t i = 0; i < block->predecessors_count; i++) {
668
1.73k
      zend_ssa_replace_control_link(op_array, ssa, predecessors[i], block_num, block->successors[0]);
669
1.73k
    }
670
1.73k
    zend_ssa_remove_block(op_array, ssa, block_num);
671
1.73k
    if (fe_fetch_block && fe_fetch_block->successors[0] == fe_fetch_block->successors[1]) {
672
      /* The body of "foreach" loop was removed */
673
4
      int ssa_var = ssa->ops[fe_fetch_block->start + fe_fetch_block->len - 1].op2_def;
674
4
      if (ssa_var >= 0) {
675
4
        zend_ssa_remove_uses_of_var(ssa, ssa_var);
676
4
      }
677
4
    }
678
1.73k
  }
679
2.14k
}
680
681
static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
682
132k
{
683
132k
  int removed_ops = 0;
684
132k
  uint32_t block_num = 0;
685
686
388k
  for (block_num = 1; block_num < ssa->cfg.blocks_count; block_num++) {
687
255k
    zend_basic_block *block = &ssa->cfg.blocks[block_num];
688
689
255k
    if (!(block->flags & ZEND_BB_REACHABLE)) {
690
8.71k
      continue;
691
8.71k
    }
692
247k
    compress_block(op_array, block);
693
247k
    if (block->len == 0) {
694
1.01k
      zend_ssa_unlink_block(op_array, ssa, block, block_num);
695
1.01k
    }
696
247k
  }
697
698
132k
  block_num = 0;
699
132k
  while (block_num < ssa->cfg.blocks_count
700
132k
    && !(ssa->cfg.blocks[block_num].flags & ZEND_BB_REACHABLE)) {
701
0
    block_num++;
702
0
  }
703
513k
  while (block_num < ssa->cfg.blocks_count) {
704
380k
    uint32_t next_block_num = block_num + 1;
705
380k
    zend_basic_block *block = &ssa->cfg.blocks[block_num];
706
380k
    uint32_t op_num;
707
380k
    zend_op *opline;
708
380k
    zend_ssa_op *ssa_op;
709
380k
    bool can_follow = true;
710
711
402k
    while (next_block_num < ssa->cfg.blocks_count
712
269k
      && !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) {
713
21.8k
      if (ssa->cfg.blocks[next_block_num].flags & ZEND_BB_UNREACHABLE_FREE) {
714
48
        can_follow = false;
715
48
      }
716
21.8k
      next_block_num++;
717
21.8k
    }
718
719
380k
    if (block->len) {
720
379k
      op_num = block->start + block->len - 1;
721
379k
      opline = op_array->opcodes + op_num;
722
379k
      ssa_op = ssa->ops + op_num;
723
724
379k
      switch (opline->opcode) {
725
27.3k
        case ZEND_JMP:
726
28.6k
optimize_jmp:
727
28.6k
          if (block->successors[0] == next_block_num && can_follow) {
728
1.48k
            MAKE_NOP(opline);
729
1.48k
            removed_ops++;
730
1.48k
            goto optimize_nop;
731
1.48k
          }
732
27.1k
          break;
733
27.1k
        case ZEND_JMPZ:
734
14.1k
optimize_jmpz:
735
14.1k
          if (opline->op1_type == IS_CONST) {
736
473
            if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
737
308
              MAKE_NOP(opline);
738
308
              removed_ops++;
739
308
              take_successor_1(ssa, block_num, block);
740
308
              goto optimize_nop;
741
308
            } else {
742
165
              opline->opcode = ZEND_JMP;
743
165
              COPY_NODE(opline->op1, opline->op2);
744
165
              take_successor_0(ssa, block_num, block);
745
165
              goto optimize_jmp;
746
165
            }
747
13.6k
          } else {
748
13.6k
            if (block->successors[0] == next_block_num && can_follow) {
749
166
              take_successor_0(ssa, block_num, block);
750
166
              if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
751
10
                opline->opcode = ZEND_CHECK_VAR;
752
10
                opline->op2.num = 0;
753
156
              } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
754
104
                zend_ssa_remove_instr(ssa, opline, ssa_op);
755
104
                removed_ops++;
756
104
                goto optimize_nop;
757
104
              } else {
758
52
                opline->opcode = ZEND_FREE;
759
52
                opline->op2.num = 0;
760
52
              }
761
166
            }
762
13.6k
          }
763
13.5k
          break;
764
17.3k
        case ZEND_JMPNZ:
765
18.4k
optimize_jmpnz:
766
18.4k
          if (opline->op1_type == IS_CONST) {
767
798
            if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
768
575
              opline->opcode = ZEND_JMP;
769
575
              COPY_NODE(opline->op1, opline->op2);
770
575
              take_successor_0(ssa, block_num, block);
771
575
              goto optimize_jmp;
772
575
            } else {
773
223
              MAKE_NOP(opline);
774
223
              removed_ops++;
775
223
              take_successor_1(ssa, block_num, block);
776
223
              goto optimize_nop;
777
223
            }
778
17.6k
          } else if (block->successors_count == 2) {
779
17.6k
            if (block->successors[0] == next_block_num && can_follow) {
780
116
              take_successor_0(ssa, block_num, block);
781
116
              if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_UNDEF)) {
782
0
                opline->opcode = ZEND_CHECK_VAR;
783
0
                opline->op2.num = 0;
784
116
              } else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
785
102
                zend_ssa_remove_instr(ssa, opline, ssa_op);
786
102
                removed_ops++;
787
102
                goto optimize_nop;
788
102
              } else {
789
14
                opline->opcode = ZEND_FREE;
790
14
                opline->op2.num = 0;
791
14
              }
792
116
            }
793
17.6k
          }
794
17.5k
          break;
795
17.5k
        case ZEND_JMPZ_EX:
796
1.22k
          if (ssa->vars[ssa_op->result_def].use_chain < 0
797
1.22k
              && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
798
444
            opline->opcode = ZEND_JMPZ;
799
444
            opline->result_type = IS_UNUSED;
800
444
            zend_ssa_remove_result_def(ssa, ssa_op);
801
444
            goto optimize_jmpz;
802
779
          } else if (opline->op1_type == IS_CONST) {
803
75
            if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
804
57
              opline->opcode = ZEND_BOOL;
805
57
              take_successor_1(ssa, block_num, block);
806
57
            }
807
75
          }
808
779
          break;
809
948
        case ZEND_JMPNZ_EX:
810
948
          if (ssa->vars[ssa_op->result_def].use_chain < 0
811
946
              && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
812
112
            opline->opcode = ZEND_JMPNZ;
813
112
            opline->result_type = IS_UNUSED;
814
112
            zend_ssa_remove_result_def(ssa, ssa_op);
815
112
            goto optimize_jmpnz;
816
836
          } else if (opline->op1_type == IS_CONST) {
817
110
            if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
818
0
              opline->opcode = ZEND_BOOL;
819
0
              take_successor_1(ssa, block_num, block);
820
0
            }
821
110
          }
822
836
          break;
823
2.18k
        case ZEND_JMP_SET:
824
2.18k
          if (ssa->vars[ssa_op->result_def].use_chain < 0
825
2.18k
              && ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
826
1.01k
            opline->opcode = ZEND_JMPNZ;
827
1.01k
            opline->result_type = IS_UNUSED;
828
1.01k
            zend_ssa_remove_result_def(ssa, ssa_op);
829
1.01k
            goto optimize_jmpnz;
830
1.16k
          } else if (opline->op1_type == IS_CONST) {
831
10
            if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
832
6
              MAKE_NOP(opline);
833
6
              removed_ops++;
834
6
              take_successor_1(ssa, block_num, block);
835
6
              zend_ssa_remove_result_def(ssa, ssa_op);
836
6
              goto optimize_nop;
837
6
            }
838
10
          }
839
1.16k
          break;
840
4.39k
        case ZEND_COALESCE:
841
4.39k
        {
842
4.39k
          zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
843
4.39k
          if (opline->op1_type == IS_CONST
844
750
              && var->use_chain < 0 && var->phi_use_chain == NULL) {
845
712
            if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
846
142
              zend_ssa_remove_result_def(ssa, ssa_op);
847
142
              MAKE_NOP(opline);
848
142
              removed_ops++;
849
142
              take_successor_1(ssa, block_num, block);
850
142
              goto optimize_nop;
851
570
            } else {
852
570
              opline->opcode = ZEND_JMP;
853
570
              opline->result_type = IS_UNUSED;
854
570
              zend_ssa_remove_result_def(ssa, ssa_op);
855
570
              COPY_NODE(opline->op1, opline->op2);
856
570
              take_successor_0(ssa, block_num, block);
857
570
              goto optimize_jmp;
858
570
            }
859
712
          }
860
3.67k
          break;
861
4.39k
        }
862
64.4k
        case ZEND_JMP_NULL:
863
64.4k
        {
864
64.4k
          zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
865
64.4k
          if (opline->op1_type == IS_CONST
866
68
              && var->use_chain < 0 && var->phi_use_chain == NULL) {
867
40
            if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
868
12
              opline->opcode = ZEND_JMP;
869
12
              opline->result_type = IS_UNUSED;
870
12
              zend_ssa_remove_result_def(ssa, ssa_op);
871
12
              COPY_NODE(opline->op1, opline->op2);
872
12
              take_successor_0(ssa, block_num, block);
873
12
              goto optimize_jmp;
874
28
            } else {
875
28
              zend_ssa_remove_result_def(ssa, ssa_op);
876
28
              MAKE_NOP(opline);
877
28
              removed_ops++;
878
28
              take_successor_1(ssa, block_num, block);
879
28
              goto optimize_nop;
880
28
            }
881
40
          }
882
64.4k
          break;
883
64.4k
        }
884
64.4k
        case ZEND_SWITCH_LONG:
885
118
        case ZEND_SWITCH_STRING:
886
442
        case ZEND_MATCH:
887
442
          if (opline->op1_type == IS_CONST) {
888
22
            zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
889
22
            uint8_t type = Z_TYPE_P(zv);
890
22
            bool correct_type =
891
22
              (opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
892
22
              || (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
893
22
              || (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
894
895
            /* Switch statements have a fallback chain for loose comparison. In those
896
             * cases the SWITCH_* instruction is a NOP. Match does strict comparison and
897
             * thus jumps to the default branch on mismatched types, so we need to
898
             * convert MATCH to a jmp. */
899
22
            if (!correct_type && opline->opcode != ZEND_MATCH) {
900
10
              removed_ops++;
901
10
              MAKE_NOP(opline);
902
10
              opline->extended_value = 0;
903
10
              take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]);
904
10
              goto optimize_nop;
905
10
            }
906
907
12
            uint32_t target;
908
12
            if (correct_type) {
909
6
              HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
910
6
              zval *jmp_zv = type == IS_LONG
911
6
                ? zend_hash_index_find(jmptable, Z_LVAL_P(zv))
912
6
                : zend_hash_find(jmptable, Z_STR_P(zv));
913
914
6
              if (jmp_zv) {
915
2
                target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv));
916
4
              } else {
917
4
                target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
918
4
              }
919
6
            } else {
920
6
              ZEND_ASSERT(opline->opcode == ZEND_MATCH);
921
6
              target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
922
6
            }
923
12
            opline->opcode = ZEND_JMP;
924
12
            opline->extended_value = 0;
925
12
            SET_UNUSED(opline->op1);
926
12
            ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target);
927
12
            SET_UNUSED(opline->op2);
928
12
            take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]);
929
12
            goto optimize_jmp;
930
12
          }
931
420
          break;
932
420
        case ZEND_NOP:
933
2.51k
optimize_nop:
934
2.51k
          compress_block(op_array, block);
935
2.51k
          if (block->len == 0) {
936
1.21k
            if (block_num > 0) {
937
1.12k
              zend_ssa_unlink_block(op_array, ssa, block, block_num);
938
              /* backtrack to previous basic block */
939
1.12k
              int backtracking_block_num = block_num;
940
9.29k
              do {
941
9.29k
                backtracking_block_num--;
942
9.29k
              } while (backtracking_block_num >= 0
943
9.29k
                && !(ssa->cfg.blocks[backtracking_block_num].flags & ZEND_BB_REACHABLE));
944
1.12k
              if (backtracking_block_num >= 0) {
945
1.12k
                block_num = backtracking_block_num;
946
1.12k
                continue;
947
1.12k
              }
948
1.12k
            }
949
1.21k
          }
950
1.38k
          break;
951
247k
        default:
952
247k
          break;
953
379k
      }
954
379k
    }
955
956
379k
    block_num = next_block_num;
957
379k
  }
958
959
132k
  return removed_ops;
960
132k
}
961
962
static bool zend_dfa_try_to_replace_result(zend_op_array *op_array, zend_ssa *ssa, int def, int cv_var)
963
12.0k
{
964
12.0k
  int result_var = ssa->ops[def].result_def;
965
12.0k
  uint32_t cv = EX_NUM_TO_VAR(ssa->vars[cv_var].var);
966
967
12.0k
  if (result_var >= 0
968
12.0k
   && !(ssa->var_info[cv_var].type & MAY_BE_REF)
969
6.90k
   && ssa->vars[cv_var].alias == NO_ALIAS
970
6.90k
   && ssa->vars[result_var].phi_use_chain == NULL
971
6.89k
   && ssa->vars[result_var].sym_use_chain == NULL) {
972
6.89k
    int use = ssa->vars[result_var].use_chain;
973
974
6.89k
    if (use >= 0
975
6.44k
     && zend_ssa_next_use(ssa->ops, result_var, use) < 0
976
6.44k
     && op_array->opcodes[use].opcode != ZEND_FREE
977
6.44k
     && op_array->opcodes[use].opcode != ZEND_SEND_VAL
978
6.43k
     && op_array->opcodes[use].opcode != ZEND_SEND_VAL_EX
979
6.41k
     && op_array->opcodes[use].opcode != ZEND_VERIFY_RETURN_TYPE
980
6.41k
     && op_array->opcodes[use].opcode != ZEND_YIELD) {
981
6.40k
      if (use > def) {
982
6.40k
        int i = use;
983
6.40k
        const zend_op *opline = &op_array->opcodes[use];
984
985
12.5k
        while (i > def) {
986
7.43k
          if ((opline->op1_type == IS_CV && opline->op1.var == cv)
987
6.23k
           || (opline->op2_type == IS_CV && opline->op2.var == cv)
988
6.16k
           || (opline->result_type == IS_CV && opline->result.var == cv)) {
989
1.27k
            return false;
990
1.27k
          }
991
6.16k
          opline--;
992
6.16k
          i--;
993
6.16k
        }
994
995
        /* Update opcodes and reconstruct SSA */
996
5.13k
        ssa->vars[result_var].definition = -1;
997
5.13k
        ssa->vars[result_var].use_chain = -1;
998
5.13k
        ssa->ops[def].result_def = -1;
999
1000
5.13k
        op_array->opcodes[def].result_type = IS_UNUSED;
1001
5.13k
        op_array->opcodes[def].result.var = 0;
1002
1003
5.13k
        if (ssa->ops[use].op1_use == result_var) {
1004
1.62k
          ssa->ops[use].op1_use = cv_var;
1005
1.62k
          ssa->ops[use].op1_use_chain = ssa->vars[cv_var].use_chain;
1006
1.62k
          ssa->vars[cv_var].use_chain = use;
1007
1008
1.62k
          op_array->opcodes[use].op1_type = IS_CV;
1009
1.62k
          op_array->opcodes[use].op1.var = cv;
1010
3.51k
        } else if (ssa->ops[use].op2_use == result_var) {
1011
3.51k
          ssa->ops[use].op2_use = cv_var;
1012
3.51k
          ssa->ops[use].op2_use_chain = ssa->vars[cv_var].use_chain;
1013
3.51k
          ssa->vars[cv_var].use_chain = use;
1014
1015
3.51k
          op_array->opcodes[use].op2_type = IS_CV;
1016
3.51k
          op_array->opcodes[use].op2.var = cv;
1017
3.51k
        } else if (ssa->ops[use].result_use == result_var) {
1018
0
          ssa->ops[use].result_use = cv_var;
1019
0
          ssa->ops[use].res_use_chain = ssa->vars[cv_var].use_chain;
1020
0
          ssa->vars[cv_var].use_chain = use;
1021
1022
0
          op_array->opcodes[use].result_type = IS_CV;
1023
0
          op_array->opcodes[use].result.var = cv;
1024
0
        }
1025
1026
5.13k
        return true;
1027
6.40k
      }
1028
6.40k
    }
1029
6.89k
  }
1030
1031
5.59k
  return false;
1032
12.0k
}
1033
1034
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map)
1035
66.4k
{
1036
66.4k
  if (ctx->debug_level & ZEND_DUMP_BEFORE_DFA_PASS) {
1037
0
    zend_dump_op_array(op_array, ZEND_DUMP_SSA, "before dfa pass", ssa);
1038
0
  }
1039
1040
66.4k
  if (ssa->var_info) {
1041
66.4k
    int op_1;
1042
66.4k
    int v;
1043
66.4k
    int remove_nops = 0;
1044
66.4k
    zend_op *opline;
1045
66.4k
    zend_ssa_op *ssa_op;
1046
66.4k
    zval tmp;
1047
1048
66.4k
#if ZEND_DEBUG_DFA
1049
66.4k
    ssa_verify_integrity(op_array, ssa, "before dfa");
1050
66.4k
#endif
1051
1052
66.4k
    if (ZEND_OPTIMIZER_PASS_8 & ctx->optimization_level) {
1053
66.4k
      if (sccp_optimize_op_array(ctx, op_array, ssa, call_map)) {
1054
1.39k
        remove_nops = 1;
1055
1.39k
      }
1056
1057
66.4k
      if (zend_dfa_optimize_jmps(op_array, ssa)) {
1058
556
        remove_nops = 1;
1059
556
      }
1060
1061
66.4k
#if ZEND_DEBUG_DFA
1062
66.4k
      ssa_verify_integrity(op_array, ssa, "after sccp");
1063
66.4k
#endif
1064
66.4k
      if (ZEND_FUNC_INFO(op_array)) {
1065
66.4k
        if (zend_dfa_optimize_calls(op_array, ssa)) {
1066
0
          remove_nops = 1;
1067
0
        }
1068
66.4k
      }
1069
66.4k
      if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_8) {
1070
0
        zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after sccp pass", ssa);
1071
0
      }
1072
66.4k
#if ZEND_DEBUG_DFA
1073
66.4k
      ssa_verify_integrity(op_array, ssa, "after calls");
1074
66.4k
#endif
1075
66.4k
    }
1076
1077
66.4k
    if (ZEND_OPTIMIZER_PASS_14 & ctx->optimization_level) {
1078
66.4k
      if (dce_optimize_op_array(op_array, ctx, ssa, 0)) {
1079
5.00k
        remove_nops = 1;
1080
5.00k
      }
1081
66.4k
      if (zend_dfa_optimize_jmps(op_array, ssa)) {
1082
221
        remove_nops = 1;
1083
221
      }
1084
66.4k
      if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_14) {
1085
0
        zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dce pass", ssa);
1086
0
      }
1087
66.4k
#if ZEND_DEBUG_DFA
1088
66.4k
      ssa_verify_integrity(op_array, ssa, "after dce");
1089
66.4k
#endif
1090
66.4k
    }
1091
1092
1.04M
    for (v = op_array->last_var; v < ssa->vars_count; v++) {
1093
1094
976k
      op_1 = ssa->vars[v].definition;
1095
1096
976k
      if (op_1 < 0) {
1097
172k
        continue;
1098
172k
      }
1099
1100
804k
      opline = op_array->opcodes + op_1;
1101
804k
      ssa_op = &ssa->ops[op_1];
1102
1103
      /* Convert LONG constants to DOUBLE */
1104
804k
      if (ssa->var_info[v].use_as_double) {
1105
2
        if (opline->opcode == ZEND_ASSIGN
1106
2
         && opline->op2_type == IS_CONST
1107
2
         && ssa->ops[op_1].op1_def == v
1108
2
         && !RETURN_VALUE_USED(opline)
1109
2
        ) {
1110
1111
// op_1: ASSIGN ? -> #v [use_as_double], long(?) => ASSIGN ? -> #v, double(?)
1112
1113
2
          zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
1114
2
          ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
1115
2
          ZVAL_DOUBLE(&tmp, zval_get_double(zv));
1116
2
          opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
1117
1118
2
        } else if (opline->opcode == ZEND_QM_ASSIGN
1119
0
         && opline->op1_type == IS_CONST
1120
0
        ) {
1121
1122
// op_1: QM_ASSIGN #v [use_as_double], long(?) => QM_ASSIGN #v, double(?)
1123
1124
0
          zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
1125
0
          ZEND_ASSERT(Z_TYPE_INFO_P(zv) == IS_LONG);
1126
0
          ZVAL_DOUBLE(&tmp, zval_get_double(zv));
1127
0
          opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
1128
0
        }
1129
1130
804k
      } else {
1131
804k
        if (opline->opcode == ZEND_ADD
1132
797k
         || opline->opcode == ZEND_SUB
1133
792k
         || opline->opcode == ZEND_MUL
1134
786k
         || opline->opcode == ZEND_IS_EQUAL
1135
783k
         || opline->opcode == ZEND_IS_NOT_EQUAL
1136
781k
         || opline->opcode == ZEND_IS_SMALLER
1137
774k
         || opline->opcode == ZEND_IS_SMALLER_OR_EQUAL
1138
804k
        ) {
1139
1140
31.5k
          if (opline->op1_type == IS_CONST && opline->op2_type != IS_CONST) {
1141
3.37k
            zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
1142
1143
3.37k
            if ((OP2_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
1144
34
             && Z_TYPE_INFO_P(zv) == IS_LONG) {
1145
1146
// op_1: #v.? = ADD long(?), #?.? [double] => #v.? = ADD double(?), #?.? [double]
1147
1148
8
              ZVAL_DOUBLE(&tmp, zval_get_double(zv));
1149
8
              opline->op1.constant = zend_optimizer_add_literal(op_array, &tmp);
1150
8
              zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
1151
8
            }
1152
3.37k
            if (opline->opcode == ZEND_ADD) {
1153
711
              zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
1154
1155
711
              if (((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
1156
92
                && Z_TYPE_INFO_P(zv) == IS_LONG
1157
35
                && Z_LVAL_P(zv) == 0)
1158
711
               || ((OP2_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
1159
18
                && Z_TYPE_INFO_P(zv) == IS_DOUBLE
1160
0
                && Z_DVAL_P(zv) == 0.0)) {
1161
1162
// op_1: #v.? = ADD 0, #?.? [double,long] => #v.? = QM_ASSIGN #?.?
1163
1164
0
                opline->opcode = ZEND_QM_ASSIGN;
1165
0
                opline->op1_type = opline->op2_type;
1166
0
                opline->op1.var = opline->op2.var;
1167
0
                opline->op2_type = IS_UNUSED;
1168
0
                opline->op2.num = 0;
1169
0
                ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
1170
0
                ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
1171
0
                ssa->ops[op_1].op2_use = -1;
1172
0
                ssa->ops[op_1].op2_use_chain = -1;
1173
0
              }
1174
2.66k
            } else if (opline->opcode == ZEND_MUL
1175
16
             && (OP2_INFO() & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
1176
2
              zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
1177
1178
2
              if ((Z_TYPE_INFO_P(zv) == IS_LONG
1179
0
                && Z_LVAL_P(zv) == 2)
1180
2
               || (Z_TYPE_INFO_P(zv) == IS_DOUBLE
1181
0
                && Z_DVAL_P(zv) == 2.0
1182
0
                && !(OP2_INFO() & MAY_BE_LONG))) {
1183
1184
// op_1: #v.? = MUL 2, #x.? [double,long] => #v.? = ADD #x.?, #x.?
1185
1186
0
                opline->opcode = ZEND_ADD;
1187
0
                opline->op1_type = opline->op2_type;
1188
0
                opline->op1.var = opline->op2.var;
1189
0
                ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
1190
0
                ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
1191
0
              }
1192
2
            }
1193
28.1k
          } else if (opline->op1_type != IS_CONST && opline->op2_type == IS_CONST) {
1194
12.3k
            zval *zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
1195
1196
12.3k
            if ((OP1_INFO() & MAY_BE_ANY) == MAY_BE_DOUBLE
1197
229
             && Z_TYPE_INFO_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG) {
1198
1199
// op_1: #v.? = ADD #?.? [double], long(?) => #v.? = ADD #?.? [double], double(?)
1200
1201
109
              ZVAL_DOUBLE(&tmp, zval_get_double(zv));
1202
109
              opline->op2.constant = zend_optimizer_add_literal(op_array, &tmp);
1203
109
              zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
1204
109
            }
1205
12.3k
            if (opline->opcode == ZEND_ADD || opline->opcode == ZEND_SUB) {
1206
3.08k
              if (((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_LONG
1207
796
                && Z_TYPE_INFO_P(zv) == IS_LONG
1208
763
                && Z_LVAL_P(zv) == 0)
1209
3.06k
               || ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_DOUBLE
1210
58
                && Z_TYPE_INFO_P(zv) == IS_DOUBLE
1211
28
                && Z_DVAL_P(zv) == 0.0)) {
1212
1213
// op_1: #v.? = ADD #?.? [double,long], 0 => #v.? = QM_ASSIGN #?.?
1214
1215
20
                opline->opcode = ZEND_QM_ASSIGN;
1216
20
                opline->op2_type = IS_UNUSED;
1217
20
                opline->op2.num = 0;
1218
20
              }
1219
9.25k
            } else if (opline->opcode == ZEND_MUL
1220
2.40k
             && (OP1_INFO() & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
1221
652
              zv = CT_CONSTANT_EX(op_array, opline->op2.constant);
1222
1223
652
              if ((Z_TYPE_INFO_P(zv) == IS_LONG
1224
514
                && Z_LVAL_P(zv) == 2)
1225
590
               || (Z_TYPE_INFO_P(zv) == IS_DOUBLE
1226
37
                && Z_DVAL_P(zv) == 2.0
1227
68
                && !(OP1_INFO() & MAY_BE_LONG))) {
1228
1229
// op_1: #v.? = MUL #x.? [double,long], 2 => #v.? = ADD #x.?, #x.?
1230
1231
68
                opline->opcode = ZEND_ADD;
1232
68
                opline->op2_type = opline->op1_type;
1233
68
                opline->op2.var = opline->op1.var;
1234
68
                ssa->ops[op_1].op2_use = ssa->ops[op_1].op1_use;
1235
68
                ssa->ops[op_1].op2_use_chain = ssa->ops[op_1].op1_use_chain;
1236
68
              }
1237
652
            }
1238
12.3k
          }
1239
772k
        } else if (opline->opcode == ZEND_CONCAT) {
1240
7.57k
          if (!(OP1_INFO() & MAY_BE_OBJECT)
1241
5.21k
           && !(OP2_INFO() & MAY_BE_OBJECT)) {
1242
3.90k
            opline->opcode = ZEND_FAST_CONCAT;
1243
3.90k
          }
1244
764k
        } else if (opline->opcode == ZEND_VERIFY_RETURN_TYPE
1245
1.87k
         && opline->op1_type != IS_CONST
1246
1.74k
         && ssa->ops[op_1].op1_def == v
1247
1.74k
         && ssa->ops[op_1].op1_use >= 0) {
1248
1.74k
          int orig_var = ssa->ops[op_1].op1_use;
1249
1.74k
          int ret = ssa->vars[v].use_chain;
1250
1251
1.74k
          if (ssa->ops[op_1].op1_use_chain == -1
1252
1.70k
           && can_elide_return_type_check(ctx->script, op_array, ssa, &ssa->ops[op_1])) {
1253
1254
// op_1: VERIFY_RETURN_TYPE #orig_var.? [T] -> #v.? [T] => NOP
1255
1256
849
            zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
1257
1258
849
            if (ret >= 0) {
1259
849
              ssa->ops[ret].op1_use = orig_var;
1260
849
              ssa->ops[ret].op1_use_chain = ssa->vars[orig_var].use_chain;
1261
849
              ssa->vars[orig_var].use_chain = ret;
1262
849
            }
1263
1264
849
            ssa->vars[v].definition = -1;
1265
849
            ssa->vars[v].use_chain = -1;
1266
1267
849
            ssa->ops[op_1].op1_def = -1;
1268
849
            ssa->ops[op_1].op1_use = -1;
1269
1270
849
            MAKE_NOP(opline);
1271
849
            remove_nops = 1;
1272
894
          } else if (ret >= 0
1273
894
           && ssa->ops[ret].op1_use == v
1274
894
           && ssa->ops[ret].op1_use_chain == -1
1275
894
           && can_elide_return_type_check(ctx->script, op_array, ssa, &ssa->ops[op_1])) {
1276
1277
// op_1: VERIFY_RETURN_TYPE #orig_var.? [T] -> #v.? [T] => NOP
1278
1279
30
            zend_ssa_replace_use_chain(ssa, op_1, ret, orig_var);
1280
1281
30
            ssa->ops[ret].op1_use = orig_var;
1282
30
            ssa->ops[ret].op1_use_chain = ssa->ops[op_1].op1_use_chain;
1283
1284
30
            ssa->vars[v].definition = -1;
1285
30
            ssa->vars[v].use_chain = -1;
1286
1287
30
            ssa->ops[op_1].op1_def = -1;
1288
30
            ssa->ops[op_1].op1_use = -1;
1289
1290
30
            MAKE_NOP(opline);
1291
30
            remove_nops = 1;
1292
30
          }
1293
1.74k
        }
1294
804k
      }
1295
1296
804k
      if (opline->opcode == ZEND_QM_ASSIGN
1297
6.51k
       && ssa->ops[op_1].result_def == v
1298
6.51k
       && opline->op1_type & (IS_TMP_VAR|IS_VAR)
1299
768
       && !(ssa->var_info[v].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
1300
804k
      ) {
1301
1302
102
        int src_var = ssa->ops[op_1].op1_use;
1303
1304
102
        if (src_var >= 0
1305
102
         && !(ssa->var_info[src_var].type & MAY_BE_REF)
1306
102
         && (ssa->var_info[src_var].type & (MAY_BE_UNDEF|MAY_BE_ANY))
1307
102
         && ssa->vars[src_var].definition >= 0
1308
16
         && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
1309
16
         && ssa->ops[ssa->vars[src_var].definition].result_use < 0
1310
16
         && ssa->vars[src_var].use_chain == op_1
1311
16
         && ssa->ops[op_1].op1_use_chain < 0
1312
16
         && !ssa->vars[src_var].phi_use_chain
1313
16
         && !ssa->vars[src_var].sym_use_chain
1314
16
         && opline_supports_assign_contraction(
1315
16
           op_array, ssa, &op_array->opcodes[ssa->vars[src_var].definition],
1316
16
           src_var, opline->result.var)
1317
16
         && !variable_defined_or_used_in_range(ssa, EX_VAR_TO_NUM(opline->result.var),
1318
16
            ssa->vars[src_var].definition+1, op_1)
1319
102
        ) {
1320
1321
16
          int orig_var = ssa->ops[op_1].result_use;
1322
16
          int op_2 = ssa->vars[src_var].definition;
1323
1324
// op_2: #src_var.T = OP ...                                        => #v.CV = OP ...
1325
// op_1: QM_ASSIGN #src_var.T #orig_var.CV [undef,scalar] -> #v.CV,    NOP
1326
1327
16
          if (orig_var >= 0) {
1328
0
            zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
1329
0
          }
1330
1331
          /* Reconstruct SSA */
1332
16
          ssa->vars[v].definition = op_2;
1333
16
          ssa->ops[op_2].result_def = v;
1334
1335
16
          ssa->vars[src_var].definition = -1;
1336
16
          ssa->vars[src_var].use_chain = -1;
1337
1338
16
          ssa->ops[op_1].op1_use = -1;
1339
16
          ssa->ops[op_1].op1_def = -1;
1340
16
          ssa->ops[op_1].op1_use_chain = -1;
1341
16
          ssa->ops[op_1].result_use = -1;
1342
16
          ssa->ops[op_1].result_def = -1;
1343
16
          ssa->ops[op_1].res_use_chain = -1;
1344
1345
          /* Update opcodes */
1346
16
          op_array->opcodes[op_2].result_type = opline->result_type;
1347
16
          op_array->opcodes[op_2].result.var = opline->result.var;
1348
1349
16
          MAKE_NOP(opline);
1350
16
          remove_nops = 1;
1351
1352
16
          if (op_array->opcodes[op_2].opcode == ZEND_SUB
1353
2
           && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
1354
2
           && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
1355
0
           && op_array->opcodes[op_2].op2_type == IS_CONST
1356
0
           && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
1357
0
           && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
1358
0
           && ssa->ops[op_2].op1_use >= 0
1359
0
           && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1360
1361
0
            op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
1362
0
            SET_UNUSED(op_array->opcodes[op_2].op2);
1363
0
            SET_UNUSED(op_array->opcodes[op_2].result);
1364
1365
0
            ssa->ops[op_2].result_def = -1;
1366
0
            ssa->ops[op_2].op1_def = v;
1367
1368
16
          } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
1369
0
           && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
1370
0
           && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
1371
0
           && op_array->opcodes[op_2].op2_type == IS_CONST
1372
0
           && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
1373
0
           && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
1374
0
           && ssa->ops[op_2].op1_use >= 0
1375
0
           && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1376
1377
0
            op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
1378
0
            SET_UNUSED(op_array->opcodes[op_2].op2);
1379
0
            SET_UNUSED(op_array->opcodes[op_2].result);
1380
1381
0
            ssa->ops[op_2].result_def = -1;
1382
0
            ssa->ops[op_2].op1_def = v;
1383
1384
16
          } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
1385
0
           && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
1386
0
           && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
1387
0
           && op_array->opcodes[op_2].op1_type == IS_CONST
1388
0
           && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
1389
0
           && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
1390
0
           && ssa->ops[op_2].op2_use >= 0
1391
0
           && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1392
1393
0
            op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
1394
0
            op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
1395
0
            op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
1396
0
            SET_UNUSED(op_array->opcodes[op_2].op2);
1397
0
            SET_UNUSED(op_array->opcodes[op_2].result);
1398
1399
0
            ssa->ops[op_2].result_def = -1;
1400
0
            ssa->ops[op_2].op1_def = v;
1401
0
            ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
1402
0
            ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
1403
0
            ssa->ops[op_2].op2_use = -1;
1404
0
            ssa->ops[op_2].op2_use_chain = -1;
1405
0
          }
1406
16
        }
1407
102
      }
1408
1409
804k
      if (ssa->vars[v].var >= op_array->last_var) {
1410
        /* skip TMP and VAR */
1411
678k
        continue;
1412
678k
      }
1413
1414
125k
      if (ssa->ops[op_1].op1_def == v
1415
98.3k
       && RETURN_VALUE_USED(opline)) {
1416
20.6k
        if (opline->opcode == ZEND_ASSIGN
1417
11.2k
         || opline->opcode == ZEND_ASSIGN_OP
1418
9.38k
         || opline->opcode == ZEND_PRE_INC
1419
11.9k
         || opline->opcode == ZEND_PRE_DEC) {
1420
11.9k
          zend_dfa_try_to_replace_result(op_array, ssa, op_1, v);
1421
11.9k
        } else if (opline->opcode == ZEND_POST_INC) {
1422
779
          int result_var = ssa->ops[op_1].result_def;
1423
1424
779
          if (result_var >= 0
1425
779
           && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
1426
410
            int use = ssa->vars[result_var].use_chain;
1427
1428
410
            if (use >= 0 && op_array->opcodes[use].opcode == ZEND_IS_SMALLER
1429
42
             && ssa->ops[use].op1_use == result_var
1430
42
             && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
1431
42
              opline->opcode = ZEND_PRE_INC;
1432
42
              op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
1433
42
            }
1434
410
          }
1435
7.89k
        } else if (opline->opcode == ZEND_POST_DEC) {
1436
963
          int result_var = ssa->ops[op_1].result_def;
1437
1438
963
          if (result_var >= 0
1439
963
           && (ssa->var_info[result_var].type & ((MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF) - (MAY_BE_LONG|MAY_BE_DOUBLE))) == 0) {
1440
345
            int use = ssa->vars[result_var].use_chain;
1441
1442
345
            if (use >= 0 && op_array->opcodes[use].opcode == ZEND_IS_SMALLER
1443
1
             && ssa->ops[use].op2_use == result_var
1444
1
             && zend_dfa_try_to_replace_result(op_array, ssa, op_1, v)) {
1445
1
              opline->opcode = ZEND_PRE_DEC;
1446
1
              op_array->opcodes[use].opcode = ZEND_IS_SMALLER_OR_EQUAL;
1447
1
            }
1448
345
          }
1449
963
        }
1450
20.6k
      }
1451
1452
125k
      if (opline->opcode == ZEND_ASSIGN
1453
52.6k
       && ssa->ops[op_1].op1_def == v
1454
52.6k
       && !RETURN_VALUE_USED(opline)
1455
125k
      ) {
1456
47.1k
        int orig_var = ssa->ops[op_1].op1_use;
1457
1458
47.1k
        if (orig_var >= 0
1459
47.0k
         && !(ssa->var_info[orig_var].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
1460
47.1k
        ) {
1461
10.3k
          int src_var = ssa->ops[op_1].op2_use;
1462
1463
10.3k
          if ((opline->op2_type & (IS_TMP_VAR|IS_VAR))
1464
5.73k
           && src_var >= 0
1465
5.73k
           && !(ssa->var_info[src_var].type & MAY_BE_REF)
1466
5.72k
           && (ssa->var_info[src_var].type & (MAY_BE_UNDEF|MAY_BE_ANY))
1467
5.62k
           && ssa->vars[src_var].definition >= 0
1468
5.35k
           && ssa->ops[ssa->vars[src_var].definition].result_def == src_var
1469
5.35k
           && ssa->ops[ssa->vars[src_var].definition].result_use < 0
1470
5.29k
           && ssa->vars[src_var].use_chain == op_1
1471
5.22k
           && ssa->ops[op_1].op2_use_chain < 0
1472
5.22k
           && !ssa->vars[src_var].phi_use_chain
1473
5.22k
           && !ssa->vars[src_var].sym_use_chain
1474
5.22k
           && opline_supports_assign_contraction(
1475
5.22k
             op_array, ssa, &op_array->opcodes[ssa->vars[src_var].definition],
1476
5.22k
             src_var, opline->op1.var)
1477
3.67k
           && !variable_defined_or_used_in_range(ssa, EX_VAR_TO_NUM(opline->op1.var),
1478
3.67k
              ssa->vars[src_var].definition+1, op_1)
1479
10.3k
          ) {
1480
1481
3.67k
            int op_2 = ssa->vars[src_var].definition;
1482
1483
// op_2: #src_var.T = OP ...                                     => #v.CV = OP ...
1484
// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, #src_var.T    NOP
1485
1486
3.67k
            zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
1487
            /* Reconstruct SSA */
1488
3.67k
            ssa->vars[v].definition = op_2;
1489
3.67k
            ssa->ops[op_2].result_def = v;
1490
1491
3.67k
            ssa->vars[src_var].definition = -1;
1492
3.67k
            ssa->vars[src_var].use_chain = -1;
1493
1494
3.67k
            ssa->ops[op_1].op1_use = -1;
1495
3.67k
            ssa->ops[op_1].op2_use = -1;
1496
3.67k
            ssa->ops[op_1].op1_def = -1;
1497
3.67k
            ssa->ops[op_1].op1_use_chain = -1;
1498
1499
            /* Update opcodes */
1500
3.67k
            op_array->opcodes[op_2].result_type = opline->op1_type;
1501
3.67k
            op_array->opcodes[op_2].result.var = opline->op1.var;
1502
1503
3.67k
            MAKE_NOP(opline);
1504
3.67k
            remove_nops = 1;
1505
1506
3.67k
            if (op_array->opcodes[op_2].opcode == ZEND_SUB
1507
359
             && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
1508
196
             && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
1509
30
             && op_array->opcodes[op_2].op2_type == IS_CONST
1510
10
             && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
1511
10
             && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
1512
0
             && ssa->ops[op_2].op1_use >= 0
1513
0
             && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1514
1515
0
              op_array->opcodes[op_2].opcode = ZEND_PRE_DEC;
1516
0
              SET_UNUSED(op_array->opcodes[op_2].op2);
1517
0
              SET_UNUSED(op_array->opcodes[op_2].result);
1518
1519
0
              ssa->ops[op_2].result_def = -1;
1520
0
              ssa->ops[op_2].op1_def = v;
1521
1522
3.67k
            } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
1523
1.49k
             && op_array->opcodes[op_2].op1_type == op_array->opcodes[op_2].result_type
1524
833
             && op_array->opcodes[op_2].op1.var == op_array->opcodes[op_2].result.var
1525
512
             && op_array->opcodes[op_2].op2_type == IS_CONST
1526
47
             && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == IS_LONG
1527
12
             && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op2.constant)) == 1
1528
0
             && ssa->ops[op_2].op1_use >= 0
1529
0
             && !(ssa->var_info[ssa->ops[op_2].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1530
1531
0
              op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
1532
0
              SET_UNUSED(op_array->opcodes[op_2].op2);
1533
0
              SET_UNUSED(op_array->opcodes[op_2].result);
1534
1535
0
              ssa->ops[op_2].result_def = -1;
1536
0
              ssa->ops[op_2].op1_def = v;
1537
1538
3.67k
            } else if (op_array->opcodes[op_2].opcode == ZEND_ADD
1539
1.49k
             && op_array->opcodes[op_2].op2_type == op_array->opcodes[op_2].result_type
1540
847
             && op_array->opcodes[op_2].op2.var == op_array->opcodes[op_2].result.var
1541
184
             && op_array->opcodes[op_2].op1_type == IS_CONST
1542
4
             && Z_TYPE_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == IS_LONG
1543
0
             && Z_LVAL_P(CT_CONSTANT_EX(op_array, op_array->opcodes[op_2].op1.constant)) == 1
1544
0
             && ssa->ops[op_2].op2_use >= 0
1545
0
             && !(ssa->var_info[ssa->ops[op_2].op2_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1546
1547
0
              op_array->opcodes[op_2].opcode = ZEND_PRE_INC;
1548
0
              op_array->opcodes[op_2].op1_type = op_array->opcodes[op_2].op2_type;
1549
0
              op_array->opcodes[op_2].op1.var = op_array->opcodes[op_2].op2.var;
1550
0
              SET_UNUSED(op_array->opcodes[op_2].op2);
1551
0
              SET_UNUSED(op_array->opcodes[op_2].result);
1552
1553
0
              ssa->ops[op_2].result_def = -1;
1554
0
              ssa->ops[op_2].op1_def = v;
1555
0
              ssa->ops[op_2].op1_use = ssa->ops[op_2].op2_use;
1556
0
              ssa->ops[op_2].op1_use_chain = ssa->ops[op_2].op2_use_chain;
1557
0
              ssa->ops[op_2].op2_use = -1;
1558
0
              ssa->ops[op_2].op2_use_chain = -1;
1559
0
            }
1560
6.63k
          } else if (opline->op2_type == IS_CONST
1561
3.34k
           || ((opline->op2_type & (IS_TMP_VAR|IS_VAR|IS_CV))
1562
3.34k
               && ssa->ops[op_1].op2_use >= 0
1563
3.34k
               && ssa->ops[op_1].op2_def < 0)
1564
6.63k
          ) {
1565
1566
// op_1: ASSIGN #orig_var.CV [undef,scalar] -> #v.CV, CONST|TMPVAR => QM_ASSIGN v.CV, CONST|TMPVAR
1567
1568
6.63k
            if (ssa->ops[op_1].op1_use != ssa->ops[op_1].op2_use) {
1569
6.43k
              zend_ssa_unlink_use_chain(ssa, op_1, orig_var);
1570
6.43k
            } else {
1571
207
              ssa->ops[op_1].op2_use_chain = ssa->ops[op_1].op1_use_chain;
1572
207
            }
1573
1574
            /* Reconstruct SSA */
1575
6.63k
            ssa->ops[op_1].result_def = v;
1576
6.63k
            ssa->ops[op_1].op1_def = -1;
1577
6.63k
            ssa->ops[op_1].op1_use = ssa->ops[op_1].op2_use;
1578
6.63k
            ssa->ops[op_1].op1_use_chain = ssa->ops[op_1].op2_use_chain;
1579
6.63k
            ssa->ops[op_1].op2_use = -1;
1580
6.63k
            ssa->ops[op_1].op2_use_chain = -1;
1581
1582
            /* Update opcode */
1583
6.63k
            opline->result_type = opline->op1_type;
1584
6.63k
            opline->result.var = opline->op1.var;
1585
6.63k
            opline->op1_type = opline->op2_type;
1586
6.63k
            opline->op1.var = opline->op2.var;
1587
6.63k
            opline->op2_type = IS_UNUSED;
1588
6.63k
            opline->op2.var = 0;
1589
6.63k
            opline->opcode = ZEND_QM_ASSIGN;
1590
6.63k
          }
1591
10.3k
        }
1592
1593
78.7k
      } else if (opline->opcode == ZEND_ASSIGN_OP
1594
4.68k
       && opline->extended_value == ZEND_ADD
1595
1.23k
       && ssa->ops[op_1].op1_def == v
1596
1.23k
       && opline->op2_type == IS_CONST
1597
216
       && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
1598
190
       && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
1599
74
       && ssa->ops[op_1].op1_use >= 0
1600
74
       && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1601
1602
// op_1: ASSIGN_ADD #?.CV [undef,null,int,foat] ->#v.CV, int(1) => PRE_INC #?.CV ->#v.CV
1603
1604
10
        opline->opcode = ZEND_PRE_INC;
1605
10
        opline->extended_value = 0;
1606
10
        SET_UNUSED(opline->op2);
1607
1608
78.7k
      } else if (opline->opcode == ZEND_ASSIGN_OP
1609
4.67k
       && opline->extended_value == ZEND_SUB
1610
257
       && ssa->ops[op_1].op1_def == v
1611
257
       && opline->op2_type == IS_CONST
1612
150
       && Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == IS_LONG
1613
137
       && Z_LVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) == 1
1614
2
       && ssa->ops[op_1].op1_use >= 0
1615
2
       && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
1616
1617
// op_1: ASSIGN_SUB #?.CV [undef,null,int,foat] -> #v.CV, int(1) => PRE_DEC #?.CV ->#v.CV
1618
1619
0
        opline->opcode = ZEND_PRE_DEC;
1620
0
        opline->extended_value = 0;
1621
0
        SET_UNUSED(opline->op2);
1622
1623
78.7k
      } else if (ssa->ops[op_1].op1_def == v
1624
51.1k
       && !RETURN_VALUE_USED(opline)
1625
35.6k
       && ssa->ops[op_1].op1_use >= 0
1626
35.6k
       && !(ssa->var_info[ssa->ops[op_1].op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))
1627
12.5k
       && opline->opcode == ZEND_ASSIGN_OP
1628
1.71k
       && opline->extended_value != ZEND_CONCAT) {
1629
1630
// op_1: ASSIGN_OP #orig_var.CV [undef,null,bool,int,double] -> #v.CV, ? => #v.CV = ADD #orig_var.CV, ?
1631
1632
        /* Reconstruct SSA */
1633
1.45k
        ssa->ops[op_1].result_def = ssa->ops[op_1].op1_def;
1634
1.45k
        ssa->ops[op_1].op1_def = -1;
1635
1636
        /* Update opcode */
1637
1.45k
        opline->opcode = opline->extended_value;
1638
1.45k
        opline->extended_value = 0;
1639
1.45k
        opline->result_type = opline->op1_type;
1640
1.45k
        opline->result.var = opline->op1.var;
1641
1642
1.45k
      }
1643
125k
    }
1644
1645
66.4k
#if ZEND_DEBUG_DFA
1646
66.4k
    ssa_verify_integrity(op_array, ssa, "after dfa");
1647
66.4k
#endif
1648
1649
66.4k
    if (remove_nops) {
1650
6.94k
      zend_ssa_remove_nops(op_array, ssa, ctx);
1651
6.94k
#if ZEND_DEBUG_DFA
1652
6.94k
      ssa_verify_integrity(op_array, ssa, "after nop");
1653
6.94k
#endif
1654
6.94k
    }
1655
66.4k
  }
1656
1657
66.4k
  if (ctx->debug_level & ZEND_DUMP_AFTER_DFA_PASS) {
1658
0
    zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dfa pass", ssa);
1659
0
  }
1660
66.4k
}
1661
1662
void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1663
0
{
1664
0
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
1665
0
  zend_ssa ssa;
1666
1667
0
  if (zend_dfa_analyze_op_array(op_array, ctx, &ssa) == FAILURE) {
1668
0
    zend_arena_release(&ctx->arena, checkpoint);
1669
0
    return;
1670
0
  }
1671
1672
0
  zend_dfa_optimize_op_array(op_array, ctx, &ssa, NULL);
1673
1674
  /* Destroy SSA */
1675
0
  zend_arena_release(&ctx->arena, checkpoint);
1676
0
}