OSX/iOS: Fix SDK incompatibility.
[luajit-2.0.git] / src / vm_arm64.dasc
bloba6ce050789bc542f4fd1567ea1c388a4388377c4
1 |// Low-level VM code for ARM64 CPUs.
2 |// Bytecode interpreter, fast functions and helper functions.
3 |// Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
5 |.arch arm64
6 |.section code_op, code_sub
8 |.actionlist build_actionlist
9 |.globals GLOB_
10 |.globalnames globnames
11 |.externnames extnames
13 |// Note: The ragged indentation of the instructions is intentional.
14 |//       The starting columns indicate data dependencies.
16 |//-----------------------------------------------------------------------
18 |// ARM64 registers and the AAPCS64 ABI 1.0 at a glance:
19 |//
20 |// x0-x17 temp, x19-x28 callee-saved, x29 fp, x30 lr
21 |// x18 is reserved on most platforms. Don't use it, save it or restore it.
22 |// x31 doesn't exist. Register number 31 either means xzr/wzr (zero) or sp,
23 |// depending on the instruction.
24 |// v0-v7 temp, v8-v15 callee-saved (only d8-d15 preserved), v16-v31 temp
25 |//
26 |// x0-x7/v0-v7 hold parameters and results.
28 |// Fixed register assignments for the interpreter.
30 |// The following must be C callee-save.
31 |.define BASE,          x19     // Base of current Lua stack frame.
32 |.define KBASE,         x20     // Constants of current Lua function.
33 |.define PC,            x21     // Next PC.
34 |.define GLREG,         x22     // Global state.
35 |.define LREG,          x23     // Register holding lua_State (also in SAVE_L).
36 |.define TISNUM,        x24     // Constant LJ_TISNUM << 47.
37 |.define TISNUMhi,      x25     // Constant LJ_TISNUM << 15.
38 |.define TISNIL,        x26     // Constant -1LL.
39 |.define fp,            x29     // Yes, we have to maintain a frame pointer.
41 |.define ST_INTERP,     w26     // Constant -1.
43 |// The following temporaries are not saved across C calls, except for RA/RC.
44 |.define RA,            x27
45 |.define RC,            x28
46 |.define RB,            x17
47 |.define RAw,           w27
48 |.define RCw,           w28
49 |.define RBw,           w17
50 |.define INS,           x16
51 |.define INSw,          w16
52 |.define ITYPE,         x15
53 |.define TMP0,          x8
54 |.define TMP1,          x9
55 |.define TMP2,          x10
56 |.define TMP3,          x11
57 |.define TMP0w,         w8
58 |.define TMP1w,         w9
59 |.define TMP2w,         w10
60 |.define TMP3w,         w11
62 |// Calling conventions. Also used as temporaries.
63 |.define CARG1,         x0
64 |.define CARG2,         x1
65 |.define CARG3,         x2
66 |.define CARG4,         x3
67 |.define CARG5,         x4
68 |.define CARG1w,        w0
69 |.define CARG2w,        w1
70 |.define CARG3w,        w2
71 |.define CARG4w,        w3
72 |.define CARG5w,        w4
74 |.define FARG1,         d0
75 |.define FARG2,         d1
77 |.define CRET1,         x0
78 |.define CRET1w,        w0
80 |//-----------------------------------------------------------------------
82 |// ARM64e pointer authentication codes (PAC).
83 |.if PAUTH
84 |.macro sp_auth; pacibsp; .endmacro
85 |.macro br_auth, reg; braaz reg; .endmacro
86 |.macro blr_auth, reg; blraaz reg; .endmacro
87 |.macro ret_auth; retab; .endmacro
88 |.else
89 |.macro sp_auth; .endmacro
90 |.macro br_auth, reg; br reg; .endmacro
91 |.macro blr_auth, reg; blr reg; .endmacro
92 |.macro ret_auth; ret; .endmacro
93 |.endif
95 |//-----------------------------------------------------------------------
97 |// Stack layout while in interpreter. Must match with lj_frame.h.
99 |.define CFRAME_SPACE,  208
100 |//----- 16 byte aligned, <-- sp entering interpreter
101 |.define SAVE_FP_LR_,   192
102 |.define SAVE_GPR_,     112             // 112+10*8: 64 bit GPR saves
103 |.define SAVE_FPR_,     48              // 48+8*8: 64 bit FPR saves
104 |// Unused              [sp, #44]       // 32 bit values
105 |.define SAVE_NRES,     [sp, #40]
106 |.define SAVE_ERRF,     [sp, #36]
107 |.define SAVE_MULTRES,  [sp, #32]
108 |.define TMPD,          [sp, #24]       // 64 bit values
109 |.define SAVE_L,        [sp, #16]
110 |.define SAVE_PC,       [sp, #8]
111 |.define SAVE_CFRAME,   [sp, #0]
112 |//----- 16 byte aligned, <-- sp while in interpreter.
114 |.define TMPDofs,       #24
116 |.if WIN
117 |// Windows unwind data is suited to r1 stored first.
118 |.macro stp_unwind, r1, r2, where
119 |  stp r1, r2, where
120 |.endmacro
121 |.macro ldp_unwind, r1, r2, where
122 |  ldp r1, r2, where
123 |.endmacro
124 |.macro ldp_unwind, r1, r2, where, post_index
125 |  ldp r1, r2, where, post_index
126 |.endmacro
127 |.else
128 |// Otherwise store r2 first for compact unwind info (OSX).
129 |.macro stp_unwind, r1, r2, where
130 |  stp r2, r1, where
131 |.endmacro
132 |.macro ldp_unwind, r1, r2, where
133 |  ldp r2, r1, where
134 |.endmacro
135 |.macro ldp_unwind, r1, r2, where, post_index
136 |  ldp r2, r1, where, post_index
137 |.endmacro
138 |.endif
140 |.macro save_, gpr1, gpr2, fpr1, fpr2
141 |  stp_unwind d..fpr1, d..fpr2, [sp, # SAVE_FPR_+(14-fpr1)*8]
142 |  stp_unwind x..gpr1, x..gpr2, [sp, # SAVE_GPR_+(27-gpr1)*8]
143 |.endmacro
144 |.macro rest_, gpr1, gpr2, fpr1, fpr2
145 |  ldp_unwind d..fpr1, d..fpr2, [sp, # SAVE_FPR_+(14-fpr1)*8]
146 |  ldp_unwind x..gpr1, x..gpr2, [sp, # SAVE_GPR_+(27-gpr1)*8]
147 |.endmacro
149 |.macro saveregs
150 |  sp_auth
151 |  sub sp, sp, # CFRAME_SPACE
152 |  stp fp, lr, [sp, # SAVE_FP_LR_]
153 |  add fp, sp, # SAVE_FP_LR_
154 |  stp_unwind x19, x20, [sp, # SAVE_GPR_+(27-19)*8]
155 |  save_ 21, 22, 8, 9
156 |  save_ 23, 24, 10, 11
157 |  save_ 25, 26, 12, 13
158 |  save_ 27, 28, 14, 15
159 |.endmacro
160 |.macro restoreregs
161 |  ldp_unwind x19, x20, [sp, # SAVE_GPR_+(27-19)*8]
162 |  rest_ 21, 22, 8, 9
163 |  rest_ 23, 24, 10, 11
164 |  rest_ 25, 26, 12, 13
165 |  rest_ 27, 28, 14, 15
166 |  ldp fp, lr, [sp, # SAVE_FP_LR_]
167 |  add sp, sp, # CFRAME_SPACE
168 |.endmacro
170 |// Type definitions. Some of these are only used for documentation.
171 |.type L,               lua_State,      LREG
172 |.type GL,              global_State,   GLREG
173 |.type TVALUE,          TValue
174 |.type GCOBJ,           GCobj
175 |.type STR,             GCstr
176 |.type TAB,             GCtab
177 |.type LFUNC,           GCfuncL
178 |.type CFUNC,           GCfuncC
179 |.type PROTO,           GCproto
180 |.type UPVAL,           GCupval
181 |.type NODE,            Node
182 |.type NARGS8,          int
183 |.type TRACE,           GCtrace
184 |.type SBUF,            SBuf
186 |//-----------------------------------------------------------------------
188 |// Trap for not-yet-implemented parts.
189 |.macro NYI; brk; .endmacro
191 |//-----------------------------------------------------------------------
193 |// Access to frame relative to BASE.
194 |.define FRAME_FUNC,    #-16
195 |.define FRAME_PC,      #-8
197 |// Endian-specific defines.
198 |.if ENDIAN_LE
199 |.define LO,            0
200 |.define OFS_RD,        2
201 |.define OFS_RB,        3
202 |.define OFS_RA,        1
203 |.define OFS_OP,        0
204 |.else
205 |.define LO,            4
206 |.define OFS_RD,        0
207 |.define OFS_RB,        0
208 |.define OFS_RA,        2
209 |.define OFS_OP,        3
210 |.endif
212 |.macro decode_RA, dst, ins; ubfx dst, ins, #8, #8; .endmacro
213 |.macro decode_RB, dst, ins; ubfx dst, ins, #24, #8; .endmacro
214 |.macro decode_RC, dst, ins; ubfx dst, ins, #16, #8; .endmacro
215 |.macro decode_RD, dst, ins; ubfx dst, ins, #16, #16; .endmacro
216 |.macro decode_RC8RD, dst, src; ubfiz dst, src, #3, #8; .endmacro
218 |// Instruction decode+dispatch.
219 |.macro ins_NEXT
220 |  ldr INSw, [PC], #4
221 |  add TMP1, GL, INS, uxtb #3
222 |   decode_RA RA, INS
223 |  ldr TMP0, [TMP1, #GG_G2DISP]
224 |   decode_RD RC, INS
225 |  br_auth TMP0
226 |.endmacro
228 |// Instruction footer.
229 |.if 1
230 |  // Replicated dispatch. Less unpredictable branches, but higher I-Cache use.
231 |  .define ins_next, ins_NEXT
232 |  .define ins_next_, ins_NEXT
233 |.else
234 |  // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch.
235 |  // Affects only certain kinds of benchmarks (and only with -j off).
236 |  .macro ins_next
237 |    b ->ins_next
238 |  .endmacro
239 |  .macro ins_next_
240 |  ->ins_next:
241 |    ins_NEXT
242 |  .endmacro
243 |.endif
245 |// Call decode and dispatch.
246 |.macro ins_callt
247 |  // BASE = new base, CARG3 = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC
248 |  ldr PC, LFUNC:CARG3->pc
249 |  ldr INSw, [PC], #4
250 |  add TMP1, GL, INS, uxtb #3
251 |   decode_RA RA, INS
252 |  ldr TMP0, [TMP1, #GG_G2DISP]
253 |   add RA, BASE, RA, lsl #3
254 |  br_auth TMP0
255 |.endmacro
257 |.macro ins_call
258 |  // BASE = new base, CARG3 = LFUNC/CFUNC, RC = nargs*8, PC = caller PC
259 |  str PC, [BASE, FRAME_PC]
260 |  ins_callt
261 |.endmacro
263 |//-----------------------------------------------------------------------
265 |// Macros to check the TValue type and extract the GCobj. Branch on failure.
266 |.macro checktp, reg, tp, target
267 |  asr ITYPE, reg, #47
268 |  cmn ITYPE, #-tp
269 |   and reg, reg, #LJ_GCVMASK
270 |  bne target
271 |.endmacro
272 |.macro checktp, dst, reg, tp, target
273 |  asr ITYPE, reg, #47
274 |  cmn ITYPE, #-tp
275 |   and dst, reg, #LJ_GCVMASK
276 |  bne target
277 |.endmacro
278 |.macro checkstr, reg, target; checktp reg, LJ_TSTR, target; .endmacro
279 |.macro checktab, reg, target; checktp reg, LJ_TTAB, target; .endmacro
280 |.macro checkfunc, reg, target; checktp reg, LJ_TFUNC, target; .endmacro
281 |.macro checkint, reg, target
282 |  cmp TISNUMhi, reg, lsr #32
283 |  bne target
284 |.endmacro
285 |.macro checknum, reg, target
286 |  cmp TISNUMhi, reg, lsr #32
287 |  bls target
288 |.endmacro
289 |.macro checknumber, reg, target
290 |  cmp TISNUMhi, reg, lsr #32
291 |  blo target
292 |.endmacro
294 |.macro init_constants
295 |  movn TISNIL, #0
296 |  movz TISNUM, #(LJ_TISNUM>>1)&0xffff, lsl #48
297 |  movz TISNUMhi, #(LJ_TISNUM>>1)&0xffff, lsl #16
298 |.endmacro
300 |.macro mov_false, reg; movn reg, #0x8000, lsl #32; .endmacro
301 |.macro mov_true, reg; movn reg, #0x0001, lsl #48; .endmacro
302 |.macro mov_nil, reg; mov reg, TISNIL; .endmacro
303 |.macro cmp_nil, reg; cmp reg, TISNIL; .endmacro
304 |.macro add_TISNUM, dst, src; add dst, src, TISNUM; .endmacro
306 #define GL_J(field)     (GG_G2J + (int)offsetof(jit_State, field))
308 #define PC2PROTO(field)  ((int)offsetof(GCproto, field)-(int)sizeof(GCproto))
310 |.macro hotcheck, delta
311 |  lsr CARG1, PC, #1
312 |  and CARG1, CARG1, #126
313 |  add CARG1, CARG1, #GG_G2DISP+GG_DISP2HOT
314 |  ldrh CARG2w, [GL, CARG1]
315 |  subs CARG2, CARG2, #delta
316 |  strh CARG2w, [GL, CARG1]
317 |.endmacro
319 |.macro hotloop
320 |  hotcheck HOTCOUNT_LOOP
321 |  blo ->vm_hotloop
322 |.endmacro
324 |.macro hotcall
325 |  hotcheck HOTCOUNT_CALL
326 |  blo ->vm_hotcall
327 |.endmacro
329 |// Set current VM state.
330 |.macro mv_vmstate, reg, st; movn reg, #LJ_VMST_..st; .endmacro
331 |.macro st_vmstate, reg; str reg, GL->vmstate; .endmacro
333 |// Move table write barrier back. Overwrites mark and tmp.
334 |.macro barrierback, tab, mark, tmp
335 |  ldr tmp, GL->gc.grayagain
336 |   and mark, mark, #~LJ_GC_BLACK       // black2gray(tab)
337 |  str tab, GL->gc.grayagain
338 |   strb mark, tab->marked
339 |  str tmp, tab->gclist
340 |.endmacro
342 |//-----------------------------------------------------------------------
344 #if !LJ_DUALNUM
345 #error "Only dual-number mode supported for ARM64 target"
346 #endif
348 /* Generate subroutines used by opcodes and other parts of the VM. */
349 /* The .code_sub section should be last to help static branch prediction. */
350 static void build_subroutines(BuildCtx *ctx)
352   |.code_sub
353   |
354   |//-----------------------------------------------------------------------
355   |//-- Return handling ----------------------------------------------------
356   |//-----------------------------------------------------------------------
357   |
358   |->vm_returnp:
359   |  // See vm_return. Also: RB = previous base.
360   |  tbz PC, #2, ->cont_dispatch        // (PC & FRAME_P) == 0?
361   |
362   |  // Return from pcall or xpcall fast func.
363   |  ldr PC, [RB, FRAME_PC]             // Fetch PC of previous frame.
364   |   mov_true TMP0
365   |  mov BASE, RB
366   |  // Prepending may overwrite the pcall frame, so do it at the end.
367   |   str TMP0, [RA, #-8]!              // Prepend true to results.
368   |
369   |->vm_returnc:
370   |  adds RC, RC, #8                    // RC = (nresults+1)*8.
371   |  mov CRET1, #LUA_YIELD
372   |  beq ->vm_unwind_c_eh
373   |  str RCw, SAVE_MULTRES
374   |  ands CARG1, PC, #FRAME_TYPE
375   |  beq ->BC_RET_Z                     // Handle regular return to Lua.
376   |
377   |->vm_return:
378   |  // BASE = base, RA = resultptr, RC/MULTRES = (nresults+1)*8, PC = return
379   |  // CARG1 = PC & FRAME_TYPE
380   |  and RB, PC, #~FRAME_TYPEP
381   |   cmp CARG1, #FRAME_C
382   |  sub RB, BASE, RB                   // RB = previous base.
383   |   bne ->vm_returnp
384   |
385   |  str RB, L->base
386   |   ldrsw CARG2, SAVE_NRES            // CARG2 = nresults+1.
387   |    mv_vmstate TMP0w, C
388   |   sub BASE, BASE, #16
389   |  subs TMP2, RC, #8
390   |    st_vmstate TMP0w
391   |  beq >2
392   |1:
393   |  subs TMP2, TMP2, #8
394   |   ldr TMP0, [RA], #8
395   |   str TMP0, [BASE], #8
396   |  bne <1
397   |2:
398   |  cmp RC, CARG2, lsl #3              // More/less results wanted?
399   |  bne >6
400   |3:
401   |  str BASE, L->top                   // Store new top.
402   |
403   |->vm_leave_cp:
404   |  ldr RC, SAVE_CFRAME                // Restore previous C frame.
405   |   mov CRET1, #0                     // Ok return status for vm_pcall.
406   |  str RC, L->cframe
407   |
408   |->vm_leave_unw:
409   |  restoreregs
410   |  ret_auth
411   |
412   |6:
413   |  bgt >7                             // Less results wanted?
414   |  // More results wanted. Check stack size and fill up results with nil.
415   |  ldr CARG3, L->maxstack
416   |  cmp BASE, CARG3
417   |  bhs >8
418   |   str TISNIL, [BASE], #8
419   |  add RC, RC, #8
420   |  b <2
421   |
422   |7:  // Less results wanted.
423   |  cbz CARG2, <3                      // LUA_MULTRET+1 case?
424   |  sub CARG1, RC, CARG2, lsl #3
425   |  sub BASE, BASE, CARG1              // Shrink top.
426   |  b <3
427   |
428   |8:  // Corner case: need to grow stack for filling up results.
429   |  // This can happen if:
430   |  // - A C function grows the stack (a lot).
431   |  // - The GC shrinks the stack in between.
432   |  // - A return back from a lua_call() with (high) nresults adjustment.
433   |  str BASE, L->top                   // Save current top held in BASE (yes).
434   |  mov CARG1, L
435   |  bl extern lj_state_growstack       // (lua_State *L, int n)
436   |  ldr BASE, L->top                   // Need the (realloced) L->top in BASE.
437   |  ldrsw CARG2, SAVE_NRES
438   |  b <2
439   |
440   |->vm_unwind_c:                       // Unwind C stack, return from vm_pcall.
441   |  // (void *cframe, int errcode)
442   |  add fp, CARG1, # SAVE_FP_LR_
443   |  mov sp, CARG1
444   |  mov CRET1, CARG2
445   |  ldr L, SAVE_L
446   |  ldr GL, L->glref
447   |->vm_unwind_c_eh:                    // Landing pad for external unwinder.
448   |   mv_vmstate TMP0w, C
449   |   st_vmstate TMP0w
450   |  b ->vm_leave_unw
451   |
452   |->vm_unwind_ff:                      // Unwind C stack, return from ff pcall.
453   |  // (void *cframe)
454   |  add fp, CARG1, # SAVE_FP_LR_
455   |  mov sp, CARG1
456   |  ldr L, SAVE_L
457   |    init_constants
458   |   ldr GL, L->glref                  // Setup pointer to global state.
459   |->vm_unwind_ff_eh:                   // Landing pad for external unwinder.
460   |    mov RC, #16                      // 2 results: false + error message.
461   |  ldr BASE, L->base
462   |    mov_false TMP0
463   |  sub RA, BASE, #8                   // Results start at BASE-8.
464   |  ldr PC, [BASE, FRAME_PC]           // Fetch PC of previous frame.
465   |    str TMP0, [BASE, #-8]            // Prepend false to error message.
466   |   st_vmstate ST_INTERP
467   |  b ->vm_returnc
468   |
469   |//-----------------------------------------------------------------------
470   |//-- Grow stack for calls -----------------------------------------------
471   |//-----------------------------------------------------------------------
472   |
473   |->vm_growstack_c:                    // Grow stack for C function.
474   |  // CARG1 = L
475   |  mov CARG2, #LUA_MINSTACK
476   |  b >2
477   |
478   |->vm_growstack_l:                    // Grow stack for Lua function.
479   |  // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC
480   |  add RC, BASE, RC
481   |   sub RA, RA, BASE
482   |    mov CARG1, L
483   |  stp BASE, RC, L->base
484   |   add PC, PC, #4                    // Must point after first instruction.
485   |   lsr CARG2, RA, #3
486   |2:
487   |  // L->base = new base, L->top = top
488   |  str PC, SAVE_PC
489   |  bl extern lj_state_growstack       // (lua_State *L, int n)
490   |  ldp BASE, RC, L->base
491   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
492   |   sub NARGS8:RC, RC, BASE
493   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
494   |  // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC
495   |  ins_callt                          // Just retry the call.
496   |
497   |//-----------------------------------------------------------------------
498   |//-- Entry points into the assembler VM ---------------------------------
499   |//-----------------------------------------------------------------------
500   |
501   |->vm_resume:                         // Setup C frame and resume thread.
502   |  // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0)
503   |  saveregs
504   |  mov L, CARG1
505   |    ldr GL, L->glref                 // Setup pointer to global state.
506   |  mov BASE, CARG2
507   |   str L, SAVE_L
508   |  mov PC, #FRAME_CP
509   |   str wzr, SAVE_NRES
510   |    add TMP0, sp, #CFRAME_RESUME
511   |  ldrb TMP1w, L->status
512   |   str wzr, SAVE_ERRF
513   |   str L, SAVE_PC                    // Any value outside of bytecode is ok.
514   |   str xzr, SAVE_CFRAME
515   |    str TMP0, L->cframe
516   |  cbz TMP1w, >3
517   |
518   |  // Resume after yield (like a return).
519   |  str L, GL->cur_L
520   |  mov RA, BASE
521   |   ldp BASE, CARG1, L->base
522   |    init_constants
523   |  ldr PC, [BASE, FRAME_PC]
524   |     strb wzr, L->status
525   |   sub RC, CARG1, BASE
526   |  ands CARG1, PC, #FRAME_TYPE
527   |   add RC, RC, #8
528   |     st_vmstate ST_INTERP
529   |   str RCw, SAVE_MULTRES
530   |  beq ->BC_RET_Z
531   |  b ->vm_return
532   |
533   |->vm_pcall:                          // Setup protected C frame and enter VM.
534   |  // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef)
535   |  saveregs
536   |  mov PC, #FRAME_CP
537   |  str CARG4w, SAVE_ERRF
538   |  b >1
539   |
540   |->vm_call:                           // Setup C frame and enter VM.
541   |  // (lua_State *L, TValue *base, int nres1)
542   |  saveregs
543   |  mov PC, #FRAME_C
544   |
545   |1:  // Entry point for vm_pcall above (PC = ftype).
546   |  ldr RC, L:CARG1->cframe
547   |   str CARG3w, SAVE_NRES
548   |    mov L, CARG1
549   |   str CARG1, SAVE_L
550   |    ldr GL, L->glref                 // Setup pointer to global state.
551   |     mov BASE, CARG2
552   |   str CARG1, SAVE_PC                // Any value outside of bytecode is ok.
553   |  add TMP0, sp, #0
554   |   str RC, SAVE_CFRAME
555   |  str TMP0, L->cframe                // Add our C frame to cframe chain.
556   |
557   |3:  // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype).
558   |  str L, GL->cur_L
559   |  ldp RB, CARG1, L->base             // RB = old base (for vmeta_call).
560   |  add PC, PC, BASE
561   |    init_constants
562   |  sub PC, PC, RB                     // PC = frame delta + frame type
563   |   sub NARGS8:RC, CARG1, BASE
564   |    st_vmstate ST_INTERP
565   |
566   |->vm_call_dispatch:
567   |  // RB = old base, BASE = new base, RC = nargs*8, PC = caller PC
568   |  ldr CARG3, [BASE, FRAME_FUNC]
569   |  checkfunc CARG3, ->vmeta_call
570   |
571   |->vm_call_dispatch_f:
572   |  ins_call
573   |  // BASE = new base, CARG3 = func, RC = nargs*8, PC = caller PC
574   |
575   |->vm_cpcall:                         // Setup protected C frame, call C.
576   |  // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp)
577   |  saveregs
578   |  mov L, CARG1
579   |   ldr RA, L:CARG1->stack
580   |  str CARG1, SAVE_L
581   |    ldr GL, L->glref                 // Setup pointer to global state.
582   |   ldr RB, L->top
583   |  str CARG1, SAVE_PC                 // Any value outside of bytecode is ok.
584   |  ldr RC, L->cframe
585   |   sub RA, RA, RB                    // Compute -savestack(L, L->top).
586   |   str RAw, SAVE_NRES                // Neg. delta means cframe w/o frame.
587   |  str wzr, SAVE_ERRF                 // No error function.
588   |  add TMP0, sp, #0
589   |   str RC, SAVE_CFRAME
590   |  str TMP0, L->cframe                // Add our C frame to cframe chain.
591   |    str L, GL->cur_L
592   |  blr_auth CARG4             // (lua_State *L, lua_CFunction func, void *ud)
593   |  mov BASE, CRET1
594   |   mov PC, #FRAME_CP
595   |  cbnz BASE, <3                      // Else continue with the call.
596   |  b ->vm_leave_cp                    // No base? Just remove C frame.
597   |
598   |//-----------------------------------------------------------------------
599   |//-- Metamethod handling ------------------------------------------------
600   |//-----------------------------------------------------------------------
601   |
602   |//-- Continuation dispatch ----------------------------------------------
603   |
604   |->cont_dispatch:
605   |  // BASE = meta base, RA = resultptr, RC = (nresults+1)*8
606   |  ldr LFUNC:CARG3, [RB, FRAME_FUNC]
607   |    ldr CARG1, [BASE, #-32]          // Get continuation.
608   |   mov CARG4, BASE
609   |   mov BASE, RB                      // Restore caller BASE.
610   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
611   |.if FFI
612   |    cmp CARG1, #1
613   |.endif
614   |   ldr PC, [CARG4, #-24]             // Restore PC from [cont|PC].
615   |    add TMP0, RA, RC
616   |    str TISNIL, [TMP0, #-8]          // Ensure one valid arg.
617   |.if FFI
618   |    bls >1
619   |.endif
620   |  ldr CARG3, LFUNC:CARG3->pc
621   |  ldr KBASE, [CARG3, #PC2PROTO(k)]
622   |  // BASE = base, RA = resultptr, CARG4 = meta base
623   |    br_auth CARG1
624   |
625   |.if FFI
626   |1:
627   |  beq ->cont_ffi_callback            // cont = 1: return from FFI callback.
628   |  // cont = 0: tailcall from C function.
629   |   sub CARG4, CARG4, #32
630   |   sub RC, CARG4, BASE
631   |  b ->vm_call_tail
632   |.endif
633   |
634   |->cont_cat:                          // RA = resultptr, CARG4 = meta base
635   |  ldr INSw, [PC, #-4]
636   |   sub CARG2, CARG4, #32
637   |   ldr TMP0, [RA]
638   |     str BASE, L->base
639   |  decode_RB RB, INS
640   |   decode_RA RA, INS
641   |  add TMP1, BASE, RB, lsl #3
642   |  subs TMP1, CARG2, TMP1
643   |  beq >1
644   |   str TMP0, [CARG2]
645   |  lsr CARG3, TMP1, #3
646   |  b ->BC_CAT_Z
647   |
648   |1:
649   |   str TMP0, [BASE, RA, lsl #3]
650   |  b ->cont_nop
651   |
652   |//-- Table indexing metamethods -----------------------------------------
653   |
654   |->vmeta_tgets1:
655   |  movn CARG4, #~LJ_TSTR
656   |   add CARG2, BASE, RB, lsl #3
657   |  add CARG4, STR:RC, CARG4, lsl #47
658   |  b >2
659   |
660   |->vmeta_tgets:
661   |  movk CARG2, #(LJ_TTAB>>1)&0xffff, lsl #48
662   |  str CARG2, GL->tmptv
663   |  add CARG2, GL, #offsetof(global_State, tmptv)
664   |2:
665   |   add CARG3, sp, TMPDofs
666   |  str CARG4, TMPD
667   |  b >1
668   |
669   |->vmeta_tgetb:                       // RB = table, RC = index
670   |  add_TISNUM RC, RC
671   |   add CARG2, BASE, RB, lsl #3
672   |   add CARG3, sp, TMPDofs
673   |  str RC, TMPD
674   |  b >1
675   |
676   |->vmeta_tgetv:                       // RB = table, RC = key
677   |  add CARG2, BASE, RB, lsl #3
678   |   add CARG3, BASE, RC, lsl #3
679   |1:
680   |   str BASE, L->base
681   |  mov CARG1, L
682   |   str PC, SAVE_PC
683   |  bl extern lj_meta_tget             // (lua_State *L, TValue *o, TValue *k)
684   |  // Returns TValue * (finished) or NULL (metamethod).
685   |  cbz CRET1, >3
686   |  ldr TMP0, [CRET1]
687   |  str TMP0, [BASE, RA, lsl #3]
688   |  ins_next
689   |
690   |3:  // Call __index metamethod.
691   |  // BASE = base, L->top = new base, stack = cont/func/t/k
692   |   sub TMP1, BASE, #FRAME_CONT
693   |  ldr BASE, L->top
694   |    mov NARGS8:RC, #16               // 2 args for func(t, k).
695   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Guaranteed to be a function here.
696   |    str PC, [BASE, #-24]             // [cont|PC]
697   |   sub PC, BASE, TMP1
698   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
699   |  b ->vm_call_dispatch_f
700   |
701   |->vmeta_tgetr:
702   |  sxtw CARG2, TMP1w
703   |  bl extern lj_tab_getinth           // (GCtab *t, int32_t key)
704   |  // Returns cTValue * or NULL.
705   |  mov_nil TMP0
706   |  cbz CRET1, ->BC_TGETR_Z
707   |  ldr TMP0, [CRET1]
708   |  b ->BC_TGETR_Z
709   |
710   |//-----------------------------------------------------------------------
711   |
712   |->vmeta_tsets1:
713   |  movn CARG4, #~LJ_TSTR
714   |   add CARG2, BASE, RB, lsl #3
715   |  add CARG4, STR:RC, CARG4, lsl #47
716   |  b >2
717   |
718   |->vmeta_tsets:
719   |  movk CARG2, #(LJ_TTAB>>1)&0xffff, lsl #48
720   |  str CARG2, GL->tmptv
721   |  add CARG2, GL, #offsetof(global_State, tmptv)
722   |2:
723   |   add CARG3, sp, TMPDofs
724   |  str CARG4, TMPD
725   |  b >1
726   |
727   |->vmeta_tsetb:                       // RB = table, RC = index
728   |  add_TISNUM RC, RC
729   |   add CARG2, BASE, RB, lsl #3
730   |   add CARG3, sp, TMPDofs
731   |  str RC, TMPD
732   |  b >1
733   |
734   |->vmeta_tsetv:
735   |  add CARG2, BASE, RB, lsl #3
736   |   add CARG3, BASE, RC, lsl #3
737   |1:
738   |   str BASE, L->base
739   |  mov CARG1, L
740   |   str PC, SAVE_PC
741   |  bl extern lj_meta_tset             // (lua_State *L, TValue *o, TValue *k)
742   |  // Returns TValue * (finished) or NULL (metamethod).
743   |   ldr TMP0, [BASE, RA, lsl #3]
744   |  cbz CRET1, >3
745   |  // NOBARRIER: lj_meta_tset ensures the table is not black.
746   |   str TMP0, [CRET1]
747   |  ins_next
748   |
749   |3:  // Call __newindex metamethod.
750   |  // BASE = base, L->top = new base, stack = cont/func/t/k/(v)
751   |   sub TMP1, BASE, #FRAME_CONT
752   |  ldr BASE, L->top
753   |    mov NARGS8:RC, #24               // 3 args for func(t, k, v).
754   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Guaranteed to be a function here.
755   |   str TMP0, [BASE, #16]             // Copy value to third argument.
756   |    str PC, [BASE, #-24]             // [cont|PC]
757   |   sub PC, BASE, TMP1
758   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
759   |  b ->vm_call_dispatch_f
760   |
761   |->vmeta_tsetr:
762   |  sxtw CARG3, TMP1w
763   |  str BASE, L->base
764   |  mov CARG1, L
765   |  str PC, SAVE_PC
766   |  bl extern lj_tab_setinth  // (lua_State *L, GCtab *t, int32_t key)
767   |  // Returns TValue *.
768   |  b ->BC_TSETR_Z
769   |
770   |//-- Comparison metamethods ---------------------------------------------
771   |
772   |->vmeta_comp:
773   |  add CARG2, BASE, RA, lsl #3
774   |   sub PC, PC, #4
775   |  add CARG3, BASE, RC, lsl #3
776   |   str BASE, L->base
777   |  mov CARG1, L
778   |   str PC, SAVE_PC
779   |  uxtb CARG4w, INSw
780   |  bl extern lj_meta_comp  // (lua_State *L, TValue *o1, *o2, int op)
781   |  // Returns 0/1 or TValue * (metamethod).
782   |3:
783   |  cmp CRET1, #1
784   |  bhi ->vmeta_binop
785   |4:
786   |   ldrh RBw, [PC, # OFS_RD]
787   |    add PC, PC, #4
788   |   add RB, PC, RB, lsl #2
789   |   sub RB, RB, #0x20000
790   |  csel PC, PC, RB, lo
791   |->cont_nop:
792   |  ins_next
793   |
794   |->cont_ra:                           // RA = resultptr
795   |  ldr INSw, [PC, #-4]
796   |   ldr TMP0, [RA]
797   |  decode_RA TMP1, INS
798   |   str TMP0, [BASE, TMP1, lsl #3]
799   |  b ->cont_nop
800   |
801   |->cont_condt:                        // RA = resultptr
802   |  ldr TMP0, [RA]
803   |   mov_true TMP1
804   |  cmp TMP1, TMP0                     // Branch if result is true.
805   |  b <4
806   |
807   |->cont_condf:                        // RA = resultptr
808   |  ldr TMP0, [RA]
809   |   mov_false TMP1
810   |  cmp TMP0, TMP1                     // Branch if result is false.
811   |  b <4
812   |
813   |->vmeta_equal:
814   |  // CARG2, CARG3, CARG4 are already set by BC_ISEQV/BC_ISNEV.
815   |  and TAB:CARG3, CARG3, #LJ_GCVMASK
816   |  sub PC, PC, #4
817   |   str BASE, L->base
818   |   mov CARG1, L
819   |  str PC, SAVE_PC
820   |  bl extern lj_meta_equal  // (lua_State *L, GCobj *o1, *o2, int ne)
821   |  // Returns 0/1 or TValue * (metamethod).
822   |  b <3
823   |
824   |->vmeta_equal_cd:
825   |.if FFI
826   |  sub PC, PC, #4
827   |   str BASE, L->base
828   |   mov CARG1, L
829   |   mov CARG2, INS
830   |  str PC, SAVE_PC
831   |  bl extern lj_meta_equal_cd         // (lua_State *L, BCIns op)
832   |  // Returns 0/1 or TValue * (metamethod).
833   |  b <3
834   |.endif
835   |
836   |->vmeta_istype:
837   |  sub PC, PC, #4
838   |   str BASE, L->base
839   |   mov CARG1, L
840   |   mov CARG2, RA
841   |   mov CARG3, RC
842   |  str PC, SAVE_PC
843   |  bl extern lj_meta_istype  // (lua_State *L, BCReg ra, BCReg tp)
844   |  b ->cont_nop
845   |
846   |//-- Arithmetic metamethods ---------------------------------------------
847   |
848   |->vmeta_arith_vn:
849   |  add CARG3, BASE, RB, lsl #3
850   |   add CARG4, KBASE, RC, lsl #3
851   |  b >1
852   |
853   |->vmeta_arith_nv:
854   |  add CARG4, BASE, RB, lsl #3
855   |   add CARG3, KBASE, RC, lsl #3
856   |  b >1
857   |
858   |->vmeta_unm:
859   |  add CARG3, BASE, RC, lsl #3
860   |  mov CARG4, CARG3
861   |  b >1
862   |
863   |->vmeta_arith_vv:
864   |  add CARG3, BASE, RB, lsl #3
865   |   add CARG4, BASE, RC, lsl #3
866   |1:
867   |  uxtb CARG5w, INSw
868   |   add CARG2, BASE, RA, lsl #3
869   |    str BASE, L->base
870   |   mov CARG1, L
871   |    str PC, SAVE_PC
872   |  bl extern lj_meta_arith  // (lua_State *L, TValue *ra,*rb,*rc, BCReg op)
873   |  // Returns NULL (finished) or TValue * (metamethod).
874   |  cbz CRET1, ->cont_nop
875   |
876   |  // Call metamethod for binary op.
877   |->vmeta_binop:
878   |  // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2
879   |  sub TMP1, CRET1, BASE
880   |   str PC, [CRET1, #-24]             // [cont|PC]
881   |  add PC, TMP1, #FRAME_CONT
882   |  mov BASE, CRET1
883   |   mov NARGS8:RC, #16                // 2 args for func(o1, o2).
884   |  b ->vm_call_dispatch
885   |
886   |->vmeta_len:
887   |  add CARG2, BASE, RC, lsl #3
888 #if LJ_52
889   |  mov TAB:RC, TAB:CARG1              // Save table (ignored for other types).
890 #endif
891   |   str BASE, L->base
892   |  mov CARG1, L
893   |   str PC, SAVE_PC
894   |  bl extern lj_meta_len              // (lua_State *L, TValue *o)
895   |  // Returns NULL (retry) or TValue * (metamethod base).
896 #if LJ_52
897   |  cbnz CRET1, ->vmeta_binop          // Binop call for compatibility.
898   |  mov TAB:CARG1, TAB:RC
899   |  b ->BC_LEN_Z
900 #else
901   |  b ->vmeta_binop                    // Binop call for compatibility.
902 #endif
903   |
904   |//-- Call metamethod ----------------------------------------------------
905   |
906   |->vmeta_call:                        // Resolve and call __call metamethod.
907   |  // RB = old base, BASE = new base, RC = nargs*8
908   |  mov CARG1, L
909   |   str RB, L->base                   // This is the callers base!
910   |  sub CARG2, BASE, #16
911   |   str PC, SAVE_PC
912   |  add CARG3, BASE, NARGS8:RC
913   |  bl extern lj_meta_call     // (lua_State *L, TValue *func, TValue *top)
914   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Guaranteed to be a function here.
915   |   add NARGS8:RC, NARGS8:RC, #8      // Got one more argument now.
916   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
917   |  ins_call
918   |
919   |->vmeta_callt:                       // Resolve __call for BC_CALLT.
920   |  // BASE = old base, RA = new base, RC = nargs*8
921   |  mov CARG1, L
922   |   str BASE, L->base
923   |  sub CARG2, RA, #16
924   |   str PC, SAVE_PC
925   |  add CARG3, RA, NARGS8:RC
926   |  bl extern lj_meta_call     // (lua_State *L, TValue *func, TValue *top)
927   |  ldr TMP1, [RA, FRAME_FUNC]         // Guaranteed to be a function here.
928   |   ldr PC, [BASE, FRAME_PC]
929   |   add NARGS8:RC, NARGS8:RC, #8      // Got one more argument now.
930   |  and LFUNC:CARG3, TMP1, #LJ_GCVMASK
931   |  b ->BC_CALLT2_Z
932   |
933   |//-- Argument coercion for 'for' statement ------------------------------
934   |
935   |->vmeta_for:
936   |  mov CARG1, L
937   |   str BASE, L->base
938   |  mov CARG2, RA
939   |   str PC, SAVE_PC
940   |  bl extern lj_meta_for      // (lua_State *L, TValue *base)
941   |  ldr INSw, [PC, #-4]
942   |.if JIT
943   |   uxtb TMP0w, INSw
944   |.endif
945   |  decode_RA RA, INS
946   |  decode_RD RC, INS
947   |.if JIT
948   |   cmp TMP0, #BC_JFORI
949   |   beq =>BC_JFORI
950   |.endif
951   |  b =>BC_FORI
952   |
953   |//-----------------------------------------------------------------------
954   |//-- Fast functions -----------------------------------------------------
955   |//-----------------------------------------------------------------------
956   |
957   |.macro .ffunc, name
958   |->ff_ .. name:
959   |.endmacro
960   |
961   |.macro .ffunc_1, name
962   |->ff_ .. name:
963   |  ldr CARG1, [BASE]
964   |   cmp NARGS8:RC, #8
965   |   blo ->fff_fallback
966   |.endmacro
967   |
968   |.macro .ffunc_2, name
969   |->ff_ .. name:
970   |  ldp CARG1, CARG2, [BASE]
971   |   cmp NARGS8:RC, #16
972   |   blo ->fff_fallback
973   |.endmacro
974   |
975   |.macro .ffunc_n, name
976   |  .ffunc name
977   |  ldr CARG1, [BASE]
978   |   cmp NARGS8:RC, #8
979   |  ldr FARG1, [BASE]
980   |   blo ->fff_fallback
981   |  checknum CARG1, ->fff_fallback
982   |.endmacro
983   |
984   |.macro .ffunc_nn, name
985   |  .ffunc name
986   |  ldp CARG1, CARG2, [BASE]
987   |   cmp NARGS8:RC, #16
988   |  ldp FARG1, FARG2, [BASE]
989   |   blo ->fff_fallback
990   |  checknum CARG1, ->fff_fallback
991   |  checknum CARG2, ->fff_fallback
992   |.endmacro
993   |
994   |// Inlined GC threshold check. Caveat: uses CARG1 and CARG2.
995   |.macro ffgccheck
996   |  ldp CARG1, CARG2, GL->gc.total     // Assumes threshold follows total.
997   |  cmp CARG1, CARG2
998   |  blt >1
999   |  bl ->fff_gcstep
1000   |1:
1001   |.endmacro
1002   |
1003   |//-- Base library: checks -----------------------------------------------
1004   |
1005   |.ffunc_1 assert
1006   |   ldr PC, [BASE, FRAME_PC]
1007   |  mov_false TMP1
1008   |  cmp CARG1, TMP1
1009   |  bhs ->fff_fallback
1010   |  str CARG1, [BASE, #-16]
1011   |  sub RB, BASE, #8
1012   |  subs RA, NARGS8:RC, #8
1013   |   add RC, NARGS8:RC, #8             // Compute (nresults+1)*8.
1014   |  cbz RA, ->fff_res                  // Done if exactly 1 argument.
1015   |1:
1016   |   ldr CARG1, [RB, #16]
1017   |  sub RA, RA, #8
1018   |   str CARG1, [RB], #8
1019   |  cbnz RA, <1
1020   |  b ->fff_res
1021   |
1022   |.ffunc_1 type
1023   |  mov TMP0, #~LJ_TISNUM
1024   |  asr ITYPE, CARG1, #47
1025   |  cmn ITYPE, #~LJ_TISNUM
1026   |  csinv TMP1, TMP0, ITYPE, lo
1027   |  add TMP1, TMP1, #offsetof(GCfuncC, upvalue)/8
1028   |  ldr CARG1, [CFUNC:CARG3, TMP1, lsl #3]
1029   |  b ->fff_restv
1030   |
1031   |//-- Base library: getters and setters ---------------------------------
1032   |
1033   |.ffunc_1 getmetatable
1034   |  asr ITYPE, CARG1, #47
1035   |  cmn ITYPE, #-LJ_TTAB
1036   |  ccmn ITYPE, #-LJ_TUDATA, #4, ne
1037   |   and TAB:CARG1, CARG1, #LJ_GCVMASK
1038   |  bne >6
1039   |1:  // Field metatable must be at same offset for GCtab and GCudata!
1040   |  ldr TAB:RB, TAB:CARG1->metatable
1041   |2:
1042   |   mov_nil CARG1
1043   |   ldr STR:RC, GL->gcroot[GCROOT_MMNAME+MM_metatable]
1044   |  cbz TAB:RB, ->fff_restv
1045   |  ldr TMP1w, TAB:RB->hmask
1046   |   ldr TMP2w, STR:RC->sid
1047   |    ldr NODE:CARG3, TAB:RB->node
1048   |  and TMP1w, TMP1w, TMP2w            // idx = str->sid & tab->hmask
1049   |  add TMP1, TMP1, TMP1, lsl #1
1050   |  movn CARG4, #~LJ_TSTR
1051   |    add NODE:CARG3, NODE:CARG3, TMP1, lsl #3  // node = tab->node + idx*3*8
1052   |  add CARG4, STR:RC, CARG4, lsl #47  // Tagged key to look for.
1053   |3:  // Rearranged logic, because we expect _not_ to find the key.
1054   |  ldp CARG1, TMP0, NODE:CARG3->val
1055   |   ldr NODE:CARG3, NODE:CARG3->next
1056   |  cmp TMP0, CARG4
1057   |  beq >5
1058   |  cbnz NODE:CARG3, <3
1059   |4:
1060   |  mov CARG1, RB                      // Use metatable as default result.
1061   |  movk CARG1, #(LJ_TTAB>>1)&0xffff, lsl #48
1062   |  b ->fff_restv
1063   |5:
1064   |  cmp_nil TMP0
1065   |  bne ->fff_restv
1066   |  b <4
1067   |
1068   |6:
1069   |  movn TMP0, #~LJ_TISNUM
1070   |  cmp ITYPE, TMP0
1071   |  csel ITYPE, ITYPE, TMP0, hs
1072   |  sub TMP1, GL, ITYPE, lsl #3
1073   |  ldr TAB:RB, [TMP1, #offsetof(global_State, gcroot[GCROOT_BASEMT])-8]
1074   |  b <2
1075   |
1076   |.ffunc_2 setmetatable
1077   |  // Fast path: no mt for table yet and not clearing the mt.
1078   |  checktp TMP1, CARG1, LJ_TTAB, ->fff_fallback
1079   |   ldr TAB:TMP0, TAB:TMP1->metatable
1080   |  asr ITYPE, CARG2, #47
1081   |   ldrb TMP2w, TAB:TMP1->marked
1082   |  cmn ITYPE, #-LJ_TTAB
1083   |    and TAB:CARG2, CARG2, #LJ_GCVMASK
1084   |  ccmp TAB:TMP0, #0, #0, eq
1085   |  bne ->fff_fallback
1086   |    str TAB:CARG2, TAB:TMP1->metatable
1087   |   tbz TMP2w, #2, ->fff_restv        // isblack(table)
1088   |  barrierback TAB:TMP1, TMP2w, TMP0
1089   |  b ->fff_restv
1090   |
1091   |.ffunc rawget
1092   |  ldr CARG2, [BASE]
1093   |   cmp NARGS8:RC, #16
1094   |   blo ->fff_fallback
1095   |  checktab CARG2, ->fff_fallback
1096   |   mov CARG1, L
1097   |   add CARG3, BASE, #8
1098   |  bl extern lj_tab_get  // (lua_State *L, GCtab *t, cTValue *key)
1099   |  // Returns cTValue *.
1100   |  ldr CARG1, [CRET1]
1101   |  b ->fff_restv
1102   |
1103   |//-- Base library: conversions ------------------------------------------
1104   |
1105   |.ffunc tonumber
1106   |  // Only handles the number case inline (without a base argument).
1107   |  ldr CARG1, [BASE]
1108   |   cmp NARGS8:RC, #8
1109   |   bne ->fff_fallback
1110   |  checknumber CARG1, ->fff_fallback
1111   |  b ->fff_restv
1112   |
1113   |.ffunc_1 tostring
1114   |  // Only handles the string or number case inline.
1115   |  asr ITYPE, CARG1, #47
1116   |  cmn ITYPE, #-LJ_TSTR
1117   |  // A __tostring method in the string base metatable is ignored.
1118   |  beq ->fff_restv
1119   |  // Handle numbers inline, unless a number base metatable is present.
1120   |  ldr TMP1, GL->gcroot[GCROOT_BASEMT_NUM]
1121   |   str BASE, L->base
1122   |  cmn ITYPE, #-LJ_TISNUM
1123   |  ccmp TMP1, #0, #0, ls
1124   |   str PC, SAVE_PC                   // Redundant (but a defined value).
1125   |  bne ->fff_fallback
1126   |  ffgccheck
1127   |  mov CARG1, L
1128   |  mov CARG2, BASE
1129   |  bl extern lj_strfmt_number         // (lua_State *L, cTValue *o)
1130   |  // Returns GCstr *.
1131   |   movn TMP1, #~LJ_TSTR
1132   |  ldr BASE, L->base
1133   |   add CARG1, CARG1, TMP1, lsl #47
1134   |  b ->fff_restv
1135   |
1136   |//-- Base library: iterators -------------------------------------------
1137   |
1138   |.ffunc_1 next
1139   |  checktp CARG1, LJ_TTAB, ->fff_fallback
1140   |  str TISNIL, [BASE, NARGS8:RC]      // Set missing 2nd arg to nil.
1141   |  ldr PC, [BASE, FRAME_PC]
1142   |  add CARG2, BASE, #8
1143   |  sub CARG3, BASE, #16
1144   |  bl extern lj_tab_next              // (GCtab *t, cTValue *key, TValue *o)
1145   |  // Returns 1=found, 0=end, -1=error.
1146   |   mov RC, #(2+1)*8
1147   |  tbnz CRET1w, #31, ->fff_fallback   // Invalid key.
1148   |  cbnz CRET1, ->fff_res              // Found key/value.
1149   |  // End of traversal: return nil.
1150   |  str TISNIL, [BASE, #-16]
1151   |  b ->fff_res1
1152   |
1153   |.ffunc_1 pairs
1154   |  checktp TMP1, CARG1, LJ_TTAB, ->fff_fallback
1155 #if LJ_52
1156   |  ldr TAB:CARG2, TAB:TMP1->metatable
1157 #endif
1158   |   ldr CFUNC:CARG4, CFUNC:CARG3->upvalue[0]
1159   |    ldr PC, [BASE, FRAME_PC]
1160 #if LJ_52
1161   |  cbnz TAB:CARG2, ->fff_fallback
1162 #endif
1163   |  mov RC, #(3+1)*8
1164   |  stp CFUNC:CARG4, CARG1, [BASE, #-16]
1165   |   str TISNIL, [BASE]
1166   |  b ->fff_res
1167   |
1168   |.ffunc_2 ipairs_aux
1169   |  checktab CARG1, ->fff_fallback
1170   |   checkint CARG2, ->fff_fallback
1171   |  ldr TMP1w, TAB:CARG1->asize
1172   |   ldr CARG3, TAB:CARG1->array
1173   |    ldr TMP0w, TAB:CARG1->hmask
1174   |  add CARG2w, CARG2w, #1
1175   |  cmp CARG2w, TMP1w
1176   |    ldr PC, [BASE, FRAME_PC]
1177   |     add_TISNUM TMP2, CARG2
1178   |   mov RC, #(0+1)*8
1179   |     str TMP2, [BASE, #-16]
1180   |  bhs >2                             // Not in array part?
1181   |  ldr TMP0, [CARG3, CARG2, lsl #3]
1182   |1:
1183   |   mov TMP1, #(2+1)*8
1184   |   cmp_nil TMP0
1185   |  str TMP0, [BASE, #-8]
1186   |   csel RC, RC, TMP1, eq
1187   |  b ->fff_res
1188   |2:  // Check for empty hash part first. Otherwise call C function.
1189   |  cbz TMP0w, ->fff_res
1190   |  bl extern lj_tab_getinth           // (GCtab *t, int32_t key)
1191   |  // Returns cTValue * or NULL.
1192   |  cbz CRET1, ->fff_res
1193   |  ldr TMP0, [CRET1]
1194   |  b <1
1195   |
1196   |.ffunc_1 ipairs
1197   |  checktp TMP1, CARG1, LJ_TTAB, ->fff_fallback
1198 #if LJ_52
1199   |  ldr TAB:CARG2, TAB:TMP1->metatable
1200 #endif
1201   |   ldr CFUNC:CARG4, CFUNC:CARG3->upvalue[0]
1202   |    ldr PC, [BASE, FRAME_PC]
1203 #if LJ_52
1204   |  cbnz TAB:CARG2, ->fff_fallback
1205 #endif
1206   |  mov RC, #(3+1)*8
1207   |  stp CFUNC:CARG4, CARG1, [BASE, #-16]
1208   |   str TISNUM, [BASE]
1209   |  b ->fff_res
1210   |
1211   |//-- Base library: catch errors ----------------------------------------
1212   |
1213   |.ffunc pcall
1214   |  ldr TMP1, L->maxstack
1215   |  add TMP2, BASE, NARGS8:RC
1216   |  cmp TMP1, TMP2
1217   |  blo ->fff_fallback
1218   |   cmp NARGS8:RC, #8
1219   |  ldrb TMP0w, GL->hookmask
1220   |   blo ->fff_fallback
1221   |   sub NARGS8:RC, NARGS8:RC, #8
1222   |    mov RB, BASE
1223   |    add BASE, BASE, #16
1224   |  ubfx TMP0w, TMP0w, #HOOK_ACTIVE_SHIFT, #1
1225   |  add PC, TMP0, #16+FRAME_PCALL
1226   |   beq ->vm_call_dispatch
1227   |1:
1228   |   add TMP2, BASE, NARGS8:RC
1229   |2:
1230   |   ldr TMP0, [TMP2, #-16]
1231   |   str TMP0, [TMP2, #-8]!
1232   |  cmp TMP2, BASE
1233   |  bne <2
1234   |  b ->vm_call_dispatch
1235   |
1236   |.ffunc xpcall
1237   |  ldr TMP1, L->maxstack
1238   |  add TMP2, BASE, NARGS8:RC
1239   |  cmp TMP1, TMP2
1240   |  blo ->fff_fallback
1241   |     ldp CARG1, CARG2, [BASE]
1242   |  ldrb TMP0w, GL->hookmask
1243   |   subs NARGS8:TMP1, NARGS8:RC, #16
1244   |   blo ->fff_fallback
1245   |    mov RB, BASE
1246   |     asr ITYPE, CARG2, #47
1247   |  ubfx TMP0w, TMP0w, #HOOK_ACTIVE_SHIFT, #1
1248   |     cmn ITYPE, #-LJ_TFUNC
1249   |  add PC, TMP0, #24+FRAME_PCALL
1250   |     bne ->fff_fallback              // Traceback must be a function.
1251   |   mov NARGS8:RC, NARGS8:TMP1
1252   |    add BASE, BASE, #24
1253   |     stp CARG2, CARG1, [RB]          // Swap function and traceback.
1254   |   cbz NARGS8:RC, ->vm_call_dispatch
1255   |  b <1
1256   |
1257   |//-- Coroutine library --------------------------------------------------
1258   |
1259   |.macro coroutine_resume_wrap, resume
1260   |.if resume
1261   |.ffunc_1 coroutine_resume
1262   |  checktp CARG1, LJ_TTHREAD, ->fff_fallback
1263   |.else
1264   |.ffunc coroutine_wrap_aux
1265   |  ldr L:CARG1, CFUNC:CARG3->upvalue[0].gcr
1266   |  and L:CARG1, CARG1, #LJ_GCVMASK
1267   |.endif
1268   |   ldr PC, [BASE, FRAME_PC]
1269   |     str BASE, L->base
1270   |  ldp RB, CARG2, L:CARG1->base
1271   |   ldrb TMP1w, L:CARG1->status
1272   |  add TMP0, CARG2, TMP1
1273   |   str PC, SAVE_PC
1274   |  cmp TMP0, RB
1275   |  beq ->fff_fallback
1276   |   cmp TMP1, #LUA_YIELD
1277   |    add TMP0, CARG2, #8
1278   |   csel CARG2, CARG2, TMP0, hs
1279   |   ldr CARG4, L:CARG1->maxstack
1280   |   add CARG3, CARG2, NARGS8:RC
1281   |    ldr RB, L:CARG1->cframe
1282   |   ccmp CARG3, CARG4, #2, ls
1283   |    ccmp RB, #0, #2, ls
1284   |    bhi ->fff_fallback
1285   |.if resume
1286   |  sub CARG3, CARG3, #8               // Keep resumed thread in stack for GC.
1287   |  add BASE, BASE, #8
1288   |  sub NARGS8:RC, NARGS8:RC, #8
1289   |.endif
1290   |  str CARG3, L:CARG1->top
1291   |  str BASE, L->top
1292   |  cbz NARGS8:RC, >3
1293   |2:  // Move args to coroutine.
1294   |   ldr TMP0, [BASE, RB]
1295   |  cmp RB, NARGS8:RC
1296   |   str TMP0, [CARG2, RB]
1297   |   add RB, RB, #8
1298   |  bne <2
1299   |3:
1300   |  mov CARG3, #0
1301   |   mov L:RA, L:CARG1
1302   |  mov CARG4, #0
1303   |  bl ->vm_resume                     // (lua_State *L, TValue *base, 0, 0)
1304   |  // Returns thread status.
1305   |4:
1306   |  ldp CARG3, CARG4, L:RA->base
1307   |   cmp CRET1, #LUA_YIELD
1308   |  ldr BASE, L->base
1309   |    str L, GL->cur_L
1310   |    st_vmstate ST_INTERP
1311   |   bhi >8
1312   |  sub RC, CARG4, CARG3
1313   |   ldr CARG1, L->maxstack
1314   |   add CARG2, BASE, RC
1315   |  cbz RC, >6                         // No results?
1316   |  cmp CARG2, CARG1
1317   |   mov RB, #0
1318   |  bhi >9                             // Need to grow stack?
1319   |
1320   |  sub CARG4, RC, #8
1321   |   str CARG3, L:RA->top              // Clear coroutine stack.
1322   |5:  // Move results from coroutine.
1323   |   ldr TMP0, [CARG3, RB]
1324   |  cmp RB, CARG4
1325   |   str TMP0, [BASE, RB]
1326   |   add RB, RB, #8
1327   |  bne <5
1328   |6:
1329   |.if resume
1330   |  mov_true TMP1
1331   |   add RC, RC, #16
1332   |7:
1333   |  str TMP1, [BASE, #-8]              // Prepend true/false to results.
1334   |   sub RA, BASE, #8
1335   |.else
1336   |   mov RA, BASE
1337   |   add RC, RC, #8
1338   |.endif
1339   |  ands CARG1, PC, #FRAME_TYPE
1340   |   str PC, SAVE_PC
1341   |   str RCw, SAVE_MULTRES
1342   |  beq ->BC_RET_Z
1343   |  b ->vm_return
1344   |
1345   |8:  // Coroutine returned with error (at co->top-1).
1346   |.if resume
1347   |  ldr TMP0, [CARG4, #-8]!
1348   |   mov_false TMP1
1349   |    mov RC, #(2+1)*8
1350   |  str CARG4, L:RA->top               // Remove error from coroutine stack.
1351   |  str TMP0, [BASE]                   // Copy error message.
1352   |  b <7
1353   |.else
1354   |  mov CARG1, L
1355   |  mov CARG2, L:RA
1356   |  bl extern lj_ffh_coroutine_wrap_err  // (lua_State *L, lua_State *co)
1357   |  // Never returns.
1358   |.endif
1359   |
1360   |9:  // Handle stack expansion on return from yield.
1361   |  mov CARG1, L
1362   |  lsr CARG2, RC, #3
1363   |  bl extern lj_state_growstack       // (lua_State *L, int n)
1364   |  mov CRET1, #0
1365   |  b <4
1366   |.endmacro
1367   |
1368   |  coroutine_resume_wrap 1            // coroutine.resume
1369   |  coroutine_resume_wrap 0            // coroutine.wrap
1370   |
1371   |.ffunc coroutine_yield
1372   |  ldr TMP0, L->cframe
1373   |   add TMP1, BASE, NARGS8:RC
1374   |    mov CRET1, #LUA_YIELD
1375   |   stp BASE, TMP1, L->base
1376   |  tbz TMP0, #0, ->fff_fallback
1377   |   str xzr, L->cframe
1378   |    strb CRET1w, L->status
1379   |  b ->vm_leave_unw
1380   |
1381   |//-- Math library -------------------------------------------------------
1382   |
1383   |.macro math_round, func, round
1384   |  .ffunc math_ .. func
1385   |  ldr CARG1, [BASE]
1386   |   cmp NARGS8:RC, #8
1387   |  ldr d0, [BASE]
1388   |   blo ->fff_fallback
1389   |  cmp TISNUMhi, CARG1, lsr #32
1390   |  beq ->fff_restv
1391   |  blo ->fff_fallback
1392   |  round d0, d0
1393   |  b ->fff_resn
1394   |.endmacro
1395   |
1396   |  math_round floor, frintm
1397   |  math_round ceil, frintp
1398   |
1399   |.ffunc_1 math_abs
1400   |  checknumber CARG1, ->fff_fallback
1401   |  and CARG1, CARG1, #U64x(7fffffff,ffffffff)
1402   |  bne ->fff_restv
1403   |  eor CARG2w, CARG1w, CARG1w, asr #31
1404   |   movz CARG3, #0x41e0, lsl #48      // 2^31.
1405   |  subs CARG1w, CARG2w, CARG1w, asr #31
1406   |   add_TISNUM CARG1, CARG1
1407   |  csel CARG1, CARG1, CARG3, pl
1408   |  // Fallthrough.
1409   |
1410   |->fff_restv:
1411   |  // CARG1 = TValue result.
1412   |  ldr PC, [BASE, FRAME_PC]
1413   |  str CARG1, [BASE, #-16]
1414   |->fff_res1:
1415   |  // PC = return.
1416   |  mov RC, #(1+1)*8
1417   |->fff_res:
1418   |  // RC = (nresults+1)*8, PC = return.
1419   |  ands CARG1, PC, #FRAME_TYPE
1420   |   str RCw, SAVE_MULTRES
1421   |   sub RA, BASE, #16
1422   |  bne ->vm_return
1423   |  ldr INSw, [PC, #-4]
1424   |  decode_RB RB, INS
1425   |5:
1426   |  cmp RC, RB, lsl #3                 // More results expected?
1427   |  blo >6
1428   |  decode_RA TMP1, INS
1429   |  // Adjust BASE. KBASE is assumed to be set for the calling frame.
1430   |  sub BASE, RA, TMP1, lsl #3
1431   |  ins_next
1432   |
1433   |6:  // Fill up results with nil.
1434   |  add TMP1, RA, RC
1435   |   add RC, RC, #8
1436   |  str TISNIL, [TMP1, #-8]
1437   |  b <5
1438   |
1439   |.macro math_extern, func
1440   |  .ffunc_n math_ .. func
1441   |  bl extern func
1442   |  b ->fff_resn
1443   |.endmacro
1444   |
1445   |.macro math_extern2, func
1446   |  .ffunc_nn math_ .. func
1447   |  bl extern func
1448   |  b ->fff_resn
1449   |.endmacro
1450   |
1451   |.ffunc_n math_sqrt
1452   |  fsqrt d0, d0
1453   |->fff_resn:
1454   |  ldr PC, [BASE, FRAME_PC]
1455   |  str d0, [BASE, #-16]
1456   |  b ->fff_res1
1457   |
1458   |.ffunc math_log
1459   |  ldr CARG1, [BASE]
1460   |   cmp NARGS8:RC, #8
1461   |  ldr FARG1, [BASE]
1462   |   bne ->fff_fallback                        // Need exactly 1 argument.
1463   |  checknum CARG1, ->fff_fallback
1464   |  bl extern log
1465   |  b ->fff_resn
1466   |
1467   |  math_extern log10
1468   |  math_extern exp
1469   |  math_extern sin
1470   |  math_extern cos
1471   |  math_extern tan
1472   |  math_extern asin
1473   |  math_extern acos
1474   |  math_extern atan
1475   |  math_extern sinh
1476   |  math_extern cosh
1477   |  math_extern tanh
1478   |  math_extern2 pow
1479   |  math_extern2 atan2
1480   |  math_extern2 fmod
1481   |
1482   |.ffunc_2 math_ldexp
1483   |  ldr FARG1, [BASE]
1484   |  checknum CARG1, ->fff_fallback
1485   |  checkint CARG2, ->fff_fallback
1486   |  sxtw CARG1, CARG2w
1487   |  bl extern ldexp                    // (double x, int exp)
1488   |  b ->fff_resn
1489   |
1490   |.ffunc_n math_frexp
1491   |  add CARG1, sp, TMPDofs
1492   |  bl extern frexp
1493   |   ldr CARG2w, TMPD
1494   |    ldr PC, [BASE, FRAME_PC]
1495   |  str d0, [BASE, #-16]
1496   |    mov RC, #(2+1)*8
1497   |   add_TISNUM CARG2, CARG2
1498   |   str CARG2, [BASE, #-8]
1499   |  b ->fff_res
1500   |
1501   |.ffunc_n math_modf
1502   |  sub CARG1, BASE, #16
1503   |   ldr PC, [BASE, FRAME_PC]
1504   |  bl extern modf
1505   |   mov RC, #(2+1)*8
1506   |  str d0, [BASE, #-8]
1507   |  b ->fff_res
1508   |
1509   |.macro math_minmax, name, cond, fcond
1510   |  .ffunc_1 name
1511   |   add RB, BASE, RC
1512   |   add RA, BASE, #8
1513   |  checkint CARG1, >4
1514   |1:  // Handle integers.
1515   |  ldr CARG2, [RA]
1516   |   cmp RA, RB
1517   |   bhs ->fff_restv
1518   |  checkint CARG2, >3
1519   |  cmp CARG1w, CARG2w
1520   |   add RA, RA, #8
1521   |  csel CARG1, CARG2, CARG1, cond
1522   |  b <1
1523   |3:  // Convert intermediate result to number and continue below.
1524   |  scvtf d0, CARG1w
1525   |  blo ->fff_fallback
1526   |  ldr d1, [RA]
1527   |  b >6
1528   |
1529   |4:
1530   |  ldr d0, [BASE]
1531   |  blo ->fff_fallback
1532   |5:  // Handle numbers.
1533   |  ldr CARG2, [RA]
1534   |  ldr d1, [RA]
1535   |   cmp RA, RB
1536   |   bhs ->fff_resn
1537   |  checknum CARG2, >7
1538   |6:
1539   |  fcmp d0, d1
1540   |   add RA, RA, #8
1541   |  fcsel d0, d1, d0, fcond
1542   |  b <5
1543   |7:  // Convert integer to number and continue above.
1544   |  scvtf d1, CARG2w
1545   |  blo ->fff_fallback
1546   |  b <6
1547   |.endmacro
1548   |
1549   |  math_minmax math_min, gt, pl
1550   |  math_minmax math_max, lt, le
1551   |
1552   |//-- String library -----------------------------------------------------
1553   |
1554   |.ffunc string_byte                   // Only handle the 1-arg case here.
1555   |  ldp PC, CARG1, [BASE, FRAME_PC]
1556   |   cmp NARGS8:RC, #8
1557   |  asr ITYPE, CARG1, #47
1558   |  ccmn ITYPE, #-LJ_TSTR, #0, eq
1559   |   and STR:CARG1, CARG1, #LJ_GCVMASK
1560   |  bne ->fff_fallback
1561   |  ldrb TMP0w, STR:CARG1[1]           // Access is always ok (NUL at end).
1562   |   ldr CARG3w, STR:CARG1->len
1563   |  add_TISNUM TMP0, TMP0
1564   |  str TMP0, [BASE, #-16]
1565   |  mov RC, #(0+1)*8
1566   |   cbz CARG3, ->fff_res
1567   |  b ->fff_res1
1568   |
1569   |.ffunc string_char                   // Only handle the 1-arg case here.
1570   |  ffgccheck
1571   |  ldp PC, CARG1, [BASE, FRAME_PC]
1572   |  cmp CARG1w, #255
1573   |   ccmp NARGS8:RC, #8, #0, ls                // Need exactly 1 argument.
1574   |  bne ->fff_fallback
1575   |  checkint CARG1, ->fff_fallback
1576   |  mov CARG3, #1
1577   |  // Point to the char inside the integer in the stack slot.
1578   |.if ENDIAN_LE
1579   |  mov CARG2, BASE
1580   |.else
1581   |  add CARG2, BASE, #7
1582   |.endif
1583   |->fff_newstr:
1584   |  // CARG2 = str, CARG3 = len.
1585   |   str BASE, L->base
1586   |  mov CARG1, L
1587   |   str PC, SAVE_PC
1588   |  bl extern lj_str_new               // (lua_State *L, char *str, size_t l)
1589   |->fff_resstr:
1590   |  // Returns GCstr *.
1591   |  ldr BASE, L->base
1592   |   movn TMP1, #~LJ_TSTR
1593   |  add CARG1, CARG1, TMP1, lsl #47
1594   |  b ->fff_restv
1595   |
1596   |.ffunc string_sub
1597   |  ffgccheck
1598   |  ldr CARG1, [BASE]
1599   |    ldr CARG3, [BASE, #16]
1600   |   cmp NARGS8:RC, #16
1601   |    movn RB, #0
1602   |   beq >1
1603   |   blo ->fff_fallback
1604   |    checkint CARG3, ->fff_fallback
1605   |    sxtw RB, CARG3w
1606   |1:
1607   |  ldr CARG2, [BASE, #8]
1608   |  checkstr CARG1, ->fff_fallback
1609   |   ldr TMP1w, STR:CARG1->len
1610   |  checkint CARG2, ->fff_fallback
1611   |  sxtw CARG2, CARG2w
1612   |  // CARG1 = str, TMP1 = str->len, CARG2 = start, RB = end
1613   |   add TMP2, RB, TMP1
1614   |   cmp RB, #0
1615   |  add TMP0, CARG2, TMP1
1616   |   csinc RB, RB, TMP2, ge            // if (end < 0) end += len+1
1617   |  cmp CARG2, #0
1618   |  csinc CARG2, CARG2, TMP0, ge       // if (start < 0) start += len+1
1619   |   cmp RB, #0
1620   |   csel RB, RB, xzr, ge              // if (end < 0) end = 0
1621   |  cmp CARG2, #1
1622   |  csinc CARG2, CARG2, xzr, ge        // if (start < 1) start = 1
1623   |   cmp RB, TMP1
1624   |   csel RB, RB, TMP1, le             // if (end > len) end = len
1625   |  add CARG1, STR:CARG1, #sizeof(GCstr)-1
1626   |   subs CARG3, RB, CARG2             // len = end - start
1627   |  add CARG2, CARG1, CARG2
1628   |   add CARG3, CARG3, #1              // len += 1
1629   |   bge ->fff_newstr
1630   |  add STR:CARG1, GL, #offsetof(global_State, strempty)
1631   |   movn TMP1, #~LJ_TSTR
1632   |  add CARG1, CARG1, TMP1, lsl #47
1633   |  b ->fff_restv
1634   |
1635   |.macro ffstring_op, name
1636   |  .ffunc string_ .. name
1637   |  ffgccheck
1638   |  ldr CARG2, [BASE]
1639   |   cmp NARGS8:RC, #8
1640   |  asr ITYPE, CARG2, #47
1641   |  ccmn ITYPE, #-LJ_TSTR, #0, hs
1642   |   and STR:CARG2, CARG2, #LJ_GCVMASK
1643   |  bne ->fff_fallback
1644   |  ldr TMP0, GL->tmpbuf.b
1645   |   add SBUF:CARG1, GL, #offsetof(global_State, tmpbuf)
1646   |   str BASE, L->base
1647   |   str PC, SAVE_PC
1648   |   str L, GL->tmpbuf.L
1649   |  str TMP0, GL->tmpbuf.w
1650   |  bl extern lj_buf_putstr_ .. name
1651   |  bl extern lj_buf_tostr
1652   |  b ->fff_resstr
1653   |.endmacro
1654   |
1655   |ffstring_op reverse
1656   |ffstring_op lower
1657   |ffstring_op upper
1658   |
1659   |//-- Bit library --------------------------------------------------------
1660   |
1661   |// FP number to bit conversion for soft-float. Clobbers CARG1-CARG3
1662   |->vm_tobit_fb:
1663   |  bls ->fff_fallback
1664   |  add CARG2, CARG1, CARG1
1665   |  mov CARG3, #1076
1666   |  sub CARG3, CARG3, CARG2, lsr #53
1667   |  cmp CARG3, #53
1668   |  bhi >1
1669   |  and CARG2, CARG2, #U64x(001fffff,ffffffff)
1670   |  orr CARG2, CARG2, #U64x(00200000,00000000)
1671   |   cmp CARG1, #0
1672   |  lsr CARG2, CARG2, CARG3
1673   |   cneg CARG1w, CARG2w, mi
1674   |  br lr
1675   |1:
1676   |  mov CARG1w, #0
1677   |  br lr
1678   |
1679   |.macro .ffunc_bit, name
1680   |  .ffunc_1 bit_..name
1681   |  adr lr, >1
1682   |  checkint CARG1, ->vm_tobit_fb
1683   |1:
1684   |.endmacro
1685   |
1686   |.macro .ffunc_bit_op, name, ins
1687   |  .ffunc_bit name
1688   |  mov RA, #8
1689   |  mov TMP0w, CARG1w
1690   |  adr lr, >2
1691   |1:
1692   |  ldr CARG1, [BASE, RA]
1693   |   cmp RA, NARGS8:RC
1694   |    add RA, RA, #8
1695   |   bge >9
1696   |  checkint CARG1, ->vm_tobit_fb
1697   |2:
1698   |  ins TMP0w, TMP0w, CARG1w
1699   |  b <1
1700   |.endmacro
1701   |
1702   |.ffunc_bit_op band, and
1703   |.ffunc_bit_op bor, orr
1704   |.ffunc_bit_op bxor, eor
1705   |
1706   |.ffunc_bit tobit
1707   |  mov TMP0w, CARG1w
1708   |9:  // Label reused by .ffunc_bit_op users.
1709   |  add_TISNUM CARG1, TMP0
1710   |  b ->fff_restv
1711   |
1712   |.ffunc_bit bswap
1713   |  rev TMP0w, CARG1w
1714   |  add_TISNUM CARG1, TMP0
1715   |  b ->fff_restv
1716   |
1717   |.ffunc_bit bnot
1718   |  mvn TMP0w, CARG1w
1719   |  add_TISNUM CARG1, TMP0
1720   |  b ->fff_restv
1721   |
1722   |.macro .ffunc_bit_sh, name, ins, shmod
1723   |  .ffunc bit_..name
1724   |  ldp TMP0, CARG1, [BASE]
1725   |   cmp NARGS8:RC, #16
1726   |   blo ->fff_fallback
1727   |  adr lr, >1
1728   |  checkint CARG1, ->vm_tobit_fb
1729   |1:
1730   |.if shmod == 0
1731   |  mov TMP1, CARG1
1732   |.else
1733   |  neg TMP1, CARG1
1734   |.endif
1735   |  mov CARG1, TMP0
1736   |  adr lr, >2
1737   |  checkint CARG1, ->vm_tobit_fb
1738   |2:
1739   |  ins TMP0w, CARG1w, TMP1w
1740   |  add_TISNUM CARG1, TMP0
1741   |  b ->fff_restv
1742   |.endmacro
1743   |
1744   |.ffunc_bit_sh lshift, lsl, 0
1745   |.ffunc_bit_sh rshift, lsr, 0
1746   |.ffunc_bit_sh arshift, asr, 0
1747   |.ffunc_bit_sh rol, ror, 1
1748   |.ffunc_bit_sh ror, ror, 0
1749   |
1750   |//-----------------------------------------------------------------------
1751   |
1752   |->fff_fallback:                      // Call fast function fallback handler.
1753   |  // BASE = new base, RC = nargs*8
1754   |   ldp CFUNC:CARG3, PC, [BASE, FRAME_FUNC]   // Fallback may overwrite PC.
1755   |  ldr TMP2, L->maxstack
1756   |  add TMP1, BASE, NARGS8:RC
1757   |  stp BASE, TMP1, L->base
1758   |   and CFUNC:CARG3, CARG3, #LJ_GCVMASK
1759   |  add TMP1, TMP1, #8*LUA_MINSTACK
1760   |   ldr CARG3, CFUNC:CARG3->f
1761   |    str PC, SAVE_PC                  // Redundant (but a defined value).
1762   |  cmp TMP1, TMP2
1763   |   mov CARG1, L
1764   |  bhi >5                             // Need to grow stack.
1765   |   blr_auth CARG3                    // (lua_State *L)
1766   |  // Either throws an error, or recovers and returns -1, 0 or nresults+1.
1767   |   ldr BASE, L->base
1768   |  cmp CRET1w, #0
1769   |   lsl RC, CRET1, #3
1770   |   sub RA, BASE, #16
1771   |  bgt ->fff_res                      // Returned nresults+1?
1772   |1:  // Returned 0 or -1: retry fast path.
1773   |   ldr CARG1, L->top
1774   |    ldr CFUNC:CARG3, [BASE, FRAME_FUNC]
1775   |   sub NARGS8:RC, CARG1, BASE
1776   |  bne ->vm_call_tail                 // Returned -1?
1777   |    and CFUNC:CARG3, CARG3, #LJ_GCVMASK
1778   |  ins_callt                          // Returned 0: retry fast path.
1779   |
1780   |// Reconstruct previous base for vmeta_call during tailcall.
1781   |->vm_call_tail:
1782   |  ands TMP0, PC, #FRAME_TYPE
1783   |   and TMP1, PC, #~FRAME_TYPEP
1784   |  bne >3
1785   |  ldrb RAw, [PC, #-4+OFS_RA]
1786   |  lsl RA, RA, #3
1787   |  add TMP1, RA, #16
1788   |3:
1789   |  sub RB, BASE, TMP1
1790   |  b ->vm_call_dispatch               // Resolve again for tailcall.
1791   |
1792   |5:  // Grow stack for fallback handler.
1793   |  mov CARG2, #LUA_MINSTACK
1794   |  bl extern lj_state_growstack       // (lua_State *L, int n)
1795   |  ldr BASE, L->base
1796   |  cmp CARG1, CARG1                   // Set zero-flag to force retry.
1797   |  b <1
1798   |
1799   |->fff_gcstep:                        // Call GC step function.
1800   |  // BASE = new base, RC = nargs*8
1801   |  sp_auth
1802   |   add CARG2, BASE, NARGS8:RC        // Calculate L->top.
1803   |  mov RA, lr
1804   |   stp BASE, CARG2, L->base
1805   |   str PC, SAVE_PC                   // Redundant (but a defined value).
1806   |  mov CARG1, L
1807   |  bl extern lj_gc_step               // (lua_State *L)
1808   |  ldp BASE, CARG2, L->base
1809   |   ldr CFUNC:CARG3, [BASE, FRAME_FUNC]
1810   |  mov lr, RA                         // Help return address predictor.
1811   |  sub NARGS8:RC, CARG2, BASE         // Calculate nargs*8.
1812   |   and CFUNC:CARG3, CARG3, #LJ_GCVMASK
1813   |  ret_auth
1814   |
1815   |//-----------------------------------------------------------------------
1816   |//-- Special dispatch targets -------------------------------------------
1817   |//-----------------------------------------------------------------------
1818   |
1819   |->vm_record:                         // Dispatch target for recording phase.
1820   |.if JIT
1821   |  ldrb CARG1w, GL->hookmask
1822   |  tst CARG1, #HOOK_VMEVENT           // No recording while in vmevent.
1823   |  bne >5
1824   |  // Decrement the hookcount for consistency, but always do the call.
1825   |   ldr CARG2w, GL->hookcount
1826   |  tst CARG1, #HOOK_ACTIVE
1827   |  bne >1
1828   |   sub CARG2w, CARG2w, #1
1829   |  tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT
1830   |  beq >1
1831   |   str CARG2w, GL->hookcount
1832   |  b >1
1833   |.endif
1834   |
1835   |->vm_rethook:                        // Dispatch target for return hooks.
1836   |  ldrb TMP2w, GL->hookmask
1837   |  tbz TMP2w, #HOOK_ACTIVE_SHIFT, >1  // Hook already active?
1838   |5:  // Re-dispatch to static ins.
1839   |  ldr TMP0, [TMP1, #GG_G2DISP+GG_DISP2STATIC]
1840   |  br_auth TMP0
1841   |
1842   |->vm_inshook:                        // Dispatch target for instr/line hooks.
1843   |  ldrb TMP2w, GL->hookmask
1844   |   ldr TMP3w, GL->hookcount
1845   |  tbnz TMP2w, #HOOK_ACTIVE_SHIFT, <5 // Hook already active?
1846   |  tst TMP2w, #LUA_MASKLINE|LUA_MASKCOUNT
1847   |  beq <5
1848   |   sub TMP3w, TMP3w, #1
1849   |   str TMP3w, GL->hookcount
1850   |   cbz TMP3w, >1
1851   |  tbz TMP2w, #LUA_HOOKLINE, <5
1852   |1:
1853   |  mov CARG1, L
1854   |   str BASE, L->base
1855   |  mov CARG2, PC
1856   |  // SAVE_PC must hold the _previous_ PC. The callee updates it with PC.
1857   |  bl extern lj_dispatch_ins          // (lua_State *L, const BCIns *pc)
1858   |3:
1859   |  ldr BASE, L->base
1860   |4:  // Re-dispatch to static ins.
1861   |  ldr INSw, [PC, #-4]
1862   |  add TMP1, GL, INS, uxtb #3
1863   |   decode_RA RA, INS
1864   |  ldr TMP0, [TMP1, #GG_G2DISP+GG_DISP2STATIC]
1865   |   decode_RD RC, INS
1866   |  br_auth TMP0
1867   |
1868   |->cont_hook:                         // Continue from hook yield.
1869   |  ldr CARG1, [CARG4, #-40]
1870   |   add PC, PC, #4
1871   |  str CARG1w, SAVE_MULTRES           // Restore MULTRES for *M ins.
1872   |  b <4
1873   |
1874   |->vm_hotloop:                        // Hot loop counter underflow.
1875   |.if JIT
1876   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Same as curr_topL(L).
1877   |   add CARG1, GL, #GG_G2DISP+GG_DISP2J
1878   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
1879   |   str PC, SAVE_PC
1880   |  ldr CARG3, LFUNC:CARG3->pc
1881   |   mov CARG2, PC
1882   |   str L, [GL, #GL_J(L)]
1883   |  ldrb CARG3w, [CARG3, #PC2PROTO(framesize)]
1884   |   str BASE, L->base
1885   |  add CARG3, BASE, CARG3, lsl #3
1886   |  str CARG3, L->top
1887   |  bl extern lj_trace_hot             // (jit_State *J, const BCIns *pc)
1888   |  b <3
1889   |.endif
1890   |
1891   |->vm_callhook:                       // Dispatch target for call hooks.
1892   |  mov CARG2, PC
1893   |.if JIT
1894   |  b >1
1895   |.endif
1896   |
1897   |->vm_hotcall:                        // Hot call counter underflow.
1898   |.if JIT
1899   |  orr CARG2, PC, #1
1900   |1:
1901   |.endif
1902   |  add TMP1, BASE, NARGS8:RC
1903   |   str PC, SAVE_PC
1904   |   mov CARG1, L
1905   |   sub RA, RA, BASE
1906   |  stp BASE, TMP1, L->base
1907   |  bl extern lj_dispatch_call         // (lua_State *L, const BCIns *pc)
1908   |  // Returns ASMFunction.
1909   |  ldp BASE, TMP1, L->base
1910   |   str xzr, SAVE_PC                  // Invalidate for subsequent line hook.
1911   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
1912   |  add RA, BASE, RA
1913   |  sub NARGS8:RC, TMP1, BASE
1914   |   ldr INSw, [PC, #-4]
1915   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
1916   |  br_auth CRET1
1917   |
1918   |->cont_stitch:                       // Trace stitching.
1919   |.if JIT
1920   |  // RA = resultptr, CARG4 = meta base
1921   |   ldr RBw, SAVE_MULTRES
1922   |  ldr INSw, [PC, #-4]
1923   |    ldr TRACE:CARG3, [CARG4, #-40]   // Save previous trace.
1924   |   subs RB, RB, #8
1925   |  decode_RA RC, INS                  // Call base.
1926   |    and CARG3, CARG3, #LJ_GCVMASK
1927   |   beq >2
1928   |1:  // Move results down.
1929   |  ldr CARG1, [RA], #8
1930   |   subs RB, RB, #8
1931   |  str CARG1, [BASE, RC, lsl #3]
1932   |    add RC, RC, #1
1933   |   bne <1
1934   |2:
1935   |   decode_RA RA, INS
1936   |   decode_RB RB, INS
1937   |   add RA, RA, RB
1938   |3:
1939   |   cmp RA, RC
1940   |   bhi >9                            // More results wanted?
1941   |
1942   |  ldrh RAw, TRACE:CARG3->traceno
1943   |  ldrh RCw, TRACE:CARG3->link
1944   |  cmp RCw, RAw
1945   |  beq ->cont_nop                     // Blacklisted.
1946   |  cmp RCw, #0
1947   |  bne =>BC_JLOOP                     // Jump to stitched trace.
1948   |
1949   |  // Stitch a new trace to the previous trace.
1950   |  mov CARG1, #GL_J(exitno)
1951   |  str RAw, [GL, CARG1]
1952   |  mov CARG1, #GL_J(L)
1953   |  str L, [GL, CARG1]
1954   |  str BASE, L->base
1955   |  add CARG1, GL, #GG_G2J
1956   |  mov CARG2, PC
1957   |  bl extern lj_dispatch_stitch       // (jit_State *J, const BCIns *pc)
1958   |  ldr BASE, L->base
1959   |  b ->cont_nop
1960   |
1961   |9:  // Fill up results with nil.
1962   |  str TISNIL, [BASE, RC, lsl #3]
1963   |  add RC, RC, #1
1964   |  b <3
1965   |.endif
1966   |
1967   |->vm_profhook:                       // Dispatch target for profiler hook.
1968 #if LJ_HASPROFILE
1969   |  mov CARG1, L
1970   |   str BASE, L->base
1971   |  mov CARG2, PC
1972   |  bl extern lj_dispatch_profile      // (lua_State *L, const BCIns *pc)
1973   |  // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction.
1974   |  ldr BASE, L->base
1975   |  sub PC, PC, #4
1976   |  b ->cont_nop
1977 #endif
1978   |
1979   |//-----------------------------------------------------------------------
1980   |//-- Trace exit handler -------------------------------------------------
1981   |//-----------------------------------------------------------------------
1982   |
1983   |.macro savex_, a, b
1984   |  stp d..a, d..b, [sp, #a*8]
1985   |  stp x..a, x..b, [sp, #32*8+a*8]
1986   |.endmacro
1987   |
1988   |->vm_exit_handler:
1989   |.if JIT
1990   |  sub     sp, sp, #(64*8)
1991   |  savex_, 0, 1
1992   |  savex_, 2, 3
1993   |  savex_, 4, 5
1994   |  savex_, 6, 7
1995   |  savex_, 8, 9
1996   |  savex_, 10, 11
1997   |  savex_, 12, 13
1998   |  savex_, 14, 15
1999   |  savex_, 16, 17
2000   |  savex_, 18, 19
2001   |  savex_, 20, 21
2002   |  savex_, 22, 23
2003   |  savex_, 24, 25
2004   |  savex_, 26, 27
2005   |  savex_, 28, 29
2006   |  stp d30, d31, [sp, #30*8]
2007   |  ldr CARG1, [sp, #64*8]     // Load original value of lr.
2008   |   add CARG3, sp, #64*8      // Recompute original value of sp.
2009   |    mv_vmstate CARG4w, EXIT
2010   |   stp xzr, CARG3, [sp, #62*8]       // Store 0/sp in RID_LR/RID_SP.
2011   |  sub CARG1, CARG1, lr
2012   |   ldr L, GL->cur_L
2013   |  lsr CARG1, CARG1, #2
2014   |   ldr BASE, GL->jit_base
2015   |  sub CARG1, CARG1, #2
2016   |   ldr CARG2w, [lr]          // Load trace number.
2017   |    st_vmstate CARG4w
2018   |.if ENDIAN_BE
2019   |   rev32 CARG2, CARG2
2020   |.endif
2021   |   str BASE, L->base
2022   |  ubfx CARG2w, CARG2w, #5, #16
2023   |  str CARG1w, [GL, #GL_J(exitno)]
2024   |   str CARG2w, [GL, #GL_J(parent)]
2025   |   str L, [GL, #GL_J(L)]
2026   |  str xzr, GL->jit_base
2027   |  add CARG1, GL, #GG_G2J
2028   |  mov CARG2, sp
2029   |  bl extern lj_trace_exit            // (jit_State *J, ExitState *ex)
2030   |  // Returns MULTRES (unscaled) or negated error code.
2031   |  ldr CARG2, L->cframe
2032   |   ldr BASE, L->base
2033   |  and sp, CARG2, #CFRAME_RAWMASK
2034   |   ldr PC, SAVE_PC                   // Get SAVE_PC.
2035   |  str L, SAVE_L                      // Set SAVE_L (on-trace resume/yield).
2036   |  b >1
2037   |.endif
2038   |
2039   |->vm_exit_interp:
2040   |  // CARG1 = MULTRES or negated error code, BASE, PC and GL set.
2041   |.if JIT
2042   |  ldr L, SAVE_L
2043   |1:
2044   |   init_constants
2045   |  cmn CARG1w, #LUA_ERRERR
2046   |  bhs >9                             // Check for error from exit.
2047   |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2048   |   lsl RC, CARG1, #3
2049   |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2050   |   str RCw, SAVE_MULTRES
2051   |   str BASE, L->base
2052   |  ldr CARG2, LFUNC:CARG2->pc
2053   |   str xzr, GL->jit_base
2054   |    mv_vmstate CARG4w, INTERP
2055   |  ldr KBASE, [CARG2, #PC2PROTO(k)]
2056   |  // Modified copy of ins_next which handles function header dispatch, too.
2057   |  ldrb RBw, [PC, # OFS_OP]
2058   |   ldr INSw, [PC], #4
2059   |    st_vmstate CARG4w
2060   |  cmn CARG1w, #17                    // Static dispatch?
2061   |  beq >5
2062   |  cmp RBw, #BC_FUNCC+2               // Fast function?
2063   |   add TMP1, GL, INS, uxtb #3
2064   |  bhs >4
2065   |2:
2066   |  cmp RBw, #BC_FUNCF                 // Function header?
2067   |  add TMP0, GL, RB, uxtb #3
2068   |  ldr RB, [TMP0, #GG_G2DISP]
2069   |   decode_RA RA, INS
2070   |   lsr TMP0, INS, #16
2071   |   csel RC, TMP0, RC, lo
2072   |   blo >3
2073   |   ldr CARG3, [BASE, FRAME_FUNC]
2074   |   sub RC, RC, #8
2075   |   add RA, BASE, RA, lsl #3  // Yes: RA = BASE+framesize*8, RC = nargs*8
2076   |   and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2077   |3:
2078   |  br_auth RB
2079   |
2080   |4:  // Check frame below fast function.
2081   |  ldr CARG1, [BASE, FRAME_PC]
2082   |  ands CARG2, CARG1, #FRAME_TYPE
2083   |  bne <2                     // Trace stitching continuation?
2084   |  // Otherwise set KBASE for Lua function below fast function.
2085   |  ldr CARG3w, [CARG1, #-4]
2086   |  decode_RA CARG1, CARG3
2087   |  sub CARG2, BASE, CARG1, lsl #3
2088   |  ldr LFUNC:CARG3, [CARG2, #-32]
2089   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2090   |  ldr CARG3, LFUNC:CARG3->pc
2091   |  ldr KBASE, [CARG3, #PC2PROTO(k)]
2092   |  b <2
2093   |
2094   |5:  // Dispatch to static entry of original ins replaced by BC_JLOOP.
2095   |  ldr RA, [GL, #GL_J(trace)]
2096   |  decode_RD RC, INS
2097   |  ldr TRACE:RA, [RA, RC, lsl #3]
2098   |  ldr INSw, TRACE:RA->startins
2099   |  add TMP0, GL, INS, uxtb #3
2100   |   decode_RA RA, INS
2101   |  ldr RB, [TMP0, #GG_G2DISP+GG_DISP2STATIC]
2102   |   decode_RD RC, INS
2103   |  br_auth RB
2104   |
2105   |9:  // Rethrow error from the right C frame.
2106   |  neg CARG2w, CARG1w
2107   |  mov CARG1, L
2108   |  bl extern lj_err_trace             // (lua_State *L, int errcode)
2109   |.endif
2110   |
2111   |//-----------------------------------------------------------------------
2112   |//-- Math helper functions ----------------------------------------------
2113   |//-----------------------------------------------------------------------
2114   |
2115   |  // int lj_vm_modi(int dividend, int divisor);
2116   |->vm_modi:
2117   |    eor CARG4w, CARG1w, CARG2w
2118   |    cmp CARG4w, #0
2119   |  eor CARG3w, CARG1w, CARG1w, asr #31
2120   |   eor CARG4w, CARG2w, CARG2w, asr #31
2121   |  sub CARG3w, CARG3w, CARG1w, asr #31
2122   |   sub CARG4w, CARG4w, CARG2w, asr #31
2123   |  udiv CARG1w, CARG3w, CARG4w
2124   |  msub CARG1w, CARG1w, CARG4w, CARG3w
2125   |    ccmp CARG1w, #0, #4, mi
2126   |    sub CARG3w, CARG1w, CARG4w
2127   |    csel CARG1w, CARG1w, CARG3w, eq
2128   |  eor CARG3w, CARG1w, CARG2w
2129   |  cmp CARG3w, #0
2130   |  cneg CARG1w, CARG1w, mi
2131   |  ret
2132   |
2133   |//-----------------------------------------------------------------------
2134   |//-- Miscellaneous functions --------------------------------------------
2135   |//-----------------------------------------------------------------------
2136   |
2137   |.define NEXT_TAB,            TAB:CARG1
2138   |.define NEXT_RES,            CARG1
2139   |.define NEXT_IDX,            CARG2w
2140   |.define NEXT_LIM,            CARG3w
2141   |.define NEXT_TMP0,           TMP0
2142   |.define NEXT_TMP0w,          TMP0w
2143   |.define NEXT_TMP1,           TMP1
2144   |.define NEXT_TMP1w,          TMP1w
2145   |.define NEXT_RES_PTR,        sp
2146   |.define NEXT_RES_VAL,        [sp]
2147   |.define NEXT_RES_KEY,        [sp, #8]
2148   |
2149   |// TValue *lj_vm_next(GCtab *t, uint32_t idx)
2150   |// Next idx returned in CRET2w.
2151   |->vm_next:
2152   |.if JIT
2153   |  ldr NEXT_LIM, NEXT_TAB->asize
2154   |   ldr NEXT_TMP1, NEXT_TAB->array
2155   |1:  // Traverse array part.
2156   |  subs NEXT_TMP0w, NEXT_IDX, NEXT_LIM
2157   |  bhs >5                             // Index points after array part?
2158   |  ldr NEXT_TMP0, [NEXT_TMP1, NEXT_IDX, uxtw #3]
2159   |  cmn NEXT_TMP0, #-LJ_TNIL
2160   |   cinc NEXT_IDX, NEXT_IDX, eq
2161   |  beq <1                             // Skip holes in array part.
2162   |  str NEXT_TMP0, NEXT_RES_VAL
2163   |   movz NEXT_TMP0w, #(LJ_TISNUM>>1)&0xffff, lsl #16
2164   |   stp NEXT_IDX, NEXT_TMP0w, NEXT_RES_KEY
2165   |  add NEXT_IDX, NEXT_IDX, #1
2166   |  mov NEXT_RES, NEXT_RES_PTR
2167   |4:
2168   |  ret
2169   |
2170   |5:  // Traverse hash part.
2171   |  ldr NEXT_TMP1w, NEXT_TAB->hmask
2172   |   ldr NODE:NEXT_RES, NEXT_TAB->node
2173   |   add NEXT_TMP0w, NEXT_TMP0w, NEXT_TMP0w, lsl #1
2174   |  add NEXT_LIM, NEXT_LIM, NEXT_TMP1w
2175   |   add NODE:NEXT_RES, NODE:NEXT_RES, NEXT_TMP0w, uxtw #3
2176   |6:
2177   |  cmp NEXT_IDX, NEXT_LIM
2178   |  bhi >9
2179   |  ldr NEXT_TMP0, NODE:NEXT_RES->val
2180   |  cmn NEXT_TMP0, #-LJ_TNIL
2181   |   add NEXT_IDX, NEXT_IDX, #1
2182   |  bne <4
2183   |  // Skip holes in hash part.
2184   |  add NODE:NEXT_RES, NODE:NEXT_RES, #sizeof(Node)
2185   |  b <6
2186   |
2187   |9:  // End of iteration. Set the key to nil (not the value).
2188   |  movn NEXT_TMP0, #0
2189   |  str NEXT_TMP0, NEXT_RES_KEY
2190   |  mov NEXT_RES, NEXT_RES_PTR
2191   |  ret
2192   |.endif
2193   |
2194   |//-----------------------------------------------------------------------
2195   |//-- FFI helper functions -----------------------------------------------
2196   |//-----------------------------------------------------------------------
2197   |
2198   |// Handler for callback functions.
2199   |// Saveregs already performed. Callback slot number in w9, g in x10.
2200   |->vm_ffi_callback:
2201   |.if FFI
2202   |.type CTSTATE, CTState, PC
2203   |  saveregs
2204   |  ldr CTSTATE, GL:x10->ctype_state
2205   |  mov GL, x10
2206   |    add x10, sp, # CFRAME_SPACE
2207   |  str w9, CTSTATE->cb.slot
2208   |  stp x0, x1, CTSTATE->cb.gpr[0]
2209   |   stp d0, d1, CTSTATE->cb.fpr[0]
2210   |  stp x2, x3, CTSTATE->cb.gpr[2]
2211   |   stp d2, d3, CTSTATE->cb.fpr[2]
2212   |  stp x4, x5, CTSTATE->cb.gpr[4]
2213   |   stp d4, d5, CTSTATE->cb.fpr[4]
2214   |  stp x6, x7, CTSTATE->cb.gpr[6]
2215   |   stp d6, d7, CTSTATE->cb.fpr[6]
2216   |    str x10, CTSTATE->cb.stack
2217   |  mov CARG1, CTSTATE
2218   |   str CTSTATE, SAVE_PC              // Any value outside of bytecode is ok.
2219   |  mov CARG2, sp
2220   |  bl extern lj_ccallback_enter       // (CTState *cts, void *cf)
2221   |  // Returns lua_State *.
2222   |  ldp BASE, RC, L:CRET1->base
2223   |   init_constants
2224   |   mov L, CRET1
2225   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
2226   |  sub RC, RC, BASE
2227   |   st_vmstate ST_INTERP
2228   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2229   |  ins_callt
2230   |.endif
2231   |
2232   |->cont_ffi_callback:                 // Return from FFI callback.
2233   |.if FFI
2234   |  ldr CTSTATE, GL->ctype_state
2235   |   stp BASE, CARG4, L->base
2236   |  str L, CTSTATE->L
2237   |  mov CARG1, CTSTATE
2238   |  mov CARG2, RA
2239   |  bl extern lj_ccallback_leave       // (CTState *cts, TValue *o)
2240   |  ldp x0, x1, CTSTATE->cb.gpr[0]
2241   |   ldp d0, d1, CTSTATE->cb.fpr[0]
2242   |  b ->vm_leave_unw
2243   |.endif
2244   |
2245   |->vm_ffi_call:                       // Call C function via FFI.
2246   |  // Caveat: needs special frame unwinding, see below.
2247   |.if FFI
2248   |  .type CCSTATE, CCallState, x19
2249   |  sp_auth
2250   |  stp_unwind CCSTATE, x20, [sp, #-32]!
2251   |  stp fp, lr, [sp, #16]
2252   |  add fp, sp, #16
2253   |  mov CCSTATE, x0
2254   |  ldr TMP0w, CCSTATE:x0->spadj
2255   |   ldrb TMP1w, CCSTATE->nsp
2256   |    add TMP2, CCSTATE, #offsetof(CCallState, stack)
2257   |   subs TMP1, TMP1, #8
2258   |    ldr TMP3, CCSTATE->func
2259   |  sub sp, sp, TMP0
2260   |   bmi >2
2261   |1:  // Copy stack slots
2262   |  ldr TMP0, [TMP2, TMP1]
2263   |  str TMP0, [sp, TMP1]
2264   |  subs TMP1, TMP1, #8
2265   |  bpl <1
2266   |2:
2267   |  ldp x0, x1, CCSTATE->gpr[0]
2268   |   ldp d0, d1, CCSTATE->fpr[0]
2269   |  ldp x2, x3, CCSTATE->gpr[2]
2270   |   ldp d2, d3, CCSTATE->fpr[2]
2271   |  ldp x4, x5, CCSTATE->gpr[4]
2272   |   ldp d4, d5, CCSTATE->fpr[4]
2273   |  ldp x6, x7, CCSTATE->gpr[6]
2274   |   ldp d6, d7, CCSTATE->fpr[6]
2275   |  ldr x8, CCSTATE->retp
2276   |  blr_auth TMP3
2277   |  sub sp, fp, #16
2278   |  stp x0, x1, CCSTATE->gpr[0]
2279   |   stp d0, d1, CCSTATE->fpr[0]
2280   |   stp d2, d3, CCSTATE->fpr[2]
2281   |  ldp fp, lr, [sp, #16]
2282   |  ldp_unwind CCSTATE, x20, [sp], #32
2283   |  ret_auth
2284   |.endif
2285   |// Note: vm_ffi_call must be the last function in this object file!
2286   |
2287   |//-----------------------------------------------------------------------
2290 /* Generate the code for a single instruction. */
2291 static void build_ins(BuildCtx *ctx, BCOp op, int defop)
2293   int vk = 0;
2294   |=>defop:
2296   switch (op) {
2298   /* -- Comparison ops ---------------------------------------------------- */
2300   /* Remember: all ops branch for a true comparison, fall through otherwise. */
2302   case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT:
2303     |  // RA = src1, RC = src2, JMP with RC = target
2304     |  ldr CARG1, [BASE, RA, lsl #3]
2305     |    ldrh RBw, [PC, # OFS_RD]
2306     |   ldr CARG2, [BASE, RC, lsl #3]
2307     |    add PC, PC, #4
2308     |    add RB, PC, RB, lsl #2
2309     |    sub RB, RB, #0x20000
2310     |  checkint CARG1, >3
2311     |   checkint CARG2, >4
2312     |  cmp CARG1w, CARG2w
2313     if (op == BC_ISLT) {
2314       |  csel PC, RB, PC, lt
2315     } else if (op == BC_ISGE) {
2316       |  csel PC, RB, PC, ge
2317     } else if (op == BC_ISLE) {
2318       |  csel PC, RB, PC, le
2319     } else {
2320       |  csel PC, RB, PC, gt
2321     }
2322     |1:
2323     |  ins_next
2324     |
2325     |3:  // RA not int.
2326     |    ldr FARG1, [BASE, RA, lsl #3]
2327     |  blo ->vmeta_comp
2328     |    ldr FARG2, [BASE, RC, lsl #3]
2329     |   cmp TISNUMhi, CARG2, lsr #32
2330     |   bhi >5
2331     |   bne ->vmeta_comp
2332     |  // RA number, RC int.
2333     |  scvtf FARG2, CARG2w
2334     |  b >5
2335     |
2336     |4:  // RA int, RC not int
2337     |    ldr FARG2, [BASE, RC, lsl #3]
2338     |   blo ->vmeta_comp
2339     |  // RA int, RC number.
2340     |  scvtf FARG1, CARG1w
2341     |
2342     |5:  // RA number, RC number
2343     |  fcmp FARG1, FARG2
2344     |  // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't.
2345     if (op == BC_ISLT) {
2346       |  csel PC, RB, PC, lo
2347     } else if (op == BC_ISGE) {
2348       |  csel PC, RB, PC, hs
2349     } else if (op == BC_ISLE) {
2350       |  csel PC, RB, PC, ls
2351     } else {
2352       |  csel PC, RB, PC, hi
2353     }
2354     |  b <1
2355     break;
2357   case BC_ISEQV: case BC_ISNEV:
2358     vk = op == BC_ISEQV;
2359     |  // RA = src1, RC = src2, JMP with RC = target
2360     |  ldr CARG1, [BASE, RA, lsl #3]
2361     |   add RC, BASE, RC, lsl #3
2362     |    ldrh RBw, [PC, # OFS_RD]
2363     |   ldr CARG3, [RC]
2364     |    add PC, PC, #4
2365     |    add RB, PC, RB, lsl #2
2366     |    sub RB, RB, #0x20000
2367     |  asr ITYPE, CARG3, #47
2368     |  cmn ITYPE, #-LJ_TISNUM
2369     if (vk) {
2370       |  bls ->BC_ISEQN_Z
2371     } else {
2372       |  bls ->BC_ISNEN_Z
2373     }
2374     |  // RC is not a number.
2375     |   asr TMP0, CARG1, #47
2376     |.if FFI
2377     |  // Check if RC or RA is a cdata.
2378     |  cmn ITYPE, #-LJ_TCDATA
2379     |   ccmn TMP0, #-LJ_TCDATA, #4, ne
2380     |  beq ->vmeta_equal_cd
2381     |.endif
2382     |  cmp CARG1, CARG3
2383     |  bne >2
2384     |  // Tag and value are equal.
2385     if (vk) {
2386       |->BC_ISEQV_Z:
2387       |  mov PC, RB                     // Perform branch.
2388     }
2389     |1:
2390     |  ins_next
2391     |
2392     |2:  // Check if the tags are the same and it's a table or userdata.
2393     |  cmp ITYPE, TMP0
2394     |  ccmn ITYPE, #-LJ_TISTABUD, #2, eq
2395     if (vk) {
2396       |  bhi <1
2397     } else {
2398       |  bhi ->BC_ISEQV_Z               // Reuse code from opposite instruction.
2399     }
2400     |  // Different tables or userdatas. Need to check __eq metamethod.
2401     |  // Field metatable must be at same offset for GCtab and GCudata!
2402     |  and TAB:CARG2, CARG1, #LJ_GCVMASK
2403     |  ldr TAB:TMP2, TAB:CARG2->metatable
2404     if (vk) {
2405       |  cbz TAB:TMP2, <1               // No metatable?
2406       |  ldrb TMP1w, TAB:TMP2->nomm
2407       |   mov CARG4, #0                 // ne = 0
2408       |  tbnz TMP1w, #MM_eq, <1         // 'no __eq' flag set: done.
2409     } else {
2410       |  cbz TAB:TMP2, ->BC_ISEQV_Z     // No metatable?
2411       |  ldrb TMP1w, TAB:TMP2->nomm
2412       |   mov CARG4, #1                 // ne = 1.
2413       |  tbnz TMP1w, #MM_eq, ->BC_ISEQV_Z       // 'no __eq' flag set: done.
2414     }
2415     |  b ->vmeta_equal
2416     break;
2418   case BC_ISEQS: case BC_ISNES:
2419     vk = op == BC_ISEQS;
2420     |  // RA = src, RC = str_const (~), JMP with RC = target
2421     |  ldr CARG1, [BASE, RA, lsl #3]
2422     |   mvn RC, RC
2423     |    ldrh RBw, [PC, # OFS_RD]
2424     |   ldr CARG2, [KBASE, RC, lsl #3]
2425     |    add PC, PC, #4
2426     |   movn TMP0, #~LJ_TSTR
2427     |.if FFI
2428     |  asr ITYPE, CARG1, #47
2429     |.endif
2430     |    add RB, PC, RB, lsl #2
2431     |   add CARG2, CARG2, TMP0, lsl #47
2432     |    sub RB, RB, #0x20000
2433     |.if FFI
2434     |  cmn ITYPE, #-LJ_TCDATA
2435     |  beq ->vmeta_equal_cd
2436     |.endif
2437     |  cmp CARG1, CARG2
2438     if (vk) {
2439       |  csel PC, RB, PC, eq
2440     } else {
2441       |  csel PC, RB, PC, ne
2442     }
2443     |  ins_next
2444     break;
2446   case BC_ISEQN: case BC_ISNEN:
2447     vk = op == BC_ISEQN;
2448     |  // RA = src, RC = num_const (~), JMP with RC = target
2449     |  ldr CARG1, [BASE, RA, lsl #3]
2450     |   add RC, KBASE, RC, lsl #3
2451     |    ldrh RBw, [PC, # OFS_RD]
2452     |   ldr CARG3, [RC]
2453     |    add PC, PC, #4
2454     |    add RB, PC, RB, lsl #2
2455     |    sub RB, RB, #0x20000
2456     if (vk) {
2457       |->BC_ISEQN_Z:
2458     } else {
2459       |->BC_ISNEN_Z:
2460     }
2461     |  checkint CARG1, >4
2462     |   checkint CARG3, >6
2463     |  cmp CARG1w, CARG3w
2464     |1:
2465     if (vk) {
2466       |  csel PC, RB, PC, eq
2467       |2:
2468     } else {
2469       |2:
2470       |  csel PC, RB, PC, ne
2471     }
2472     |3:
2473     |  ins_next
2474     |
2475     |4:  // RA not int.
2476     |.if FFI
2477     |  blo >7
2478     |.else
2479     |  blo <2
2480     |.endif
2481     |    ldr FARG1, [BASE, RA, lsl #3]
2482     |    ldr FARG2, [RC]
2483     |   cmp TISNUMhi, CARG3, lsr #32
2484     |   bne >5
2485     |  // RA number, RC int.
2486     |  scvtf FARG2, CARG3w
2487     |5:
2488     |  // RA number, RC number.
2489     |  fcmp FARG1, FARG2
2490     |  b <1
2491     |
2492     |6:  // RA int, RC number
2493     |  ldr FARG2, [RC]
2494     |  scvtf FARG1, CARG1w
2495     |  fcmp FARG1, FARG2
2496     |  b <1
2497     |
2498     |.if FFI
2499     |7:
2500     |  asr ITYPE, CARG1, #47
2501     |  cmn ITYPE, #-LJ_TCDATA
2502     |  bne <2
2503     |  b ->vmeta_equal_cd
2504     |.endif
2505     break;
2507   case BC_ISEQP: case BC_ISNEP:
2508     vk = op == BC_ISEQP;
2509     |  // RA = src, RC = primitive_type (~), JMP with RC = target
2510     |  ldr TMP0, [BASE, RA, lsl #3]
2511     |   ldrh RBw, [PC, # OFS_RD]
2512     |   add PC, PC, #4
2513     |  add RC, RC, #1
2514     |   add RB, PC, RB, lsl #2
2515     |.if FFI
2516     |  asr ITYPE, TMP0, #47
2517     |  cmn ITYPE, #-LJ_TCDATA
2518     |  beq ->vmeta_equal_cd
2519     |  cmn RC, ITYPE
2520     |.else
2521     |  cmn RC, TMP0, asr #47
2522     |.endif
2523     |   sub RB, RB, #0x20000
2524     if (vk) {
2525       |  csel PC, RB, PC, eq
2526     } else {
2527       |  csel PC, RB, PC, ne
2528     }
2529     |  ins_next
2530     break;
2532   /* -- Unary test and copy ops ------------------------------------------- */
2534   case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF:
2535     |  // RA = dst or unused, RC = src, JMP with RC = target
2536     |   ldrh RBw, [PC, # OFS_RD]
2537     |  ldr TMP0, [BASE, RC, lsl #3]
2538     |   add PC, PC, #4
2539     |  mov_false TMP1
2540     |   add RB, PC, RB, lsl #2
2541     |  cmp TMP0, TMP1
2542     |   sub RB, RB, #0x20000
2543     if (op == BC_ISTC || op == BC_IST) {
2544       if (op == BC_ISTC) {
2545         |  csel RA, RA, RC, lo
2546       }
2547       |  csel PC, RB, PC, lo
2548     } else {
2549       if (op == BC_ISFC) {
2550         |  csel RA, RA, RC, hs
2551       }
2552       |  csel PC, RB, PC, hs
2553     }
2554     if (op == BC_ISTC || op == BC_ISFC) {
2555       |  str TMP0, [BASE, RA, lsl #3]
2556     }
2557     |  ins_next
2558     break;
2560   case BC_ISTYPE:
2561     |  // RA = src, RC = -type
2562     |  ldr TMP0, [BASE, RA, lsl #3]
2563     |  cmn RC, TMP0, asr #47
2564     |  bne ->vmeta_istype
2565     |  ins_next
2566     break;
2567   case BC_ISNUM:
2568     |  // RA = src, RC = -(TISNUM-1)
2569     |  ldr TMP0, [BASE, RA]
2570     |  checknum TMP0, ->vmeta_istype
2571     |  ins_next
2572     break;
2574   /* -- Unary ops --------------------------------------------------------- */
2576   case BC_MOV:
2577     |  // RA = dst, RC = src
2578     |  ldr TMP0, [BASE, RC, lsl #3]
2579     |  str TMP0, [BASE, RA, lsl #3]
2580     |  ins_next
2581     break;
2582   case BC_NOT:
2583     |  // RA = dst, RC = src
2584     |  ldr TMP0, [BASE, RC, lsl #3]
2585     |   mov_false TMP1
2586     |   mov_true TMP2
2587     |  cmp TMP0, TMP1
2588     |  csel TMP0, TMP1, TMP2, lo
2589     |  str TMP0, [BASE, RA, lsl #3]
2590     |  ins_next
2591     break;
2592   case BC_UNM:
2593     |  // RA = dst, RC = src
2594     |  ldr TMP0, [BASE, RC, lsl #3]
2595     |  asr ITYPE, TMP0, #47
2596     |  cmn ITYPE, #-LJ_TISNUM
2597     |  bhi ->vmeta_unm
2598     |  eor TMP0, TMP0, #U64x(80000000,00000000)
2599     |  bne >5
2600     |  negs TMP0w, TMP0w
2601     |   movz CARG3, #0x41e0, lsl #48    // 2^31.
2602     |   add_TISNUM TMP0, TMP0
2603     |  csel TMP0, TMP0, CARG3, vc
2604     |5:
2605     |  str TMP0, [BASE, RA, lsl #3]
2606     |  ins_next
2607     break;
2608   case BC_LEN:
2609     |  // RA = dst, RC = src
2610     |  ldr CARG1, [BASE, RC, lsl #3]
2611     |  asr ITYPE, CARG1, #47
2612     |  cmn ITYPE, #-LJ_TSTR
2613     |   and CARG1, CARG1, #LJ_GCVMASK
2614     |  bne >2
2615     |  ldr CARG1w, STR:CARG1->len
2616     |1:
2617     |  add_TISNUM CARG1, CARG1
2618     |  str CARG1, [BASE, RA, lsl #3]
2619     |  ins_next
2620     |
2621     |2:
2622     |  cmn ITYPE, #-LJ_TTAB
2623     |  bne ->vmeta_len
2624 #if LJ_52
2625     |  ldr TAB:CARG2, TAB:CARG1->metatable
2626     |  cbnz TAB:CARG2, >9
2627     |3:
2628 #endif
2629     |->BC_LEN_Z:
2630     |  bl extern lj_tab_len             // (GCtab *t)
2631     |  // Returns uint32_t (but less than 2^31).
2632     |  b <1
2633     |
2634 #if LJ_52
2635     |9:
2636     |  ldrb TMP1w, TAB:CARG2->nomm
2637     |  tbnz TMP1w, #MM_len, <3          // 'no __len' flag set: done.
2638     |  b ->vmeta_len
2639 #endif
2640     break;
2642   /* -- Binary ops -------------------------------------------------------- */
2644     |.macro ins_arithcheck_int, target
2645     |  checkint CARG1, target
2646     |  checkint CARG2, target
2647     |.endmacro
2648     |
2649     |.macro ins_arithcheck_num, target
2650     |  checknum CARG1, target
2651     |  checknum CARG2, target
2652     |.endmacro
2653     |
2654     |.macro ins_arithcheck_nzdiv, target
2655     |  cbz CARG2w, target
2656     |.endmacro
2657     |
2658     |.macro ins_arithhead
2659     ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN);
2660     ||if (vk == 1) {
2661     |   and RC, RC, #255
2662     |    decode_RB RB, INS
2663     ||} else {
2664     |   decode_RB RB, INS
2665     |    and RC, RC, #255
2666     ||}
2667     |.endmacro
2668     |
2669     |.macro ins_arithload, reg1, reg2
2670     |  // RA = dst, RB = src1, RC = src2 | num_const
2671     ||switch (vk) {
2672     ||case 0:
2673     |   ldr reg1, [BASE, RB, lsl #3]
2674     |    ldr reg2, [KBASE, RC, lsl #3]
2675     ||  break;
2676     ||case 1:
2677     |   ldr reg1, [KBASE, RC, lsl #3]
2678     |    ldr reg2, [BASE, RB, lsl #3]
2679     ||  break;
2680     ||default:
2681     |   ldr reg1, [BASE, RB, lsl #3]
2682     |    ldr reg2, [BASE, RC, lsl #3]
2683     ||  break;
2684     ||}
2685     |.endmacro
2686     |
2687     |.macro ins_arithfallback, ins
2688     ||switch (vk) {
2689     ||case 0:
2690     |   ins ->vmeta_arith_vn
2691     ||  break;
2692     ||case 1:
2693     |   ins ->vmeta_arith_nv
2694     ||  break;
2695     ||default:
2696     |   ins ->vmeta_arith_vv
2697     ||  break;
2698     ||}
2699     |.endmacro
2700     |
2701     |.macro ins_arithmod, res, reg1, reg2
2702     |  fdiv d2, reg1, reg2
2703     |  frintm d2, d2
2704     |  // Cannot use fmsub, because FMA is not enabled by default.
2705     |  fmul d2, d2, reg2
2706     |  fsub res, reg1, d2
2707     |.endmacro
2708     |
2709     |.macro ins_arithdn, intins, fpins
2710     |  ins_arithhead
2711     |  ins_arithload CARG1, CARG2
2712     |  ins_arithcheck_int >5
2713     |.if "intins" == "smull"
2714     |  smull CARG1, CARG1w, CARG2w
2715     |  cmp CARG1, CARG1, sxtw
2716     |   mov CARG1w, CARG1w
2717     |  ins_arithfallback bne
2718     |.elif "intins" == "ins_arithmodi"
2719     |  ins_arithfallback ins_arithcheck_nzdiv
2720     |  bl ->vm_modi
2721     |.else
2722     |  intins CARG1w, CARG1w, CARG2w
2723     |  ins_arithfallback bvs
2724     |.endif
2725     |  add_TISNUM CARG1, CARG1
2726     |  str CARG1, [BASE, RA, lsl #3]
2727     |4:
2728     |  ins_next
2729     |
2730     |5:  // FP variant.
2731     |  ins_arithload FARG1, FARG2
2732     |  ins_arithfallback ins_arithcheck_num
2733     |  fpins FARG1, FARG1, FARG2
2734     |  str FARG1, [BASE, RA, lsl #3]
2735     |  b <4
2736     |.endmacro
2737     |
2738     |.macro ins_arithfp, fpins
2739     |  ins_arithhead
2740     |  ins_arithload CARG1, CARG2
2741     |  ins_arithload FARG1, FARG2
2742     |  ins_arithfallback ins_arithcheck_num
2743     |.if "fpins" == "fpow"
2744     |  bl extern pow
2745     |.else
2746     |  fpins FARG1, FARG1, FARG2
2747     |.endif
2748     |  str FARG1, [BASE, RA, lsl #3]
2749     |  ins_next
2750     |.endmacro
2752   case BC_ADDVN: case BC_ADDNV: case BC_ADDVV:
2753     |  ins_arithdn adds, fadd
2754     break;
2755   case BC_SUBVN: case BC_SUBNV: case BC_SUBVV:
2756     |  ins_arithdn subs, fsub
2757     break;
2758   case BC_MULVN: case BC_MULNV: case BC_MULVV:
2759     |  ins_arithdn smull, fmul
2760     break;
2761   case BC_DIVVN: case BC_DIVNV: case BC_DIVVV:
2762     |  ins_arithfp fdiv
2763     break;
2764   case BC_MODVN: case BC_MODNV: case BC_MODVV:
2765     |  ins_arithdn ins_arithmodi, ins_arithmod
2766     break;
2767   case BC_POW:
2768     |  // NYI: (partial) integer arithmetic.
2769     |  ins_arithfp fpow
2770     break;
2772   case BC_CAT:
2773     |  decode_RB RB, INS
2774     |   and RC, RC, #255
2775     |  // RA = dst, RB = src_start, RC = src_end
2776     |   str BASE, L->base
2777     |  sub CARG3, RC, RB
2778     |  add CARG2, BASE, RC, lsl #3
2779     |->BC_CAT_Z:
2780     |  // RA = dst, CARG2 = top-1, CARG3 = left
2781     |  mov CARG1, L
2782     |   str PC, SAVE_PC
2783     |  bl extern lj_meta_cat            // (lua_State *L, TValue *top, int left)
2784     |  // Returns NULL (finished) or TValue * (metamethod).
2785     |  ldrb RBw, [PC, #-4+OFS_RB]
2786     |   ldr BASE, L->base
2787     |   cbnz CRET1, ->vmeta_binop
2788     |  ldr TMP0, [BASE, RB, lsl #3]
2789     |  str TMP0, [BASE, RA, lsl #3]     // Copy result to RA.
2790     |  ins_next
2791     break;
2793   /* -- Constant ops ------------------------------------------------------ */
2795   case BC_KSTR:
2796     |  // RA = dst, RC = str_const (~)
2797     |  mvn RC, RC
2798     |  ldr TMP0, [KBASE, RC, lsl #3]
2799     |   movn TMP1, #~LJ_TSTR
2800     |  add TMP0, TMP0, TMP1, lsl #47
2801     |  str TMP0, [BASE, RA, lsl #3]
2802     |  ins_next
2803     break;
2804   case BC_KCDATA:
2805     |.if FFI
2806     |  // RA = dst, RC = cdata_const (~)
2807     |  mvn RC, RC
2808     |  ldr TMP0, [KBASE, RC, lsl #3]
2809     |   movn TMP1, #~LJ_TCDATA
2810     |  add TMP0, TMP0, TMP1, lsl #47
2811     |  str TMP0, [BASE, RA, lsl #3]
2812     |  ins_next
2813     |.endif
2814     break;
2815   case BC_KSHORT:
2816     |  // RA = dst, RC = int16_literal
2817     |  sxth RCw, RCw
2818     |  add_TISNUM TMP0, RC
2819     |  str TMP0, [BASE, RA, lsl #3]
2820     |  ins_next
2821     break;
2822   case BC_KNUM:
2823     |  // RA = dst, RC = num_const
2824     |  ldr TMP0, [KBASE, RC, lsl #3]
2825     |  str TMP0, [BASE, RA, lsl #3]
2826     |  ins_next
2827     break;
2828   case BC_KPRI:
2829     |  // RA = dst, RC = primitive_type (~)
2830     |  mvn TMP0, RC, lsl #47
2831     |  str TMP0, [BASE, RA, lsl #3]
2832     |  ins_next
2833     break;
2834   case BC_KNIL:
2835     |  // RA = base, RC = end
2836     |  add RA, BASE, RA, lsl #3
2837     |   add RC, BASE, RC, lsl #3
2838     |  str TISNIL, [RA], #8
2839     |1:
2840     |   cmp RA, RC
2841     |  str TISNIL, [RA], #8
2842     |   blt <1
2843     |  ins_next_
2844     break;
2846   /* -- Upvalue and function ops ------------------------------------------ */
2848   case BC_UGET:
2849     |  // RA = dst, RC = uvnum
2850     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2851     |   add RC, RC, #offsetof(GCfuncL, uvptr)/8
2852     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2853     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RC, lsl #3]
2854     |  ldr CARG2, UPVAL:CARG2->v
2855     |  ldr TMP0, [CARG2]
2856     |  str TMP0, [BASE, RA, lsl #3]
2857     |  ins_next
2858     break;
2859   case BC_USETV:
2860     |  // RA = uvnum, RC = src
2861     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2862     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2863     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2864     |  ldr UPVAL:CARG1, [LFUNC:CARG2, RA, lsl #3]
2865     |   ldr CARG3, [BASE, RC, lsl #3]
2866     |    ldr CARG2, UPVAL:CARG1->v
2867     |  ldrb TMP2w, UPVAL:CARG1->marked
2868     |  ldrb TMP0w, UPVAL:CARG1->closed
2869     |    asr ITYPE, CARG3, #47
2870     |   str CARG3, [CARG2]
2871     |    add ITYPE, ITYPE, #-LJ_TISGCV
2872     |  tst TMP2w, #LJ_GC_BLACK          // isblack(uv)
2873     |  ccmp TMP0w, #0, #4, ne           // && uv->closed
2874     |    ccmn ITYPE, #-(LJ_TNUMX - LJ_TISGCV), #0, ne   // && tvisgcv(v)
2875     |  bhi >2
2876     |1:
2877     |  ins_next
2878     |
2879     |2:  // Check if new value is white.
2880     |  and GCOBJ:CARG3, CARG3, #LJ_GCVMASK
2881     |  ldrb TMP1w, GCOBJ:CARG3->gch.marked
2882     |  tst TMP1w, #LJ_GC_WHITES         // iswhite(str)
2883     |  beq <1
2884     |  // Crossed a write barrier. Move the barrier forward.
2885     |  mov CARG1, GL
2886     |  bl extern lj_gc_barrieruv        // (global_State *g, TValue *tv)
2887     |  b <1
2888     break;
2889   case BC_USETS:
2890     |  // RA = uvnum, RC = str_const (~)
2891     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2892     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2893     |    mvn RC, RC
2894     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2895     |  ldr UPVAL:CARG1, [LFUNC:CARG2, RA, lsl #3]
2896     |   ldr STR:CARG3, [KBASE, RC, lsl #3]
2897     |   movn TMP0, #~LJ_TSTR
2898     |    ldr CARG2, UPVAL:CARG1->v
2899     |  ldrb TMP2w, UPVAL:CARG1->marked
2900     |   add TMP0, STR:CARG3, TMP0, lsl #47
2901     |    ldrb TMP1w, STR:CARG3->marked
2902     |   str TMP0, [CARG2]
2903     |  tbnz TMP2w, #2, >2               // isblack(uv)
2904     |1:
2905     |  ins_next
2906     |
2907     |2:  // Check if string is white and ensure upvalue is closed.
2908     |  ldrb TMP0w, UPVAL:CARG1->closed
2909     |    tst TMP1w, #LJ_GC_WHITES       // iswhite(str)
2910     |  ccmp TMP0w, #0, #4, ne
2911     |  beq <1
2912     |  // Crossed a write barrier. Move the barrier forward.
2913     |  mov CARG1, GL
2914     |  bl extern lj_gc_barrieruv        // (global_State *g, TValue *tv)
2915     |  b <1
2916     break;
2917   case BC_USETN:
2918     |  // RA = uvnum, RC = num_const
2919     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2920     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2921     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2922     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA, lsl #3]
2923     |   ldr TMP0, [KBASE, RC, lsl #3]
2924     |  ldr CARG2, UPVAL:CARG2->v
2925     |   str TMP0, [CARG2]
2926     |  ins_next
2927     break;
2928   case BC_USETP:
2929     |  // RA = uvnum, RC = primitive_type (~)
2930     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2931     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2932     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2933     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA, lsl #3]
2934     |   mvn TMP0, RC, lsl #47
2935     |  ldr CARG2, UPVAL:CARG2->v
2936     |   str TMP0, [CARG2]
2937     |  ins_next
2938     break;
2940   case BC_UCLO:
2941     |  // RA = level, RC = target
2942     |  ldr CARG3, L->openupval
2943     |   add RC, PC, RC, lsl #2
2944     |    str BASE, L->base
2945     |   sub PC, RC, #0x20000
2946     |  cbz CARG3, >1
2947     |  mov CARG1, L
2948     |  add CARG2, BASE, RA, lsl #3
2949     |  bl extern lj_func_closeuv        // (lua_State *L, TValue *level)
2950     |  ldr BASE, L->base
2951     |1:
2952     |  ins_next
2953     break;
2955   case BC_FNEW:
2956     |  // RA = dst, RC = proto_const (~) (holding function prototype)
2957     |  mvn RC, RC
2958     |   str BASE, L->base
2959     |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
2960     |    str PC, SAVE_PC
2961     |   ldr CARG2, [KBASE, RC, lsl #3]
2962     |    mov CARG1, L
2963     |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2964     |  // (lua_State *L, GCproto *pt, GCfuncL *parent)
2965     |  bl extern lj_func_newL_gc
2966     |  // Returns GCfuncL *.
2967     |  ldr BASE, L->base
2968     |   movn TMP0, #~LJ_TFUNC
2969     |   add CRET1, CRET1, TMP0, lsl #47
2970     |  str CRET1, [BASE, RA, lsl #3]
2971     |  ins_next
2972     break;
2974   /* -- Table ops --------------------------------------------------------- */
2976   case BC_TNEW:
2977   case BC_TDUP:
2978     |  // RA = dst, RC = (hbits|asize) | tab_const (~)
2979     |  ldp CARG3, CARG4, GL->gc.total   // Assumes threshold follows total.
2980     |   str BASE, L->base
2981     |   str PC, SAVE_PC
2982     |   mov CARG1, L
2983     |  cmp CARG3, CARG4
2984     |  bhs >5
2985     |1:
2986     if (op == BC_TNEW) {
2987       |  and CARG2, RC, #0x7ff
2988       |   lsr CARG3, RC, #11
2989       |  cmp CARG2, #0x7ff
2990       |  mov TMP0, #0x801
2991       |  csel CARG2, CARG2, TMP0, ne
2992       |  bl extern lj_tab_new  // (lua_State *L, int32_t asize, uint32_t hbits)
2993       |  // Returns GCtab *.
2994     } else {
2995       |  mvn RC, RC
2996       |  ldr CARG2, [KBASE, RC, lsl #3]
2997       |  bl extern lj_tab_dup  // (lua_State *L, Table *kt)
2998       |  // Returns GCtab *.
2999     }
3000     |  ldr BASE, L->base
3001     |   movk CRET1, #(LJ_TTAB>>1)&0xffff, lsl #48
3002     |  str CRET1, [BASE, RA, lsl #3]
3003     |  ins_next
3004     |
3005     |5:
3006     |  bl extern lj_gc_step_fixtop  // (lua_State *L)
3007     |  mov CARG1, L
3008     |  b <1
3009     break;
3011   case BC_GGET:
3012     |  // RA = dst, RC = str_const (~)
3013   case BC_GSET:
3014     |  // RA = src, RC = str_const (~)
3015     |  ldr LFUNC:CARG1, [BASE, FRAME_FUNC]
3016     |   mvn RC, RC
3017     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3018     |  ldr TAB:CARG2, LFUNC:CARG1->env
3019     |   ldr STR:RC, [KBASE, RC, lsl #3]
3020     if (op == BC_GGET) {
3021       |  b ->BC_TGETS_Z
3022     } else {
3023       |  b ->BC_TSETS_Z
3024     }
3025     break;
3027   case BC_TGETV:
3028     |  decode_RB RB, INS
3029     |   and RC, RC, #255
3030     |  // RA = dst, RB = table, RC = key
3031     |  ldr CARG2, [BASE, RB, lsl #3]
3032     |   ldr TMP1, [BASE, RC, lsl #3]
3033     |  checktab CARG2, ->vmeta_tgetv
3034     |  checkint TMP1, >9                // Integer key?
3035     |  ldr CARG3, TAB:CARG2->array
3036     |   ldr CARG1w, TAB:CARG2->asize
3037     |  add CARG3, CARG3, TMP1, uxtw #3
3038     |   cmp TMP1w, CARG1w               // In array part?
3039     |   bhs ->vmeta_tgetv
3040     |  ldr TMP0, [CARG3]
3041     |  cmp_nil TMP0
3042     |  beq >5
3043     |1:
3044     |  str TMP0, [BASE, RA, lsl #3]
3045     |  ins_next
3046     |
3047     |5:  // Check for __index if table value is nil.
3048     |  ldr TAB:CARG1, TAB:CARG2->metatable
3049     |  cbz TAB:CARG1, <1                // No metatable: done.
3050     |  ldrb TMP1w, TAB:CARG1->nomm
3051     |  tbnz TMP1w, #MM_index, <1        // 'no __index' flag set: done.
3052     |  b ->vmeta_tgetv
3053     |
3054     |9:
3055     |  asr ITYPE, TMP1, #47
3056     |  cmn ITYPE, #-LJ_TSTR             // String key?
3057     |  bne ->vmeta_tgetv
3058     |   and STR:RC, TMP1, #LJ_GCVMASK
3059     |  b ->BC_TGETS_Z
3060     break;
3061   case BC_TGETS:
3062     |  decode_RB RB, INS
3063     |   and RC, RC, #255
3064     |  // RA = dst, RB = table, RC = str_const (~)
3065     |  ldr CARG2, [BASE, RB, lsl #3]
3066     |   mvn RC, RC
3067     |   ldr STR:RC, [KBASE, RC, lsl #3]
3068     |  checktab CARG2, ->vmeta_tgets1
3069     |->BC_TGETS_Z:
3070     |  // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = dst
3071     |  ldr TMP1w, TAB:CARG2->hmask
3072     |   ldr TMP2w, STR:RC->sid
3073     |    ldr NODE:CARG3, TAB:CARG2->node
3074     |  and TMP1w, TMP1w, TMP2w          // idx = str->sid & tab->hmask
3075     |  add TMP1, TMP1, TMP1, lsl #1
3076     |  movn CARG4, #~LJ_TSTR
3077     |    add NODE:CARG3, NODE:CARG3, TMP1, lsl #3  // node = tab->node + idx*3*8
3078     |  add CARG4, STR:RC, CARG4, lsl #47        // Tagged key to look for.
3079     |1:
3080     |  ldp TMP0, CARG1, NODE:CARG3->val
3081     |   ldr NODE:CARG3, NODE:CARG3->next
3082     |  cmp CARG1, CARG4
3083     |  bne >4
3084     |  cmp_nil TMP0
3085     |  beq >5
3086     |3:
3087     |  str TMP0, [BASE, RA, lsl #3]
3088     |  ins_next
3089     |
3090     |4:  // Follow hash chain.
3091     |  cbnz NODE:CARG3, <1
3092     |  // End of hash chain: key not found, nil result.
3093     |   mov_nil TMP0
3094     |
3095     |5:  // Check for __index if table value is nil.
3096     |  ldr TAB:CARG1, TAB:CARG2->metatable
3097     |  cbz TAB:CARG1, <3                // No metatable: done.
3098     |  ldrb TMP1w, TAB:CARG1->nomm
3099     |  tbnz TMP1w, #MM_index, <3        // 'no __index' flag set: done.
3100     |  b ->vmeta_tgets
3101     break;
3102   case BC_TGETB:
3103     |  decode_RB RB, INS
3104     |   and RC, RC, #255
3105     |  // RA = dst, RB = table, RC = index
3106     |  ldr CARG2, [BASE, RB, lsl #3]
3107     |  checktab CARG2, ->vmeta_tgetb
3108     |  ldr CARG3, TAB:CARG2->array
3109     |   ldr CARG1w, TAB:CARG2->asize
3110     |  add CARG3, CARG3, RC, lsl #3
3111     |   cmp RCw, CARG1w                 // In array part?
3112     |   bhs ->vmeta_tgetb
3113     |  ldr TMP0, [CARG3]
3114     |  cmp_nil TMP0
3115     |  beq >5
3116     |1:
3117     |  str TMP0, [BASE, RA, lsl #3]
3118     |  ins_next
3119     |
3120     |5:  // Check for __index if table value is nil.
3121     |  ldr TAB:CARG1, TAB:CARG2->metatable
3122     |  cbz TAB:CARG1, <1                // No metatable: done.
3123     |  ldrb TMP1w, TAB:CARG1->nomm
3124     |  tbnz TMP1w, #MM_index, <1        // 'no __index' flag set: done.
3125     |  b ->vmeta_tgetb
3126     break;
3127   case BC_TGETR:
3128     |  decode_RB RB, INS
3129     |   and RC, RC, #255
3130     |  // RA = dst, RB = table, RC = key
3131     |  ldr CARG1, [BASE, RB, lsl #3]
3132     |   ldr TMP1, [BASE, RC, lsl #3]
3133     |  and TAB:CARG1, CARG1, #LJ_GCVMASK
3134     |  ldr CARG3, TAB:CARG1->array
3135     |   ldr TMP2w, TAB:CARG1->asize
3136     |  add CARG3, CARG3, TMP1w, uxtw #3
3137     |   cmp TMP1w, TMP2w                // In array part?
3138     |   bhs ->vmeta_tgetr
3139     |  ldr TMP0, [CARG3]
3140     |->BC_TGETR_Z:
3141     |  str TMP0, [BASE, RA, lsl #3]
3142     |  ins_next
3143     break;
3145   case BC_TSETV:
3146     |  decode_RB RB, INS
3147     |   and RC, RC, #255
3148     |  // RA = src, RB = table, RC = key
3149     |  ldr CARG2, [BASE, RB, lsl #3]
3150     |   ldr TMP1, [BASE, RC, lsl #3]
3151     |  checktab CARG2, ->vmeta_tsetv
3152     |  checkint TMP1, >9                // Integer key?
3153     |  ldr CARG3, TAB:CARG2->array
3154     |   ldr CARG1w, TAB:CARG2->asize
3155     |  add CARG3, CARG3, TMP1, uxtw #3
3156     |   cmp TMP1w, CARG1w               // In array part?
3157     |   bhs ->vmeta_tsetv
3158     |  ldr TMP1, [CARG3]
3159     |   ldr TMP0, [BASE, RA, lsl #3]
3160     |    ldrb TMP2w, TAB:CARG2->marked
3161     |  cmp_nil TMP1                     // Previous value is nil?
3162     |  beq >5
3163     |1:
3164     |   str TMP0, [CARG3]
3165     |    tbnz TMP2w, #2, >7             // isblack(table)
3166     |2:
3167     |   ins_next
3168     |
3169     |5:  // Check for __newindex if previous value is nil.
3170     |  ldr TAB:CARG1, TAB:CARG2->metatable
3171     |  cbz TAB:CARG1, <1                // No metatable: done.
3172     |  ldrb TMP1w, TAB:CARG1->nomm
3173     |  tbnz TMP1w, #MM_newindex, <1     // 'no __newindex' flag set: done.
3174     |  b ->vmeta_tsetv
3175     |
3176     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3177     |  barrierback TAB:CARG2, TMP2w, TMP1
3178     |  b <2
3179     |
3180     |9:
3181     |  asr ITYPE, TMP1, #47
3182     |  cmn ITYPE, #-LJ_TSTR             // String key?
3183     |  bne ->vmeta_tsetv
3184     |   and STR:RC, TMP1, #LJ_GCVMASK
3185     |  b ->BC_TSETS_Z
3186     break;
3187   case BC_TSETS:
3188     |  decode_RB RB, INS
3189     |   and RC, RC, #255
3190     |  // RA = dst, RB = table, RC = str_const (~)
3191     |  ldr CARG2, [BASE, RB, lsl #3]
3192     |   mvn RC, RC
3193     |   ldr STR:RC, [KBASE, RC, lsl #3]
3194     |  checktab CARG2, ->vmeta_tsets1
3195     |->BC_TSETS_Z:
3196     |  // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = src
3197     |  ldr TMP1w, TAB:CARG2->hmask
3198     |   ldr TMP2w, STR:RC->sid
3199     |    ldr NODE:CARG3, TAB:CARG2->node
3200     |  and TMP1w, TMP1w, TMP2w          // idx = str->sid & tab->hmask
3201     |  add TMP1, TMP1, TMP1, lsl #1
3202     |  movn CARG4, #~LJ_TSTR
3203     |    add NODE:CARG3, NODE:CARG3, TMP1, lsl #3  // node = tab->node + idx*3*8
3204     |  add CARG4, STR:RC, CARG4, lsl #47        // Tagged key to look for.
3205     |   strb wzr, TAB:CARG2->nomm       // Clear metamethod cache.
3206     |1:
3207     |  ldp TMP1, CARG1, NODE:CARG3->val
3208     |   ldr NODE:TMP3, NODE:CARG3->next
3209     |    ldrb TMP2w, TAB:CARG2->marked
3210     |  cmp CARG1, CARG4
3211     |  bne >5
3212     |   ldr TMP0, [BASE, RA, lsl #3]
3213     |  cmp_nil TMP1                     // Previous value is nil?
3214     |  beq >4
3215     |2:
3216     |   str TMP0, NODE:CARG3->val
3217     |    tbnz TMP2w, #2, >7             // isblack(table)
3218     |3:
3219     |  ins_next
3220     |
3221     |4:  // Check for __newindex if previous value is nil.
3222     |  ldr TAB:CARG1, TAB:CARG2->metatable
3223     |  cbz TAB:CARG1, <2                // No metatable: done.
3224     |  ldrb TMP1w, TAB:CARG1->nomm
3225     |  tbnz TMP1w, #MM_newindex, <2     // 'no __newindex' flag set: done.
3226     |  b ->vmeta_tsets
3227     |
3228     |5:  // Follow hash chain.
3229     |  mov NODE:CARG3, NODE:TMP3
3230     |  cbnz NODE:TMP3, <1
3231     |  // End of hash chain: key not found, add a new one.
3232     |
3233     |  // But check for __newindex first.
3234     |  ldr TAB:CARG1, TAB:CARG2->metatable
3235     |  cbz TAB:CARG1, >6                // No metatable: continue.
3236     |  ldrb TMP1w, TAB:CARG1->nomm
3237     |  // 'no __newindex' flag NOT set: check.
3238     |  tbz TMP1w, #MM_newindex, ->vmeta_tsets
3239     |6:
3240     |  movn TMP1, #~LJ_TSTR
3241     |   str PC, SAVE_PC
3242     |  add TMP0, STR:RC, TMP1, lsl #47
3243     |   str BASE, L->base
3244     |   mov CARG1, L
3245     |  str TMP0, TMPD
3246     |   add CARG3, sp, TMPDofs
3247     |  bl extern lj_tab_newkey          // (lua_State *L, GCtab *t, TValue *k)
3248     |  // Returns TValue *.
3249     |  ldr BASE, L->base
3250     |  ldr TMP0, [BASE, RA, lsl #3]
3251     |  str TMP0, [CRET1]
3252     |  b <3                             // No 2nd write barrier needed.
3253     |
3254     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3255     |  barrierback TAB:CARG2, TMP2w, TMP1
3256     |  b <3
3257     break;
3258   case BC_TSETB:
3259     |  decode_RB RB, INS
3260     |   and RC, RC, #255
3261     |  // RA = src, RB = table, RC = index
3262     |  ldr CARG2, [BASE, RB, lsl #3]
3263     |  checktab CARG2, ->vmeta_tsetb
3264     |  ldr CARG3, TAB:CARG2->array
3265     |   ldr CARG1w, TAB:CARG2->asize
3266     |  add CARG3, CARG3, RC, lsl #3
3267     |   cmp RCw, CARG1w                 // In array part?
3268     |   bhs ->vmeta_tsetb
3269     |  ldr TMP1, [CARG3]
3270     |   ldr TMP0, [BASE, RA, lsl #3]
3271     |    ldrb TMP2w, TAB:CARG2->marked
3272     |  cmp_nil TMP1                     // Previous value is nil?
3273     |  beq >5
3274     |1:
3275     |   str TMP0, [CARG3]
3276     |    tbnz TMP2w, #2, >7             // isblack(table)
3277     |2:
3278     |   ins_next
3279     |
3280     |5:  // Check for __newindex if previous value is nil.
3281     |  ldr TAB:CARG1, TAB:CARG2->metatable
3282     |  cbz TAB:CARG1, <1                // No metatable: done.
3283     |  ldrb TMP1w, TAB:CARG1->nomm
3284     |  tbnz TMP1w, #MM_newindex, <1     // 'no __newindex' flag set: done.
3285     |  b ->vmeta_tsetb
3286     |
3287     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3288     |  barrierback TAB:CARG2, TMP2w, TMP1
3289     |  b <2
3290     break;
3291   case BC_TSETR:
3292     |  decode_RB RB, INS
3293     |   and RC, RC, #255
3294     |  // RA = src, RB = table, RC = key
3295     |  ldr CARG2, [BASE, RB, lsl #3]
3296     |   ldr TMP1, [BASE, RC, lsl #3]
3297     |  and TAB:CARG2, CARG2, #LJ_GCVMASK
3298     |  ldr CARG1, TAB:CARG2->array
3299     |    ldrb TMP2w, TAB:CARG2->marked
3300     |   ldr CARG4w, TAB:CARG2->asize
3301     |  add CARG1, CARG1, TMP1, uxtw #3
3302     |    tbnz TMP2w, #2, >7             // isblack(table)
3303     |2:
3304     |   cmp TMP1w, CARG4w               // In array part?
3305     |   bhs ->vmeta_tsetr
3306     |->BC_TSETR_Z:
3307     |   ldr TMP0, [BASE, RA, lsl #3]
3308     |   str TMP0, [CARG1]
3309     |   ins_next
3310     |
3311     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3312     |  barrierback TAB:CARG2, TMP2w, TMP0
3313     |  b <2
3314     break;
3316   case BC_TSETM:
3317     |  // RA = base (table at base-1), RC = num_const (start index)
3318     |  add RA, BASE, RA, lsl #3
3319     |1:
3320     |   ldr RBw, SAVE_MULTRES
3321     |  ldr TAB:CARG2, [RA, #-8]         // Guaranteed to be a table.
3322     |   ldr TMP1, [KBASE, RC, lsl #3]   // Integer constant is in lo-word.
3323     |    sub RB, RB, #8
3324     |    cbz RB, >4                     // Nothing to copy?
3325     |  and TAB:CARG2, CARG2, #LJ_GCVMASK
3326     |  ldr CARG1w, TAB:CARG2->asize
3327     |   add CARG3w, TMP1w, RBw, lsr #3
3328     |   ldr CARG4, TAB:CARG2->array
3329     |  cmp CARG3, CARG1
3330     |    add RB, RA, RB
3331     |  bhi >5
3332     |   add TMP1, CARG4, TMP1w, uxtw #3
3333     |    ldrb TMP2w, TAB:CARG2->marked
3334     |3:  // Copy result slots to table.
3335     |   ldr TMP0, [RA], #8
3336     |   str TMP0, [TMP1], #8
3337     |  cmp RA, RB
3338     |  blo <3
3339     |    tbnz TMP2w, #2, >7             // isblack(table)
3340     |4:
3341     |  ins_next
3342     |
3343     |5:  // Need to resize array part.
3344     |   str BASE, L->base
3345     |  mov CARG1, L
3346     |   str PC, SAVE_PC
3347     |  bl extern lj_tab_reasize         // (lua_State *L, GCtab *t, int nasize)
3348     |  // Must not reallocate the stack.
3349     |  b <1
3350     |
3351     |7:  // Possible table write barrier for any value. Skip valiswhite check.
3352     |  barrierback TAB:CARG2, TMP2w, TMP1
3353     |  b <4
3354     break;
3356   /* -- Calls and vararg handling ----------------------------------------- */
3358   case BC_CALLM:
3359     |  // RA = base, (RB = nresults+1,) RC = extra_nargs
3360     |  ldr TMP0w, SAVE_MULTRES
3361     |  decode_RC8RD NARGS8:RC, RC
3362     |  add NARGS8:RC, NARGS8:RC, TMP0
3363     |  b ->BC_CALL_Z
3364     break;
3365   case BC_CALL:
3366     |  decode_RC8RD NARGS8:RC, RC
3367     |  // RA = base, (RB = nresults+1,) RC = (nargs+1)*8
3368     |->BC_CALL_Z:
3369     |  mov RB, BASE                     // Save old BASE for vmeta_call.
3370     |  add BASE, BASE, RA, lsl #3
3371     |  ldr CARG3, [BASE], #16
3372     |   sub NARGS8:RC, NARGS8:RC, #8
3373     |  checkfunc CARG3, ->vmeta_call
3374     |  ins_call
3375     break;
3377   case BC_CALLMT:
3378     |  // RA = base, (RB = 0,) RC = extra_nargs
3379     |  ldr TMP0w, SAVE_MULTRES
3380     |  add NARGS8:RC, TMP0, RC, lsl #3
3381     |  b ->BC_CALLT1_Z
3382     break;
3383   case BC_CALLT:
3384     |  lsl NARGS8:RC, RC, #3
3385     |  // RA = base, (RB = 0,) RC = (nargs+1)*8
3386     |->BC_CALLT1_Z:
3387     |  add RA, BASE, RA, lsl #3
3388     |  ldr TMP1, [RA], #16
3389     |   sub NARGS8:RC, NARGS8:RC, #8
3390     |  checktp CARG3, TMP1, LJ_TFUNC, ->vmeta_callt
3391     |  ldr PC, [BASE, FRAME_PC]
3392     |->BC_CALLT2_Z:
3393     |   mov RB, #0
3394     |   ldrb TMP2w, LFUNC:CARG3->ffid
3395     |  tst PC, #FRAME_TYPE
3396     |  bne >7
3397     |1:
3398     |  str TMP1, [BASE, FRAME_FUNC]     // Copy function down, but keep PC.
3399     |  cbz NARGS8:RC, >3
3400     |2:
3401     |  ldr TMP0, [RA, RB]
3402     |   add TMP1, RB, #8
3403     |   cmp TMP1, NARGS8:RC
3404     |  str TMP0, [BASE, RB]
3405     |    mov RB, TMP1
3406     |   bne <2
3407     |3:
3408     |  cmp TMP2, #1                     // (> FF_C) Calling a fast function?
3409     |  bhi >5
3410     |4:
3411     |  ins_callt
3412     |
3413     |5:  // Tailcall to a fast function with a Lua frame below.
3414     |  ldrb RAw, [PC, #-4+OFS_RA]
3415     |  sub CARG1, BASE, RA, lsl #3
3416     |  ldr LFUNC:CARG1, [CARG1, #-32]
3417     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3418     |  ldr CARG1, LFUNC:CARG1->pc
3419     |  ldr KBASE, [CARG1, #PC2PROTO(k)]
3420     |  b <4
3421     |
3422     |7:  // Tailcall from a vararg function.
3423     |  eor PC, PC, #FRAME_VARG
3424     |  tst PC, #FRAME_TYPEP             // Vararg frame below?
3425     |  csel TMP2, RB, TMP2, ne          // Clear ffid if no Lua function below.
3426     |  bne <1
3427     |  sub BASE, BASE, PC
3428     |  ldr PC, [BASE, FRAME_PC]
3429     |  tst PC, #FRAME_TYPE
3430     |  csel TMP2, RB, TMP2, ne          // Clear ffid if no Lua function below.
3431     |  b <1
3432     break;
3434   case BC_ITERC:
3435     |  // RA = base, (RB = nresults+1, RC = nargs+1 (2+1))
3436     |  add RA, BASE, RA, lsl #3
3437     |  ldr CARG3, [RA, #-24]
3438     |    mov RB, BASE                   // Save old BASE for vmeta_call.
3439     |   ldp CARG1, CARG2, [RA, #-16]
3440     |    add BASE, RA, #16
3441     |    mov NARGS8:RC, #16             // Iterators get 2 arguments.
3442     |  str CARG3, [RA]                  // Copy callable.
3443     |   stp CARG1, CARG2, [RA, #16]     // Copy state and control var.
3444     |  checkfunc CARG3, ->vmeta_call
3445     |  ins_call
3446     break;
3448   case BC_ITERN:
3449     |.if JIT
3450     |  hotloop
3451     |.endif
3452     |->vm_IITERN:
3453     |  // RA = base, (RB = nresults+1, RC = nargs+1 (2+1))
3454     |  add RA, BASE, RA, lsl #3
3455     |  ldr TAB:RB, [RA, #-16]
3456     |    ldrh TMP3w, [PC, # OFS_RD]
3457     |  ldr CARG1w, [RA, #-8+LO]         // Get index from control var.
3458     |    add PC, PC, #4
3459     |    add TMP3, PC, TMP3, lsl #2
3460     |  and TAB:RB, RB, #LJ_GCVMASK
3461     |    sub TMP3, TMP3, #0x20000
3462     |  ldr TMP1w, TAB:RB->asize
3463     |   ldr CARG2, TAB:RB->array
3464     |1:  // Traverse array part.
3465     |  subs RC, CARG1, TMP1
3466     |   add CARG3, CARG2, CARG1, lsl #3
3467     |  bhs >5                           // Index points after array part?
3468     |   ldr TMP0, [CARG3]
3469     |   cmp_nil TMP0
3470     |   cinc CARG1, CARG1, eq           // Skip holes in array part.
3471     |   beq <1
3472     |   add_TISNUM CARG1, CARG1
3473     |   stp CARG1, TMP0, [RA]
3474     |    add CARG1, CARG1, #1
3475     |3:
3476     |    str CARG1w, [RA, #-8+LO]       // Update control var.
3477     |  mov PC, TMP3
3478     |4:
3479     |  ins_next
3480     |
3481     |5:  // Traverse hash part.
3482     |  ldr TMP2w, TAB:RB->hmask
3483     |   ldr NODE:RB, TAB:RB->node
3484     |6:
3485     |   add CARG1, RC, RC, lsl #1
3486     |  cmp RC, TMP2                     // End of iteration? Branch to ITERN+1.
3487     |   add NODE:CARG3, NODE:RB, CARG1, lsl #3  // node = tab->node + idx*3*8
3488     |  bhi <4
3489     |  ldp TMP0, CARG1, NODE:CARG3->val
3490     |  cmp_nil TMP0
3491     |   add RC, RC, #1
3492     |  beq <6                           // Skip holes in hash part.
3493     |  stp CARG1, TMP0, [RA]
3494     |  add CARG1, RC, TMP1
3495     |  b <3
3496     break;
3498   case BC_ISNEXT:
3499     |  // RA = base, RC = target (points to ITERN)
3500     |  add RA, BASE, RA, lsl #3
3501     |  ldr CFUNC:CARG1, [RA, #-24]
3502     |     add RC, PC, RC, lsl #2
3503     |   ldp TAB:CARG3, CARG4, [RA, #-16]
3504     |     sub RC, RC, #0x20000
3505     |  checkfunc CFUNC:CARG1, >5
3506     |   asr TMP0, TAB:CARG3, #47
3507     |  ldrb TMP1w, CFUNC:CARG1->ffid
3508     |   cmp_nil CARG4
3509     |   ccmn TMP0, #-LJ_TTAB, #0, eq
3510     |  ccmp TMP1w, #FF_next_N, #0, eq
3511     |  bne >5
3512     |  mov TMP0w, #0xfffe7fff           // LJ_KEYINDEX
3513     |  lsl TMP0, TMP0, #32
3514     |  str TMP0, [RA, #-8]              // Initialize control var.
3515     |1:
3516     |     mov PC, RC
3517     |  ins_next
3518     |
3519     |5:  // Despecialize bytecode if any of the checks fail.
3520     |.if JIT
3521     |  ldrb TMP2w, [RC, # OFS_OP]
3522     |.endif
3523     |  mov TMP0, #BC_JMP
3524     |   mov TMP1, #BC_ITERC
3525     |  strb TMP0w, [PC, #-4+OFS_OP]
3526     |.if JIT
3527     |  cmp TMP2w, #BC_ITERN
3528     |  bne >6
3529     |.endif
3530     |   strb TMP1w, [RC, # OFS_OP]
3531     |  b <1
3532     |.if JIT
3533     |6:  // Unpatch JLOOP.
3534     |  ldr RA, [GL, #GL_J(trace)]
3535     |  ldrh TMP2w, [RC, # OFS_RD]
3536     |  ldr TRACE:RA, [RA, TMP2, lsl #3]
3537     |  ldr TMP2w, TRACE:RA->startins
3538     |  bfxil TMP2w, TMP1w, #0, #8
3539     |  str TMP2w, [RC]
3540     |  b <1
3541     |.endif
3542     break;
3544   case BC_VARG:
3545     |  decode_RB RB, INS
3546     |   and RC, RC, #255
3547     |  // RA = base, RB = (nresults+1), RC = numparams
3548     |  ldr TMP1, [BASE, FRAME_PC]
3549     |  add TMP0, BASE, RC, lsl #3
3550     |   add RC, BASE, RA, lsl #3        // RC = destination
3551     |  add TMP0, TMP0, #FRAME_VARG
3552     |   add TMP2, RC, RB, lsl #3
3553     |  sub RA, TMP0, TMP1               // RA = vbase
3554     |  // Note: RA may now be even _above_ BASE if nargs was < numparams.
3555     |   sub TMP3, BASE, #16             // TMP3 = vtop
3556     |  cbz RB, >5
3557     |   sub TMP2, TMP2, #16
3558     |1:  // Copy vararg slots to destination slots.
3559     |  cmp RA, TMP3
3560     |  ldr TMP0, [RA], #8
3561     |  csinv TMP0, TMP0, xzr, lo        // TISNIL = ~xzr
3562     |   cmp RC, TMP2
3563     |  str TMP0, [RC], #8
3564     |   blo <1
3565     |2:
3566     |  ins_next
3567     |
3568     |5:  // Copy all varargs.
3569     |  ldr TMP0, L->maxstack
3570     |   subs TMP2, TMP3, RA
3571     |   csel RB, xzr, TMP2, le          // MULTRES = (max(vtop-vbase,0)+1)*8
3572     |   add RB, RB, #8
3573     |  add TMP1, RC, TMP2
3574     |   str RBw, SAVE_MULTRES
3575     |   ble <2                          // Nothing to copy.
3576     |  cmp TMP1, TMP0
3577     |  bhi >7
3578     |6:
3579     |  ldr TMP0, [RA], #8
3580     |  str TMP0, [RC], #8
3581     |  cmp RA, TMP3
3582     |  blo <6
3583     |  b <2
3584     |
3585     |7:  // Grow stack for varargs.
3586     |  lsr CARG2, TMP2, #3
3587     |   stp BASE, RC, L->base
3588     |  mov CARG1, L
3589     |  sub RA, RA, BASE                 // Need delta, because BASE may change.
3590     |   str PC, SAVE_PC
3591     |  bl extern lj_state_growstack     // (lua_State *L, int n)
3592     |  ldp BASE, RC, L->base
3593     |  add RA, BASE, RA
3594     |  sub TMP3, BASE, #16
3595     |  b <6
3596     break;
3598   /* -- Returns ----------------------------------------------------------- */
3600   case BC_RETM:
3601     |  // RA = results, RC = extra results
3602     |  ldr TMP0w, SAVE_MULTRES
3603     |   ldr PC, [BASE, FRAME_PC]
3604     |    add RA, BASE, RA, lsl #3
3605     |  add RC, TMP0, RC, lsl #3
3606     |  b ->BC_RETM_Z
3607     break;
3609   case BC_RET:
3610     |  // RA = results, RC = nresults+1
3611     |  ldr PC, [BASE, FRAME_PC]
3612     |   lsl RC, RC, #3
3613     |    add RA, BASE, RA, lsl #3
3614     |->BC_RETM_Z:
3615     |   str RCw, SAVE_MULTRES
3616     |1:
3617     |  ands CARG1, PC, #FRAME_TYPE
3618     |   eor CARG2, PC, #FRAME_VARG
3619     |  bne ->BC_RETV2_Z
3620     |
3621     |->BC_RET_Z:
3622     |  // BASE = base, RA = resultptr, RC = (nresults+1)*8, PC = return
3623     |  ldr INSw, [PC, #-4]
3624     |  subs TMP1, RC, #8
3625     |   sub CARG3, BASE, #16
3626     |  beq >3
3627     |2:
3628     |  ldr TMP0, [RA], #8
3629     |   add BASE, BASE, #8
3630     |   sub TMP1, TMP1, #8
3631     |  str TMP0, [BASE, #-24]
3632     |   cbnz TMP1, <2
3633     |3:
3634     |  decode_RA RA, INS
3635     |  sub CARG4, CARG3, RA, lsl #3
3636     |   decode_RB RB, INS
3637     |  ldr LFUNC:CARG1, [CARG4, FRAME_FUNC]
3638     |5:
3639     |  cmp RC, RB, lsl #3               // More results expected?
3640     |  blo >6
3641     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3642     |  mov BASE, CARG4
3643     |  ldr CARG2, LFUNC:CARG1->pc
3644     |  ldr KBASE, [CARG2, #PC2PROTO(k)]
3645     |   ins_next
3646     |
3647     |6:  // Fill up results with nil.
3648     |  add BASE, BASE, #8
3649     |   add RC, RC, #8
3650     |  str TISNIL, [BASE, #-24]
3651     |  b <5
3652     |
3653     |->BC_RETV1_Z:  // Non-standard return case.
3654     |  add RA, BASE, RA, lsl #3
3655     |->BC_RETV2_Z:
3656     |  tst CARG2, #FRAME_TYPEP
3657     |  bne ->vm_return
3658     |  // Return from vararg function: relocate BASE down.
3659     |  sub BASE, BASE, CARG2
3660     |  ldr PC, [BASE, FRAME_PC]
3661     |  b <1
3662     break;
3664   case BC_RET0: case BC_RET1:
3665     |  // RA = results, RC = nresults+1
3666     |  ldr PC, [BASE, FRAME_PC]
3667     |   lsl RC, RC, #3
3668     |   str RCw, SAVE_MULTRES
3669     |  ands CARG1, PC, #FRAME_TYPE
3670     |   eor CARG2, PC, #FRAME_VARG
3671     |  bne ->BC_RETV1_Z
3672     |   ldr INSw, [PC, #-4]
3673     if (op == BC_RET1) {
3674       |  ldr TMP0, [BASE, RA, lsl #3]
3675     }
3676     |  sub CARG4, BASE, #16
3677     |   decode_RA RA, INS
3678     |  sub BASE, CARG4, RA, lsl #3
3679     if (op == BC_RET1) {
3680       |  str TMP0, [CARG4], #8
3681     }
3682     |   decode_RB RB, INS
3683     |  ldr LFUNC:CARG1, [BASE, FRAME_FUNC]
3684     |5:
3685     |  cmp RC, RB, lsl #3
3686     |  blo >6
3687     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3688     |  ldr CARG2, LFUNC:CARG1->pc
3689     |  ldr KBASE, [CARG2, #PC2PROTO(k)]
3690     |  ins_next
3691     |
3692     |6:  // Fill up results with nil.
3693     |  add RC, RC, #8
3694     |  str TISNIL, [CARG4], #8
3695     |  b <5
3696     break;
3698   /* -- Loops and branches ------------------------------------------------ */
3700   |.define FOR_IDX,  [RA];      .define FOR_TIDX,  [RA, #4]
3701   |.define FOR_STOP, [RA, #8];  .define FOR_TSTOP, [RA, #12]
3702   |.define FOR_STEP, [RA, #16]; .define FOR_TSTEP, [RA, #20]
3703   |.define FOR_EXT,  [RA, #24]; .define FOR_TEXT,  [RA, #28]
3705   case BC_FORL:
3706     |.if JIT
3707     |  hotloop
3708     |.endif
3709     |  // Fall through. Assumes BC_IFORL follows.
3710     break;
3712   case BC_JFORI:
3713   case BC_JFORL:
3714 #if !LJ_HASJIT
3715     break;
3716 #endif
3717   case BC_FORI:
3718   case BC_IFORL:
3719     |  // RA = base, RC = target (after end of loop or start of loop)
3720     vk = (op == BC_IFORL || op == BC_JFORL);
3721     |  add RA, BASE, RA, lsl #3
3722     |  ldp CARG1, CARG2, FOR_IDX                // CARG1 = IDX, CARG2 = STOP
3723     |   ldr CARG3, FOR_STEP                     // CARG3 = STEP
3724     if (op != BC_JFORL) {
3725       |   add RC, PC, RC, lsl #2
3726       |   sub RC, RC, #0x20000
3727     }
3728     |  checkint CARG1, >5
3729     if (!vk) {
3730       |  checkint CARG2, ->vmeta_for
3731       |   checkint CARG3, ->vmeta_for
3732       |  tbnz CARG3w, #31, >4
3733       |  cmp CARG1w, CARG2w
3734     } else {
3735       |  adds CARG1w, CARG1w, CARG3w
3736       |  bvs >2
3737       |   add_TISNUM TMP0, CARG1
3738       |  tbnz CARG3w, #31, >4
3739       |  cmp CARG1w, CARG2w
3740     }
3741     |1:
3742     if (op == BC_FORI) {
3743       |  csel PC, RC, PC, gt
3744     } else if (op == BC_JFORI) {
3745       |  mov PC, RC
3746       |  ldrh RCw, [RC, #-4+OFS_RD]
3747     } else if (op == BC_IFORL) {
3748       |  csel PC, RC, PC, le
3749     }
3750     if (vk) {
3751       |   str TMP0, FOR_IDX
3752       |   str TMP0, FOR_EXT
3753     } else {
3754       |  str CARG1, FOR_EXT
3755     }
3756     if (op == BC_JFORI || op == BC_JFORL) {
3757       |  ble =>BC_JLOOP
3758     }
3759     |2:
3760     |   ins_next
3761     |
3762     |4:  // Invert check for negative step.
3763     |  cmp CARG2w, CARG1w
3764     |  b <1
3765     |
3766     |5:  // FP loop.
3767     |  ldp d0, d1, FOR_IDX
3768     |  blo ->vmeta_for
3769     if (!vk) {
3770       |  checknum CARG2, ->vmeta_for
3771       |   checknum CARG3, ->vmeta_for
3772       |  str d0, FOR_EXT
3773     } else {
3774       |  ldr d2, FOR_STEP
3775       |  fadd d0, d0, d2
3776     }
3777     |  tbnz CARG3, #63, >7
3778     |  fcmp d0, d1
3779     |6:
3780     if (vk) {
3781       |  str d0, FOR_IDX
3782       |  str d0, FOR_EXT
3783     }
3784     if (op == BC_FORI) {
3785       |  csel PC, RC, PC, hi
3786     } else if (op == BC_JFORI) {
3787       |  ldrh RCw, [RC, #-4+OFS_RD]
3788       |  bls =>BC_JLOOP
3789     } else if (op == BC_IFORL) {
3790       |  csel PC, RC, PC, ls
3791     } else {
3792       |  bls =>BC_JLOOP
3793     }
3794     |  b <2
3795     |
3796     |7:  // Invert check for negative step.
3797     |  fcmp d1, d0
3798     |  b <6
3799     break;
3801   case BC_ITERL:
3802     |.if JIT
3803     |  hotloop
3804     |.endif
3805     |  // Fall through. Assumes BC_IITERL follows.
3806     break;
3808   case BC_JITERL:
3809 #if !LJ_HASJIT
3810     break;
3811 #endif
3812   case BC_IITERL:
3813     |  // RA = base, RC = target
3814     |  ldr CARG1, [BASE, RA, lsl #3]
3815     |   add TMP1, BASE, RA, lsl #3
3816     |  cmp_nil CARG1
3817     |  beq >1                           // Stop if iterator returned nil.
3818     if (op == BC_JITERL) {
3819       |  str CARG1, [TMP1, #-8]
3820       |  b =>BC_JLOOP
3821     } else {
3822       |  add TMP0, PC, RC, lsl #2       // Otherwise save control var + branch.
3823       |  sub PC, TMP0, #0x20000
3824       |  str CARG1, [TMP1, #-8]
3825     }
3826     |1:
3827     |  ins_next
3828     break;
3830   case BC_LOOP:
3831     |  // RA = base, RC = target (loop extent)
3832     |  // Note: RA/RC is only used by trace recorder to determine scope/extent
3833     |  // This opcode does NOT jump, it's only purpose is to detect a hot loop.
3834     |.if JIT
3835     |  hotloop
3836     |.endif
3837     |  // Fall through. Assumes BC_ILOOP follows.
3838     break;
3840   case BC_ILOOP:
3841     |  // RA = base, RC = target (loop extent)
3842     |  ins_next
3843     break;
3845   case BC_JLOOP:
3846     |.if JIT
3847     |  // RA = base (ignored), RC = traceno
3848     |  ldr CARG1, [GL, #GL_J(trace)]
3849     |   st_vmstate wzr  // Traces on ARM64 don't store the trace #, so use 0.
3850     |  ldr TRACE:RC, [CARG1, RC, lsl #3]
3851     |.if PAUTH
3852     |  ldr RA, TRACE:RC->mcauth
3853     |.else
3854     |  ldr RA, TRACE:RC->mcode
3855     |.endif
3856     |   str BASE, GL->jit_base
3857     |   str L, GL->tmpbuf.L
3858     |  sub sp, sp, #16  // See SPS_FIXED. Avoids sp adjust in every root trace.
3859     |.if PAUTH
3860     |  braa RA, RC
3861     |.else
3862     |  br RA
3863     |.endif
3864     |.endif
3865     break;
3867   case BC_JMP:
3868     |  // RA = base (only used by trace recorder), RC = target
3869     |  add RC, PC, RC, lsl #2
3870     |  sub PC, RC, #0x20000
3871     |  ins_next
3872     break;
3874   /* -- Function headers -------------------------------------------------- */
3876   case BC_FUNCF:
3877     |.if JIT
3878     |  hotcall
3879     |.endif
3880   case BC_FUNCV:  /* NYI: compiled vararg functions. */
3881     |  // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow.
3882     break;
3884   case BC_JFUNCF:
3885 #if !LJ_HASJIT
3886     break;
3887 #endif
3888   case BC_IFUNCF:
3889     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8
3890     |  ldr CARG1, L->maxstack
3891     |   ldrb TMP1w, [PC, #-4+PC2PROTO(numparams)]
3892     |    ldr KBASE, [PC, #-4+PC2PROTO(k)]
3893     |  cmp RA, CARG1
3894     |  bhi ->vm_growstack_l
3895     |2:
3896     |  cmp NARGS8:RC, TMP1, lsl #3      // Check for missing parameters.
3897     |  blo >3
3898     if (op == BC_JFUNCF) {
3899       |  decode_RD RC, INS
3900       |  b =>BC_JLOOP
3901     } else {
3902       |  ins_next
3903     }
3904     |
3905     |3:  // Clear missing parameters.
3906     |  str TISNIL, [BASE, NARGS8:RC]
3907     |  add NARGS8:RC, NARGS8:RC, #8
3908     |  b <2
3909     break;
3911   case BC_JFUNCV:
3912 #if !LJ_HASJIT
3913     break;
3914 #endif
3915     |  NYI  // NYI: compiled vararg functions
3916     break;  /* NYI: compiled vararg functions. */
3918   case BC_IFUNCV:
3919     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8
3920     |  ldr CARG1, L->maxstack
3921     |   movn TMP0, #~LJ_TFUNC
3922     |   add TMP2, BASE, RC
3923     |   add LFUNC:CARG3, CARG3, TMP0, lsl #47
3924     |  add RA, RA, RC
3925     |  sub CARG1, CARG1, #8
3926     |   add TMP0, RC, #16+FRAME_VARG
3927     |   str LFUNC:CARG3, [TMP2], #8     // Store (tagged) copy of LFUNC.
3928     |    ldr KBASE, [PC, #-4+PC2PROTO(k)]
3929     |  cmp RA, CARG1
3930     |   str TMP0, [TMP2], #8            // Store delta + FRAME_VARG.
3931     |  bhs ->vm_growstack_l
3932     |   sub RC, TMP2, #16
3933     |  ldrb TMP1w, [PC, #-4+PC2PROTO(numparams)]
3934     |   mov RA, BASE
3935     |   mov BASE, TMP2
3936     |  cbz TMP1, >2
3937     |1:
3938     |  cmp RA, RC                       // Less args than parameters?
3939     |  bhs >3
3940     |   ldr TMP0, [RA]
3941     |  sub TMP1, TMP1, #1
3942     |    str TISNIL, [RA], #8           // Clear old fixarg slot (help the GC).
3943     |   str TMP0, [TMP2], #8
3944     |  cbnz TMP1, <1
3945     |2:
3946     |  ins_next
3947     |
3948     |3:
3949     |  sub TMP1, TMP1, #1
3950     |   str TISNIL, [TMP2], #8
3951     |  cbz TMP1, <2
3952     |  b <3
3953     break;
3955   case BC_FUNCC:
3956   case BC_FUNCCW:
3957     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = CFUNC, RC = nargs*8
3958     if (op == BC_FUNCC) {
3959       |  ldr CARG4, CFUNC:CARG3->f
3960     } else {
3961       |  ldr CARG4, GL->wrapf
3962     }
3963     |   add CARG2, RA, NARGS8:RC
3964     |   ldr CARG1, L->maxstack
3965     |  add RC, BASE, NARGS8:RC
3966     |   cmp CARG2, CARG1
3967     |  stp BASE, RC, L->base
3968     if (op == BC_FUNCCW) {
3969       |  ldr CARG2, CFUNC:CARG3->f
3970     }
3971     |    mv_vmstate TMP0w, C
3972     |  mov CARG1, L
3973     |   bhi ->vm_growstack_c            // Need to grow stack.
3974     |    st_vmstate TMP0w
3975     |  blr_auth CARG4                   // (lua_State *L [, lua_CFunction f])
3976     |  // Returns nresults.
3977     |  ldp BASE, TMP1, L->base
3978     |    str L, GL->cur_L
3979     |   sbfiz RC, CRET1, #3, #32
3980     |    st_vmstate ST_INTERP
3981     |  ldr PC, [BASE, FRAME_PC]
3982     |   sub RA, TMP1, RC                // RA = L->top - nresults*8
3983     |  b ->vm_returnc
3984     break;
3986   /* ---------------------------------------------------------------------- */
3988   default:
3989     fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]);
3990     exit(2);
3991     break;
3992   }
3995 static int build_backend(BuildCtx *ctx)
3997   int op;
3999   dasm_growpc(Dst, BC__MAX);
4001   build_subroutines(ctx);
4003   |.code_op
4004   for (op = 0; op < BC__MAX; op++)
4005     build_ins(ctx, (BCOp)op, op);
4007   return BC__MAX;
4010 /* Emit pseudo frame-info for all assembler functions. */
4011 static void emit_asm_debug(BuildCtx *ctx)
4013   int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code);
4014   int i;
4015   switch (ctx->mode) {
4016   case BUILD_elfasm:
4017     fprintf(ctx->fp, "\t.section .debug_frame,\"\",%%progbits\n");
4018     fprintf(ctx->fp,
4019         ".Lframe0:\n"
4020         "\t.long .LECIE0-.LSCIE0\n"
4021         ".LSCIE0:\n"
4022         "\t.long 0xffffffff\n"
4023         "\t.byte 0x1\n"
4024         "\t.string \"\"\n"
4025         "\t.uleb128 0x1\n"
4026         "\t.sleb128 -8\n"
4027         "\t.byte 30\n"                          /* Return address is in lr. */
4028         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4029         "\t.align 3\n"
4030         ".LECIE0:\n\n");
4031     fprintf(ctx->fp,
4032         ".LSFDE0:\n"
4033         "\t.long .LEFDE0-.LASFDE0\n"
4034         ".LASFDE0:\n"
4035         "\t.long .Lframe0\n"
4036         "\t.quad .Lbegin\n"
4037         "\t.quad %d\n"
4038         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4039         "\t.byte 0x9d\n\t.uleb128 2\n",         /* offset fp */
4040         fcofs);
4041     for (i = 19; i <= 28; i++)  /* offset x19-x28 */
4042       fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, i+(3-19));
4043     for (i = 8; i <= 15; i++)  /* offset d8-d15 */
4044       fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n",
4045               64+i, i+(3+(28-19+1)-8));
4046     fprintf(ctx->fp,
4047         "\t.align 3\n"
4048         ".LEFDE0:\n\n");
4049 #if LJ_HASFFI
4050     fprintf(ctx->fp,
4051         ".LSFDE1:\n"
4052         "\t.long .LEFDE1-.LASFDE1\n"
4053         ".LASFDE1:\n"
4054         "\t.long .Lframe0\n"
4055         "\t.quad lj_vm_ffi_call\n"
4056         "\t.quad %d\n"
4057         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4058         "\t.byte 0x9d\n\t.uleb128 2\n"          /* offset fp */
4059         "\t.byte 0x93\n\t.uleb128 3\n"          /* offset x19 */
4060         "\t.byte 0x94\n\t.uleb128 4\n"          /* offset x20 */
4061         "\t.align 3\n"
4062         ".LEFDE1:\n\n", (int)ctx->codesz - fcofs);
4063 #endif
4064 #if !LJ_NO_UNWIND
4065     fprintf(ctx->fp, "\t.section .eh_frame,\"a\",%%progbits\n");
4066     fprintf(ctx->fp,
4067         ".Lframe1:\n"
4068         "\t.long .LECIE1-.LSCIE1\n"
4069         ".LSCIE1:\n"
4070         "\t.long 0\n"
4071         "\t.byte 0x1\n"
4072         "\t.string \"zPR\"\n"
4073         "\t.uleb128 0x1\n"
4074         "\t.sleb128 -8\n"
4075         "\t.byte 30\n"                          /* Return address is in lr. */
4076         "\t.uleb128 6\n"                        /* augmentation length */
4077         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4078         "\t.long lj_err_unwind_dwarf-.\n"
4079         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4080         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4081         "\t.align 3\n"
4082         ".LECIE1:\n\n");
4083     fprintf(ctx->fp,
4084         ".LSFDE2:\n"
4085         "\t.long .LEFDE2-.LASFDE2\n"
4086         ".LASFDE2:\n"
4087         "\t.long .LASFDE2-.Lframe1\n"
4088         "\t.long .Lbegin-.\n"
4089         "\t.long %d\n"
4090         "\t.uleb128 0\n"                        /* augmentation length */
4091         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4092         "\t.byte 0x9d\n\t.uleb128 2\n",         /* offset fp */
4093         fcofs);
4094     for (i = 19; i <= 28; i++)  /* offset x19-x28 */
4095       fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, i+(3-19));
4096     for (i = 8; i <= 15; i++)  /* offset d8-d15 */
4097       fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n",
4098               64+i, i+(3+(28-19+1)-8));
4099     fprintf(ctx->fp,
4100         "\t.align 3\n"
4101         ".LEFDE2:\n\n");
4102 #if LJ_HASFFI
4103     fprintf(ctx->fp,
4104         ".Lframe2:\n"
4105         "\t.long .LECIE2-.LSCIE2\n"
4106         ".LSCIE2:\n"
4107         "\t.long 0\n"
4108         "\t.byte 0x1\n"
4109         "\t.string \"zR\"\n"
4110         "\t.uleb128 0x1\n"
4111         "\t.sleb128 -8\n"
4112         "\t.byte 30\n"                          /* Return address is in lr. */
4113         "\t.uleb128 1\n"                        /* augmentation length */
4114         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4115         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4116         "\t.align 3\n"
4117         ".LECIE2:\n\n");
4118     fprintf(ctx->fp,
4119         ".LSFDE3:\n"
4120         "\t.long .LEFDE3-.LASFDE3\n"
4121         ".LASFDE3:\n"
4122         "\t.long .LASFDE3-.Lframe2\n"
4123         "\t.long lj_vm_ffi_call-.\n"
4124         "\t.long %d\n"
4125         "\t.uleb128 0\n"                        /* augmentation length */
4126         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4127         "\t.byte 0x9d\n\t.uleb128 2\n"          /* offset fp */
4128         "\t.byte 0x93\n\t.uleb128 3\n"          /* offset x19 */
4129         "\t.byte 0x94\n\t.uleb128 4\n"          /* offset x20 */
4130         "\t.align 3\n"
4131         ".LEFDE3:\n\n", (int)ctx->codesz - fcofs);
4132 #endif
4133 #endif
4134     break;
4135 #if !LJ_NO_UNWIND
4136   case BUILD_machasm: {
4137 #if LJ_HASFFI
4138     int fcsize = 0;
4139 #endif
4140     int j;
4141     fprintf(ctx->fp, "\t.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support\n");
4142     fprintf(ctx->fp,
4143         "EH_frame1:\n"
4144         "\t.set L$set$x,LECIEX-LSCIEX\n"
4145         "\t.long L$set$x\n"
4146         "LSCIEX:\n"
4147         "\t.long 0\n"
4148         "\t.byte 0x1\n"
4149         "\t.ascii \"zPR\\0\"\n"
4150         "\t.uleb128 0x1\n"
4151         "\t.sleb128 -8\n"
4152         "\t.byte 30\n"                          /* Return address is in lr. */
4153         "\t.uleb128 6\n"                        /* augmentation length */
4154         "\t.byte 0x9b\n"                        /* indirect|pcrel|sdata4 */
4155         "\t.long _lj_err_unwind_dwarf@GOT-.\n"
4156         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4157         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4158         "\t.align 3\n"
4159         "LECIEX:\n\n");
4160     for (j = 0; j < ctx->nsym; j++) {
4161       const char *name = ctx->sym[j].name;
4162       int32_t size = ctx->sym[j+1].ofs - ctx->sym[j].ofs;
4163       if (size == 0) continue;
4164 #if LJ_HASFFI
4165       if (!strcmp(name, "_lj_vm_ffi_call")) { fcsize = size; continue; }
4166 #endif
4167       fprintf(ctx->fp,
4168         "LSFDE%d:\n"
4169         "\t.set L$set$%d,LEFDE%d-LASFDE%d\n"
4170         "\t.long L$set$%d\n"
4171         "LASFDE%d:\n"
4172         "\t.long LASFDE%d-EH_frame1\n"
4173         "\t.long %s-.\n"
4174         "\t.long %d\n"
4175         "\t.uleb128 0\n"                        /* augmentation length */
4176         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4177         "\t.byte 0x9d\n\t.uleb128 2\n",         /* offset fp */
4178         j, j, j, j, j, j, j, name, size);
4179       for (i = 19; i <= 28; i++)  /* offset x19-x28 */
4180         fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, i+(3-19));
4181       for (i = 8; i <= 15; i++)  /* offset d8-d15 */
4182         fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n",
4183                 64+i, i+(3+(28-19+1)-8));
4184       fprintf(ctx->fp,
4185         "\t.align 3\n"
4186         "LEFDE%d:\n\n", j);
4187     }
4188 #if LJ_HASFFI
4189     if (fcsize) {
4190       fprintf(ctx->fp,
4191         "EH_frame2:\n"
4192         "\t.set L$set$y,LECIEY-LSCIEY\n"
4193         "\t.long L$set$y\n"
4194         "LSCIEY:\n"
4195         "\t.long 0\n"
4196         "\t.byte 0x1\n"
4197         "\t.ascii \"zR\\0\"\n"
4198         "\t.uleb128 0x1\n"
4199         "\t.sleb128 -8\n"
4200         "\t.byte 30\n"                          /* Return address is in lr. */
4201         "\t.uleb128 1\n"                        /* augmentation length */
4202         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4203         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4204         "\t.align 3\n"
4205         "LECIEY:\n\n");
4206       fprintf(ctx->fp,
4207         "LSFDEY:\n"
4208         "\t.set L$set$yy,LEFDEY-LASFDEY\n"
4209         "\t.long L$set$yy\n"
4210         "LASFDEY:\n"
4211         "\t.long LASFDEY-EH_frame2\n"
4212         "\t.long _lj_vm_ffi_call-.\n"
4213         "\t.long %d\n"
4214         "\t.uleb128 0\n"                        /* augmentation length */
4215         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4216         "\t.byte 0x9d\n\t.uleb128 2\n"          /* offset fp */
4217         "\t.byte 0x93\n\t.uleb128 3\n"          /* offset x19 */
4218         "\t.byte 0x94\n\t.uleb128 4\n"          /* offset x20 */
4219         "\t.align 3\n"
4220         "LEFDEY:\n\n", fcsize);
4221     }
4222 #endif
4223     fprintf(ctx->fp, ".subsections_via_symbols\n");
4224     }
4225     break;
4226 #endif
4227   default:
4228     break;
4229   }