• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / luajit / 6036488607

31 Aug 2023 10:56AM UTC coverage: 88.147% (+0.1%) from 88.01%
6036488607

push

github

fckxorg
tools: add cli flag to run profile dump parsers

It is really inconvenient to use a standalone shell script to parse
binary dumps from profilers. That is why this commit introduces a
CLI flag for tools in the LuaJIT, so now it is possible to parse
a memprof dump as simple as:
```
luajit -tm memprof.bin
luajit -tm --leak-only memprof.bin
```

And the sysprof too:
```
luajit -ts sysprof.bin
luajit -ts --split sysprof.bin
```

The `luajit-parse-memprof` and `luajit-parse-sysprof` are purged
as a result of these changes.

Closes tarantool/tarantool#5688

5336 of 5975 branches covered (0.0%)

Branch coverage included in aggregate %.

21 of 21 new or added lines in 1 file covered. (100.0%)

20476 of 23308 relevant lines covered (87.85%)

1295859.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.86
/src/lj_opt_loop.c
1
/*
2
** LOOP: Loop Optimizations.
3
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
4
*/
5

6
#define lj_opt_loop_c
7
#define LUA_CORE
8

9
#include "lj_obj.h"
10

11
#if LJ_HASJIT
12

13
#include "lj_err.h"
14
#include "lj_buf.h"
15
#include "lj_ir.h"
16
#include "lj_jit.h"
17
#include "lj_iropt.h"
18
#include "lj_trace.h"
19
#include "lj_snap.h"
20
#include "lj_vm.h"
21

22
/* Loop optimization:
23
**
24
** Traditional Loop-Invariant Code Motion (LICM) splits the instructions
25
** of a loop into invariant and variant instructions. The invariant
26
** instructions are hoisted out of the loop and only the variant
27
** instructions remain inside the loop body.
28
**
29
** Unfortunately LICM is mostly useless for compiling dynamic languages.
30
** The IR has many guards and most of the subsequent instructions are
31
** control-dependent on them. The first non-hoistable guard would
32
** effectively prevent hoisting of all subsequent instructions.
33
**
34
** That's why we use a special form of unrolling using copy-substitution,
35
** combined with redundancy elimination:
36
**
37
** The recorded instruction stream is re-emitted to the compiler pipeline
38
** with substituted operands. The substitution table is filled with the
39
** refs returned by re-emitting each instruction. This can be done
40
** on-the-fly, because the IR is in strict SSA form, where every ref is
41
** defined before its use.
42
**
43
** This aproach generates two code sections, separated by the LOOP
44
** instruction:
45
**
46
** 1. The recorded instructions form a kind of pre-roll for the loop. It
47
** contains a mix of invariant and variant instructions and performs
48
** exactly one loop iteration (but not necessarily the 1st iteration).
49
**
50
** 2. The loop body contains only the variant instructions and performs
51
** all remaining loop iterations.
52
**
53
** On first sight that looks like a waste of space, because the variant
54
** instructions are present twice. But the key insight is that the
55
** pre-roll honors the control-dependencies for *both* the pre-roll itself
56
** *and* the loop body!
57
**
58
** It also means one doesn't have to explicitly model control-dependencies
59
** (which, BTW, wouldn't help LICM much). And it's much easier to
60
** integrate sparse snapshotting with this approach.
61
**
62
** One of the nicest aspects of this approach is that all of the
63
** optimizations of the compiler pipeline (FOLD, CSE, FWD, etc.) can be
64
** reused with only minor restrictions (e.g. one should not fold
65
** instructions across loop-carried dependencies).
66
**
67
** But in general all optimizations can be applied which only need to look
68
** backwards into the generated instruction stream. At any point in time
69
** during the copy-substitution process this contains both a static loop
70
** iteration (the pre-roll) and a dynamic one (from the to-be-copied
71
** instruction up to the end of the partial loop body).
72
**
73
** Since control-dependencies are implicitly kept, CSE also applies to all
74
** kinds of guards. The major advantage is that all invariant guards can
75
** be hoisted, too.
76
**
77
** Load/store forwarding works across loop iterations, too. This is
78
** important if loop-carried dependencies are kept in upvalues or tables.
79
** E.g. 'self.idx = self.idx + 1' deep down in some OO-style method may
80
** become a forwarded loop-recurrence after inlining.
81
**
82
** Since the IR is in SSA form, loop-carried dependencies have to be
83
** modeled with PHI instructions. The potential candidates for PHIs are
84
** collected on-the-fly during copy-substitution. After eliminating the
85
** redundant ones, PHI instructions are emitted *below* the loop body.
86
**
87
** Note that this departure from traditional SSA form doesn't change the
88
** semantics of the PHI instructions themselves. But it greatly simplifies
89
** on-the-fly generation of the IR and the machine code.
90
*/
91

