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

tarantool / luajit / 6406140978

04 Oct 2023 12:42PM UTC coverage: 88.226% (+0.09%) from 88.14%
6406140978

push

github

fckxorg
Fix snapshot PC when linking to BC_JLOOP that was a BC_RET*.

Reported by Arseny Vakhrushev.
Fix contributed by Peter Cawley.

(cherry-picked from commit 5c46f4773)

As specified in the comment in `lj_record_stop`, all loops must
set `J->pc` to the next instruction. However, the chunk of logic
in `lj_trace_exit` expects it to be set to `BC_JLOOP` itself if
it used to be a `BC_RET`. This wrong pc results in the execution
of random data that goes after `BC_JLOOP` in the case of
restoration from the snapshot.

This patch fixes that behavior by adapting the loop recording
logic to this specific case.

NOTICE: This patch is only a part of the original commit,
and the other part is backported in the previous commit. The
patch was split into two, so the test case becomes easier to
implement since it can now depend on this assertion instead
of memory layout.

Maxim Kokryashkin:
* added the description and the test for the problem

Part of tarantool/tarantool#9145

5339 of 5973 branches covered (0.0%)

Branch coverage included in aggregate %.

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

20482 of 23294 relevant lines covered (87.93%)

2749084.93 hits per line

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

89.14
/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)
304✔
68
{
69
  if (!lj_strscan_numberobj(o))
304✔
70
    lj_trace_err(J, LJ_TRERR_BADTYPE);
×
71
  return tvisint(o) ? intV(o) : lj_num2int(numV(o));
304✔
72
}
73

74
/* Get runtime value of string argument. */
75
static GCstr *argv2str(jit_State *J, TValue *o)
364✔
76
{
77
  if (LJ_LIKELY(tvisstr(o))) {
364✔
78
    return strV(o);
364✔
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)
68✔
91
{
92
  TValue *frame = J->L->base-1;
68✔
93
  if (frame_islua(frame))
68✔
94
    return (ptrdiff_t)bc_b(frame_pc(frame)[-1]) - 1;
68✔
95
  else
96
    return -1;
97
}
98

99
/* Trace stitching: add continuation below frame to start a new trace. */
100
static void recff_stitch(jit_State *J)
206✔
101
{
102
  ASMFunction cont = lj_cont_stitch;
206✔
103
  lua_State *L = J->L;
206✔
104
  TValue *base = L->base;
206✔
105
  BCReg nslot = J->maxslot + 1 + LJ_FR2;
206✔
106
  TValue *nframe = base + 1 + LJ_FR2;
206✔
107
  const BCIns *pc = frame_pc(base-1);
206✔
108
  TValue *pframe = frame_prevl(base-1);
206✔
109

110
  /* Move func + args up in Lua stack and insert continuation. */
111
  memmove(&base[1], &base[-1-LJ_FR2], sizeof(TValue)*nslot);
206✔
112
  setframe_ftsz(nframe, ((char *)nframe - (char *)pframe) + FRAME_CONT);
206✔
113
  setcont(base-LJ_FR2, cont);
206✔
114
  setframe_pc(base, pc);
206✔
115
  setnilV(base-1-LJ_FR2);  /* Incorrect, but rec_check_slots() won't run anymore. */
206✔
116
  L->base += 2 + LJ_FR2;
206✔
117
  L->top += 2 + LJ_FR2;
206✔
118

119
  /* Ditto for the IR. */
120
  memmove(&J->base[1], &J->base[-1-LJ_FR2], sizeof(TRef)*nslot);
206✔
121
#if LJ_FR2
122
  J->base[2] = TREF_FRAME;
206✔
123
  J->base[-1] = lj_ir_k64(J, IR_KNUM, u64ptr(contptr(cont)));
206✔
124
  J->base[0] = lj_ir_k64(J, IR_KNUM, u64ptr(pc)) | TREF_CONT;
206✔
125
#else
126
  J->base[0] = lj_ir_kptr(J, contptr(cont)) | TREF_CONT;
127
#endif
128
  J->ktrace = tref_ref((J->base[-1-LJ_FR2] = lj_ir_ktrace(J)));
206✔
129
  J->base += 2 + LJ_FR2;
206✔
130
  J->baseslot += 2 + LJ_FR2;
206✔
131
  J->framedepth++;
206✔
132

133
  lj_record_stop(J, LJ_TRLINK_STITCH, 0);
206✔
134

135
  /* Undo Lua stack changes. */
136
  memmove(&base[-1-LJ_FR2], &base[1], sizeof(TValue)*nslot);
206✔
137
  setframe_pc(base-1, pc);
206✔
138
  L->base -= 2 + LJ_FR2;
206✔
139
  L->top -= 2 + LJ_FR2;
206✔
140
}
206✔
141

142
/* Fallback handler for fast functions that are not recorded (yet). */
143
static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd)
249✔
144
{
145
  if (J->cur.nins < (IRRef)J->param[JIT_P_minstitch] + REF_BASE) {
249✔
146
    lj_trace_err_info(J, LJ_TRERR_TRACEUV);
8✔
147
  } else {
148
    /* Can only stitch from Lua call. */
149
    if (J->framedepth && frame_islua(J->L->base-1)) {
241✔
150
      BCOp op = bc_op(*frame_pc(J->L->base-1));
226✔
151
      /* Stitched trace cannot start with *M op with variable # of args. */
152
      if (!(op == BC_CALLM || op == BC_CALLMT ||
226✔
153
            op == BC_RETM || op == BC_TSETM)) {
215✔
154
        switch (J->fn->c.ffid) {
207✔
155
        case FF_error:
156
        case FF_debug_sethook:
157
        case FF_jit_flush:
158
          break;  /* Don't stitch across special builtins. */
159
        default:
206✔
160
          recff_stitch(J);  /* Use trace stitching. */
206✔
161
          rd->nres = -1;
206✔
162
          return;
206✔
163
        }
164
      }
34✔
165
    }
166
    /* Otherwise stop trace and return to interpreter. */
167
    lj_record_stop(J, LJ_TRLINK_RETURN, 0);
35✔
168
    rd->nres = -1;
35✔
169
  }
170
}
171

172
/* Fallback handler for unsupported variants of fast functions. */
173
#define recff_nyiu        recff_nyi
174

175
/* Must stop the trace for classic C functions with arbitrary side-effects. */
176
#define recff_c                recff_nyi
177

178
/* Emit BUFHDR for the global temporary buffer. */
179
static TRef recff_bufhdr(jit_State *J)
297✔
180
{
181
  return emitir(IRT(IR_BUFHDR, IRT_PGC),
297✔
182
                lj_ir_kptr(J, &J2G(J)->tmpbuf), IRBUFHDR_RESET);
183
}
184

185
/* -- Base library fast functions ----------------------------------------- */
186

187
static void LJ_FASTCALL recff_assert(jit_State *J, RecordFFData *rd)
366✔
188
{
189
  /* Arguments already specialized. The interpreter throws for nil/false. */
190
  rd->nres = J->maxslot;  /* Pass through all arguments. */
366✔
191
}
366✔
192

