Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/jit/ir/ir_gcm.c
Line
Count
Source
1
/*
2
 * IR - Lightweight JIT Compilation Framework
3
 * (GCM - Global Code Motion and Scheduler)
4
 * This file is part of the IR Project distributed under the MIT-style LICENSE.
5
 * Authors: Dmitry Stogov <dmitry@php.net>
6
 *
7
 * The GCM algorithm is based on Cliff Click's publication
8
 * See: C. Click. "Global code motion, global value numbering" Submitted to PLDI'95.
9
 */
10
11
#include "ir.h"
12
#include "ir_private.h"
13
14
1.05M
#define IR_GCM_IS_SCHEDULED_EARLY(b) (((int32_t)(b)) < 0)
15
662k
#define IR_GCM_EARLY_BLOCK(b)        ((uint32_t)-((int32_t)(b)))
16
17
#define IR_GCM_SPLIT 1
18
#define IR_SCHEDULE_SWAP_OPS 1
19
20
static uint32_t ir_gcm_schedule_early(ir_ctx *ctx, ir_ref ref, ir_list *queue_late)
21
331k
{
22
331k
  ir_ref n, *p, input;
23
331k
  ir_insn *insn;
24
331k
  uint32_t dom_depth;
25
331k
  uint32_t b, result;
26
27
331k
  insn = &ctx->ir_base[ref];
28
29
331k
  IR_ASSERT(insn->op != IR_PARAM && insn->op != IR_VAR);
30
331k
  IR_ASSERT(insn->op != IR_PHI && insn->op != IR_PI);
31
32
331k
  result = 1;
33
331k
  dom_depth = 0;
34
35
331k
  n = insn->inputs_count;
36
981k
  for (p = insn->ops + 1; n > 0; p++, n--) {
37
649k
    input = *p;
38
649k
    if (input > 0) {
39
352k
      b = ctx->cfg_map[input];
40
352k
      if (IR_GCM_IS_SCHEDULED_EARLY(b)) {
41
178
        b = IR_GCM_EARLY_BLOCK(b);
42
352k
      } else if (!b) {
43
31.1k
        b = ir_gcm_schedule_early(ctx, input, queue_late);
44
31.1k
      }
45
352k
      if (dom_depth < ctx->cfg_blocks[b].dom_depth) {
46
276k
        dom_depth = ctx->cfg_blocks[b].dom_depth;
47
276k
        result = b;
48
276k
      }
49
352k
    }
50
649k
  }
51
52
331k
  ctx->cfg_map[ref] = IR_GCM_EARLY_BLOCK(result);
53
331k
  ir_list_push_unchecked(queue_late, ref);
54
331k
  return result;
55
331k
}
56
57
/* Last Common Ancestor */
58
static uint32_t ir_gcm_find_lca(ir_ctx *ctx, uint32_t b1, uint32_t b2)
59
42.2k
{
60
42.2k
  uint32_t dom_depth;
61
62
42.2k
  dom_depth = ctx->cfg_blocks[b2].dom_depth;
63
45.2k
  while (ctx->cfg_blocks[b1].dom_depth > dom_depth) {
64
3.02k
    b1 = ctx->cfg_blocks[b1].dom_parent;
65
3.02k
  }
66
42.2k
  dom_depth = ctx->cfg_blocks[b1].dom_depth;
67
66.0k
  while (ctx->cfg_blocks[b2].dom_depth > dom_depth) {
68
23.7k
    b2 = ctx->cfg_blocks[b2].dom_parent;
69
23.7k
  }
70
44.5k
  while (b1 != b2) {
71
2.32k
    b1 = ctx->cfg_blocks[b1].dom_parent;
72
2.32k
    b2 = ctx->cfg_blocks[b2].dom_parent;
73
2.32k
  }
74
42.2k
  return b2;
75
42.2k
}
76
77
static uint32_t ir_gcm_select_best_block(ir_ctx *ctx, ir_ref ref, uint32_t lca)
78
74.1k
{
79
74.1k
  ir_block *bb = &ctx->cfg_blocks[lca];
80
74.1k
  uint32_t loop_depth = bb->loop_depth;
81
74.1k
  uint32_t flags, best, b;
82
83
74.1k
  if (!loop_depth) {
84
70.4k
    return lca;
85
70.4k
  }
86
87
#if 0 /* This is not necessary anymore. Conditions may be fused with IF across BBs. */
88
  if (ctx->ir_base[ref].op >= IR_EQ && ctx->ir_base[ref].op <= IR_UGT) {
89
    ir_use_list *use_list = &ctx->use_lists[ref];
90
91
    if (use_list->count == 1) {
92
      ir_ref use = ctx->use_edges[use_list->refs];
93
      ir_insn *insn = &ctx->ir_base[use];
94
      if (insn->op == IR_IF || insn->op == IR_GUARD || insn->op == IR_GUARD_NOT) {
95
        /* Don't hoist invariant comparison */
96
        return lca;
97
      }
98
    }
99
  }
100
#endif
101
102
3.71k
  flags = (bb->flags & IR_BB_LOOP_HEADER) ? bb->flags : ctx->cfg_blocks[bb->loop_header].flags;
103
3.71k
  if ((flags & IR_BB_LOOP_WITH_ENTRY)
104
58
   && !(ctx->binding && ir_binding_find(ctx, ref))) {
105
    /* Don't move loop invariant code across an OSR ENTRY if we can't restore it */
106
58
    return lca;
107
58
  }
108
109
3.66k
  best = b = lca;
110
3.91k
  do {
111
3.91k
    b = bb->dom_parent;
112
3.91k
    bb = &ctx->cfg_blocks[b];
113
3.91k
    if (bb->loop_depth < loop_depth) {
114
6
      if (!bb->loop_depth) {
115
4
#if 1
116
        /* Avoid LICM if LOOP doesn't have a pre-header block */
117
4
        ir_block *loop_bb = &ctx->cfg_blocks[best];
118
119
4
        if (!(loop_bb->flags & IR_BB_LOOP_HEADER)) {
120
4
          loop_bb = &ctx->cfg_blocks[loop_bb->loop_header];
121
4
        }
122
4
        if (loop_bb->predecessors_count > 2) {
123
0
          int n = loop_bb->predecessors_count;
124
0
          uint32_t *p = ctx->cfg_edges + loop_bb->predecessors;
125
126
0
          while (n && *p != b) {
127
0
            n--; p++;
128
0
          }
129
0
          if (!n) {
130
0
            break;
131
0
          }
132
0
        }
133
4
#endif
134
4
        best = b;
135
4
        break;
136
4
      }
137
2
      flags = (bb->flags & IR_BB_LOOP_HEADER) ? bb->flags : ctx->cfg_blocks[bb->loop_header].flags;
138
2
      if ((flags & IR_BB_LOOP_WITH_ENTRY)
139
0
       && !(ctx->binding && ir_binding_find(ctx, ref))) {
140
0
        break;
141
0
      }
142
2
      loop_depth = bb->loop_depth;
143
2
      best = b;
144
2
    }
145
3.91k
  } while (b != ctx->cfg_map[ref]);
146
147
3.66k
  return best;
148
3.71k
}
149
150
#if IR_GCM_SPLIT
151
/* Partially Dead Code Elimination through splitting the node and sinking the clones
152
 *
153
 * This code is based on the Benedikt Meurer's idea first implemented in V8.
154
 * See: https://codereview.chromium.org/899433005
155
 */
