Coverage Report

Created: 2025-09-27 06:26

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