193
static void LJ_FASTCALL recff_type(jit_State *J, RecordFFData *rd)
36✔
194
{
195
  /* Arguments already specialized. Result is a constant string. Neat, huh? */
196
  uint32_t t;
36✔
197
  if (tvisnumber(&rd->argv[0]))
36✔
198
    t = ~LJ_TNUMX;
199
  else if (LJ_64 && !LJ_GC64 && tvislightud(&rd->argv[0]))
31✔
200
    t = ~LJ_TLIGHTUD;
201
  else
202
    t = ~itype(&rd->argv[0]);
31✔
203
  J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[t]));
36✔
204
  UNUSED(rd);
36✔
205
}
36✔
206

207
static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd)
17✔
208
{
209
  TRef tr = J->base[0];
17✔
210
  if (tr) {
17✔
211
    RecordIndex ix;
17✔
212
    ix.tab = tr;
17✔
213
    copyTV(J->L, &ix.tabv, &rd->argv[0]);
17✔
214
    if (lj_record_mm_lookup(J, &ix, MM_metatable))
17✔
215
      J->base[0] = ix.mobj;
1✔
216
    else
217
      J->base[0] = ix.mt;
16✔
218
  }  /* else: Interpreter will throw. */
219
}
17✔
220

221
static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd)
31✔
222
{
223
  TRef tr = J->base[0];
31✔
224
  TRef mt = J->base[1];
31✔
225
  if (tref_istab(tr) && (tref_istab(mt) || (mt && tref_isnil(mt)))) {
31✔
226
    TRef fref, mtref;
31✔
227
    RecordIndex ix;
31✔
228
    ix.tab = tr;
31✔
229
    copyTV(J->L, &ix.tabv, &rd->argv[0]);
31✔
230
    lj_record_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable. */
31✔
231
    fref = emitir(IRT(IR_FREF, IRT_PGC), tr, IRFL_TAB_META);
31✔
232
    mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt;
31✔
233
    emitir(IRT(IR_FSTORE, IRT_TAB), fref, mtref);
31✔
234
    if (!tref_isnil(mt))
31✔
235
      emitir(IRT(IR_TBAR, IRT_TAB), tr, 0);
30✔
236
    J->base[0] = tr;
31✔
237
    J->needsnap = 1;
31✔
238
  }  /* else: Interpreter will throw. */
239
}
31✔
240

241
static void LJ_FASTCALL recff_rawget(jit_State *J, RecordFFData *rd)
2✔
242
{
243
  RecordIndex ix;
2✔
244
  ix.tab = J->base[0]; ix.key = J->base[1];
2✔
245
  if (tref_istab(ix.tab) && ix.key) {
2✔
246
    ix.val = 0; ix.idxchain = 0;
2✔
247
    settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
2✔
248
    copyTV(J->L, &ix.keyv, &rd->argv[1]);
2✔
249
    J->base[0] = lj_record_idx(J, &ix);
2✔
250
  }  /* else: Interpreter will throw. */
251
}
2✔
252

253
static void LJ_FASTCALL recff_rawset(jit_State *J, RecordFFData *rd)
5✔
254
{
255
  RecordIndex ix;
5✔
256
  ix.tab = J->base[0]; ix.key = J->base[1]; ix.val = J->base[2];
5✔
257
  if (tref_istab(ix.tab) && ix.key && ix.val) {
5✔
258
    ix.idxchain = 0;
5✔
259
    settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
5✔
260
    copyTV(J->L, &ix.keyv, &rd->argv[1]);
5✔
261
    copyTV(J->L, &ix.valv, &rd->argv[2]);
5✔
262
    lj_record_idx(J, &ix);
5✔
263
    /* Pass through table at J->base[0] as result. */
264
  }  /* else: Interpreter will throw. */
265
}
5✔
266

267
static void LJ_FASTCALL recff_rawequal(jit_State *J, RecordFFData *rd)
2✔
268
{
269
  TRef tra = J->base[0];
2✔
270
  TRef trb = J->base[1];
2✔
271
  if (tra && trb) {
2✔
272
    int diff = lj_record_objcmp(J, tra, trb, &rd->argv[0], &rd->argv[1]);
2✔
273
    J->base[0] = diff ? TREF_FALSE : TREF_TRUE;
3✔
274
  }  /* else: Interpreter will throw. */
275
}
2✔
276

277
#if LJ_52
278
static void LJ_FASTCALL recff_rawlen(jit_State *J, RecordFFData *rd)
279
{
280
  TRef tr = J->base[0];
281
  if (tref_isstr(tr))
282
    J->base[0] = emitir(IRTI(IR_FLOAD), tr, IRFL_STR_LEN);
283
  else if (tref_istab(tr))
284
    J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, tr);
285
  /* else: Interpreter will throw. */
286
  UNUSED(rd);
287
}
288
#endif
289

290
/* Determine mode of select() call. */
291
int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv)
13✔
292
{
293
  if (tref_isstr(tr) && *strVdata(tv) == '#') {  /* select('#', ...) */
13✔
294
    if (strV(tv)->len == 1) {
3✔
295
      emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, strV(tv)));
3✔
296
    } else {
297
      TRef trptr = emitir(IRT(IR_STRREF, IRT_PGC), tr, lj_ir_kint(J, 0));
×
298
      TRef trchar = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY);
×
299
      emitir(IRTG(IR_EQ, IRT_INT), trchar, lj_ir_kint(J, '#'));
×
300
    }
301
    return 0;
3✔
302
  } else {  /* select(n, ...) */
303
    int32_t start = argv2int(J, tv);
10✔
304
    if (start == 0) lj_trace_err(J, LJ_TRERR_BADTYPE);  /* A bit misleading. */
10✔
305
    return start;
306
  }
307
}
308

309
static void LJ_FASTCALL recff_select(jit_State *J, RecordFFData *rd)
4✔
310
{
311
  TRef tr = J->base[0];
4✔
312
  if (tr) {
4✔
313
    ptrdiff_t start = lj_ffrecord_select_mode(J, tr, &rd->argv[0]);
4✔
314
    if (start == 0) {  /* select('#', ...) */
4✔
315
      J->base[0] = lj_ir_kint(J, J->maxslot - 1);
2✔
316
    } else if (tref_isk(tr)) {  /* select(k, ...) */
2✔
317
      ptrdiff_t n = (ptrdiff_t)J->maxslot;
2✔
318
      if (start < 0) start += n;
2✔
319
      else if (start > n) start = n;
2✔
320
      rd->nres = n - start;
2✔
321
      if (start >= 1) {
2✔
322
        ptrdiff_t i;
323
        for (i = 0; i < n - start; i++)
5✔
324
          J->base[i] = J->base[start+i];
3✔
325
      }  /* else: Interpreter will throw. */
326
    } else {
327
      recff_nyiu(J, rd);
×
328
      return;
×
329
    }
330
  }  /* else: Interpreter will throw. */
331
}
332