92
/* Some local macros to save typing. Undef'd at the end. */
93
#define IR(ref)                (&J->cur.ir[(ref)])
94

95
/* Pass IR on to next optimization in chain (FOLD). */
96
#define emitir(ot, a, b)        (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J))
97

98
/* Emit raw IR without passing through optimizations. */
99
#define emitir_raw(ot, a, b)        (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J))
100

101
/* -- PHI elimination ----------------------------------------------------- */
102

103
/* Emit or eliminate collected PHIs. */
104
static void loop_emit_phi(jit_State *J, IRRef1 *subst, IRRef1 *phi, IRRef nphi,
1,258✔
105
                          SnapNo onsnap)
106
{
107
  int passx = 0;
1,258✔
108
  IRRef i, j, nslots;
1,258✔
109
  IRRef invar = J->chain[IR_LOOP];
1,258✔
110
  /* Pass #1: mark redundant and potentially redundant PHIs. */
111
  for (i = 0, j = 0; i < nphi; i++) {
3,078✔
112
    IRRef lref = phi[i];
1,820✔
113
    IRRef rref = subst[lref];
1,820✔
114
    if (lref == rref || rref == REF_DROP) {  /* Invariants are redundant. */
1,820✔
115
      irt_clearphi(IR(lref)->t);
61✔
116
    } else {
117
      phi[j++] = (IRRef1)lref;
1,759✔
118
      if (!(IR(rref)->op1 == lref || IR(rref)->op2 == lref)) {
1,759✔
119
        /* Quick check for simple recurrences failed, need pass2. */
120
        irt_setmark(IR(lref)->t);
333✔
121
        passx = 1;
333✔
122
      }
123
    }
124
  }
125
  nphi = j;
1,258✔
126
  /* Pass #2: traverse variant part and clear marks of non-redundant PHIs. */
127
  if (passx) {
1,258✔
128
    SnapNo s;
193✔
129
    for (i = J->cur.nins-1; i > invar; i--) {
3,941✔
130
      IRIns *ir = IR(i);
3,748✔
131
      if (!irref_isk(ir->op2)) irt_clearmark(IR(ir->op2)->t);
3,748✔
132
      if (!irref_isk(ir->op1)) {
3,748✔
133
        irt_clearmark(IR(ir->op1)->t);
3,620✔
134
        if (ir->op1 < invar &&
3,620✔
135
            ir->o >= IR_CALLN && ir->o <= IR_CARG) {  /* ORDER IR */
958✔
136
          ir = IR(ir->op1);
137
          while (ir->o == IR_CARG) {
4✔
138
            if (!irref_isk(ir->op2)) irt_clearmark(IR(ir->op2)->t);
×
139
            if (irref_isk(ir->op1)) break;
×
140
            ir = IR(ir->op1);
×
141
            irt_clearmark(ir->t);
×
142
          }
143
        }
144
      }
145
    }
146
    for (s = J->cur.nsnap-1; s >= onsnap; s--) {
546✔
147
      SnapShot *snap = &J->cur.snap[s];
353✔
148
      SnapEntry *map = &J->cur.snapmap[snap->mapofs];
353✔
149
      MSize n, nent = snap->nent;
353✔
150
      for (n = 0; n < nent; n++) {
1,171✔
151
        IRRef ref = snap_ref(map[n]);
818✔
152
        if (!irref_isk(ref)) irt_clearmark(IR(ref)->t);
818✔
153
      }
154
    }
155
  }
156
  /* Pass #3: add PHIs for variant slots without a corresponding SLOAD. */
157
  nslots = J->baseslot+J->maxslot;
1,258✔
158
  for (i = 1; i < nslots; i++) {
15,417✔
159
    IRRef ref = tref_ref(J->slot[i]);
14,159✔
160
    while (!irref_isk(ref) && ref != subst[ref]) {
14,170✔
161
      IRIns *ir = IR(ref);
2,956✔
162
      irt_clearmark(ir->t);  /* Unmark potential uses, too. */
2,956✔
163
      if (irt_isphi(ir->t) || irt_ispri(ir->t))
2,956✔
164
        break;
165
      irt_setphi(ir->t);
176✔
166
      if (nphi >= LJ_MAX_PHI)
176✔
167
        lj_trace_err(J, LJ_TRERR_PHIOV);
×
168
      phi[nphi++] = (IRRef1)ref;
176✔
169
      ref = subst[ref];
176✔
170
      if (ref > invar)
176✔
171
        break;
172
    }
173
  }
174
  /* Pass #4: propagate non-redundant PHIs. */
175
  while (passx) {
1,451✔
176
    passx = 0;
177
    for (i = 0; i < nphi; i++) {
777✔
178
      IRRef lref = phi[i];
584✔
179
      IRIns *ir = IR(lref);
584✔
180
      if (!irt_ismarked(ir->t)) {  /* Propagate only from unmarked PHIs. */
584✔
181
        IRIns *irr = IR(subst[lref]);
520✔
182
        if (irt_ismarked(irr->t)) {  /* Right ref points to other PHI? */
520✔
183
          irt_clearmark(irr->t);  /* Mark that PHI as non-redundant. */
×
184
          passx = 1;  /* Retry. */
×
185
        }
186
      }
187
    }
188
  }
189
  /* Pass #5: emit PHI instructions or eliminate PHIs. */
190
  for (i = 0; i < nphi; i++) {
3,193✔
191
    IRRef lref = phi[i];
1,935✔
192
    IRIns *ir = IR(lref);
1,935✔
193
    if (!irt_ismarked(ir->t)) {  /* Emit PHI if not marked. */
1,935✔
194
      IRRef rref = subst[lref];
1,871✔
195
      if (rref > invar)
1,871✔
196
        irt_setphi(IR(rref)->t);
1,813✔
197
      emitir_raw(IRT(IR_PHI, irt_type(ir->t)), lref, rref);
1,871✔
198
    } else {  /* Otherwise eliminate PHI. */
199
      irt_clearmark(ir->t);
64✔
200
      irt_clearphi(ir->t);
64✔
201
    }
202
  }
203
}
1,258✔
204

