Coverage Report

Created: 2026-02-14 06:52

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
 * 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_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_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f)
54
0
{
55
0
  uint32_t j;
56
57
0
  fprintf(f, "(");
58
0
  if (params_count > 0) {
59
0
    fprintf(f, "%s", ir_type_cname[param_types[0]]);
60
0
    for (j = 1; j < params_count; j++) {
61
0
      fprintf(f, ", %s", ir_type_cname[param_types[j]]);
62
0
    }
63
0
    if (flags & IR_VARARG_FUNC) {
64
0
      fprintf(f, ", ...");
65
0
    }
66
0
  } else if (flags & IR_VARARG_FUNC) {
67
0
    fprintf(f, "...");
68
0
  }
69
0
  fprintf(f, "): %s", ir_type_cname[ret_type]);
70
0
  ir_print_call_conv(flags, f);
71
0
  if (flags & IR_CONST_FUNC) {
72
0
    fprintf(f, " __const");
73
0
  } else if (flags & IR_PURE_FUNC) {
74
0
    fprintf(f, " __pure");
75
0
  }
76
0
}
77
78
void ir_print_func_proto(const ir_ctx *ctx, const char *name, bool prefix, FILE *f)
79
0
{
80
0
  if (ctx->flags & IR_STATIC) {
81
0
    fprintf(f, "static ");
82
0
  }
83
0
  fprintf(f, "func %s%s(",
84
0
    prefix ? "@" : "",
85
0
    name);
86
0
  if (ctx->ir_base[2].op == IR_PARAM) {
87
0
    ir_insn *insn = &ctx->ir_base[2];
88
89
0
    fprintf(f, "%s", ir_type_cname[insn->type]);
90
0
    insn++;
91
0
    while (insn->op == IR_PARAM) {
92
0
      fprintf(f, ", %s", ir_type_cname[insn->type]);
93
0
      insn++;;
94
0
    }
95
0
    if (ctx->flags & IR_VARARG_FUNC) {
96
0
      fprintf(f, ", ...");
97
0
    }
98
0
  } else if (ctx->flags & IR_VARARG_FUNC) {
99
0
    fprintf(f, "...");
100
0
  }
101
0
  fprintf(f, "): %s", ir_type_cname[ctx->ret_type != (ir_type)-1 ? ctx->ret_type : IR_VOID]);
102
0
  ir_print_call_conv(ctx->flags, f);
103
0
  if (ctx->flags & IR_CONST_FUNC) {
104
0
    fprintf(f, " __const");
105
0
  } else if (ctx->flags & IR_PURE_FUNC) {
106
0
    fprintf(f, " __pure");
107
0
  }
108
0
}
109
110
static void ir_save_dessa_moves(const ir_ctx *ctx, int b, ir_block *bb, FILE *f)
111
0
{
112
0
  uint32_t succ;
113
0
  ir_block *succ_bb;
114
0
  ir_use_list *use_list;
115
0
  ir_ref k, i, *p, use_ref, input;
116
0
  ir_insn *use_insn;
117
118
0
  IR_ASSERT(bb->successors_count == 1);
119
0
  succ = ctx->cfg_edges[bb->successors];
120
0
  succ_bb = &ctx->cfg_blocks[succ];
121
0
  IR_ASSERT(succ_bb->predecessors_count > 1);
122
0
  use_list = &ctx->use_lists[succ_bb->start];
123
0
  k = ir_phi_input_number(ctx, succ_bb, b);
124
125
0
  for (i = use_list->count, p = &ctx->use_edges[use_list->refs]; i > 0; p++, i--) {
126
0
    use_ref = *p;
127
0
    use_insn = &ctx->ir_base[use_ref];
128
0
    if (use_insn->op == IR_PHI) {
129
0
      input = ir_insn_op(use_insn, k);
130
0
      if (IR_IS_CONST_REF(input)) {
131
0
        fprintf(f, "\t# DESSA MOV c_%d", -input);
132
0
      } else if (ctx->vregs[input] != ctx->vregs[use_ref]) {
133
0
        fprintf(f, "\t# DESSA MOV d_%d {R%d}", input, ctx->vregs[input]);
134
0
      } else {
135
0
        continue;
136
0
      }
137
0
      if (ctx->regs) {
138
0
        int8_t *regs = ctx->regs[use_ref];
139
0
        int8_t reg = regs[k];
140
0
        if (reg != IR_REG_NONE) {
141
0
          fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[input].type),
142
0
            (reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
143
0
        }
144
0
      }
145
0
      fprintf(f, " -> d_%d {R%d}", use_ref, ctx->vregs[use_ref]);
146
0
      if (ctx->regs) {
147
0
        int8_t reg = ctx->regs[use_ref][0];
148
0
        if (reg != IR_REG_NONE) {
149
0
          fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[use_ref].type),
150
0
            (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
151
0
        }
152
0
      }
153
0
      fprintf(f, "\n");
154
0
    }
155
0
  }
156
0
}
157
158
void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f)
159
0
{
160
0
  ir_ref i, j, n, ref, *p;
161
0
  ir_insn *insn;
162
0
  uint32_t flags;
163
0
  bool first;
164
165
0
  fprintf(f, "{\n");
166
0
  for (i = IR_UNUSED + 1, insn = ctx->ir_base - i; i < ctx->consts_count; i++, insn--) {
167
0
    fprintf(f, "\t%s c_%d = ", ir_type_cname[insn->type], i);
168
0
    if (insn->op == IR_FUNC) {
169
0
      fprintf(f, "func %s%s",
170
0
        (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
171
0
        ir_get_str(ctx, insn->val.name));
172
0
      ir_print_proto(ctx, insn->proto, f);
173
0
    } else if (insn->op == IR_SYM) {
174
0
      fprintf(f, "sym(%s%s)",
175
0
        (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
176
0
        ir_get_str(ctx, insn->val.name));
177
0
    } else if (insn->op == IR_LABEL) {
178
0
      fprintf(f, "label(%s%s)",
179
0
        (save_flags & IR_SAVE_SAFE_NAMES) ? "@" : "",
180
0
        ir_get_str(ctx, insn->val.name));
181
0
    } else if (insn->op == IR_FUNC_ADDR) {
182
0
      fprintf(f, "func *");
183
0
      ir_print_const(ctx, insn, f, true);
184
0
      ir_print_proto(ctx, insn->proto, f);
185
0
    } else {
186
0
      ir_print_const(ctx, insn, f, true);
187
0
    }
188
0
    fprintf(f, ";\n");
189
0
  }
190
191
0
  for (i = IR_UNUSED + 1, insn = ctx->ir_base + i; i < ctx->insns_count;) {
192
0
    flags = ir_op_flags[insn->op];
193
194
0
    if ((save_flags & IR_SAVE_CFG)
195
0
     && ctx->cfg_map
196
0
     && (int32_t)ctx->cfg_map[i] > 0 /* the node may be scheduled incompletely */
197
0
     && ctx->cfg_blocks[ctx->cfg_map[i]].start == i) {
198
0
      uint32_t b = ctx->cfg_map[i];
199
0
      ir_block *bb = &ctx->cfg_blocks[b];
200
0
      fprintf(f, "#BB%d: end=l_%d", b, bb->end);
201
0
      if (bb->flags & IR_BB_UNREACHABLE) {
202
0
        fprintf(f, ", U");
203
0
      }
204
0
      if (bb->dom_parent > 0) {
205
0
        fprintf(f, ", idom=BB%d(%d)", bb->dom_parent, bb->dom_depth);
206
0
      }
207
0
      if (bb->loop_depth != 0) {
208
0
        if (bb->flags & IR_BB_LOOP_HEADER) {
209
0
          if (bb->loop_header > 0) {
210
0
            fprintf(f, ", loop=HDR,BB%d(%d)", bb->loop_header, bb->loop_depth);
211
0
          } else {
212
0
            IR_ASSERT(bb->loop_depth == 1);
213
0
            fprintf(f, ", loop=HDR(%d)", bb->loop_depth);
214
0
          }
215
0
        } else {
216
0
          IR_ASSERT(bb->loop_header > 0);
217
0
          fprintf(f, ", loop=BB%d(%d)", bb->loop_header, bb->loop_depth);
218
0
        }
219
0
      }
220
0
      if (bb->flags & IR_BB_IRREDUCIBLE_LOOP) {
221
0
        fprintf(f, ", IRREDUCIBLE");
222
0
      }
223
0
      if (bb->predecessors_count) {
224
0
        uint32_t i;
225
226
0
        fprintf(f, ", pred(%d)=[BB%d", bb->predecessors_count, ctx->cfg_edges[bb->predecessors]);
227
0
        for (i = 1; i < bb->predecessors_count; i++) {
228
0
          fprintf(f, ", BB%d", ctx->cfg_edges[bb->predecessors + i]);
229
0
        }
230
0
        fprintf(f, "]");
231
0
      }
232
0
      if (bb->successors_count) {
233
0
        uint32_t i;
234
235
0
        fprintf(f, ", succ(%d)=[BB%d", bb->successors_count, ctx->cfg_edges[bb->successors]);
236
0
        for (i = 1; i < bb->successors_count; i++) {
237
0
          fprintf(f, ", BB%d", ctx->cfg_edges[bb->successors + i]);
238
0
        }
239
0
        fprintf(f, "]");
240
0
      }
241
0
      fprintf(f, "\n");
242
0
    }
243
244
0
    if (flags & IR_OP_FLAG_CONTROL) {
245
0
      if (!(flags & IR_OP_FLAG_MEM) || insn->type == IR_VOID) {
246
0
        fprintf(f, "\tl_%d = ", i);
247
0
      } else {
248
0
        fprintf(f, "\t%s d_%d", ir_type_cname[insn->type], i);
249
0
        if (save_flags & IR_SAVE_REGS) {
250
0
          if (ctx->vregs && ctx->vregs[i]) {
251
0
            fprintf(f, " {R%d}", ctx->vregs[i]);
252
0
          }
253
0
          if (ctx->regs) {
254
0
            int8_t reg = ctx->regs[i][0];
255
0
            if (reg != IR_REG_NONE) {
256
0
              fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
257
0
                (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
258
0
            }
259
0
          }
260
0
        }
261
0
        fprintf(f, ", l_%d = ", i);
262
0
      }
263
0
    } else {
264
0
      fprintf(f, "\t");
265
0
      if (flags & IR_OP_FLAG_DATA) {
266
0
        fprintf(f, "%s d_%d", ir_type_cname[insn->type], i);
267
0
        if (save_flags & IR_SAVE_REGS) {
268
0
          if (ctx->vregs && ctx->vregs[i]) {
269
0
            fprintf(f, " {R%d}", ctx->vregs[i]);
270
0
          }
271
0
          if (ctx->regs) {
272
0
            int8_t reg = ctx->regs[i][0];
273
0
            if (reg != IR_REG_NONE) {
274
0
              fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), insn->type),
275
0
                (reg & (IR_REG_SPILL_STORE|IR_REG_SPILL_SPECIAL)) ? ":store" : "");
276
0
            }
277
0
          }
278
0
        }
279
0
        fprintf(f, " = ");
280
0
      }
281
0
    }
282
0
    fprintf(f, "%s", ir_op_name[insn->op]);
283
0
    n = ir_operands_count(ctx, insn);
284
0
    if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) {
285
0
      fprintf(f, "/%d", n);
286
0
    } else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL) && n != 2) {
287
0
      fprintf(f, "/%d", n - 2);
288
0
    } else if (insn->op == IR_PHI && n != 3) {
289
0
      fprintf(f, "/%d", n - 1);
290
0
    } else if (insn->op == IR_SNAPSHOT) {
291
0
      fprintf(f, "/%d", n - 1);
292
0
    }