333
static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd)
173✔
334
{
335
  TRef tr = J->base[0];
173✔
336
  TRef base = J->base[1];
173✔
337
  if (tr && !tref_isnil(base)) {
173✔
338
    base = lj_opt_narrow_toint(J, base);
4✔
339
    if (!tref_isk(base) || IR(tref_ref(base))->i != 10) {
4✔
340
      recff_nyiu(J, rd);
4✔
341
      return;
4✔
342
    }
343
  }
344
  if (tref_isnumber_str(tr)) {
169✔
345
    if (tref_isstr(tr)) {
125✔
346
      TValue tmp;
115✔
347
      if (!lj_strscan_num(strV(&rd->argv[0]), &tmp)) {
115✔
348
        recff_nyiu(J, rd);  /* Would need an inverted STRTO for this case. */
×
349
        return;
×
350
      }
351
      tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0);
115✔
352
    }
353
#if LJ_HASFFI
354
  } else if (tref_iscdata(tr)) {
44✔
355
    lj_crecord_tonumber(J, rd);
15✔
356
    return;
15✔
357
#endif
358
  } else {
359
    tr = TREF_NIL;
360
  }
361
  J->base[0] = tr;
154✔
362
  UNUSED(rd);
173✔
363
}
364

365
static TValue *recff_metacall_cp(lua_State *L, lua_CFunction dummy, void *ud)
3✔
366
{
367
  jit_State *J = (jit_State *)ud;
3✔
368
  lj_record_tailcall(J, 0, 1);
3✔
369
  UNUSED(L); UNUSED(dummy);
3✔
370
  return NULL;
3✔
371
}
372

373
static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm)
374
{
375
  RecordIndex ix;
376
  ix.tab = J->base[0];
377
  copyTV(J->L, &ix.tabv, &rd->argv[0]);
378
  if (lj_record_mm_lookup(J, &ix, mm)) {  /* Has metamethod? */
379
    int errcode;
380
    TValue argv0;
381
    /* Temporarily insert metamethod below object. */
382
    J->base[1+LJ_FR2] = J->base[0];
383
    J->base[0] = ix.mobj;
384
    copyTV(J->L, &argv0, &rd->argv[0]);
385
    copyTV(J->L, &rd->argv[1+LJ_FR2], &rd->argv[0]);
386
    copyTV(J->L, &rd->argv[0], &ix.mobjv);
387
    /* Need to protect lj_record_tailcall because it may throw. */
388
    errcode = lj_vm_cpcall(J->L, NULL, J, recff_metacall_cp);
389
    /* Always undo Lua stack changes to avoid confusing the interpreter. */
390
    copyTV(J->L, &rd->argv[0], &argv0);
391
    if (errcode)
392
      lj_err_throw(J->L, errcode);  /* Propagate errors. */
393
    rd->nres = -1;  /* Pending call. */
394
    return 1;  /* Tailcalled to metamethod. */
395
  }
396
  return 0;
397
}
398

399
static void LJ_FASTCALL recff_tostring(jit_State *J, RecordFFData *rd)
53✔
400
{
401
  TRef tr = J->base[0];
53✔
402
  if (tref_isstr(tr)) {
53✔
403
    /* Ignore __tostring in the string base metatable. */
404
    /* Pass on result in J->base[0]. */
405
  } else if (tr && !recff_metacall(J, rd, MM_tostring)) {
39✔
406
    if (tref_isnumber(tr)) {
36✔
407
      J->base[0] = emitir(IRT(IR_TOSTR, IRT_STR), tr,
34✔
408
                          tref_isnum(tr) ? IRTOSTR_NUM : IRTOSTR_INT);
409
    } else if (tref_ispri(tr)) {
2✔
410
      J->base[0] = lj_ir_kstr(J, lj_strfmt_obj(J->L, &rd->argv[0]));
1✔
411
    } else {
412
      recff_nyiu(J, rd);
1✔
413
      return;
1✔
414
    }
415
  }
416
}
417

418
static void LJ_FASTCALL recff_ipairs_aux(jit_State *J, RecordFFData *rd)
104✔
419
{
420
  RecordIndex ix;
104✔
421
  ix.tab = J->base[0];
104✔
422
  if (tref_istab(ix.tab)) {
104✔
423
    if (!tvisnumber(&rd->argv[1]))  /* No support for string coercion. */
104✔
424
      lj_trace_err(J, LJ_TRERR_BADTYPE);
×
425
    setintV(&ix.keyv, numberVint(&rd->argv[1])+1);
104✔
426
    settabV(J->L, &ix.tabv, tabV(&rd->argv[0]));
104✔
427
    ix.val = 0; ix.idxchain = 0;
104✔
428
    ix.key = lj_opt_narrow_toint(J, J->base[1]);
104✔
429
    J->base[0] = ix.key = emitir(IRTI(IR_ADD), ix.key, lj_ir_kint(J, 1));
104✔
430
    J->base[1] = lj_record_idx(J, &ix);
104✔
431
    rd->nres = tref_isnil(J->base[1]) ? 0 : 2;
182✔
432
  }  /* else: Interpreter will throw. */
433
}
104✔
434

435
static void LJ_FASTCALL recff_xpairs(jit_State *J, RecordFFData *rd)
30✔
436
{
437
  TRef tr = J->base[0];
30✔
438
  if (!((LJ_52 || (LJ_HASFFI && tref_iscdata(tr))) &&
30✔
439
        recff_metacall(J, rd, MM_pairs + rd->data))) {
×
440
    if (tref_istab(tr)) {
30✔
441
      J->base[0] = lj_ir_kfunc(J, funcV(&J->fn->c.upvalue[0]));
30✔
442
      J->base[1] = tr;
30✔
443
      J->base[2] = rd->data ? lj_ir_kint(J, 0) : TREF_NIL;
30✔
444
      rd->nres = 3;
30✔
445
    }  /* else: Interpreter will throw. */
446
  }
447
}
30✔
448

449
static void LJ_FASTCALL recff_pcall(jit_State *J, RecordFFData *rd)
24✔
450
{
451
  if (J->maxslot >= 1) {
24✔
452
#if LJ_FR2
453
    /* Shift function arguments up. */
454
    memmove(J->base + 1, J->base, sizeof(TRef) * J->maxslot);
24✔
455
#endif
456
    lj_record_call(J, 0, J->maxslot - 1);
24✔
457
    rd->nres = -1;  /* Pending call. */
24✔
458
    J->needsnap = 1;  /* Start catching on-trace errors. */
24✔
459
  }  /* else: Interpreter will throw. */
460
}
24✔
461

462
static TValue *recff_xpcall_cp(lua_State *L, lua_CFunction dummy, void *ud)
7✔
463
{
464
  jit_State *J = (jit_State *)ud;
7✔
465
  lj_record_call(J, 1, J->maxslot - 2);
7✔
466
  UNUSED(L); UNUSED(dummy);
7✔
467
  return NULL;
7✔
468
}
469

