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_emit.c
Line
Count
Source
1
/*
2
 * IR - Lightweight JIT Compilation Framework
3
 * (Native code generator based on DynAsm)
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
#ifndef _GNU_SOURCE
9
# define _GNU_SOURCE
10
#endif
11
12
#include "ir.h"
13
14
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
15
# include "ir_x86.h"
16
#elif defined(IR_TARGET_AARCH64)
17
# include "ir_aarch64.h"
18
#else
19
# error "Unknown IR target"
20
#endif
21
22
#include "ir_private.h"
23
#ifndef _WIN32
24
# include <dlfcn.h>
25
#else
26
# define WIN32_LEAN_AND_MEAN
27
# include <windows.h>
28
# include <psapi.h>
29
#endif
30
31
#if defined(__linux__) || defined(__sun)
32
# include <alloca.h>
33
#endif
34
35
#define DASM_M_GROW(ctx, t, p, sz, need) \
36
66.1k
  do { \
37
66.1k
    size_t _sz = (sz), _need = (need); \
38
66.1k
    if (_sz < _need) { \
39
66.1k
      size_t _limit = sizeof(t) * DASM_SEC2POS(1); \
40
66.1k
      if (_need > _limit) { \
41
0
        Dst_REF->status = DASM_S_NOMEM; \
42
0
        return; \
43
0
      } \
44
66.1k
      if (_sz < 16) _sz = 16; \
45
254k
      while (_sz < _need) _sz += _sz; \
46
66.1k
      if (_sz > _limit) _sz = _limit; \
47
66.1k
      (p) = (t *)ir_mem_realloc((p), _sz); \
48
66.1k
      (sz) = _sz; \
49
66.1k
    } \
50
66.1k
  } while(0)
51
52
40.2k
#define DASM_M_FREE(ctx, p, sz) ir_mem_free(p)
53
54
#ifdef IR_DEBUG
55
# define DASM_CHECKS
56
0
# define DASM_ABORT IR_ASSERT(0)
57
#endif
58
59
typedef struct _ir_copy {
60
  ir_type type;
61
  ir_reg  from;
62
  ir_reg  to;
63
} ir_copy;
64
65
#define IR_U32_HI IR_U64 /* type for dessa copy of the high 32-bit value of constant */
66
67
typedef struct _ir_dessa_copy {
68
  ir_type type;
69
  int32_t from; /* negative - constant ref, [0..IR_REG_NUM) - CPU reg, [IR_REG_NUM...) - memory slot */
70
  int32_t to;   /* [0..IR_REG_NUM) - CPU reg, [IR_REG_NUM...) - memory slot  */
71
} ir_dessa_copy;
72
73
const ir_proto_t *ir_call_proto(const ir_ctx *ctx, const ir_insn *insn)
74
187k
{
75
187k
  if (IR_IS_CONST_REF(insn->op2)) {
76
176k
    const ir_insn *func = &ctx->ir_base[insn->op2];
77
78
176k
    if (func->op == IR_FUNC || func->op == IR_FUNC_ADDR) {
79
176k
      if (func->proto) {
80
44
        return (const ir_proto_t *)ir_get_str(ctx, func->proto);
81
44
      }
82
176k
    }
83
176k
  } else if (ctx->ir_base[insn->op2].op == IR_PROTO) {
84
0
    return (const ir_proto_t *)ir_get_str(ctx, ctx->ir_base[insn->op2].op2);
85
0
  }
86
187k
  return NULL;
87
187k
}
88
89
IR_ALWAYS_INLINE uint32_t ir_rule(const ir_ctx *ctx, ir_ref ref)
90
1.45M
{
91
1.45M
  IR_ASSERT(!IR_IS_CONST_REF(ref));
92
1.45M
  return ctx->rules[ref];
93
1.45M
}
94
95
IR_ALWAYS_INLINE bool ir_in_same_block(ir_ctx *ctx, ir_ref ref)
96
419k
{
97
419k
  return ref > ctx->bb_start;
98
419k
}
99
100
101
static ir_reg ir_get_param_reg(const ir_ctx *ctx, ir_ref ref)
102
44
{
103
44
  ir_use_list *use_list = &ctx->use_lists[1];
104
44
  int i;
105
44
  ir_ref use, *p;
106
44
  ir_insn *insn;
107
44
  int int_param = 0;
108
44
  int fp_param = 0;
109
#if IR_SIMD && defined(IR_TARGET_X86)
110
  int vector_param = 0;
111
#endif
112
44
  const ir_call_conv_dsc *cc = ir_get_call_conv_dsc(ctx->flags);
113
114
64
  for (i = use_list->count, p = &ctx->use_edges[use_list->refs]; i > 0; p++, i--) {
115
64
    use = *p;
116
64
    insn = &ctx->ir_base[use];
117
64
    if (insn->op == IR_PARAM) {
118
64
      if (IR_IS_TYPE_INT(insn->type)) {
119
64
        if (use == ref) {
120
44
          if (ctx->value_params && ctx->value_params[insn->op3 - 1].align && cc->pass_struct_by_val) {
121
            /* struct passed by value on stack */
122
0
            return IR_REG_NONE;
123
44
          } else if (int_param < cc->int_param_regs_count) {
124
#if IR_X86_I64
125
            if (insn->type == IR_I64 || insn->type == IR_U64) {
126
              return IR_REG_NONE;
127
            }
128
#endif
129
44
            return cc->int_param_regs[int_param];
130
44
          } else {
131
0
            return IR_REG_NONE;
132
0
          }
133
44
        } else if (ctx->value_params && ctx->value_params[insn->op3 - 1].align && cc->pass_struct_by_val) {
134
          /* struct passed by value on stack */
135
0
          continue;
136
0
        }
137
20
        int_param++;
138
20
        if (cc->shadow_param_regs) {
139
0
          fp_param++;
140
0
        }
141
#if IR_X86_I64
142
        if (insn->type == IR_I64 || insn->type == IR_U64) {
143
          int_param++;
144
          if (cc->shadow_param_regs) {
145
            fp_param++;
146
          }
147
        }
148
#endif
149
#if IR_SIMD && defined(IR_TARGET_X86)
150
      } else if (IR_IS_TYPE_VECTOR(insn->type)) {
151
        if (use == ref) {
152
          if (vector_param < cc->vector_param_regs_count) {
153
            return cc->vector_param_regs[vector_param];
154
          } else {
155
            return IR_REG_NONE;
156
          }
157
        }
158
        vector_param++;
159
#endif
160
20
      } else {
161
0
        IR_ASSERT(IR_IS_TYPE_FP(insn->type) || IR_IS_TYPE_VECTOR(insn->type));
162
0
        if (use == ref) {
163
0
          if (fp_param < cc->fp_param_regs_count) {
164
0
            return cc->fp_param_regs[fp_param];
165
0
          } else {
166
0
            return IR_REG_NONE;
167
0
          }
168
0
        }
169
0
        fp_param++;
170
0
        if (cc->shadow_param_regs) {
171
0
          int_param++;
172
0
        }
173
0
      }
174
64
    }
175
64
  }
176
0
  return IR_REG_NONE;
177
44
}
178
179
/* This remaps spill slot of the VAR, where PARAM is stored, to the PARAMs slot.
180
 * This eliminates copying, but more important, this fixes va_start() compilation
181
 * on systems, where it's defined through address calculation macro (e.g. MSVC x86).
182
 */
183
static void ir_remap_param_spill_slot(ir_ctx *ctx, ir_ref param, int32_t spill_slot)
184
0
{
185
0
  ir_use_list *param_use_list = &ctx->use_lists[param];
186
187
0
  if (param_use_list->count >= 1) {
188
0
    ir_ref param_store = ctx->use_edges[param_use_list->refs];
189
0
    ir_insn *param_store_insn = &ctx->ir_base[param_store];
190
191
0
    if (param_store_insn->op == IR_VSTORE && (ctx->rules[param_store] & IR_SKIPPED)) {
192
0
      ir_ref var = param_store_insn->op2;
193
194
0
      IR_ASSERT(param_store_insn->op3 == param);
195
0
      IR_ASSERT(ctx->ir_base[var].op == IR_VAR);
196
197
      /* Remap all related VADDR and VLOAD */
198
0
      ir_use_list *var_use_list = &ctx->use_lists[var];
199
0
      ir_ref n = var_use_list->count;
200
0
      ir_ref *p = &ctx->use_edges[var_use_list->refs];
201
0
      int32_t old_slot = IR_VREG_TO_STACK_SLOT(ctx->vregs[var]);
202
203
0
      for (; n > 0; p++, n--) {
204
0
        ir_ref use = *p;
205
0
        ir_insn *insn = &ctx->ir_base[use];
206
207
0
        if (insn->op == IR_VADDR) {
208
0
          if (ctx->rules[use] == IR_STATIC_ALLOCA) {
209
0
            IR_ASSERT(ctx->vregs[use] == ctx->vregs[var]);
210
0
            ctx->vregs[use] = IR_STACK_SLOT_TO_VREG(spill_slot);
211
0
          }
212
0
        } else if (insn->op == IR_VLOAD || insn->op == IR_VLOAD_v) {
213
0
          if (ctx->vregs[use] > 0) {
214
0
            ir_live_interval *ival = ctx->live_intervals[ctx->vregs[use]];
215
0
            if (ival && ival->stack_spill_pos == old_slot) {
216
0
              ival->stack_spill_pos = spill_slot;
217
0
            }
218
0
          }
219
0
        } else if (insn->op == IR_VSTORE || insn->op == IR_VSTORE_v) {
220
0
          if (insn->op3 > 0 && ctx->vregs[insn->op3] > 0) {
221
0
            ir_live_interval *ival = ctx->live_intervals[ctx->vregs[insn->op3]];
222
0
            if (ival && ival->stack_spill_pos == old_slot) {
223
0
              ival->stack_spill_pos = spill_slot;
224
0
            }
225
0
          }
226
0
        }
227
0
      }
228
229
      /* Remap VAR itself */
230
0
      ctx->vregs[var] = IR_STACK_SLOT_TO_VREG(spill_slot);
231
0
    }
232
0
  }
233
0
}
234
235
static int ir_get_args_regs(const ir_ctx *ctx, const ir_insn *insn, const ir_call_conv_dsc *cc, int8_t *regs)
236
80.1k
{
237
80.1k
  int j, n;
238
80.1k
  ir_type type;
239
80.1k
  int int_param = 0;
240
80.1k
  int fp_param = 0;
241
#if IR_SIMD && defined(IR_TARGET_X86)
242
  int vector_param = 0;
243
#endif
244
80.1k
  int count = 0;
245
246
80.1k
  n = insn->inputs_count;
247
80.1k
  n = IR_MIN(n, IR_MAX_REG_ARGS + 2);
248
202k
  for (j = 3; j <= n; j++) {
249
122k
    ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)];
