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

tarantool / luajit / 9593251616

20 Jun 2024 07:14AM UTC coverage: 92.646% (+0.02%) from 92.624%
9593251616

push

github

Buristan
cmake: fix warning about minimum required version

Since CMake 3.27 compatibility with versions of CMake older than
3.5 is deprecated [1]. CMake produces an annoying warning on the
configuration stage:

| CMake Deprecation Warning at src/CMakeLists.txt:7 (cmake_minimum_required):
|  Compatibility with CMake < 3.5 will be removed from a future version of
|  CMake.

We cannot bump a minimum required CMake version without bumping it in
the Tarantool build system. However, we can set a <policy_max>
(introduced in CMake 3.12, see [1]) and suppress a warning. <policy_max>
means that this CMake version is known to work properly and will not
result in any build errors right away for higher versions.

Note that the current CMake minimum required version in Tarantool is
equal to 2.8, but <policy_max> is introduced in CMake 3.12 [1].
However, according to [1] it is not a problem because if CMake is "older
than 3.12, the extra ... dots will be seen as version component
separators, resulting in the ...<max> part being ignored and preserving
the pre-3.12 behavior of basing policies on <min>".

<policy_max> is set to 3.18 because compatibility with versions of CMake
older than 2.8.12 is deprecated. Calls to
cmake_minimum_required(VERSION) that do not specify at least 2.8.12 as
their policy version (optionally via ...<max>) will produce a
deprecation warning in CMake 3.19 and above [2]. Compatibility with
2.8.12 is needed for CMP0002 [3], see commit 049e296ee114 ("test: run
LuaJIT tests via CMake").

1. https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html
2. https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html#policy-settings
3. https://cmake.org/cmake/help/latest/policy/CMP0002.html

5665 of 6021 branches covered (94.09%)

Branch coverage included in aggregate %.

21621 of 23431 relevant lines covered (92.28%)

2937993.42 hits per line

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

92.03
/src/lj_ffrecord.c
1
/*
2
** Fast function call recorder.
3
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
4
*/
5

6
#define lj_ffrecord_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_str.h"
15
#include "lj_tab.h"
16
#include "lj_frame.h"
17
#include "lj_bc.h"
18
#include "lj_ff.h"
19
#include "lj_ir.h"
20
#include "lj_jit.h"
21
#include "lj_ircall.h"
22
#include "lj_iropt.h"
23
#include "lj_trace.h"
24
#include "lj_record.h"
25
#include "lj_ffrecord.h"
26
#include "lj_crecord.h"
27
#include "lj_dispatch.h"
28
#include "lj_vm.h"
29
#include "lj_strscan.h"
30
#include "lj_strfmt.h"
31

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

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

38
/* -- Fast function recording handlers ------------------------------------ */
39

40
/* Conventions for fast function call handlers:
41
**
42
** The argument slots start at J->base[0]. All of them are guaranteed to be
43
** valid and type-specialized references. J->base[J->maxslot] is set to 0
44
** as a sentinel. The runtime argument values start at rd->argv[0].
45
**
46
** In general fast functions should check for presence of all of their
47
** arguments and for the correct argument types. Some simplifications
48
** are allowed if the interpreter throws instead. But even if recording
49
** is aborted, the generated IR must be consistent (no zero-refs).
50
**
51
** The number of results in rd->nres is set to 1. Handlers that return
52
** a different number of results need to override it. A negative value
53
** prevents return processing (e.g. for pending calls).
54
**
55
** Results need to be stored starting at J->base[0]. Return processing
56
** moves them to the right slots later.
57
**
58
** The per-ffid auxiliary data is the value of the 2nd part of the
59
** LJLIB_REC() annotation. This allows handling similar functionality
60
** in a common handler.
61
*/
62

63
/* Type of handler to record a fast function. */
64
typedef void (LJ_FASTCALL *RecordFunc)(jit_State *J, RecordFFData *rd);
65

66
/* Get runtime value of int argument. */
67
static int32_t argv2int(jit_State *J, TValue *o)
354✔
68
{
69
  if (!lj_strscan_numberobj(o))
354✔
70
    lj_trace_err(J, LJ_TRERR_BADTYPE);
×
71
  return tvisint(o) ? intV(o) : lj_num2int(numV(o));
354✔
72
}
73

74
/* Get runtime value of string argument. */
75
static GCstr *argv2str(jit_State *J, TValue *o)
454✔
76
{
77
  if (LJ_LIKELY(tvisstr(o))) {
454✔
78
    return strV(o);
454✔
79
  } else {
80
    GCstr *s;
×
81
    if (!tvisnumber(o))
×
82
      lj_trace_err(J, LJ_TRERR_BADTYPE);
×
83
    s = lj_strfmt_number(J->L, o);
×
84
    setstrV(J->L, o, s);
×
85
    return s;
×
86
  }
87
}
88

89
/* Return number of results wanted by caller. */
90
static ptrdiff_t results_wanted(jit_State *J)
81✔
91
{
92
  TValue *frame = J->L->base-1;
81✔
93
  if (frame_islua(frame))
81✔
94
    return (ptrdiff_t)bc_b(frame_pc(frame)[-1]) - 1;
81✔
95
  else
96
    return -1;
97
}
98

99
static TValue *rec_stop_stitch_cp(lua_State *L, lua_CFunction dummy, void *ud)
227✔
100
{
101
  jit_State *J = (jit_State *)ud;
227✔
102
  lj_record_stop(J, LJ_TRLINK_STITCH, 0);
227✔
103
  UNUSED(L); UNUSED(dummy);
224✔
104
  return NULL;
224✔
105
}
106

107
/* Trace stitching: add continuation below frame to start a new trace. */
108
static void recff_stitch(jit_State *J)
227✔
109
{
110
  ASMFunction cont = lj_cont_stitch;
227✔
111
  lua_State *L = J->L;
227✔
112
  TValue *base = L->base;
227✔
113
  BCReg nslot = J->maxslot + 1 + LJ_FR2;
227✔
114
  TValue *nframe = base + 1 + LJ_FR2;
227✔
115
  const BCIns *pc = frame_pc(base-1);
227✔
116
  TValue *pframe = frame_prevl(base-1);
227✔
117
  int errcode;
227✔
118

119
  /* Move func + args up in Lua stack and insert continuation. */
120
  memmove(&base[1], &base[-1-LJ_FR2], sizeof(TValue)*nslot);
227✔
121
  setframe_ftsz(nframe, ((char *)nframe - (char *)pframe) + FRAME_CONT);
227✔
122
  setcont(base-LJ_FR2, cont);
227✔
123
  setframe_pc(base, pc);
227✔
124
  setnilV(base-1-LJ_FR2);  /* Incorrect, but rec_check_slots() won't run anymore. */
227✔
125
  L->base += 2 + LJ_FR2;
227✔
126
  L->top += 2 + LJ_FR2;
227✔
127

128
  /* Ditto for the IR. */
129
  memmove(&J->base[1], &J->base[-1-LJ_FR2], sizeof(TRef)*nslot);
227✔
130
#if LJ_FR2
131
  J->base[2] = TREF_FRAME;
227✔
132
  J->base[-1] = lj_ir_k64(J, IR_KNUM, u64ptr(contptr(cont)));
227✔
133
  J->base[0] = lj_ir_k64(J, IR_KNUM, u64ptr(pc)) | TREF_CONT;
227✔
134
#else
135
  J->base[0] = lj_ir_kptr(J, contptr(cont)) | TREF_CONT;
136
#endif
137
  J->ktrace = tref_ref((J->base[-1-LJ_FR2] = lj_ir_ktrace(J)));
227✔
138
  J->base += 2 + LJ_FR2;
227✔
139
  J->baseslot += 2 + LJ_FR2;
227✔
140
  J->framedepth++;
227✔
141

142
  errcode = lj_vm_cpcall(L, NULL, J, rec_stop_stitch_cp);
227✔
143

144
  /* Undo Lua stack changes. */
145
  memmove(&base[-1-LJ_FR2], &base[1], sizeof(TValue)*nslot);
227✔
146
  setframe_pc(base-1, pc);
227✔
147
  L->base -= 2 + LJ_FR2;
227✔
148
  L->top -= 2 + LJ_FR2;
227✔
149

150
  if (errcode) {
227✔
151
    if (errcode == LUA_ERRRUN)
3✔
152
      copyTV(L, L->top-1, L->top + (1 + LJ_FR2));
1✔
153
    else
154
      setintV(L->top-1, (int32_t)LJ_TRERR_RECERR);
2✔
155
    lj_err_throw(L, errcode);  /* Propagate errors. */
3✔
156
  }
157
}
224✔
158

159
/* Fallback handler for fast functions that are not recorded (yet). */
160
static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd)
267✔
161
{
162
  if (J->cur.nins < (IRRef)J->param[JIT_P_minstitch] + REF_BASE) {
267✔
163
    lj_trace_err_info(J, LJ_TRERR_TRACEUV);
8✔
164
  } else {
165
    /* Can only stitch from Lua call. */
166
    if (J->framedepth && frame_islua(J->L->base-1)) {
259✔
167
      BCOp op = bc_op(*frame_pc(J->L->base-1));
244✔
168
      /* Stitched trace cannot start with *M op with variable # of args. */
169
      if (!(op == BC_CALLM || op == BC_CALLMT ||
244✔
170
            op == BC_RETM || op == BC_TSETM)) {
234✔
171
        switch (J->fn->c.ffid) {
228✔
172
        case FF_error:
173
        case FF_debug_sethook:
174
        case FF_jit_flush:
175
          break;  /* Don't stitch across special builtins. */
176
        default:
227✔
177
          recff_stitch(J);  /* Use trace stitching. */
227✔
178
          rd->nres = -1;
224✔
179
          return;
224✔
180
        }
181
      }
31✔
182
    }
183
    /* Otherwise stop trace and return to interpreter. */
184
    lj_record_stop(J, LJ_TRLINK_RETURN, 0);
32✔
185
    rd->nres = -1;
32✔
186
  }
187
}
188

