Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/Optimizer/block_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: Andi Gutmans <andi@php.net>                                 |
16
   |          Zeev Suraski <zeev@php.net>                                 |
17
   |          Stanislav Malyshev <stas@zend.com>                          |
18
   |          Dmitry Stogov <dmitry@php.net>                              |
19
   +----------------------------------------------------------------------+
20
*/
21
22
#include "Optimizer/zend_optimizer.h"
23
#include "Optimizer/zend_optimizer_internal.h"
24
#include "zend_API.h"
25
#include "zend_constants.h"
26
#include "zend_execute.h"
27
#include "zend_vm.h"
28
#include "zend_bitset.h"
29
#include "zend_cfg.h"
30
#include "zend_dump.h"
31
32
/* Checks if a constant (like "true") may be replaced by its value */
33
bool zend_optimizer_get_persistent_constant(zend_string *name, zval *result, bool copy)
34
19.2k
{
35
19.2k
  const zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
36
19.2k
  if (c) {
37
26
    if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
38
26
     && !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
39
0
     && (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
40
0
      || !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
41
0
      ZVAL_COPY_VALUE(result, &c->value);
42
0
      if (copy) {
43
0
        Z_TRY_ADDREF_P(result);
44
0
      }
45
0
      return true;
46
26
    } else {
47
26
      return false;
48
26
    }
49
26
  }
50
51
  /* Special constants null/true/false can always be substituted. */
52
19.2k
  c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
53
19.2k
  if (c) {
54
0
    ZVAL_COPY_VALUE(result, &c->value);
55
0
    return true;
56
0
  }
57
19.2k
  return false;
58
19.2k
}
59
60
/* Data dependencies macros */
61
62
1.71M
#define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)]
63
1.35M
#define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline
64
65
static void strip_leading_nops(const zend_op_array *op_array, zend_basic_block *b)
66
9.21k
{
67
9.21k
  const zend_op *opcodes = op_array->opcodes;
68
69
11.2k
  do {
70
11.2k
    b->start++;
71
11.2k
    b->len--;
72
11.2k
  } while (b->len > 0 && opcodes[b->start].opcode == ZEND_NOP);
73
9.21k
}
74
75
static void strip_nops(const zend_op_array *op_array, zend_basic_block *b)
76
487k
{
77
487k
  uint32_t i, j;
78
79
487k
  if (b->len == 0) {
80
967
    return;
81
967
  }
82
83
486k
  if (op_array->opcodes[b->start].opcode == ZEND_NOP) {
84
3.61k
    strip_leading_nops(op_array, b);
85
3.61k
  }
86
87
486k
  if (b->len == 0) {
88
320
    return;
89
320
  }
90
91
  /* strip the inside NOPs */
92
486k
  i = j = b->start + 1;
93
2.91M
  while (i < b->start + b->len) {
94
2.43M
    if (op_array->opcodes[i].opcode != ZEND_NOP) {
95
2.40M
      if (i != j) {
96
53.5k
        op_array->opcodes[j] = op_array->opcodes[i];
97
53.5k
      }
98
2.40M
      j++;
99
2.40M
    }
100
2.43M
    i++;
101
2.43M
  }
102
486k
  b->len = j - b->start;
103
509k
  while (j < i) {
104
22.8k
    MAKE_NOP(op_array->opcodes + j);
105
22.8k
    j++;
106
22.8k
  }
107
486k
}
108
109
68
static uint32_t get_const_switch_target(const zend_cfg *cfg, const zend_op_array *op_array, const zend_basic_block *block, zend_op *opline, const zval *val) {
110
68
  HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
111
68
  zval *zv;
112
68
  if ((opline->opcode == ZEND_SWITCH_LONG && Z_TYPE_P(val) != IS_LONG)
113
68
      || (opline->opcode == ZEND_SWITCH_STRING && Z_TYPE_P(val) != IS_STRING)) {
114
    /* fallback to next block */
115
0
    return block->successors[block->successors_count - 1];
116
0
  }
117
68
  if (opline->opcode == ZEND_MATCH && Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_STRING) {
118
    /* always jump to the default arm */
119
14
    return block->successors[block->successors_count - 1];
120
14
  }
121
54
  if (Z_TYPE_P(val) == IS_LONG) {
122
30
    zv = zend_hash_index_find(jumptable, Z_LVAL_P(val));
123
30
  } else {
124
24
    ZEND_ASSERT(Z_TYPE_P(val) == IS_STRING);
125
24
    zv = zend_hash_find(jumptable, Z_STR_P(val));
126
24
  }
127
54
  if (!zv) {
128
    /* default */
129
30
    return block->successors[block->successors_count - (opline->opcode == ZEND_MATCH ? 1 : 2)];
130
30
  }
131
24
  return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
132
54
}
133
134
static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array, zend_bitset used_ext, zend_cfg *cfg, zend_op **Tsource, uint32_t *opt_count)
135
487k
{
136
487k
  zend_op *opline, *src;
137
487k
  zend_op *end, *last_op = NULL;
138
139
487k
  if (block->len == 0) {
140
221
    return;
141
221
  }
142
143
487k
  if (op_array->opcodes[block->start].opcode == ZEND_NOP) {
144
    /* remove leading NOPs */
145
5.60k
    strip_leading_nops(op_array, block);
146
5.60k
  }
147
148
487k
  opline = op_array->opcodes + block->start;
149
487k
  end = opline + block->len;
150
3.41M
  while (opline < end) {
151
    /* Constant Propagation: strip X = QM_ASSIGN(const) */
152
2.92M
    if (opline->op1_type == IS_TMP_VAR &&
153
799k
        opline->opcode != ZEND_FREE) {
154
771k
      src = VAR_SOURCE(opline->op1);
155
771k
      if (src &&
156
678k
          src->opcode == ZEND_QM_ASSIGN &&
157
2.58k
          src->op1_type == IS_CONST
158
771k
      ) {
159
1.73k
        znode_op op1 = opline->op1;
160
1.73k
        if (opline->opcode == ZEND_VERIFY_RETURN_TYPE) {
161
2
          COPY_NODE(opline->result, opline->op1);
162
2
          COPY_NODE(opline->op1, src->op1);
163
2
          VAR_SOURCE(op1) = NULL;
164
2
          MAKE_NOP(src);
165
2
          ++(*opt_count);
166
1.73k
        } else {
167
1.73k
          zval c;
168
1.73k
          ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
169
1.73k
          if (opline->opcode != ZEND_CASE
170
1.73k
           && opline->opcode != ZEND_CASE_STRICT
171
1.72k
           && opline->opcode != ZEND_FETCH_LIST_R
172
1.70k
           && opline->opcode != ZEND_SWITCH_LONG
173
1.70k
           && opline->opcode != ZEND_SWITCH_STRING
174
1.70k
           && opline->opcode != ZEND_MATCH
175
1.69k
           && opline->opcode != ZEND_MATCH_ERROR
176
1.69k
           && zend_optimizer_update_op1_const(op_array, opline, &c)) {
177
1.64k
            VAR_SOURCE(op1) = NULL;
178
1.64k
            if (opline->opcode != ZEND_JMP_NULL
179
1.63k
             && !zend_bitset_in(used_ext, VAR_NUM(src->result.var))
180
             /* FETCH_W with ZEND_FETCH_GLOBAL_LOCK does not free op1, which will be used again. */
181
1.14k
             && !(opline->opcode == ZEND_FETCH_W && (opline->extended_value & ZEND_FETCH_GLOBAL_LOCK))) {
182
1.14k
              literal_dtor(&ZEND_OP1_LITERAL(src));
183
1.14k
              MAKE_NOP(src);
184
1.14k
            }
185
1.64k
            ++(*opt_count);
186
1.64k
          } else {
187
96
            zval_ptr_dtor_nogc(&c);
188
96
          }
189
1.73k
        }
190
1.73k
      }
191
771k
    }
192
193
    /* Constant Propagation: strip X = QM_ASSIGN(const) */
194
2.92M
    if (opline->op2_type == IS_TMP_VAR) {
195
486k
      src = VAR_SOURCE(opline->op2);
196
486k
      if (src &&
197
452k
          src->opcode == ZEND_QM_ASSIGN &&
198
540
          src->op1_type == IS_CONST) {
199
200
148
        znode_op op2 = opline->op2;
201
148
        zval c;
202
203
148
        ZVAL_COPY(&c, &ZEND_OP1_LITERAL(src));
204
148
        if (zend_optimizer_update_op2_const(op_array, opline, &c)) {
205
138
          VAR_SOURCE(op2) = NULL;
206
138
          if (!zend_bitset_in(used_ext, VAR_NUM(src->result.var))) {
207
138
            literal_dtor(&ZEND_OP1_LITERAL(src));
208
138
            MAKE_NOP(src);
209
138
          }
210
138
          ++(*opt_count);
211
138
        } else {
212
10
          zval_ptr_dtor_nogc(&c);
213
10
        }
214
148
      }
215
486k
    }
216
217
2.92M
    switch (opline->opcode) {
218
154k
      case ZEND_ECHO:
219
154k
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
220
35.1k
          src = VAR_SOURCE(opline->op1);
221
35.1k
          if (src &&
222
32.4k
              src->opcode == ZEND_CAST &&
223
126
              src->extended_value == IS_STRING) {
224
            /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */
225
84
            VAR_SOURCE(opline->op1) = NULL;
226
84
            COPY_NODE(opline->op1, src->op1);
227
84
            MAKE_NOP(src);
228
84
            ++(*opt_count);
229
84
          }
230
119k
        } else if (opline->op1_type == IS_CONST &&
231
117k
                   Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE) {
232
117k
          if (last_op == opline - 1) {
233
            /* compress consecutive ECHO's.
234
             * Float to string conversion may be affected by current
235
             * locale setting.
236
             */
237
4.83k
            size_t l, old_len;
238
239
4.83k
            if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
240
42
              convert_to_string(&ZEND_OP1_LITERAL(opline));
241
42
            }
242
4.83k
            if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) {
243
22
              convert_to_string(&ZEND_OP1_LITERAL(last_op));
244
22
            }
245
4.83k
            old_len = Z_STRLEN(ZEND_OP1_LITERAL(last_op));
246
4.83k
            l = old_len + Z_STRLEN(ZEND_OP1_LITERAL(opline));
247
4.83k
            if (!Z_REFCOUNTED(ZEND_OP1_LITERAL(last_op))) {
248
1.69k
              zend_string *tmp = zend_string_alloc(l, 0);
249
1.69k
              memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP1_LITERAL(last_op)), old_len);
250
1.69k
              Z_STR(ZEND_OP1_LITERAL(last_op)) = tmp;
251
3.14k
            } else {
252
3.14k
              Z_STR(ZEND_OP1_LITERAL(last_op)) = zend_string_extend(Z_STR(ZEND_OP1_LITERAL(last_op)), l, 0);
253
3.14k
            }
254
4.83k
            Z_TYPE_INFO(ZEND_OP1_LITERAL(last_op)) = IS_STRING_EX;
255
4.83k
            memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op)) + old_len, Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline)));