250
122k
    type = arg->type;
251
122k
    if (IR_IS_TYPE_INT(type)) {
252
122k
      if (int_param < cc->int_param_regs_count && arg->op != IR_ARGVAL) {
253
#if IR_X86_I64
254
        if (type == IR_I64 || type == IR_U64) {
255
          regs[j] = IR_REG_NONE;
256
          count = j + 1;
257
          int_param += 2;
258
          if (cc->shadow_param_regs) {
259
            fp_param += 2;
260
          }
261
          continue;
262
        }
263
#endif
264
122k
        regs[j] = cc->int_param_regs[int_param];
265
122k
        count = j + 1;
266
122k
        int_param++;
267
122k
        if (cc->shadow_param_regs) {
268
0
          fp_param++;
269
0
        }
270
122k
      } else {
271
0
        regs[j] = IR_REG_NONE;
272
0
      }
273
#if IR_SIMD && defined(IR_TARGET_X86)
274
    } else if (IR_IS_TYPE_VECTOR(type)) {
275
      if (vector_param < cc->vector_param_regs_count) {
276
        regs[j] = cc->vector_param_regs[vector_param];
277
        count = j + 1;
278
        vector_param++;
279
      } else {
280
        regs[j] = IR_REG_NONE;
281
      }
282
#endif
283
122k
    } else {
284
0
      IR_ASSERT(IR_IS_TYPE_FP(type) || IR_IS_TYPE_VECTOR(type));
285
0
      if (fp_param < cc->fp_param_regs_count) {
286
0
        regs[j] = cc->fp_param_regs[fp_param];
287
0
        count = j + 1;
288
0
        fp_param++;
289
0
        if (cc->shadow_param_regs) {
290
0
          int_param++;
291
0
        }
292
0
      } else {
293
0
        regs[j] = IR_REG_NONE;
294
0
      }
295
0
    }
296
122k
  }
297
80.1k
  return count;
298
80.1k
}
299
300
static bool ir_is_same_mem_var(const ir_ctx *ctx, ir_ref r1, int32_t offset)
301
0
{
302
0
  ir_live_interval *ival1;
303
0
  int32_t o1;
304
305
0
  if (IR_IS_CONST_REF(r1)) {
306
0
    return 0;
307
0
  }
308
309
0
  IR_ASSERT(ctx->vregs[r1] > 0);
310
0
  ival1 = ctx->live_intervals[ctx->vregs[r1]];
311
0
  IR_ASSERT(ival1);
312
0
  o1 = ival1->stack_spill_pos;
313
0
  IR_ASSERT(o1 != -1);
314
0
  return o1 == offset;
315
0
}
316
317
void *ir_resolve_sym_name(const char *name)
318
0
{
319
0
  void *addr;
320
321
0
#ifndef _WIN32
322
0
  void *handle = NULL;
323
0
# ifdef RTLD_DEFAULT
324
0
  handle = RTLD_DEFAULT;
325
0
# endif
326
0
  addr = dlsym(handle, name);
327
#else
328
  HMODULE mods[256];
329
  DWORD cbNeeded;
330
  uint32_t i = 0;
331
332
  addr = NULL;
333
334
  EnumProcessModules(GetCurrentProcess(), mods, sizeof(mods), &cbNeeded);
335
336
  while(i < (cbNeeded / sizeof(HMODULE))) {
337
    addr = GetProcAddress(mods[i], name);
338
    if (addr) {
339
      return addr;
340
    }
341
    i++;
342
  }
343
#endif
344
0
  return addr;
345
0
}
346
347
#ifdef IR_SNAPSHOT_HANDLER_DCL
348
  IR_SNAPSHOT_HANDLER_DCL();