470
static void LJ_FASTCALL recff_xpcall(jit_State *J, RecordFFData *rd)
7✔
471
{
472
  if (J->maxslot >= 2) {
7✔
473
    TValue argv0, argv1;
7✔
474
    TRef tmp;
7✔
475
    int errcode;
7✔
476
    /* Swap function and traceback. */
477
    tmp = J->base[0]; J->base[0] = J->base[1]; J->base[1] = tmp;
7✔
478
    copyTV(J->L, &argv0, &rd->argv[0]);
7✔
479
    copyTV(J->L, &argv1, &rd->argv[1]);
7✔
480
    copyTV(J->L, &rd->argv[0], &argv1);
7✔
481
    copyTV(J->L, &rd->argv[1], &argv0);
7✔
482
#if LJ_FR2
483
    /* Shift function arguments up. */
484
    memmove(J->base + 2, J->base + 1, sizeof(TRef) * (J->maxslot-1));
7✔
485
#endif
486
    /* Need to protect lj_record_call because it may throw. */
487
    errcode = lj_vm_cpcall(J->L, NULL, J, recff_xpcall_cp);
7✔
488
    /* Always undo Lua stack swap to avoid confusing the interpreter. */
489
    copyTV(J->L, &rd->argv[0], &argv0);
7✔
490
    copyTV(J->L, &rd->argv[1], &argv1);
7✔
491
    if (errcode)
7✔
492
      lj_err_throw(J->L, errcode);  /* Propagate errors. */
×
493
    rd->nres = -1;  /* Pending call. */
7✔
494
    J->needsnap = 1;  /* Start catching on-trace errors. */
7✔
495
  }  /* else: Interpreter will throw. */
496
}
7✔
497

498
static void LJ_FASTCALL recff_getfenv(jit_State *J, RecordFFData *rd)
3✔
499
{
500
  TRef tr = J->base[0];
3✔
501
  /* Only support getfenv(0) for now. */
502
  if (tref_isint(tr) && tref_isk(tr) && IR(tref_ref(tr))->i == 0) {
3✔
503
    TRef trl = emitir(IRT(IR_LREF, IRT_THREAD), 0, 0);
2✔
504
    J->base[0] = emitir(IRT(IR_FLOAD, IRT_TAB), trl, IRFL_THREAD_ENV);
2✔
505
    return;
2✔
506
  }
507
  recff_nyiu(J, rd);
1✔
508
}
509

510
/* -- Math library fast functions ----------------------------------------- */
511

512
static void LJ_FASTCALL recff_math_abs(jit_State *J, RecordFFData *rd)
188✔
513
{
514
  TRef tr = lj_ir_tonum(J, J->base[0]);
188✔
515
  J->base[0] = emitir(IRTN(IR_ABS), tr, lj_ir_ksimd(J, LJ_KSIMD_ABS));
188✔
516
  UNUSED(rd);
188✔
517
}
188✔
518

519
/* Record rounding functions math.floor and math.ceil. */
520
static void LJ_FASTCALL recff_math_round(jit_State *J, RecordFFData *rd)
59✔
521
{
522
  TRef tr = J->base[0];
59✔
523
  if (!tref_isinteger(tr)) {  /* Pass through integers unmodified. */
59✔
524
    tr = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, tr), rd->data);
55✔
525
    /* Result is integral (or NaN/Inf), but may not fit an int32_t. */
526
    if (LJ_DUALNUM) {  /* Try to narrow using a guarded conversion to int. */
55✔
527
      lua_Number n = lj_vm_foldfpm(numberVnum(&rd->argv[0]), rd->data);
528
      if (n == (lua_Number)lj_num2int(n))
529
        tr = emitir(IRTGI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_CHECK);
530
    }
531
    J->base[0] = tr;
55✔
532
  }
533
}
59✔
534

535
/* Record unary math.* functions, mapped to IR_FPMATH opcode. */
536
static void LJ_FASTCALL recff_math_unary(jit_State *J, RecordFFData *rd)
2✔
537
{
538
  J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data);
2✔
539
}
2✔
540

541
/* Record math.log. */
542
static void LJ_FASTCALL recff_math_log(jit_State *J, RecordFFData *rd)
1✔
543
{
544
  TRef tr = lj_ir_tonum(J, J->base[0]);
1✔
545
  if (J->base[1]) {
1✔
546
#ifdef LUAJIT_NO_LOG2
547
    uint32_t fpm = IRFPM_LOG;
548
#else
549
    uint32_t fpm = IRFPM_LOG2;
×
550
#endif
551
    TRef trb = lj_ir_tonum(J, J->base[1]);
×
552
    tr = emitir(IRTN(IR_FPMATH), tr, fpm);
×
553
    trb = emitir(IRTN(IR_FPMATH), trb, fpm);
×
554
    trb = emitir(IRTN(IR_DIV), lj_ir_knum_one(J), trb);
×
555
    tr = emitir(IRTN(IR_MUL), tr, trb);
×
556
  } else {
557
    tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_LOG);
1✔
558
  }
559
  J->base[0] = tr;
1✔
560
  UNUSED(rd);
1✔
561
}
1✔
562

563
/* Record math.atan2. */
564
static void LJ_FASTCALL recff_math_atan2(jit_State *J, RecordFFData *rd)
1✔
565
{
566
  TRef tr = lj_ir_tonum(J, J->base[0]);
1✔
567
  TRef tr2 = lj_ir_tonum(J, J->base[1]);
1✔
568
  J->base[0] = lj_ir_call(J, IRCALL_atan2, tr, tr2);
1✔
569
  UNUSED(rd);
1✔
570
}
1✔
571

572
/* Record math.ldexp. */
573
static void LJ_FASTCALL recff_math_ldexp(jit_State *J, RecordFFData *rd)
131✔
574
{
575
  TRef tr = lj_ir_tonum(J, J->base[0]);
131✔
576
#if LJ_TARGET_X86ORX64
577
  TRef tr2 = lj_ir_tonum(J, J->base[1]);
131✔
578
#else
579
  TRef tr2 = lj_opt_narrow_toint(J, J->base[1]);
580
#endif
581
  J->base[0] = emitir(IRTN(IR_LDEXP), tr, tr2);
131✔
582
  UNUSED(rd);
131✔
583
}
131✔
584

585
static void LJ_FASTCALL recff_math_call(jit_State *J, RecordFFData *rd)
5✔
586
{
587
  TRef tr = lj_ir_tonum(J, J->base[0]);
5✔
588
  J->base[0] = emitir(IRTN(IR_CALLN), tr, rd->data);
5✔
589
}
5✔
590

591
static void LJ_FASTCALL recff_math_pow(jit_State *J, RecordFFData *rd)
×
592
{
593
  J->base[0] = lj_opt_narrow_arith(J, J->base[0], J->base[1],
×
594
                                   &rd->argv[0], &rd->argv[1], IR_POW);
×
595
  UNUSED(rd);
×
596
}
×
597

