Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/Optimizer/escape_analysis.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache, Escape Analysis                                        |
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: Dmitry Stogov <dmitry@php.net>                              |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#include "Optimizer/zend_optimizer.h"
18
#include "Optimizer/zend_optimizer_internal.h"
19
#include "zend_bitset.h"
20
#include "zend_cfg.h"
21
#include "zend_ssa.h"
22
#include "zend_inference.h"
23
#include "zend_dump.h"
24
25
/*
26
 * T. Kotzmann and H. Mossenbock. Escape analysis  in the context of dynamic
27
 * compilation and deoptimization. In Proceedings of the International
28
 * Conference on Virtual Execution Environments, pages 111-120, Chicago,
29
 * June 2005
30
 */
31
32
static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */
33
1.40k
{
34
1.40k
  int i;
35
36
11.5k
  for (i = 0; i < count; i++) {
37
10.1k
    parent[i] = i;
38
10.1k
    size[i] = 1;
39
10.1k
  }
40
1.40k
}
41
/* }}} */
42
43
static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */
44
19.5k
{
45
19.5k
  int p = parent[i];
46
47
25.4k
  while (i != p) {
48
5.97k
    p = parent[p];
49
5.97k
    parent[i] = p;
50
5.97k
    i = p;
51
5.97k
    p = parent[i];
52
5.97k
  }
53
19.5k
  return i;
54
19.5k
}
55
/* }}} */
56
57
static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */
58
4.70k
{
59
4.70k
  int r1 = union_find_root(parent, i);
60
4.70k
  int r2 = union_find_root(parent, j);
61
62
4.70k
  if (r1 != r2) {
63
3.02k
    if (size[r1] < size[r2]) {
64
926
      parent[r1] = r2;
65
926
      size[r2] += size[r1];
66
2.09k
    } else {
67
2.09k
      parent[r2] = r1;
68
2.09k
      size[r1] += size[r2];
69
2.09k
    }
70
3.02k
  }
71
4.70k
}
72
/* }}} */
73
74
static zend_result zend_build_equi_escape_sets(int *parent, const zend_op_array *op_array, const zend_ssa *ssa) /* {{{ */
75
1.40k
{
76
1.40k
  zend_ssa_var *ssa_vars = ssa->vars;
77
1.40k
  int ssa_vars_count = ssa->vars_count;
78
1.40k
  int i;
79
1.40k
  int *size;
80
1.40k
  ALLOCA_FLAG(use_heap)
81
82
1.40k
  size = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
83
1.40k
  if (!size) {
84
0
    return FAILURE;
85
0
  }
86
1.40k
  union_find_init(parent, size, ssa_vars_count);
87
88
11.5k
  for (i = 0; i < ssa_vars_count; i++) {
89
10.1k
    if (ssa_vars[i].definition_phi) {
90
405
      const zend_ssa_phi *p = ssa_vars[i].definition_phi;
91
405
      if (p->pi >= 0) {
92
8
        union_find_unite(parent, size, i, p->sources[0]);
93
397
      } else {
94
1.19k
        for (uint32_t j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
95
796
          union_find_unite(parent, size, i, p->sources[j]);
96
796
        }
97
397
      }
98
9.71k
    } else if (ssa_vars[i].definition >= 0) {
99
7.47k
      int def = ssa_vars[i].definition;
100
7.47k
      const zend_ssa_op *op = ssa->ops + def;
101
7.47k
      const zend_op *opline =  op_array->opcodes + def;
102
103
7.47k
      if (op->op1_def >= 0) {
104
2.53k
        if (op->op1_use >= 0) {
105
2.53k
          if (opline->opcode != ZEND_ASSIGN) {
106
1.55k
            union_find_unite(parent, size, op->op1_def, op->op1_use);
107
1.55k
          }
108
2.53k
        }
109
2.53k
        if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
110
831
          union_find_unite(parent, size, op->op1_def, op->op2_use);
111
831
        }
112
2.53k
      }
113
7.47k
      if (op->op2_def >= 0) {
114
94
        if (op->op2_use >= 0) {
115
94
          union_find_unite(parent, size, op->op2_def, op->op2_use);
116
94
        }
117
94
      }
118
7.47k
      if (op->result_def >= 0) {
119
6.63k
        if (op->result_use >= 0) {
120
302
          if (opline->opcode != ZEND_QM_ASSIGN) {
121
302
            union_find_unite(parent, size, op->result_def, op->result_use);
122
302
          }
123
302
        }
124
6.63k
        if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) {
125
170
          union_find_unite(parent, size, op->result_def, op->op1_use);
126
170
        }
127
6.63k
        if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
128
444
          union_find_unite(parent, size, op->result_def, op->op2_use);
129
444
        }
130
6.63k
        if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) {
131
502
          union_find_unite(parent, size, op->result_def, op->op1_def);
132
502
        }