349
#endif
350
351
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
352
static void* ir_sym_addr(ir_ctx *ctx, const ir_insn *addr_insn)
353
0
{
354
0
  void *addr;
355
356
0
  if (ctx->loader && ctx->loader->resolve_sym_name) {
357
0
    addr = ctx->loader->resolve_sym_name(ctx->loader, ctx, addr_insn->val.name, IR_RESOLVE_SYM_SILENT);
358
0
  } else {
359
0
    const char *name = ir_get_str(ctx, addr_insn->val.name);
360
0
    addr = ir_resolve_sym_name(name);
361
0
  }
362
0
  return addr;
363
0
}
364
#endif
365
366
static void* ir_sym_val(ir_ctx *ctx, const ir_insn *addr_insn)
367
0
{
368
0
  void *addr;
369
370
0
  if (ctx->loader && ctx->loader->resolve_sym_name) {
371
0
    addr = ctx->loader->resolve_sym_name(ctx->loader, ctx, addr_insn->val.name, addr_insn->op == IR_FUNC ? IR_RESOLVE_SYM_ADD_THUNK : 0);
372
0
  } else {
373
0
    const char *name = ir_get_str(ctx, addr_insn->val.name);
374
0
    addr = ir_resolve_sym_name(name);
375
0
  }
376
0
  IR_ASSERT(addr);
377
0
  return addr;
378
0
}
379
380
static void *ir_call_addr(ir_ctx *ctx, ir_insn *insn, ir_insn *addr_insn)
381
137k
{
382
137k
  void *addr;
383
384
137k
  IR_ASSERT(addr_insn->type == IR_ADDR);
385
137k
  if (addr_insn->op == IR_FUNC) {
386
0
    addr = ir_sym_val(ctx, addr_insn);
387
137k
  } else {
388
137k
    IR_ASSERT(addr_insn->op == IR_ADDR || addr_insn->op == IR_FUNC_ADDR);
389
137k
    addr = (void*)addr_insn->val.addr;
390
137k
  }
391
137k
  return addr;
392
137k
}
393
394
static void *ir_jmp_addr(ir_ctx *ctx, ir_insn *insn, ir_insn *addr_insn)
395
49.2k
{
396
49.2k
  void *addr = ir_call_addr(ctx, insn, addr_insn);
397
398
49.2k
#ifdef IR_SNAPSHOT_HANDLER
399
49.2k
  if (ctx->ir_base[insn->op1].op == IR_SNAPSHOT) {
400
188
    addr = IR_SNAPSHOT_HANDLER(ctx, insn->op1, &ctx->ir_base[insn->op1], addr);
401
188
  }
402
49.2k
#endif
403
49.2k
  return addr;
404
49.2k
}
405
406
void ir_set_fused_reg(ir_ctx *ctx, ir_ref root, ir_ref ref, ir_ref op, int8_t reg)
407
0
{
408
0
  char key[10];
409
0
  ir_ref ref_and_op = ref * sizeof(ir_ref) + op;
410
411
0
  if (!ctx->fused_regs) {
412
0
    ctx->fused_regs = ir_mem_malloc(sizeof(ir_strtab));
413
0
    ir_strtab_init(ctx->fused_regs, 8, 128);
414
0
  }
415
0
  memcpy(key, &root, sizeof(ir_ref));
416
0
  memcpy(key + 4, &ref_and_op, sizeof(ir_ref));
417
0
  ir_strtab_set(ctx->fused_regs, key, 8, 0x10000000 | (uint8_t)reg);
418
0
}
419
420
static int8_t ir_get_fused_reg_ex(ir_ctx *ctx, ir_ref root, ir_ref ref_and_op)
421
0
{
422
0
  if (ctx->fused_regs) {
423
0
    char key[10];
424
0
    ir_ref val;
425
426
0
    memcpy(key, &root, sizeof(ir_ref));
427
0
    memcpy(key + 4, &ref_and_op, sizeof(ir_ref));
428
429
0
    val = ir_strtab_find(ctx->fused_regs, key, 8);
430
0
    if (val) {
431
0
      return val;
432
0
    }
433
0
  }
434
0
  return ((int8_t*)ctx->regs)[ref_and_op];
435
0
}
436
437
static int8_t ir_get_fused_reg(ir_ctx *ctx, ir_ref root, ir_ref ref, ir_ref op)
438
0
{
439
0
  return ir_get_fused_reg_ex(ctx, root, ref * sizeof(ir_ref) + op);
440
0
}
441
442
#if defined(__GNUC__)
443
# pragma GCC diagnostic push
444
# pragma GCC diagnostic ignored "-Warray-bounds"
445
# pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
446
#endif
447
448
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
449
# include "dynasm/dasm_proto.h"
450
# include "dynasm/dasm_x86.h"
451
#elif defined(IR_TARGET_AARCH64)
452
# include "dynasm/dasm_proto.h"
453
static int ir_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset);
454
# define DASM_ADD_VENEER ir_add_veneer
455
# include "dynasm/dasm_arm64.h"
456
#else
457
# error "Unknown IR target"
458
#endif
459
460
#if defined(__GNUC__)
461
# pragma GCC diagnostic pop
462
#endif
463
464
/* Forward Declarations */
465
static void ir_emit_osr_entry_loads(ir_ctx *ctx, int b, ir_block *bb);
466
static int ir_parallel_copy(ir_ctx *ctx, ir_copy *copies, int count, ir_reg tmp_reg, ir_reg tmp_fp_reg);
467
static void ir_emit_dessa_moves(ir_ctx *ctx, int b, ir_block *bb);
468
469
typedef struct _ir_common_backend_data {
470
    ir_reg_alloc_data  ra_data;
471
  dasm_State        *dasm_state;
472
  ir_bitset          emit_constants;
473
} ir_common_backend_data;
474
475
static int ir_get_const_label(ir_ctx *ctx, ir_ref ref)
476
735
{
477
735
  ir_common_backend_data *data = ctx->data;
478
735
  int label = ctx->cfg_blocks_count - ref;
479
480
735
  IR_ASSERT(IR_IS_CONST_REF(ref));
481
735
  ir_bitset_incl(data->emit_constants, -ref);
482
735
  return label;
483
735
}
484
485
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
486
# include <ir_emit_x86.h>
487
#elif defined(IR_TARGET_AARCH64)
488
# include <ir_emit_aarch64.h>
489
#else
490
# error "Unknown IR target"
491
#endif
492
493
static IR_NEVER_INLINE void ir_emit_osr_entry_loads(ir_ctx *ctx, int b, ir_block *bb)
494
0
{
495
0
  ir_list *list = (ir_list*)ctx->osr_entry_loads;
496
0
  int pos = 0, count, i;
497
0
  ir_ref ref;
498
499
0
  IR_ASSERT(ctx->binding);
500
0
  IR_ASSERT(list);
501
0
  while (1) {
502
0
    i = ir_list_at(list, pos);
503
0
    if (b == i) {
504
0
      break;
505
0
    }
506
0
    IR_ASSERT(i != 0); /* end marker */
507
0
    pos++;
508
0
    count = ir_list_at(list, pos);
509
0
    pos += count + 1;
510
0
  }
511
0
  pos++;
512
0
  count = ir_list_at(list, pos);
513
0
  pos++;
514
515
0
  for (i = 0; i < count; i++, pos++) {
516
0
    ref = ir_list_at(list, pos);
517
0
    IR_ASSERT(ref >= 0 && ctx->vregs[ref] > 0 && ctx->live_intervals[ctx->vregs[ref]]);
518
0
    if (!(ctx->live_intervals[ctx->vregs[ref]]->flags & IR_LIVE_INTERVAL_SPILLED)) {
519
      /* not spilled */
520
0
      ir_reg reg = ctx->live_intervals[ctx->vregs[ref]]->reg;
521
0
      ir_type type = ctx->ir_base[ref].type;
522
0
      int32_t offset = -ir_binding_find(ctx, ref);
523
524
0
      IR_ASSERT(offset > 0);
525
0
      ir_emit_load_mem(ctx, type, reg, IR_MEM_BO(ctx->spill_base, offset));
526
0
    } else {
527
0
      IR_ASSERT(ctx->live_intervals[ctx->vregs[ref]]->flags & IR_LIVE_INTERVAL_SPILL_SPECIAL);
528
0
    }
529
0
  }
530
0
}
531
532
/*
533
 * Parallel copy sequentialization algorithm
534
 *
535
 * The implementation is based on algorithm 1 desriebed in
536
 * "Revisiting Out-of-SSA Translation for Correctness, Code Quality and Efficiency",
537
 * Benoit Boissinot, Alain Darte, Fabrice Rastello, Benoit Dupont de Dinechin, Christophe Guillon.
538
 * 2009 International Symposium on Code Generation and Optimization, Seattle, WA, USA, 2009,
539
 * pp. 114-125, doi: 10.1109/CGO.2009.19.
540
 */
541
static int ir_parallel_copy(ir_ctx *ctx, ir_copy *copies, int count, ir_reg tmp_reg, ir_reg tmp_fp_reg)
542
34.1k
{
543
34.1k
  int i;
544
34.1k
  int8_t *pred, *loc, *types;
545
34.1k
  ir_reg to, from;
546
34.1k
  ir_type type;
547
34.1k
  ir_regset todo, ready, srcs;
548
549
34.1k
  if (count == 1) {
550
16.2k
    to = copies[0].to;
551
16.2k
    from = copies[0].from;
552
16.2k
    IR_ASSERT(from != to);
553
16.2k
    type = copies[0].type;
554
16.2k
    if (IR_IS_TYPE_INT(type)) {
555
16.2k
      ir_emit_mov(ctx, type, to, from);
556
16.2k
    } else {
557
0
      ir_emit_fp_mov(ctx, type, to, from);
558
0
    }
559
16.2k
    return 1;
560
16.2k
  }
561
562
17.8k
  loc = alloca(IR_REG_NUM * 3 * sizeof(int8_t));
563
17.8k
  pred = loc + IR_REG_NUM;
564
17.8k
  types = pred + IR_REG_NUM;
565
17.8k
  todo = IR_REGSET_EMPTY;
566
17.8k
  srcs = IR_REGSET_EMPTY;
567
568
53.5k
  for (i = 0; i < count; i++) {
569
35.7k
    from = copies[i].from;
570
35.7k
    to = copies[i].to;
571
35.7k
    IR_ASSERT(from != to);
572
35.7k
    IR_REGSET_INCL(srcs, from);
573
35.7k
    loc[from] = from;
574
35.7k
    pred[to] = from;
575
35.7k
    types[from] = copies[i].type;
576
35.7k
    IR_ASSERT(!IR_REGSET_IN(todo, to));
577
35.7k
    IR_REGSET_INCL(todo, to);
578
35.7k
  }
579
580
17.8k
  ready = IR_REGSET_DIFFERENCE(todo, srcs);
581
582
17.8k
  if (ready == todo) {
583
53.5k
    for (i = 0; i < count; i++) {
584
35.6k
      from = copies[i].from;
585
35.6k
      to = copies[i].to;
586
35.6k
      IR_ASSERT(from != to);
587
35.6k
      type = copies[i].type;
588
35.6k
      if (IR_IS_TYPE_INT(type)) {
589
35.6k
        ir_emit_mov(ctx, type, to, from);
590
35.6k
      } else {
591
0
        ir_emit_fp_mov(ctx, type, to, from);
592
0
      }
593
35.6k
    }
594
17.8k
    return 1;
595
17.8k
  }
596
597
  /* temporary registers can't be the same as some of the destinations */
598
8
  IR_ASSERT(tmp_reg == IR_REG_NONE || !IR_REGSET_IN(todo, tmp_reg));
599
8
  IR_ASSERT(tmp_fp_reg == IR_REG_NONE || !IR_REGSET_IN(todo, tmp_fp_reg));
600
601
  /* first we resolve all "windmill blades" - trees (this doesn't requre temporary registers) */
602
24
  while (ready != IR_REGSET_EMPTY) {
603
16
    ir_reg r;
604
605
16
    to = ir_regset_pop_first(&ready);
606
16
    from = pred[to];
607
16
    r = loc[from];
608
16
    type = types[from];
609
16
    if (IR_IS_TYPE_INT(type)) {
610
16
      ir_emit_mov_ext(ctx, type, to, r);
611
16
    } else {
612
0
      ir_emit_fp_mov(ctx, type, to, r);
613
0
    }
614
16
    IR_REGSET_EXCL(todo, to);
615
16
    loc[from] = to;
616
16
    if (from == r && IR_REGSET_IN(todo, from)) {
617
8
      IR_REGSET_INCL(ready, from);
618
8
    }
619
16
  }
620
8
  if (todo == IR_REGSET_EMPTY) {
621
8
    return 1;
622
8
  }
623
624
  /* at this point the sources that are the same as temoraries are already moved */
625
0
  IR_ASSERT(tmp_reg == IR_REG_NONE || !IR_REGSET_IN(srcs, tmp_reg) || pred[loc[tmp_reg]] == tmp_reg);
626
0
  IR_ASSERT(tmp_fp_reg == IR_REG_NONE || !IR_REGSET_IN(srcs, tmp_fp_reg) || pred[loc[tmp_fp_reg]] == tmp_fp_reg);
627
628
  /* now we resolve all "windmill axles" - cycles (this reuires temporary registers) */
629
0
  while (todo != IR_REGSET_EMPTY) {
630
0
    to = ir_regset_pop_first(&todo);
631
0
    from = pred[to];
632
0
    IR_ASSERT(to != loc[from]);
633
0
    type = types[from];
634
0
    if (IR_IS_TYPE_INT(type)) {
635
0
#ifdef IR_HAVE_SWAP_INT
636
0
      if (pred[from] == to) {
637
0
        if (ir_type_size[types[to]] > ir_type_size[type]) {
638
0
          type = types[to];
639
0
        }
640
0
        ir_emit_swap(ctx, type, to, from);
641
0
        IR_REGSET_EXCL(todo, from);
642
0
        loc[to] = from;
643
0
        loc[from] = to;
644
0
        continue;
645
0
      }
646
0
#endif
647
0
      IR_ASSERT(tmp_reg != IR_REG_NONE);
648
0
      IR_ASSERT(tmp_reg >= IR_REG_GP_FIRST && tmp_reg <= IR_REG_GP_LAST);
649
0
      ir_emit_mov(ctx, type, tmp_reg, to);
650
0
      loc[to] = tmp_reg;
651
0
    } else {
652
#ifdef IR_HAVE_SWAP_FP
653
      if (pred[from] == to && types[to] == type) {
654
        ir_emit_swap_fp(ctx, type, to, from);
655
        IR_REGSET_EXCL(todo, from);
656
        loc[to] = from;
657
        loc[from] = to;
658
        continue;
659
      }
660
#endif
661
0
      IR_ASSERT(tmp_fp_reg != IR_REG_NONE);
662
0
      IR_ASSERT(tmp_fp_reg >= IR_REG_FP_FIRST && tmp_fp_reg <= IR_REG_FP_LAST);
663
0
      ir_emit_fp_mov(ctx, type, tmp_fp_reg, to);
664
0
      loc[to] = tmp_fp_reg;
665
0
    }
666
0
    while (1) {
667
0
      ir_reg r;
668
669
0
      from = pred[to];
670
0
      r = loc[from];
671
0
      type = types[from];
672
0
      if (IR_IS_TYPE_INT(type)) {
673
0
        ir_emit_mov_ext(ctx, type, to, r);
674
0
      } else {
675
0
        ir_emit_fp_mov(ctx, type, to, r);
676
0
      }
677
0
      IR_REGSET_EXCL(todo, to);
678
0
      loc[from] = to;
679
0
      if (from == r && IR_REGSET_IN(todo, from)) {
680
0
        to = from;
681
0
      } else {
682
0
        break;
683
0
      }
684
0
    }
685
0
  }
686
687
0
  return 1;
688
0
}
689
690
static void ir_emit_dessa_move(ir_ctx *ctx, ir_mem *mem_slots,
691
                               ir_type type, ir_ref to, ir_ref from,
692
                               ir_reg tmp_reg, ir_reg tmp_fp_reg)