205
/* -- Loop unrolling using copy-substitution ------------------------------ */
206

207
/* Copy-substitute snapshot. */
208
static void loop_subst_snap(jit_State *J, SnapShot *osnap,
2,069✔
209
                            SnapEntry *loopmap, IRRef1 *subst)
210
{
211
  SnapEntry *nmap, *omap = &J->cur.snapmap[osnap->mapofs];
2,069✔
212
  SnapEntry *nextmap = &J->cur.snapmap[snap_nextofs(&J->cur, osnap)];
2,069✔
213
  MSize nmapofs;
2,069✔
214
  MSize on, ln, nn, onent = osnap->nent;
2,069✔
215
  BCReg nslots = osnap->nslots;
2,069✔
216
  SnapShot *snap = &J->cur.snap[J->cur.nsnap];
2,069✔
217
  if (irt_isguard(J->guardemit)) {  /* Guard inbetween? */
2,069✔
218
    nmapofs = J->cur.nsnapmap;
1,726✔
219
    J->cur.nsnap++;  /* Add new snapshot. */
1,726✔
220
  } else {  /* Otherwise overwrite previous snapshot. */
221
    snap--;
343✔
222
    nmapofs = snap->mapofs;
343✔
223
  }
224
  J->guardemit.irt = 0;
2,069✔
225
  /* Setup new snapshot. */
226
  snap->mapofs = (uint32_t)nmapofs;
2,069✔
227
  snap->ref = (IRRef1)J->cur.nins;
2,069✔
228
  snap->mcofs = 0;
2,069✔
229
  snap->nslots = nslots;
2,069✔
230
  snap->topslot = osnap->topslot;
2,069✔
231
  snap->count = 0;
2,069✔
232
  nmap = &J->cur.snapmap[nmapofs];
2,069✔
233
  /* Substitute snapshot slots. */
234
  on = ln = nn = 0;
2,069✔
235
  while (on < onent) {
7,255✔
236
    SnapEntry osn = omap[on], lsn = loopmap[ln];
5,186✔
237
    if (snap_slot(lsn) < snap_slot(osn)) {  /* Copy slot from loop map. */
5,186✔
238
      nmap[nn++] = lsn;
639✔
239
      ln++;
639✔
240
    } else {  /* Copy substituted slot from snapshot map. */
241
      if (snap_slot(lsn) == snap_slot(osn)) ln++;  /* Shadowed loop slot. */
4,547✔
242
      if (!irref_isk(snap_ref(osn)))
4,547✔
243
        osn = snap_setref(osn, subst[snap_ref(osn)]);
3,067✔
244
      nmap[nn++] = osn;
4,547✔
245
      on++;
4,547✔
246
    }
247
  }
248
  while (snap_slot(loopmap[ln]) < nslots)  /* Copy remaining loop slots. */
2,246✔
249
    nmap[nn++] = loopmap[ln++];
177✔
250
  snap->nent = (uint8_t)nn;
2,069✔
251
  omap += onent;
2,069✔
252
  nmap += nn;
2,069✔
253
  while (omap < nextmap)  /* Copy PC + frame links. */
6,207✔
254
    *nmap++ = *omap++;
4,138✔
255
  J->cur.nsnapmap = (uint32_t)(nmap - J->cur.snapmap);
2,069✔
256
}
2,069✔
257