598
static void LJ_FASTCALL recff_math_minmax(jit_State *J, RecordFFData *rd)
589✔
599
{
600
  TRef tr = lj_ir_tonumber(J, J->base[0]);
589✔
601
  uint32_t op = rd->data;
589✔
602
  BCReg i;
589✔
603
  for (i = 1; J->base[i] != 0; i++) {
1,178✔
604
    TRef tr2 = lj_ir_tonumber(J, J->base[i]);
589✔
605
    IRType t = IRT_INT;
589✔
606
    if (!(tref_isinteger(tr) && tref_isinteger(tr2))) {
589✔
607
      if (tref_isinteger(tr)) tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT);
534✔
608
      if (tref_isinteger(tr2)) tr2 = emitir(IRTN(IR_CONV), tr2, IRCONV_NUM_INT);
534✔
609
      t = IRT_NUM;
610
    }
611
    tr = emitir(IRT(op, t), tr, tr2);
589✔
612
  }
613
  J->base[0] = tr;
589✔
614
}
589✔
615

616
static void LJ_FASTCALL recff_math_random(jit_State *J, RecordFFData *rd)
7✔
617
{
618
  GCudata *ud = udataV(&J->fn->c.upvalue[0]);
7✔
619
  TRef tr, one;
7✔
620
  lj_ir_kgc(J, obj2gco(ud), IRT_UDATA);  /* Prevent collection. */
7✔
621
  tr = lj_ir_call(J, IRCALL_lj_math_random_step, lj_ir_kptr(J, uddata(ud)));
7✔
622
  one = lj_ir_knum_one(J);
7✔
623
  tr = emitir(IRTN(IR_SUB), tr, one);
7✔
624
  if (J->base[0]) {
7✔
625
    TRef tr1 = lj_ir_tonum(J, J->base[0]);
6✔
626
    if (J->base[1]) {  /* d = floor(d*(r2-r1+1.0)) + r1 */
6✔
627
      TRef tr2 = lj_ir_tonum(J, J->base[1]);
2✔
628
      tr2 = emitir(IRTN(IR_SUB), tr2, tr1);
2✔
629
      tr2 = emitir(IRTN(IR_ADD), tr2, one);
2✔
630
      tr = emitir(IRTN(IR_MUL), tr, tr2);
2✔
631
      tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR);
2✔
632
      tr = emitir(IRTN(IR_ADD), tr, tr1);
2✔
633
    } else {  /* d = floor(d*r1) + 1.0 */
634
      tr = emitir(IRTN(IR_MUL), tr, tr1);
4✔
635
      tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR);
4✔
636
      tr = emitir(IRTN(IR_ADD), tr, one);
4✔
637
    }
638
  }
639
  J->base[0] = tr;
7✔
640
  UNUSED(rd);
7✔
641
}
7✔
642

643
/* -- Bit library fast functions ------------------------------------------ */
644

645
/* Record bit.tobit. */
646
static void LJ_FASTCALL recff_bit_tobit(jit_State *J, RecordFFData *rd)
8✔
647
{
648
  TRef tr = J->base[0];
8✔
649
#if LJ_HASFFI
650
  if (tref_iscdata(tr)) { recff_bit64_tobit(J, rd); return; }
8✔
651
#endif
652
  J->base[0] = lj_opt_narrow_tobit(J, tr);
8✔
653
  UNUSED(rd);
8✔
654
}
655

656
/* Record unary bit.bnot, bit.bswap. */
657
static void LJ_FASTCALL recff_bit_unary(jit_State *J, RecordFFData *rd)
4✔
658
{
659
#if LJ_HASFFI
660
  if (recff_bit64_unary(J, rd))
4✔
661
    return;
662
#endif
663
  J->base[0] = emitir(IRTI(rd->data), lj_opt_narrow_tobit(J, J->base[0]), 0);
2✔
664
}
665

666
/* Record N-ary bit.band, bit.bor, bit.bxor. */
667
static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd)
195✔
668
{
669
#if LJ_HASFFI
670
  if (recff_bit64_nary(J, rd))
195✔
671
    return;
672
#endif
673
  {
674
    TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
171✔
675
    uint32_t ot = IRTI(rd->data);
171✔
676
    BCReg i;
171✔
677
    for (i = 1; J->base[i] != 0; i++)
342✔
678
      tr = emitir(ot, tr, lj_opt_narrow_tobit(J, J->base[i]));
171✔
679
    J->base[0] = tr;
171✔
680
  }
681
}
682

683
/* Record bit shifts. */
684
static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd)
48✔
685
{
686
#if LJ_HASFFI
687
  if (recff_bit64_shift(J, rd))
48✔
688
    return;
689
#endif
690
  {
691
    TRef tr = lj_opt_narrow_tobit(J, J->base[0]);
34✔
692
    TRef tsh = lj_opt_narrow_tobit(J, J->base[1]);
33✔
693
    IROp op = (IROp)rd->data;
33✔
694
    if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) &&
33✔
695
        !tref_isk(tsh))
696
      tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 31));
697
#ifdef LJ_TARGET_UNIFYROT
698
    if (op == (LJ_TARGET_UNIFYROT == 1 ? IR_BROR : IR_BROL)) {
699
      op = LJ_TARGET_UNIFYROT == 1 ? IR_BROL : IR_BROR;
700
      tsh = emitir(IRTI(IR_NEG), tsh, tsh);
701
    }
702
#endif
703
    J->base[0] = emitir(IRTI(op), tr, tsh);
33✔
704
  }
705
}
706

707
static void LJ_FASTCALL recff_bit_tohex(jit_State *J, RecordFFData *rd)
13✔
708
{
709
#if LJ_HASFFI
710
  TRef hdr = recff_bufhdr(J);
13✔
711
  TRef tr = recff_bit64_tohex(J, rd, hdr);
13✔
712
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
13✔
713
#else
714
  recff_nyiu(J, rd);  /* Don't bother working around this NYI. */
715
#endif
716
}
13✔
717

718
/* -- String library fast functions --------------------------------------- */
719

720
/* Specialize to relative starting position for string. */
721
static TRef recff_string_start(jit_State *J, GCstr *s, int32_t *st, TRef tr,
722
                               TRef trlen, TRef tr0)
723
{
724
  int32_t start = *st;
725
  if (start < 0) {
726
    emitir(IRTGI(IR_LT), tr, tr0);
727
    tr = emitir(IRTI(IR_ADD), trlen, tr);
728
    start = start + (int32_t)s->len;
729
    emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), tr, tr0);
730
    if (start < 0) {
731
      tr = tr0;
732
      start = 0;
733
    }
734
  } else if (start == 0) {
735
    emitir(IRTGI(IR_EQ), tr, tr0);
736
    tr = tr0;
737
  } else {
738
    tr = emitir(IRTI(IR_ADD), tr, lj_ir_kint(J, -1));
739
    emitir(IRTGI(IR_GE), tr, tr0);
740
    start--;
741
  }
742
  *st = start;
743
  return tr;
744
}
745

746
/* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */
747
static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd)
158✔
748
{
749
  TRef trstr = lj_ir_tostr(J, J->base[0]);
158✔
750
  TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN);
158✔
751
  TRef tr0 = lj_ir_kint(J, 0);
158✔
752
  TRef trstart, trend;
158✔
753
  GCstr *str = argv2str(J, &rd->argv[0]);
158✔
754
  int32_t start, end;