693
3.79k
{
694
3.79k
  ir_mem mem_from, mem_to;
695
696
3.79k
  IR_ASSERT(from != to);
697
3.79k
  if (to < IR_REG_NUM) {
698
3.79k
    if (IR_IS_CONST_REF(from)) {
699
3.79k
      if (-from < ctx->consts_count) {
700
#if IR_X86_I64
701
        if (type == IR_U32_HI) {
702
          ir_emit_load_i64_hi(ctx, to, from);
703
        } else
704
#endif
705
        /* constant reference */
706
3.79k
        ir_emit_load(ctx, type, to, from);
707
3.79k
      } else {
708
        /* local variable address */
709
0
        ir_load_local_addr(ctx, to, -from - ctx->consts_count);
710
0
      }
711
3.79k
    } else if (from < IR_REG_NUM) {
712
5
      if (IR_IS_TYPE_INT(type)) {
713
5
        ir_emit_mov(ctx, type, to, from);
714
5
      } else {
715
0
        ir_emit_fp_mov(ctx, type, to, from);
716
0
      }
717
5
    } else {
718
0
      mem_from = mem_slots[from - IR_REG_NUM];
719
0
      ir_emit_load_mem(ctx, type, to, mem_from);
720
0
    }
721
3.79k
  } else {
722
0
    mem_to = mem_slots[to - IR_REG_NUM];
723
0
    if (IR_IS_CONST_REF(from)) {
724
0
      if (-from < ctx->consts_count) {
725
        /* constant reference */
726
#if IR_X86_I64
727
        if (type == IR_U32_HI) {
728
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
729
          ir_emit_store_mem_imm(ctx, IR_U32, mem_to, ctx->ir_base[from].val.u32_hi);
730
#else
731
          IR_ASSERT(tmp_reg != IR_REG_NONE);
732
          ir_emit_load_i64_hi(ctx, tmp_reg, from);
733
          ir_emit_store_mem(ctx, IR_U32, mem_to, tmp_reg);
734
#endif
735
          return;
736
        } else
737
#endif
738
0
#if defined(IR_TARGET_X86) || defined(IR_TARGET_X64)
739
0
        if (IR_IS_TYPE_INT(type)
740
0
         && !IR_IS_SYM_CONST(ctx->ir_base[from].op)
741
0
         && (ir_type_size[type] != 8 || IR_IS_SIGNED_32BIT(ctx->ir_base[from].val.i64))) {
742
0
          ir_emit_store_mem_imm(ctx, type, mem_to, ctx->ir_base[from].val.i32);
743
0
          return;
744
0
        }
745
0
#endif
746
0
        ir_reg tmp = IR_IS_TYPE_INT(type) ? tmp_reg : tmp_fp_reg;
747
0
        IR_ASSERT(tmp != IR_REG_NONE);
748
0
        ir_emit_load(ctx, type, tmp, from);
749
0
        ir_emit_store_mem(ctx, type, mem_to, tmp);
750
0
      } else {
751
        /* local variable address */
752
0
        IR_ASSERT(IR_IS_TYPE_INT(type));
753
0
        IR_ASSERT(tmp_reg != IR_REG_NONE);
754
0
        ir_load_local_addr(ctx, tmp_reg, -from - ctx->consts_count);
755
0
        ir_emit_store_mem(ctx, type, mem_to, tmp_reg);
756
0
      }
757
0
    } else if (from < IR_REG_NUM) {
758
0
      ir_emit_store_mem(ctx, type, mem_to, from);
759
0
    } else {
760
0
      mem_from = mem_slots[from - IR_REG_NUM];
761
0
      IR_ASSERT(IR_MEM_VAL(mem_to) != IR_MEM_VAL(mem_from));
762
0
      ir_reg tmp = IR_IS_TYPE_INT(type) ?  tmp_reg : tmp_fp_reg;
763
0
      IR_ASSERT(tmp != IR_REG_NONE);
764
0
      ir_emit_load_mem(ctx, type, tmp, mem_from);
765
0
      ir_emit_store_mem(ctx, type, mem_to, tmp);
766
0
    }
767
0
  }
768
3.79k
}
769
770
IR_ALWAYS_INLINE void ir_dessa_resolve_cycle(ir_ctx *ctx, ir_mem *mem_slots, int32_t *pred, int32_t *loc,
771
                                             int8_t *types, ir_bitset todo, int32_t root,
772
                                             ir_reg tmp_reg, ir_reg tmp_fp_reg)