156
157
typedef struct _ir_gcm_split_data {
158
  ir_sparse_set totally_useful;
159
  ir_list       worklist;
160
} ir_gcm_split_data;
161
162
static void _push_predecessors(ir_ctx *ctx, ir_block *bb, ir_gcm_split_data *data)
163
6.57k
{
164
6.57k
  uint32_t *p, i, n = bb->predecessors_count;
165
166
6.57k
  IR_ASSERT(n > 0);
167
6.57k
  p = ctx->cfg_edges + bb->predecessors;
168
6.63k
  do {
169
6.63k
    i = *p;
170
6.63k
    if (!ir_sparse_set_in(&data->totally_useful, i)) {
171
6.32k
      ir_list_push(&data->worklist, i);
172
6.32k
    }
173
6.63k
    p++;
174
6.63k
    n--;
175
6.63k
  } while (n > 0);
176
6.57k
}
177
178
static bool _check_successors(ir_ctx *ctx, ir_block *bb, ir_gcm_split_data *data)
179
3.54k
{
180
3.54k
  uint32_t *p, i, n = bb->successors_count;
181
182
3.54k
  if (n <= 1) {
183
118
    IR_ASSERT(ir_sparse_set_in(&data->totally_useful, ctx->cfg_edges[bb->successors]));
184
118
    return 1;
185
118
  }
186
187
3.42k
  p = ctx->cfg_edges + bb->successors;
188
6.78k
  do {
189
6.78k
    i = *p;
190
6.78k
    if (!ir_sparse_set_in(&data->totally_useful, i)) {
191
323
      return 0;
192
323
    }
193
6.46k
    p++;
194
6.46k
    n--;
195
6.46k
  } while (n > 0);
196
197
3.10k
  return 1;
198
3.42k
}
199
200
static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b)
201
40.6k
{
202
40.6k
  ir_use_list *use_list;
203
40.6k
  ir_insn *insn;
204
40.6k
  ir_ref n, *p, use;
205
40.6k
  uint32_t i;
206
40.6k
  ir_gcm_split_data *data = ctx->data;
207
208
40.6k
  IR_ASSERT(b > 0 && b <= ctx->cfg_blocks_count);
209
210
  /* 1. Find a set of blocks where the node is TOTALLY_USEFUL (not PARTIALLY_DEAD)
211
   * 1.1. Collect the blocks where the node is really USED.
212
   */
213
40.6k
  ir_sparse_set_clear(&data->totally_useful);
214
215
40.6k
  use_list = &ctx->use_lists[ref];
216
40.6k
  n = use_list->count;
217
49.1k
  for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
218
47.0k
    use = *p;
219
47.0k
    i = ctx->cfg_map[use];
220
47.0k
    if (!i) {
221
0
      continue;
222
0
    }
223
47.0k
    insn = &ctx->ir_base[use];
224
47.0k
    if (insn->op == IR_PHI) {
225
4.65k
      ir_ref *p = insn->ops + 2; /* PHI data inputs */
226
4.65k
      ir_ref *q = ctx->ir_base[insn->op1].ops + 1; /* MERGE inputs */
227
4.65k
      ir_ref n = insn->inputs_count - 1;
228
229
13.9k
      for (;n > 0; p++, q++, n--) {
230
9.31k
        if (*p == ref) {
231
4.65k
          i = ctx->cfg_map[*q];
232
4.65k
          IR_ASSERT(i > 0 && i <= ctx->cfg_blocks_count);
233
4.65k
          if (!ir_sparse_set_in(&data->totally_useful, i)) {
234
4.65k
            if (i == b) return 0; /* node is totally-useful in the scheduled block */
235
4.65k
            ir_sparse_set_add(&data->totally_useful, i);
236
4.65k
          }
237
4.65k
        }
238
9.31k
      }
239
42.4k
    } else {
240
42.4k
      IR_ASSERT(i > 0 && i <= ctx->cfg_blocks_count);
241
42.4k
      if (!ir_sparse_set_in(&data->totally_useful, i)) {
242
42.3k
        if (i == b) return 0; /* node is totally-useful in the scheduled block */
243
3.78k
        ir_sparse_set_add(&data->totally_useful, i);
244
3.78k
      }
245
42.4k
    }
246
47.0k
  }
247
248
2.06k
#ifdef IR_DEBUG
249
2.06k
  if (ctx->flags & IR_DEBUG_GCM_SPLIT) {
250
0
    bool first = 1;
251
0
    fprintf(stderr, "*** Split partially dead node d_%d scheduled to BB%d\n", ref, b);
252
0
    IR_SPARSE_SET_FOREACH(&data->totally_useful, i) {
253
0
      if (first) {
254
0
        fprintf(stderr, "\td_%d is USED in [BB%d", ref, i);
255
0
        first = 0;
256
0
      } else {
257
0
        fprintf(stderr, ", BB%d", i);
258
0
      }
259
0
    } IR_SPARSE_SET_FOREACH_END();
260
0
    fprintf(stderr, "]\n");
261
0
  }
262
2.06k
#endif
263
264
  /* 1.2. Iteratively check the predecessors of already found TOTALLY_USEFUL blocks and
265
   *      add them into TOTALLY_USEFUL set if all of their successors are already there.
266
   */
267
7.48k
  IR_SPARSE_SET_FOREACH(&data->totally_useful, i) {
268
7.48k
    _push_predecessors(ctx, &ctx->cfg_blocks[i], data);
269
7.48k
  } IR_SPARSE_SET_FOREACH_END();
270
271
4.03k
  while (ir_list_len(&data->worklist)) {
272
4.03k
    i = ir_list_pop(&data->worklist);
273
4.03k
    if (!ir_sparse_set_in(&data->totally_useful, i)) {
274
3.54k
      ir_block *bb = &ctx->cfg_blocks[i];
275
276
3.54k
      if (_check_successors(ctx, bb, data)) {
277
3.22k
        if (i == b) {
278
          /* node is TOTALLY_USEFUL in the scheduled block */
279
2.06k
          ir_list_clear(&data->worklist);
280
2.06k
          return 0;
281
2.06k
        }
282
1.15k
        ir_sparse_set_add(&data->totally_useful, i);
283
1.15k
        _push_predecessors(ctx, bb, data);
284
1.15k
      }
285
3.54k
    }
286
4.03k
  }
287
288
1
  IR_ASSERT(!ir_sparse_set_in(&data->totally_useful, b));
289
290
1
#ifdef IR_DEBUG
291
1
  if (ctx->flags & IR_DEBUG_GCM_SPLIT) {
292
0
    bool first = 1;
293
0
    IR_SPARSE_SET_FOREACH(&data->totally_useful, i) {
294
0
      if (first) {
295
0
        fprintf(stderr, "\td_%d is TOTALLY_USEFUL in [BB%d", ref, i);
296
0
        first = 0;
297
0
      } else {
298
0
        fprintf(stderr, ", BB%d", i);
299
0
      }
300
0
    } IR_SPARSE_SET_FOREACH_END();
301
0
    fprintf(stderr, "]\n");
302
0
  }
303
1
#endif
304
305
  /* 2. Split the USEs into partitions */
306
1
  use_list = &ctx->use_lists[ref];
307
1
  ir_hashtab hash;
308
1
  uint32_t j, clone, clones_count = 0, uses_count = 0;
309
1
  struct {
310
1
    ir_ref   ref;
311
1
    uint32_t block;
312
1
    uint32_t lca;
313
1
    uint32_t use_count;
314
1
    uint32_t use;
315
1
  } *clones = ir_mem_malloc(sizeof(*clones) * use_list->count);
316
1
  struct {
317
1
    ir_ref   ref;
318
1
    uint32_t block;
319
1
    uint32_t next;
320
1
  } *uses = ir_mem_malloc(sizeof(*uses) * use_list->count);
321
322
1
  ir_hashtab_init(&hash, use_list->count);
323
1
  n = use_list->count;
324
4
  for (p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
325
3
    ir_ref src;
326
327
3
    use = *p;
328
3
    src = ctx->cfg_map[use];
329
3
    insn = &ctx->ir_base[use];
330
    /* dead PHIs (src == NULL) are not scheduled, so they go together with other unscheduled uses */
331
3
    if (src && insn->op == IR_PHI) {
332
0
      ir_ref *p = insn->ops + 2; /* PHI data inputs */
333
0
      ir_ref *q = ctx->ir_base[insn->op1].ops + 1; /* MERGE inputs */
334
0
      ir_ref n = insn->inputs_count - 1;
335
336
      /* PHIs must be processed once */
337
0
      if (ir_hashtab_find(&hash, -use) != (ir_ref)IR_INVALID_VAL) {
338
0
        continue;
339
0
      }
340
0
      ir_hashtab_add(&hash, -use, IR_NULL);
341
0
      for (;n > 0; p++, q++, n--) {
342
0
        if (*p == ref) {
343
0
          j = i = ctx->cfg_map[*q];
344
0
          while (ir_sparse_set_in(&data->totally_useful, ctx->cfg_blocks[j].idom)) {
345
0
            j = ctx->cfg_blocks[j].idom;
346
0
          }
347
0
          clone = ir_hashtab_find(&hash, j);
348
0
          if (clone == IR_INVALID_VAL) {
349
0
            clone = clones_count++;
350
0
            ir_hashtab_add(&hash, j, clone);
351
0
            clones[clone].block = j;
352
0
            clones[clone].lca = i;
353
0
            clones[clone].use_count = 0;
354
0
            clones[clone].use = (uint32_t)-1;
355
0
          } else {
356
0
            clones[clone].lca = ir_gcm_find_lca(ctx, clones[clone].lca, i);
357
0
          }
358
0
          uses[uses_count].ref = use;
359
0
          uses[uses_count].block = i;
360
0
          uses[uses_count].next = clones[clone].use;
361
0
          clones[clone].use_count++;
362
0
          clones[clone].use = uses_count++;
363
0
        }
364
0
      }
365
3
    } else {
366
3
      j = src;
367
3
      if (j) {
368
3
        IR_ASSERT(j > 0);
369
6
        while (ir_sparse_set_in(&data->totally_useful, ctx->cfg_blocks[j].idom)) {
370
3
          j = ctx->cfg_blocks[j].idom;
371
3
        }
372
3
      }
373
3
      clone = ir_hashtab_find(&hash, j);
374
3
      if (clone == IR_INVALID_VAL) {
375
2
        clone = clones_count++;
376
2
        ir_hashtab_add(&hash, j, clone);
377
2
        clones[clone].block = j;
378
2
        clones[clone].lca = src;
379
2
        clones[clone].use_count = 0;
380
2
        clones[clone].use = -1;
381
2
      } else {
382
1
        clones[clone].lca = ir_gcm_find_lca(ctx, clones[clone].lca, src);
383
1
      }
384
3
      uses[uses_count].ref = use;
385
3
      uses[uses_count].block = src;
386
3
      uses[uses_count].next = clones[clone].use;
387
3
      clones[clone].use_count++;
388
3
      clones[clone].use = uses_count++;
389
3
    }
390
3
  }
391
392
  /* Select best blocks to insert clones */