158✔
755
  if (rd->data) {  /* string.sub(str, start [,end]) */
158✔
756
    start = argv2int(J, &rd->argv[1]);
93✔
757
    trstart = lj_opt_narrow_toint(J, J->base[1]);
93✔
758
    trend = J->base[2];
93✔
759
    if (tref_isnil(trend)) {
93✔
760
      trend = lj_ir_kint(J, -1);
22✔
761
      end = -1;
22✔
762
    } else {
763
      trend = lj_opt_narrow_toint(J, trend);
71✔
764
      end = argv2int(J, &rd->argv[2]);
71✔
765
    }
766
  } else {  /* string.byte(str, [,start [,end]]) */
767
    if (tref_isnil(J->base[1])) {
65✔
768
      start = 1;
2✔
769
      trstart = lj_ir_kint(J, 1);
2✔
770
    } else {
771
      start = argv2int(J, &rd->argv[1]);
63✔
772
      trstart = lj_opt_narrow_toint(J, J->base[1]);
63✔
773
    }
774
    if (J->base[1] && !tref_isnil(J->base[2])) {
65✔
775
      trend = lj_opt_narrow_toint(J, J->base[2]);
61✔
776
      end = argv2int(J, &rd->argv[2]);
61✔
777
    } else {
778
      trend = trstart;
4✔
779
      end = start;
4✔
780
    }
781
  }
782
  if (end < 0) {
158✔
783
    emitir(IRTGI(IR_LT), trend, tr0);
68✔
784
    trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend),
68✔
785
                   lj_ir_kint(J, 1));
786
    end = end+(int32_t)str->len+1;
68✔
787
  } else if ((MSize)end <= str->len) {
90✔
788
    emitir(IRTGI(IR_ULE), trend, trlen);
81✔
789
  } else {
790
    emitir(IRTGI(IR_UGT), trend, trlen);
9✔
791
    end = (int32_t)str->len;
9✔
792
    trend = trlen;
9✔
793
  }
794
  trstart = recff_string_start(J, str, &start, trstart, trlen, tr0);
158✔
795
  if (rd->data) {  /* Return string.sub result. */
158✔
796
    if (end - start >= 0) {
93✔
797
      /* Also handle empty range here, to avoid extra traces. */
798
      TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart);
85✔
799
      emitir(IRTGI(IR_GE), trslen, tr0);
85✔
800
      trptr = emitir(IRT(IR_STRREF, IRT_PGC), trstr, trstart);
85✔
801
      J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen);
85✔
802
    } else {  /* Range underflow: return empty string. */
803
      emitir(IRTGI(IR_LT), trend, trstart);
8✔
804
      J->base[0] = lj_ir_kstr(J, &J2G(J)->strempty);
8✔
805
    }
806
  } else {  /* Return string.byte result(s). */
807
    ptrdiff_t i, len = end - start;
65✔
808
    if (len > 0) {
65✔
809
      TRef trslen = emitir(IRTI(IR_SUB), trend, trstart);
53✔
810
      emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len));
53✔
811
      if (J->baseslot + len > LJ_MAX_JSLOTS)
53✔
812
        lj_trace_err_info(J, LJ_TRERR_STACKOV);
2✔
813
      rd->nres = len;
51✔
814
      for (i = 0; i < len; i++) {
160✔
815
        TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i));
109✔
816
        tmp = emitir(IRT(IR_STRREF, IRT_PGC), trstr, tmp);
109✔
817
        J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY);
109✔
818
      }
819
    } else {  /* Empty range or range underflow: return no results. */
820
      emitir(IRTGI(IR_LE), trend, trstart);
12✔
821
      rd->nres = 0;
12✔
822
    }
823
  }
824
}
156✔
825

826
static void LJ_FASTCALL recff_string_char(jit_State *J, RecordFFData *rd)
40✔
827
{
828
  TRef k255 = lj_ir_kint(J, 255);
40✔
829
  BCReg i;
40✔
830
  for (i = 0; J->base[i] != 0; i++) {  /* Convert char values to strings. */
173✔
831
    TRef tr = lj_opt_narrow_toint(J, J->base[i]);
93✔
832
    emitir(IRTGI(IR_ULE), tr, k255);
93✔
833
    J->base[i] = emitir(IRT(IR_TOSTR, IRT_STR), tr, IRTOSTR_CHAR);
93✔
834
  }
835
  if (i > 1) {  /* Concatenate the strings, if there's more than one. */
40✔
836
    TRef hdr = recff_bufhdr(J), tr = hdr;
34✔
837
    for (i = 0; J->base[i] != 0; i++)
156✔
838
      tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, J->base[i]);
88✔
839
    J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
34✔
840
  } else if (i == 0) {
6✔
841
    J->base[0] = lj_ir_kstr(J, &J2G(J)->strempty);
1✔
842
  }
843
  UNUSED(rd);
40✔
844
}
40✔
845

846
static void LJ_FASTCALL recff_string_rep(jit_State *J, RecordFFData *rd)
44✔
847
{
848
  TRef str = lj_ir_tostr(J, J->base[0]);
44✔
849
  TRef rep = lj_opt_narrow_toint(J, J->base[1]);
44✔
850
  TRef hdr, tr, str2 = 0;
44✔
851
  if (!tref_isnil(J->base[2])) {
44✔
852
    TRef sep = lj_ir_tostr(J, J->base[2]);
3✔
853
    int32_t vrep = argv2int(J, &rd->argv[1]);
3✔
854
    emitir(IRTGI(vrep > 1 ? IR_GT : IR_LE), rep, lj_ir_kint(J, 1));
4✔
855
    if (vrep > 1) {
3✔
856
      TRef hdr2 = recff_bufhdr(J);
2✔
857
      TRef tr2 = emitir(IRT(IR_BUFPUT, IRT_PGC), hdr2, sep);
2✔
858
      tr2 = emitir(IRT(IR_BUFPUT, IRT_PGC), tr2, str);
2✔
859
      str2 = emitir(IRT(IR_BUFSTR, IRT_STR), tr2, hdr2);
2✔
860
    }
861
  }
862
  tr = hdr = recff_bufhdr(J);
44✔
863
  if (str2) {
44✔
864
    tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, str);
2✔
865
    str = str2;
2✔
866
    rep = emitir(IRTI(IR_ADD), rep, lj_ir_kint(J, -1));
2✔
867
  }
868
  tr = lj_ir_call(J, IRCALL_lj_buf_putstr_rep, tr, str, rep);
44✔
869
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
44✔
870
}
44✔
871

872
static void LJ_FASTCALL recff_string_op(jit_State *J, RecordFFData *rd)
17✔
873
{
874
  TRef str = lj_ir_tostr(J, J->base[0]);
17✔
875
  TRef hdr = recff_bufhdr(J);
17✔
876
  TRef tr = lj_ir_call(J, rd->data, hdr, str);
17✔
877
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
17✔
878
}
17✔
879

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

