Coverage Report

Created: 2026-01-18 06:49

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