133
6.63k
      }
134
7.47k
    }
135
10.1k
  }
136
137
11.5k
  for (i = 0; i < ssa_vars_count; i++) {
138
10.1k
    parent[i] = union_find_root(parent, i);
139
10.1k
  }
140
141
1.40k
  free_alloca(size, use_heap);
142
143
1.40k
  return SUCCESS;
144
1.40k
}
145
/* }}} */
146
147
static bool is_allocation_def(const zend_op_array *op_array, const zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
148
47.8k
{
149
47.8k
  const zend_ssa_op *ssa_op = ssa->ops + def;
150
47.8k
  zend_op *opline = op_array->opcodes + def;
151
152
47.8k
  if (ssa_op->result_def == var) {
153
44.1k
    switch (opline->opcode) {
154
1.61k
      case ZEND_INIT_ARRAY:
155
1.61k
        return true;
156
5.43k
      case ZEND_NEW: {
157
          /* objects with destructors should escape */
158
5.43k
        const zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
159
5.43k
          script, op_array, opline);
160
5.43k
        uint32_t forbidden_flags =
161
          /* These flags will always cause an exception */
162
5.43k
          ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
163
5.43k
          | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
164
5.43k
        if (ce
165
5.19k
         && !ce->parent
166
5.19k
         && !ce->create_object
167
66
         && ce->default_object_handlers->get_constructor == zend_std_get_constructor
168
66
         && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
169
66
         && !ce->constructor
170
66
         && !ce->destructor
171
66
         && !ce->__get
172
66
         && !ce->__set
173
66
         && !ce->__isset
174
66
         && !ce->num_hooked_props
175
66
         && !(ce->ce_flags & forbidden_flags)
176
60
         && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
177
60
          return true;
178
60
        }
179
5.37k
        break;
180
5.43k
      }
181
5.37k
      case ZEND_QM_ASSIGN:
182
460
        if (opline->op1_type == IS_CONST
183
0
         && Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) {
184
0
          return true;
185
0
        }
186
460
        if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
187
204
          return true;
188
204
        }
189
256
        break;
190
256
      case ZEND_ASSIGN:
191
106
        if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
192
80
          return true;
193
80
        }
194
26
        break;
195
44.1k
    }
196
44.1k
  } else if (ssa_op->op1_def == var) {
197
3.48k
    switch (opline->opcode) {
198
1.16k
      case ZEND_ASSIGN:
199
1.16k
        if (opline->op2_type == IS_CONST
200
455
         && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) {
201
158
          return true;
202
158
        }
203
1.00k
        if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
204
199
          return true;
205
199
        }
206
810
        break;
207
810
      case ZEND_ASSIGN_DIM:
208
534
        if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
209
          /* implicit object/array allocation */
210
534
          return true;
211
534
        }
212
0
        break;
213
3.48k
    }
214
3.48k
  }
215
216
45.0k
  return false;