773
0
{
774
0
  ir_ref from;
775
0
  ir_mem tmp_spill_slot;
776
0
  ir_type type;
777
0
  int32_t to = root;
778
779
0
  IR_MEM_VAL(tmp_spill_slot) = 0;
780
0
  IR_ASSERT(!IR_IS_CONST_REF(to));
781
0
  from = pred[to];
782
0
  type = types[from];
783
0
  IR_ASSERT(!IR_IS_CONST_REF(from));
784
0
  IR_ASSERT(from != to);
785
0
  IR_ASSERT(loc[from] == from);
786
787
0
  if (IR_IS_TYPE_INT(type)) {
788
0
#ifdef IR_HAVE_SWAP_INT
789
0
    if (pred[from] == to && to < IR_REG_NUM && from < IR_REG_NUM) {
790
      /* a simple cycle from 2 elements */
791
0
      if (ir_type_size[types[to]] > ir_type_size[type]) {
792
0
        type = types[to];
793
0
      }
794
0
      ir_emit_swap(ctx, type, to, from);
795
0
      ir_bitset_excl(todo, from);
796
0
      ir_bitset_excl(todo, to);
797
0
      loc[to] = from;
798
0
      loc[from] = to;
799
0
      return;
800
0
    }
801
0
#endif
802
0
    IR_ASSERT(tmp_reg != IR_REG_NONE);
803
0
    IR_ASSERT(tmp_reg >= IR_REG_GP_FIRST && tmp_reg <= IR_REG_GP_LAST);
804
0
    loc[to] = tmp_reg;
805
0
    if (to < IR_REG_NUM) {
806
0
      ir_emit_mov(ctx, type, tmp_reg, to);
807
0
    } else {
808
0
      ir_emit_load_mem_int(ctx, type, tmp_reg, mem_slots[to - IR_REG_NUM]);
809
0
    }
810
0
  } else {
811
#ifdef IR_HAVE_SWAP_FP
812
    if (pred[from] == to && to < IR_REG_NUM && from < IR_REG_NUM && types[to] == type) {
813
      /* a simple cycle from 2 elements */
814
      ir_emit_swap_fp(ctx, type, to, from);
815
      IR_REGSET_EXCL(todo, from);
816
      IR_REGSET_EXCL(todo, to);
817
      loc[to] = from;
818
      loc[from] = to;
819
      return;
820
    }
821
#endif
822
0
    IR_ASSERT(tmp_fp_reg != IR_REG_NONE);
823
0
    IR_ASSERT(tmp_fp_reg >= IR_REG_FP_FIRST && tmp_fp_reg <= IR_REG_FP_LAST);
824
0
    loc[to] = tmp_fp_reg;
825
0
    if (to < IR_REG_NUM) {
826
0
      ir_emit_fp_mov(ctx, type, tmp_fp_reg, to);
827
0
    } else {
828
0
      ir_emit_load_mem_fp(ctx, type, tmp_fp_reg, mem_slots[to - IR_REG_NUM]);
829
0
    }
830
0
  }
831
832
0
  while (1) {
833
0
    int32_t r;
834
835
0
    from = pred[to];
836
0
    r = loc[from];
837
0
    type = types[to];
838
839
0
    if (from == root) break;
840
841
    /* Memory to memory move inside an isolated or "blocked" cycle requres an additional temporary register */
842
0
    if (to >= IR_REG_NUM && r >= IR_REG_NUM) {
843
0
      ir_reg tmp = IR_IS_TYPE_INT(type) ?  tmp_reg : tmp_fp_reg;
844
845
0
      if (!IR_MEM_VAL(tmp_spill_slot)) {
846
        /* Free a register, saving it in a temporary spill slot */
847
0
        tmp_spill_slot = IR_MEM_BO(IR_REG_STACK_POINTER, -16);
848
0
        ir_emit_store_mem(ctx, type, tmp_spill_slot, tmp);
849
0
      }
850
0
      ir_emit_dessa_move(ctx, mem_slots, type, to, r, tmp_reg, tmp_fp_reg);
851
0
    } else {
852
0
      ir_emit_dessa_move(ctx, mem_slots, type, to, r, IR_REG_NONE, IR_REG_NONE);
853
0
    }
854
0
    ir_bitset_excl(todo, to);
855
0
    loc[from] = to;
856
0
    to = from;
857
0
  }
858
859
0
  type = types[to];
860
0
  if (IR_MEM_VAL(tmp_spill_slot)) {
861
0
    ir_emit_load_mem(ctx, type, IR_IS_TYPE_INT(type) ? tmp_reg : tmp_fp_reg, tmp_spill_slot);
862
0
  }
863
0
  ir_emit_dessa_move(ctx, mem_slots, type, to, loc[from], IR_REG_NONE, IR_REG_NONE);
864
0
  ir_bitset_excl(todo, to);
865
0
  loc[from] = to;
866
0
}
867
868
static int ir_dessa_parallel_copy(ir_ctx *ctx, ir_dessa_copy *copies, int count,
869
                                  ir_mem *mem_slots, int mem_slots_count,
870
                                  ir_reg tmp_reg, ir_reg tmp_fp_reg)
