Coverage Report

Created: 2025-07-23 06:33

/src/php-src/Zend/Optimizer/zend_dfg.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend Engine, DFG - Data Flow Graph                                   |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) The PHP Group                                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 3.01 of the PHP license,      |
8
   | that is bundled with this package in the file LICENSE, and is        |
9
   | available through the world-wide-web at the following url:           |
10
   | https://www.php.net/license/3_01.txt                                 |
11
   | If you did not receive a copy of the PHP license and are unable to   |
12
   | obtain it through the world-wide-web, please send a note to          |
13
   | license@php.net so we can mail you a copy immediately.               |
14
   +----------------------------------------------------------------------+
15
   | Authors: Dmitry Stogov <dmitry@php.net>                              |
16
   +----------------------------------------------------------------------+
17
*/
18
19
#include "zend_compile.h"
20
#include "zend_dfg.h"
21
22
static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
23
1.74M
{
24
1.74M
  uint32_t var_num;
25
1.74M
  const zend_op *next;
26
27
1.74M
  if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
28
1.00M
    var_num = EX_VAR_TO_NUM(opline->op1.var);
29
1.00M
    if (!zend_bitset_in(def, var_num)) {
30
244k
      zend_bitset_incl(use, var_num);
31
244k
    }
32
1.00M
  }
33
1.74M
  if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0
34
1.74M
    && opline->opcode != ZEND_FE_FETCH_R
35
1.74M
    && opline->opcode != ZEND_FE_FETCH_RW)
36
1.74M
   || (opline->op2_type == IS_CV)) {
37
309k
    var_num = EX_VAR_TO_NUM(opline->op2.var);
38
309k
    if (!zend_bitset_in(def, var_num)) {
39
164k
      zend_bitset_incl(use, var_num);
40
164k
    }
41
309k
  }
42
1.74M
  if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
43
1.74M
   && opline->result_type == IS_CV
44
1.74M
   && opline->opcode != ZEND_RECV) {
45
0
    var_num = EX_VAR_TO_NUM(opline->result.var);
46
0
    if (!zend_bitset_in(def, var_num)) {
47
0
      zend_bitset_incl(use, var_num);
48
0
    }
49
0
  }
50
51
1.74M
  switch (opline->opcode) {
52
61.2k
    case ZEND_ASSIGN:
53
61.2k
      if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
54
0
        zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
55
0
      }
56
61.2k
      if (opline->op1_type == IS_CV) {
57
117k
add_op1_def:
58
117k
        zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var));
59
117k
      }
60
117k
      break;
61
117k
    case ZEND_ASSIGN_REF:
62
2.15k
      if (opline->op2_type == IS_CV) {
63
1.08k
        zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
64
1.08k
      }
65
2.15k
      if (opline->op1_type == IS_CV) {
66
1.55k
        goto add_op1_def;
67
1.55k
      }
68
602
      break;
69
7.42k
    case ZEND_ASSIGN_DIM:
70
15.4k
    case ZEND_ASSIGN_OBJ:
71
15.4k
      next = opline + 1;
72
15.4k
      if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
73
8.94k
        var_num = EX_VAR_TO_NUM(next->op1.var);
74
8.94k
        if (!zend_bitset_in(def, var_num)) {
75
1.19k
          zend_bitset_incl(use, var_num);
76
1.19k
        }
77
8.94k
        if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
78
0
          zend_bitset_incl(def, var_num);
79
0
        }
80
8.94k
      }
81
15.4k
      if (opline->op1_type == IS_CV) {
82
9.57k
        goto add_op1_def;
83
9.57k
      }
84
5.89k
      break;
85
5.89k
    case ZEND_ASSIGN_OBJ_REF:
86
283
      next = opline + 1;
87
283
      if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
88
283
        var_num = EX_VAR_TO_NUM(next->op1.var);
89
283
        if (!zend_bitset_in(def, var_num)) {
90
48
          zend_bitset_incl(use, var_num);
91
48
        }
92
283
        if (next->op1_type == IS_CV) {
93
195
          zend_bitset_incl(def, var_num);
94
195
        }
95
283
      }
96
283
      if (opline->op1_type == IS_CV) {
97
160
        goto add_op1_def;
98
160
      }
99
123
      break;
100
1.02k
    case ZEND_ASSIGN_STATIC_PROP:
101
1.02k
      next = opline + 1;
102
1.02k
      if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
103
666
        var_num = EX_VAR_TO_NUM(next->op1.var);
104
666
        if (!zend_bitset_in(def, var_num)) {
105
318
          zend_bitset_incl(use, var_num);
106
318
        }
107
666
        if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
108
0
          zend_bitset_incl(def, var_num);
109
0
        }
110
666
      }
111
1.02k
      break;