258
typedef struct LoopState {
259
  jit_State *J;
260
  IRRef1 *subst;
261
  MSize sizesubst;
262
} LoopState;
263

264
/* Unroll loop. */
265
static void loop_unroll(LoopState *lps)
1,260✔
266
{
267
  jit_State *J = lps->J;
1,260✔
268
  IRRef1 phi[LJ_MAX_PHI];
1,260✔
269
  uint32_t nphi = 0;
1,260✔
270
  IRRef1 *subst;
1,260✔
271
  SnapNo onsnap;
1,260✔
272
  SnapShot *osnap, *loopsnap;
1,260✔
273
  SnapEntry *loopmap, *psentinel;
1,260✔
274
  IRRef ins, invar;
1,260✔
275

276
  /* Allocate substitution table.
277
  ** Only non-constant refs in [REF_BIAS,invar) are valid indexes.
278
  */
279
  invar = J->cur.nins;
1,260✔
280
  lps->sizesubst = invar - REF_BIAS;
1,260✔
281
  lps->subst = lj_mem_newvec(J->L, lps->sizesubst, IRRef1);
1,260✔
282
  subst = lps->subst - REF_BIAS;
1,260✔
283
  subst[REF_BASE] = REF_BASE;
1,260✔
284

285
  /* LOOP separates the pre-roll from the loop body. */
286
  emitir_raw(IRTG(IR_LOOP, IRT_NIL), 0, 0);
1,260✔
287

288
  /* Grow snapshot buffer and map for copy-substituted snapshots.
289
  ** Need up to twice the number of snapshots minus #0 and loop snapshot.
290
  ** Need up to twice the number of entries plus fallback substitutions
291
  ** from the loop snapshot entries for each new snapshot.
292
  ** Caveat: both calls may reallocate J->cur.snap and J->cur.snapmap!
293
  */
294
  onsnap = J->cur.nsnap;
1,260✔
295
  lj_snap_grow_buf(J, 2*onsnap-2);
1,260✔
296
  lj_snap_grow_map(J, J->cur.nsnapmap*2+(onsnap-2)*J->cur.snap[onsnap-1].nent);
1,260✔
297

298
  /* The loop snapshot is used for fallback substitutions. */
299
  loopsnap = &J->cur.snap[onsnap-1];
1,260✔
300
  loopmap = &J->cur.snapmap[loopsnap->mapofs];
1,260✔
301
  /* The PC of snapshot #0 and the loop snapshot must match. */
302
  psentinel = &loopmap[loopsnap->nent];
1,260✔
303
  lj_assertJ(*psentinel == J->cur.snapmap[J->cur.snap[0].nent],
1,260✔
304
             "mismatched PC for loop snapshot");
305
  *psentinel = SNAP(255, 0, 0);  /* Replace PC with temporary sentinel. */
1,260✔
306

307
  /* Start substitution with snapshot #1 (#0 is empty for root traces). */
308
  osnap = &J->cur.snap[1];
1,260✔
309

310
  /* Copy and substitute all recorded instructions and snapshots. */
311
  for (ins = REF_FIRST; ins < invar; ins++) {
29,639✔
312
    IRIns *ir;
28,381✔
313
    IRRef op1, op2;
28,381✔
314

315
    if (ins >= osnap->ref)  /* Instruction belongs to next snapshot? */
28,381✔
316
      loop_subst_snap(J, osnap++, loopmap, subst);  /* Copy-substitute it. */
2,069✔
317

318
    /* Substitute instruction operands. */
319
    ir = IR(ins);
28,381✔
320
    op1 = ir->op1;
28,381✔
321
    if (!irref_isk(op1)) op1 = subst[op1];
28,381✔
322
    op2 = ir->op2;
28,381✔
323
    if (!irref_isk(op2)) op2 = subst[op2];
28,381✔
324
    if (irm_kind(lj_ir_mode[ir->o]) == IRM_N &&
28,381✔
325
        op1 == ir->op1 && op2 == ir->op2) {  /* Regular invariant ins? */
8,392✔
326
      subst[ins] = (IRRef1)ins;  /* Shortcut. */
7,013✔
327
    } else {
328
      /* Re-emit substituted instruction to the FOLD/CSE/etc. pipeline. */
329
      IRType1 t = ir->t;  /* Get this first, since emitir may invalidate ir. */
21,368✔
330
      IRRef ref = tref_ref(emitir(ir->ot & ~IRT_ISPHI, op1, op2));
21,368✔
331
      subst[ins] = (IRRef1)ref;
21,367✔
332
      if (ref != ins) {
21,367✔
333
        IRIns *irr = IR(ref);
14,127✔
334
        if (ref < invar) {  /* Loop-carried dependency? */
14,127✔
335
          /* Potential PHI? */
336
          if (!irref_isk(ref) && !irt_isphi(irr->t) && !irt_ispri(irr->t)) {
2,164✔
337
            irt_setphi(irr->t);
1,822✔
338
            if (nphi >= LJ_MAX_PHI)
1,822✔
339
              lj_trace_err(J, LJ_TRERR_PHIOV);
×
340
            phi[nphi++] = (IRRef1)ref;
1,822✔
341
          }
342
          /* Check all loop-carried dependencies for type instability. */
343
          if (!irt_sametype(t, irr->t)) {
2,164✔
344
            if (irt_isinteger(t) && irt_isinteger(irr->t))
57✔
345
              continue;
29✔
346
            else if (irt_isnum(t) && irt_isinteger(irr->t))  /* Fix int->num. */
28✔
347
              ref = tref_ref(emitir(IRTN(IR_CONV), ref, IRCONV_NUM_INT));
27✔
348
            else if (irt_isnum(irr->t) && irt_isinteger(t))  /* Fix num->int. */
1✔
349
              ref = tref_ref(emitir(IRTGI(IR_CONV), ref,
×
350
                                    IRCONV_INT_NUM|IRCONV_CHECK));
351
            else
352
              lj_trace_err(J, LJ_TRERR_TYPEINS);
1✔
353
            subst[ins] = (IRRef1)ref;
27✔
354
            irr = IR(ref);
27✔
355
            goto phiconv;
27✔
356
          }
357
        } else if (ref != REF_DROP && irr->o == IR_CONV &&
11,963✔
358
                   ref > invar && irr->op1 < invar) {
457✔
359
          /* May need an extra PHI for a CONV. */
360
          ref = irr->op1;
320✔
361
          irr = IR(ref);
320✔
362
        phiconv:
347✔
363
          if (ref < invar && !irref_isk(ref) && !irt_isphi(irr->t)) {
347✔
364
            irt_setphi(irr->t);
1✔
365
            if (nphi >= LJ_MAX_PHI)
1✔
366
              lj_trace_err(J, LJ_TRERR_PHIOV);
×
367
            phi[nphi++] = (IRRef1)ref;
1✔
368
          }
369
        }
370
      }
371
    }
372
  }
373
  if (!irt_isguard(J->guardemit))  /* Drop redundant snapshot. */
1,258✔
374
    J->cur.nsnapmap = (uint32_t)J->cur.snap[--J->cur.nsnap].mapofs;
2✔
375
  lj_assertJ(J->cur.nsnapmap <= J->sizesnapmap, "bad snapshot map index");
1,258✔
376
  *psentinel = J->cur.snapmap[J->cur.snap[0].nent];  /* Restore PC. */
1,258✔
377

378
  loop_emit_phi(J, subst, phi, nphi, onsnap);
1,258✔
379
}
1,258✔
380