256
4.83k
            Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0';
257
4.83k
            zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
258
4.83k
            ZVAL_STR(&ZEND_OP1_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP1_LITERAL(last_op))));
259
4.83k
            ZVAL_NULL(&ZEND_OP1_LITERAL(last_op));
260
4.83k
            MAKE_NOP(last_op);
261
4.83k
            ++(*opt_count);
262
4.83k
          }
263
117k
          last_op = opline;
264
117k
        }
265
154k
        break;
266
267
468
      case ZEND_MATCH_ERROR:
268
468
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
269
130
          src = VAR_SOURCE(opline->op1);
270
130
          VAR_SOURCE(opline->op1) = NULL;
271
130
        }
272
468
        break;
273
274
28.4k
      case ZEND_FREE:
275
        /* Note: Only remove the source if the source is local to this block.
276
         * If it's not local, then the other blocks successors must also eventually either FREE or consume the temporary,
277
         * hence removing the temporary is not safe in the general case, especially when other consumers are not FREE.
278
         * A FREE may not be removed without also removing the source's result, because otherwise that would cause a memory leak. */
279
28.4k
        if (opline->extended_value == ZEND_FREE_VOID_CAST) {
280
          /* Keep the ZEND_FREE opcode alive. */
281
28.3k
        } else if (opline->op1_type == IS_TMP_VAR) {
282
28.1k
          src = VAR_SOURCE(opline->op1);
283
28.1k
          if (src) {
284
24.0k
            switch (src->opcode) {
285
21
              case ZEND_BOOL:
286
212
              case ZEND_BOOL_NOT:
287
                /* T = BOOL(X), FREE(T) => T = BOOL(X) */
288
                /* The remaining BOOL is removed by a separate optimization */
289
                /* The source is a bool, no source removals take place, so this may be done non-locally. */
290
212
                VAR_SOURCE(opline->op1) = NULL;
291
212
                MAKE_NOP(opline);
292
212
                ++(*opt_count);
293
212
                break;
294
206
              case ZEND_QM_ASSIGN:
295
206
                if (src < op_array->opcodes + block->start) {
296
12
                  break;
297
12
                }
298
194
                src->result_type = IS_UNUSED;
299
194
                VAR_SOURCE(opline->op1) = NULL;
300
194
                MAKE_NOP(opline);
301
194
                ++(*opt_count);
302
194
                if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
303
88
                  src->opcode = ZEND_FREE;
304
106
                } else if (src->op1_type == IS_CONST) {
305
104
                  MAKE_NOP(src);
306
104
                } else if (src->op1_type == IS_CV) {
307
2
                  src->opcode = ZEND_CHECK_VAR;
308
2
                  SET_UNUSED(src->result);
309
2
                }
310
194
                break;
311
23.6k
              default:
312
23.6k
                if (!zend_op_may_elide_result(src->opcode)) {
313
21.2k
                  break;
314
21.2k
                }
315
2.40k
                if (src < op_array->opcodes + block->start) {
316
8
                  break;
317
8
                }
318
2.40k
                src->result_type = IS_UNUSED;
319
2.40k
                VAR_SOURCE(opline->op1) = NULL;
320
2.40k
                MAKE_NOP(opline);
321
2.40k
                ++(*opt_count);
322
2.40k
                break;
323
24.0k
            }
324
24.0k
          }
325
28.1k
        } else if (opline->op1_type == IS_VAR) {
326
228
          src = VAR_SOURCE(opline->op1);
327
          /* V = OP, FREE(V) => OP. NOP */
328
228
          if (src >= op_array->opcodes + block->start &&
329
6
              src->opcode != ZEND_FETCH_R &&
330
4
              src->opcode != ZEND_FETCH_STATIC_PROP_R &&
331
4
              src->opcode != ZEND_FETCH_DIM_R &&
332
4
              src->opcode != ZEND_FETCH_OBJ_R &&
333
0
              src->opcode != ZEND_NEW &&
334
0
              src->opcode != ZEND_FETCH_THIS) {
335
0
            src->result_type = IS_UNUSED;
336
0
            MAKE_NOP(opline);
337
0
            ++(*opt_count);
338
0
            if (src->opcode == ZEND_QM_ASSIGN) {
339
0
              if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
340
0
                src->opcode = ZEND_FREE;
341
0
              } else {
342
0
                MAKE_NOP(src);
343
0
              }
344
0
            }
345
0
          }
346
228
        }
347
28.4k
        break;
348
349
#if 0
350
    /* pre-evaluate functions:
351
       constant(x)
352
       function_exists(x)
353
       extension_loaded(x)
354
       BAD: interacts badly with Accelerator
355
    */
356
    if((opline->op1_type & IS_VAR) &&
357
       VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL &&
358
       VAR_SOURCE(opline->op1)->extended_value == 1) {
359
      zend_op *fcall = VAR_SOURCE(opline->op1);
360
      zend_op *sv = fcall-1;
361
      if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL &&
362
         sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING &&
363
         Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1
364
         ) {
365
        zval *arg = &OPLINE_OP1_LITERAL(sv);
366
        char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name;
367
        size_t flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len;
368
        if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) ||
369
              (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0)
370
              ) {
371
          zend_function *function;
372
          if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) {
373
            literal_dtor(arg);
374
            MAKE_NOP(sv);
375
            MAKE_NOP(fcall);
376
            LITERAL_BOOL(opline->op1, 1);
377
            opline->op1_type = IS_CONST;
378
          }
379
        } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) {
380
          zval c;
381
          if (zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, true ELS_CC)) {
382
            literal_dtor(arg);
383
            MAKE_NOP(sv);
384
            MAKE_NOP(fcall);
385
            ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c);
386
            /* no copy ctor - get already copied it */
387
            opline->op1_type = IS_CONST;
388
          }
389
        } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) {
390
          if(zend_hash_exists(&module_registry, Z_STR_P(arg))) {
391
            literal_dtor(arg);
392
            MAKE_NOP(sv);
393
            MAKE_NOP(fcall);
394
            LITERAL_BOOL(opline->op1, 1);
395
            opline->op1_type = IS_CONST;
396
          }
397
        }
398
      }
399
    }
400
#endif
401
402
28.4k
      case ZEND_FETCH_LIST_R:
403
1.69k
      case ZEND_FETCH_LIST_W:
404
1.69k
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
405
          /* LIST variable will be deleted later by FREE */
406
1.43k
          Tsource[VAR_NUM(opline->op1.var)] = NULL;
407
1.43k
        }
408
1.69k
        break;
409
410
14
      case ZEND_SWITCH_LONG:
411
77
      case ZEND_SWITCH_STRING:
412
357
      case ZEND_MATCH:
413
357
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
414
          /* SWITCH variable will be deleted later by FREE, so we can't optimize it */
415
73
          Tsource[VAR_NUM(opline->op1.var)] = NULL;
416
73
          break;
417
73
        }
418
284
        if (opline->op1_type == IS_CONST) {
419
68
          uint32_t target = get_const_switch_target(cfg, op_array, block, opline, &ZEND_OP1_LITERAL(opline));
420
68
          literal_dtor(&ZEND_OP1_LITERAL(opline));
421
68
          literal_dtor(&ZEND_OP2_LITERAL(opline));
422
68
          opline->opcode = ZEND_JMP;
423
68
          opline->op1_type = IS_UNUSED;
424
68
          opline->op2_type = IS_UNUSED;
425
68
          block->successors_count = 1;
426
68
          block->successors[0] = target;
427
68
        }
428
284
        break;
429
430
0
      case ZEND_EXT_STMT:
431
0
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
432
          /* Variable will be deleted later by FREE, so we can't optimize it */
433
0
          Tsource[VAR_NUM(opline->op1.var)] = NULL;
434
0
          break;
435
0
        }
436
0
        break;
437
438
172
      case ZEND_CASE:
439
410
      case ZEND_CASE_STRICT:
440
3.22k
      case ZEND_COPY_TMP:
441
3.22k
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
442
          /* Variable will be deleted later by FREE, so we can't optimize it */
443
3.22k
          Tsource[VAR_NUM(opline->op1.var)] = NULL;
444
3.22k
          break;
445
3.22k
        }
446
0
        if (opline->op1_type == IS_CONST &&
447
0
            opline->op2_type == IS_CONST) {
448
0
          goto optimize_constant_binary_op;
449
0
        }
450
            /*
451
             * CASE(TRUE, X)       => BOOL(X)
452
             * CASE(FALSE, X)      => BOOL_NOT(X)
453
             */
454
0
        if (opline->op1_type == IS_CONST &&
455
0
          (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_FALSE ||
456
0
           Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_TRUE)) {
457
          /* Optimization of comparison with "null" is not safe,
458
           * because ("0" == null) is not equal to !("0")
459
           */
460
0
          opline->opcode =
461
0
            ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP1_LITERAL(opline))) == IS_TRUE)) ?
462
0
            ZEND_BOOL : ZEND_BOOL_NOT;
463
0
          COPY_NODE(opline->op1, opline->op2);
464
0
          SET_UNUSED(opline->op2);
465
0
          ++(*opt_count);
466
0
          goto optimize_bool;
467
0
        } else if (opline->op2_type == IS_CONST &&
468
0
                   (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_FALSE ||
469
0
                    Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_TRUE)) {
470
          /* Optimization of comparison with "null" is not safe,
471
           * because ("0" == null) is not equal to !("0")
472
           */
473
0
          opline->opcode =
474
0
            ((opline->opcode != ZEND_IS_NOT_EQUAL) == ((Z_TYPE(ZEND_OP2_LITERAL(opline))) == IS_TRUE)) ?
475
0
            ZEND_BOOL : ZEND_BOOL_NOT;
476
0
          SET_UNUSED(opline->op2);
477
0
          ++(*opt_count);
478
0
          goto optimize_bool;
479
0
        }
480
0
        break;
481
482
23.8k
      case ZEND_IS_EQUAL:
483
26.6k
      case ZEND_IS_NOT_EQUAL:
484
26.6k
        if (opline->op1_type == IS_CONST &&
485
0
          opline->op2_type == IS_CONST) {
486
0
          goto optimize_constant_binary_op;
487
0
        }
488
        /* IS_EQ(TRUE, X)      => BOOL(X)
489
         * IS_EQ(FALSE, X)     => BOOL_NOT(X)
490
         * IS_NOT_EQ(TRUE, X)  => BOOL_NOT(X)
491
         * IS_NOT_EQ(FALSE, X) => BOOL(X)
492
         * Those optimizations are not safe if the other operand ends up being NAN
493
         * as BOOL/BOOL_NOT will warn, while IS_EQUAL/IS_NOT_EQUAL do not.
494
         */
495
26.6k
        break;
496
26.6k
      case ZEND_IS_IDENTICAL:
497
2.24k
        if (opline->op1_type == IS_CONST &&
498
0
          opline->op2_type == IS_CONST) {
499
0
          goto optimize_constant_binary_op;
500
0
        }