112
104
    case ZEND_ASSIGN_STATIC_PROP_REF:
113
104
      next = opline + 1;
114
104
      if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
115
104
        var_num = EX_VAR_TO_NUM(next->op1.var);
116
104
        if (!zend_bitset_in(def, var_num)) {
117
4
          zend_bitset_incl(use, var_num);
118
4
        }
119
104
        if (next->op1_type == IS_CV) {
120
36
          zend_bitset_incl(def, var_num);
121
36
        }
122
104
      }
123
104
      break;
124
22
    case ZEND_ASSIGN_STATIC_PROP_OP:
125
22
    case ZEND_FRAMELESS_ICALL_3:
126
22
      next = opline + 1;
127
22
      if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
128
22
        var_num = EX_VAR_TO_NUM(next->op1.var);
129
22
        if (!zend_bitset_in(def, var_num)) {
130
0
          zend_bitset_incl(use, var_num);
131
0
        }
132
22
      }
133
22
      break;
134
1.17k
    case ZEND_ASSIGN_DIM_OP:
135
1.80k
    case ZEND_ASSIGN_OBJ_OP:
136
1.80k
      next = opline + 1;
137
1.80k
      if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
138
871
        var_num = EX_VAR_TO_NUM(next->op1.var);
139
871
        if (!zend_bitset_in(def, var_num)) {
140
133
          zend_bitset_incl(use, var_num);
141
133
        }
142
871
      }
143
1.80k
      if (opline->op1_type == IS_CV) {
144
1.35k
        goto add_op1_def;
145
1.35k
      }
146
455
      break;
147
4.61k
    case ZEND_ASSIGN_OP:
148
8.95k
    case ZEND_PRE_INC:
149
9.46k
    case ZEND_PRE_DEC:
150
10.2k
    case ZEND_POST_INC:
151
11.4k
    case ZEND_POST_DEC:
152
12.0k
    case ZEND_BIND_GLOBAL:
153
26.7k
    case ZEND_BIND_STATIC:
154
26.9k
    case ZEND_BIND_INIT_STATIC_OR_JMP:
155
27.4k
    case ZEND_SEND_VAR_NO_REF:
156
30.3k
    case ZEND_SEND_VAR_NO_REF_EX:
157
36.3k
    case ZEND_SEND_VAR_EX:
158
37.7k
    case ZEND_SEND_FUNC_ARG:
159
39.9k
    case ZEND_SEND_REF:
160
40.5k
    case ZEND_SEND_UNPACK:
161
41.1k
    case ZEND_FE_RESET_RW:
162
42.0k
    case ZEND_MAKE_REF:
163
42.4k
    case ZEND_PRE_INC_OBJ:
164
42.6k
    case ZEND_PRE_DEC_OBJ:
165
42.7k
    case ZEND_POST_INC_OBJ:
166
42.8k
    case ZEND_POST_DEC_OBJ:
167
43.6k
    case ZEND_UNSET_DIM:
168
44.3k
    case ZEND_UNSET_OBJ:
169
46.7k
    case ZEND_FETCH_DIM_W:
170
47.2k
    case ZEND_FETCH_DIM_RW:
171
47.9k
    case ZEND_FETCH_DIM_FUNC_ARG:
172
48.1k
    case ZEND_FETCH_DIM_UNSET:
173
48.4k
    case ZEND_FETCH_LIST_W:
174
48.4k
      if (opline->op1_type == IS_CV) {
175
40.0k
        goto add_op1_def;
176
40.0k
      }
177
8.39k
      break;
178
34.8k
    case ZEND_SEND_VAR:
179
36.4k
    case ZEND_CAST:
180
48.3k
    case ZEND_QM_ASSIGN:
181
49.5k
    case ZEND_JMP_SET:
182
53.9k
    case ZEND_COALESCE:
183
56.5k
    case ZEND_FE_RESET_R:
184
56.5k
      if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
185
0
        goto add_op1_def;
186
0
      }
187
56.5k
      break;
188
56.5k
    case ZEND_ADD_ARRAY_UNPACK:
189
188
      var_num = EX_VAR_TO_NUM(opline->result.var);
190
188
      if (!zend_bitset_in(def, var_num)) {
191
0
        zend_bitset_incl(use, var_num);
192
0
      }
193
188
      break;
194
19.2k
    case ZEND_ADD_ARRAY_ELEMENT:
195
19.2k
      var_num = EX_VAR_TO_NUM(opline->result.var);
196
19.2k
      if (!zend_bitset_in(def, var_num)) {
197
734
        zend_bitset_incl(use, var_num);
198
734
      }
199
19.2k
      ZEND_FALLTHROUGH;
200
23.2k
    case ZEND_INIT_ARRAY:
201
23.2k
      if (((build_flags & ZEND_SSA_RC_INFERENCE)
202
23.2k
            || (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
203
23.2k
          && opline->op1_type == IS_CV) {
204
191
        goto add_op1_def;
205
191
      }
206
23.0k
      break;
207
23.0k
    case ZEND_YIELD:
208
1.78k
      if (opline->op1_type == IS_CV
209
1.78k
          && ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
210
331
            || (build_flags & ZEND_SSA_RC_INFERENCE))) {
211
62
        goto add_op1_def;
212
62
      }
213
1.72k
      break;
214
1.76k
    case ZEND_UNSET_CV:
215
1.76k
      goto add_op1_def;
216
3.37k
    case ZEND_VERIFY_RETURN_TYPE:
217
3.37k
      if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
218
1.64k
        goto add_op1_def;
219
1.64k
      }
220
1.73k
      break;
221
2.65k
    case ZEND_FE_FETCH_R:
222
3.33k
    case ZEND_FE_FETCH_RW:
223
#if 0
224
      /* This special case was handled above the switch */
225
      if (opline->op2_type != IS_CV) {
226
        op2_use = -1; /* not used */
227
      }
228
#endif
229
3.33k
      zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
230
3.33k
      break;
231
14.2k
    case ZEND_BIND_LEXICAL:
232
14.2k
      if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
233
460
        zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
234
460
      }
235
14.2k
      break;
236
1.51M
    default:
237
1.51M
      break;
238
1.74M
  }
239
240
1.74M
  if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
241
956k
    zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var));
242
956k
  }
243
1.74M
}
244
/* }}} */
245
246
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
247
0
{
248
0
  _zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
249
0
}
250
/* }}} */
251
252
void zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
253
78.5k
{
254
78.5k
  int set_size;
255
78.5k
  zend_basic_block *blocks = cfg->blocks;
256
78.5k
  int blocks_count = cfg->blocks_count;
257
78.5k
  zend_bitset tmp, def, use, in, out;
258
78.5k
  int k;
259
78.5k
  int j;
260
261
78.5k
  set_size = dfg->size;
262
78.5k
  tmp = dfg->tmp;
263
78.5k
  def = dfg->def;
264
78.5k
  use = dfg->use;
265
78.5k
  in  = dfg->in;
266
78.5k
  out = dfg->out;
267
268
  /* Collect "def" and "use" sets */
269
302k
  for (j = 0; j < blocks_count; j++) {
270
223k
    zend_op *opline, *end;
271
223k
    zend_bitset b_use, b_def;
272
273
223k
    if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
274
28
      continue;
275
28
    }
276
277
223k
    opline = op_array->opcodes + blocks[j].start;
278
223k
    end = opline + blocks[j].len;
279
223k
    b_use = DFG_BITSET(use, set_size, j);
280
223k
    b_def = DFG_BITSET(def, set_size, j);
281
1.99M
    for (; opline < end; opline++) {
282
1.76M
      if (opline->opcode != ZEND_OP_DATA) {
283
1.74M
        _zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
284
1.74M
      }
285
1.76M
    }
286
223k
  }
287
288
  /* Calculate "in" and "out" sets */
289
78.5k
  {
290
78.5k
    uint32_t worklist_len = zend_bitset_len(blocks_count);
291
78.5k
    zend_bitset worklist;
292
78.5k
    ALLOCA_FLAG(use_heap);
293
78.5k
    worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
294
78.5k
    memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
295
302k
    for (j = 0; j < blocks_count; j++) {
296
223k
      zend_bitset_incl(worklist, j);
297
223k
    }
298
348k
    while (!zend_bitset_empty(worklist, worklist_len)) {
299
      /* We use the last block on the worklist, because predecessors tend to be located
300
       * before the succeeding block, so this converges faster. */
301
269k
      j = zend_bitset_last(worklist, worklist_len);
302
269k
      zend_bitset_excl(worklist, j);
303
304
269k
      if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
305
28
        continue;
306
28
      }
307
269k
      if (blocks[j].successors_count != 0) {
308
190k
        zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
309
286k
        for (k = 1; k < blocks[j].successors_count; k++) {
310
96.2k
          zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
311
96.2k
        }
312
190k
      } else {
313
79.7k
        zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
314
79.7k
      }
315
269k
      zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
316
269k
      if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
317
192k
        zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
318
319
        /* Add predecessors of changed block to worklist */
320
192k
        {
321
192k
          int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
322
438k
          for (k = 0; k < blocks[j].predecessors_count; k++) {
323
245k
            zend_bitset_incl(worklist, predecessors[k]);
324
245k
          }
325
192k
        }
326
192k
      }
327
269k
    }
328
329
78.5k
    free_alloca(worklist, use_heap);
330
78.5k
  }
331
78.5k
}
332
/* }}} */