381
/* Undo any partial changes made by the loop optimization. */
382
static void loop_undo(jit_State *J, IRRef ins, SnapNo nsnap, MSize nsnapmap)
2✔
383
{
384
  ptrdiff_t i;
2✔
385
  SnapShot *snap = &J->cur.snap[nsnap-1];
2✔
386
  SnapEntry *map = J->cur.snapmap;
2✔
387
  map[snap->mapofs + snap->nent] = map[J->cur.snap[0].nent];  /* Restore PC. */
2✔
388
  J->cur.nsnapmap = (uint32_t)nsnapmap;
2✔
389
  J->cur.nsnap = nsnap;
2✔
390
  J->guardemit.irt = 0;
2✔
391
  lj_ir_rollback(J, ins);
2✔
392
  for (i = 0; i < BPROP_SLOTS; i++) {  /* Remove backprop. cache entries. */
36✔
393
    BPropEntry *bp = &J->bpropcache[i];
32✔
394
    if (bp->val >= ins)
32✔
395
      bp->key = 0;
×
396
  }
397
  for (ins--; ins >= REF_FIRST; ins--) {  /* Remove flags. */
20✔
398
    IRIns *ir = IR(ins);
18✔
399
    irt_clearphi(ir->t);
18✔
400
    irt_clearmark(ir->t);
18✔
401
  }
402
}
2✔
403