217
47.8k
}
218
/* }}} */
219
220
static bool is_local_def(const zend_op_array *op_array, const zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
221
5.55k
{
222
5.55k
  const zend_ssa_op *op = ssa->ops + def;
223
5.55k
  const zend_op *opline = op_array->opcodes + def;
224
225
5.55k
  if (op->result_def == var) {
226
4.09k
    switch (opline->opcode) {
227
866
      case ZEND_INIT_ARRAY:
228
1.11k
      case ZEND_ADD_ARRAY_ELEMENT:
229
1.22k
      case ZEND_QM_ASSIGN:
230
1.30k
      case ZEND_ASSIGN:
231
1.30k
        return true;
232
48
      case ZEND_NEW: {
233
        /* objects with destructors should escape */
234
48
        const zend_class_entry *ce = zend_optimizer_get_class_entry_from_op1(
235
48
          script, op_array, opline);
236
48
        if (ce
237
40
         && !ce->create_object
238
32
         && ce->default_object_handlers->get_constructor == zend_std_get_constructor
239
32
         && ce->default_object_handlers->dtor_obj == zend_objects_destroy_object
240
32
         && !ce->constructor
241
32
         && !ce->destructor
242
32
         && !ce->__get
243
32
         && !ce->__set
244
32
         && !ce->__isset
245
32
         && !ce->num_hooked_props
246
32
         && !ce->parent) {
247
32
          return true;
248
32
        }
249
16
        break;
250
48
      }
251
4.09k
    }
252
4.09k
  } else if (op->op1_def == var) {
253
1.42k
    switch (opline->opcode) {
254
537
      case ZEND_ASSIGN:
255
883
      case ZEND_ASSIGN_DIM:
256
889
      case ZEND_ASSIGN_OBJ:
257
889
      case ZEND_ASSIGN_OBJ_REF:
258
1.00k
      case ZEND_ASSIGN_DIM_OP:
259
1.00k
      case ZEND_ASSIGN_OBJ_OP:
260
1.00k
      case ZEND_PRE_INC_OBJ:
261
1.00k
      case ZEND_PRE_DEC_OBJ:
262
1.00k
      case ZEND_POST_INC_OBJ:
263
1.00k
      case ZEND_POST_DEC_OBJ:
264
1.00k
        return true;
265
1.42k
    }
266
1.42k
  }
267
268
3.21k
  return false;
269
5.55k
}
270
/* }}} */
271
272
/* ArrayAccess methods receive the object, so dimension accesses on it let it escape. */
273
static bool may_be_array_access(const zend_ssa_var_info *info) /* {{{ */
274
386
{
275
386
  if (!(info->type & MAY_BE_OBJECT)) {
276
126
    return false;
277
126
  }
278
279
260
  const zend_class_entry *ce = info->ce;
280
260
  if (!ce || info->is_instanceof) {
281
260
    return true;
282
260
  }
283
0
  if (ce->ce_flags & ZEND_ACC_LINKED) {
284
0
    return ce->arrayaccess_funcs_ptr != NULL;
285
0
  }
286
  /* The interfaces of unlinked classes are not known yet. */
287
0
  return ce->num_interfaces || ce->parent_name;
288
0
}
289
/* }}} */
290
291
static bool is_escape_use(const zend_op_array *op_array, const zend_ssa *ssa, int use, int var) /* {{{ */
292
1.47k
{
293
1.47k
  const zend_ssa_op *ssa_op = ssa->ops + use;
294
1.47k
  const zend_op *opline = op_array->opcodes + use;
295
296
1.47k
  if (ssa_op->op1_use == var) {
297
914
    switch (opline->opcode) {
298
10
      case ZEND_ASSIGN:
299
        /* no_val */
300
10
        break;
301
66
      case ZEND_QM_ASSIGN:
302
66
        if (opline->op1_type == IS_CV) {
303
66
          if (OP1_INFO() & MAY_BE_OBJECT) {
304
            /* object aliasing */
305
66
            return true;
306
66
          }
307
66
        }
308
0
        break;
309
0
      case ZEND_ISSET_ISEMPTY_DIM_OBJ:
310
80
      case ZEND_FETCH_DIM_R:
311
186
      case ZEND_FETCH_DIM_IS:
312
204
      case ZEND_ASSIGN_DIM_OP:
313
386
      case ZEND_ASSIGN_DIM:
314
386
        if (may_be_array_access(&ssa->var_info[ssa_op->op1_use])) {
315
260
          return true;
316
260
        }
317
126
        break;
318
126
      case ZEND_ISSET_ISEMPTY_PROP_OBJ:
319
6
      case ZEND_FETCH_OBJ_R:
320
6
      case ZEND_FETCH_OBJ_IS:
321
6
        break;
322
0
      case ZEND_ASSIGN_OP:
323
0
        return true;
324
0
      case ZEND_ASSIGN_OBJ_OP:
325
0
      case ZEND_ASSIGN_STATIC_PROP_OP:
326
0
      case ZEND_ASSIGN_OBJ:
327
0
        break;
328
0
      case ZEND_ASSIGN_OBJ_REF:
329
        /* The property may now alias a variable outside of the object. */
330
0
        return true;
331
0
      case ZEND_PRE_INC_OBJ:
332
0
      case ZEND_PRE_DEC_OBJ:
333
0
      case ZEND_POST_INC_OBJ:
334
0
      case ZEND_POST_DEC_OBJ:
335
0
        break;
336
16
      case ZEND_INIT_ARRAY:
337
16
      case ZEND_ADD_ARRAY_ELEMENT:
338
16
        if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
339
0
          return true;
340
0
        }
341
16
        if (OP1_INFO() & MAY_BE_OBJECT) {
342
          /* object aliasing */
343
0
          return true;
344
0
        }
345
        /* reference dependencies processed separately */
346
16
        break;
347
16
      case ZEND_OP_DATA:
348
2
        if ((opline-1)->opcode != ZEND_ASSIGN_DIM
349
0
         && (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
350
0
          return true;
351
0
        }
352
2
        if (OP1_INFO() & MAY_BE_OBJECT) {
353
          /* object aliasing */
354
0
          return true;
355
0
        }
356
2
        opline--;
357
2
        ssa_op--;
358
2
        if (opline->op1_type != IS_CV
359
0
         || (OP1_INFO() & MAY_BE_REF)
360
2
         || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
361
          /* assignment into escaping structure */
362
2
          return true;
363
2
        }
364
        /* reference dependencies processed separately */
365
0
        break;
366
428
      default:
367
428
        return true;
368
914
    }
369
914
  }
370
371
719
  if (ssa_op->op2_use == var) {
372
319
    switch (opline->opcode) {
373
199
      case ZEND_ASSIGN:
374
199
        if (opline->op1_type != IS_CV
375
199
         || (OP1_INFO() & MAY_BE_REF)
376
199
         || (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
377
          /* assignment into escaping variable */
378
199
          return true;
379
199
        }
380
0
        if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
381
0
          if (OP2_INFO() & MAY_BE_OBJECT) {
382
            /* object aliasing */
383
0
            return true;
384
0
          }
385
0
        }
386
0
        break;
387
120
      default:
388
120
        return true;
389
319
    }
390
319
  }
391
392
400
  if (ssa_op->result_use == var) {
393
252
    switch (opline->opcode) {
394
0
      case ZEND_ASSIGN:
395
0
      case ZEND_QM_ASSIGN:
396
0
      case ZEND_INIT_ARRAY:
397
252
      case ZEND_ADD_ARRAY_ELEMENT:
398
252
        break;
399
0
      default:
400
0
        return true;
401
252
    }
402
252
  }
403
404
400
  return false;
405
400
}
406
/* }}} */
407
408
zend_result zend_ssa_escape_analysis(const zend_script *script, const zend_op_array *op_array, const zend_ssa *ssa) /* {{{ */
409
24.7k
{
410
24.7k
  zend_ssa_var *ssa_vars = ssa->vars;
411
24.7k
  int ssa_vars_count = ssa->vars_count;
412
24.7k
  int i, root, use;
413
24.7k
  int *ees;
414
24.7k
  bool has_allocations;
415
24.7k
  int num_non_escaped;
416
24.7k
  ALLOCA_FLAG(use_heap)
417
418
24.7k
  if (!ssa_vars) {
419
0
    return SUCCESS;
420
0
  }
421
422
24.7k
  has_allocations = false;
423
91.1k
  for (i = op_array->last_var; i < ssa_vars_count; i++) {
424
67.7k
    if (ssa_vars[i].definition >= 0
425
65.5k
      && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
426
46.2k
      && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
427
1.40k
      has_allocations = true;
428
1.40k
      break;
429
1.40k
    }
430
67.7k
  }
431
24.7k
  if (!has_allocations) {
432
23.3k
    return SUCCESS;
433
23.3k
  }
434
435
436
  /* 1. Build EES (Equi-Escape Sets) */
437
1.40k
  ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
438
1.40k
  if (!ees) {
439
0
    return FAILURE;
440
0
  }
441
442
1.40k
  if (zend_build_equi_escape_sets(ees, op_array, ssa) == FAILURE) {
443
0
    free_alloca(ees, use_heap);
444
0
    return FAILURE;
445
0
  }
446
447
  /* 2. Identify Allocations */
448
1.40k
  num_non_escaped = 0;
449
9.28k
  for (i = op_array->last_var; i < ssa_vars_count; i++) {
450
7.87k
    root = ees[i];
451
7.87k
    if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) {
452
      /* already escape. skip */
453
7.07k
    } else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) {
454
0
      if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
455
0
        num_non_escaped--;
456
0
      }
457
0
      ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
458
7.07k
    } else if (ssa_vars[i].definition >= 0
459
6.86k
       && (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
460
5.55k
      if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
461
3.21k
        if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
462
198
          num_non_escaped--;
463
198
        }
464
3.21k
        ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
465
3.21k
      } else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN
466
1.55k
       && is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
467
1.44k
        ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE;
468
1.44k
        num_non_escaped++;
469
1.44k
      }
470
5.55k
    }
471
7.87k
  }
472
473
  /* 3. Mark escaped EES */
474
1.40k
  if (num_non_escaped) {
475
5.52k
    for (i = 0; i < ssa_vars_count; i++) {
476
4.45k
      if (ssa_vars[i].use_chain >= 0) {
477
4.12k
        root = ees[i];
478
4.12k
        if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
479
2.92k
          FOREACH_USE(ssa_vars + i, use) {
480
2.92k
            if (is_escape_use(op_array, ssa, use, i)) {
481
1.07k
              ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
482
1.07k
              num_non_escaped--;
483
1.07k
              if (num_non_escaped == 0) {
484
923
                i = ssa_vars_count;
485
923
              }
486
1.07k
              break;
487
1.07k
            }
488
2.92k
          } FOREACH_USE_END();
489
1.45k
        }
490
4.12k
      }
491
4.45k
    }
492
1.07k
  }
493
494
  /* 4. Process referential dependencies */
495
1.40k
  if (num_non_escaped) {
496
156
    bool changed;
497
498
156
    do {
499
156
      changed = false;
500
1.24k
      for (i = 0; i < ssa_vars_count; i++) {
501
1.09k
        if (ssa_vars[i].use_chain >= 0) {
502
940
          root = ees[i];
503
940
          if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
504
308
            FOREACH_USE(ssa_vars + i, use) {
505
308
              const zend_ssa_op *op = ssa->ops + use;
506
308
              const zend_op *opline = op_array->opcodes + use;
507
308
              int enclosing_root;
508
509
308
              if (opline->opcode == ZEND_OP_DATA &&
510
0
                  ((opline-1)->opcode == ZEND_ASSIGN_DIM ||
511
0
                   (opline-1)->opcode == ZEND_ASSIGN_OBJ ||
512
0
                   (opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) &&
513
0
                  op->op1_use == i &&
514
0
                  (op-1)->op1_use >= 0) {
515
0
                enclosing_root = ees[(op-1)->op1_use];
516
166
              } else if ((opline->opcode == ZEND_INIT_ARRAY ||
517
150
                   opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
518
34
                  op->op1_use == i &&
519
16
                  op->result_def >= 0) {
520
16
                enclosing_root = ees[op->result_def];
521
150
              } else {
522
150
                continue;
523
150
              }
524
525
16
              if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN ||
526
16
                  ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) {
527
14
                  if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) {
528
0
                  ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
529
14
                  } else {
530
14
                  ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state;
531
14
                }
532
14
                if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
533
14
                  num_non_escaped--;
534
14
                  if (num_non_escaped == 0) {
535
14
                    changed = false;
536
14
                  } else {
537
0
                    changed = true;
538
0
                  }
539
14
                  break;
540
14
                } else {
541
0
                  changed = true;
542
0
                }
543
14
              }
544
16
            } FOREACH_USE_END();
545
142
          }
546
940
        }
547
1.09k
      }
548
156
    } while (changed);
549
156
  }
550
551
  /* 5. Propagate values of escape sets to variables */
552
11.5k
  for (i = 0; i < ssa_vars_count; i++) {
553
10.1k
    root = ees[i];
554
10.1k
    if (i != root) {
555
3.02k
      ssa_vars[i].escape_state = ssa_vars[root].escape_state;
556
3.02k
    }
557
10.1k
  }
558
559
1.40k
  free_alloca(ees, use_heap);
560
561
1.40k
  return SUCCESS;
562
1.40k
}
563
/* }}} */