Coverage Report

Created: 2026-01-18 06:49

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 (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_observer.h"
30
31
36.3k
#define INVALID_VAR ((uint32_t)-1)
32
#define GET_AVAILABLE_T()         \
33
2.23M
  for (i = 0; i < T; i++) {       \
34
2.23M
    if (!zend_bitset_in(taken_T, i)) { \
35
8.21k
      break;              \
36
8.21k
    }                  \
37
2.23M
  }                    \
38
8.21k
  zend_bitset_incl(taken_T, i);     \
39
8.21k
  if (i > max) {             \
40
3.50k
    max = i;              \
41
3.50k
  }
42
43
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
44
278
{
45
278
  uint32_t T = op_array->T;
46
278
  int offset = op_array->last_var;
47
278
  uint32_t bitset_len;
48
278
  zend_bitset taken_T;  /* T index in use */
49
278
  zend_op **start_of_T; /* opline where T is first used */
50
278
  int *map_T;       /* Map's the T to its new index */
51
278
  zend_op *opline, *end;
52
278
  int currT;
53
278
  int i;
54
278
  int max = -1;
55
278
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
56
57
278
  bitset_len = zend_bitset_len(T);
58
278
  taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
59
278
  start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
60
278
  map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
61
278
  memset(map_T, 0xff, T * sizeof(int));
62
63
278
  end = op_array->opcodes;
64
278
  opline = &op_array->opcodes[op_array->last - 1];
65
66
  /* Find T definition points */
67
27.3k
  while (opline >= end) {
68
27.0k
    if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
69
19.3k
      start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
70
19.3k
    }
71
27.0k
    opline--;
72
27.0k
  }
73
74
278
  zend_bitset_clear(taken_T, bitset_len);
75
76
278
  end = op_array->opcodes;
77
278
  opline = &op_array->opcodes[op_array->last - 1];
78
79
27.3k
  while (opline >= end) {
80
27.0k
    if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
81
14.0k
      currT = VAR_NUM(opline->op1.var) - offset;
82
14.0k
      if (opline->opcode == ZEND_ROPE_END) {
83
162
        int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
84
162
        int var;
85
86
162
        var = max;
87
4.05k
        while (var >= 0 && !zend_bitset_in(taken_T, var)) {
88
3.89k
          var--;
89
3.89k
        }
90
162
        max = MAX(max, var + num);
91
162
        var = var + 1;
92
162
        map_T[currT] = var;
93
162
        zend_bitset_incl(taken_T, var);
94
162
        opline->op1.var = NUM_VAR(var + offset);
95
4.78k
        while (num > 1) {
96
4.62k
          num--;
97
4.62k
          zend_bitset_incl(taken_T, var + num);
98
4.62k
        }
99
13.8k
      } else {
100
13.8k
        if (map_T[currT] == INVALID_VAR) {
101
4.72k
          int use_new_var = 0;
102
103
          /* Code in "finally" blocks may modify temporary variables.
104
           * We allocate new temporaries for values that need to
105
           * relive FAST_CALLs.
106
           */
107
4.72k
          if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
108
0
              (opline->opcode == ZEND_RETURN ||
109
0
               opline->opcode == ZEND_GENERATOR_RETURN ||
110
0
               opline->opcode == ZEND_RETURN_BY_REF ||
111
0
               opline->opcode == ZEND_FREE ||
112
0
               opline->opcode == ZEND_FE_FREE)) {
113
0
            zend_op *curr = opline;
114
115
0
            while (--curr >= end) {
116
0
              if (curr->opcode == ZEND_FAST_CALL) {
117
0
                use_new_var = 1;
118
0
                break;
119
0
              } else if (curr->opcode != ZEND_FREE &&
120
0
                         curr->opcode != ZEND_FE_FREE &&
121
0
                         curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
122
0
                         curr->opcode != ZEND_DISCARD_EXCEPTION) {
123
0
                break;
124
0
              }
125
0
            }
126
0
          }
127
4.72k
          if (use_new_var) {
128
0
            i = ++max;
129
0
            zend_bitset_incl(taken_T, i);
130
4.72k
          } else {
131
4.72k
            GET_AVAILABLE_T();
132
4.72k
          }
133
4.72k
          map_T[currT] = i;
134
4.72k
        }
135
13.8k
        opline->op1.var = NUM_VAR(map_T[currT] + offset);
136
13.8k
      }
137
14.0k
    }
138
139
27.0k
    if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
140
3.14k
      currT = VAR_NUM(opline->op2.var) - offset;
141
3.14k
      if (map_T[currT] == INVALID_VAR) {
142
3.14k
        GET_AVAILABLE_T();
143
3.14k
        map_T[currT] = i;
144
3.14k
      }
145
3.14k
      opline->op2.var = NUM_VAR(map_T[currT] + offset);
146
3.14k
    }
147
148
27.0k
    if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
149
19.3k
      currT = VAR_NUM(opline->result.var) - offset;
150
19.3k
      if (map_T[currT] == INVALID_VAR) {
151
        /* As a result of DCE, an opcode may have an unused result. */
152
349
        GET_AVAILABLE_T();
153
349
        map_T[currT] = i;
154
349
      }
155
19.3k
      opline->result.var = NUM_VAR(map_T[currT] + offset);
156
19.3k
      if (start_of_T[currT] == opline) {
157
        /* ZEND_FAST_CALL can not share temporary var with others
158
         * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
159
         * which could be ahead of it */
160
8.38k
        if (opline->opcode != ZEND_FAST_CALL) {
161
8.38k
          zend_bitset_excl(taken_T, map_T[currT]);
162
8.38k
        }
163
8.38k
        if (opline->opcode == ZEND_ROPE_INIT) {
164
162
          uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
165
4.78k
          while (num > 1) {
166
4.62k
            num--;
167
4.62k
            zend_bitset_excl(taken_T, map_T[currT]+num);
168
4.62k
          }
169
162
        }
170
8.38k
      }
171
19.3k
    }
172
173
27.0k
    opline--;
174
27.0k
  }
175
176
278
  zend_arena_release(&ctx->arena, checkpoint);
177
278
  op_array->T = max + 1 + ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled
178
278
}