871
3.79k
{
872
3.79k
  int i;
873
3.79k
  int32_t *pred, *loc, to, from;
874
3.79k
  int8_t *types;
875
3.79k
  ir_type type;
876
3.79k
  uint32_t len;
877
3.79k
  ir_bitset todo, ready, srcs, visited;
878
879
3.79k
  if (count == 1) {
880
3.79k
    to = copies[0].to;
881
3.79k
    from = copies[0].from;
882
3.79k
    IR_ASSERT(from != to);
883
3.79k
    type = copies[0].type;
884
3.79k
    ir_emit_dessa_move(ctx, mem_slots, type, to, from, tmp_reg, tmp_fp_reg);
885
3.79k
    return 1;
886
3.79k
  }
887
888
0
  len = IR_REG_NUM + mem_slots_count + 1;
889
0
  loc = ir_mem_malloc(len * 2 * sizeof(int32_t) + len * sizeof(int8_t));
890
0
  pred = loc + len;
891
0
  types = (int8_t*)(pred + len);
892
893
0
  len = ir_bitset_len(len);
894
0
  todo = ir_mem_malloc(len * IR_BITSET_BITS / 8 * 4);
895
0
  memset(todo, 0, len * IR_BITSET_BITS / 8 * 2);
896
0
  srcs = todo + len;
897
0
  ready = srcs + len;
898
0
  visited = ready + len;
899
900
0
  for (i = 0; i < count; i++) {
901
0
    from = copies[i].from;
902
0
    to = copies[i].to;
903
0
    IR_ASSERT(from != to);
904
0
    if (!IR_IS_CONST_REF(from)) {
905
0
      ir_bitset_incl(srcs, from);
906
0
      loc[from] = from;
907
0
    }
908
0
    pred[to] = from;
909
0
    types[to] = copies[i].type;
910
0
    IR_ASSERT(!ir_bitset_in(todo, to));
911
0
    ir_bitset_incl(todo, to);
912
0
  }
913
914
  /* temporary registers can't be the same as some of the sources */
915
0
  IR_ASSERT(tmp_reg == IR_REG_NONE || !ir_bitset_in(srcs, tmp_reg));
916
0
  IR_ASSERT(tmp_fp_reg == IR_REG_NONE || !ir_bitset_in(srcs, tmp_fp_reg));
917
918
  /* first we resolve all "windmill blades" - trees, that don't set temporary registers */
919
0
  ir_bitset_copy(ready, todo, len);
920
0
  ir_bitset_difference(ready, srcs, len);
921
0
  if (tmp_reg != IR_REG_NONE) {
922
0
    ir_bitset_excl(ready, tmp_reg);
923
0
  }
924
0
  if (tmp_fp_reg != IR_REG_NONE) {
925
0
    ir_bitset_excl(ready, tmp_fp_reg);
926
0
  }
927
0
  while ((to = ir_bitset_pop_first(ready, len)) >= 0) {
928
0
    ir_bitset_excl(todo, to);
929
0
    type = types[to];
930
0
    from = pred[to];
931
0
    if (IR_IS_CONST_REF(from)) {
932
0
      ir_emit_dessa_move(ctx, mem_slots, type, to, from, tmp_reg, tmp_fp_reg);
933
0
    } else {
934
0
      int32_t r = loc[from];
935
0
      ir_emit_dessa_move(ctx, mem_slots, type, to, r, tmp_reg, tmp_fp_reg);
936
0
      loc[from] = to;
937
0
      if (from == r && ir_bitset_in(todo, from) && from != tmp_reg && from != tmp_fp_reg) {
938
0
        ir_bitset_incl(ready, from);
939
0
      }
940
0
    }
941
0
  }
942
943
  /* then we resolve all "windmill axles" - cycles (this requres temporary registers) */
944
0
  ir_bitset_copy(ready, todo, len);
945
0
  ir_bitset_intersection(ready, srcs, len);
946
0
  while ((to = ir_bitset_first(ready, len)) >= 0) {
947
0
    ir_bitset_clear(visited, len);
948
0
    ir_bitset_incl(visited, to);
949
0
    to = pred[to];
950
0
    while (!IR_IS_CONST_REF(to) && ir_bitset_in(ready, to)) {
951
0
      to = pred[to];
952
0
      IR_ASSERT(!IR_IS_CONST_REF(to));
953
0
      if (ir_bitset_in(visited, to)) {
954
        /* We found a cycle. Resolve it. */
955
0
        ir_bitset_incl(visited, to);
956
0
        ir_dessa_resolve_cycle(ctx, mem_slots, pred, loc, types, todo, to, tmp_reg, tmp_fp_reg);
957
0
        break;
958
0
      }
959
0
      ir_bitset_incl(visited, to);
960
0
    }
961
0
    ir_bitset_difference(ready, visited, len);
962
0
  }
963
964
  /* finally we resolve remaining "windmill blades" - trees that set temporary registers */
965
0
  ir_bitset_copy(ready, todo, len);
966
0
  ir_bitset_difference(ready, srcs, len);
967
0
  while ((to = ir_bitset_pop_first(ready, len)) >= 0) {
968
0
    ir_bitset_excl(todo, to);
969
0
    type = types[to];
970
0
    from = pred[to];
971
0
#ifdef IR_DEBUG
972
    /* If destionation is set, it can't be used as temporary anymore */
973
0
    if (to == tmp_reg) {
974
0
      tmp_reg = IR_REG_NONE;
975
0
    }
976
0
    if (to == tmp_fp_reg) {
977
0
      tmp_fp_reg = IR_REG_NONE;
978
0
    }
979
0
#endif
980
0
    if (IR_IS_CONST_REF(from)) {
981
0
      ir_emit_dessa_move(ctx, mem_slots, type, to, from, tmp_reg, tmp_fp_reg);
982
0
    } else {
983
0
      int32_t r = loc[from];
984
0
      ir_emit_dessa_move(ctx, mem_slots, type, to, r, tmp_reg, tmp_fp_reg);
985
0
      loc[from] = to;
986
0
      if (from == r && ir_bitset_in(todo, from)) {
987
0
        ir_bitset_incl(ready, from);
988
0
      }
989
0
    }
990
0
  }
991
992
0
  IR_ASSERT(ir_bitset_empty(todo, len));
993
994
0
  ir_mem_free(todo);
995
0
  ir_mem_free(loc);
996
0
  return 1;
997
0
}
998
999
static uint32_t _find_mem_slot(ir_mem *mem_slots, uint32_t *mem_slots_count, ir_mem mem)
1000
0
{
1001
0
  uint32_t j, n = *mem_slots_count;
1002
1003
0
  for (j = 0; j < n; j++) {
1004
0
    if (IR_MEM_VAL(mem_slots[j]) == IR_MEM_VAL(mem)) return j;
1005
0
  }
1006
0
  mem_slots[n] = mem;
1007
0
  *mem_slots_count = n + 1;
1008
0
  return n;
1009
0
}
1010
1011
static bool ir_dessa_needs_spill_load(ir_ctx *ctx, ir_use_list *use_list, ir_ref skip, int op_num, ir_reg reg)
1012
0
{
1013
0
  ir_ref *p, n, use;
1014
0
  ir_insn *insn;
1015
1016
0
  for (n = use_list->count, p = &ctx->use_edges[use_list->refs]; n > 0; p++, n--) {
1017
0
    use = *p;
1018
0
    if (use == skip) continue;
1019
0
    insn = &ctx->ir_base[use];
1020
0
    if (insn->op == IR_PHI) {
1021
0
      ir_reg src = ir_get_alocated_reg(ctx, use, op_num);
1022
1023
0
      if (IR_REG_NUM(src) == reg) {
1024
        /* the same input register used twice */
1025
0
        return 1;
1026
0
      }
1027
0
    }
1028
0
  }
1029
0
  return 0;
1030
0
}
1031
1032
static void ir_emit_dessa_moves(ir_ctx *ctx, int b, ir_block *bb)
1033
3.79k
{
1034
3.79k
  uint32_t succ, k, n = 0;
1035
3.79k
  ir_block *succ_bb;
1036
3.79k
  ir_use_list *use_list;
1037
3.79k
  ir_ref i, *p;
1038
3.79k
  ir_dessa_copy *copies;
1039
3.79k
  ir_mem *mem_slots;
1040
3.79k
  uint32_t mem_slots_count = 0;
1041
3.79k
  ir_reg tmp_reg = ctx->regs[bb->end][0];
1042
3.79k
  ir_reg tmp_fp_reg = ctx->regs[bb->end][1];
1043
1044
3.79k
  IR_ASSERT(bb->successors_count == 1);
1045
3.79k
  succ = ctx->cfg_edges[bb->successors];
1046
3.79k
  succ_bb = &ctx->cfg_blocks[succ];
1047
3.79k
  IR_ASSERT(succ_bb->predecessors_count > 1);
1048
3.79k
  use_list = &ctx->use_lists[succ_bb->start];
1049
3.79k
  k = ir_phi_input_number(ctx, succ_bb, b);
1050
1051
#if IR_X86_I64
1052
  copies = alloca((use_list->count - 1) * 2 * sizeof(ir_dessa_copy));
1053
  mem_slots = alloca((use_list->count - 1) * 2 * 2 * sizeof(ir_mem));
1054
#else
1055
3.79k
  copies = alloca((use_list->count - 1) * sizeof(ir_dessa_copy));
1056
3.79k
  mem_slots = alloca((use_list->count - 1) * 2 * sizeof(ir_mem));
1057
3.79k
#endif
1058
1059
11.3k
  for (i = use_list->count, p = &ctx->use_edges[use_list->refs]; i > 0; p++, i--) {
1060
7.59k
    ir_ref ref = *p;
1061
7.59k
    ir_insn *insn = &ctx->ir_base[ref];
1062
1063
7.59k
    if (insn->op == IR_PHI && ctx->vregs[ref] > 0) {
1064
3.80k
      ir_ref input = ir_insn_op(insn, k);
1065
3.80k
      ir_reg src = ir_get_alocated_reg(ctx, ref, k);
1066
3.80k
      ir_reg dst = ctx->regs[ref][0];
1067
3.80k
      ir_ref from, to;
1068
#if IR_X86_I64
1069
      ir_reg src_hi = IR_REG_NONE, dst_hi = IR_REG_NONE;
1070
#endif
1071
1072
3.80k
      IR_ASSERT(dst == IR_REG_NONE || !IR_REG_SPILLED(dst));
1073
3.80k
      if (IR_IS_CONST_REF(input)) {
1074
3.79k
        from = input;
1075
3.79k
      } else if (ir_rule(ctx, input) == IR_STATIC_ALLOCA) {
1076
        /* encode local variable address */
1077
0
        from = -(ctx->consts_count + input);
1078
6
      } else if (src != IR_REG_NONE
1079
6
       && (!IR_REG_SPILLED(src)
1080
6
        || ir_dessa_needs_spill_load(ctx, use_list, ref, k, IR_REG_NUM(src)))) {
1081
6
        if (IR_REG_SPILLED(src)) {
1082
0
          src = IR_REG_NUM(src);
1083
#if IR_X86_I64
1084
          if (insn->type == IR_I64 || insn->type == IR_U64) {
1085
            ir_reg reg_hi = IR_REG_I64_HI(src);
1086
            ir_reg reg_lo = IR_REG_I64_LO(src);
1087
            ir_emit_load_i64_lo(ctx, reg_lo, input);
1088
            ir_emit_load_i64_hi(ctx, reg_hi, input);
1089
          } else
1090
#endif
1091
0
          ir_emit_load(ctx, insn->type, src, input);
1092
0
        }
1093
#if IR_X86_I64
1094
        if (insn->type == IR_I64 || insn->type == IR_U64) {
1095
          src_hi = IR_REG_I64_HI(src);
1096
          src = IR_REG_I64_LO(src);
1097
        }
1098
#endif
1099
6
        from = src;
1100
6
      } else {
1101
0
        ir_mem mem = ir_vreg_spill_slot(ctx, ctx->vregs[input]);
1102
1103
0
        from = IR_REG_NUM + _find_mem_slot(mem_slots, &mem_slots_count, mem);
1104
0
      }
1105
3.80k
      if (dst != IR_REG_NONE) {
1106
3.80k
        IR_ASSERT(!IR_REG_SPILLED(dst));
1107
#if IR_X86_I64
1108
        if (insn->type == IR_I64 || insn->type == IR_U64) {
1109
          dst_hi = IR_REG_I64_HI(dst);
1110
          dst = IR_REG_I64_LO(dst);
1111
        }
1112
#endif
1113
3.80k
        to = dst;
1114
3.80k
      } else {
1115
0
        ir_mem mem = ir_vreg_spill_slot(ctx, ctx->vregs[ref]);
1116
1117
0
        to = IR_REG_NUM + _find_mem_slot(mem_slots, &mem_slots_count, mem);
1118
0
      }
1119
#if IR_X86_I64
1120
      if (insn->type == IR_I64 || insn->type == IR_U64) {
1121
        if (from != to) {
1122
          copies[n].type = IR_U32;
1123
          copies[n].from = from;
1124
          copies[n].to = to;
1125
          n++;
1126
        } else if (from >= IR_REG_NUM) {
1127
          continue;
1128
        }
1129
        if (from < 0) {
1130
          /* pass */
1131
        } else if (from < IR_REG_NUM) {
1132
          from = src_hi;
1133
        } else {
1134
          ir_mem mem = IR_MEM_I64_HI(mem_slots[from - IR_REG_NUM]);
1135
1136
          from = IR_REG_NUM + _find_mem_slot(mem_slots, &mem_slots_count, mem);
1137
        }
1138
        if (to < IR_REG_NUM) {
1139
          to = dst_hi;
1140
        } else {
1141
          ir_mem mem = IR_MEM_I64_HI(mem_slots[to - IR_REG_NUM]);
1142
1143
          to = IR_REG_NUM + _find_mem_slot(mem_slots, &mem_slots_count, mem);
1144
        }
1145
        if (from != to) {
1146
          copies[n].type = (from < 0) ? IR_U32_HI : IR_U32;
1147
          copies[n].from = from;
1148
          copies[n].to = to;
1149
          n++;
1150
        }
1151
        continue;
1152
      }
1153
#endif
1154
3.80k
      if (to != from) {
1155
3.79k
        copies[n].type = insn->type;
1156
3.79k
        copies[n].from = from;
1157
3.79k
        copies[n].to = to;
1158
3.79k
        n++;
1159
3.79k
      }
1160
3.80k
    }
1161
7.59k
  }
1162
1163
3.79k
  if (n > 0) {
1164
3.79k
    ir_dessa_parallel_copy(ctx, copies, n, mem_slots, mem_slots_count, tmp_reg, tmp_fp_reg);
1165
3.79k
  }
1166
3.79k
}
1167
1168
/* TAILCALL optimization */
1169
static bool ir_may_be_local_addr(ir_ctx *ctx, ir_insn *insn)
1170
0
{
1171
0
  if (insn->op == IR_PARAM) return 0;
1172
1173
0
  return 1;
1174
0
}
1175
1176
static bool ir_try_tailcall(ir_ctx *ctx, ir_ref ref, ir_insn *insn)
1177
0
{
1178
0
  ir_ref proto_ref = IR_UNUSED;
1179
0
  const ir_proto_t *proto = NULL;
1180
0
  const ir_call_conv_dsc *cc;
1181
0
  int32_t params_stack_size, copy_stack;
1182
1183
0
  if (IR_IS_CONST_REF(insn->op2)) {
1184
0
    const ir_insn *func = &ctx->ir_base[insn->op2];
1185
1186
0
    if (func->op == IR_FUNC && func->proto) {
1187
0
      uint32_t rule = ir_match_builtin_call(ctx, func);
1188
1189
0
      if (rule) {
1190
0
        ctx->rules[ref] = rule;
1191
0
        return 0;
1192
0
      }
1193
0
      proto_ref = func->proto;
1194
0
    } else if (func->op == IR_FUNC_ADDR) {
1195
0
      proto_ref = func->proto;
1196
0
    }
1197
0
  } else if (ctx->ir_base[insn->op2].op == IR_PROTO) {
1198
0
    proto_ref = ctx->ir_base[insn->op2].op2;
1199
0
  }
1200
1201
0
  if (!proto_ref) return 0;
1202
0
  proto = (const ir_proto_t *)ir_get_str(ctx, proto_ref);
1203
1204
0
  if ((proto->flags & IR_CALL_CONV_MASK) != (ctx->flags & IR_CALL_CONV_MASK)) return 0;
1205
1206
0
  cc = ir_get_call_conv_dsc(proto ? proto->flags : IR_CC_DEFAULT);
1207
0
  copy_stack = 0;
1208
0
  params_stack_size = ir_call_used_stack(ctx, insn, cc, &copy_stack);
1209
0
  if (cc->shadow_store_size && params_stack_size == cc->shadow_store_size) {
1210
0
    params_stack_size = 0;
1211
0
  }
1212
1213
  // TODO: "params_stack_size" must match the "args_stack_size"
1214
0
  if (params_stack_size) return 0;
1215
1216
  /* check for passing addresses of local variable */
1217
0
  uint32_t n = insn->inputs_count;
1218
0
  for (uint32_t i = 3; i <= n; i++) {
1219
0
    ir_ref input = ir_insn_op(insn, i);
1220
0
    if (!IR_IS_CONST_REF(input) && ctx->ir_base[input].type == IR_ADDR) {
1221
      /* Passing addrss of local varible to TAILCALL is disallowd */
1222
0
      if (ir_may_be_local_addr(ctx, &ctx->ir_base[input])) {
1223
0
        return 0;
1224
0
      }
1225
0
    }
1226
0
  }
1227
1228
0
#if defined(IR_TARGET_X64) || defined(IR_TARGET_X86)
1229
0
  if (!IR_IS_CONST_REF(insn->op2)) {
1230
0
    if (ctx->ir_base[insn->op2].op == IR_PROTO) {
1231
0
      if (IR_IS_CONST_REF(ctx->ir_base[insn->op2].op1)) {
1232
0
        ctx->rules[insn->op2] = IR_FUSED | IR_SIMPLE | IR_PROTO;
1233
0
      } else {
1234
0
        ir_match_fuse_load(ctx, ctx->ir_base[insn->op2].op1, ref);
1235
0
        if (ctx->rules[ctx->ir_base[insn->op2].op1] & IR_FUSED) {
1236
0
          ctx->rules[insn->op2] = IR_FUSED | IR_SIMPLE | IR_PROTO;
1237
0
        }
1238
0
       }
1239
0
    } else {
1240
0
      ir_match_fuse_load(ctx, insn->op2, ref);
1241
0
    }
1242
0
  }
1243
0
#endif
1244
1245
0
  ctx->rules[ref] = IR_TAILCALL | IR_NO_REG;
1246
1247
0
  return 1;
1248
0
}
1249
1250
#if 0
1251
static bool ir_try_tailcalls(ir_ctx *ctx, ir_insn *merge_insn)
1252
{
1253
  ir_ref count = 0, n = merge_insn->inputs_count;
1254
  ir_ref end, ref, *p = merge_insn->ops + 1;
1255
  ir_insn *insn;
1256
1257
  do {
1258
    end = *p;
1259
    insn = &ctx->ir_base[end];
1260
    IR_ASSERT(insn->op == IR_END);
1261
    ref = insn->op1;
1262
    insn = &ctx->ir_base[ref];
1263
    if (insn->op == IR_CALL) {
1264
      if (ir_try_tailcall(ctx, ref, insn)) {
1265
        ctx->rules[end] = IR_SKIPPED | IR_NOP;
1266
        count++;
1267
      }
1268
    } else if (insn->op == IR_MERGE) {
1269
      if (ir_try_tailcalls(ctx, insn)) {
1270
        ctx->rules[end] = IR_SKIPPED | IR_NOP;
1271
        ctx->rules[ref] = IR_SKIPPED | IR_NOP;
1272
        count++;
1273
      }
1274
    }
1275
    p++;
1276
  } while (--n != 0);
1277
1278
  return count == merge_insn->inputs_count;
1279
}
1280
#endif
1281
1282
static size_t ir_calc_args_stack(const ir_ctx *ctx)
1283
0
{
1284
0
  ir_use_list *use_list = &ctx->use_lists[1];
1285
0
  ir_insn *insn;
1286
0
  ir_ref i, n, *p, use;
1287
0
  int int_param_num = 0;
1288
0
  int fp_param_num = 0;
1289
#if IR_SIMD && defined(IR_TARGET_X86)
1290
  int vector_param_num = 0;
1291
#endif
1292
0
  ir_reg src_reg;
1293
0
  const ir_call_conv_dsc *cc = ir_get_call_conv_dsc(ctx->flags);
1294
0
  int32_t stack_offset = 0;
1295
1296
0
  n = use_list->count;
1297
0
  for (i = 0, p = &ctx->use_edges[use_list->refs]; i < n; i++, p++) {
1298
0
    use = *p;
1299
0
    insn = &ctx->ir_base[use];
1300
0
    if (insn->op == IR_PARAM) {
1301
0
      if (IR_IS_TYPE_INT(insn->type)) {
1302
0
        if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) {
1303
          /* struct passed by value on stack */
1304
0
          uint32_t align = ctx->value_params[insn->op3 - 1].align;
1305
1306
0
          align = IR_MAX(sizeof(void*), align);
1307
0
          stack_offset = IR_ALIGNED_SIZE(stack_offset, align);
1308
0
          stack_offset += ctx->value_params[insn->op3 - 1].size;
1309
0
          stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*));
1310
0
          continue;
1311
0
        } else if (int_param_num < cc->int_param_regs_count) {
1312
0
          src_reg = cc->int_param_regs[int_param_num];
1313
#if IR_X86_I64
1314
          if (src_reg != IR_REG_NONE && (insn->type == IR_I64 || insn->type == IR_U64)) {
1315
            if (int_param_num + 1 < cc->int_param_regs_count) {
1316
              int_param_num++;
1317
              if (cc->shadow_param_regs) {
1318
                fp_param_num++;
1319
              }
1320
            }
1321
            src_reg = IR_REG_NONE;
1322
          }
1323
#endif
1324
0
        } else {
1325
0
          src_reg = IR_REG_NONE;
1326
0
        }
1327
0
        int_param_num++;
1328
0
        if (cc->shadow_param_regs) {
1329
0
          fp_param_num++;
1330
0
        }
1331
#if IR_SIMD && defined(IR_TARGET_X86)
1332
      } else if (IR_IS_TYPE_VECTOR(insn->type)) {
1333
        if (vector_param_num < cc->vector_param_regs_count) {
1334
          src_reg = cc->vector_param_regs[vector_param_num];
1335
        } else {
1336
          src_reg = IR_REG_NONE;
1337
        }
1338
        vector_param_num++;
1339
#endif
1340
0
      } else {
1341
0
        IR_ASSERT(IR_IS_TYPE_FP(insn->type) || IR_IS_TYPE_VECTOR(insn->type));
1342
0
        if (fp_param_num < cc->fp_param_regs_count) {
1343
0
          src_reg = cc->fp_param_regs[fp_param_num];
1344
0
        } else {
1345
0
          src_reg = IR_REG_NONE;
1346
0
        }
1347
0
        fp_param_num++;
1348
0
        if (cc->shadow_param_regs) {
1349
0
          int_param_num++;
1350
0
        }
1351
0
      }