189
/* Fallback handler for unsupported variants of fast functions. */
190
#define recff_nyiu        recff_nyi
191

192
/* Must stop the trace for classic C functions with arbitrary side-effects. */
193
#define recff_c                recff_nyi
194

195
/* Emit BUFHDR for the global temporary buffer. */
196
static TRef recff_bufhdr(jit_State *J)
341✔
197
{
198
  return emitir(IRT(IR_BUFHDR, IRT_PGC),
341✔
199
                lj_ir_kptr(J, &J2G(J)->tmpbuf), IRBUFHDR_RESET);
200
}
201

202
/* -- Base library fast functions ----------------------------------------- */
203

204
static void LJ_FASTCALL recff_assert(jit_State *J, RecordFFData *rd)
21,152✔
205
{
206
  /* Arguments already specialized. The interpreter throws for nil/false. */
207
  rd->nres = J->maxslot;  /* Pass through all arguments. */
21,152✔
208
}
21,152✔
209

210
static void LJ_FASTCALL recff_type(jit_State *J, RecordFFData *rd)
34✔
211
{
212
  /* Arguments already specialized. Result is a constant string. Neat, huh? */
213
  uint32_t t;
34✔
214
  if (tvisnumber(&rd->argv[0]))
34✔
215
    t = ~LJ_TNUMX;
216
  else if (LJ_64 && !LJ_GC64 && tvislightud(&rd->argv[0]))
31✔
217
    t = ~LJ_TLIGHTUD;
218
  else
219
    t = ~itype(&rd->argv[0]);
31✔
220
  J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[t]));
34✔
221
  UNUSED(rd);
34✔
222
}
34✔
223

224
static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd)
15✔
225
{
226
  TRef tr = J->base[0];
15✔
227
  if (tr) {
15✔
228
    RecordIndex ix;
15✔
229
    ix.tab = tr;
15✔
230
    copyTV(J->L, &ix.tabv, &rd->argv[0]);
15✔
231
    if (lj_record_mm_lookup(J, &ix, MM_metatable))
15✔
232
      J->base[0] = ix.mobj;
1✔
233
    else
234
      J->base[0] = ix.mt;
14✔
235
  }  /* else: Interpreter will throw. */
236
}
15✔
237

238
static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd)
30✔
239
{
240
  TRef tr = J->base[0];
30✔
241
  TRef mt = J->base[1];
30✔
242
  if (tref_istab(tr) && (tref_istab(mt) || (mt && tref_isnil(mt)))) {
30✔
243
    TRef fref, mtref;
30✔
244
    RecordIndex ix;
30✔
245
    ix.tab = tr;
30✔
246
    copyTV(J->L, &ix.tabv, &rd->argv[0]);
30✔
247
    lj_record_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable. */
30✔
248
    fref = emitir(IRT(IR_FREF, IRT_PGC), tr, IRFL_TAB_META);
30✔
249
    mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt;
30✔
250
    emitir(IRT(IR_FSTORE, IRT_TAB), fref, mtref);
30✔
251
    if (!tref_isnil(mt))
30✔
252
      emitir(IRT(IR_TBAR, IRT_TAB), tr, 0);
28✔
253
    J->base[0] = tr;
30✔
254
    J->needsnap = 1;
30✔
255
  }  /* else: Interpreter will throw. */
256
}
30✔
257

258
static void LJ_FASTCALL recff_rawget(jit_State *J, RecordFFData *rd)
2✔
259
{
260
  RecordIndex ix;
2✔
261
  ix.tab = J->base[0]; ix.key = J->base[1];
2✔
262
  if (tref_istab(ix.tab) && ix.key) {
2✔
263
    ix.val = 0; ix.idxchain = 0;
2✔
264
    settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
2✔
265
    copyTV(J->L, &ix.keyv, &rd->argv[1]);
2✔
266
    J->base[0] = lj_record_idx(J, &ix);
2✔
267
  }  /* else: Interpreter will throw. */
268
}
2✔
269

270
static void LJ_FASTCALL recff_rawset(jit_State *J, RecordFFData *rd)
7✔
271
{
272
  RecordIndex ix;
7✔
273
  ix.tab = J->base[0]; ix.key = J->base[1]; ix.val = J->base[2];
7✔
274
  if (tref_istab(ix.tab) && ix.key && ix.val) {
7✔
275
    ix.idxchain = 0;
7✔
276
    settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
7✔
277
    copyTV(J->L, &ix.keyv, &rd->argv[1]);
7✔
278
    copyTV(J->L, &ix.valv, &rd->argv[2]);
7✔
279
    lj_record_idx(J, &ix);
7✔
280
    /* Pass through table at J->base[0] as result. */
281
  }  /* else: Interpreter will throw. */
282
}
7✔
283

284
static void LJ_FASTCALL recff_rawequal(jit_State *J, RecordFFData *rd)
2✔
285
{
286
  TRef tra = J->base[0];
2✔
287
  TRef trb = J->base[1];
2✔
288
  if (tra && trb) {
2✔
289
    int diff = lj_record_objcmp(J, tra, trb, &rd->argv[0], &rd->argv[1]);
2✔
290
    J->base[0] = diff ? TREF_FALSE : TREF_TRUE;
3✔
291
  }  /* else: Interpreter will throw. */
292
}
2✔
293

294
#if LJ_52
295
static void LJ_FASTCALL recff_rawlen(jit_State *J, RecordFFData *rd)
296
{
297
  TRef tr = J->base[0];
298
  if (tref_isstr(tr))
299
    J->base[0] = emitir(IRTI(IR_FLOAD), tr, IRFL_STR_LEN);
300
  else if (tref_istab(tr))
301
    J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, tr);
302
  /* else: Interpreter will throw. */
303
  UNUSED(rd);
304
}
305
#endif
306

307
/* Determine mode of select() call. */
308
int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv)
13✔
309
{
310
  if (tref_isstr(tr) && *strVdata(tv) == '#') {  /* select('#', ...) */
13✔
311
    if (strV(tv)->len == 1) {
3✔
312
      emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, strV(tv)));
3✔
313
    } else {
314
      TRef trptr = emitir(IRT(IR_STRREF, IRT_PGC), tr, lj_ir_kint(J, 0));
×
315
      TRef trchar = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY);