393
3
  for (i = 0; i < clones_count; i++) {
394
2
    uint32_t b0 = clones[i].block;
395
2
    uint32_t lca = clones[i].lca;
396
397
2
    if (b0 != lca) {
398
1
      ir_block *bb = &ctx->cfg_blocks[lca];
399
1
      uint32_t loop_depth = bb->loop_depth;
400
401
1
      if (loop_depth) {
402
0
        uint32_t b;
403
0
        uint32_t best;
404
405
0
        best = b = lca;
406
0
        do {
407
0
          b = bb->dom_parent;
408
0
          bb = &ctx->cfg_blocks[b];
409
0
          if (bb->loop_depth < loop_depth) {
410
0
            if (!bb->loop_depth) {
411
0
              best = b;
412
0
              break;
413
0
            }
414
0
            loop_depth = bb->loop_depth;
415
0
            best = b;
416
0
          }
417
0
        } while (b != b0);
418
0
        lca = best;
419
0
      }
420
1
      clones[i].block = lca;
421
1
    }
422
2
  }
423
424
  // TODO: instead of inserting clone into the block where the expressin is partially available,
425
  //       we should insert PHI and the actual clones into the block sources where it's not available
426
  //       (similar to SSAPRE)
427
428
1
#ifdef IR_DEBUG
429
1
  if (ctx->flags & IR_DEBUG_GCM_SPLIT) {
430
0
    for (i = 0; i < clones_count; i++) {
431
0
      uint32_t u = clones[i].use;
432
433
0
      fprintf(stderr, "\tCLONE #%d in BB%d USES(%d)=[d_%d/BB%d",
434
0
        i, clones[i].block, clones[i].use_count, uses[u].ref, uses[u].block);
435
0
      u = uses[u].next;
436
0
      while (u != (uint32_t)-1) {
437
0
        fprintf(stderr, ", d_%d/BB%d", uses[u].ref, uses[u].block);
438
0
        u = uses[u].next;
439
0
      }
440
0
      fprintf(stderr, "]\n");
441
0
    }
442
0
  }
443
1
#endif
444
445
  /* Create Clones */
446
1
  insn = &ctx->ir_base[ref];
447
1
  clones[0].ref = ref;
448
2
  for (i = 1; i < clones_count; i++) {
449
1
    clones[i].ref = clone = ir_emit(ctx, insn->optx, insn->op1, insn->op2, insn->op3);
450
1
    insn = &ctx->ir_base[ref];
451
    /* Depending on the flags in IR_OPS, these can be references or data. */
452
1
    if (insn->op1 > 0 && insn->inputs_count >= 1) ir_use_list_add(ctx, insn->op1, clone);
453
1
    if (insn->op2 > 0 && insn->inputs_count >= 2) ir_use_list_add(ctx, insn->op2, clone);
454
1
    if (insn->op3 > 0 && insn->inputs_count >= 3) ir_use_list_add(ctx, insn->op3, clone);
455
1
  }
456
457
  /* Reconstruct IR: Update DEF->USE lists, CFG mapping and etc */
458
1
  n = ctx->use_lists[ref].refs;
459
3
  for (i = 0; i < clones_count; i++) {
460
2
    clone = clones[i].ref;
461
2
    if (clones[i].block
462
2
     && clones[i].use_count == 1
463
1
     && ctx->cfg_blocks[clones[i].block].loop_depth >= ctx->cfg_blocks[uses[clones[i].use].block].loop_depth) {
464
      /* TOTALLY_USEFUL block may be a head of a diamond above the real usage.
465
       * Sink it down to the real usage block.
466
       * Clones with few uses will be sunk into the LCA block.
467
       */
468
1
      clones[i].block = uses[clones[i].use].block;
469
1
    }
470
2
    ctx->cfg_map[clone] = clones[i].block;
471
2
    ctx->use_lists[clone].count = clones[i].use_count;
472
2
    ctx->use_lists[clone].refs = n;
473
474
2
    uint32_t u = clones[i].use;
475
5
    while (u != (uint32_t)-1) {
476
3
      uint32_t src = uses[u].block;
477
3
      use = uses[u].ref;
478
3
      ctx->use_edges[n++] = use;
479
3
      u = uses[u].next;
480
3
      if (i > 0) {
481
        /* replace inputs */
482
1
        ir_insn *insn = &ctx->ir_base[use];
483
1
        ir_ref k, l = insn->inputs_count;
484
485
1
        if (src && insn->op == IR_PHI) {
486
0
          ir_insn *merge = &ctx->ir_base[insn->op1];
487
0
          for (k = 2; k <= l; k++) {
488
0
            j = ctx->cfg_map[ir_insn_op(merge, k - 1)];
489
0
            if (j == src) {
490
0
              IR_ASSERT(ir_insn_op(insn, k) == ref);
491
0
              if (j != clones[i].block) {
492
0
                uint32_t dom_depth = ctx->cfg_blocks[clones[i].block].dom_depth;
493
0
                while (ctx->cfg_blocks[j].dom_depth > dom_depth) {
494
0
                  j = ctx->cfg_blocks[j].dom_parent;
495
0
                }
496
0
                if (j != clones[i].block) {
497
0
                  continue;
498
0
                }
499
0
              }
500
0
              ir_insn_set_op(insn, k, clone);
501
0
              break;
502
0
            }
503
0
          }
504
1
        } else {
505
2
          for (k = 1; k <= l; k++) {
506
2
            if (ir_insn_op(insn, k) == ref) {
507
1
              ir_insn_set_op(insn, k, clone);
508
1
              break;
509
1
            }
510
2
          }
511
1
        }
512
1
      }
513
3
    }
514
2
  }
515
516
1
  ir_mem_free(uses);
517
1
  ir_mem_free(clones);
518
1
  ir_hashtab_free(&hash);
519
520
1
#ifdef IR_DEBUG
521
1
  if (ctx->flags & IR_DEBUG_GCM_SPLIT) {
522
0
    ir_check(ctx);
523
0
  }
524
1
#endif
525
526
1
  return 1;