501
502
2.24k
        if (opline->op1_type == IS_CONST &&
503
0
          (Z_TYPE(ZEND_OP1_LITERAL(opline)) <= IS_TRUE && Z_TYPE(ZEND_OP1_LITERAL(opline)) >= IS_NULL)) {
504
          /* IS_IDENTICAL(TRUE, T)  => TYPE_CHECK(T, TRUE)
505
           * IS_IDENTICAL(FALSE, T) => TYPE_CHECK(T, FALSE)
506
           * IS_IDENTICAL(NULL, T)  => TYPE_CHECK(T, NULL)
507
           */
508
0
          opline->opcode = ZEND_TYPE_CHECK;
509
0
          opline->extended_value = (1 << Z_TYPE(ZEND_OP1_LITERAL(opline)));
510
0
          COPY_NODE(opline->op1, opline->op2);
511
0
          SET_UNUSED(opline->op2);
512
0
          ++(*opt_count);
513
0
          goto optimize_type_check;
514
2.24k
        } else if (opline->op2_type == IS_CONST &&
515
1.43k
          (Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_TRUE && Z_TYPE(ZEND_OP2_LITERAL(opline)) >= IS_NULL)) {
516
          /* IS_IDENTICAL(T, TRUE)  => TYPE_CHECK(T, TRUE)
517
           * IS_IDENTICAL(T, FALSE) => TYPE_CHECK(T, FALSE)
518
           * IS_IDENTICAL(T, NULL)  => TYPE_CHECK(T, NULL)
519
           */
520
118
          opline->opcode = ZEND_TYPE_CHECK;
521
118
          opline->extended_value = (1 << Z_TYPE(ZEND_OP2_LITERAL(opline)));
522
118
          SET_UNUSED(opline->op2);
523
118
          ++(*opt_count);
524
118
          goto optimize_type_check;
525
118
        }
526
2.13k
        break;
527
2.13k
      case ZEND_TYPE_CHECK:
528
1.20k
optimize_type_check:
529
1.20k
        if (opline->extended_value == (1 << IS_TRUE) || opline->extended_value == (1 << IS_FALSE)) {
530
181
          if (opline->op1_type == IS_TMP_VAR &&
531
161
            !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
532
161
            src = VAR_SOURCE(opline->op1);
533
534
161
            if (src) {
535
161
              switch (src->opcode) {
536
0
                case ZEND_BOOL:
537
0
                case ZEND_BOOL_NOT:
538
                  /* T = BOOL(X)     + TYPE_CHECK(T, TRUE)  -> BOOL(X), NOP
539
                   * T = BOOL(X)     + TYPE_CHECK(T, FALSE) -> BOOL_NOT(X), NOP
540
                   * T = BOOL_NOT(X) + TYPE_CHECK(T, TRUE)  -> BOOL_NOT(X), NOP
541
                   * T = BOOL_NOT(X) + TYPE_CHECK(T, FALSE) -> BOOL(X), NOP
542
                   */
543
0
                  src->opcode =
544
0
                    ((src->opcode == ZEND_BOOL) == (opline->extended_value == (1 << IS_TRUE))) ?
545
0
                    ZEND_BOOL : ZEND_BOOL_NOT;
546
0
                  COPY_NODE(src->result, opline->result);
547
0
                  SET_VAR_SOURCE(src);
548
0
                  MAKE_NOP(opline);
549
0
                  ++(*opt_count);
550
0
                  break;
551
161
              }
552
161
            }
553
161
          }
554
181
        }
555
1.20k
        break;
556
  
557
4.68k
      case ZEND_BOOL:
558
11.5k
      case ZEND_BOOL_NOT:
559
14.6k
      optimize_bool:
560
14.6k
        if (opline->op1_type == IS_CONST) {
561
1.53k
          goto optimize_const_unary_op;
562
1.53k
        }
563
13.0k
        if (opline->op1_type == IS_TMP_VAR &&
564
11.4k
            !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
565
10.9k
          src = VAR_SOURCE(opline->op1);
566
10.9k
          if (src) {
567
10.9k
            switch (src->opcode) {
568
1.69k
              case ZEND_BOOL_NOT:
569
                /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */
570
1.69k
                VAR_SOURCE(opline->op1) = NULL;
571
1.69k
                COPY_NODE(opline->op1, src->op1);
572
1.69k
                opline->opcode = (opline->opcode == ZEND_BOOL) ? ZEND_BOOL_NOT : ZEND_BOOL;
573
1.69k
                MAKE_NOP(src);
574
1.69k
                ++(*opt_count);
575
1.69k
                goto optimize_bool;
576
1.37k
              case ZEND_BOOL:
577
                /* T = BOOL(X) + BOOL(T) -> NOP, BOOL(X) */
578
1.37k
                VAR_SOURCE(opline->op1) = NULL;
579
1.37k
                COPY_NODE(opline->op1, src->op1);
580
1.37k
                MAKE_NOP(src);
581
1.37k
                ++(*opt_count);
582
1.37k
                goto optimize_bool;
583
192
              case ZEND_IS_EQUAL:
584
192
                if (opline->opcode == ZEND_BOOL_NOT) {
585
0
                  src->opcode = ZEND_IS_NOT_EQUAL;
586
0
                }
587
192
                COPY_NODE(src->result, opline->result);
588
192
                SET_VAR_SOURCE(src);
589
192
                MAKE_NOP(opline);
590
192
                ++(*opt_count);
591
192
                break;
592
171
              case ZEND_IS_NOT_EQUAL:
593
171
                if (opline->opcode == ZEND_BOOL_NOT) {
594
0
                  src->opcode = ZEND_IS_EQUAL;
595
0
                }
596
171
                COPY_NODE(src->result, opline->result);
597
171
                SET_VAR_SOURCE(src);
598
171
                MAKE_NOP(opline);
599
171
                ++(*opt_count);
600
171
                break;
601
120
              case ZEND_IS_IDENTICAL:
602
120
                if (opline->opcode == ZEND_BOOL_NOT) {
603
0
                  src->opcode = ZEND_IS_NOT_IDENTICAL;
604
0
                }
605
120
                COPY_NODE(src->result, opline->result);
606
120
                SET_VAR_SOURCE(src);
607
120
                MAKE_NOP(opline);
608
120
                ++(*opt_count);
609
120
                break;
610
74
              case ZEND_IS_NOT_IDENTICAL:
611
74
                if (opline->opcode == ZEND_BOOL_NOT) {
612
0
                  src->opcode = ZEND_IS_IDENTICAL;
613
0
                }
614
74
                COPY_NODE(src->result, opline->result);
615
74
                SET_VAR_SOURCE(src);
616
74
                MAKE_NOP(opline);
617
74
                ++(*opt_count);
618
74
                break;
619
126
              case ZEND_IS_SMALLER:
620
126
                if (opline->opcode == ZEND_BOOL_NOT) {
621
2
                  uint8_t tmp_type;
622
2
                  uint32_t tmp;
623
624
2
                  src->opcode = ZEND_IS_SMALLER_OR_EQUAL;
625
2
                  tmp_type = src->op1_type;
626
2
                  src->op1_type = src->op2_type;
627
2
                  src->op2_type = tmp_type;
628
2
                  tmp = src->op1.num;
629
2
                  src->op1.num = src->op2.num;
630
2
                  src->op2.num = tmp;
631
2
                }
632
126
                COPY_NODE(src->result, opline->result);
633
126
                SET_VAR_SOURCE(src);
634
126
                MAKE_NOP(opline);
635
126
                ++(*opt_count);
636
126
                break;
637
302
              case ZEND_IS_SMALLER_OR_EQUAL:
638
302
                if (opline->opcode == ZEND_BOOL_NOT) {
639
2
                  uint8_t tmp_type;
640
2
                  uint32_t tmp;
641
642
2
                  src->opcode = ZEND_IS_SMALLER;
643
2
                  tmp_type = src->op1_type;
644
2
                  src->op1_type = src->op2_type;
645
2
                  src->op2_type = tmp_type;
646
2
                  tmp = src->op1.num;
647
2
                  src->op1.num = src->op2.num;
648
2
                  src->op2.num = tmp;
649
2
                }
650
302
                COPY_NODE(src->result, opline->result);
651
302
                SET_VAR_SOURCE(src);
652
302
                MAKE_NOP(opline);
653
302
                ++(*opt_count);
654
302
                break;
655
10
              case ZEND_ISSET_ISEMPTY_CV:
656
20
              case ZEND_ISSET_ISEMPTY_VAR:
657
242
              case ZEND_ISSET_ISEMPTY_DIM_OBJ:
658
294
              case ZEND_ISSET_ISEMPTY_PROP_OBJ:
659
312
              case ZEND_ISSET_ISEMPTY_STATIC_PROP:
660
350
              case ZEND_INSTANCEOF:
661
487
              case ZEND_TYPE_CHECK:
662
498
              case ZEND_DEFINED:
663
498
              case ZEND_IN_ARRAY:
664
528
              case ZEND_ARRAY_KEY_EXISTS:
665
528
                if (opline->opcode == ZEND_BOOL_NOT) {
666
394
                  break;
667
394
                }
668
134
                COPY_NODE(src->result, opline->result);
669
134
                SET_VAR_SOURCE(src);
670
134
                MAKE_NOP(opline);
671
134
                ++(*opt_count);
672
134
                break;
673
10.9k
            }
674
10.9k
          }
675
10.9k
        }
676
10.0k
        break;
677
678
28.9k
      case ZEND_JMPZ:
679
39.6k
      case ZEND_JMPNZ:
680
41.1k
          while (1) {
681
41.1k
          if (opline->op1_type == IS_CONST) {
682
573
            ++(*opt_count);
683
573
            block->successors_count = 1;
684
573
            if (zend_is_true(&ZEND_OP1_LITERAL(opline)) ==
685
573
                (opline->opcode == ZEND_JMPZ)) {
686
687
258
              MAKE_NOP(opline);
688
258
              block->successors[0] = block->successors[1];
689
258
              block->len--;
690
258
              cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
691
258
              break;
692
315
            } else {
693
315
              zend_basic_block *next = cfg->blocks + block->successors[1];
694
695
315
              next->flags &= ~ZEND_BB_FOLLOW;
696
315
              if (!(next->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) {
697
265
                next->flags &= ~ZEND_BB_REACHABLE;
698
265
              }
699
315
              opline->opcode = ZEND_JMP;
700
315
              COPY_NODE(opline->op1, opline->op2);
701
315
              break;
702
315
            }
703
40.5k
          } else if (opline->op1_type == IS_TMP_VAR &&
704
38.7k
                     !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
705
38.3k
            src = VAR_SOURCE(opline->op1);
706
38.3k
            if (src) {
707
38.3k
              if (src->opcode == ZEND_BOOL_NOT) {
708
852
                VAR_SOURCE(opline->op1) = NULL;
709
852
                COPY_NODE(opline->op1, src->op1);
710
                /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */
711
852
                opline->opcode = INV_COND(opline->opcode);
712
852
                MAKE_NOP(src);
713
852
                ++(*opt_count);
714
852
                continue;
715
37.4k
              } else if (src->opcode == ZEND_BOOL ||
716
36.8k
                         src->opcode == ZEND_QM_ASSIGN) {
717
614
                VAR_SOURCE(opline->op1) = NULL;
718
614
                COPY_NODE(opline->op1, src->op1);
719
614
                MAKE_NOP(src);
720
614
                ++(*opt_count);
721
614
                continue;
722
614
              }
723
38.3k
            }
724
38.3k
          }
725
39.1k
          break;
726
41.1k
        }
727
39.6k
        break;
728
729
1.70k
      case ZEND_JMPZ_EX:
730
3.32k
      case ZEND_JMPNZ_EX:
731
3.66k
        while (1) {
732
3.66k
          if (opline->op1_type == IS_CONST) {
733
697
            bool is_jmpz_ex = opline->opcode == ZEND_JMPZ_EX;
734
697
            if (zend_is_true(&ZEND_OP1_LITERAL(opline)) == is_jmpz_ex) {
735
736
199
              ++(*opt_count);
737
199
              opline->opcode = ZEND_QM_ASSIGN;
738
199
              zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
739
199
              ZVAL_BOOL(&ZEND_OP1_LITERAL(opline), is_jmpz_ex);
740
199
              opline->op2.num = 0;
741
199
              block->successors_count = 1;
742
199
              block->successors[0] = block->successors[1];
743
199
              cfg->blocks[block->successors[0]].flags |= ZEND_BB_FOLLOW;
744
199
              break;
745
199
            }
746
2.96k
          } else if (opline->op1_type == IS_TMP_VAR &&
747
2.91k
                     (!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var)) ||
748
2.56k
                      opline->result.var == opline->op1.var)) {
749
2.56k
            src = VAR_SOURCE(opline->op1);
750
2.56k
            if (src) {
751
2.18k
              if (src->opcode == ZEND_BOOL ||
752
1.84k
                  src->opcode == ZEND_QM_ASSIGN) {
753
343
                VAR_SOURCE(opline->op1) = NULL;
754
343
                COPY_NODE(opline->op1, src->op1);
755
343
                MAKE_NOP(src);
756
343
                ++(*opt_count);
757
343
                continue;
758
343
              }
759
2.18k
            }
760
2.56k
          }
761
3.12k
          break;
762
3.66k
        }
763
3.32k
        break;
764
765
60.6k
      case ZEND_CONCAT:
766
91.4k
      case ZEND_FAST_CONCAT:
767
91.4k
        if (opline->op1_type == IS_CONST &&
768
24.9k
            opline->op2_type == IS_CONST) {
769
12
          goto optimize_constant_binary_op;
770
12
        }
771
772
91.4k
        if (opline->op2_type == IS_CONST &&
773
39.4k
            opline->op1_type == IS_TMP_VAR) {
774
775
30.6k
          src = VAR_SOURCE(opline->op1);
776
30.6k
            if (src &&
777
30.5k
              (src->opcode == ZEND_CONCAT ||
778
26.8k
               src->opcode == ZEND_FAST_CONCAT) &&
779
24.9k
              src->op2_type == IS_CONST) {
780
            /* compress consecutive CONCATs */
781
818
            size_t l, old_len;
782
783
818
            if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
784
0
              convert_to_string(&ZEND_OP2_LITERAL(opline));
785
0
            }
786
818
            if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) {
787
0
              convert_to_string(&ZEND_OP2_LITERAL(src));
788
0
            }
789
790
818
            VAR_SOURCE(opline->op1) = NULL;
791
818
            COPY_NODE(opline->op1, src->op1);
792
818
            old_len = Z_STRLEN(ZEND_OP2_LITERAL(src));
793
818
            l = old_len + Z_STRLEN(ZEND_OP2_LITERAL(opline));
794
818
            if (!Z_REFCOUNTED(ZEND_OP2_LITERAL(src))) {
795
224
              zend_string *tmp = zend_string_alloc(l, 0);
796
224
              memcpy(ZSTR_VAL(tmp), Z_STRVAL(ZEND_OP2_LITERAL(src)), old_len);
797
224
              Z_STR(ZEND_OP2_LITERAL(src)) = tmp;
798
594
            } else {
799
594
              Z_STR(ZEND_OP2_LITERAL(src)) = zend_string_extend(Z_STR(ZEND_OP2_LITERAL(src)), l, 0);
800
594
            }
801
818
            Z_TYPE_INFO(ZEND_OP2_LITERAL(src)) = IS_STRING_EX;
802
818
            memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src)) + old_len, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
803
818
            Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0';
804
818
            zval_ptr_dtor_str(&ZEND_OP2_LITERAL(opline));
805
818
            ZVAL_STR(&ZEND_OP2_LITERAL(opline), zend_new_interned_string(Z_STR(ZEND_OP2_LITERAL(src))));
806
818
            ZVAL_NULL(&ZEND_OP2_LITERAL(src));
807
818
            MAKE_NOP(src);
808
818
            ++(*opt_count);
809
818
          }
810
30.6k
        }
811
812
91.4k
        if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
813
36.2k
          src = VAR_SOURCE(opline->op1);
814
36.2k
          if (src &&
815
35.9k
              src->opcode == ZEND_CAST &&
816
112
              src->extended_value == IS_STRING &&
817
6
              src->op1_type != IS_CONST) {
818
            /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */
819
4
            VAR_SOURCE(opline->op1) = NULL;
820
4
            COPY_NODE(opline->op1, src->op1);
821
4
            MAKE_NOP(src);
822
4
            ++(*opt_count);
823
4
          }
824
36.2k
              }
825
91.4k
        if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
826
49.5k
          src = VAR_SOURCE(opline->op2);
827
49.5k
          if (src &&
828
49.4k
              src->opcode == ZEND_CAST &&
829
190
              src->extended_value == IS_STRING &&
830
106
              src->op1_type != IS_CONST) {
831
            /* convert T1 = CAST(STRING, X), T2 = CONCAT(Y, T1) to T2 = CONCAT(Y,X) */
832
90
            VAR_SOURCE(opline->op2) = NULL;
833
90
            COPY_NODE(opline->op2, src->op1);
834
90
            MAKE_NOP(src);
835
90
            ++(*opt_count);
836
90
          }
837
49.5k
        }
838
91.4k
        if (opline->op1_type == IS_CONST &&
839
24.9k
            Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
840
24.9k
            Z_STRLEN(ZEND_OP1_LITERAL(opline)) == 0) {
841
          /* convert CONCAT('', X) => CAST(STRING, X) */
842
10
          literal_dtor(&ZEND_OP1_LITERAL(opline));
843
10
          opline->opcode = ZEND_CAST;
844
10
          opline->extended_value = IS_STRING;
845
10
          COPY_NODE(opline->op1, opline->op2);
846
10
          opline->op2_type = IS_UNUSED;
847
10
          opline->op2.var = 0;
848
10
          ++(*opt_count);
849
91.4k
        } else if (opline->op2_type == IS_CONST &&
850
39.4k
                 Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
851
39.4k
                 Z_STRLEN(ZEND_OP2_LITERAL(opline)) == 0) {
852
          /* convert CONCAT(X, '') => CAST(STRING, X) */
853
20
          literal_dtor(&ZEND_OP2_LITERAL(opline));
854
20
          opline->opcode = ZEND_CAST;
855
20
          opline->extended_value = IS_STRING;
856
20
          opline->op2_type = IS_UNUSED;
857
20
          opline->op2.var = 0;
858
20
          ++(*opt_count);
859
91.4k
        } else if (opline->opcode == ZEND_CONCAT &&
860
60.6k
                   (opline->op1_type == IS_CONST ||
861
47.1k
                    (opline->op1_type == IS_TMP_VAR &&
862
25.3k
                     VAR_SOURCE(opline->op1) &&
863
25.1k
                     (VAR_SOURCE(opline->op1)->opcode == ZEND_FAST_CONCAT ||
864
13.3k
                      VAR_SOURCE(opline->op1)->opcode == ZEND_ROPE_END ||
865
13.2k
                      VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CONSTANT ||
866
12.1k
                      VAR_SOURCE(opline->op1)->opcode == ZEND_FETCH_CLASS_CONSTANT))) &&
867
26.4k
                   (opline->op2_type == IS_CONST ||
868
15.3k
                    (opline->op2_type == IS_TMP_VAR &&
869
14.9k
                     VAR_SOURCE(opline->op2) &&
870
14.9k
                     (VAR_SOURCE(opline->op2)->opcode == ZEND_FAST_CONCAT ||
871
14.9k
                      VAR_SOURCE(opline->op2)->opcode == ZEND_ROPE_END ||
872
4.29k
                      VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CONSTANT ||
873
23.0k
                      VAR_SOURCE(opline->op2)->opcode == ZEND_FETCH_CLASS_CONSTANT)))) {
874
23.0k
          opline->opcode = ZEND_FAST_CONCAT;
875
23.0k
          ++(*opt_count);
876
23.0k
        }
877
91.4k
        break;
878
879
8.34k
      case ZEND_ADD:
880
14.4k
      case ZEND_SUB:
881
22.0k
      case ZEND_MUL:
882
26.3k
      case ZEND_DIV:
883
29.4k
      case ZEND_MOD:
884
31.6k
      case ZEND_SL:
885
32.8k
      case ZEND_SR:
886
41.6k
      case ZEND_IS_SMALLER:
887
45.0k
      case ZEND_IS_SMALLER_OR_EQUAL:
888
45.6k
      case ZEND_IS_NOT_IDENTICAL:
889
47.4k
      case ZEND_BOOL_XOR:
890
48.6k
      case ZEND_BW_OR:
891
61.5k
      case ZEND_BW_AND:
892
72.1k
      case ZEND_BW_XOR:
893
72.1k
        if (opline->op1_type == IS_CONST &&
894
13.2k
            opline->op2_type == IS_CONST) {
895
          /* evaluate constant expressions */
896
9.36k
          zval result;
897
898
9.37k
optimize_constant_binary_op:
899
9.37k
          if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
900
38
            literal_dtor(&ZEND_OP1_LITERAL(opline));
901
38
            literal_dtor(&ZEND_OP2_LITERAL(opline));
902
38
            opline->opcode = ZEND_QM_ASSIGN;
903
38
            SET_UNUSED(opline->op2);
904
38
            zend_optimizer_update_op1_const(op_array, opline, &result);
905
38
            ++(*opt_count);
906
38
          }
907
9.37k
        }
908
72.1k
        break;
909
910
72.1k
      case ZEND_BW_NOT:
911
20.1k
        if (opline->op1_type == IS_CONST) {
912
          /* evaluate constant unary ops */
913
103
          zval result;
914
915
1.63k
optimize_const_unary_op:
916
1.63k
          if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
917
1.51k
            literal_dtor(&ZEND_OP1_LITERAL(opline));
918
1.51k
            opline->opcode = ZEND_QM_ASSIGN;
919
1.51k
            zend_optimizer_update_op1_const(op_array, opline, &result);
920
1.51k
            ++(*opt_count);
921
1.51k
          }
922
1.63k
        }
923
21.7k
        break;
924
925
21.7k
      case ZEND_CAST:
926
2.91k
        if (opline->op1_type == IS_CONST) {
927
          /* cast of constant operand */
928
335
          zval result;
929
930
335
          if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
931
4
            literal_dtor(&ZEND_OP1_LITERAL(opline));
932
4
            opline->opcode = ZEND_QM_ASSIGN;
933
4
            opline->extended_value = 0;
934
4
            zend_optimizer_update_op1_const(op_array, opline, &result);
935
4
            ++(*opt_count);
936
4
          }
937
335
        }
938
2.91k
        break;
939
940
2.67k
      case ZEND_STRLEN:
941
2.67k
        if (opline->op1_type == IS_CONST) {
942
34
          zval result;
943
944
34
          if (zend_optimizer_eval_strlen(&result, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
945
0
            literal_dtor(&ZEND_OP1_LITERAL(opline));
946
0
            opline->opcode = ZEND_QM_ASSIGN;
947
0
            zend_optimizer_update_op1_const(op_array, opline, &result);
948
0
            ++(*opt_count);
949
0
          }
950
34
        }
951
2.67k
        break;
952
953
108k
      case ZEND_RETURN:
954
108k
        if (opline->op1_type == IS_TMP_VAR) {
955
6.03k
          src = VAR_SOURCE(opline->op1);
956
6.03k
          if (src && src->opcode == ZEND_QM_ASSIGN) {
957
32
            zend_op *op = src + 1;
958
32
            bool optimize = true;
959
960
32
            while (op < opline) {
961
32
              if ((op->op1_type == opline->op1_type
962
10
                && op->op1.var == opline->op1.var)
963
22
               || (op->op2_type == opline->op1_type
964
32
                && op->op2.var == opline->op1.var)) {
965
32
                optimize = false;
966
32
                break;
967
32
              }
968
0
              op++;
969
0
            }
970
971
32
            if (optimize) {
972
              /* T = QM_ASSIGN(X), RETURN(T) to NOP, RETURN(X) */
973
0
              VAR_SOURCE(opline->op1) = NULL;
974
0
              COPY_NODE(opline->op1, src->op1);
975
0
              MAKE_NOP(src);
976
0
              ++(*opt_count);
977
0
            }
978
32
          }
979
6.03k
        }
980
108k
        break;
981
982
14.9k
      case ZEND_QM_ASSIGN:
983
14.9k
        if (opline->op1_type == opline->result_type &&
984
4.21k
            opline->op1.var == opline->result.var) {
985
          /* strip T = QM_ASSIGN(T) */
986
0
          MAKE_NOP(opline);
987
0
          ++(*opt_count);
988
14.9k
        } else if (opline->op1_type == IS_TMP_VAR &&
989
4.21k
                   opline->result_type == IS_TMP_VAR &&
990
4.21k
                   !zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
991
          /* T1 = ..., T2 = QM_ASSIGN(T1) to T2 = ..., NOP */
992
2.39k
          src = VAR_SOURCE(opline->op1);
993
2.39k
          if (src &&
994
2.39k
            src->opcode != ZEND_COPY_TMP &&
995
            /* See gh20628_borked_live_range_calc.phpt. */
996
2.39k
            src->opcode != ZEND_NEW &&
997
2.35k
            src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
998
2.35k
            src->opcode != ZEND_ADD_ARRAY_UNPACK &&
999
2.35k
            (src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||
1000
2.35k
             src == opline -1)) {
1001
2.35k
            src->result.var = opline->result.var;
1002
2.35k
            VAR_SOURCE(opline->op1) = NULL;
1003
2.35k
            VAR_SOURCE(opline->result) = src;
1004
2.35k
            MAKE_NOP(opline);
1005
2.35k
            ++(*opt_count);
1006
2.35k
          }
1007
2.39k
        }
1008
14.9k
        break;
1009
2.92M
    }
1010
1011
    /* get variable source */
1012
2.92M
    if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
1013
1.35M
      SET_VAR_SOURCE(opline);
1014
1.35M
    }
1015
2.92M
    opline++;
1016
2.92M
  }
1017
487k
}
1018
1019
/* Rebuild plain (optimized) op_array from CFG */
1020
static void assemble_code_blocks(const zend_cfg *cfg, zend_op_array *op_array, zend_optimizer_ctx *ctx)
1021
83.6k
{
1022
83.6k
  zend_basic_block *blocks = cfg->blocks;
1023
83.6k
  const zend_basic_block *end = blocks + cfg->blocks_count;
1024
83.6k
  zend_basic_block *b;
1025
83.6k
  zend_op *new_opcodes;
1026
83.6k
  zend_op *opline;
1027
83.6k
  uint32_t len = 0;
1028
1029
445k
  for (b = blocks; b < end; b++) {
1030
362k
    if (b->len == 0) {
1031
27.8k
      continue;
1032
27.8k
    }
1033
334k
    if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1034
319k
      opline = op_array->opcodes + b->start + b->len - 1;
1035
319k
      if (opline->opcode == ZEND_JMP) {
1036
49.7k
        const zend_basic_block *next = b + 1;
1037
1038
61.1k
        while (next < end && !(next->flags & ZEND_BB_REACHABLE)) {
1039
11.4k
          next++;
1040
11.4k
        }
1041
49.7k
        if (next < end && next == blocks + b->successors[0]) {
1042
          /* JMP to the next block - strip it */
1043
16
          MAKE_NOP(opline);
1044
16
          b->len--;
1045
16
        }
1046
269k
      } else if (b->len == 1 && opline->opcode == ZEND_NOP) {
1047
        /* skip empty block */
1048
0
        b->len--;
1049
0
      }
1050
319k
      len += b->len;
1051
319k
    } else {
1052
      /* this block will not be used, delete all constants there */
1053
15.2k
      const zend_op *op = op_array->opcodes + b->start;
1054
15.2k
      const zend_op *last_op = op + b->len;
1055
34.6k
      for (; op < last_op; op++) {
1056
19.3k
        if (op->op1_type == IS_CONST) {
1057
13.6k
          literal_dtor(&ZEND_OP1_LITERAL(op));
1058
13.6k
        }
1059
19.3k
        if (op->op2_type == IS_CONST) {
1060
429
          literal_dtor(&ZEND_OP2_LITERAL(op));
1061
429
        }
1062
19.3k
      }
1063
15.2k
    }
1064
334k
  }
1065
1066
83.6k
  new_opcodes = emalloc(len * sizeof(zend_op));
1067
83.6k
  opline = new_opcodes;
1068
1069
  /* Copy code of reachable blocks into a single buffer */
1070
445k
  for (b = blocks; b < end; b++) {
1071
362k
    if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1072
319k
      memcpy(opline, op_array->opcodes + b->start, b->len * sizeof(zend_op));
1073
319k
      b->start = opline - new_opcodes;
1074
319k
      opline += b->len;
1075
319k
    }
1076
362k
  }
1077
1078
  /* adjust jump targets */
1079
83.6k
  efree(op_array->opcodes);
1080
83.6k
  op_array->opcodes = new_opcodes;
1081
83.6k
  op_array->last = len;
1082
1083
445k
  for (b = blocks; b < end; b++) {
1084
362k
    if (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0) {
1085
43.1k
      continue;
1086
43.1k
    }
1087
318k
    opline = op_array->opcodes + b->start + b->len - 1;
1088
318k
    switch (opline->opcode) {
1089
694
      case ZEND_FAST_CALL:
1090
50.4k
      case ZEND_JMP:
1091
50.4k
        ZEND_SET_OP_JMP_ADDR(opline, opline->op1, new_opcodes + blocks[b->successors[0]].start);
1092
50.4k
        break;
1093
16.5k
      case ZEND_JMPZ:
1094
25.4k
      case ZEND_JMPNZ:
1095
26.3k
      case ZEND_JMPZ_EX:
1096
27.2k
      case ZEND_JMPNZ_EX:
1097
40.3k
      case ZEND_FE_RESET_R:
1098
41.1k
      case ZEND_FE_RESET_RW:
1099
42.5k
      case ZEND_JMP_SET:
1100
45.7k
      case ZEND_COALESCE:
1101
46.8k
      case ZEND_ASSERT_CHECK:
1102
78.2k
      case ZEND_JMP_NULL:
1103
78.3k
      case ZEND_BIND_INIT_STATIC_OR_JMP:
1104
78.3k
      case ZEND_JMP_FRAMELESS:
1105
78.3k
        ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
1106
78.3k
        break;
1107
5.79k
      case ZEND_CATCH:
1108
5.79k
        if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1109
4.38k
          ZEND_SET_OP_JMP_ADDR(opline, opline->op2, new_opcodes + blocks[b->successors[0]].start);
1110
4.38k
        }
1111
5.79k
        break;
1112
13.1k
      case ZEND_FE_FETCH_R:
1113
13.9k
      case ZEND_FE_FETCH_RW:
1114
13.9k
        opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[0]].start);
1115
13.9k
        break;
1116
14
      case ZEND_SWITCH_LONG:
1117
66
      case ZEND_SWITCH_STRING:
1118
204
      case ZEND_MATCH:
1119
204
      {
1120
204
        const HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
1121
204
        zval *zv;
1122
204
        uint32_t s = 0;
1123
204
        ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable));
1124
1125
2.00k
        ZEND_HASH_FOREACH_VAL(jumptable, zv) {
1126
2.00k
          Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
1127
2.00k
        } ZEND_HASH_FOREACH_END();
1128
204
        opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);
1129
204
        break;
1130
204
      }
1131
318k
    }
1132
318k
  }
1133
1134
  /* adjust exception jump targets & remove unused try_catch_array entries */
1135
83.6k
  if (op_array->last_try_catch) {
1136
19.6k
    uint32_t i, j;
1137
19.6k
    uint32_t *map;
1138
19.6k
    ALLOCA_FLAG(use_heap);
1139
1140
19.6k
    map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_try_catch, use_heap);
1141
45.2k
    for (i = 0, j = 0; i< op_array->last_try_catch; i++) {
1142
25.6k
      if (blocks[cfg->map[op_array->try_catch_array[i].try_op]].flags & ZEND_BB_REACHABLE) {
1143
25.6k
        map[i] = j;
1144
25.6k
        op_array->try_catch_array[j].try_op = blocks[cfg->map[op_array->try_catch_array[i].try_op]].start;
1145
25.6k
        if (op_array->try_catch_array[i].catch_op) {
1146
25.0k
          op_array->try_catch_array[j].catch_op = blocks[cfg->map[op_array->try_catch_array[i].catch_op]].start;
1147
25.0k
        } else {
1148
603
          op_array->try_catch_array[j].catch_op =  0;
1149
603
        }
1150
25.6k
        if (op_array->try_catch_array[i].finally_op) {
1151
746
          op_array->try_catch_array[j].finally_op = blocks[cfg->map[op_array->try_catch_array[i].finally_op]].start;
1152
24.8k
        } else {
1153
24.8k
          op_array->try_catch_array[j].finally_op =  0;
1154
24.8k
        }
1155
25.6k
        if (!op_array->try_catch_array[i].finally_end) {
1156
24.8k
          op_array->try_catch_array[j].finally_end = 0;
1157
24.8k
        } else {
1158
746
          op_array->try_catch_array[j].finally_end = blocks[cfg->map[op_array->try_catch_array[i].finally_end]].start;
1159
746
        }
1160
25.6k
        j++;
1161
25.6k
      }
1162
25.6k
    }
1163
19.6k
    if (i != j) {
1164
12
      op_array->last_try_catch = j;
1165
12
      if (j == 0) {
1166
6
        efree(op_array->try_catch_array);
1167
6
        op_array->try_catch_array = NULL;
1168
6
      }
1169
1170
12
      if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1171
0
        zend_op *finally_opline = new_opcodes;
1172
0
        const zend_op *last_finally_op = finally_opline + len;
1173
0
        while (finally_opline < last_finally_op) {
1174
0
          if (finally_opline->opcode == ZEND_FAST_RET &&
1175
0
              finally_opline->op2.num != (uint32_t)-1 &&
1176
0
              finally_opline->op2.num < j) {
1177
0
            finally_opline->op2.num = map[finally_opline->op2.num];
1178
0
          }
1179
0
          finally_opline++;
1180
0
        }
1181
0
      }
1182
12
    }
1183
19.6k
    free_alloca(map, use_heap);
1184
19.6k
  }
1185
1186
  /* rebuild map (just for printing) */
1187
83.6k
  memset(cfg->map, -1, sizeof(int) * op_array->last);
1188
445k
  for (int n = 0; n < cfg->blocks_count; n++) {
1189
362k
    if (cfg->blocks[n].flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1190
319k
      cfg->map[cfg->blocks[n].start] = n;
1191
319k
    }
1192
362k
  }
1193
83.6k
}
1194
1195
static zend_always_inline zend_basic_block *get_target_block(const zend_cfg *cfg, const zend_basic_block *block, int n, uint32_t *opt_count)
1196
169k
{
1197
169k
  int b;
1198
169k
  zend_basic_block *target_block = cfg->blocks + block->successors[n];
1199
1200
169k
  if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
1201
274
    do {
1202
274
      b = target_block->successors[0];
1203
274
      target_block = cfg->blocks + b;
1204
274
    } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
1205
180
    block->successors[n] = b;
1206
180
    ++(*opt_count);
1207
180
  }
1208
169k
  return target_block;
1209
169k
}
1210
1211
static zend_always_inline zend_basic_block *get_follow_block(const zend_cfg *cfg, const zend_basic_block *block, int n, uint32_t *opt_count)
1212
42.2k
{
1213
42.2k
  int b;
1214
42.2k
  zend_basic_block *target_block = cfg->blocks + block->successors[n];
1215
1216
42.2k
  if (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED)) {
1217
816
    do {
1218
816
      b = target_block->successors[0];
1219
816
      target_block = cfg->blocks + b;
1220
816
    } while (target_block->len == 0 && !(target_block->flags & ZEND_BB_PROTECTED));
1221
746
    block->successors[n] = b;
1222
746
    ++(*opt_count);
1223
746
  }
1224
42.2k
  return target_block;
1225
42.2k
}
1226
1227
static zend_always_inline zend_basic_block *get_next_block(const zend_cfg *cfg, zend_basic_block *block)
1228
89.1k
{
1229
89.1k
  zend_basic_block *next_block = block + 1;
1230
89.1k
  const zend_basic_block *end = cfg->blocks + cfg->blocks_count;
1231
1232
104k
  while (1) {
1233
104k
    if (next_block == end) {
1234
785
      return NULL;
1235
103k
    } else if (next_block->flags & ZEND_BB_REACHABLE) {
1236
88.3k
      break;
1237
88.3k
    }
1238
14.9k
    next_block++;
1239
14.9k
  }
1240
88.3k
  while (next_block->len == 0 && !(next_block->flags & (ZEND_BB_TARGET|ZEND_BB_PROTECTED))) {
1241
61
    next_block = cfg->blocks + next_block->successors[0];
1242
61
  }
1243
88.3k
  return next_block;
1244
89.1k
}
1245
1246
1247
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
1248
static zend_always_inline bool in_hitlist(int target, const int *jmp_hitlist, int jmp_hitlist_count)
1249
608
{
1250
608
  int i;
1251
1252
608
  for (i = 0; i < jmp_hitlist_count; i++) {
1253
231
    if (jmp_hitlist[i] == target) {
1254
231
      return 1;
1255
231
    }
1256
231
  }
1257
377
  return 0;
1258
608
}
1259
1260
#define CHECK_LOOP(target) \
1261
608
  if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
1262
377
    jmp_hitlist[jmp_hitlist_count++] = target;  \
1263
377
  } else { \
1264
231
    break; \
1265
231
  }
1266
1267
static void zend_jmp_optimization(zend_basic_block *block, zend_op_array *op_array, const zend_cfg *cfg, int *jmp_hitlist, uint32_t *opt_count)
1268
487k
{
1269
  /* last_op is the last opcode of the current block */
1270
487k
  zend_basic_block *target_block, *follow_block, *next_block;
1271
487k
  zend_op *last_op, *target;
1272
487k
  int next, jmp_hitlist_count;
1273
1274
487k
  if (block->len == 0) {
1275
1.28k
    return;
1276
1.28k
  }
1277
1278
486k
  last_op = op_array->opcodes + block->start + block->len - 1;
1279
486k
  switch (last_op->opcode) {
1280
88.3k
    case ZEND_JMP:
1281
88.3k
      jmp_hitlist_count = 0;
1282
1283
88.3k
      target_block = get_target_block(cfg, block, 0, opt_count);
1284
88.3k
      while (target_block->len == 1) {
1285
47.5k
        target = op_array->opcodes + target_block->start;
1286
47.5k
        if (target->opcode == ZEND_JMP) {
1287
          /* JMP L, L: JMP L1 -> JMP L1 */
1288
40
          next = target_block->successors[0];
1289
47.4k
        } else {
1290
47.4k
          break;
1291
47.4k
        }
1292
40
        CHECK_LOOP(next);
1293
22
        block->successors[0] = next;
1294
22
        ++(*opt_count);
1295
22
        target_block = get_target_block(cfg, block, 0, opt_count);
1296
22
      }
1297
1298
88.3k
      next_block = get_next_block(cfg, block);
1299
88.3k
      if (target_block == next_block) {
1300
        /* JMP(next) -> NOP */
1301
845
        MAKE_NOP(last_op);
1302
845
        ++(*opt_count);
1303
845
        block->len--;
1304
87.5k
      } else if (target_block->len == 1) {
1305
47.2k
        target = op_array->opcodes + target_block->start;
1306
47.2k
        if ((target->opcode == ZEND_RETURN ||
1307
47.0k
                    target->opcode == ZEND_RETURN_BY_REF ||
1308
47.0k
                    target->opcode == ZEND_GENERATOR_RETURN) &&
1309
294
                   !(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
1310
          /* JMP L, L: RETURN to immediate RETURN */
1311
30
          *last_op = *target;
1312
30
          if (last_op->op1_type == IS_CONST) {
1313
30
            zval zv;
1314
30
            ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(last_op));
1315
30
            last_op->op1.constant = zend_optimizer_add_literal(op_array, &zv);
1316
30
          }
1317
30
          block->successors_count = 0;
1318
30
          ++(*opt_count);
1319
30
        }
1320
47.2k
      }
1321
88.3k
      break;
1322
1323
1.84k
    case ZEND_JMP_SET:
1324
6.40k
    case ZEND_COALESCE:
1325
38.3k
    case ZEND_JMP_NULL:
1326
38.3k
      jmp_hitlist_count = 0;
1327
1328
38.3k
      target_block = get_target_block(cfg, block, 0, opt_count);
1329
38.3k
      while (target_block->len == 1) {
1330
3.69k
        target = op_array->opcodes + target_block->start;
1331
1332
3.69k
        if (target->opcode == ZEND_JMP) {
1333
          /* JMP_SET(X, L), L: JMP(L2) -> JMP_SET(X, L2) */
1334
32
          next = target_block->successors[0];
1335
32
          CHECK_LOOP(next);
1336
32
          block->successors[0] = next;
1337
32
          ++(*opt_count);
1338
3.66k
        } else {
1339
3.66k
          break;
1340
3.66k
        }
1341
32
        target_block = get_target_block(cfg, block, 0, opt_count);
1342
32
      }
1343
38.3k
      break;
1344
1345
27.8k
    case ZEND_JMPZ:
1346
39.1k
    case ZEND_JMPNZ:
1347
39.1k
      jmp_hitlist_count = 0;
1348
1349
39.1k
      target_block = get_target_block(cfg, block, 0, opt_count);
1350
39.4k
      while (target_block->len == 1) {
1351
26.8k
        target = op_array->opcodes + target_block->start;
1352
1353
26.8k
        if (target->opcode == ZEND_JMP) {
1354
          /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */
1355
84
          next = target_block->successors[0];
1356
26.7k
        } else if (target->opcode == last_op->opcode &&
1357
853
                   SAME_VAR(target->op1, last_op->op1)) {
1358
          /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */
1359
430
          next = target_block->successors[0];
1360
26.3k
        } else if (target->opcode == INV_COND(last_op->opcode) &&
1361
114
                   SAME_VAR(target->op1, last_op->op1)) {
1362
          /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */
1363
20
          next = target_block->successors[1];
1364
26.3k
        } else {
1365
26.3k
          break;
1366
26.3k
        }
1367
534
        CHECK_LOOP(next);
1368
321
        block->successors[0] = next;
1369
321
        ++(*opt_count);
1370
321
        target_block = get_target_block(cfg, block, 0, opt_count);
1371
321
      }
1372
1373
39.1k
      follow_block = get_follow_block(cfg, block, 1, opt_count);
1374
39.1k
      if (target_block == follow_block) {
1375
        /* L: JMP[N]Z(X, L+1) -> NOP or FREE(X) */
1376
555
        zend_optimizer_convert_to_free_op1(op_array, last_op);
1377
555
        if (last_op->opcode == ZEND_NOP) {
1378
0
          block->len--;
1379
0
        }
1380
555
        block->successors_count = 1;
1381
555
        ++(*opt_count);
1382
38.5k
      } else if (follow_block->len == 1) {
1383
7.23k
        target = op_array->opcodes + follow_block->start;
1384
7.23k
        if (target->opcode == ZEND_JMP) {
1385
916
            if (block->successors[0] == follow_block->successors[0]) {
1386
            /* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
1387
165
            zend_optimizer_convert_to_free_op1(op_array, last_op);
1388
165
            if (last_op->opcode == ZEND_NOP) {
1389
0
              block->len--;
1390
0
            }
1391
165
            block->successors[0] = follow_block - cfg->blocks;
1392
165
            block->successors_count = 1;
1393
165
            ++(*opt_count);
1394
165
            break;
1395
751
          } else if (!(follow_block->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED))) {
1396
751
            next_block = get_next_block(cfg, follow_block);
1397
1398
751
            if (target_block == next_block) {
1399
              /* JMPZ(X,L1) JMP(L2) L1: -> JMPNZ(X,L2) NOP*/
1400
1401
294
              last_op->opcode = INV_COND(last_op->opcode);
1402
1403
294
              block->successors[0] = follow_block->successors[0];
1404
294
              block->successors[1] = next_block - cfg->blocks;
1405
1406
294
              follow_block->flags &= ~ZEND_BB_REACHABLE;
1407
294
              MAKE_NOP(target);
1408
294
              follow_block->len = 0;
1409
1410
294
              next_block->flags |= ZEND_BB_FOLLOW;
1411
1412
294
              break;
1413
294
            }
1414
751
          }
1415
916
        }
1416
7.23k
      }
1417
38.6k
      break;
1418
1419
38.6k
    case ZEND_JMPNZ_EX:
1420
3.12k
    case ZEND_JMPZ_EX:
1421
3.12k
      jmp_hitlist_count = 0;
1422
1423
3.12k
      target_block = get_target_block(cfg, block, 0, opt_count);
1424
3.12k
      while (target_block->len == 1) {
1425
1.23k
        target = op_array->opcodes + target_block->start;
1426
1427
1.23k
        if (target->opcode == ZEND_JMP) {
1428
          /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */
1429
0
          next = target_block->successors[0];
1430
1.23k
        } else if (target->opcode == last_op->opcode-3 &&
1431
0
                   (SAME_VAR(target->op1, last_op->result) ||
1432
0
                    SAME_VAR(target->op1, last_op->op1))) {
1433
          /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1434
0
          next = target_block->successors[0];
1435
1.23k
        } else if (target->opcode == last_op->opcode &&
1436
0
                   target->result.var == last_op->result.var &&
1437
0
                   (SAME_VAR(target->op1, last_op->result) ||
1438
0
                    SAME_VAR(target->op1, last_op->op1))) {
1439
          /* T = JMPZ_EX(X, L1), L1: T = JMPZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L2) */
1440
0
          next = target_block->successors[0];
1441
1.23k
        } else if (target->opcode == INV_EX_COND(last_op->opcode) &&
1442
2
                   (SAME_VAR(target->op1, last_op->result) ||
1443
2
                    SAME_VAR(target->op1, last_op->op1))) {
1444
          /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */
1445
2
          next = target_block->successors[1];
1446
1.22k
        } else if (target->opcode == INV_EX_COND_EX(last_op->opcode) &&
1447
28
                   target->result.var == last_op->result.var &&
1448
20
                   (SAME_VAR(target->op1, last_op->result) ||
1449
20
                    SAME_VAR(target->op1, last_op->op1))) {
1450
          /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX({X|T}, L2) -> T = JMPZ_EX(X, L1+1) */
1451
0
          next = target_block->successors[1];
1452
1.22k
        } else if (target->opcode == ZEND_BOOL &&
1453
0
                   (SAME_VAR(target->op1, last_op->result) ||
1454
0
                    SAME_VAR(target->op1, last_op->op1))) {
1455
          /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
1456
             Z = JMPZ_EX(X,L1+1) */
1457
1458
          /* NOTE: This optimization pattern is not safe, but works, */
1459
          /*       because result of JMPZ_EX instruction             */
1460
          /*       is not used on the following path and             */
1461
          /*       should be used once on the branch path.           */
1462
          /*                                                         */
1463
          /*       The pattern works well only if jumps processed in */
1464
          /*       direct order, otherwise it breaks JMPZ_EX         */
1465
          /*       sequences too early.                              */
1466
0
          last_op->result.var = target->result.var;
1467
0
          next = target_block->successors[0];
1468
1.22k
        } else {
1469
1.22k
          break;
1470
1.22k
        }
1471
2
        CHECK_LOOP(next);
1472
2
        block->successors[0] = next;
1473
2
        ++(*opt_count);
1474
2
        target_block = get_target_block(cfg, block, 0, opt_count);
1475
2
      }
1476
1477
3.12k
      follow_block = get_follow_block(cfg, block, 1, opt_count);
1478
3.12k
      if (target_block == follow_block) {
1479
        /* L: T = JMP[N]Z_EX(X, L+1) -> T = BOOL(X) */
1480
0
        last_op->opcode = ZEND_BOOL;
1481
0
        last_op->op2.num = 0;
1482
0
        block->successors_count = 1;
1483
0
        ++(*opt_count);
1484
0
        break;
1485
0
      }
1486
3.12k
      break;
1487
486k
  }
1488
486k
}
1489
1490
/* Global data dependencies */
1491
1492
/* Find a set of variables which are used outside of the block where they are
1493
 * defined. We won't apply some optimization patterns for such variables. */
1494
static void zend_t_usage(const zend_cfg *cfg, const zend_op_array *op_array, zend_bitset used_ext, zend_optimizer_ctx *ctx)
1495
103k
{
1496
103k
  int n;
1497
103k
  zend_basic_block *block, *next_block;
1498
103k
  uint32_t var_num;
1499
103k
  uint32_t bitset_len;
1500
103k
  zend_bitset usage;
1501
103k
  zend_bitset defined_here;
1502
103k
  void *checkpoint;
1503
103k
  zend_op *opline, *end;
1504
1505
1506
103k
  if (op_array->T == 0) {
1507
    /* shortcut - if no Ts, nothing to do */
1508
12.9k
    return;
1509
12.9k
  }
1510
1511
90.5k
  checkpoint = zend_arena_checkpoint(ctx->arena);
1512
90.5k
  bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
1513
90.5k
  defined_here = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
1514
1515
90.5k
  zend_bitset_clear(defined_here, bitset_len);
1516
518k
  for (n = 1; n < cfg->blocks_count; n++) {
1517
428k
    block = cfg->blocks + n;
1518
1519
428k
    if (!(block->flags & ZEND_BB_REACHABLE)) {
1520
43.7k
      continue;
1521
43.7k
    }
1522
1523
384k
    opline = op_array->opcodes + block->start;
1524
384k
    end = opline + block->len;
1525
384k
    if (!(block->flags & ZEND_BB_FOLLOW) ||
1526
284k
        (block->flags & ZEND_BB_TARGET)) {
1527
      /* Skip continuation of "extended" BB */
1528
203k
      zend_bitset_clear(defined_here, bitset_len);
1529
203k
    }
1530
1531
2.11M
    while (opline<end) {
1532
1.73M
      if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1533
490k
        var_num = VAR_NUM(opline->op1.var);
1534
490k
        if (!zend_bitset_in(defined_here, var_num)) {
1535
234k
          zend_bitset_incl(used_ext, var_num);
1536
234k
        }
1537
490k
      }
1538
1.73M
      if (opline->op2_type == IS_VAR) {
1539
712
        var_num = VAR_NUM(opline->op2.var);
1540
712
        if (opline->opcode == ZEND_FE_FETCH_R ||
1541
712
            opline->opcode == ZEND_FE_FETCH_RW) {
1542
          /* these opcode use the op2 as result */
1543
12
          zend_bitset_incl(defined_here, var_num);
1544
700
        } else if (!zend_bitset_in(defined_here, var_num)) {
1545
2
          zend_bitset_incl(used_ext, var_num);
1546
2
        }
1547
1.73M
      } else if (opline->op2_type == IS_TMP_VAR) {
1548
339k
        var_num = VAR_NUM(opline->op2.var);
1549
339k
        if (!zend_bitset_in(defined_here, var_num)) {
1550
33.5k
          zend_bitset_incl(used_ext, var_num);
1551
33.5k
        }
1552
339k
      }
1553
1554
1.73M
      if (opline->result_type == IS_VAR) {
1555
8.59k
        var_num = VAR_NUM(opline->result.var);
1556
8.59k
        zend_bitset_incl(defined_here, var_num);
1557
1.72M
      } else if (opline->result_type == IS_TMP_VAR) {
1558
821k
        var_num = VAR_NUM(opline->result.var);
1559
821k
        switch (opline->opcode) {
1560
5.24k
          case ZEND_ADD_ARRAY_ELEMENT:
1561
5.28k
          case ZEND_ADD_ARRAY_UNPACK:
1562
213k
          case ZEND_ROPE_ADD:
1563
            /* these opcodes use the result as argument */
1564
213k
            if (!zend_bitset_in(defined_here, var_num)) {
1565
172k
              zend_bitset_incl(used_ext, var_num);
1566
172k
            }
1567
213k
            break;
1568
608k
          default :
1569
608k
            zend_bitset_incl(defined_here, var_num);
1570
821k
        }
1571
821k
      }
1572
1.73M
      opline++;
1573
1.73M
    }
1574
384k
  }
1575
1576
90.5k
  if (ctx->debug_level & ZEND_DUMP_BLOCK_PASS_VARS) {
1577
0
    bool printed = false;
1578
0
    uint32_t i;
1579
1580
0
    for (i = op_array->last_var; i< op_array->T; i++) {
1581
0
      if (zend_bitset_in(used_ext, i)) {
1582
0
        if (!printed) {
1583
0
          fprintf(stderr, "NON-LOCAL-VARS: %d", i);
1584
0
          printed = true;
1585
0
        } else {
1586
0
          fprintf(stderr, ", %d", i);
1587
0
        }
1588
0
      }
1589
0
    }
1590
0
    if (printed) {
1591
0
      fprintf(stderr, "\n");
1592
0
    }
1593
0
  }
1594
1595
90.5k
  usage = defined_here;
1596
90.5k
  next_block = NULL;