293
0
    first = 1;
294
0
    for (j = 1, p = insn->ops + 1; j <= n; j++, p++) {
295
0
      uint32_t opnd_kind = IR_OPND_KIND(flags, j);
296
297
0
      ref = *p;
298
0
      if (ref) {
299
0
        switch (opnd_kind) {
300
0
          case IR_OPND_DATA:
301
0
            if (IR_IS_CONST_REF(ref)) {
302
0
              fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
303
0
            } else {
304
0
              fprintf(f, "%sd_%d", first ? "(" : ", ", ref);
305
0
            }
306
0
            if (save_flags & IR_SAVE_REGS) {
307
0
              if (ctx->vregs && ref > 0 && ctx->vregs[ref]) {
308
0
                fprintf(f, " {R%d}", ctx->vregs[ref]);
309
0
              }
310
0
              if (ctx->regs) {
311
0
                int8_t *regs = ctx->regs[i];
312
0
                int8_t reg = regs[j];
313
0
                if (reg != IR_REG_NONE) {
314
0
                  fprintf(f, " {%%%s%s}", ir_reg_name(IR_REG_NUM(reg), ctx->ir_base[ref].type),
315
0
                    (reg & (IR_REG_SPILL_LOAD|IR_REG_SPILL_SPECIAL)) ? ":load" : "");
316
0
                }
317
0
              }
318
0
            }
319
0
            first = 0;
320
0
            break;
321
0
          case IR_OPND_CONTROL:
322
0
          case IR_OPND_CONTROL_DEP:
323
0
          case IR_OPND_CONTROL_REF:
324
0
            fprintf(f, "%sl_%d", first ? "(" : ", ", ref);
325
0
            first = 0;
326
0
            break;
327
0
          case IR_OPND_STR:
328
0
            fprintf(f, "%s\"%s\"", first ? "(" : ", ", ir_get_str(ctx, ref));
329
0
            first = 0;
330
0
            break;
331
0
          case IR_OPND_PROTO:
332
0
            fprintf(f, "%sfunc ", first ? "(" : ", ");
333
0
            ir_print_proto(ctx, ref, f);
334
0
            break;
335
0
          case IR_OPND_PROB:
336
0
            if (ref == 0) {
337
0
              break;
338
0
            }
339
0
            IR_FALLTHROUGH;
340
0
          case IR_OPND_NUM:
341
0
            fprintf(f, "%s%d", first ? "(" : ", ", ref);
342
0
            first = 0;
343
0
            break;
344
0
          case IR_OPND_LABEL_REF:
345
0
            if (ref) {
346
0
              IR_ASSERT(IR_IS_CONST_REF(ref));
347
0
              fprintf(f, "%sc_%d", first ? "(" : ", ", -ref);
348
0
              first = 0;
349
0
            }
350
0
            break;
351
0
        }
352
0
      } else if (opnd_kind == IR_OPND_NUM) {
353
0
        fprintf(f, "%s%d", first ? "(" : ", ", ref);
354
0
        first = 0;
355
0
      } else if (j != n &&
356
0
          (IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) {
357
0
        fprintf(f, "%snull", first ? "(" : ", ");
358
0
        first = 0;
359
0
      }
360
0
    }
361
0
    if (first) {
362
0
      fprintf(f, ";");
363
0
    } else if (ctx->value_params
364
0
     && insn->op == IR_PARAM
365
0
     && ctx->value_params[insn->op3 - 1].align) {
366
0
      fprintf(f, ") ByVal(%d, %d);",
367
0
        ctx->value_params[insn->op3 - 1].size,
368
0
        ctx->value_params[insn->op3 - 1].align);
369
0
    } else {
370
0
      fprintf(f, ");");
371
0
    }
372
0
    first = 1;
373
0
    if (((flags & IR_OP_FLAG_DATA) || ((flags & IR_OP_FLAG_MEM) && insn->type != IR_VOID)) && ctx->binding) {
374
0
      ir_ref var = ir_binding_find(ctx, i);
375
0
      if (var) {
376
0
        IR_ASSERT(var < 0);
377
0
        fprintf(f, " # BIND(0x%x);", -var);
378
0
        first = 0;
379
0
      }
380
0
    }
381
382
0
    if ((save_flags & IR_SAVE_CFG_MAP)
383
0
     && ctx->cfg_map
384
0
     && ctx->cfg_map[i] > 0) { /* the node may be scheduled incompletely */
385
0
      if (first) {
386
0
        fprintf(f, " #");
387
0
        first = 0;
388
0
      }
389
0
      fprintf(f, " BLOCK=BB%d;", ctx->cfg_map[i]);
390
0
    }
391
392
0
    if ((save_flags & IR_SAVE_RULES)
393
0
     && ctx->rules) {
394
0
      uint32_t rule = ctx->rules[i];
395
0
      uint32_t id = rule & IR_RULE_MASK;
396
397
0
      if (first) {
398
0
        fprintf(f, " #");
399
0
        first = 0;
400
0
      }
401
0
      if (id < IR_LAST_OP) {
402
0
        fprintf(f, " RULE(%s", ir_op_name[id]);
403
0
      } else {
404
0
        IR_ASSERT(id > IR_LAST_OP /*&& id < IR_LAST_RULE*/);
405
0
        fprintf(f, " RULE(%s", ir_rule_name[id - IR_LAST_OP]);
406
0
      }
407
0
      if (rule & IR_FUSED) {
408
0
        fprintf(f, ":FUSED");
409
0
      }
410
0
      if (rule & IR_SKIPPED) {
411
0
        fprintf(f, ":SKIPPED");
412
0
      }
413
0
      if (rule & IR_SIMPLE) {
414
0
        fprintf(f, ":SIMPLE");
415
0
      }
416
0
      fprintf(f, ");");
417
0
    }
418
419
0
    if ((save_flags & IR_SAVE_USE_LISTS)
420
0
     && ctx->use_lists
421
0
     && ctx->use_lists[i].count) {
422
0
      ir_use_list *use_list = &ctx->use_lists[i];
423
0
      ir_ref n = use_list->count;
424
0
      ir_ref *p = ctx->use_edges + use_list->refs;
425
426
0
      if (first) {
427
0
        fprintf(f, " #");
428
0
        first = 0;
429
0
      }
430
0
      fprintf(f, " USE_LIST(%d)=[%05d", n, *p);
431
0
      for (p++, n--; n; p++, n--) {
432
0
        fprintf(f, ", %05d", *p);
433
0
      }
434
0
      fprintf(f, "];");
435
0
    }
436
0
    fprintf(f, "\n");
437
438
0
    if ((save_flags & (IR_SAVE_CFG|IR_SAVE_REGS)) == (IR_SAVE_CFG|IR_SAVE_REGS)
439
0
     && ctx->cfg_map
440
0
     && ctx->cfg_map[i]
441
0
     && ctx->cfg_blocks[ctx->cfg_map[i]].end == i) {
442
0
      uint32_t b = ctx->cfg_map[i];
443
0
      ir_block *bb = &ctx->cfg_blocks[b];
444
0
      if (bb->flags & IR_BB_DESSA_MOVES) {
445
0
        ir_save_dessa_moves(ctx, b, bb, f);
446
0
      }
447
0
    }
448
449
0
    n = ir_insn_inputs_to_len(n);
450
0
    i += n;
451
0
    insn += n;
452
0
  }
453
0
  fprintf(f, "}\n");
454
0
}