×
316
      emitir(IRTG(IR_EQ, IRT_INT), trchar, lj_ir_kint(J, '#'));
×
317
    }
318
    return 0;
3✔
319
  } else {  /* select(n, ...) */
320
    int32_t start = argv2int(J, tv);
10✔
321
    if (start == 0) lj_trace_err(J, LJ_TRERR_BADTYPE);  /* A bit misleading. */
10✔
322
    return start;
323
  }
324
}
325

326
static void LJ_FASTCALL recff_select(jit_State *J, RecordFFData *rd)
5✔
327
{
328
  TRef tr = J->base[0];
5✔
329
  if (tr) {
5✔
330
    ptrdiff_t start = lj_ffrecord_select_mode(J, tr, &rd->argv[0]);
5✔
331
    if (start == 0) {  /* select('#', ...) */
5✔
332
      J->base[0] = lj_ir_kint(J, J->maxslot - 1);
2✔
333
    } else if (tref_isk(tr)) {  /* select(k, ...) */
3✔
334
      ptrdiff_t n = (ptrdiff_t)J->maxslot;
3✔
335
      if (start < 0) start += n;
3✔
336
      else if (start > n) start = n;
2✔
337
      if (start >= 1) {
3✔
338
        ptrdiff_t i;
2✔
339
        rd->nres = n - start;
2✔
340
        for (i = 0; i < n - start; i++)
5✔
341
          J->base[i] = J->base[start+i];
3✔
342
      }  /* else: Interpreter will throw. */
343
    } else {
344
      recff_nyiu(J, rd);
×
345
      return;
×
346
    }
347
  }  /* else: Interpreter will throw. */
348
}
349

350
static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd)
712✔
351
{
352
  TRef tr = J->base[0];
712✔
353
  TRef base = J->base[1];
712✔
354
  if (tr && !tref_isnil(base)) {
712✔
355
    base = lj_opt_narrow_toint(J, base);
3✔
356
    if (!tref_isk(base) || IR(tref_ref(base))->i != 10) {
3✔
357
      recff_nyiu(J, rd);
3✔
358
      return;
3✔
359
    }
360
  }
361
  if (tref_isnumber_str(tr)) {
709✔
362
    if (tref_isstr(tr)) {
125✔
363
      TValue tmp;
115✔
364
      if (!lj_strscan_num(strV(&rd->argv[0]), &tmp)) {
115✔
365
        recff_nyiu(J, rd);  /* Would need an inverted STRTO for this case. */
×
366
        return;
×
367
      }
368
      tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0);
115✔
369
    }
370
#if LJ_HASFFI
371
  } else if (tref_iscdata(tr)) {
584✔
372
    lj_crecord_tonumber(J, rd);
534✔
373
    return;
534✔
374
#endif
375
  } else {
376
    tr = TREF_NIL;
377
  }
378
  J->base[0] = tr;
175✔
379
  UNUSED(rd);
712✔
380
}
381

382
static TValue *recff_metacall_cp(lua_State *L, lua_CFunction dummy, void *ud)
3✔
383
{
384
  jit_State *J = (jit_State *)ud;
3✔
385
  lj_record_tailcall(J, 0, 1);
3✔
386
  UNUSED(L); UNUSED(dummy);
3✔
387
  return NULL;
3✔
388
}
389

390
static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm)
391
{
392
  RecordIndex ix;
393
  ix.tab = J->base[0];
394
  copyTV(J->L, &ix.tabv, &rd->argv[0]);
395
  if (lj_record_mm_lookup(J, &ix, mm)) {  /* Has metamethod? */
396
    int errcode;
397
    TValue argv0;
398
    /* Temporarily insert metamethod below object. */
399
    J->base[1+LJ_FR2] = J->base[0];
400
    J->base[0] = ix.mobj;
401
    copyTV(J->L, &argv0, &rd->argv[0]);
402
    copyTV(J->L, &rd->argv[1+LJ_FR2], &rd->argv[0]);
403
    copyTV(J->L, &rd->argv[0], &ix.mobjv);
404
    /* Need to protect lj_record_tailcall because it may throw. */
405
    errcode = lj_vm_cpcall(J->L, NULL, J, recff_metacall_cp);
406
    /* Always undo Lua stack changes to avoid confusing the interpreter. */
407
    copyTV(J->L, &rd->argv[0], &argv0);
408
    if (errcode)
409
      lj_err_throw(J->L, errcode);  /* Propagate errors. */
410
    rd->nres = -1;  /* Pending call. */
411
    return 1;  /* Tailcalled to metamethod. */
412
  }
413
  return 0;
414
}
415

416
static void LJ_FASTCALL recff_tostring(jit_State *J, RecordFFData *rd)
60✔
417
{
418
  TRef tr = J->base[0];
60✔
419
  if (tref_isstr(tr)) {
60✔
420
    /* Ignore __tostring in the string base metatable. */
421
    /* Pass on result in J->base[0]. */
422
  } else if (tr && !recff_metacall(J, rd, MM_tostring)) {
46✔
423
    if (tref_isnumber(tr)) {
43✔
424
      J->base[0] = emitir(IRT(IR_TOSTR, IRT_STR), tr,
41✔
425
                          tref_isnum(tr) ? IRTOSTR_NUM : IRTOSTR_INT);
426
    } else if (tref_ispri(tr)) {
2✔
427
      J->base[0] = lj_ir_kstr(J, lj_strfmt_obj(J->L, &rd->argv[0]));
1✔
428
    } else {
429
      recff_nyiu(J, rd);
1✔
430
      return;
1✔
431
    }
432
  }
433
}
434

435
static void LJ_FASTCALL recff_ipairs_aux(jit_State *J, RecordFFData *rd)
103✔
436
{
437
  RecordIndex ix;
103✔
438
  ix.tab = J->base[0];
103✔
439
  if (tref_istab(ix.tab)) {
103✔
440
    if (!tvisnumber(&rd->argv[1]))  /* No support for string coercion. */
103✔
441
      lj_trace_err(J, LJ_TRERR_BADTYPE);
×
442
    setintV(&ix.keyv, numberVint(&rd->argv[1])+1);
103✔
443
    settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
103✔
444
    ix.val = 0; ix.idxchain = 0;
103✔
445
    ix.key = lj_opt_narrow_toint(J, J->base[1]);
103✔
446
    J->base[0] = ix.key = emitir(IRTI(IR_ADD), ix.key, lj_ir_kint(J, 1));
103✔
447
    J->base[1] = lj_record_idx(J, &ix);
103✔
448
    rd->nres = tref_isnil(J->base[1]) ? 0 : 2;
180✔
449
  }  /* else: Interpreter will throw. */
450
}
103✔
451

452
static void LJ_FASTCALL recff_xpairs(jit_State *J, RecordFFData *rd)
31✔
453
{
454
  TRef tr = J->base[0];
31✔
455
  if (!((LJ_52 || (LJ_HASFFI && tref_iscdata(tr))) &&
31✔
456
        recff_metacall(J, rd, MM_pairs + rd->data))) {
×
457
    if (tref_istab(tr)) {
31✔
458
      J->base[0] = lj_ir_kfunc(J, funcV(&J->fn->c.upvalue[0]));
31✔
459
      J->base[1] = tr;
31✔
460
      J->base[2] = rd->data ? lj_ir_kint(J, 0) : TREF_NIL;
31✔
461
      rd->nres = 3;
31✔
462
    }  /* else: Interpreter will throw. */
463
  }
464
}
31✔
465

466
static void LJ_FASTCALL recff_pcall(jit_State *J, RecordFFData *rd)
31✔
467
{
468
  if (J->maxslot >= 1) {
31✔
469
#if LJ_FR2
470
    /* Shift function arguments up. */
471
    memmove(J->base + 1, J->base, sizeof(TRef) * J->maxslot);
31✔
472
#endif
473
    lj_record_call(J, 0, J->maxslot - 1);
31✔
474
    rd->nres = -1;  /* Pending call. */
31✔
475
    J->needsnap = 1;  /* Start catching on-trace errors. */
31✔
476
  }  /* else: Interpreter will throw. */
477
}
31✔
478

