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