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