479
static TValue *recff_xpcall_cp(lua_State *L, lua_CFunction dummy, void *ud)
9✔
480
{
481
  jit_State *J = (jit_State *)ud;
9✔
482
  lj_record_call(J, 1, J->maxslot - 2);
9✔
483
  UNUSED(L); UNUSED(dummy);
9✔
484
  return NULL;
9✔
485
}
486

487
static void LJ_FASTCALL recff_xpcall(jit_State *J, RecordFFData *rd)
9✔
488
{
489
  if (J->maxslot >= 2) {
9✔
490
    TValue argv0, argv1;
9✔
491
    TRef tmp;
9✔
492
    int errcode;
9✔
493
    /* Swap function and traceback. */
494
    tmp = J->base[0]; J->base[0] = J->base[1]; J->base[1] = tmp;
9✔
495
    copyTV(J->L, &argv0, &rd->argv[0]);
9✔
496
    copyTV(J->L, &argv1, &rd->argv[1]);
9✔
497
    copyTV(J->L, &rd->argv[0], &argv1);
9✔
498
    copyTV(J->L, &rd->argv[1], &argv0);
9✔
499
#if LJ_FR2
500
    /* Shift function arguments up. */
501
    memmove(J->base + 2, J->base + 1, sizeof(TRef) * (J->maxslot-1));
9✔
502
#endif
503
    /* Need to protect lj_record_call because it may throw. */
504
    errcode = lj_vm_cpcall(J->L, NULL, J, recff_xpcall_cp);
9✔
505
    /* Always undo Lua stack swap to avoid confusing the interpreter. */
506
    copyTV(J->L, &rd->argv[0], &argv0);
9✔
507
    copyTV(J->L, &rd->argv[1], &argv1);
9✔
508
    if (errcode)
9✔
509
      lj_err_throw(J->L, errcode);  /* Propagate errors. */
×
510
    rd->nres = -1;  /* Pending call. */
9✔
511
    J->needsnap = 1;  /* Start catching on-trace errors. */
9✔
512
  }  /* else: Interpreter will throw. */
513
}
9✔
514

515
static void LJ_FASTCALL recff_getfenv(jit_State *J, RecordFFData *rd)
3✔
516
{
517
  TRef tr = J->base[0];
3✔
518
  /* Only support getfenv(0) for now. */
519
  if (tref_isint(tr) && tref_isk(tr) && IR(tref_ref(tr))->i == 0) {
3✔
520
    TRef trl = emitir(IRT(IR_LREF, IRT_THREAD), 0, 0);
2✔
521
    J->base[0] = emitir(IRT(IR_FLOAD, IRT_TAB), trl, IRFL_THREAD_ENV);
2✔
522
    return;
2✔
523
  }
524
  recff_nyiu(J, rd);
1✔
525
}
526

527
/* -- Math library fast functions ----------------------------------------- */
528

529
static void LJ_FASTCALL recff_math_abs(jit_State *J, RecordFFData *rd)
189✔
530
{
531
  TRef tr = lj_ir_tonum(J, J->base[0]);
189✔
532
  J->base[0] = emitir(IRTN(IR_ABS), tr, lj_ir_ksimd(J, LJ_KSIMD_ABS));
189✔
533
  UNUSED(rd);
189✔
534
}
189✔
535

536
/* Record rounding functions math.floor and math.ceil. */
537
static void LJ_FASTCALL recff_math_round(jit_State *J, RecordFFData *rd)
60✔
538
{
539
  TRef tr = J->base[0];
60✔
540
  if (!tref_isinteger(tr)) {  /* Pass through integers unmodified. */
60✔
541
    tr = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, tr), rd->data);
56✔
542
    /* Result is integral (or NaN/Inf), but may not fit an int32_t. */
543
    if (LJ_DUALNUM) {  /* Try to narrow using a guarded conversion to int. */
56✔
544
      lua_Number n = lj_vm_foldfpm(numberVnum(&rd->argv[0]), rd->data);
545
      if (n == (lua_Number)lj_num2int(n))
546
        tr = emitir(IRTGI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_CHECK);
547
    }
548
    J->base[0] = tr;
56✔
549
  }
550
}
60✔
551

552
/* Record unary math.* functions, mapped to IR_FPMATH opcode. */
553
static void LJ_FASTCALL recff_math_unary(jit_State *J, RecordFFData *rd)
2✔
554
{
555
  J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data);
2✔
556
}
2✔
557

558
/* Record math.log. */
559
static void LJ_FASTCALL recff_math_log(jit_State *J, RecordFFData *rd)
1✔
560
{
561
  TRef tr = lj_ir_tonum(J, J->base[0]);
1✔
562
  if (J->base[1]) {
1✔
563
#ifdef LUAJIT_NO_LOG2
564
    uint32_t fpm = IRFPM_LOG;
565
#else
566
    uint32_t fpm = IRFPM_LOG2;
×
567
#endif
568
    TRef trb = lj_ir_tonum(J, J->base[1]);
×
569
    tr = emitir(IRTN(IR_FPMATH), tr, fpm);
×
570
    trb = emitir(IRTN(IR_FPMATH), trb, fpm);
×
571
    trb = emitir(IRTN(IR_DIV), lj_ir_knum_one(J), trb);
×
572
    tr = emitir(IRTN(IR_MUL), tr, trb);
×
573
  } else {
574
    tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_LOG);
1✔
575
  }
576
  J->base[0] = tr;
1✔
577
  UNUSED(rd);
1✔
578
}
1✔
579

580
/* Record math.atan2. */
581
static void LJ_FASTCALL recff_math_atan2(jit_State *J, RecordFFData *rd)
1✔
582
{
583
  TRef tr = lj_ir_tonum(J, J->base[0]);
1✔
584
  TRef tr2 = lj_ir_tonum(J, J->base[1]);
1✔
585
  J->base[0] = lj_ir_call(J, IRCALL_atan2, tr, tr2);
1✔
586
  UNUSED(rd);
1✔
587
}
1✔
588

589
/* Record math.ldexp. */
590
static void LJ_FASTCALL recff_math_ldexp(jit_State *J, RecordFFData *rd)
131✔
591
{
592
  TRef tr = lj_ir_tonum(J, J->base[0]);
131✔
593
#if LJ_TARGET_X86ORX64
594
  TRef tr2 = lj_ir_tonum(J, J->base[1]);
131✔
595
#else
596
  TRef tr2 = lj_opt_narrow_toint(J, J->base[1]);
597
#endif
598
  J->base[0] = emitir(IRTN(IR_LDEXP), tr, tr2);
131✔
599
  UNUSED(rd);
131✔
600
}
131✔
601

602
static void LJ_FASTCALL recff_math_call(jit_State *J, RecordFFData *rd)
5✔
603
{
604
  TRef tr = lj_ir_tonum(J, J->base[0]);
5✔
605
  J->base[0] = emitir(IRTN(IR_CALLN), tr, rd->data);
5✔
606
}
5✔
607

608
static void LJ_FASTCALL recff_math_pow(jit_State *J, RecordFFData *rd)
×
609
{
610
  J->base[0] = lj_opt_narrow_arith(J, J->base[0], J->base[1],
×
611
                                   &rd->argv[0], &rd->argv[1], IR_POW);
×
612
  UNUSED(rd);
×
613
}
×
614

615
static void LJ_FASTCALL recff_math_minmax(jit_State *J, RecordFFData *rd)
96,373✔
616
{
617
  TRef tr = lj_ir_tonumber(J, J->base[0]);
96,373✔
618
  uint32_t op = rd->data;
96,373✔
619
  BCReg i;
96,373✔
620
  for (i = 1; J->base[i] != 0; i++) {
192,746✔
621
    TRef tr2 = lj_ir_tonumber(J, J->base[i]);
96,373✔
622
    IRType t = IRT_INT;
96,373✔
623
    if (!(tref_isinteger(tr) && tref_isinteger(tr2))) {
96,373✔
624
      if (tref_isinteger(tr)) tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT);
93,926✔
625
      if (tref_isinteger(tr2)) tr2 = emitir(IRTN(IR_CONV), tr2, IRCONV_NUM_INT);
93,926✔
626
      t = IRT_NUM;
627
    }
628
    tr = emitir(IRT(op, t), tr, tr2);
96,373✔
629
  }