940
static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
176✔
941
{
942
  TRef trfmt = lj_ir_tostr(J, J->base[0]);
176✔
943
  GCstr *fmt = argv2str(J, &rd->argv[0]);
176✔
944
  int arg = 1;
176✔
945
  TRef hdr, tr;
176✔
946
  FormatState fs;
176✔
947
  SFormat sf;
176✔
948
  /* Specialize to the format string. */
949
  emitir(IRTG(IR_EQ, IRT_STR), trfmt, lj_ir_kstr(J, fmt));
176✔
950
  tr = hdr = recff_bufhdr(J);
176✔
951
  lj_strfmt_init(&fs, strdata(fmt), fmt->len);
176✔
952
  while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {  /* Parse format. */
501✔
953
    TRef tra = sf == STRFMT_LIT ? 0 : J->base[arg++];
333✔
954
    TRef trsf = lj_ir_kint(J, (int32_t)sf);
333✔
955
    IRCallID id;
333✔
956
    switch (STRFMT_TYPE(sf)) {
333✔
957
    case STRFMT_LIT:
121✔
958
      tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr,
121✔
959
                  lj_ir_kstr(J, lj_str_new(J->L, fs.str, fs.len)));
960
      break;
121✔
961
    case STRFMT_INT:
962
      id = IRCALL_lj_strfmt_putfnum_int;
963
    handle_int:
43✔
964
      if (!tref_isinteger(tra))
43✔
965
        goto handle_num;
27✔
966
      if (sf == STRFMT_INT) { /* Shortcut for plain %d. */
16✔
967
        tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr,
8✔
968
                    emitir(IRT(IR_TOSTR, IRT_STR), tra, IRTOSTR_INT));
969
      } else {
970
#if LJ_HASFFI
971
        tra = emitir(IRT(IR_CONV, IRT_U64), tra,
8✔
972
                     (IRT_INT|(IRT_U64<<5)|IRCONV_SEXT));
973
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putfxint, tr, trsf, tra);
8✔
974
        lj_needsplit(J);
975
#else
976
        recff_nyiu(J, rd);  /* Don't bother working around this NYI. */
977
        return;
978
#endif
979
      }
980
      break;
981
    case STRFMT_UINT:
4✔
982
      id = IRCALL_lj_strfmt_putfnum_uint;
4✔
983
      goto handle_int;
4✔
984
    case STRFMT_NUM:
985
      id = IRCALL_lj_strfmt_putfnum;
986
    handle_num:
146✔
987
      tra = lj_ir_tonum(J, tra);
146✔
988
      tr = lj_ir_call(J, id, tr, trsf, tra);
146✔
989
      if (LJ_SOFTFP32) lj_needsplit(J);
146✔
990
      break;
146✔
991
    case STRFMT_STR:
50✔
992
      if (!tref_isstr(tra)) {
50✔
993
        recff_nyiu(J, rd);  /* NYI: __tostring and non-string types for %s. */
8✔
994
        return;
16✔
995
      }
996
      if (sf == STRFMT_STR)  /* Shortcut for plain %s. */
42✔
997
        tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, tra);
36✔
998
      else if ((sf & STRFMT_T_QUOTED))
6✔
999
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putquoted, tr, tra);
2✔
1000
      else
1001
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putfstr, tr, trsf, tra);
4✔
1002
      break;
1003
    case STRFMT_CHAR:
×
1004
      tra = lj_opt_narrow_toint(J, tra);
×
1005
      if (sf == STRFMT_CHAR)  /* Shortcut for plain %c. */
×
1006
        tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr,
×
1007
                    emitir(IRT(IR_TOSTR, IRT_STR), tra, IRTOSTR_CHAR));
1008
      else
1009
        tr = lj_ir_call(J, IRCALL_lj_strfmt_putfchar, tr, trsf, tra);
×
1010
      break;
1011
    case STRFMT_PTR:  /* NYI */
×
1012
    case STRFMT_ERR:
1013
    default:
1014
      recff_nyiu(J, rd);
×
1015
      return;
×
1016
    }
1017
  }
1018
  J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
168✔
1019
}
1020

1021
/* -- Table library fast functions ---------------------------------------- */
1022

1023
static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd)
34✔
1024
{
1025
  RecordIndex ix;
34✔
1026
  ix.tab = J->base[0];
34✔
1027
  ix.val = J->base[1];
34✔
1028
  rd->nres = 0;
34✔
1029
  if (tref_istab(ix.tab) && ix.val) {
34✔
1030
    if (!J->base[2]) {  /* Simple push: t[#t+1] = v */
34✔
1031
      TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, ix.tab);
33✔
1032
      GCtab *t = tabV(&rd->argv[0]);
33✔
1033
      ix.key = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1));
33✔
1034
      settabV(J->L, &ix.tabv, t);
33✔
1035
      setintV(&ix.keyv, lj_tab_len(t) + 1);
33✔
1036
      ix.idxchain = 0;
33✔
1037
      lj_record_idx(J, &ix);  /* Set new value. */
33✔
1038
    } else {  /* Complex case: insert in the middle. */
1039
      recff_nyiu(J, rd);
1✔
1040
      return;
1✔
1041
    }
1042
  }  /* else: Interpreter will throw. */
1043
}
1044

1045
static void LJ_FASTCALL recff_table_concat(jit_State *J, RecordFFData *rd)
11✔
1046
{
1047
  TRef tab = J->base[0];
11✔
1048
  if (tref_istab(tab)) {
11✔
1049
    TRef sep = !tref_isnil(J->base[1]) ?
22✔
1050
               lj_ir_tostr(J, J->base[1]) : lj_ir_knull(J, IRT_STR);
11✔
1051
    TRef tri = (J->base[1] && !tref_isnil(J->base[2])) ?
11✔
1052
               lj_opt_narrow_toint(J, J->base[2]) : lj_ir_kint(J, 1);
22✔
1053
    TRef tre = (J->base[1] && J->base[2] && !tref_isnil(J->base[3])) ?
11✔
1054
               lj_opt_narrow_toint(J, J->base[3]) :
14✔
1055
               lj_ir_call(J, IRCALL_lj_tab_len, tab);
8✔
1056
    TRef hdr = recff_bufhdr(J);
11✔
1057
    TRef tr = lj_ir_call(J, IRCALL_lj_buf_puttab, hdr, tab, sep, tri, tre);
11✔
1058
    emitir(IRTG(IR_NE, IRT_PTR), tr, lj_ir_kptr(J, NULL));
11✔
1059
    J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
11✔
1060
  }  /* else: Interpreter will throw. */
1061
  UNUSED(rd);
11✔
1062
}
11✔
1063

1064
static void LJ_FASTCALL recff_table_new(jit_State *J, RecordFFData *rd)
1✔
1065
{
1066
  TRef tra = lj_opt_narrow_toint(J, J->base[0]);
1✔
1067
  TRef trh = lj_opt_narrow_toint(J, J->base[1]);
1✔
1068
  J->base[0] = lj_ir_call(J, IRCALL_lj_tab_new_ah, tra, trh);
1✔
1069
  UNUSED(rd);
1✔
1070
}
1✔
1071

1072
static void LJ_FASTCALL recff_table_clear(jit_State *J, RecordFFData *rd)
×
1073
{
1074
  TRef tr = J->base[0];
×
1075
  if (tref_istab(tr)) {
×
1076
    rd->nres = 0;
×
1077
    lj_ir_call(J, IRCALL_lj_tab_clear, tr);
×
1078
    J->needsnap = 1;
×
1079
  }  /* else: Interpreter will throw. */
1080
}
×
1081