1352
0
      if (src_reg == IR_REG_NONE) {
1353
0
        if (sizeof(void*) == 8) {
1354
0
          stack_offset += sizeof(void*);
1355
0
        } else {
1356
0
          stack_offset += IR_MAX(sizeof(void*), ir_get_type_size(insn->type));
1357
0
        }
1358
0
      }
1359
0
    }
1360
0
  }
1361
1362
0
  return stack_offset;
1363
0
}
1364
1365
static void ir_match_tailcalls(ir_ctx *ctx)
1366
0
{
1367
0
  ir_ref ref;
1368
0
  ir_insn *insn;
1369
0
  size_t args_stack_size = (size_t)-1;
1370
1371
0
  ref = ctx->ir_base[1].op1;
1372
0
  while (ref) {
1373
0
    insn = &ctx->ir_base[ref];
1374
0
    if (insn->op == IR_RETURN) {
1375
0
      if (insn->op1 == insn->op2) {
1376
0
        if (ctx->ir_base[insn->op1].op == IR_CALL) {
1377
0
          if (args_stack_size == (size_t)-1) {
1378
0
            args_stack_size = ir_calc_args_stack(ctx);
1379
            // TODO: "args_stack_size" must match the "params_stack_size"
1380
0
            if (args_stack_size) return;
1381
0
          }
1382
0
          if (ir_try_tailcall(ctx, insn->op1, &ctx->ir_base[insn->op1])) {
1383
0
            ctx->rules[ref] = IR_SKIPPED | IR_NOP;
1384
0
          }
1385
0
        }
1386
0
      } else if (insn->op2 == IR_UNUSED) {
1387
0
        if (ctx->ir_base[insn->op1].op == IR_CALL) {
1388
0
          if (args_stack_size == (size_t)-1) {
1389
0
            args_stack_size = ir_calc_args_stack(ctx);
1390
            // TODO: "args_stack_size" must match the "params_stack_size"
1391
0
            if (args_stack_size) return;
1392
0
          }
1393
0
          if (ir_try_tailcall(ctx, insn->op1, &ctx->ir_base[insn->op1])) {
1394
0
            ctx->rules[ref] = IR_SKIPPED | IR_NOP;
1395
0
          }
1396
#if 0
1397
        } else if (ctx->ir_base[insn->op1].op == IR_MERGE) {
1398
          if (args_stack_size == (size_t)-1) {
1399
            args_stack_size = ir_calc_args_stack(ctx);
1400
            // TODO: "args_stack_size" must match the "params_stack_size"
1401
            if (args_stack_size) return;
1402
          }
1403
          if (ir_try_tailcalls(ctx, &ctx->ir_base[insn->op1])) {
1404
            ctx->rules[insn->op1] = IR_SKIPPED | IR_NOP;
1405
            ctx->rules[ref] = IR_SKIPPED | IR_NOP;
1406
          }
1407
#endif
1408
0
        }
1409
0
      }
1410
0
    }
1411
0
    ref = insn->op3;
1412
0
  }
1413
0
}
1414
1415
int ir_match(ir_ctx *ctx)
1416
9.93k
{
1417
9.93k
  uint32_t b;
1418
9.93k
  ir_ref start, ref, *prev_ref;
1419
9.93k
  ir_block *bb;
1420
9.93k
  ir_insn *insn;
1421
9.93k
  uint32_t entries_count = 0;
1422
1423
9.93k
  ctx->rules = ir_mem_calloc(ctx->insns_count, sizeof(uint32_t));
1424
1425
9.93k
  prev_ref = ctx->prev_ref;
1426
9.93k
  if (!prev_ref) {
1427
0
    ir_build_prev_refs(ctx);
1428
0
    prev_ref = ctx->prev_ref;
1429
0
  }
1430
1431
9.93k
  if (ctx->entries_count) {
1432
3.03k
    ctx->entries = ir_mem_malloc(ctx->entries_count * sizeof(ir_ref));
1433
3.03k
  }
1434
1435
9.93k
  if ((ctx->flags & IR_OPT_TAILCALL)
1436
0
   && (ctx->flags & IR_FUNCTION)
1437
0
   && !(ctx->flags & IR_VARARG_FUNC)) {
1438
0
    ir_match_tailcalls(ctx);
1439
0
  }
1440
1441
326k
  for (b = ctx->cfg_blocks_count, bb = ctx->cfg_blocks + b; b > 0; b--, bb--) {
1442
316k
    IR_ASSERT(!(bb->flags & IR_BB_UNREACHABLE));
1443
316k
    start = bb->start;
1444
316k
    if (UNEXPECTED(bb->flags & IR_BB_ENTRY)) {
1445
3.16k
      IR_ASSERT(entries_count < ctx->entries_count);
1446
3.16k
      insn = &ctx->ir_base[start];
1447
3.16k
      IR_ASSERT(insn->op == IR_ENTRY);
1448
3.16k
      insn->op3 = entries_count;
1449
3.16k
      ctx->entries[entries_count] = b;
1450
3.16k
      entries_count++;
1451
3.16k
    }
1452
316k
    ctx->rules[start] = IR_SKIPPED | IR_NOP;
1453
316k
    if (ctx->ir_base[start].op == IR_BEGIN && ctx->ir_base[start].op2) {
1454
4
      ctx->flags2 |= IR_HAS_BLOCK_ADDR;
1455
4
    }
1456
316k
    ref = bb->end;
1457
316k
    if (bb->successors_count == 1) {
1458
188k
      insn = &ctx->ir_base[ref];
1459
188k
      if (insn->op == IR_END || insn->op == IR_LOOP_END) {
1460
185k
        if (!ctx->rules[ref]) {
1461
185k
          ctx->rules[ref] = insn->op;
1462
185k
        }
1463
185k
        ref = prev_ref[ref];
1464
185k
        if (ref == start && ctx->cfg_edges[bb->successors] != b) {
1465
92.6k
          if (EXPECTED(!(bb->flags & IR_BB_ENTRY))) {
1466
89.6k
            bb->flags |= IR_BB_EMPTY;
1467
89.6k
          } else if (ctx->flags & IR_MERGE_EMPTY_ENTRIES) {
1468
0
            bb->flags |= IR_BB_EMPTY;
1469
0
            if (ctx->cfg_edges[bb->successors] == b + 1) {
1470
0
              (bb + 1)->flags |= IR_BB_PREV_EMPTY_ENTRY;
1471
0
            }
1472
0
          }
1473
92.6k
          continue;
1474
92.6k
        }
1475
185k
      }
1476
188k
    }
1477
1478
224k
    ctx->bb_start = start; /* bb_start is used by matcher to avoid fusion of insns from different blocks */
1479
1480
1.47M
    while (ref != start) {
1481
1.24M
      uint32_t rule = ctx->rules[ref];
1482
1483
1.24M
      if (!rule) {
1484
683k
        ctx->rules[ref] = rule = ir_match_insn(ctx, ref);
1485
683k
      }
1486
1.24M
      ir_match_insn2(ctx, ref, rule);
1487
1.24M
      ref = prev_ref[ref];
1488
1.24M
    }
1489
224k
  }
1490
1491
9.93k
  if (ctx->entries_count) {
1492
3.03k
    ctx->entries_count = entries_count;
1493
3.03k
    if (!entries_count) {
1494
0
      ir_mem_free(ctx->entries);
1495
0
      ctx->entries = NULL;
1496
0
    }
1497
3.03k
  }
1498
1499
9.93k
  return 1;
1500
9.93k
}
1501
1502
int32_t ir_get_spill_slot_offset(const ir_ctx *ctx, ir_ref ref)
1503
4
{
1504
4
  int32_t offset;
1505
1506
4
  IR_ASSERT(ref >= 0 && ctx->vregs[ref] > 0 && ctx->live_intervals[ctx->vregs[ref]]);
1507
4
  offset = ctx->live_intervals[ctx->vregs[ref]]->stack_spill_pos;
1508
4
  IR_ASSERT(offset != -1);
1509
4
  return IR_SPILL_POS_TO_OFFSET(offset);
1510
4
}
1511
1512
const ir_call_conv_dsc *ir_get_call_conv_dsc(uint32_t flags)
1513
207k
{
1514
#ifdef IR_TARGET_X86
1515
  if ((flags & IR_CALL_CONV_MASK) == IR_CC_FASTCALL) {
1516
    return &ir_call_conv_x86_fastcall;
1517
  }
1518
#elif defined(IR_TARGET_X64)
1519
207k
  switch (flags & IR_CALL_CONV_MASK) {
1520
187k
    case IR_CC_DEFAULT:              return &ir_call_conv_default;
1521
19.7k
    case IR_CC_FASTCALL:             return &ir_call_conv_default;
1522
0
    case IR_CC_PRESERVE_NONE:        return &ir_call_conv_x86_64_preserve_none;
1523
0
    case IR_CC_X86_64_SYSV:          return &ir_call_conv_x86_64_sysv;
1524
0
    case IR_CC_X86_64_MS:            return &ir_call_conv_x86_64_ms;
1525
0
    default: break;
1526
207k
  }
1527
#elif defined(IR_TARGET_AARCH64)
1528
  switch (flags & IR_CALL_CONV_MASK) {
1529
    case IR_CC_DEFAULT:              return &ir_call_conv_default;
1530
    case IR_CC_FASTCALL:             return &ir_call_conv_default;
1531
    case IR_CC_PRESERVE_NONE:        return &ir_call_conv_aarch64_preserve_none;
1532
    case IR_CC_AARCH64_SYSV:         return &ir_call_conv_aarch64_sysv;
1533
    case IR_CC_AARCH64_DARWIN:       return &ir_call_conv_aarch64_darwin;
1534
    default: break;
1535
  }
1536
#endif
1537
0
  IR_ASSERT((flags & IR_CALL_CONV_MASK) == IR_CC_DEFAULT || (flags & IR_CALL_CONV_MASK) == IR_CC_BUILTIN);
1538
0
  return &ir_call_conv_default;
1539
0
}