630
  J->base[0] = tr;
96,373✔
631
}
96,373✔
632

633
static void LJ_FASTCALL recff_math_random(jit_State *J, RecordFFData *rd)
10✔
634
{
635
  GCudata *ud = udataV(&J->fn->c.upvalue[0]);
10✔
636
  TRef tr, one;
10✔
637
  lj_ir_kgc(J, obj2gco(ud), IRT_UDATA);  /* Prevent collection. */
10✔
638
  tr = lj_ir_call(J, IRCALL_lj_math_random_step, lj_ir_kptr(J, uddata(ud)));
10✔
639
  one = lj_ir_knum_one(J);
10✔
640
  tr = emitir(IRTN(IR_SUB), tr, one);
10✔
641
  if (J->base[0]) {
10✔
642
    TRef tr1 = lj_ir_tonum(J, J->base[0]);
9✔
643
    if (J->base[1]) {  /* d = floor(d*(r2-r1+1.0)) + r1 */
9✔
644
      TRef tr2 = lj_ir_tonum(J, J->base[1]);
5✔
645
      tr2 = emitir(IRTN(IR_SUB), tr2, tr1);
5✔
646
      tr2 = emitir(IRTN(IR_ADD), tr2, one);
5✔
647
      tr = emitir(IRTN(IR_MUL), tr, tr2);
5✔
648
      tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR);
5✔
649
      tr = emitir(IRTN(IR_ADD), tr, tr1);
5✔
650
    } else {  /* d = floor(d*r1) + 1.0 */
651
      tr = emitir(IRTN(IR_MUL), tr, tr1);
4✔
652
      tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR);
4✔
653
      tr = emitir(IRTN(IR_ADD), tr, one);
4✔
654
    }
655
  }
656
  J->base[0] = tr;
10✔
657
  UNUSED(rd);
10✔
658
}
10✔
659

660
/* -- Bit library fast functions ------------------------------------------ */
661

662
/* Record bit.tobit. */
663
static void LJ_FASTCALL recff_bit_tobit(jit_State *J, RecordFFData *rd)
8✔
664
{
665
  TRef tr = J->base[0];
8✔
666
#if LJ_HASFFI
667
  if (tref_iscdata(tr)) { recff_bit64_tobit(J, rd); return; }
8✔
668
#endif
669
  J->base[0] = lj_opt_narrow_tobit(J, tr);
8✔
670
  UNUSED(rd);
8✔
671
}
672

673
/* Record unary bit.bnot, bit.bswap. */
674
static void LJ_FASTCALL recff_bit_unary(jit_State *J, RecordFFData *rd)
4✔
675
{
676
#if LJ_HASFFI
677
  if (recff_bit64_unary(J, rd))
4✔
678
    return;
679
#endif
680
  J->base[0] = emitir(IRTI(rd->data), lj_opt_narrow_tobit(J, J->base[0]), 0);
2✔
681
}
682

683
/* Record N-ary bit.band, bit.bor, bit.bxor. */
684
static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd)
39,252✔
685
{
686
#if LJ_HASFFI
687
  if (recff_bit64_nary(J, rd))
39,252✔
688
    return;
689
#endif
690
  {
691
    TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
39,228✔
692
    uint32_t ot = IRTI(rd->data);
39,228✔
693
    BCReg i;
39,228✔
694
    for (i = 1; J->base[i] != 0; i++)
78,456✔
695
      tr = emitir(ot, tr, lj_opt_narrow_tobit(J, J->base[i]));
39,228✔
696
    J->base[0] = tr;
39,228✔
697
  }
698
}
699

700
/* Record bit shifts. */
701
static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd)
51✔
702
{
703
#if LJ_HASFFI
704
  if (recff_bit64_shift(J, rd))
51✔
705
    return;
706
#endif
707
  {
708
    TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
37✔
709
    TRef tsh = lj_opt_narrow_tobit(J, J->base[1]);
36✔
710
    IROp op = (IROp)rd->data;
36✔
711
    if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) &&
36✔
712
        !tref_isk(tsh))
713
      tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 31));
714
#ifdef LJ_TARGET_UNIFYROT
715
    if (op == (LJ_TARGET_UNIFYROT == 1 ? IR_BROR : IR_BROL)) {
716
      op = LJ_TARGET_UNIFYROT == 1 ? IR_BROL : IR_BROR;
717
      tsh = emitir(IRTI(IR_NEG), tsh, tsh);
718
    }
719
#endif
720
    J->base[0] = emitir(IRTI(op), tr, tsh);
36✔
721
  }
722
}
723

724
static void LJ_FASTCALL recff_bit_tohex(jit_State *J, RecordFFData *rd)
13✔
725
{
726
#if LJ_HASFFI
727
  TRef hdr = recff_bufhdr(J);
13✔
728
  TRef tr = recff_bit64_tohex(J, rd, hdr);
13✔
729
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
13✔
730
#else
731
  recff_nyiu(J, rd);  /* Don't bother working around this NYI. */
732
#endif
733
}
13✔
734

735
/* -- String library fast functions --------------------------------------- */
736

737
/* Specialize to relative starting position for string. */
738
static TRef recff_string_start(jit_State *J, GCstr *s, int32_t *st, TRef tr,
739
                               TRef trlen, TRef tr0)
740
{
741
  int32_t start = *st;
742
  if (start < 0) {
743
    emitir(IRTGI(IR_LT), tr, tr0);
744
    tr = emitir(IRTI(IR_ADD), trlen, tr);
745
    start = start + (int32_t)s->len;
746
    emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), tr, tr0);
747
    if (start < 0) {
748
      tr = tr0;
749
      start = 0;
750
    }
751
  } else if (start == 0) {
752
    emitir(IRTGI(IR_EQ), tr, tr0);
753
    tr = tr0;
754
  } else {
755
    tr = emitir(IRTI(IR_ADD), tr, lj_ir_kint(J, -1));
756
    emitir(IRTGI(IR_GE), tr, tr0);
757
    start--;
758
  }
759
  *st = start;
760
  return tr;
761
}
762

763
/* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */
764
static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd)
184✔
765
{
766
  TRef trstr = lj_ir_tostr(J, J->base[0]);
184✔
767
  TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN);
184✔
768
  TRef tr0 = lj_ir_kint(J, 0);
184✔
769
  TRef trstart, trend;
184✔
770
  GCstr *str = argv2str(J, &rd->argv[0]);
184✔
771
  int32_t start, end;
184✔
772
  if (rd->data) {  /* string.sub(str, start [,end]) */
184✔
773
    start = argv2int(J, &rd->argv[1]);
102✔
774
    trstart = lj_opt_narrow_toint(J, J->base[1]);
102✔
775
    trend = J->base[2];
102✔
776
    if (tref_isnil(trend)) {
102✔
777
      trend = lj_ir_kint(J, -1);
25✔
778
      end = -1;
25✔
779
    } else {
780
      trend = lj_opt_narrow_toint(J, trend);
77✔
781
      end = argv2int(J, &rd->argv[2]);
77✔
782
    }
783
  } else {  /* string.byte(str, [,start [,end]]) */
784
    if (tref_isnil(J->base[1])) {
82✔
785
      start = 1;
2✔
786
      trstart = lj_ir_kint(J, 1);
2✔
787
    } else {
788
      start = argv2int(J, &rd->argv[1]);
80✔
789
      trstart = lj_opt_narrow_toint(J, J->base[1]);
80✔
790
    }
791
    if (J->base[1] && !tref_isnil(J->base[2])) {
82✔
792
      trend = lj_opt_narrow_toint(J, J->base[2]);
78✔
793
      end = argv2int(J, &rd->argv[2]);
78✔
794
    } else {
795
      trend = trstart;
4✔
796
      end = start;
4✔
797
    }
798
  }