404
/* Protected callback for loop optimization. */
405
static TValue *cploop_opt(lua_State *L, lua_CFunction dummy, void *ud)
1,260✔
406
{
407
  UNUSED(L); UNUSED(dummy);
1,260✔
408
  loop_unroll((LoopState *)ud);
1,260✔
409
  return NULL;
1,258✔
410
}
411

412
/* Loop optimization. */
413
int lj_opt_loop(jit_State *J)
1,260✔
414
{
415
  IRRef nins = J->cur.nins;
1,260✔
416
  SnapNo nsnap = J->cur.nsnap;
1,260✔
417
  MSize nsnapmap = J->cur.nsnapmap;
1,260✔
418
  LoopState lps;
1,260✔
419
  int errcode;
1,260✔
420
  lps.J = J;
1,260✔
421
  lps.subst = NULL;
1,260✔
422
  lps.sizesubst = 0;
1,260✔
423
  errcode = lj_vm_cpcall(J->L, NULL, &lps, cploop_opt);
1,260✔
424
  lj_mem_freevec(J2G(J), lps.subst, lps.sizesubst, IRRef1);
1,260✔
425
  if (LJ_UNLIKELY(errcode)) {
1,260✔
426
    lua_State *L = J->L;
2✔
427
    if (errcode == LUA_ERRRUN && tvisnumber(L->top-1)) {  /* Trace error? */
2✔
428
      int32_t e = numberVint(L->top-1);
2✔
429
      switch ((TraceError)e) {
2✔
430
      case LJ_TRERR_TYPEINS:  /* Type instability. */
2✔
431
      case LJ_TRERR_GFAIL:  /* Guard would always fail. */
432
        /* Unrolling via recording fixes many cases, e.g. a flipped boolean. */
433
        if (--J->instunroll < 0)  /* But do not unroll forever. */
2✔
434
          break;
435
        L->top--;  /* Remove error object. */
2✔
436
        loop_undo(J, nins, nsnap, nsnapmap);
2✔
437
        return 1;  /* Loop optimization failed, continue recording. */
2✔
438
      default:
439
        break;
440
      }
441
    }
×
442
    lj_err_throw(L, errcode);  /* Propagate all other errors. */
×
443
  }
444
  return 0;  /* Loop optimization is ok. */
445
}
446

447
#undef IR
448
#undef emitir
449
#undef emitir_raw
450

451
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc