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_save.c
Line
Count
Source
1
/*
2
 * IR - Lightweight JIT Compilation Framework
3
 * (IR saver)
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
void ir_print_proto(const ir_ctx *ctx, ir_ref func_proto, FILE *f)
12
0
{
13
0
  if (func_proto) {
14
0
    const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, func_proto);
15
0
    ir_print_proto_ex(proto->flags, proto->ret_type, proto->params_count, proto->param_types, f);
16
0
  } else {
17
0
    fprintf(f, "(): int32_t");
18
0
  }
19
0
}
20
21
static void ir_print_call_conv(uint32_t flags, FILE *f)
22
0
{
23
0
  switch (flags & IR_CALL_CONV_MASK) {
24
0
    case IR_CC_BUILTIN:
25
0
      fprintf(f, " __builtin");
26
0
      break;
27
0
    case IR_CC_FASTCALL:
28
0
      fprintf(f, " __fastcall");
29
0
      break;
30
0
    case IR_CC_PRESERVE_NONE:
31
0
      fprintf(f, " __preserve_none");
32
0
      break;
33
0
#if defined(IR_TARGET_X64)
34
0
    case IR_CC_X86_64_SYSV:
35
0
      fprintf(f, " __sysv");
36
0
      break;
37
0
    case IR_CC_X86_64_MS:
38
0
      fprintf(f, " __win64");
39
0
      break;
40
#elif defined(IR_TARGET_AARCH64)
41
    case IR_CC_AARCH64_SYSV:
42
      fprintf(f, " __sysv");
43
      break;
44
    case IR_CC_AARCH64_DARWIN:
45
      fprintf(f, " __darwin");
46
      break;
47
#endif
48
0
    default:
49
0
      IR_ASSERT((flags & IR_CALL_CONV_MASK) == IR_CC_DEFAULT);
50
0
  }
51
0
}
52
53
void ir_print_type_cname(ir_type type, FILE *f)
54
0
{
55
0
  if (IR_IS_TYPE_VECTOR(type)) {
56
0
    fprintf(f, "<%s*%d>", ir_type_cname[IR_VECTOR_BASE_TYPE(type)], IR_VECTOR_LENGTH(type));
57
0
  } else {
58
0
    fprintf(f, "%s", ir_type_cname[type]);
59
0
  }
60
0
}
61
62
void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f)
63
0
{
64
0
  uint32_t j;
65
66
0
  fprintf(f, "(");
67
0
  if (params_count > 0) {
68
0
    ir_print_type_cname(param_types[0], f);
69
0
    for (j = 1; j < params_count; j++) {
70
0
      fprintf(f, ", ");
71
0
      ir_print_type_cname(param_types[j], f);
72
0
    }
73
0
    if (flags & IR_VARARG_FUNC) {
74
0
      fprintf(f, ", ...");
75
0
    }
76
0
  } else if (flags & IR_VARARG_FUNC) {
77
0
    fprintf(f, "...");
78
0
  }
79
0
  fprintf(f, "): ");
80
0
  ir_print_type_cname(ret_type, f);
81
0
  ir_print_call_conv(flags, f);
82
0
  if (flags & IR_CONST_FUNC) {
83
0
    fprintf(f, " __const");
84
0
  } else if (flags & IR_PURE_FUNC) {
85
0
    fprintf(f, " __pure");
86
0
  }
87
0
}
88
89
void ir_print_func_proto(const ir_ctx *ctx, const char *name, bool prefix, FILE *f)
90
0
{
91
0
  if (ctx->flags & IR_STATIC) {
92
0
    fprintf(f, "static ");
93
0
  }
94
0
  fprintf(f, "func %s%s(",
95
0
    prefix ? "@" : "",
96
0
    name);
97
0
  if (ctx->ir_base[2].op == IR_PARAM) {
98
0
    ir_insn *insn = &ctx->ir_base[2];
99
100
0
    ir_print_type_cname(insn->type, f);
101
0
    insn++;
102
0
    while (insn->op == IR_PARAM) {
103
0
      fprintf(f, ", ");
104
0
      ir_print_type_cname(insn->type, f);
105
0
      insn++;;
106
0
    }
107
0
    if (ctx->flags & IR_VARARG_FUNC) {
108
0
      fprintf(f, ", ...");
109
0
    }
110
0
  } else if (ctx->flags & IR_VARARG_FUNC) {
111
0
    fprintf(f, "...");
112
0
  }
113
0
  fprintf(f, "): ");
114
0
  ir_print_type_cname(ctx->ret_type != (ir_type)-1 ? ctx->ret_type : IR_VOID, f);
115
0
  ir_print_call_conv(ctx->flags, f);
116
0
  if (ctx->flags & IR_CONST_FUNC) {
117
0
    fprintf(f, " __const");
118
0
  } else if (ctx->flags & IR_PURE_FUNC) {
119
0
    fprintf(f, " __pure");
120
0
  }
121
0
}
122
123
static void ir_save_dessa_moves(const ir_ctx *ctx, int b, ir_block *bb, FILE *f)
124
0
{
125
0
  uint32_t succ;
126
0
  ir_block *succ_bb;
127
0
  ir_use_list *use_list;
128
0
  ir_ref k, i, *p, use_ref, input;
129
0
  ir_insn *use_insn;
130
131
0
  IR_ASSERT(bb->successors_count == 1);
132
0
  succ = ctx->cfg_edges[bb->successors];
133
0
  succ_bb = &ctx->cfg_blocks[succ];
134
0
  IR_ASSERT(succ_bb->predecessors_count > 1);
135
0
  use_list = &ctx->use_lists[succ_bb->start];
136
0
  k = ir_phi_input_number(ctx, succ_bb, b);
137
138
0
  for (i = use_list->count, p = &ctx->use_edges[use_list->refs]; i > 0; p++, i--) {
139
0
    use_ref = *p;
140
0
    use_insn = &ctx->ir_base[use_ref];
141
0
    if (use_insn->op == IR_PHI) {
142
0
      input = ir_insn_op(use_insn, k);
143
0
      if (IR_IS_CONST_REF(input)) {
144
0
        fprintf(f, "\t# DESSA MOV c_%d", -input);
145
0
      } else if (ctx->vregs[input] != ctx->vregs[use_ref]) {
146
0
        fprintf(f, "\t# DESSA MOV d_%d {R%d}", input, ctx->vregs[input]);
147
0
      } else {
148
0
        continue;
149
0
      }
150
0
      if (ctx->regs) {
151
0
        int8_t *regs = ctx->regs[use_ref];
152
0
        int8_t reg = regs[k];
153
0
        if (reg != IR_REG_NONE) {
154
0
          ir_dump_reg(ctx, reg, input, 0, f);
155
0
        }
156
0
      }
157
0
      fprintf(f, " -> d_%d {R%d}", use_ref, ctx->vregs[use_ref]);
158
0
      if (ctx->regs) {
159
0
        int8_t reg = ctx->regs[use_ref][0];
160
0
        if (reg != IR_REG_NONE) {
161
0
          ir_dump_reg(ctx, reg, use_ref, 1, f);
162
0
        }
163
0
      }
164
0
      fprintf(f, "\n");
165
0
    }
166
0
  }
167
0
}
168
169
void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f)
170
0
{
171
0
  ir_ref i, j, n, ref, *p;
172
0
  ir_insn *insn;
173
0
  uint32_t flags;
174
0
  bool first;
175
176
0
  fprintf(f, "{\n");
177
  /* Separate behavior to keep tests compatibility. TODO: remove the old behavior */