799
  if (end < 0) {
184✔
800
    emitir(IRTGI(IR_LT), trend, tr0);
87✔
801
    trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend),
87✔
802
                   lj_ir_kint(J, 1));
803
    end = end+(int32_t)str->len+1;
87✔
804
  } else if ((MSize)end <= str->len) {
97✔
805
    emitir(IRTGI(IR_ULE), trend, trlen);
88✔
806
  } else {
807
    emitir(IRTGI(IR_UGT), trend, trlen);
9✔
808
    end = (int32_t)str->len;
9✔
809
    trend = trlen;
9✔
810
  }
811
  trstart = recff_string_start(J, str, &start, trstart, trlen, tr0);
184✔
812
  if (rd->data) {  /* Return string.sub result. */
184✔
813
    if (end - start >= 0) {
102✔
814
      /* Also handle empty range here, to avoid extra traces. */
815
      TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart);
94✔
816
      emitir(IRTGI(IR_GE), trslen, tr0);
94✔
817
      trptr = emitir(IRT(IR_STRREF, IRT_PGC), trstr, trstart);
94✔
818
      J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen);
94✔
819
    } else {  /* Range underflow: return empty string. */
820
      emitir(IRTGI(IR_LT), trend, trstart);
8✔
821
      J->base[0] = lj_ir_kstr(J, &J2G(J)->strempty);
8✔
822
    }
823
  } else {  /* Return string.byte result(s). */
824
    ptrdiff_t i, len = end - start;
82✔
825
    if (len > 0) {
82✔
826
      TRef trslen = emitir(IRTI(IR_SUB), trend, trstart);
60✔
827
      emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len));
60✔
828
      if (J->baseslot + len > LJ_MAX_JSLOTS)
60✔
829
        lj_trace_err_info(J, LJ_TRERR_STACKOV);
3✔
830
      rd->nres = len;
57✔
831
      for (i = 0; i < len; i++) {
176✔
832
        TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i));
119✔
833
        tmp = emitir(IRT(IR_STRREF, IRT_PGC), trstr, tmp);
119✔
834
        J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY);
119✔
835
      }
836
    } else {  /* Empty range or range underflow: return no results. */
837
      emitir(IRTGI(IR_LE), trend, trstart);
22✔
838
      rd->nres = 0;
22✔
839
    }
840
  }
841
}
181✔
842

843
static void LJ_FASTCALL recff_string_char(jit_State *J, RecordFFData *rd)
56✔
844
{
845
  TRef k255 = lj_ir_kint(J, 255);
56✔
846
  BCReg i;
56✔
847
  for (i = 0; J->base[i] != 0; i++) {  /* Convert char values to strings. */
221✔
848
    TRef tr = lj_opt_narrow_toint(J, J->base[i]);
109✔
849
    emitir(IRTGI(IR_ULE), tr, k255);
109✔
850
    J->base[i] = emitir(IRT(IR_TOSTR, IRT_STR), tr, IRTOSTR_CHAR);
109✔
851
  }
852
  if (i > 1) {  /* Concatenate the strings, if there's more than one. */
56✔
853
    TRef hdr = recff_bufhdr(J), tr = hdr;
34✔
854
    for (i = 0; J->base[i] != 0; i++)
156✔
855
      tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, J->base[i]);
88✔
856
    J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
34✔
857
  } else if (i == 0) {
22✔
858
    J->base[0] = lj_ir_kstr(J, &J2G(J)->strempty);
1✔
859
  }
860
  UNUSED(rd);
56✔
861
}
56✔
862

863
static void LJ_FASTCALL recff_string_rep(jit_State *J, RecordFFData *rd)
65✔
864
{
865
  TRef str = lj_ir_tostr(J, J->base[0]);
65✔
866
  TRef rep = lj_opt_narrow_toint(J, J->base[1]);
65✔
867
  TRef hdr, tr, str2 = 0;
65✔
868
  if (!tref_isnil(J->base[2])) {
65✔
869
    TRef sep = lj_ir_tostr(J, J->base[2]);
4✔
870
    int32_t vrep = argv2int(J, &rd->argv[1]);
4✔
871
    emitir(IRTGI(vrep > 1 ? IR_GT : IR_LE), rep, lj_ir_kint(J, 1));
6✔
872
    if (vrep > 1) {
4✔
873
      TRef hdr2 = recff_bufhdr(J);
2✔
874
      TRef tr2 = emitir(IRT(IR_BUFPUT, IRT_PGC), hdr2, sep);
2✔
875
      tr2 = emitir(IRT(IR_BUFPUT, IRT_PGC), tr2, str);
2✔
876
      str2 = emitir(IRT(IR_BUFSTR, IRT_STR), tr2, hdr2);
2✔
877
    }
878
  }
879
  tr = hdr = recff_bufhdr(J);
65✔
880
  if (str2) {
65✔
881
    tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, str);
2✔
882
    str = str2;
2✔
883
    rep = emitir(IRTI(IR_ADD), rep, lj_ir_kint(J, -1));
2✔
884
  }
885
  tr = lj_ir_call(J, IRCALL_lj_buf_putstr_rep, tr, str, rep);
65✔
886
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
65✔
887
}
65✔
888

889
static void LJ_FASTCALL recff_string_op(jit_State *J, RecordFFData *rd)
16✔
890
{
891
  TRef str = lj_ir_tostr(J, J->base[0]);
16✔
892
  TRef hdr = recff_bufhdr(J);
16✔
893
  TRef tr = lj_ir_call(J, rd->data, hdr, str);
16✔
894
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
16✔
895
}
16✔
896

897
static void LJ_FASTCALL recff_string_find(jit_State *J, RecordFFData *rd)
36✔
898
{
899
  TRef trstr = lj_ir_tostr(J, J->base[0]);
36✔
900
  TRef trpat = lj_ir_tostr(J, J->base[1]);
36✔
901
  TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN);
36✔
902
  TRef tr0 = lj_ir_kint(J, 0);
36✔
903
  TRef trstart;
36✔
904
  GCstr *str = argv2str(J, &rd->argv[0]);
36✔
905
  GCstr *pat = argv2str(J, &rd->argv[1]);
36✔
906
  int32_t start;
36✔
907
  J->needsnap = 1;
36✔
908
  if (tref_isnil(J->base[2])) {
36✔
909
    trstart = lj_ir_kint(J, 1);
33✔
910
    start = 1;
33✔
911
  } else {
912
    trstart = lj_opt_narrow_toint(J, J->base[2]);
3✔
913
    start = argv2int(J, &rd->argv[2]);
3✔
914
  }
915
  trstart = recff_string_start(J, str, &start, trstart, trlen, tr0);
36✔
916
  if ((MSize)start <= str->len) {
36✔
917
    emitir(IRTGI(IR_ULE), trstart, trlen);
36✔
918
  } else {
919
    emitir(IRTGI(IR_UGT), trstart, trlen);
×
920
#if LJ_52
921
    J->base[0] = TREF_NIL;
922
    return;
923
#else
924
    trstart = trlen;
×
925
    start = str->len;
×
926
#endif
927
  }
928
  /* Fixed arg or no pattern matching chars? (Specialized to pattern string.) */