1082
/* -- I/O library fast functions ------------------------------------------ */
1083

1084
/* Get FILE* for I/O function. Any I/O error aborts recording, so there's
1085
** no need to encode the alternate cases for any of the guards.
1086
*/
1087
static TRef recff_io_fp(jit_State *J, TRef *udp, int32_t id)
32✔
1088
{
1089
  TRef tr, ud, fp;
32✔
1090
  if (id) {  /* io.func() */
32✔
1091
#if LJ_GC64
1092
    /* TODO: fix ARM32 asm_fload(), so we can use this for all archs. */
1093
    ud = lj_ir_ggfload(J, IRT_UDATA, GG_OFS(g.gcroot[id]));
32✔
1094
#else
1095
    tr = lj_ir_kptr(J, &J2G(J)->gcroot[id]);
1096
    ud = emitir(IRT(IR_XLOAD, IRT_UDATA), tr, 0);
1097
#endif
1098
  } else {  /* fp:method() */
1099
    ud = J->base[0];
×
1100
    if (!tref_isudata(ud))
×
1101
      lj_trace_err(J, LJ_TRERR_BADTYPE);
×
1102
    tr = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE);
×
1103
    emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE));
×
1104
  }
1105
  *udp = ud;
32✔
1106
  fp = emitir(IRT(IR_FLOAD, IRT_PTR), ud, IRFL_UDATA_FILE);
32✔
1107
  emitir(IRTG(IR_NE, IRT_PTR), fp, lj_ir_knull(J, IRT_PTR));
32✔
1108
  return fp;
32✔
1109
}
1110

1111
static void LJ_FASTCALL recff_io_write(jit_State *J, RecordFFData *rd)
32✔
1112
{
1113
  TRef ud, fp = recff_io_fp(J, &ud, rd->data);
32✔
1114
  TRef zero = lj_ir_kint(J, 0);
32✔
1115
  TRef one = lj_ir_kint(J, 1);
32✔
1116
  ptrdiff_t i = rd->data == 0 ? 1 : 0;
32✔
1117
  for (; J->base[i]; i++) {
100✔
1118
    TRef str = lj_ir_tostr(J, J->base[i]);
68✔
1119
    TRef buf = emitir(IRT(IR_STRREF, IRT_PGC), str, zero);
68✔
1120
    TRef len = emitir(IRTI(IR_FLOAD), str, IRFL_STR_LEN);
68✔
1121
    if (tref_isk(len) && IR(tref_ref(len))->i == 1) {
68✔
1122
      IRIns *irs = IR(tref_ref(str));
4✔
1123
      TRef tr = (irs->o == IR_TOSTR && irs->op2 == IRTOSTR_CHAR) ?
8✔
1124
                irs->op1 :
4✔
1125
                emitir(IRT(IR_XLOAD, IRT_U8), buf, IRXLOAD_READONLY);
4✔
1126
      tr = lj_ir_call(J, IRCALL_fputc, tr, fp);
4✔
1127
      if (results_wanted(J) != 0)  /* Check result only if not ignored. */
4✔
1128
        emitir(IRTGI(IR_NE), tr, lj_ir_kint(J, -1));
×
1129
    } else {
1130
      TRef tr = lj_ir_call(J, IRCALL_fwrite, buf, one, len, fp);
64✔
1131
      if (results_wanted(J) != 0)  /* Check result only if not ignored. */
64✔
1132
        emitir(IRTGI(IR_EQ), tr, len);
×
1133
    }
1134
  }
1135
  J->base[0] = LJ_52 ? ud : TREF_TRUE;
32✔
1136
}
32✔
1137

1138
static void LJ_FASTCALL recff_io_flush(jit_State *J, RecordFFData *rd)
×
1139
{
1140
  TRef ud, fp = recff_io_fp(J, &ud, rd->data);
×
1141
  TRef tr = lj_ir_call(J, IRCALL_fflush, fp);
×
1142
  if (results_wanted(J) != 0)  /* Check result only if not ignored. */
×
1143
    emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0));
×
1144
  J->base[0] = TREF_TRUE;
×
1145
}
×
1146

1147
/* -- Debug library fast functions ---------------------------------------- */
1148

1149
static void LJ_FASTCALL recff_debug_getmetatable(jit_State *J, RecordFFData *rd)
×
1150
{
1151
  GCtab *mt;
×
1152
  TRef mtref;
×
1153
  TRef tr = J->base[0];
×
1154
  if (tref_istab(tr)) {
×
1155
    mt = tabref(tabV(&rd->argv[0])->metatable);
×
1156
    mtref = emitir(IRT(IR_FLOAD, IRT_TAB), tr, IRFL_TAB_META);
×
1157
  } else if (tref_isudata(tr)) {
×
1158
    mt = tabref(udataV(&rd->argv[0])->metatable);
×
1159
    mtref = emitir(IRT(IR_FLOAD, IRT_TAB), tr, IRFL_UDATA_META);
×
1160
  } else {
1161
    mt = tabref(basemt_obj(J2G(J), &rd->argv[0]));
×
1162
    J->base[0] = mt ? lj_ir_ktab(J, mt) : TREF_NIL;
×
1163
    return;
×
1164
  }
1165
  emitir(IRTG(mt ? IR_NE : IR_EQ, IRT_TAB), mtref, lj_ir_knull(J, IRT_TAB));
×
1166
  J->base[0] = mt ? mtref : TREF_NIL;
×
1167
}
1168

1169
/* -- Record calls to fast functions -------------------------------------- */
1170

1171
#include "lj_recdef.h"
1172

1173
static uint32_t recdef_lookup(GCfunc *fn)
3,544✔
1174
{
1175
  if (fn->c.ffid < sizeof(recff_idmap)/sizeof(recff_idmap[0]))
3,544✔
1176
    return recff_idmap[fn->c.ffid];
3,426✔
1177
  else
1178
    return 0;
1179
}
1180

1181
/* Record entry to a fast function or C function. */
1182
void lj_ffrecord_func(jit_State *J)
3,544✔
1183
{
1184
  RecordFFData rd;
3,544✔
1185
  uint32_t m = recdef_lookup(J->fn);
3,544✔
1186
  rd.data = m & 0xff;
3,544✔
1187
  rd.nres = 1;  /* Default is one result. */
3,544✔
1188
  rd.argv = J->L->base;
3,544✔
1189
  J->base[J->maxslot] = 0;  /* Mark end of arguments. */
3,544✔
1190
  (recff_func[m >> 8])(J, &rd);  /* Call recff_* handler. */
3,544✔
1191
  if (rd.nres >= 0) {
3,528✔
1192
    if (J->postproc == LJ_POST_NONE) J->postproc = LJ_POST_FFRETRY;
3,253✔
1193
    lj_record_ret(J, 0, rd.nres);
3,253✔
1194
  }
1195
}
3,520✔
1196

1197
#undef IR
1198
#undef emitir
1199

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