178
0
  if (ctx->flags2 & IR_HAS_LONG_CONSTANTS) {
179
0
    for (i = 1 - ctx->consts_count, insn = ctx->ir_base + i; i < IR_UNUSED; i++, insn++) {
180
0
      fprintf(f, "\t");
181
0
      ir_print_type_cname(insn->type, f);
182
0
      fprintf(f, " c_%d = ", -i);
183
0
      if (insn->op == IR_FUNC) {
184
0
        fprintf(f, "func %s%s",
185
0
          (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
186
0
          ir_get_str(ctx, insn->val.name));
187
0
        ir_print_proto(ctx, insn->proto, f);
188
0
      } else if (insn->op == IR_SYM) {
189
0
        fprintf(f, "sym(%s%s)",
190
0
          (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
191
0
          ir_get_str(ctx, insn->val.name));
192
0
      } else if (insn->op == IR_LABEL) {
193
0
        fprintf(f, "label(%s%s)",
194
0
          (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
195
0
          ir_get_str(ctx, insn->val.name));
196
0
      } else if (insn->op == IR_FUNC_ADDR) {
197
0
        fprintf(f, "func *");
198
0
        ir_print_const(ctx, insn, f, true);
199
0
        ir_print_proto(ctx, insn->proto, f);
200
0
      } else {
201
0
        ir_print_const(ctx, insn, f, true);
202
0
      }
203
0
      fprintf(f, ";\n");
204
0
      if (insn->op == IR_LONG_CONST) {
205
0
        i += IR_ALIGNED_SIZE(insn->long_const_size, sizeof(ir_insn)) / sizeof(ir_insn);
206
0
        insn += IR_ALIGNED_SIZE(insn->long_const_size, sizeof(ir_insn)) / sizeof(ir_insn);
207
0
      }
208
0
    }
209
0
  } else {
210
0
    for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
211
0
      fprintf(f, "\t");
212
0
      ir_print_type_cname(insn->type, f);
213
0
      fprintf(f, " c_%d = ", i);
214
0
      if (insn->op == IR_FUNC) {
215
0
        fprintf(f, "func %s%s",
216
0
          (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
217
0
          ir_get_str(ctx, insn->val.name));
218
0
        ir_print_proto(ctx, insn->proto, f);
219
0
      } else if (insn->op == IR_SYM) {
220
0
        fprintf(f, "sym(%s%s)",
221
0
          (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
222
0
          ir_get_str(ctx, insn->val.name));
223
0
      } else if (insn->op == IR_LABEL) {
224
0
        fprintf(f, "label(%s%s)",
225
0
          (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
226
0
          ir_get_str(ctx, insn->val.name));
227
0
      } else if (insn->op == IR_FUNC_ADDR) {
228
0
        fprintf(f, "func *");
229
0
        ir_print_const(ctx, insn, f, true);
230
0
        ir_print_proto(ctx, insn->proto, f);
231
0
      } else {
232
0
        ir_print_const(ctx, insn, f, true);
233
0
      }
234
0
      fprintf(f, ";\n");
235
0
    }
236
0
  }
237
238
0
  for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
239
0
    flags = ir_op_flags[insn->op];
240
241
0
    if ((save_flags & IR_SAVE_CFG)
242
0
     && ctx->cfg_map
243
0
     && (int32_t)ctx->cfg_map[i] > 0 /* the node may be scheduled incompletely */
244
0
     && ctx->cfg_blocks[ctx->cfg_map[i]].start == i) {
245
0
      uint32_t b = ctx->cfg_map[i];
246
0
      ir_block *bb = &ctx->cfg_blocks[b];
247
0
      fprintf(f, "#BB%d: end=l_%d", b, bb->end);
248
0
      if (bb->flags & IR_BB_UNREACHABLE) {
249
0
        fprintf(f, ", U");
250
0
      }
251
0
      if (bb->dom_parent > 0) {
252
0
        fprintf(f, ", idom=BB%d(%d)", bb->dom_parent, bb->dom_depth);
253
0
      }
254
0
      if (bb->loop_depth != 0) {
255
0
        if (bb->flags & IR_BB_LOOP_HEADER) {
256
0
          if (bb->loop_header > 0) {
257
0
            fprintf(f, ", loop=HDR,BB%d(%d)", bb->loop_header, bb->loop_depth);
258
0
          } else {
259
0
            IR_ASSERT(bb->loop_depth == 1);
260
0
            fprintf(f, ", loop=HDR(%d)", bb->loop_depth);
261
0
          }
262
0
        } else {
263
0
          IR_ASSERT(bb->loop_header > 0);
264
0
          fprintf(f, ", loop=BB%d(%d)", bb->loop_header, bb->loop_depth);
265
0
        }
266
0
      }
267
0
      if (bb->flags & IR_BB_IRREDUCIBLE_LOOP) {
268
0
        fprintf(f, ", IRREDUCIBLE");
269
0
      }
270
0
      if (bb->flags & IR_BB_IRREDUCIBLE_ENTRY) {
271
0
        fprintf(f, ", IRREDUCIBLE_ENTRY");
272
0
      }
273
0
      if (bb->predecessors_count) {
274
0
        uint32_t i;
275
276
0
        fprintf(f, ", pred(%d)=[BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
277
0
        for (i = 1; i < bb->predecessors_count; i++) {
278
0
          fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
279
0
        }
280
0
        fprintf(f, "]");
281
0
      }
282
0
      if (bb->successors_count) {
283
0
        uint32_t i;
284
285
0
        fprintf(f, ", succ(%d)=[BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
286
0
        for (i = 1; i < bb->successors_count; i++) {
287
0
          fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
288
0
        }
289
0
        fprintf(f, "]");
290
0
      }
291
0
      fprintf(f, "\n");
292
0
    }
293
294
0
    if (flags & IR_OP_FLAG_CONTROL) {
295
0
      if (!(flags & IR_OP_FLAG_MEM) || insn->type == IR_VOID) {
296
0
        fprintf(f, "\tl_%d = ", i);
297
0
      } else {
298
0
        fprintf(f, "\t");
299
0
        ir_print_type_cname(insn->type, f);
300
0
        fprintf(f, " d_%d", i);
301
0
        if (save_flags & IR_SAVE_REGS) {
302
0
          if (ctx->vregs && ctx->vregs[i] > 0) {
303
0
            fprintf(f, " {R%d}", ctx->vregs[i]);
304
0
          }
305
0
          if (ctx->regs) {
306
0
            int8_t reg = ctx->regs[i][0];
307
0
            if (reg != IR_REG_NONE) {
308
0
              ir_dump_reg(ctx, reg, i, 1, f);
309
0
            }
310
0
          }
311
0
        }
312
0
        fprintf(f, ", l_%d = ", i);
313
0
      }
314
0
    } else {
315
0
      fprintf(f, "\t");
316
0
      if (flags & IR_OP_FLAG_DATA) {
317
0
        ir_print_type_cname(insn->type, f);
318
0
        fprintf(f, " d_%d", i);
319
0
        if (save_flags & IR_SAVE_REGS) {
320
0
          if (ctx->vregs && ctx->vregs[i] > 0) {
321
0
            fprintf(f, " {R%d}", ctx->vregs[i]);
322
0
          }
323
0
          if (ctx->regs) {
324
0
            int8_t reg = ctx->regs[i][0];
325
0
            if (reg != IR_REG_NONE) {
326
0
              ir_dump_reg(ctx, reg, i, 1, f);
327
0
            }
328
0
          }
329
0
        }
330
0
        fprintf(f, " = ");
331
0
      }
332
0
    }
333
0
    fprintf(f, "%s", ir_op_name[insn->op]);
334
0
    n = ir_operands_count(ctx, insn);
335
0
    if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) {
336
0
      fprintf(f, "/%d", n);
337
0
    } else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL || insn->op == IR_ASM) && n != 2) {
338
0
      fprintf(f, "/%d", n - 2);
339
0
    } else if (insn->op == IR_PHI && n != 3) {
340
0
      fprintf(f, "/%d", n - 1);
341
0
    } else if (insn->op == IR_SNAPSHOT) {
342
0
      fprintf(f, "/%d", n - 1);
343
0
    }
344
0
    first = 1;
345
0
    for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
346
0
      uint32_t opnd_kind = IR_OPND_KIND(flags, j);
347
348
0
      ref = *p;
349
0
      if (ref) {
350
0
        switch (opnd_kind) {
351
0
          case IR_OPND_DATA:
352
0
            if (IR_IS_CONST_REF(ref)) {
353
0
              fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
354
0
            } else {
355
0
              fprintf(f, "%sd_%d", first ? "(" : ", ", ref);
356
0
            }
357
0
            if (save_flags & IR_SAVE_REGS) {
358
0
              if (ctx->vregs && ref > 0 && ctx->vregs[ref] > 0) {
359
0
                fprintf(f, " {R%d}", ctx->vregs[ref]);
360
0
              }
361
0
              if (ctx->regs) {
362
0
                int8_t *regs = ctx->regs[i];
363
0
                int8_t reg = regs[j];
364
0
                if (reg != IR_REG_NONE) {
365
0
                  ir_dump_reg(ctx, reg, ref, 0, f);
366
0
                }
367
0
              }
368
0
            }
369
0
            first = 0;
370
0
            break;
371
0
          case IR_OPND_CONTROL:
372
0
          case IR_OPND_CONTROL_DEP:
373
0
          case IR_OPND_CONTROL_REF:
374
0
          case IR_OPND_CONTROL_GUARD:
375
0
            fprintf(f, "%sl_%d", first ? "(" : ", ", ref);
376
0
            first = 0;
377
0
            break;
378
0
          case IR_OPND_STR:
379
0
            fprintf(f, "%s\"%s\"", first ? "(" : ", ", ir_get_str(ctx, ref));
380
0
            first = 0;
381
0
            break;
382
0
          case IR_OPND_PROTO:
383
0
            fprintf(f, "%sfunc ", first ? "(" : ", ");
384
0
            ir_print_proto(ctx, ref, f);
385
0
            break;
386
0
          case IR_OPND_OPT:
387
0
            if (ref == 0) {
388
0
              break;
389
0
            }
390
0
            IR_FALLTHROUGH;
391
0
          case IR_OPND_NUM:
392
0
            fprintf(f, "%s%d", first ? "(" : ", ", ref);
393
0
            first = 0;
394
0
            break;
395
0
          case IR_OPND_LABEL_REF:
396
0
            if (ref) {
397
0
              IR_ASSERT(IR_IS_CONST_REF(ref));
398
0
              fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
399
0
              first = 0;
400
0
            }
401
0
            break;
402
0
        }
403
0
      } else if (opnd_kind == IR_OPND_NUM) {
404
0
        fprintf(f, "%s%d", first ? "(" : ", ", ref);
405
0
        first = 0;
406
0
      } else if (opnd_kind == IR_OPND_CONTROL_GUARD) {
407
        /* skip */
408
0
      } else if (j != n &&
409
0
          (IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) {
410
0
        fprintf(f, "%snull", first ? "(" : ", ");
411
0
        first = 0;
412
0
      }
413
0
    }
414
0
    if (first) {
415
0
      fprintf(f, ";");
416
0
    } else if (ctx->value_params
417
0
     && insn->op == IR_PARAM
418
0
     && ctx->value_params[insn->op3 - 1].align) {
419
0
      fprintf(f, ") ByVal(%d, %d);",
420
0
        ctx->value_params[insn->op3 - 1].size,
421
0
        ctx->value_params[insn->op3 - 1].align);
422
0
    } else {
423
0
      fprintf(f, ");");
424
0
    }
425
0
    first = 1;
426
0
    if (((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) && ctx->binding) {
427
0
      ir_ref var = ir_binding_find(ctx, i);
428
0
      if (var) {
429
0
        IR_ASSERT(var < 0);
430
0
        fprintf(f, " # BIND(0x%x);", -var);
431
0
        first = 0;
432
0
      }
433
0
    }
434
435
0
    if ((save_flags & IR_SAVE_CFG_MAP)
436
0
     && ctx->cfg_map
437
0
     && ctx->cfg_map[i] > 0) { /* the node may be scheduled incompletely */
438
0
      if (first) {
439
0
        fprintf(f, " #");
440
0
        first = 0;
441
0
      }
442
0
      fprintf(f, " BLOCK=BB%d;", ctx->cfg_map[i]);
443
0
    }
444
445
0
    if ((save_flags & IR_SAVE_RULES)
446
0
     && ctx->rules) {
447
0
      uint32_t rule = ctx->rules[i];
448
0
      uint32_t id = rule & IR_RULE_MASK;
449
450
0
      if (first) {
451
0
        fprintf(f, " #");
452
0
        first = 0;
453
0
      }
454
0
      if (id < IR_LAST_OP) {
455
0
        fprintf(f, " RULE(%s", ir_op_name[id]);
456
0
      } else {
457
0
        IR_ASSERT(id > IR_LAST_OP /*&& id < IR_LAST_RULE*/);
458
0
        fprintf(f, " RULE(%s", ir_rule_name[id - IR_LAST_OP]);
459
0
      }
460
0
      if (rule & IR_FUSED) {
461
0
        fprintf(f, ":FUSED");
462
0
      }
463
0
      if (rule & IR_SKIPPED) {
464
0
        fprintf(f, ":SKIPPED");
465
0
      }
466
0
      if (rule & IR_SIMPLE) {
467
0
        fprintf(f, ":SIMPLE");
468
0
      }
469
#if IR_X86_I64
470
      if (rule & IR_TWO_REGS) {
471
        fprintf(f, ":TWO_REGS");
472
      }
473
#endif
474
0
      fprintf(f, ");");
475
0
    }
476
477
0
    if ((save_flags & IR_SAVE_USE_LISTS)
478
0
     && ctx->use_lists
479
0
     && ctx->use_lists[i].count) {
480
0
      ir_use_list *use_list = &ctx->use_lists[i];
481
0
      ir_ref n = use_list->count;
482
0
      ir_ref *p = ctx->use_edges + use_list->refs;
483
484
0
      if (first) {
485
0
        fprintf(f, " #");
486
0
        first = 0;
487
0
      }
488
0
      fprintf(f, " USE_LIST(%d)=[%05d", n, *p);
489
0
      for (p++, n--; n; p++, n--) {
490
0
        fprintf(f, ", %05d", *p);
491
0
      }
492
0
      fprintf(f, "];");
493
0
    }
494
0
    fprintf(f, "\n");
495
496
0
    if ((save_flags & (IR_SAVE_CFG|IR_SAVE_REGS)) == (IR_SAVE_CFG|IR_SAVE_REGS)
497
0
     && ctx->cfg_map
498
0
     && ctx->cfg_map[i]
499
0
     && ctx->cfg_blocks[ctx->cfg_map[i]].end == i) {
500
0
      uint32_t b = ctx->cfg_map[i];
501
0
      ir_block *bb = &ctx->cfg_blocks[b];
502
0
      if (bb->flags & IR_BB_DESSA_MOVES) {
503
0
        ir_save_dessa_moves(ctx, b, bb, f);
504
0
      }
505
0
    }
506
507
0
    n = ir_insn_inputs_to_len(n);
508
0
    i += n;
509
0
    insn += n;
510
0
  }
511
0
  fprintf(f, "}\n");
512
0
}