929
  if ((J->base[2] && tref_istruecond(J->base[3])) ||
68✔
930
      (emitir(IRTG(IR_EQ, IRT_STR), trpat, lj_ir_kstr(J, pat)),
32✔
931
       !lj_str_haspattern(pat))) {  /* Search for fixed string. */
32✔
932
    TRef trsptr = emitir(IRT(IR_STRREF, IRT_PGC), trstr, trstart);
33✔
933
    TRef trpptr = emitir(IRT(IR_STRREF, IRT_PGC), trpat, tr0);
33✔
934
    TRef trslen = emitir(IRTI(IR_SUB), trlen, trstart);
33✔
935
    TRef trplen = emitir(IRTI(IR_FLOAD), trpat, IRFL_STR_LEN);
33✔
936
    TRef tr = lj_ir_call(J, IRCALL_lj_str_find, trsptr, trpptr, trslen, trplen);
33✔
937
    TRef trp0 = lj_ir_kkptr(J, NULL);
33✔
938
    if (lj_str_find(strdata(str)+(MSize)start, strdata(pat),
33✔
939
                    str->len-(MSize)start, pat->len)) {
33✔
940
      TRef pos;
7✔
941
      emitir(IRTG(IR_NE, IRT_PGC), tr, trp0);
7✔
942
      /* Caveat: can't use STRREF trstr 0 here because that might be pointing into a wrong string due to folding. */
943
      pos = emitir(IRTI(IR_ADD), trstart, emitir(IRTI(IR_SUB), tr, trsptr));
7✔
944
      J->base[0] = emitir(IRTI(IR_ADD), pos, lj_ir_kint(J, 1));
7✔
945
      J->base[1] = emitir(IRTI(IR_ADD), pos, trplen);
7✔
946
      rd->nres = 2;
7✔
947
    } else {
948
      emitir(IRTG(IR_EQ, IRT_PGC), tr, trp0);
26✔
949
      J->base[0] = TREF_NIL;
26✔
950
    }
951
  } else {  /* Search for pattern. */
952
    recff_nyiu(J, rd);
3✔
953
    return;
3✔
954
  }
955
}
956

957
static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
198✔
958
{
959
  TRef trfmt = lj_ir_tostr(J, J->base[0]);
198✔
960
  GCstr *fmt = argv2str(J, &rd->argv[0]);
198✔
961
  int arg = 1;
198✔
962
  TRef hdr, tr;
198✔
963
  FormatState fs;
198✔
964
  SFormat sf;
198✔
965
  /* Specialize to the format string. */
966
  emitir(IRTG(IR_EQ, IRT_STR), trfmt, lj_ir_kstr(J, fmt));
198✔
967
  tr = hdr = recff_bufhdr(J);
198✔
968
  lj_strfmt_init(&fs, strdata(fmt), fmt->len);
198✔
969
  while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {  /* Parse format. */
609✔
970
    TRef tra = sf == STRFMT_LIT ? 0 : J->base[arg++];
417✔
971
    TRef trsf = lj_ir_kint(J, (int32_t)sf);
417✔
972
    IRCallID id;
417✔
973
    switch (STRFMT_TYPE(sf)) {
417✔
974
    case STRFMT_LIT:
172✔
975
      tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr,
172✔
976
                  lj_ir_kstr(J, lj_str_new(J->L, fs.str, fs.len)));
977
      break;
172✔
978
    case STRFMT_INT:
979
      id = IRCALL_lj_strfmt_putfnum_int;
980
    handle_int:
61✔
981
      if (!tref_isinteger(tra))
61✔
982
        goto handle_num;
48✔
983
      if (sf == STRFMT_INT) { /* Shortcut for plain %d. */
13✔
984
        tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr,
8✔
985
                    emitir(IRT(IR_TOSTR, IRT_STR), tra, IRTOSTR_INT));
986
      } else {
987
#if LJ_HASFFI
988
        tra = emitir(IRT(IR_CONV, IRT_U64), tra,
5✔
989
                     (IRT_INT|(IRT_U64<<5)|IRCONV_SEXT));
990
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putfxint, tr, trsf, tra);
5✔
991
        lj_needsplit(J);
992
#else
993
        recff_nyiu(J, rd);  /* Don't bother working around this NYI. */
994
        return;
995
#endif
996
      }
997
      break;
998
    case STRFMT_UINT:
11✔
999
      id = IRCALL_lj_strfmt_putfnum_uint;
11✔
1000
      goto handle_int;
11✔
1001
    case STRFMT_NUM:
1002
      id = IRCALL_lj_strfmt_putfnum;
1003
    handle_num:
167✔
1004
      tra = lj_ir_tonum(J, tra);
167✔
1005
      tr = lj_ir_call(J, id, tr, trsf, tra);
167✔
1006
      if (LJ_SOFTFP32) lj_needsplit(J);
167✔
1007
      break;
167✔
1008
    case STRFMT_STR:
65✔
1009
      if (!tref_isstr(tra)) {
65✔
1010
        recff_nyiu(J, rd);  /* NYI: __tostring and non-string types for %s. */
6✔
1011
        return;
12✔
1012
      }
1013
      if (sf == STRFMT_STR)  /* Shortcut for plain %s. */
59✔
1014
        tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, tra);
54✔
1015
      else if ((sf & STRFMT_T_QUOTED))
5✔
1016
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putquoted, tr, tra);
2✔
1017
      else
1018
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putfstr, tr, trsf, tra);
3✔
1019
      break;
1020
    case STRFMT_CHAR:
×
1021
      tra = lj_opt_narrow_toint(J, tra);
×
1022
      if (sf == STRFMT_CHAR)  /* Shortcut for plain %c. */
×
1023
        tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr,
×
1024
                    emitir(IRT(IR_TOSTR, IRT_STR), tra, IRTOSTR_CHAR));
1025
      else
1026
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putfchar, tr, trsf, tra);
×
1027
      break;
1028
    case STRFMT_PTR:  /* NYI */
×
1029
    case STRFMT_ERR:
1030
    default:
1031
      recff_nyiu(J, rd);
×
1032
      return;
×
1033
    }
1034
  }
1035
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
192✔
1036
}
1037

1038
/* -- Table library fast functions ---------------------------------------- */
1039

1040
static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd)
21✔
1041
{
1042
  RecordIndex ix;
21✔
1043
  ix.tab = J->base[0];
21✔
1044
  ix.val = J->base[1];
21✔
1045
  rd->nres = 0;
21✔
1046
  if (tref_istab(ix.tab) && ix.val) {
21✔
1047
    if (!J->base[2]) {  /* Simple push: t[#t+1] = v */
21✔
1048
      TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, ix.tab);
18✔
1049
      GCtab *t = tabV(&rd->argv[0]);
18✔
1050
      ix.key = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1));
18✔
1051
      settabV(J->L, &ix.tabv, t);
18✔
1052
      setintV(&ix.keyv, lj_tab_len(t) + 1);
18✔
1053
      ix.idxchain = 0;
18✔
1054
      lj_record_idx(J, &ix);  /* Set new value. */
18✔
1055
    } else {  /* Complex case: insert in the middle. */
1056
      recff_nyiu(J, rd);
3✔
1057
      return;
3✔
1058
    }
1059
  }  /* else: Interpreter will throw. */
1060
}
1061

1062
static void LJ_FASTCALL recff_table_concat(jit_State *J, RecordFFData *rd)
13✔
1063
{
1064
  TRef tab = J->base[0];
13✔
1065
  if (tref_istab(tab)) {
13✔
1066
    TRef sep = !tref_isnil(J->base[1]) ?
26✔
1067
               lj_ir_tostr(J, J->base[1]) : lj_ir_knull(J, IRT_STR);
13✔
1068
    TRef tri = (J->base[1] && !tref_isnil(J->base[2])) ?
12✔
1069
               lj_opt_narrow_toint(J, J->base[2]) : lj_ir_kint(J, 1);
25✔
1070
    TRef tre = (J->base[1] && J->base[2] && !tref_isnil(J->base[3])) ?
12✔
1071
               lj_opt_narrow_toint(J, J->base[3]) :
16✔
1072
               lj_ir_call(J, IRCALL_lj_tab_len, tab);
10✔
1073
    TRef hdr = recff_bufhdr(J);
13✔
1074
    TRef tr = lj_ir_call(J, IRCALL_lj_buf_puttab, hdr, tab, sep, tri, tre);
13✔
1075
    emitir(IRTG(IR_NE, IRT_PTR), tr, lj_ir_kptr(J, NULL));
13✔
1076
    J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
13✔
1077
  }  /* else: Interpreter will throw. */
1078
  UNUSED(rd);
13✔
1079
}
13✔
1080