527
1
}
528
#endif
529
530
#ifdef IR_DEBUG
531
static bool ir_gcm_dominates(ir_ctx *ctx, uint32_t b1, uint32_t b2)
532
331k
{
533
331k
  uint32_t b1_depth = ctx->cfg_blocks[b1].dom_depth;
534
331k
  const ir_block *bb2 = &ctx->cfg_blocks[b2];
535
536
479k
  while (bb2->dom_depth > b1_depth) {
537
148k
    b2 = bb2->dom_parent;
538
148k
    bb2 = &ctx->cfg_blocks[b2];
539
148k
  }
540
331k
  return b1 == b2;
541
331k
}
542
#endif
543
544
static void ir_gcm_schedule_late(ir_ctx *ctx, ir_ref ref, uint32_t b)
545
331k
{
546
331k
  ir_ref n, use;
547
331k
  uint32_t lca = 0;
548
549
331k
  IR_ASSERT(ctx->ir_base[ref].op != IR_PARAM && ctx->ir_base[ref].op != IR_VAR);
550
331k
  IR_ASSERT(ctx->ir_base[ref].op != IR_PHI && ctx->ir_base[ref].op != IR_PI);
551
552
331k
  IR_ASSERT(IR_GCM_IS_SCHEDULED_EARLY(b));
553
331k
  b = IR_GCM_EARLY_BLOCK(b);
554
331k
  ctx->cfg_map[ref] = b;
555
556
704k
  for (n = 0; n < ctx->use_lists[ref].count; n++) {
557
373k
    use = ctx->use_edges[ctx->use_lists[ref].refs + n];
558
373k
    b = ctx->cfg_map[use];
559
373k
    if (IR_GCM_IS_SCHEDULED_EARLY(b)) {
560
0
      ir_gcm_schedule_late(ctx, use, b);
561
0
      b = ctx->cfg_map[use];
562
0
      IR_ASSERT(b != 0);
563
373k
    } else if (!b) {
564
10
      continue;
565
373k
    } else if (ctx->ir_base[use].op == IR_PHI) {
566
19.5k
      ir_insn *insn = &ctx->ir_base[use];
567
19.5k
      ir_ref *p = insn->ops + 2; /* PHI data inputs */
568
19.5k
      ir_ref *q = ctx->ir_base[insn->op1].ops + 1; /* MERGE inputs */
569
19.5k
      ir_ref n = insn->inputs_count - 1;
570
571
59.0k
      for (;n > 0; p++, q++, n--) {
572
39.4k
        if (*p == ref) {
573
19.5k
          b = ctx->cfg_map[*q];
574
19.5k
          lca = !lca ? b : ir_gcm_find_lca(ctx, lca, b);
575
19.5k
        }
576
39.4k
      }
577
19.5k
      continue;
578
19.5k
    }
579
354k
    lca = !lca ? b : ir_gcm_find_lca(ctx, lca, b);
580
354k
  }
581
582
331k
  IR_ASSERT(lca != 0 && "No Common Ancestor");
583
331k
  IR_ASSERT(ir_gcm_dominates(ctx, ctx->cfg_map[ref], lca) && "Early placement doesn't dominate the late");
584
585
331k
#if IR_GCM_SPLIT
586
331k
  if (ctx->use_lists[ref].count > 1
587
40.6k
   && ir_split_partially_dead_node(ctx, ref, lca)) {
588
1
    return;
589
1
  }
590
331k
#endif
591
592
331k
  if (lca != ctx->cfg_map[ref]) {
593
74.1k
    b = ir_gcm_select_best_block(ctx, ref, lca);
594
595
74.1k
    ctx->cfg_map[ref] = b;
596
597
    /* OVERFLOW is a projection of ADD/SUB/MUL_OV and must be scheduled into the same block */
598
74.1k
    if (ctx->ir_base[ref].op >= IR_ADD_OV && ctx->ir_base[ref].op <= IR_MUL_OV) {
599
0
      ir_use_list *use_list = &ctx->use_lists[ref];
600
0
      ir_ref n, *p, use;
601
602
0
      for (n = use_list->count, p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
603
0
        use = *p;
604
0
        if (ctx->ir_base[use].op == IR_OVERFLOW) {
605
0
          ctx->cfg_map[use] = b;
606
0
          break;
607
0
        }
608
0
      }
609
0
    }
610
74.1k
  }
611
331k
}
612
613
int ir_gcm(ir_ctx *ctx)
614
9.93k
{
615
9.93k
  ir_ref k, n, *p, ref;
616
9.93k
  ir_block *bb;
617
9.93k
  ir_list queue_early;
618
9.93k
  ir_list queue_late;
619
9.93k
  uint32_t *_blocks, b;
620
9.93k
  ir_insn *insn, *use_insn;
621
9.93k
  ir_use_list *use_list;
622
623
9.93k
  IR_ASSERT(ctx->cfg_map);
624
9.93k
  _blocks = ctx->cfg_map;
625
626
9.93k
  ir_list_init(&queue_early, ctx->insns_count);
627
628
9.93k
  if (ctx->cfg_blocks_count == 1) {
629
2.98k
    ref = ctx->cfg_blocks[1].end;
630
53.6k
    do {
631
53.6k
      insn = &ctx->ir_base[ref];
632
53.6k
      _blocks[ref] = 1; /* pin to block */
633
53.6k
      if (insn->inputs_count > 1) {
634
        /* insn has input data edges */
635
33.9k
        ir_list_push_unchecked(&queue_early, ref);
636
33.9k
      }
637
53.6k
      ref = insn->op1; /* control predecessor */
638
53.6k
    } while (ref != 1); /* IR_START */
639
2.98k
    _blocks[1] = 1; /* pin to block */
640
641
2.98k
    use_list = &ctx->use_lists[1];
642
2.98k
    n = use_list->count;
643
5.97k
    for (p = &ctx->use_edges[use_list->refs]; n > 0; n--, p++) {
644
2.99k
      ref = *p;
645
2.99k
      use_insn = &ctx->ir_base[ref];
646
2.99k
      if (use_insn->op == IR_PARAM || use_insn->op == IR_VAR) {
647
4
        ctx->cfg_blocks[1].flags |= (use_insn->op == IR_PARAM) ? IR_BB_HAS_PARAM : IR_BB_HAS_VAR;
648
4
        _blocks[ref] = 1; /* pin to block */
649
4
      }
650
2.99k
    }
651
652
    /* Place all live nodes to the first block */
653
42.6k
    while (ir_list_len(&queue_early)) {
654
39.6k
      ref = ir_list_pop(&queue_early);
655
39.6k
      insn = &ctx->ir_base[ref];
656
39.6k
      n = insn->inputs_count;
657
140k
      for (p = insn->ops + 1; n > 0; p++, n--) {
658
100k
        ref = *p;
659
100k
        if (ref > 0 && _blocks[ref] == 0) {
660
5.69k
          _blocks[ref] = 1;
661
5.69k
          ir_list_push_unchecked(&queue_early, ref);
662
5.69k
        }
663
100k
      }
664
39.6k
    }
665
666
2.98k
    ir_list_free(&queue_early);
667
668
2.98k
    return 1;
669
2.98k
  }
670
671
6.95k
  ir_list_init(&queue_late, ctx->insns_count);
672
673
  /* pin and collect control and control depended (PARAM, VAR, PHI, PI) instructions */
674
6.95k
  b = ctx->cfg_blocks_count;
675
320k
  for (bb = ctx->cfg_blocks + b; b > 0; bb--, b--) {
676
313k
    IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
677
313k
    ref = bb->end;
678
679
    /* process the last instruction of the block */
680
313k
    insn = &ctx->ir_base[ref];
681
313k
    _blocks[ref] = b; /* pin to block */
682
313k
    if (insn->inputs_count > 1) {
683
      /* insn has input data edges */
684
118k
      ir_list_push_unchecked(&queue_early, ref);
685
118k
    }
686
313k
    ref = insn->op1; /* control predecessor */
687
688
1.01M
    while (ref != bb->start) {
689
704k
      insn = &ctx->ir_base[ref];
690
704k
      _blocks[ref] = b; /* pin to block */
691
704k
      if (insn->inputs_count > 1) {
692
        /* insn has input data edges */
693
518k
        ir_list_push_unchecked(&queue_early, ref);
694
518k
      }
695
704k
      if (insn->type != IR_VOID) {
696
428k
        IR_ASSERT(ir_op_flags[insn->op] & IR_OP_FLAG_MEM);
697
428k
      }
698
704k
      ref = insn->op1; /* control predecessor */
699
704k
    }
700
701
    /* process the first instruction of the block */
702
313k
    _blocks[ref] = b; /* pin to block */
703
704
313k
    use_list = &ctx->use_lists[ref];
705
313k
    n = use_list->count;
706
313k
    if (n > 1) {
707
67.0k
      for (p = &ctx->use_edges[use_list->refs]; n > 0; n--, p++) {
708
46.0k
        ref = *p;
709
46.0k
        use_insn = &ctx->ir_base[ref];
710
46.0k
        if (use_insn->op == IR_PHI || use_insn->op == IR_PI) {
711
24.6k
          bb->flags |= (use_insn->op == IR_PHI) ? IR_BB_HAS_PHI : IR_BB_HAS_PI;
712
24.6k
          if (EXPECTED(ctx->use_lists[ref].count != 0)) {
713
24.6k
            _blocks[ref] = b; /* pin to block */
714
24.6k
            ir_list_push_unchecked(&queue_early, ref);
715
24.6k
          }
716
24.6k
        } else if (use_insn->op == IR_PARAM) {
717
40
          bb->flags |= IR_BB_HAS_PARAM;
718
40
          _blocks[ref] = b; /* pin to block */
719
21.3k
        } else if (use_insn->op == IR_VAR) {
720
0
          bb->flags |= IR_BB_HAS_VAR;
721
0
          _blocks[ref] = b; /* pin to block */
722
0
        }
723
46.0k
      }
724
21.0k
    }
725
313k
  }
726
727
6.95k
  n = ir_list_len(&queue_early);
728
668k
  while (n > 0) {
729
661k
    n--;
730
661k
    ref = ir_list_at(&queue_early, n);
731
661k
    insn = &ctx->ir_base[ref];
732
661k
    k = insn->inputs_count - 1;
733
1.64M
    for (p = insn->ops + 2; k > 0; p++, k--) {
734
979k
      ref = *p;
735
979k
      if (ref > 0 && _blocks[ref] == 0) {
736
300k
        ir_gcm_schedule_early(ctx, ref, &queue_late);
737
300k
      }
738
979k
    }
739
661k
  }
740
741
6.95k
#ifdef IR_DEBUG
742
6.95k
  if (ctx->flags & IR_DEBUG_GCM) {
743
0
    fprintf(stderr, "GCM Schedule Early\n");
744
0
    for (n = 1; n < ctx->insns_count; n++) {
745
0
      fprintf(stderr, "%d -> %d\n", n, ctx->cfg_map[n]);
746
0
    }
747
0
  }
748
6.95k
#endif
749
750
6.95k
#if IR_GCM_SPLIT
751
6.95k
  ir_gcm_split_data data;
752
753
6.95k
  ir_sparse_set_init(&data.totally_useful, ctx->cfg_blocks_count + 1);
754
6.95k
  ir_list_init(&data.worklist, ctx->cfg_blocks_count + 1);
755
6.95k
  ctx->data = &data;
756
6.95k
#endif
757
758
6.95k
  n = ir_list_len(&queue_late);
759
338k
  while (n > 0) {
760
331k
    n--;
761
331k
    ref = ir_list_at(&queue_late, n);
762
331k
    b = ctx->cfg_map[ref];
763
331k
    if (IR_GCM_IS_SCHEDULED_EARLY(b)) {
764
331k
      ir_gcm_schedule_late(ctx, ref, b);
765
331k
    }
766
331k
  }
767
768
6.95k
#if IR_GCM_SPLIT
769
6.95k
  ir_list_free(&data.worklist);
770
6.95k
  ir_sparse_set_free(&data.totally_useful);
771
6.95k
  ctx->data = NULL;
772
6.95k
#endif
773
774
6.95k
  ir_list_free(&queue_early);
775
6.95k
  ir_list_free(&queue_late);
776
777
6.95k
#ifdef IR_DEBUG
778
6.95k
  if (ctx->flags & IR_DEBUG_GCM) {
779
0
    fprintf(stderr, "GCM Schedule Late\n");
780
0
    for (n = 1; n < ctx->insns_count; n++) {
781
0
      fprintf(stderr, "%d -> %d\n", n, ctx->cfg_map[n]);
782
0
    }
783
0
  }
784
6.95k
#endif
785
786
6.95k
  return 1;
787
6.95k
}
788
789
static void ir_xlat_binding(ir_ctx *ctx, ir_ref *_xlat)
790
3
{
791
3
  uint32_t n1, n2, pos;
792
3
  ir_ref key;
793
3
  ir_hashtab_bucket *b1, *b2;
794
3
  ir_hashtab *binding = ctx->binding;
795
3
  uint32_t hash_size = (uint32_t)(-(int32_t)binding->mask);
796
797
3
  memset((char*)binding->data - (hash_size * sizeof(uint32_t)), -1, hash_size * sizeof(uint32_t));
798
3
  n1 = binding->count;
799
3
  n2 = 0;
800
3
  pos = 0;
801
3
  b1 = binding->data;
802
3
  b2 = binding->data;
803
11
  while (n1 > 0) {
804
8
    key = b1->key;
805
8
    IR_ASSERT(key < ctx->insns_count);
806
8
    if (_xlat[key]) {
807
8
      key = _xlat[key];
808
8
      b2->key = key;
809
8
      if (b1->val > 0) {
810
0
        IR_ASSERT(_xlat[b1->val]);
811
0
        b2->val = _xlat[b1->val];
812
8
      } else {
813
8
        b2->val = b1->val;
814
8
      }
815
8
      key |= binding->mask;
816
8
      b2->next = ((uint32_t*)binding->data)[key];
817
8
      ((uint32_t*)binding->data)[key] = pos;
818
8
      pos += sizeof(ir_hashtab_bucket);
819
8
      b2++;
820
8
      n2++;
821
8
    }
822
8
    b1++;
823
8
    n1--;
824
8
  }
825
3
  binding->count = n2;
826
3
}
827
828
IR_ALWAYS_INLINE ir_ref ir_count_constant(const ir_ctx *ctx, ir_ref *_xlat, ir_ref ref)
829
323k
{
830
323k
  if (!_xlat[ref]) {
831
321k
    _xlat[ref] = ref; /* this is only a "used constant" marker */
832
321k
    if (ctx->ir_base[ref].op == IR_LONG_CONST) {
833
0
        ir_ref i, n = IR_ALIGNED_SIZE(ctx->ir_base[ref].long_const_size, sizeof(ir_insn)) / sizeof(ir_insn);
834
835
0
      for (i = 1; i <= n; i++) {
836
0
        _xlat[ref + i] = ref + i; /* this is only a "used constant" marker */
837
0
      }
838
0
      return n + 1;
839
0
    }
840
321k
    return 1;
841
321k
  }
842
2.62k
  return 0;
843
323k
}
844
845
IR_ALWAYS_INLINE bool ir_is_good_bb_order(ir_ctx *ctx, uint32_t b, ir_block *bb, ir_ref start)
846
306k
{
847
306k
  ir_insn *insn = &ctx->ir_base[start];
848
306k
  uint32_t n = insn->inputs_count;
849
306k
  ir_ref *p = insn->ops + 1;
850
851
306k
  if (n == 1) {
852
231k
    return ctx->cfg_map[*p] < b;
853
231k
  } else {
854
75.5k
    IR_ASSERT(n > 1);
855
260k
    for (; n > 0; p++, n--) {
856
185k
      ir_ref input = *p;
857
858
185k
      if (!IR_IS_CONST_REF(input)) {
859
185k
        uint32_t input_b = ctx->cfg_map[input];
860
861
185k
        if (input_b < b) {
862
          /* ordered */
863
182k
        } else if ((bb->flags & IR_BB_LOOP_HEADER)
864
3.21k
          && (input_b == b || ctx->cfg_blocks[input_b].loop_header == b)) {
865
          /* back-edge of reducible loop */
866
3.21k
        } else if (UNEXPECTED(bb->flags & IR_BB_IRREDUCIBLE_LOOP)
867
0
          && (ctx->cfg_blocks[input_b].loop_header == bb->loop_header)) {
868
          /* closing edge of irreducible loop */
869
0
        } else {
870
0
          return 0;
871
0
        }
872
185k
      }
873
185k
    }
874
75.5k
    return 1;
875
75.5k
  }
876
306k
}
877
878
static bool ir_belongs_to_loop(ir_ctx *ctx, uint32_t loop_header, uint32_t b)
879
0
{
880
0
  uint32_t loop_depth = ctx->cfg_blocks[loop_header].loop_depth;
881
0
  ir_block *bb = &ctx->cfg_blocks[b];
882
883
0
  if (bb->loop_depth < loop_depth) {
884
0
    return 0;
885
0
  } else if (bb->loop_depth == loop_depth) {
886
0
    if (bb->flags & IR_BB_LOOP_HEADER) {
887
0
      return b == loop_header;
888
0
    } else {
889
0
      return bb->loop_header == loop_header;
890
0
    }
891
0
  } else {
892
0
    while (bb->loop_depth > loop_depth) {
893
0
      b = bb->loop_header;
894
0
      bb = &ctx->cfg_blocks[b];
895
0
    }
896
0
    return bb->loop_header == loop_header;
897
0
  }
898
0
}
899
900
static bool ir_is_irreducable_loop_side_entry(ir_ctx *ctx, ir_block *entry, uint32_t from)
901
0
{
902
0
  if (entry->flags & IR_BB_LOOP_HEADER) {
903
0
    return 0;
904
0
  } else {
905
0
    return !ir_belongs_to_loop(ctx, entry->loop_header, from);
906
0
  }
907
0
}
908
909
static IR_NEVER_INLINE void ir_fix_bb_order(ir_ctx *ctx, ir_ref *_prev, ir_ref *_next)
910
0
{
911
0
  uint32_t b, succ, count, *q, *xlat;
912
0
  ir_block *bb;
913
0
  ir_ref ref, n, prev;
914
0
  ir_worklist worklist;
915
0
  ir_block *new_blocks;
916
917
#if 0
918
  for (b = 1, bb = ctx->cfg_blocks + 1; b <= ctx->cfg_blocks_count; b++, bb++) {
919
    if (!ir_is_good_bb_order(ctx, b, bb, bb->start)) {
920
      goto fix;
921
    }
922
  }
923
  return;
924
925
fix:
926
#endif
927
0
  count = ctx->cfg_blocks_count + 1;
928
0
  new_blocks = ir_mem_malloc(count * sizeof(ir_block));
929
0
  xlat = ir_mem_malloc(count * sizeof(uint32_t));
930
0
  ir_worklist_init(&worklist, count);
931
0
  ir_worklist_push(&worklist, 1);
932
  /* Schedule blocks bottom-up. Place block only after all its successurs (except back-edges) are placed. */
933
0
  while (ir_worklist_len(&worklist) != 0) {
934
0
next:
935
0
    b = ir_worklist_peek(&worklist);
936
0
    bb = &ctx->cfg_blocks[b];
937
0
    n = bb->successors_count;
938
0
    if (n == 1) {
939
0
      succ = ctx->cfg_edges[bb->successors];
940
0
      if (ir_bitset_in(worklist.visited, succ)) {
941
        /* already processed */
942
0
      } else if (UNEXPECTED(ctx->cfg_blocks[succ].flags & IR_BB_IRREDUCIBLE_ENTRY)
943
0
          && ir_is_irreducable_loop_side_entry(ctx, &ctx->cfg_blocks[succ], b)) {
944
        /* "side" entry of irreducible loop (ignore) */
945
0
      } else if (ir_worklist_push(&worklist, succ)) {
946
0
        goto next;
947
0
      }
948
0
    } else if (n > 1) {
949
0
      uint32_t best = 0;
950
0
      uint32_t best_loop_depth = 0;
951
952
0
      q = ctx->cfg_edges + bb->successors + n;
953
0
      do {
954
0
        q--;
955
0
        succ = *q;
956
0
        if (ir_bitset_in(worklist.visited, succ)) {
957
          /* already processed */
958
0
        } else if (UNEXPECTED(ctx->cfg_blocks[succ].flags & IR_BB_IRREDUCIBLE_ENTRY)
959
0
            && ir_is_irreducable_loop_side_entry(ctx, &ctx->cfg_blocks[succ], b)) {
960
          /* "side" entry of irreducible loop (ignore) */
961
0
        } else if (!best) {
962
0
          best = succ;
963
0
          best_loop_depth = ctx->cfg_blocks[best].loop_depth;
964
0
        } else if (ctx->cfg_blocks[succ].loop_depth < best_loop_depth) {
965
          /* prefer deeper loop */
966
0
          best = succ;
967
0
          best_loop_depth = ctx->cfg_blocks[best].loop_depth;
968
0
        }
969
0
        n--;
970
0
      } while (n > 0);
971
0
      if (best) {
972
0
        ir_worklist_push(&worklist, best);
973
0
        goto next;
974
0
      }
975
0
    }
976
977
    /* All successors of "b" are placed. Now we can place "b" itself. */
978
0
    ir_worklist_pop(&worklist);
979
0
    count--;
980
0
    new_blocks[count] = *bb;
981
0
    xlat[b] = count;
982
0
  }
983
0
  IR_ASSERT(count == 1);
984
0
  xlat[0] = 0;
985
0
  ir_worklist_free(&worklist);
986
987
0
  prev = 0;
988
0
  for (b = 1, bb = new_blocks + 1; b <= ctx->cfg_blocks_count; b++, bb++) {
989
0
    bb->idom = xlat[bb->idom];
990
0
    bb->loop_header = xlat[bb->loop_header];
991
0
    n = bb->successors_count;
992
0
    if (n > 0) {
993
0
      for (q = ctx->cfg_edges + bb->successors; n > 0; q++, n--) {
994
0
        *q = xlat[*q];
995
0
      }
996
0
    }
997
0
    n = bb->predecessors_count;
998
0
    if (n > 0) {
999
0
      for (q = ctx->cfg_edges + bb->predecessors; n > 0; q++, n--) {
1000
0
        *q = xlat[*q];
1001
0
      }
1002
0
    }
1003
0
    _next[prev] = bb->start;
1004
0
    _prev[bb->start] = prev;
1005
0
    prev = bb->end;
1006
0
  }
1007
0
  _next[0] = 0;
1008
0
  _next[prev] = 0;
1009
1010
0
  for (ref = 2; ref < ctx->insns_count; ref++) {
1011
0
    ctx->cfg_map[ref] = xlat[ctx->cfg_map[ref]];
1012
0
  }
1013
0
  ir_mem_free(xlat);
1014
1015
0
  ir_mem_free(ctx->cfg_blocks);
1016
0
  ctx->cfg_blocks = new_blocks;
1017
0
}
1018
1019
#if IR_DEBUG
1020
static void ir_schedule_print_list(const ir_ctx *ctx, uint32_t b, const ir_ref *_next,
1021
                                   ir_ref start, ir_ref end, const char *label)
1022
0
{
1023
0
  ir_ref ref;
1024
1025
0
  fprintf(stderr, "  %s [%d", label, start);
1026
0
  ref = _next[start];
1027
0
  while (ref != end) {
1028
0
    fprintf(stderr, ",%d", ref);
1029
0
    ref = _next[ref];
1030
0
  }
1031
0
  fprintf(stderr, ",%d]\n", ref);
1032
0
}
1033
#endif
1034
1035
/* Simple Stable Topological Sort */
1036
static void ir_schedule_topsort(const ir_ctx *ctx, uint32_t b, const ir_block *bb,
1037
                                ir_ref *_xlat, ir_ref *_next, ir_ref *_prev,
1038
                                ir_ref ref, ir_ref end,
1039
                                ir_ref *insns_count, ir_ref *consts_count)
1040
223k
{
1041
223k
  ir_ref i = ref;
1042
223k
  const ir_insn *insn;
1043
1044
223k
  if (bb->successors_count > 1) {
1045
113k
    ir_ref input, j = bb->end;
1046
113k
    ir_insn *end = &ctx->ir_base[j];
1047
1048
113k
    if (end->op == IR_IF) {
1049
      /* Move condition closer to IF */
1050
113k
      input = end->op2;
1051
113k
      if (input > 0
1052
113k
       && ctx->cfg_map[input] == b
1053
113k
       && !_xlat[input]
1054
113k
       && _prev[j] != input
1055
18.2k
       && (!(ir_op_flags[ctx->ir_base[input].op] & IR_OP_FLAG_CONTROL) || end->op1 == input)) {
1056
18.0k
        if (input == i) {
1057
2
          i = _next[i];
1058
2
          insn = &ctx->ir_base[i];
1059
2
        }
1060
        /* remove "input" */
1061
18.0k
        _prev[_next[input]] = _prev[input];
1062
18.0k
        _next[_prev[input]] = _next[input];
1063
        /* insert before "j" */
1064
18.0k
        _prev[input] = _prev[j];
1065
18.0k
        _next[input] = j;
1066
18.0k
        _next[_prev[j]] = input;
1067
18.0k
        _prev[j] = input;
1068
18.0k
      }
1069
113k
    }
1070
113k
  }
1071
1072
1.31M
  while (i != end) {
1073
1.09M
    ir_ref n, j, input;
1074
1.09M
    const ir_ref *p;
1075
1076
1.11M
restart:
1077
1.11M
    IR_ASSERT(ctx->cfg_map[i] == b);
1078
1.11M
    insn = &ctx->ir_base[i];
1079
1.11M
    n = insn->inputs_count;
1080
3.42M
    for (j = n, p = insn->ops + 1; j > 0; p++, j--) {
1081
2.33M
      input = *p;
1082
2.33M
      if (!_xlat[input]) {
1083
        /* input is not scheduled yet */
1084
334k
        if (input > 0) {
1085
17.6k
          if (ctx->cfg_map[input] == b) {
1086
            /* "input" should be before "i" to satisfy dependency */
1087
17.6k
#ifdef IR_DEBUG
1088
17.6k
            if (ctx->flags & IR_DEBUG_SCHEDULE) {
1089
0
              fprintf(stderr, "Wrong dependency %d:%d -> %d\n", b, input, i);
1090
0
            }
1091
17.6k
#endif
1092
            /* remove "input" */
1093
17.6k
            _prev[_next[input]] = _prev[input];
1094
17.6k
            _next[_prev[input]] = _next[input];
1095
            /* insert before "i" */
1096
17.6k
            _prev[input] = _prev[i];
1097
17.6k
            _next[input] = i;
1098
17.6k
            _next[_prev[i]] = input;
1099
17.6k
            _prev[i] = input;
1100
            /* restart from "input" */
1101
17.6k
            i = input;
1102
17.6k
            goto restart;
1103
17.6k
          }
1104
316k
        } else if (input < IR_TRUE) {
1105
315k
          *consts_count += ir_count_constant(ctx, _xlat, input);
1106
315k
        }
1107
334k
      }
1108
2.33M
    }
1109
1110
1.09M
    _xlat[i] = *insns_count;
1111
1.09M
    *insns_count += ir_insn_inputs_to_len(n);
1112
1.09M
    IR_ASSERT(_next[i] != IR_UNUSED);
1113
1.09M
    i = _next[i];
1114
1.09M
  }
1115
223k
}
1116
1117
int ir_schedule(ir_ctx *ctx)
1118
9.93k
{
1119
9.93k
  ir_ref i, j, k, n, *p, *q, ref, new_ref, prev_ref, insns_count, consts_count, use_edges_count;
1120
9.93k
  ir_ref *_xlat;
1121
9.93k
  ir_ref *edges;
1122
9.93k
  ir_ref prev_b_end;
1123
9.93k
  uint32_t b;
1124
9.93k
  ir_ref *_next = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref));
1125
9.93k
  ir_ref *_prev = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref));
1126
9.93k
  ir_block *bb;
1127
9.93k
  ir_insn *insn, *new_insn, *base;
1128
9.93k
  ir_use_list *lists, *use_list, *new_list;
1129
9.93k
  bool bad_bb_order = 0;
1130
1131
  /* Create a double-linked list of nodes ordered by BB, respecting BB->start and BB->end */
1132
9.93k
  IR_ASSERT(ctx->cfg_map[1] == 1);
1133
1134
  /* link BB boundaries */
1135
9.93k
  _prev[1] = 0;
1136
9.93k
  prev_b_end = ctx->cfg_blocks[1].end;
1137
9.93k
  _next[1] = prev_b_end;
1138
9.93k
  _prev[prev_b_end] = 1;
1139
316k
  for (b = 2, bb = ctx->cfg_blocks + 2; b <= ctx->cfg_blocks_count; b++, bb++) {
1140
306k
    ir_ref start = bb->start;
1141
306k
    ir_ref end = bb->end;
1142
306k
    _next[prev_b_end] = start;
1143
306k
    _prev[start] = prev_b_end;
1144
306k
    _next[start] = end;
1145
306k
    _prev[end] = start;
1146
306k
    prev_b_end = end;
1147
306k
    if (!ir_is_good_bb_order(ctx, b, bb, start)) {
1148
0
      bad_bb_order = 1;
1149
0
    }
1150
306k
  }
1151
9.93k
  _next[prev_b_end] = 0;
1152
1153
  /* insert intermediate BB nodes */
1154
9.93k
  use_edges_count = ctx->use_lists[1].count;
1155
1.80M
  for (i = 2, use_list = &ctx->use_lists[i]; i < ctx->insns_count; use_list++, i++) {
1156
1.79M
    b = ctx->cfg_map[i];
1157
1.79M
    if (!b) continue;
1158
1.74M
    use_edges_count += use_list->count;
1159
1.74M
    bb = &ctx->cfg_blocks[b];
1160
1.74M
    if (i != bb->start && i != bb->end) {
1161
      /* insert before "end" */
1162
1.11M
      ir_ref next = bb->end;
1163
1.11M
      ir_ref prev = _prev[next];
1164
1.11M
      _prev[i] = prev;
1165
1.11M
      _next[i] = next;
1166
1.11M
      _next[prev] = i;
1167
1.11M
      _prev[next] = i;
1168
1.11M
    }
1169
1.74M
  }
1170
1171
9.93k
  if (bad_bb_order) {
1172
0
    ir_fix_bb_order(ctx, _prev, _next);
1173
0
  }
1174
1175
9.93k
  _xlat = ir_mem_calloc((ctx->consts_count + ctx->insns_count), sizeof(ir_ref));
1176
9.93k
  _xlat += ctx->consts_count;
1177
9.93k
  _xlat[IR_TRUE] = IR_TRUE;
1178
9.93k
  _xlat[IR_FALSE] = IR_FALSE;
1179
9.93k
  _xlat[IR_NULL] = IR_NULL;
1180
9.93k
  _xlat[IR_UNUSED] = IR_UNUSED;
1181
9.93k
  insns_count = 1;
1182
9.93k
  consts_count = -(IR_TRUE - 1);
1183
1184
  /* Schedule instructions inside each BB (now just topological sort according to dependencies) */
1185
326k
  for (b = 1, bb = ctx->cfg_blocks + 1; b <= ctx->cfg_blocks_count; b++, bb++) {
1186
316k
    ir_ref start;
1187
1188
316k
#ifdef IR_DEBUG
1189
316k
    if (ctx->flags & IR_DEBUG_SCHEDULE) {
1190
0
      fprintf(stderr, "BB%d\n", b);
1191
0
      ir_schedule_print_list(ctx, b, _next, bb->start, bb->end, "INITIAL");
1192
0
    }
1193
316k
#endif
1194
1195
316k
    IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
1196
    /* Schedule BB start */
1197
316k
    start = i = bb->start;
1198
316k
    _xlat[i] = bb->start = insns_count;
1199
316k
    insn = &ctx->ir_base[i];
1200
316k
    if (insn->op == IR_BEGIN) {
1201
8
      if (insn->op2) {
1202
4
        consts_count += ir_count_constant(ctx, _xlat, insn->op2);
1203
4
      }
1204
316k
    } else if (insn->op == IR_CASE_VAL) {
1205
8
      if (insn->op2 < IR_TRUE) {
1206
8
        consts_count += ir_count_constant(ctx, _xlat, insn->op2);
1207
8
      }
1208
316k
    } else if (insn->op == IR_CASE_RANGE) {
1209
0
      if (insn->op2 < IR_TRUE) {
1210
0
        consts_count += ir_count_constant(ctx, _xlat, insn->op2);
1211
0
      }
1212
0
      if (insn->op3 < IR_TRUE) {
1213
0
        consts_count += ir_count_constant(ctx, _xlat, insn->op3);
1214
0
      }
1215
0
    }
1216
316k
    n = insn->inputs_count;
1217
316k
    insns_count += ir_insn_inputs_to_len(n);
1218
316k
    i = _next[i];
1219
316k
    insn = &ctx->ir_base[i];
1220
316k
    if (bb->flags & (IR_BB_HAS_PHI|IR_BB_HAS_PI|IR_BB_HAS_PARAM|IR_BB_HAS_VAR)) {
1221
20.6k
      int count = 0;
1222
1223
      /* Schedule PARAM, VAR, PI */
1224
20.6k
      while (insn->op == IR_PARAM || insn->op == IR_VAR || insn->op == IR_PI) {
1225
44
        _xlat[i] = insns_count;
1226
44
        insns_count += 1;
1227
44
        i = _next[i];
1228
44
        insn = &ctx->ir_base[i];
1229
44
        count++;
1230
44
      }
1231
      /* Schedule PHIs */
1232
45.2k
      while (insn->op == IR_PHI) {
1233
24.5k
        ir_ref j, *p, input;
1234
1235
24.5k
        _xlat[i] = insns_count;
1236
        /* Reuse "n" from MERGE and skip first input */
1237
24.5k
        insns_count += ir_insn_inputs_to_len(n + 1);
1238
75.2k
        for (j = n, p = insn->ops + 2; j > 0; p++, j--) {
1239
50.7k
          input = *p;
1240
50.7k
          if (input < IR_TRUE) {
1241
3.76k
            consts_count += ir_count_constant(ctx, _xlat, input);
1242
3.76k
          }
1243
50.7k
        }
1244
24.5k
        i = _next[i];
1245
24.5k
        insn = &ctx->ir_base[i];
1246
24.5k
        count++;
1247
24.5k
      }
1248
      /* Schedule remaining PHIs */
1249
20.6k
      if (UNEXPECTED(count < ctx->use_lists[start].count - 1)) {
1250
29
        ir_use_list *use_list = &ctx->use_lists[start];
1251
29
        ir_ref *p, count = use_list->count;
1252
29
        ir_ref phis = _prev[i];
1253
1254
87
        for (p = &ctx->use_edges[use_list->refs]; count > 0; p++, count--) {
1255
58
          ir_ref use = *p;
1256
58
          ir_insn *use_insn = &ctx->ir_base[use];
1257
58
          if (!_xlat[use] && ctx->cfg_map[use]) {
1258
58
            if (use_insn->op == IR_PARAM
1259
58
             || use_insn->op == IR_VAR
1260
58
             || use_insn->op == IR_PI
1261
58
             || use_insn->op == IR_PHI) {
1262
29
              IR_ASSERT(ctx->cfg_map[use] == b);
1263
29
              if (_prev[use] != phis) {
1264
                /* remove "use" */
1265
29
                _prev[_next[use]] = _prev[use];
1266
29
                _next[_prev[use]] = _next[use];
1267
                /* insert "use" after "phis" */
1268
29
                _prev[use] = phis;
1269
29
                _next[use] = _next[phis];
1270
29
                _prev[_next[phis]] = use;
1271
29
                _next[phis] = use;
1272
29
              }
1273
29
              phis = use;
1274
29
              _xlat[use] = insns_count;
1275
29
              if (use_insn->op == IR_PHI) {
1276
29
                ir_ref *q;
1277
                /* Reuse "n" from MERGE and skip first input */
1278
29
                insns_count += ir_insn_inputs_to_len(n + 1);
1279
87
                for (j = n, q = use_insn->ops + 2; j > 0; q++, j--) {
1280
58
                  ir_ref input = *q;
1281
58
                  if (input < IR_TRUE) {
1282
29
                    consts_count += ir_count_constant(ctx, _xlat, input);
1283
29
                  }
1284
58
                }
1285
29
              } else {
1286
0
                insns_count += 1;
1287
0
              }
1288
29
            }
1289
58
          }
1290
58
        }
1291
29
        i = _next[phis];
1292
29
        insn = &ctx->ir_base[i];
1293
29
      }
1294
20.6k
    }
1295
1296
316k
    if (i != bb->end) {
1297
223k
      ir_schedule_topsort(ctx, b, bb, _xlat, _next, _prev, i, bb->end, &insns_count, &consts_count);
1298
223k
    }
1299
1300
316k
#ifdef IR_DEBUG
1301
316k
    if (ctx->flags & IR_DEBUG_SCHEDULE) {
1302
0
      ir_schedule_print_list(ctx, b, _next, start, bb->end, "  FINAL");
1303
0
    }
1304
316k
#endif
1305
1306
    /* Schedule BB end */
1307
316k
    i = bb->end;
1308
316k
    insn = &ctx->ir_base[i];
1309
316k
    _xlat[i] = bb->end = insns_count;
1310
316k
    insns_count++;
1311
316k
    if (IR_INPUT_EDGES_COUNT(ir_op_flags[insn->op]) == 2) {
1312
118k
      if (insn->op2 < IR_TRUE) {
1313
4.50k
        consts_count += ir_count_constant(ctx, _xlat, insn->op2);
1314
4.50k
      }
1315
118k
    }
1316
316k
  }
1317
1318
9.93k
#if 1
1319
  /* Check if scheduling didn't make any modifications */
1320
9.93k
  if (consts_count == ctx->consts_count && insns_count == ctx->insns_count) {
1321
8.43k
    bool changed = 0;
1322
1323
696k
    for (i = 1; i != 0; i = _next[i]) {
1324
691k
      if (_xlat[i] != i) {
1325
3.79k
        changed = 1;
1326
3.79k
        break;
1327
3.79k
      }
1328
691k
    }
1329
8.43k
    if (!changed) {
1330
4.64k
      _xlat -= ctx->consts_count;
1331
4.64k
      ir_mem_free(_xlat);
1332
4.64k
      ir_mem_free(_next);
1333
1334
4.64k
      ctx->prev_ref = _prev;
1335
4.64k
      ctx->flags2 |= IR_LINEAR;
1336
4.64k
      ir_truncate(ctx);
1337
1338
4.64k
      return 1;
1339
4.64k
    }
1340
8.43k
  }
1341
5.29k
#endif
1342
1343
5.29k
  ir_mem_free(_prev);
1344
1345
5.29k
  uint32_t *map = ir_mem_calloc(insns_count, sizeof(uint32_t));
1346
5.29k
  _prev = ir_mem_malloc(insns_count * sizeof(ir_ref));
1347
5.29k
  lists = ir_mem_malloc(insns_count * sizeof(ir_use_list));
1348
5.29k
  ir_ref *use_edges = edges = ir_mem_malloc(use_edges_count * sizeof(ir_ref));
1349
5.29k
  base = ir_mem_malloc((consts_count + insns_count) * sizeof(ir_insn));
1350
5.29k
  base += consts_count;
1351
1352
  /* Copy constants */
1353
5.29k
  if (ctx->consts_count == consts_count) {
1354
4.58k
    memcpy(base - consts_count + 1, ctx->ir_base - consts_count + 1, sizeof(ir_insn) * consts_count);
1355
230k
    for (j = -consts_count + 1; j < IR_TRUE; j++) {
1356
226k
      _xlat[j] = j;
1357
226k
    }
1358
4.58k
  } else {
1359
709
    ir_insn *src = ctx->ir_base - ctx->consts_count + 1;
1360
709
    ir_insn *dst = base - consts_count + 1;
1361
1362
709
    i = -ctx->consts_count + 1;
1363
709
    j = -consts_count + 1;
1364
25.4k
    while (i < IR_TRUE) {
1365
24.6k
      if (_xlat[i]) {
1366
23.0k
        *dst = *src;
1367
23.0k
        _xlat[i] = j;
1368
23.0k
        if (dst->op == IR_LONG_CONST) {
1369
0
          ir_ref n;
1370
1371
0
          memset(dst + 1, 0, dst->long_const_size);
1372
0
          memcpy(dst + 1, src + 1, dst->long_const_size);
1373
0
          n = (ir_ref)IR_ALIGNED_SIZE(dst->long_const_size, sizeof(ir_insn)) / sizeof(ir_insn);
1374
0
          dst += n + 1;
1375
0
          src += n + 1;
1376
0
          i += n + 1;
1377
0
          j += n + 1;
1378
0
          continue;
1379
23.0k
        } else {
1380
23.0k
          dst->prev_const = 0;
1381
23.0k
          dst++;
1382
23.0k
          j++;
1383
23.0k
        }
1384
23.0k
      }
1385
24.6k
      src++;
1386
24.6k
      i++;
1387
24.6k
    }
1388
709
    IR_ASSERT(j == IR_TRUE);
1389
709
    base[IR_TRUE].optx = IR_OPT(IR_C_BOOL, IR_BOOL);
1390
709
    base[IR_TRUE].val.u64 = 1;
1391
709
    base[IR_FALSE].optx = IR_OPT(IR_C_BOOL, IR_BOOL);
1392
709
    base[IR_FALSE].val.u64 = 0;
1393
709
    base[IR_NULL].optx = IR_OPT(IR_C_ADDR, IR_ADDR);
1394
709
    base[IR_NULL].val.u64 = 0;
1395
709
    MAKE_NOP(&base[IR_UNUSED]);
1396
709
  }
1397
1398
  /* Copy instructions, use lists and use edges */
1399
5.29k
#ifdef IR_DEBUG
1400
5.29k
  ir_ref orig_use_edges_count = use_edges_count;
1401
5.29k
#endif
1402
5.29k
  prev_ref = 0;
1403
5.29k
  use_edges_count = 0;
1404
1.49M
  for (i = 1; i != 0; i = _next[i]) {
1405
1.48M
    new_ref = _xlat[i];
1406
1.48M
    map[new_ref] = ctx->cfg_map[i];
1407
1.48M
    _prev[new_ref] = prev_ref;
1408
1.48M
    prev_ref = new_ref;
1409
1410
1.48M
    use_list = &ctx->use_lists[i];
1411
1.48M
    n = use_list->count;
1412
1.48M
    k = 0;
1413
1.48M
    if (n == 1) {
1414
945k
      ref = ctx->use_edges[use_list->refs];
1415
945k
      if (EXPECTED(_xlat[ref])) {
1416
945k
        *edges = _xlat[ref];
1417
945k
        edges++;
1418
945k
        k = 1;
1419
945k
      }
1420
945k
    } else {
1421
540k
      p = &ctx->use_edges[use_list->refs];
1422
1.80M
      while (n--) {
1423
1.26M
        ref = *p;
1424
1.26M
        if (EXPECTED(_xlat[ref])) {
1425
1.26M
          *edges = _xlat[ref];
1426
1.26M
          edges++;
1427
1.26M
          k++;
1428
1.26M
        }
1429
1.26M
        p++;
1430
1.26M
      }
1431
540k
    }
1432
1.48M
    new_list = &lists[new_ref];
1433
1.48M
    new_list->refs = use_edges_count;
1434
1.48M
    use_edges_count += k;
1435
1.48M
    new_list->count = k;
1436
1437
1.48M
    insn = &ctx->ir_base[i];
1438
1.48M
    new_insn = &base[new_ref];
1439
1440
1.48M
    new_insn->optx = insn->optx;
1441
1.48M
    n = new_insn->inputs_count;
1442
1.48M
    switch (n) {
1443
5.29k
      case 0:
1444
5.29k
        new_insn->op1 = insn->op1;
1445
5.29k
        new_insn->op2 = insn->op2;
1446
5.29k
        new_insn->op3 = insn->op3;
1447
5.29k
        break;
1448
553k
      case 1:
1449
553k
        new_insn->op1 = _xlat[insn->op1];
1450
553k
        if (new_insn->op == IR_BEGIN && insn->op2) {
1451
1
          new_insn->op2 = _xlat[insn->op2];
1452
553k
        } else {
1453
553k
          new_insn->op2 = insn->op2;
1454
553k
        }
1455
553k
        new_insn->op3 = insn->op3;
1456
553k
        break;
1457
664k
      case 2:
1458
664k
        new_insn->op1 = _xlat[insn->op1];
1459
664k
        new_insn->op2 = _xlat[insn->op2];
1460
664k
        new_insn->op3 = insn->op3;
1461
664k
#if IR_SCHEDULE_SWAP_OPS
1462
        /* Swap operands according to folding rules */
1463
664k
        if (new_insn->op1 < new_insn->op2) {
1464
220k
          switch (new_insn->op) {
1465
0
            case IR_EQ:
1466
0
            case IR_NE:
1467
0
            case IR_ORDERED:
1468
0
            case IR_UNORDERED:
1469
0
            case IR_ADD:
1470
0
            case IR_MUL:
1471
0
            case IR_ADD_OV:
1472
0
            case IR_MUL_OV:
1473
0
            case IR_OR:
1474
0
            case IR_AND:
1475
0
            case IR_XOR:
1476
0
            case IR_MIN:
1477
0
            case IR_MAX:
1478
0
              SWAP_REFS(new_insn->op1, new_insn->op2);
1479
0
              break;
1480
0
            case IR_LT:
1481
0
            case IR_GE:
1482
0
            case IR_LE:
1483
0
            case IR_GT:
1484
0
            case IR_ULT:
1485
0
            case IR_UGE:
1486
0
            case IR_ULE:
1487
0
            case IR_UGT:
1488
0
              SWAP_REFS(new_insn->op1, new_insn->op2);
1489
0
              new_insn->op ^= 3; /* [U]LT <-> [U]GT, [U]LE <-> [U]GE */
1490
0
              break;
1491
220k
          }
1492
220k
        }
1493
664k
#endif
1494
664k
        break;
1495
664k
      case 3:
1496
229k
        new_insn->op1 = _xlat[insn->op1];
1497
229k
        new_insn->op2 = _xlat[insn->op2];
1498
229k
        new_insn->op3 = _xlat[insn->op3];
1499
229k
        break;
1500
33.0k
      default:
1501
173k
        for (j = n, p = insn->ops + 1, q = new_insn->ops + 1; j > 0; p++, q++, j--) {
1502
140k
          *q = _xlat[*p];
1503
140k
        }
1504
33.0k
        break;
1505
1.48M
    }
1506
1.48M
  }
1507
1508
  /* Update list of terminators (IR_OPND_CONTROL_REF) */
1509
5.29k
  insn = &base[1];
1510
5.29k
  ref = insn->op1;
1511
5.29k
  if (ref) {
1512
5.29k
    insn->op1 = ref = _xlat[ref];
1513
12.2k
    while (1) {
1514
12.2k
      insn = &base[ref];
1515
12.2k
      ref = insn->op3;
1516
12.2k
      if (!ref) {
1517
5.29k
        break;
1518
5.29k
      }
1519
6.99k
      insn->op3 = ref = _xlat[ref];
1520
6.99k
    }
1521
5.29k
  }
1522
1523
5.29k
  if (ctx->binding) {
1524
3
    ir_xlat_binding(ctx, _xlat);
1525
3
  }
1526
1527
5.29k
  _xlat -= ctx->consts_count;
1528
5.29k
  ir_mem_free(_xlat);
1529
5.29k
  ir_mem_free(_next);
1530
1531
  /* Switch to new IR buffer */
1532
5.29k
  ir_mem_free(ctx->ir_base - ctx->consts_limit);
1533
5.29k
  ctx->ir_base = base;
1534
5.29k
  ctx->insns_count = ctx->insns_limit = insns_count;
1535
5.29k
  ctx->consts_count = ctx->consts_limit = consts_count;
1536
1537
5.29k
  ir_mem_free(ctx->use_lists);
1538
5.29k
  ir_mem_free(ctx->use_edges);
1539
5.29k
  IR_ASSERT(orig_use_edges_count >= use_edges_count);
1540
5.29k
  ctx->use_lists = lists;
1541
5.29k
  ctx->use_edges = use_edges;
1542
5.29k
  ctx->use_edges_count = use_edges_count;
1543
1544
5.29k
  ir_mem_free(ctx->cfg_map);
1545
5.29k
  ctx->cfg_map = map;
1546
1547
5.29k
  ctx->prev_ref = _prev;
1548
1549
5.29k
  ctx->flags2 |= IR_LINEAR;
1550
1551
5.29k
  return 1;
1552
5.29k
}
1553
1554
void ir_build_prev_refs(ir_ctx *ctx)
1555
0
{
1556
0
  uint32_t b;
1557
0
  ir_block *bb;
1558
0
  ir_ref i, n, prev;
1559
0
  ir_insn *insn;
1560
1561
0
  ctx->prev_ref = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref));
1562
0
  prev = 0;
1563
0
  for (b = 1, bb = ctx->cfg_blocks + b; b <= ctx->cfg_blocks_count; b++, bb++) {
1564
0
    IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
1565
0
    for (i = bb->start, insn = ctx->ir_base + i; i < bb->end;) {
1566
0
      ctx->prev_ref[i] = prev;
1567
0
      n = ir_insn_len(insn);
1568
0
      prev = i;
1569
0
      i += n;
1570
0
      insn += n;
1571
0
    }
1572
0
    ctx->prev_ref[i] = prev;
1573
0
  }
1574
0
}