Coverage Report

Created: 2025-12-31 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/jit/ir/ir_dump.c
Line
Count
Source
1
/*
2
 * IR - Lightweight JIT Compilation Framework
3
 * (debug dumps)
4
 * Copyright (C) 2022 Zend by Perforce.
5
 * Authors: Dmitry Stogov <dmitry@php.net>
6
 */
7
8
#include "ir.h"
9
#include "ir_private.h"
10
11
void ir_dump(const ir_ctx *ctx, FILE *f)
12
0
{
13
0
  ir_ref i, j, n, ref, *p;
14
0
  ir_insn *insn;
15
0
  uint32_t flags;
16
17
0
  for (i = 1 - ctx->consts_count, insn = ctx->ir_base + i; i < IR_UNUSED; i++, insn++) {
18
0
    fprintf(f, "%05d %s %s(", i, ir_op_name[insn->op], ir_type_name[insn->type]);
19
0
    ir_print_const(ctx, insn, f, true);
20
0
    fprintf(f, ")\n");
21
0
  }
22
23
0
  for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count; i++, insn++) {
24
0
    flags = ir_op_flags[insn->op];
25
0
    fprintf(f, "%05d %s", i, ir_op_name[insn->op]);
26
0
    if ((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) {
27
0
      fprintf(f, " %s", ir_type_name[insn->type]);
28
0
    }
29
0
    n = ir_operands_count(ctx, insn);
30
0
    for (j = 1, p = insn->ops + 1; j <= 3; j++, p++) {
31
0
      ref = *p;
32
0
      if (ref) {
33
0
        fprintf(f, " %05d", ref);
34
0
      }
35
0
    }
36
0
    if (n > 3) {
37
0
      n -= 3;
38
0
      do {
39
0
        i++;
40
0
        insn++;
41
0
        fprintf(f, "\n%05d", i);
42
0
        for (j = 0; j < 4; j++, p++) {
43
0
          ref = *p;
44
0
          if (ref) {
45
0
            fprintf(f, " %05d", ref);
46
0
          }
47
0
        }
48
0
        n -= 4;
49
0
      } while (n > 0);
50
0
    }
51
0
    fprintf(f, "\n");
52
0
  }
53
0
}
54
55
void ir_dump_dot(const ir_ctx *ctx, const char *name, FILE *f)
56
0
{
57
0
  int DATA_WEIGHT    = 0;
58
0
  int CONTROL_WEIGHT = 5;
59
0
  int REF_WEIGHT     = 4;
60
0
  ir_ref i, j, n, ref, *p;
61
0
  ir_insn *insn;
62
0
  uint32_t flags;
63
64
0
  fprintf(f, "digraph %s {\n", name);
65
0
  fprintf(f, "\trankdir=TB;\n");
66
0
  for (i = 1 - ctx->consts_count, insn = ctx->ir_base + i; i < IR_UNUSED; i++, insn++) {
67
0
    fprintf(f, "\tc%d [label=\"C%d: CONST %s(", -i, -i, ir_type_name[insn->type]);
68
    /* FIXME(tony): We still cannot handle strings with escaped double quote inside */
69
0
    ir_print_const(ctx, insn, f, false);
70
0
    fprintf(f, ")\",style=filled,fillcolor=yellow];\n");
71
0
  }
72
73
0
  for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
74
0
    flags = ir_op_flags[insn->op];
75
0
    if (flags & IR_OP_FLAG_CONTROL) {
76
0
      if (insn->op == IR_START) {
77
0
        fprintf(f, "\t{rank=min; n%d [label=\"%d: %s\",shape=box,style=\"rounded,filled\",fillcolor=red];}\n", i, i, ir_op_name[insn->op]);
78
0
      } else if (insn->op == IR_ENTRY) {
79
0
        fprintf(f, "\t{n%d [label=\"%d: %s\",shape=box,style=\"rounded,filled\",fillcolor=red];}\n", i, i, ir_op_name[insn->op]);
80
0
      } else if (flags & IR_OP_FLAG_TERMINATOR) {
81
0
        fprintf(f, "\t{rank=max; n%d [label=\"%d: %s\",shape=box,style=\"rounded,filled\",fillcolor=red];}\n", i, i, ir_op_name[insn->op]);
82
0
      } else if (flags & IR_OP_FLAG_MEM) {
83
0
        fprintf(f, "\tn%d [label=\"%d: %s\",shape=box,style=filled,fillcolor=pink];\n", i, i, ir_op_name[insn->op]);
84
0
      } else {
85
0
        fprintf(f, "\tn%d [label=\"%d: %s\",shape=box,style=filled,fillcolor=lightcoral];\n", i, i, ir_op_name[insn->op]);
86
0
      }
87
0
    } else if (flags & IR_OP_FLAG_DATA) {
88
0
      if (IR_OPND_KIND(flags, 1) == IR_OPND_DATA) {
89
        /* not a leaf */
90
0
        fprintf(f, "\tn%d [label=\"%d: %s\"", i, i, ir_op_name[insn->op]);
91
0
        fprintf(f, ",shape=diamond,style=filled,fillcolor=deepskyblue];\n");
92
0
      } else {
93
0
        if (insn->op == IR_PARAM) {
94
0
          fprintf(f, "\tn%d [label=\"%d: %s %s \\\"%s\\\"\",style=filled,fillcolor=lightblue];\n",
95
0
            i, i, ir_op_name[insn->op], ir_type_name[insn->type], ir_get_str(ctx, insn->op2));
96
0
        } else if (insn->op == IR_VAR) {
97
0
          fprintf(f, "\tn%d [label=\"%d: %s %s \\\"%s\\\"\"];\n", i, i, ir_op_name[insn->op], ir_type_name[insn->type], ir_get_str(ctx, insn->op2));
98
0
        } else {
99
0
          fprintf(f, "\tn%d [label=\"%d: %s %s\",style=filled,fillcolor=deepskyblue];\n", i, i, ir_op_name[insn->op], ir_type_name[insn->type]);
100
0
        }
101
0
      }
102
0
    }
103
0
    n = ir_operands_count(ctx, insn);
104
0
    for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
105
0
      ref = *p;
106
0
      if (ref) {
107
0
        switch (IR_OPND_KIND(flags, j)) {
108
0
          case IR_OPND_DATA:
109
0
            if (IR_IS_CONST_REF(ref)) {
110
0
              fprintf(f, "\tc%d -> n%d [color=blue,weight=%d];\n", -ref, i, DATA_WEIGHT);
111
0
            } else if (insn->op == IR_PHI
112
0
                && ctx->ir_base[insn->op1].op == IR_LOOP_BEGIN
113
0
                && ctx->ir_base[ir_insn_op(&ctx->ir_base[insn->op1], j - 1)].op == IR_LOOP_END) {
114
0
              fprintf(f, "\tn%d -> n%d [color=blue,dir=back];\n", i, ref);
115
0
            } else {
116
0
              fprintf(f, "\tn%d -> n%d [color=blue,weight=%d];\n", ref, i, DATA_WEIGHT);
117
0
            }
118
0
            break;
119
0
          case IR_OPND_CONTROL:
120
0
            if (insn->op == IR_LOOP_BEGIN && ctx->ir_base[ref].op == IR_LOOP_END) {
121
0
              fprintf(f, "\tn%d -> n%d [style=bold,color=red,dir=back];\n", i, ref);
122
0
            } else if (insn->op == IR_ENTRY) {
123
0
              fprintf(f, "\tn%d -> n%d [style=bold,color=red,style=dashed,weight=%d];\n", ref, i, CONTROL_WEIGHT);
124
0
            } else {
125
0
              fprintf(f, "\tn%d -> n%d [style=bold,color=red,weight=%d];\n", ref, i, CONTROL_WEIGHT);
126
0
            }
127
0
            break;
128
0
          case IR_OPND_CONTROL_DEP:
129
0
          case IR_OPND_CONTROL_REF:
130
0
            fprintf(f, "\tn%d -> n%d [style=dashed,dir=back,weight=%d];\n", ref, i, REF_WEIGHT);
131
0
            break;
132
0
          case IR_OPND_LABEL_REF:
133
0
            if (ref) {
134
0
              fprintf(f, "\tc%d -> n%d [color=blue,weight=%d];\n", -ref, i, REF_WEIGHT);
135
0
            }
136
0
            break;
137
0
        }
138
0
      }
139
0
    }
140
0
    n = ir_insn_inputs_to_len(n);
141
0
    i += n;
142
0
    insn += n;
143
0
  }
144
0
  fprintf(f, "}\n");
145
0
}
146
147
void ir_dump_use_lists(const ir_ctx *ctx, FILE *f)
148
0
{
149
0
  ir_ref i, j, n, *p;
150
0
  ir_use_list *list;
151
152
0
  if (ctx->use_lists) {
153
0
    fprintf(f, "{ # Use Lists\n");
154
0
    for (i = 1, list = &ctx->use_lists[1]; i < ctx->insns_count; i++, list++) {
155
0
      n = list->count;
156
0
      if (n > 0) {
157
0
        p = &ctx->use_edges[list->refs];
158
0
        fprintf(f, "%05d(%d): [%05d", i, n, *p);
159
0
        p++;
160
0
        for (j = 1; j < n; j++, p++) {
161
0
          fprintf(f, ", %05d", *p);
162
0
        }
163
0
        fprintf(f, "]\n");
164
0
      }
165
0
    }
166
0
    fprintf(f, "}\n");
167
0
  }
168
0
}
169
170
static void ir_dump_dessa_moves(const ir_ctx *ctx, int b, ir_block *bb, FILE *f)
171
0
{
172
0
  uint32_t succ;
173
0
  ir_block *succ_bb;
174
0
  ir_use_list *use_list;
175
0
  ir_ref k, i, *p, use_ref, input;
176
0
  ir_insn *use_insn;
177
178
0
  IR_ASSERT(bb->successors_count == 1);
179
0
  succ = ctx->cfg_edges[bb->successors];
180
0
  succ_bb = &ctx->cfg_blocks[succ];
181
0
  IR_ASSERT(succ_bb->predecessors_count > 1);
182
0
  use_list = &ctx->use_lists[succ_bb->start];
183
0
  k = ir_phi_input_number(ctx, succ_bb, b);
184
185
0
  for (i = use_list->count, p = &ctx->use_edges[use_list->refs]; i > 0; p++, i--) {
186
0
    use_ref = *p;
187
0
    use_insn = &ctx->ir_base[use_ref];
188
0
    if (use_insn->op == IR_PHI) {
189
0
      input = ir_insn_op(use_insn, k);
190
0
      if (IR_IS_CONST_REF(input)) {
191
0
        fprintf(f, "\t# DESSA MOV c_%d", -input);
192
0
      } else if (ctx->vregs[input] != ctx->vregs[use_ref]) {
193
0
        fprintf(f, "\t# DESSA MOV d_%d {R%d}", input, ctx->vregs[input]);
194
0
      } else {
195
0
        continue;
196
0
      }
197
0
      if (ctx->regs) {
198
0
        int8_t *regs = ctx->regs[use_ref];
199
0
        int8_t reg = regs[k];
200
0
        if (reg != IR_REG_NONE) {
201
0
          fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[input].type),
202
0
            (reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
203
0
        }
204
0
      }
205
0
      fprintf(f, " -> d_%d {R%d}", use_ref, ctx->vregs[use_ref]);
206
0
      if (ctx->regs) {
207
0
        int8_t reg = ctx->regs[use_ref][0];
208
0
        if (reg != IR_REG_NONE) {
209
0
          fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[use_ref].type),
210
0
            (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
211
0
        }
212
0
      }
213
0
      fprintf(f, "\n");
214
0
    }
215
0
  }
216
0
}
217
218
static void ir_dump_cfg_block(ir_ctx *ctx, FILE *f, uint32_t b, ir_block *bb)
219
0
{
220
0
  fprintf(f, "BB%d:\n", b);
221
0
  fprintf(f, "\tstart=%d\n", bb->start);
222
0
  fprintf(f, "\tend=%d\n", bb->end);
223
0
  if (bb->successors_count) {
224
0
    uint32_t i;
225
226
0
    fprintf(f, "\tsuccessors(%d) [BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
227
0
    for (i = 1; i < bb->successors_count; i++) {
228
0
      fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
229
0
    }
230
0
    fprintf(f, "]\n");
231
0
  }
232
0
  if (bb->predecessors_count) {
233
0
    uint32_t i;
234
235
0
    fprintf(f, "\tpredecessors(%d) [BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
236
0
    for (i = 1; i < bb->predecessors_count; i++) {
237
0
      fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
238
0
    }
239
0
    fprintf(f, "]\n");
240
0
  }
241
0
  if (bb->dom_parent > 0) {
242
0
    fprintf(f, "\tdom_parent=BB%d\n", bb->dom_parent);
243
0
  }
244
0
  fprintf(f, "\tdom_depth=%d\n", bb->dom_depth);
245
0
  if (bb->dom_child > 0) {
246
0
    int child = bb->dom_child;
247
0
    fprintf(f, "\tdom_children [BB%d", child);
248
0
    child = ctx->cfg_blocks[child].dom_next_child;
249
0
    while (child > 0) {
250
0
      fprintf(f, ", BB%d", child);
251
0
      child = ctx->cfg_blocks[child].dom_next_child;
252
0
    }
253
0
    fprintf(f, "]\n");
254
0
  }
255
0
  if (bb->flags & IR_BB_ENTRY) {
256
0
    fprintf(f, "\tENTRY\n");
257
0
  }
258
0
  if (bb->flags & IR_BB_UNREACHABLE) {
259
0
    fprintf(f, "\tUNREACHABLE\n");
260
0
  }
261
0
  if (bb->flags & IR_BB_LOOP_HEADER) {
262
0
    if (bb->flags & IR_BB_LOOP_WITH_ENTRY) {
263
0
      fprintf(f, "\tLOOP_HEADER, LOOP_WITH_ENTRY\n");
264
0
    } else {
265
0
      fprintf(f, "\tLOOP_HEADER\n");
266
0
    }
267
0
  }
268
0
  if (bb->flags & IR_BB_IRREDUCIBLE_LOOP) {
269
0
    fprintf(stderr, "\tIRREDUCIBLE_LOOP\n");
270
0
  }
271
0
  if (bb->loop_header > 0) {
272
0
    fprintf(f, "\tloop_header=BB%d\n", bb->loop_header);
273
0
  }
274
0
  if (bb->loop_depth != 0) {
275
0
    fprintf(f, "\tloop_depth=%d\n", bb->loop_depth);
276
0
  }
277
0
  if (bb->flags & IR_BB_OSR_ENTRY_LOADS) {
278
0
    ir_list *list = (ir_list*)ctx->osr_entry_loads;
279
0
    uint32_t pos = 0, i, count;
280
281
0
    IR_ASSERT(list);
282
0
    while (1) {
283
0
      i = ir_list_at(list, pos);
284
0
      if (b == i) {
285
0
        break;
286
0
      }
287
0
      IR_ASSERT(i != 0); /* end marker */
288
0
      pos++;
289
0
      count = ir_list_at(list, pos);
290
0
      pos += count + 1;
291
0
    }
292
0
    pos++;
293
0
    count = ir_list_at(list, pos);
294
0
    pos++;
295
296
0
    for (i = 0; i < count; i++, pos++) {
297
0
      ir_ref ref = ir_list_at(list, pos);
298
0
      fprintf(f, "\tOSR_ENTRY_LOAD=d_%d\n", ref);
299
0
    }
300
0
  }
301
0
  if (bb->flags & IR_BB_DESSA_MOVES) {
302
0
    ir_dump_dessa_moves(ctx, b, bb, f);
303
0
  }
304
0
}
305
306
void ir_dump_cfg(ir_ctx *ctx, FILE *f)
307
0
{
308
0
  if (ctx->cfg_blocks) {
309
0
    uint32_t  b, i, bb_count = ctx->cfg_blocks_count;
310
0
    ir_block *bb = ctx->cfg_blocks + 1;
311
312
0
    fprintf(f, "{ # CFG\n");
313
0
    if (ctx->cfg_schedule) {
314
0
      for (i = 1; i <= bb_count; i++) {
315
0
        b = ctx->cfg_schedule[i];
316
0
        bb = &ctx->cfg_blocks[b];
317
0
        ir_dump_cfg_block(ctx, f, b, bb);
318
0
      }
319
0
    } else {
320
0
      for (b = 1; b <= bb_count; b++, bb++) {
321
0
        ir_dump_cfg_block(ctx, f, b, bb);
322
0
      }
323
0
    }
324
0
    fprintf(f, "}\n");
325
0
  }
326
0
}
327
328
void ir_dump_cfg_map(const ir_ctx *ctx, FILE *f)
329
0
{
330
0
  ir_ref i;
331
0
    uint32_t *_blocks = ctx->cfg_map;
332
333
0
    if (_blocks) {
334
0
    fprintf(f, "{ # CFG map (insn -> bb)\n");
335
0
    for (i = IR_UNUSED + 1; i < ctx->insns_count; i++) {
336
0
      fprintf(f, "%d -> %d\n", i, _blocks[i]);
337
0
    }
338
0
    fprintf(f, "}\n");
339
0
  }
340
0
}
341
342
void ir_dump_live_ranges(const ir_ctx *ctx, FILE *f)
343
0
{
344
0
    ir_ref i, j, n;
345
346
0
  if (!ctx->live_intervals) {
347
0
    return;
348
0
  }
349
0
  fprintf(f, "{ # LIVE-RANGES (vregs_count=%d)\n", ctx->vregs_count);
350
0
  for (i = 0; i <= ctx->vregs_count; i++) {
351
0
    ir_live_interval *ival = ctx->live_intervals[i];
352
353
0
    if (ival) {
354
0
      ir_live_range *p;
355
0
      ir_use_pos *use_pos;
356
357
0
      if (i == 0) {
358
0
        fprintf(f, "TMP");
359
0
      } else {
360
0
        for (j = 1; j < ctx->insns_count; j++) {
361
0
          if (ctx->vregs[j] == (uint32_t)i) {
362
0
            break;
363
0
          }
364
0
        }
365
0
        fprintf(f, "R%d (d_%d", i, j);
366
0
        for (j++; j < ctx->insns_count; j++) {
367
0
          if (ctx->vregs[j] == (uint32_t)i) {
368
0
            fprintf(f, ", d_%d", j);
369
0
          }
370
0
        }
371
0
        fprintf(f, ")");
372
0
        if (ival->stack_spill_pos != -1) {
373
0
          if (ival->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL) {
374
0
            IR_ASSERT(ctx->spill_base >= 0);
375
0
            fprintf(f, " [SPILL=0x%x(%%%s)]", ival->stack_spill_pos, ir_reg_name(ctx->spill_base, IR_ADDR));
376
0
          } else {
377
0
            fprintf(f, " [SPILL=0x%x]", ival->stack_spill_pos);
378
0
          }
379
0
        }
380
0
      }
381
0
      if (ival->next) {
382
0
        fprintf(f, "\n\t");
383
0
      } else if (ival->reg != IR_REG_NONE) {
384
0
        fprintf(f, " ");
385
0
      }
386
0
      do {
387
0
        if (ival->reg != IR_REG_NONE) {
388
0
          fprintf(f, "[%%%s]", ir_reg_name(ival->reg, ival->type));
389
0
        }
390
0
        p = &ival->range;
391
0
        fprintf(f, ": [%d.%d-%d.%d)",
392
0
          IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
393
0
          IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
394
0
        if (i == 0) {
395
          /* This is a TMP register */
396
0
          if (ival->tmp_ref == IR_LIVE_POS_TO_REF(p->start)) {
397
0
            fprintf(f, "/%d", ival->tmp_op_num);
398
0
          } else {
399
0
            fprintf(f, "/%d.%d", ival->tmp_ref, ival->tmp_op_num);
400
0
          }
401
0
        } else {
402
0
          p = p->next;
403
0
          while (p) {
404
0
            fprintf(f, ", [%d.%d-%d.%d)",
405
0
              IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
406
0
              IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
407
0
            p = p->next;
408
0
          }
409
0
        }
410
0
        use_pos = ival->use_pos;
411
0
        while (use_pos) {
412
0
          if (use_pos->flags & IR_PHI_USE) {
413
0
            IR_ASSERT(use_pos->op_num > 0);
414
0
            fprintf(f, ", PHI_USE(%d.%d, phi=d_%d/%d)",
415
0
              IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
416
0
              -use_pos->hint_ref, use_pos->op_num);
417
0
          } else if (use_pos->flags & IR_FUSED_USE) {
418
0
            fprintf(f, ", USE(%d.%d/%d.%d",
419
0
              IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
420
0
              -use_pos->hint_ref, use_pos->op_num);
421
0
            if (use_pos->hint >= 0) {
422
0
              fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type));
423
0
            }
424
0
            fprintf(f, ")");
425
0
            if (use_pos->flags & IR_USE_MUST_BE_IN_REG) {
426
0
              fprintf(f, "!");
427
0
            }
428
0
          } else {
429
0
            if (!use_pos->op_num) {
430
0
              fprintf(f, ", DEF(%d.%d",
431
0
                IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos));
432
0
            } else {
433
0
              fprintf(f, ", USE(%d.%d/%d",
434
0
                IR_LIVE_POS_TO_REF(use_pos->pos), IR_LIVE_POS_TO_SUB_REF(use_pos->pos),
435
0
                use_pos->op_num);
436
0
            }
437
0
            if (use_pos->hint >= 0) {
438
0
              fprintf(f, ", hint=%%%s", ir_reg_name(use_pos->hint, ival->type));
439
0
            }
440
0
            if (use_pos->hint_ref) {
441
0
              fprintf(f, ", hint=R%d", ctx->vregs[use_pos->hint_ref]);
442
0
            }
443
0
            fprintf(f, ")");
444
0
            if (use_pos->flags & IR_USE_MUST_BE_IN_REG) {
445
0
              fprintf(f, "!");
446
0
            }
447
0
          }
448
0
          use_pos = use_pos->next;
449
0
        }
450
0
        if (ival->next) {
451
0
          fprintf(f, "\n\t");
452
0
        }
453
0
        ival = ival->next;
454
0
      } while (ival);
455
0
      fprintf(f, "\n");
456
0
    }
457
0
  }
458
0
#if 1
459
0
  n = ctx->vregs_count + ir_regs_number() + 2;
460
0
  for (i = ctx->vregs_count + 1; i <= n; i++) {
461
0
    ir_live_interval *ival = ctx->live_intervals[i];
462
463
0
    if (ival) {
464
0
      ir_live_range *p = &ival->range;
465
0
      fprintf(f, "[%%%s] : [%d.%d-%d.%d)",
466
0
        ir_reg_name(ival->reg, ival->type),
467
0
        IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
468
0
        IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
469
0
      p = p->next;
470
0
      while (p) {
471
0
        fprintf(f, ", [%d.%d-%d.%d)",
472
0
          IR_LIVE_POS_TO_REF(p->start), IR_LIVE_POS_TO_SUB_REF(p->start),
473
0
          IR_LIVE_POS_TO_REF(p->end), IR_LIVE_POS_TO_SUB_REF(p->end));
474
0
        p = p->next;
475
0
      }
476
0
      fprintf(f, "\n");
477
0
    }
478
0
  }
479
0
#endif
480
0
  fprintf(f, "}\n");
481
0
}
482
483
void ir_dump_codegen(const ir_ctx *ctx, FILE *f)
484
0
{
485
0
  ir_ref i, j, n, ref, *p;
486
0
  ir_insn *insn;
487
0
  uint32_t flags, _b, b;
488
0
  ir_block *bb;
489
0
  bool first;
490
491
0
  fprintf(f, "{\n");
492
0
  for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
493
0
    fprintf(f, "\t%s c_%d = ", ir_type_cname[insn->type], i);
494
0
    if (insn->op == IR_FUNC) {
495
0
      fprintf(f, "func %s", ir_get_str(ctx, insn->val.name));
496
0
      ir_print_proto(ctx, insn->proto, f);
497
0
    } else if (insn->op == IR_SYM) {
498
0
      fprintf(f, "sym(%s)", ir_get_str(ctx, insn->val.name));
499
0
    } else if (insn->op == IR_LABEL) {
500
0
      fprintf(f, "label(%s)", ir_get_str(ctx, insn->val.name));
501
0
    } else if (insn->op == IR_FUNC_ADDR) {
502
0
      fprintf(f, "func *");
503
0
      ir_print_const(ctx, insn, f, true);
504
0
      ir_print_proto(ctx, insn->proto, f);
505
0
    } else {
506
0
      ir_print_const(ctx, insn, f, true);
507
0
    }
508
0
    fprintf(f, ";\n");
509
0
  }
510
511
0
  for (_b = 1; _b <= ctx->cfg_blocks_count; _b++) {
512
0
    if (ctx->cfg_schedule) {
513
0
      b = ctx->cfg_schedule[_b];
514
0
    } else {
515
0
      b = _b;
516
0
    }
517
0
    bb = &ctx->cfg_blocks[b];
518
0
    if ((bb->flags & (IR_BB_START|IR_BB_ENTRY|IR_BB_EMPTY)) == IR_BB_EMPTY) {
519
0
      continue;
520
0
    }
521
522
0
    fprintf(f, "#BB%d: end=l_%d", b, bb->end);
523
0
    if (bb->flags & IR_BB_UNREACHABLE) {
524
0
      fprintf(f, ", U");
525
0
    }
526
0
    if (bb->dom_parent > 0) {
527
0
      fprintf(f, ", idom=BB%d(%d)", bb->dom_parent, bb->dom_depth);
528
0
    }
529
0
    if (bb->loop_depth != 0) {
530
0
      if (bb->flags & IR_BB_LOOP_HEADER) {
531
0
        if (bb->loop_header > 0) {
532
0
          fprintf(f, ", loop=HDR,BB%d(%d)", bb->loop_header, bb->loop_depth);
533
0
        } else {
534
0
          IR_ASSERT(bb->loop_depth == 1);
535
0
          fprintf(f, ", loop=HDR(%d)", bb->loop_depth);
536
0
        }
537
0
      } else {
538
0
        IR_ASSERT(bb->loop_header > 0);
539
0
        fprintf(f, ", loop=BB%d(%d)", bb->loop_header, bb->loop_depth);
540
0
      }
541
0
    }
542
0
    if (bb->predecessors_count) {
543
0
      uint32_t i;
544
545
0
      fprintf(f, ", pred(%d)=[BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
546
0
      for (i = 1; i < bb->predecessors_count; i++) {
547
0
        fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
548
0
      }
549
0
      fprintf(f, "]");
550
0
    }
551
0
    if (bb->successors_count) {
552
0
      uint32_t i;
553
554
0
      fprintf(f, ", succ(%d)=[BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
555
0
      for (i = 1; i < bb->successors_count; i++) {
556
0
        fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
557
0
      }
558
0
      fprintf(f, "]");
559
0
    }
560
0
    fprintf(f, "\n");
561
562
0
    for (i = bb->start, insn = ctx->ir_base + i; i <= bb->end;) {
563
0
      flags = ir_op_flags[insn->op];
564
0
      if (flags & IR_OP_FLAG_CONTROL) {
565
0
        if (!(flags & IR_OP_FLAG_MEM) || insn->type == IR_VOID) {
566
0
          fprintf(f, "\tl_%d = ", i);
567
0
        } else {
568
0
          fprintf(f, "\t%s d_%d", ir_type_cname[insn->type], i);
569
0
          if (ctx->vregs && ctx->vregs[i]) {
570
0
            fprintf(f, " {R%d}", ctx->vregs[i]);
571
0
          }
572
0
          if (ctx->regs) {
573
0
            int8_t reg = ctx->regs[i][0];
574
0
            if (reg != IR_REG_NONE) {
575
0
              fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
576
0
                (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
577
0
            }
578
0
          }
579
0
          fprintf(f, ", l_%d = ", i);
580
0
        }
581
0
      } else {
582
0
        fprintf(f, "\t");
583
0
        if (flags & IR_OP_FLAG_DATA) {
584
0
          fprintf(f, "%s d_%d", ir_type_cname[insn->type], i);
585
0
          if (ctx->vregs && ctx->vregs[i]) {
586
0
            fprintf(f, " {R%d}", ctx->vregs[i]);
587
0
          }
588
0
          if (ctx->regs) {
589
0
            int8_t reg = ctx->regs[i][0];
590
0
            if (reg != IR_REG_NONE) {
591
0
              fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
592
0
                (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
593
0
            }
594
0
          }
595
0
          fprintf(f, " = ");
596
0
        }
597
0
      }
598
0
      fprintf(f, "%s", ir_op_name[insn->op]);
599
0
      n = ir_operands_count(ctx, insn);
600
0
      if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) {
601
0
        fprintf(f, "/%d", n);
602
0
      } else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL) && n != 2) {
603
0
        fprintf(f, "/%d", n - 2);
604
0
      } else if (insn->op == IR_PHI && n != 3) {
605
0
        fprintf(f, "/%d", n - 1);
606
0
      } else if (insn->op == IR_SNAPSHOT) {
607
0
        fprintf(f, "/%d", n - 1);
608
0
      }
609
0
      first = 1;
610
0
      for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
611
0
        uint32_t opnd_kind = IR_OPND_KIND(flags, j);
612
613
0
        ref = *p;
614
0
        if (ref) {
615
0
          switch (opnd_kind) {
616
0
            case IR_OPND_DATA:
617
0
              if (IR_IS_CONST_REF(ref)) {
618
0
                fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
619
0
              } else {
620
0
                fprintf(f, "%sd_%d", first ? "(" : ", ", ref);
621
0
              }
622
0
              if (ctx->vregs && ref > 0 && ctx->vregs[ref]) {
623
0
                fprintf(f, " {R%d}", ctx->vregs[ref]);
624
0
              }
625
0
              if (ctx->regs) {
626
0
                int8_t *regs = ctx->regs[i];
627
0
                int8_t reg = regs[j];
628
0
                if (reg != IR_REG_NONE) {
629
0
                  fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[ref].type),
630
0
                    (reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
631
0
                }
632
0
              }
633
0
              first = 0;
634
0
              break;
635
0
            case IR_OPND_CONTROL:
636
0
            case IR_OPND_CONTROL_DEP:
637
0
            case IR_OPND_CONTROL_REF:
638
0
              fprintf(f, "%sl_%d", first ? "(" : ", ", ref);
639
0
              first = 0;
640
0
              break;
641
0
            case IR_OPND_STR:
642
0
              fprintf(f, "%s\"%s\"", first ? "(" : ", ", ir_get_str(ctx, ref));
643
0
              first = 0;
644
0
              break;
645
0
            case IR_OPND_PROTO:
646
0
              fprintf(f, "%sfunc ", first ? "(" : ", ");
647
0
              ir_print_proto(ctx, ref, f);
648
0
              break;
649
0
            case IR_OPND_PROB:
650
0
              if (ref == 0) {
651
0
                break;
652
0
              }
653
0
              IR_FALLTHROUGH;
654
0
            case IR_OPND_NUM:
655
0
              fprintf(f, "%s%d", first ? "(" : ", ", ref);
656
0
              first = 0;
657
0
              break;
658
0
            case IR_OPND_LABEL_REF:
659
0
              if (ref) {
660
0
                IR_ASSERT(IR_IS_CONST_REF(ref));
661
0
                fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
662
0
              }
663
0
              break;
664
0
          }
665
0
        } else if (opnd_kind == IR_OPND_NUM) {
666
0
          fprintf(f, "%s%d", first ? "(" : ", ", ref);
667
0
          first = 0;
668
0
        } else if (j != n &&
669
0
            (IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) {
670
0
          fprintf(f, "%snull", first ? "(" : ", ");
671
0
          first = 0;
672
0
        }
673
0
      }
674
0
      if (first) {
675
0
        fprintf(f, ";");
676
0
      } else if (ctx->value_params
677
0
       && insn->op == IR_PARAM
678
0
       && ctx->value_params[insn->op3 - 1].align) {
679
0
        fprintf(f, ") ByVal(%d, %d);",
680
0
          ctx->value_params[insn->op3 - 1].size,
681
0
          ctx->value_params[insn->op3 - 1].align);
682
0
      } else {
683
0
        fprintf(f, ");");
684
0
      }
685
0
      if (((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) && ctx->binding) {
686
0
        ir_ref var = ir_binding_find(ctx, i);
687
0
        if (var) {
688
0
          IR_ASSERT(var < 0);
689
0
          fprintf(f, " # BIND(0x%x);", -var);
690
0
        }
691
0
      }
692
0
      if (ctx->rules) {
693
0
        uint32_t rule = ctx->rules[i];
694
0
        uint32_t id = rule & IR_RULE_MASK;
695
696
0
        if (id < IR_LAST_OP) {
697
0
          fprintf(f, " # RULE(%s", ir_op_name[id]);
698
0
        } else {
699
0
          IR_ASSERT(id > IR_LAST_OP /*&& id < IR_LAST_RULE*/);
700
0
          fprintf(f, " # RULE(%s", ir_rule_name[id - IR_LAST_OP]);
701
0
        }
702
0
        if (rule & IR_FUSED) {
703
0
          fprintf(f, ":FUSED");
704
0
        }
705
0
        if (rule & IR_SKIPPED) {
706
0
          fprintf(f, ":SKIPPED");
707
0
        }
708
0
        if (rule & IR_SIMPLE) {
709
0
          fprintf(f, ":SIMPLE");
710
0
        }
711
0
        fprintf(f, ")");
712
0
      }
713
0
      fprintf(f, "\n");
714
0
      n = ir_insn_inputs_to_len(n);
715
0
      i += n;
716
0
      insn += n;
717
0
    }
718
719
0
    if (bb->flags & IR_BB_DESSA_MOVES) {
720
0
      ir_dump_dessa_moves(ctx, b, bb, f);
721
0
    }
722
723
0
    insn = &ctx->ir_base[bb->end];
724
0
    if (insn->op == IR_END || insn->op == IR_LOOP_END) {
725
0
      uint32_t succ;
726
727
0
      if (bb->successors_count == 1) {
728
0
        succ = ctx->cfg_edges[bb->successors];
729
0
      } else {
730
        /* END may have a fake control edge to ENTRY */
731
0
        IR_ASSERT(bb->successors_count == 2);
732
0
        succ = ctx->cfg_edges[bb->successors];
733
0
        if (ctx->ir_base[ctx->cfg_blocks[succ].start].op == IR_ENTRY) {
734
0
          succ = ctx->cfg_edges[bb->successors + 1];
735
0
#ifdef IR_DEBUG
736
0
        } else {
737
0
          uint32_t fake_succ = ctx->cfg_edges[bb->successors + 1];
738
0
          IR_ASSERT(ctx->ir_base[ctx->cfg_blocks[fake_succ].start].op == IR_ENTRY);
739
0
#endif
740
0
        }
741
0
      }
742
0
      succ = ir_skip_empty_target_blocks(ctx, succ);
743
0
      if (ctx->cfg_schedule) {
744
0
        if (_b == ctx->cfg_blocks_count || succ != ctx->cfg_schedule[_b + 1]) {
745
0
          fprintf(f, "\t# GOTO BB%d\n", succ);
746
0
        }
747
0
      } else {
748
0
        if (succ != b + 1) {
749
0
          fprintf(f, "\t# GOTO BB%d\n", succ);
750
0
        }
751
0
      }
752
0
    } else if (insn->op == IR_IF) {
753
0
      uint32_t true_block, false_block;
754
755
0
      ir_get_true_false_blocks(ctx, b, &true_block, &false_block);
756
0
      fprintf(f, "\t# IF_TRUE BB%d, IF_FALSE BB%d\n", true_block, false_block);
757
0
    } else if (insn->op == IR_SWITCH) {
758
0
      fprintf(f, "\t# SWITCH ...\n");
759
0
    }
760
0
  }
761
0
  fprintf(f, "}\n");
762
0
}