1081
static void LJ_FASTCALL recff_table_new(jit_State *J, RecordFFData *rd)
8✔
1082
{
1083
  TRef tra = lj_opt_narrow_toint(J, J->base[0]);
8✔
1084
  TRef trh = lj_opt_narrow_toint(J, J->base[1]);
8✔
1085
  if (tref_isk(tra) && tref_isk(trh)) {
8✔
1086
    int32_t a = IR(tref_ref(tra))->i;
7✔
1087
    if (a < 0x7fff) {
7✔
1088
      uint32_t hbits = hsize2hbits(IR(tref_ref(trh))->i);
6✔
1089
      a = a > 0 ? a+1 : 0;
6✔
1090
      J->base[0] = emitir(IRTG(IR_TNEW, IRT_TAB), (uint32_t)a, hbits);
6✔
1091
      return;
6✔
1092
    }
1093
  }
1094
  J->base[0] = lj_ir_call(J, IRCALL_lj_tab_new_ah, tra, trh);
2✔
1095
  UNUSED(rd);
8✔
1096
}
1097

1098
static void LJ_FASTCALL recff_table_clear(jit_State *J, RecordFFData *rd)
6✔
1099
{
1100
  TRef tr = J->base[0];
6✔
1101
  if (tref_istab(tr)) {
6✔
1102
    rd->nres = 0;
6✔
1103
    lj_ir_call(J, IRCALL_lj_tab_clear, tr);
6✔
1104
    J->needsnap = 1;
6✔
1105
  }  /* else: Interpreter will throw. */
1106
}
6✔
1107

1108
/* -- I/O library fast functions ------------------------------------------ */
1109

1110
/* Get FILE* for I/O function. Any I/O error aborts recording, so there's
1111
** no need to encode the alternate cases for any of the guards.
1112
*/
1113
static TRef recff_io_fp(jit_State *J, TRef *udp, int32_t id)
40✔
1114
{
1115
  TRef tr, ud, fp;
40✔
1116
  if (id) {  /* io.func() */
40✔
1117
#if LJ_GC64
1118
    /* TODO: fix ARM32 asm_fload(), so we can use this for all archs. */
1119
    ud = lj_ir_ggfload(J, IRT_UDATA, GG_OFS(g.gcroot[id]));
40✔
1120
#else
1121
    tr = lj_ir_kptr(J, &J2G(J)->gcroot[id]);
1122
    ud = emitir(IRT(IR_XLOAD, IRT_UDATA), tr, 0);
1123
#endif
1124
  } else {  /* fp:method() */
1125
    ud = J->base[0];
×
1126
    if (!tref_isudata(ud))
×
1127
      lj_trace_err(J, LJ_TRERR_BADTYPE);
×
1128
    tr = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE);
×
1129
    emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE));
×
1130
  }
1131
  *udp = ud;
40✔
1132
  fp = emitir(IRT(IR_FLOAD, IRT_PTR), ud, IRFL_UDATA_FILE);
40✔
1133
  emitir(IRTG(IR_NE, IRT_PTR), fp, lj_ir_knull(J, IRT_PTR));
40✔
1134
  return fp;
40✔
1135
}
1136

1137
static void LJ_FASTCALL recff_io_write(jit_State *J, RecordFFData *rd)
40✔
1138
{
1139
  TRef ud, fp = recff_io_fp(J, &ud, rd->data);
40✔
1140
  TRef zero = lj_ir_kint(J, 0);
40✔
1141
  TRef one = lj_ir_kint(J, 1);
40✔
1142
  ptrdiff_t i = rd->data == 0 ? 1 : 0;
40✔
1143
  for (; J->base[i]; i++) {
121✔
1144
    TRef str = lj_ir_tostr(J, J->base[i]);
81✔
1145
    TRef buf = emitir(IRT(IR_STRREF, IRT_PGC), str, zero);
81✔
1146
    TRef len = emitir(IRTI(IR_FLOAD), str, IRFL_STR_LEN);
81✔
1147
    if (tref_isk(len) && IR(tref_ref(len))->i == 1) {
81✔
1148
      IRIns *irs = IR(tref_ref(str));
1✔
1149
      TRef tr = (irs->o == IR_TOSTR && irs->op2 == IRTOSTR_CHAR) ?
2✔
1150
                irs->op1 :
1✔
1151
                emitir(IRT(IR_XLOAD, IRT_U8), buf, IRXLOAD_READONLY);
1✔
1152
      tr = lj_ir_call(J, IRCALL_fputc, tr, fp);
1✔
1153
      if (results_wanted(J) != 0)  /* Check result only if not ignored. */
1✔
1154
        emitir(IRTGI(IR_NE), tr, lj_ir_kint(J, -1));
×
1155
    } else {
1156
      TRef tr = lj_ir_call(J, IRCALL_fwrite, buf, one, len, fp);
80✔
1157
      if (results_wanted(J) != 0)  /* Check result only if not ignored. */
80✔
1158
        emitir(IRTGI(IR_EQ), tr, len);
×
1159
    }
1160
  }
1161
  J->base[0] = LJ_52 ? ud : TREF_TRUE;
40✔
1162
}
40✔
1163

1164
static void LJ_FASTCALL recff_io_flush(jit_State *J, RecordFFData *rd)
×
1165
{
1166
  TRef ud, fp = recff_io_fp(J, &ud, rd->data);
×
1167
  TRef tr = lj_ir_call(J, IRCALL_fflush, fp);
×
1168
  if (results_wanted(J) != 0)  /* Check result only if not ignored. */
×
1169
    emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0));
×
1170
  J->base[0] = TREF_TRUE;
×
1171
}
×
1172

1173
/* -- Debug library fast functions ---------------------------------------- */
1174

1175
static void LJ_FASTCALL recff_debug_getmetatable(jit_State *J, RecordFFData *rd)
1✔
1176
{
1177
  GCtab *mt;
1✔
1178
  TRef mtref;
1✔
1179
  TRef tr = J->base[0];
1✔
1180
  if (tref_istab(tr)) {
1✔
1181
    mt = tabref(tabV(&rd->argv[0])->metatable);
×
1182
    mtref = emitir(IRT(IR_FLOAD, IRT_TAB), tr, IRFL_TAB_META);
×
1183
  } else if (tref_isudata(tr)) {
1✔
1184
    mt = tabref(udataV(&rd->argv[0])->metatable);
×
1185
    mtref = emitir(IRT(IR_FLOAD, IRT_TAB), tr, IRFL_UDATA_META);
×
1186
  } else {
1187
    mt = tabref(basemt_obj(J2G(J), &rd->argv[0]));
1✔
1188
    J->base[0] = mt ? lj_ir_ktab(J, mt) : TREF_NIL;
1✔
1189
    return;
1✔
1190
  }
1191
  emitir(IRTG(mt ? IR_NE : IR_EQ, IRT_TAB), mtref, lj_ir_knull(J, IRT_TAB));
×
1192
  J->base[0] = mt ? mtref : TREF_NIL;
×
1193
}
1194

1195
/* -- Record calls to fast functions -------------------------------------- */
1196

1197
#include "lj_recdef.h"
1198

1199
static uint32_t recdef_lookup(GCfunc *fn)
288,521✔
1200
{
1201
  if (fn->c.ffid < sizeof(recff_idmap)/sizeof(recff_idmap[0]))
288,521✔
1202
    return recff_idmap[fn->c.ffid];
288,412✔
1203
  else
1204
    return 0;
1205
}
1206

1207
/* Record entry to a fast function or C function. */
1208
void lj_ffrecord_func(jit_State *J)
288,521✔
1209
{
1210
  RecordFFData rd;
288,521✔
1211
  uint32_t m = recdef_lookup(J->fn);
288,521✔
1212
  rd.data = m & 0xff;
288,521✔
1213
  rd.nres = 1;  /* Default is one result. */
288,521✔
1214
  rd.argv = J->L->base;
288,521✔
1215
  J->base[J->maxslot] = 0;  /* Mark end of arguments. */
288,521✔
1216
  (recff_func[m >> 8])(J, &rd);  /* Call recff_* handler. */
288,521✔
1217
  if (rd.nres >= 0) {
288,470✔
1218
    if (J->postproc == LJ_POST_NONE) J->postproc = LJ_POST_FFRETRY;
288,166✔
1219
    lj_record_ret(J, 0, rd.nres);
288,166✔
1220
  }
1221
}
288,449✔
1222

1223
#undef IR
1224
#undef emitir
1225

1226
#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

© 2025 Coveralls, Inc