Coverage Report

Created: 2026-06-13 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/Optimizer/optimize_temp_vars_5.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache                                                         |
4
   +----------------------------------------------------------------------+
5
   | Copyright © The PHP Group and Contributors.                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to the Modified BSD License that is      |
8
   | bundled with this package in the file LICENSE, and is available      |
9
   | through the World Wide Web at <https://www.php.net/license/>.        |
10
   |                                                                      |
11
   | SPDX-License-Identifier: BSD-3-Clause                                |
12
   +----------------------------------------------------------------------+
13
   | Authors: Andi Gutmans <andi@php.net>                                 |
14
   |          Zeev Suraski <zeev@php.net>                                 |
15
   |          Stanislav Malyshev <stas@zend.com>                          |
16
   |          Dmitry Stogov <dmitry@php.net>                              |
17
   +----------------------------------------------------------------------+
18
*/
19
20
#include "Optimizer/zend_optimizer.h"
21
#include "Optimizer/zend_optimizer_internal.h"
22
#include "zend_API.h"
23
#include "zend_constants.h"
24
#include "zend_execute.h"
25
#include "zend_vm.h"
26
#include "zend_bitset.h"
27
#include "zend_observer.h"
28
29
2.01M
#define INVALID_VAR ((uint32_t)-1)
30
#define GET_AVAILABLE_T()         \
31
115M
  for (i = 0; i < T; i++) {       \
32
115M
    if (!zend_bitset_in(taken_T, i)) { \
33
679k
      break;              \
34
679k
    }                  \
35
115M
  }                    \
36
679k
  zend_bitset_incl(taken_T, i);     \
37
679k
  if (i > max) {             \
38
188k
    max = i;              \
39
188k
  }
40
41
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
42
94.6k
{
43
94.6k
  uint32_t T = op_array->T;
44
94.6k
  int offset = op_array->last_var;
45
94.6k
  uint32_t bitset_len;
46
94.6k
  zend_bitset taken_T;  /* T index in use */
47
94.6k
  zend_op **start_of_T; /* opline where T is first used */
48
94.6k
  int *map_T;       /* Map's the T to its new index */
49
94.6k
  zend_op *opline, *end;
50
94.6k
  int currT;
51
94.6k
  int i;
52
94.6k
  int max = -1;
53
94.6k
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
54
55
94.6k
  bitset_len = zend_bitset_len(T);
56
94.6k
  taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
57
94.6k
  start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
58
94.6k
  map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
59
94.6k
  memset(map_T, 0xff, T * sizeof(int));
60
61
94.6k
  end = op_array->opcodes;
62
94.6k
  opline = &op_array->opcodes[op_array->last - 1];
63
64
  /* Find T definition points */
65
2.35M
  while (opline >= end) {
66
2.26M
    if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
67
1.04M
      start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
68
1.04M
    }
69
2.26M
    opline--;
70
2.26M
  }
71
72
94.6k
  zend_bitset_clear(taken_T, bitset_len);
73
74
94.6k
  end = op_array->opcodes;
75
94.6k
  opline = &op_array->opcodes[op_array->last - 1];
76
77
2.35M
  while (opline >= end) {
78
2.26M
    if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
79
707k
      currT = VAR_NUM(opline->op1.var) - offset;
80
707k
      if (opline->opcode == ZEND_ROPE_END) {
81
26.7k
        int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
82
26.7k
        int var;
83
84
26.7k
        var = max;
85
150k
        while (var >= 0 && !zend_bitset_in(taken_T, var)) {
86
123k
          var--;
87
123k
        }
88
26.7k
        max = MAX(max, var + num);
89
26.7k
        var = var + 1;
90
26.7k
        map_T[currT] = var;
91
26.7k
        zend_bitset_incl(taken_T, var);
92
26.7k
        opline->op1.var = NUM_VAR(var + offset);
93
174k
        while (num > 1) {
94
147k
          num--;
95
147k
          zend_bitset_incl(taken_T, var + num);
96
147k
        }
97
681k
      } else {
98
681k
        if (map_T[currT] == INVALID_VAR) {
99
380k
          int use_new_var = 0;
100
101
          /* Code in "finally" blocks may modify temporary variables.
102
           * We allocate new temporaries for values that need to
103
           * relive FAST_CALLs.
104
           */
105
380k
          if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
106
2.10k
              (opline->opcode == ZEND_RETURN ||
107
2.00k
               opline->opcode == ZEND_GENERATOR_RETURN ||
108
1.98k
               opline->opcode == ZEND_RETURN_BY_REF ||
109
1.96k
               opline->opcode == ZEND_FREE ||
110
1.91k
               opline->opcode == ZEND_FE_FREE)) {
111
313
            zend_op *curr = opline;
112
113
374
            while (--curr >= end) {
114
374
              if (curr->opcode == ZEND_FAST_CALL) {
115
113
                use_new_var = 1;
116
113
                break;
117
261
              } else if (curr->opcode != ZEND_FREE &&
118
261
                         curr->opcode != ZEND_FE_FREE &&
119
241
                         curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
120
216
                         curr->opcode != ZEND_DISCARD_EXCEPTION) {
121
200
                break;
122
200
              }
123
374
            }
124
313
          }
125
380k
          if (use_new_var) {
126
113
            i = ++max;
127
113
            zend_bitset_incl(taken_T, i);
128
380k
          } else {
129
380k
            GET_AVAILABLE_T();
130
380k
          }
131
380k
          map_T[currT] = i;
132
380k
        }
133
681k
        opline->op1.var = NUM_VAR(map_T[currT] + offset);
134
681k
      }
135
707k
    }
136
137
2.26M
    if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
138
288k
      currT = VAR_NUM(opline->op2.var) - offset;
139
288k
      if (map_T[currT] == INVALID_VAR) {
140
287k
        GET_AVAILABLE_T();
141
287k
        map_T[currT] = i;
142
287k
      }
143
288k
      opline->op2.var = NUM_VAR(map_T[currT] + offset);
144
288k
    }
145
146
2.26M
    if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
147
1.04M
      currT = VAR_NUM(opline->result.var) - offset;
148
1.04M
      if (map_T[currT] == INVALID_VAR) {
149
        /* As a result of DCE, an opcode may have an unused result. */
150
11.5k
        GET_AVAILABLE_T();
151
11.5k
        map_T[currT] = i;
152
11.5k
      }
153
1.04M
      opline->result.var = NUM_VAR(map_T[currT] + offset);
154
1.04M
      if (start_of_T[currT] == opline) {
155
        /* ZEND_FAST_CALL can not share temporary var with others
156
         * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
157
         * which could be ahead of it */
158
705k
        if (opline->opcode != ZEND_FAST_CALL) {
159
705k
          zend_bitset_excl(taken_T, map_T[currT]);
160
705k
        }
161
705k
        if (opline->opcode == ZEND_ROPE_INIT) {
162
26.7k
          uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
163
174k
          while (num > 1) {
164
147k
            num--;
165
147k
            zend_bitset_excl(taken_T, map_T[currT]+num);
166
147k
          }
167
26.7k
        }
168
705k
      }
169
1.04M
    }
170
171
2.26M
    opline--;
172
2.26M
  }
173
174
94.6k
  zend_arena_release(&ctx->arena, checkpoint);
175
94.6k
  op_array->T = max + 1 + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled
176
94.6k
}