1597
609k
  for (n = cfg->blocks_count; n > 0;) {
1598
518k
    block = cfg->blocks + (--n);
1599
1600
518k
    if (!(block->flags & ZEND_BB_REACHABLE) || block->len == 0) {
1601
43.9k
      continue;
1602
43.9k
    }
1603
1604
474k
    end = op_array->opcodes + block->start;
1605
474k
    opline = end + block->len - 1;
1606
474k
    if (!next_block ||
1607
384k
        !(next_block->flags & ZEND_BB_FOLLOW) ||
1608
293k
        (next_block->flags & ZEND_BB_TARGET)) {
1609
      /* Skip continuation of "extended" BB */
1610
293k
      zend_bitset_copy(usage, used_ext, bitset_len);
1611
293k
    } else if (block->successors_count > 1) {
1612
116k
      zend_bitset_union(usage, used_ext, bitset_len);
1613
116k
    }
1614
474k
    next_block = block;
1615
1616
3.37M
    while (opline >= end) {
1617
      /* usage checks */
1618
2.90M
      if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
1619
1.36M
        if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
1620
7.29k
          switch (opline->opcode) {
1621
0
            case ZEND_ASSIGN_OP:
1622
0
            case ZEND_ASSIGN_DIM_OP:
1623
0
            case ZEND_ASSIGN_OBJ_OP:
1624
0
            case ZEND_ASSIGN_STATIC_PROP_OP:
1625
0
            case ZEND_PRE_INC:
1626
0
            case ZEND_PRE_DEC:
1627
0
            case ZEND_ASSIGN:
1628
0
            case ZEND_ASSIGN_REF:
1629
0
            case ZEND_DO_FCALL:
1630
0
            case ZEND_DO_ICALL:
1631
0
            case ZEND_DO_UCALL:
1632
0
            case ZEND_DO_FCALL_BY_NAME:
1633
0
              opline->result_type = IS_UNUSED;
1634
0
              break;
1635
0
            case ZEND_POST_INC:
1636
0
            case ZEND_POST_DEC:
1637
0
            case ZEND_POST_INC_OBJ:
1638
0
            case ZEND_POST_DEC_OBJ:
1639
0
            case ZEND_POST_INC_STATIC_PROP:
1640
0
            case ZEND_POST_DEC_STATIC_PROP:
1641
0
              opline->opcode -= 2;
1642
0
              opline->result_type = IS_UNUSED;
1643
0
              break;
1644
487
            case ZEND_QM_ASSIGN:
1645
1.35k
            case ZEND_BOOL:
1646
4.00k
            case ZEND_BOOL_NOT:
1647
4.00k
              zend_optimizer_convert_to_free_op1(op_array, opline);
1648
4.00k
              break;
1649
1.53k
            case ZEND_JMPZ_EX:
1650
3.19k
            case ZEND_JMPNZ_EX:
1651
3.19k
              opline->opcode -= 3;
1652
3.19k
              SET_UNUSED(opline->result);
1653
3.19k
              break;
1654
0
            case ZEND_ADD_ARRAY_ELEMENT:
1655
0
            case ZEND_ADD_ARRAY_UNPACK:
1656
0
            case ZEND_ROPE_ADD:
1657
0
              zend_bitset_incl(usage, VAR_NUM(opline->result.var));
1658
0
              break;
1659
7.29k
          }
1660
1.35M
        } else {
1661
1.35M
          switch (opline->opcode) {
1662
42.9k
            case ZEND_ADD_ARRAY_ELEMENT:
1663
43.1k
            case ZEND_ADD_ARRAY_UNPACK:
1664
322k
            case ZEND_ROPE_ADD:
1665
322k
              break;
1666
1.03M
            default:
1667
1.03M
              zend_bitset_excl(usage, VAR_NUM(opline->result.var));
1668
1.03M
              break;
1669
1.35M
          }
1670
1.35M
        }
1671
1.36M
      }
1672
1673
2.90M
      if (opline->op2_type == IS_VAR) {
1674
1.90k
        switch (opline->opcode) {
1675
0
          case ZEND_FE_FETCH_R:
1676
12
          case ZEND_FE_FETCH_RW:
1677
12
            zend_bitset_excl(usage, VAR_NUM(opline->op2.var));
1678
12
            break;
1679
1.89k
          default:
1680
1.89k
            zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
1681
1.89k
            break;
1682
1.90k
        }
1683
2.89M
      } else if (opline->op2_type == IS_TMP_VAR) {
1684
486k
        zend_bitset_incl(usage, VAR_NUM(opline->op2.var));
1685
486k
      }
1686
1687
2.90M
      if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1688
819k
        zend_bitset_incl(usage, VAR_NUM(opline->op1.var));
1689
819k
      }
1690
1691
2.90M
      opline--;
1692
2.90M
    }
1693
474k
  }
1694
1695
90.5k
  zend_arena_release(&ctx->arena, checkpoint);
1696
90.5k
}
1697
1698
static void zend_merge_blocks(const zend_op_array *op_array, const zend_cfg *cfg, uint32_t *opt_count)
1699
103k
{
1700
103k
  int i;
1701
103k
  zend_basic_block *b, *bb;
1702
103k
  zend_basic_block *prev = NULL;
1703
1704
638k
  for (i = 0; i < cfg->blocks_count; i++) {
1705
534k
    b = cfg->blocks + i;
1706
534k
    if (b->flags & ZEND_BB_REACHABLE) {
1707
485k
      if ((b->flags & ZEND_BB_FOLLOW) &&
1708
283k
          !(b->flags & (ZEND_BB_TARGET | ZEND_BB_PROTECTED)) &&
1709
139k
          prev && prev->successors_count == 1 && prev->successors[0] == i)
1710
25.0k
      {
1711
25.0k
        zend_op *last_op = op_array->opcodes + prev->start + prev->len - 1;
1712
25.0k
        if (prev->len != 0 && last_op->opcode == ZEND_JMP) {
1713
0
          MAKE_NOP(last_op);
1714
0
        }
1715
1716
28.1k
        for (bb = prev + 1; bb != b; bb++) {
1717
3.11k
          zend_op *op = op_array->opcodes + bb->start;
1718
3.11k
          const zend_op *end = op + bb->len;
1719
5.66k
          while (op < end) {
1720
2.54k
            if (op->op1_type == IS_CONST) {
1721
936
              literal_dtor(&ZEND_OP1_LITERAL(op));
1722
936
            }
1723
2.54k
            if (op->op2_type == IS_CONST) {
1724
532
              literal_dtor(&ZEND_OP2_LITERAL(op));
1725
532
            }
1726
2.54k
            MAKE_NOP(op);
1727
2.54k
            op++;
1728
2.54k
          }
1729
          /* make block empty */
1730
3.11k
          bb->len = 0;
1731
3.11k
        }
1732
1733
        /* re-link */
1734
25.0k
        prev->flags |= (b->flags & ZEND_BB_EXIT);
1735
25.0k
        prev->len = b->start + b->len - prev->start;
1736
25.0k
        prev->successors_count = b->successors_count;
1737
25.0k
        if (b->successors != b->successors_storage) {
1738
70
          prev->successors = b->successors;
1739
70
          b->successors = b->successors_storage;
1740
24.9k
        } else {
1741
24.9k
          memcpy(prev->successors, b->successors, b->successors_count * sizeof(int));
1742
24.9k
        }
1743
1744
        /* unlink & make block empty and unreachable */
1745
25.0k
        b->flags = 0;
1746
25.0k
        b->len = 0;
1747
25.0k
        b->successors_count = 0;
1748
25.0k
        ++(*opt_count);
1749
460k
      } else {
1750
460k
        prev = b;
1751
460k
      }
1752
485k
    }
1753
534k
  }
1754
103k
}
1755
1756
103k
#define PASSES 3
1757
1758
void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx)
1759
83.6k
{
1760
83.6k
  zend_cfg cfg;
1761
83.6k
  zend_basic_block *blocks, *end, *b;
1762
83.6k
  int pass;
1763
83.6k
  uint32_t bitset_len;
1764
83.6k
  zend_bitset usage;
1765
83.6k
  void *checkpoint;
1766
83.6k
  zend_op **Tsource;
1767
83.6k
  uint32_t opt_count;
1768
83.6k
  int *jmp_hitlist;
1769
1770
    /* Build CFG */
1771
83.6k
  checkpoint = zend_arena_checkpoint(ctx->arena);
1772
83.6k
  zend_build_cfg(&ctx->arena, op_array, 0, &cfg);
1773
1774
83.6k
  if (cfg.blocks_count * (op_array->last_var + op_array->T) > 64 * 1024 * 1024) {
1775
0
    zend_arena_release(&ctx->arena, checkpoint);
1776
0
    return;
1777
0
  }
1778
1779
83.6k
  if (ctx->debug_level & ZEND_DUMP_BEFORE_BLOCK_PASS) {
1780
0
    zend_dump_op_array(op_array, ZEND_DUMP_CFG, "before block pass", &cfg);
1781
0
  }
1782
1783
83.6k
  bitset_len = zend_bitset_len(op_array->last_var + op_array->T);
1784
83.6k
  Tsource = zend_arena_calloc(&ctx->arena, op_array->last_var + op_array->T, sizeof(zend_op *));
1785
83.6k
  usage = zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
1786
83.6k
  jmp_hitlist = zend_arena_alloc(&ctx->arena, cfg.blocks_count * sizeof(int));
1787
1788
83.6k
  blocks = cfg.blocks;
1789
83.6k
  end = blocks + cfg.blocks_count;
1790
103k
  for (pass = 0; pass < PASSES; pass++) {
1791
103k
    opt_count = 0;
1792
1793
    /* Compute data dependencies */
1794
103k
    zend_bitset_clear(usage, bitset_len);
1795
103k
    zend_t_usage(&cfg, op_array, usage, ctx);
1796
1797
    /* optimize each basic block separately */
1798
638k
    for (b = blocks; b < end; b++) {
1799
534k
      if (!(b->flags & ZEND_BB_REACHABLE)) {
1800
46.8k
        continue;
1801
46.8k
      }
1802
      /* we track data dependencies only inside a single basic block */
1803
487k
      if (!(b->flags & ZEND_BB_FOLLOW) ||
1804
306k
          (b->flags & ZEND_BB_TARGET)) {
1805
        /* Skip continuation of "extended" BB */
1806
306k
        memset(Tsource, 0, (op_array->last_var + op_array->T) * sizeof(zend_op *));
1807
306k
      }
1808
487k
      zend_optimize_block(b, op_array, usage, &cfg, Tsource, &opt_count);
1809
487k
    }
1810
1811
    /* Eliminate NOPs */
1812
638k
    for (b = blocks; b < end; b++) {
1813
534k
      if (b->flags & ZEND_BB_UNREACHABLE_FREE) {
1814
        /* In unreachable_free blocks only preserve loop var frees. */
1815
68
        for (uint32_t i = b->start; i < b->start + b->len; i++) {
1816
44
          zend_op *opline = &op_array->opcodes[i];
1817
44
          if (!zend_optimizer_is_loop_var_free(opline)) {
1818
20
            MAKE_NOP(opline);
1819
20
          }
1820
44
        }
1821
24
      }
1822
534k
      if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
1823
487k
        strip_nops(op_array, b);
1824
487k
      }
1825
534k
    }
1826
1827
103k
    opt_count = 0;
1828
1829
    /* Jump optimization for each block */
1830
638k
    for (b = blocks; b < end; b++) {
1831
534k
      if (b->flags & ZEND_BB_REACHABLE) {
1832
487k
        zend_jmp_optimization(b, op_array, &cfg, jmp_hitlist, &opt_count);
1833
487k
      }
1834
534k
    }
1835
1836
    /* Eliminate unreachable basic blocks */
1837
103k
    zend_cfg_remark_reachable_blocks(op_array, &cfg);
1838
1839
    /* Merge Blocks */
1840
103k
    zend_merge_blocks(op_array, &cfg, &opt_count);
1841
1842
103k
    if (opt_count == 0) {
1843
83.5k
      break;
1844
83.5k
    }
1845
103k
  }
1846
1847
83.6k
  assemble_code_blocks(&cfg, op_array, ctx);
1848
1849
83.6k
  if (ctx->debug_level & ZEND_DUMP_AFTER_BLOCK_PASS) {
1850
0
    zend_dump_op_array(op_array, ZEND_DUMP_CFG | ZEND_DUMP_HIDE_UNREACHABLE, "after block pass", &cfg);
1851
0
  }
1852
1853
  /* Destroy CFG */
1854
83.6k
  zend_arena_release(&ctx->arena, checkpoint);
1855
83.6k
}