Drop unused function wrapper.
[luajit-2.0.git] / src / vm_arm64.dasc
blob4753df19624c49f11ee8f5c20c6115f863b3702e
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. Clobbers CARG1-CARG2, FARG1-FARG2.
1662   |->vm_tobit_fb:
1663   |  bls ->fff_fallback
1664   |  fmov FARG1, CARG1
1665   |  movz CARG2, #0x4338, lsl #48
1666   |  fmov FARG2, CARG2
1667   |  fadd FARG1, FARG1, FARG2
1668   |  fmov CARG1w, s0
1669   |  br lr
1670   |
1671   |.macro .ffunc_bit, name
1672   |  .ffunc_1 bit_..name
1673   |  adr lr, >1
1674   |  checkint CARG1, ->vm_tobit_fb
1675   |1:
1676   |.endmacro
1677   |
1678   |.macro .ffunc_bit_op, name, ins
1679   |  .ffunc_bit name
1680   |  mov RA, #8
1681   |  mov TMP0w, CARG1w
1682   |  adr lr, >2
1683   |1:
1684   |  ldr CARG1, [BASE, RA]
1685   |   cmp RA, NARGS8:RC
1686   |    add RA, RA, #8
1687   |   bge >9
1688   |  checkint CARG1, ->vm_tobit_fb
1689   |2:
1690   |  ins TMP0w, TMP0w, CARG1w
1691   |  b <1
1692   |.endmacro
1693   |
1694   |.ffunc_bit_op band, and
1695   |.ffunc_bit_op bor, orr
1696   |.ffunc_bit_op bxor, eor
1697   |
1698   |.ffunc_bit tobit
1699   |  mov TMP0w, CARG1w
1700   |9:  // Label reused by .ffunc_bit_op users.
1701   |  add_TISNUM CARG1, TMP0
1702   |  b ->fff_restv
1703   |
1704   |.ffunc_bit bswap
1705   |  rev TMP0w, CARG1w
1706   |  add_TISNUM CARG1, TMP0
1707   |  b ->fff_restv
1708   |
1709   |.ffunc_bit bnot
1710   |  mvn TMP0w, CARG1w
1711   |  add_TISNUM CARG1, TMP0
1712   |  b ->fff_restv
1713   |
1714   |.macro .ffunc_bit_sh, name, ins, shmod
1715   |  .ffunc bit_..name
1716   |  ldp TMP0, CARG1, [BASE]
1717   |   cmp NARGS8:RC, #16
1718   |   blo ->fff_fallback
1719   |  adr lr, >1
1720   |  checkint CARG1, ->vm_tobit_fb
1721   |1:
1722   |.if shmod == 0
1723   |  mov TMP1, CARG1
1724   |.else
1725   |  neg TMP1, CARG1
1726   |.endif
1727   |  mov CARG1, TMP0
1728   |  adr lr, >2
1729   |  checkint CARG1, ->vm_tobit_fb
1730   |2:
1731   |  ins TMP0w, CARG1w, TMP1w
1732   |  add_TISNUM CARG1, TMP0
1733   |  b ->fff_restv
1734   |.endmacro
1735   |
1736   |.ffunc_bit_sh lshift, lsl, 0
1737   |.ffunc_bit_sh rshift, lsr, 0
1738   |.ffunc_bit_sh arshift, asr, 0
1739   |.ffunc_bit_sh rol, ror, 1
1740   |.ffunc_bit_sh ror, ror, 0
1741   |
1742   |//-----------------------------------------------------------------------
1743   |
1744   |->fff_fallback:                      // Call fast function fallback handler.
1745   |  // BASE = new base, RC = nargs*8
1746   |   ldp CFUNC:CARG3, PC, [BASE, FRAME_FUNC]   // Fallback may overwrite PC.
1747   |  ldr TMP2, L->maxstack
1748   |  add TMP1, BASE, NARGS8:RC
1749   |  stp BASE, TMP1, L->base
1750   |   and CFUNC:CARG3, CARG3, #LJ_GCVMASK
1751   |  add TMP1, TMP1, #8*LUA_MINSTACK
1752   |   ldr CARG3, CFUNC:CARG3->f
1753   |    str PC, SAVE_PC                  // Redundant (but a defined value).
1754   |  cmp TMP1, TMP2
1755   |   mov CARG1, L
1756   |  bhi >5                             // Need to grow stack.
1757   |   blr_auth CARG3                    // (lua_State *L)
1758   |  // Either throws an error, or recovers and returns -1, 0 or nresults+1.
1759   |   ldr BASE, L->base
1760   |  cmp CRET1w, #0
1761   |   lsl RC, CRET1, #3
1762   |   sub RA, BASE, #16
1763   |  bgt ->fff_res                      // Returned nresults+1?
1764   |1:  // Returned 0 or -1: retry fast path.
1765   |   ldr CARG1, L->top
1766   |    ldr CFUNC:CARG3, [BASE, FRAME_FUNC]
1767   |   sub NARGS8:RC, CARG1, BASE
1768   |  bne ->vm_call_tail                 // Returned -1?
1769   |    and CFUNC:CARG3, CARG3, #LJ_GCVMASK
1770   |  ins_callt                          // Returned 0: retry fast path.
1771   |
1772   |// Reconstruct previous base for vmeta_call during tailcall.
1773   |->vm_call_tail:
1774   |  ands TMP0, PC, #FRAME_TYPE
1775   |   and TMP1, PC, #~FRAME_TYPEP
1776   |  bne >3
1777   |  ldrb RAw, [PC, #-4+OFS_RA]
1778   |  lsl RA, RA, #3
1779   |  add TMP1, RA, #16
1780   |3:
1781   |  sub RB, BASE, TMP1
1782   |  b ->vm_call_dispatch               // Resolve again for tailcall.
1783   |
1784   |5:  // Grow stack for fallback handler.
1785   |  mov CARG2, #LUA_MINSTACK
1786   |  bl extern lj_state_growstack       // (lua_State *L, int n)
1787   |  ldr BASE, L->base
1788   |  cmp CARG1, CARG1                   // Set zero-flag to force retry.
1789   |  b <1
1790   |
1791   |->fff_gcstep:                        // Call GC step function.
1792   |  // BASE = new base, RC = nargs*8
1793   |  sp_auth
1794   |   add CARG2, BASE, NARGS8:RC        // Calculate L->top.
1795   |  mov RA, lr
1796   |   stp BASE, CARG2, L->base
1797   |   str PC, SAVE_PC                   // Redundant (but a defined value).
1798   |  mov CARG1, L
1799   |  bl extern lj_gc_step               // (lua_State *L)
1800   |  ldp BASE, CARG2, L->base
1801   |   ldr CFUNC:CARG3, [BASE, FRAME_FUNC]
1802   |  mov lr, RA                         // Help return address predictor.
1803   |  sub NARGS8:RC, CARG2, BASE         // Calculate nargs*8.
1804   |   and CFUNC:CARG3, CARG3, #LJ_GCVMASK
1805   |  ret_auth
1806   |
1807   |//-----------------------------------------------------------------------
1808   |//-- Special dispatch targets -------------------------------------------
1809   |//-----------------------------------------------------------------------
1810   |
1811   |->vm_record:                         // Dispatch target for recording phase.
1812   |.if JIT
1813   |  ldrb CARG1w, GL->hookmask
1814   |  tst CARG1, #HOOK_VMEVENT           // No recording while in vmevent.
1815   |  bne >5
1816   |  // Decrement the hookcount for consistency, but always do the call.
1817   |   ldr CARG2w, GL->hookcount
1818   |  tst CARG1, #HOOK_ACTIVE
1819   |  bne >1
1820   |   sub CARG2w, CARG2w, #1
1821   |  tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT
1822   |  beq >1
1823   |   str CARG2w, GL->hookcount
1824   |  b >1
1825   |.endif
1826   |
1827   |->vm_rethook:                        // Dispatch target for return hooks.
1828   |  ldrb TMP2w, GL->hookmask
1829   |  tbz TMP2w, #HOOK_ACTIVE_SHIFT, >1  // Hook already active?
1830   |5:  // Re-dispatch to static ins.
1831   |  ldr TMP0, [TMP1, #GG_G2DISP+GG_DISP2STATIC]
1832   |  br_auth TMP0
1833   |
1834   |->vm_inshook:                        // Dispatch target for instr/line hooks.
1835   |  ldrb TMP2w, GL->hookmask
1836   |   ldr TMP3w, GL->hookcount
1837   |  tbnz TMP2w, #HOOK_ACTIVE_SHIFT, <5 // Hook already active?
1838   |  tst TMP2w, #LUA_MASKLINE|LUA_MASKCOUNT
1839   |  beq <5
1840   |   sub TMP3w, TMP3w, #1
1841   |   str TMP3w, GL->hookcount
1842   |   cbz TMP3w, >1
1843   |  tbz TMP2w, #LUA_HOOKLINE, <5
1844   |1:
1845   |  mov CARG1, L
1846   |   str BASE, L->base
1847   |  mov CARG2, PC
1848   |  // SAVE_PC must hold the _previous_ PC. The callee updates it with PC.
1849   |  bl extern lj_dispatch_ins          // (lua_State *L, const BCIns *pc)
1850   |3:
1851   |  ldr BASE, L->base
1852   |4:  // Re-dispatch to static ins.
1853   |  ldr INSw, [PC, #-4]
1854   |  add TMP1, GL, INS, uxtb #3
1855   |   decode_RA RA, INS
1856   |  ldr TMP0, [TMP1, #GG_G2DISP+GG_DISP2STATIC]
1857   |   decode_RD RC, INS
1858   |  br_auth TMP0
1859   |
1860   |->cont_hook:                         // Continue from hook yield.
1861   |  ldr CARG1, [CARG4, #-40]
1862   |   add PC, PC, #4
1863   |  str CARG1w, SAVE_MULTRES           // Restore MULTRES for *M ins.
1864   |  b <4
1865   |
1866   |->vm_hotloop:                        // Hot loop counter underflow.
1867   |.if JIT
1868   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Same as curr_topL(L).
1869   |   add CARG1, GL, #GG_G2DISP+GG_DISP2J
1870   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
1871   |   str PC, SAVE_PC
1872   |  ldr CARG3, LFUNC:CARG3->pc
1873   |   mov CARG2, PC
1874   |   str L, [GL, #GL_J(L)]
1875   |  ldrb CARG3w, [CARG3, #PC2PROTO(framesize)]
1876   |   str BASE, L->base
1877   |  add CARG3, BASE, CARG3, lsl #3
1878   |  str CARG3, L->top
1879   |  bl extern lj_trace_hot             // (jit_State *J, const BCIns *pc)
1880   |  b <3
1881   |.endif
1882   |
1883   |->vm_callhook:                       // Dispatch target for call hooks.
1884   |  mov CARG2, PC
1885   |.if JIT
1886   |  b >1
1887   |.endif
1888   |
1889   |->vm_hotcall:                        // Hot call counter underflow.
1890   |.if JIT
1891   |  orr CARG2, PC, #1
1892   |1:
1893   |.endif
1894   |  add TMP1, BASE, NARGS8:RC
1895   |   str PC, SAVE_PC
1896   |   mov CARG1, L
1897   |   sub RA, RA, BASE
1898   |  stp BASE, TMP1, L->base
1899   |  bl extern lj_dispatch_call         // (lua_State *L, const BCIns *pc)
1900   |  // Returns ASMFunction.
1901   |  ldp BASE, TMP1, L->base
1902   |   str xzr, SAVE_PC                  // Invalidate for subsequent line hook.
1903   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
1904   |  add RA, BASE, RA
1905   |  sub NARGS8:RC, TMP1, BASE
1906   |   ldr INSw, [PC, #-4]
1907   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
1908   |  br_auth CRET1
1909   |
1910   |->cont_stitch:                       // Trace stitching.
1911   |.if JIT
1912   |  // RA = resultptr, CARG4 = meta base
1913   |   ldr RBw, SAVE_MULTRES
1914   |  ldr INSw, [PC, #-4]
1915   |    ldr TRACE:CARG3, [CARG4, #-40]   // Save previous trace.
1916   |   subs RB, RB, #8
1917   |  decode_RA RC, INS                  // Call base.
1918   |    and CARG3, CARG3, #LJ_GCVMASK
1919   |   beq >2
1920   |1:  // Move results down.
1921   |  ldr CARG1, [RA], #8
1922   |   subs RB, RB, #8
1923   |  str CARG1, [BASE, RC, lsl #3]
1924   |    add RC, RC, #1
1925   |   bne <1
1926   |2:
1927   |   decode_RA RA, INS
1928   |   decode_RB RB, INS
1929   |   add RA, RA, RB
1930   |3:
1931   |   cmp RA, RC
1932   |   bhi >9                            // More results wanted?
1933   |
1934   |  ldrh RAw, TRACE:CARG3->traceno
1935   |  ldrh RCw, TRACE:CARG3->link
1936   |  cmp RCw, RAw
1937   |  beq ->cont_nop                     // Blacklisted.
1938   |  cmp RCw, #0
1939   |  bne =>BC_JLOOP                     // Jump to stitched trace.
1940   |
1941   |  // Stitch a new trace to the previous trace.
1942   |  mov CARG1, #GL_J(exitno)
1943   |  str RAw, [GL, CARG1]
1944   |  mov CARG1, #GL_J(L)
1945   |  str L, [GL, CARG1]
1946   |  str BASE, L->base
1947   |  add CARG1, GL, #GG_G2J
1948   |  mov CARG2, PC
1949   |  bl extern lj_dispatch_stitch       // (jit_State *J, const BCIns *pc)
1950   |  ldr BASE, L->base
1951   |  b ->cont_nop
1952   |
1953   |9:  // Fill up results with nil.
1954   |  str TISNIL, [BASE, RC, lsl #3]
1955   |  add RC, RC, #1
1956   |  b <3
1957   |.endif
1958   |
1959   |->vm_profhook:                       // Dispatch target for profiler hook.
1960 #if LJ_HASPROFILE
1961   |  mov CARG1, L
1962   |   str BASE, L->base
1963   |  mov CARG2, PC
1964   |  bl extern lj_dispatch_profile      // (lua_State *L, const BCIns *pc)
1965   |  // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction.
1966   |  ldr BASE, L->base
1967   |  sub PC, PC, #4
1968   |  b ->cont_nop
1969 #endif
1970   |
1971   |//-----------------------------------------------------------------------
1972   |//-- Trace exit handler -------------------------------------------------
1973   |//-----------------------------------------------------------------------
1974   |
1975   |.macro savex_, a, b
1976   |  stp d..a, d..b, [sp, #a*8]
1977   |  stp x..a, x..b, [sp, #32*8+a*8]
1978   |.endmacro
1979   |
1980   |->vm_exit_handler:
1981   |.if JIT
1982   |  sub     sp, sp, #(64*8)
1983   |  savex_, 0, 1
1984   |  savex_, 2, 3
1985   |  savex_, 4, 5
1986   |  savex_, 6, 7
1987   |  savex_, 8, 9
1988   |  savex_, 10, 11
1989   |  savex_, 12, 13
1990   |  savex_, 14, 15
1991   |  savex_, 16, 17
1992   |  savex_, 18, 19
1993   |  savex_, 20, 21
1994   |  savex_, 22, 23
1995   |  savex_, 24, 25
1996   |  savex_, 26, 27
1997   |  savex_, 28, 29
1998   |  stp d30, d31, [sp, #30*8]
1999   |  ldr CARG1, [sp, #64*8]     // Load original value of lr.
2000   |   add CARG3, sp, #64*8      // Recompute original value of sp.
2001   |    mv_vmstate CARG4w, EXIT
2002   |   stp xzr, CARG3, [sp, #62*8]       // Store 0/sp in RID_LR/RID_SP.
2003   |  sub CARG1, CARG1, lr
2004   |   ldr L, GL->cur_L
2005   |  lsr CARG1, CARG1, #2
2006   |   ldr BASE, GL->jit_base
2007   |  sub CARG1, CARG1, #2
2008   |   ldr CARG2w, [lr]          // Load trace number.
2009   |    st_vmstate CARG4w
2010   |.if ENDIAN_BE
2011   |   rev32 CARG2, CARG2
2012   |.endif
2013   |   str BASE, L->base
2014   |  ubfx CARG2w, CARG2w, #5, #16
2015   |  str CARG1w, [GL, #GL_J(exitno)]
2016   |   str CARG2w, [GL, #GL_J(parent)]
2017   |   str L, [GL, #GL_J(L)]
2018   |  str xzr, GL->jit_base
2019   |  add CARG1, GL, #GG_G2J
2020   |  mov CARG2, sp
2021   |  bl extern lj_trace_exit            // (jit_State *J, ExitState *ex)
2022   |  // Returns MULTRES (unscaled) or negated error code.
2023   |  ldr CARG2, L->cframe
2024   |   ldr BASE, L->base
2025   |  and sp, CARG2, #CFRAME_RAWMASK
2026   |   ldr PC, SAVE_PC                   // Get SAVE_PC.
2027   |  str L, SAVE_L                      // Set SAVE_L (on-trace resume/yield).
2028   |  b >1
2029   |.endif
2030   |
2031   |->vm_exit_interp:
2032   |  // CARG1 = MULTRES or negated error code, BASE, PC and GL set.
2033   |.if JIT
2034   |  ldr L, SAVE_L
2035   |1:
2036   |   init_constants
2037   |  cmn CARG1w, #LUA_ERRERR
2038   |  bhs >9                             // Check for error from exit.
2039   |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2040   |   lsl RC, CARG1, #3
2041   |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2042   |   str RCw, SAVE_MULTRES
2043   |   str BASE, L->base
2044   |  ldr CARG2, LFUNC:CARG2->pc
2045   |   str xzr, GL->jit_base
2046   |    mv_vmstate CARG4w, INTERP
2047   |  ldr KBASE, [CARG2, #PC2PROTO(k)]
2048   |  // Modified copy of ins_next which handles function header dispatch, too.
2049   |  ldrb RBw, [PC, # OFS_OP]
2050   |   ldr INSw, [PC], #4
2051   |    st_vmstate CARG4w
2052   |  cmn CARG1w, #17                    // Static dispatch?
2053   |  beq >5
2054   |  cmp RBw, #BC_FUNCC+2               // Fast function?
2055   |   add TMP1, GL, INS, uxtb #3
2056   |  bhs >4
2057   |2:
2058   |  cmp RBw, #BC_FUNCF                 // Function header?
2059   |  add TMP0, GL, RB, uxtb #3
2060   |  ldr RB, [TMP0, #GG_G2DISP]
2061   |   decode_RA RA, INS
2062   |   lsr TMP0, INS, #16
2063   |   csel RC, TMP0, RC, lo
2064   |   blo >3
2065   |   ldr CARG3, [BASE, FRAME_FUNC]
2066   |   sub RC, RC, #8
2067   |   add RA, BASE, RA, lsl #3  // Yes: RA = BASE+framesize*8, RC = nargs*8
2068   |   and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2069   |3:
2070   |  br_auth RB
2071   |
2072   |4:  // Check frame below fast function.
2073   |  ldr CARG1, [BASE, FRAME_PC]
2074   |  ands CARG2, CARG1, #FRAME_TYPE
2075   |  bne <2                     // Trace stitching continuation?
2076   |  // Otherwise set KBASE for Lua function below fast function.
2077   |  ldr CARG3w, [CARG1, #-4]
2078   |  decode_RA CARG1, CARG3
2079   |  sub CARG2, BASE, CARG1, lsl #3
2080   |  ldr LFUNC:CARG3, [CARG2, #-32]
2081   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2082   |  ldr CARG3, LFUNC:CARG3->pc
2083   |  ldr KBASE, [CARG3, #PC2PROTO(k)]
2084   |  b <2
2085   |
2086   |5:  // Dispatch to static entry of original ins replaced by BC_JLOOP.
2087   |  ldr RA, [GL, #GL_J(trace)]
2088   |  decode_RD RC, INS
2089   |  ldr TRACE:RA, [RA, RC, lsl #3]
2090   |  ldr INSw, TRACE:RA->startins
2091   |  add TMP0, GL, INS, uxtb #3
2092   |   decode_RA RA, INS
2093   |  ldr RB, [TMP0, #GG_G2DISP+GG_DISP2STATIC]
2094   |   decode_RD RC, INS
2095   |  br_auth RB
2096   |
2097   |9:  // Rethrow error from the right C frame.
2098   |  neg CARG2w, CARG1w
2099   |  mov CARG1, L
2100   |  bl extern lj_err_trace             // (lua_State *L, int errcode)
2101   |.endif
2102   |
2103   |//-----------------------------------------------------------------------
2104   |//-- Math helper functions ----------------------------------------------
2105   |//-----------------------------------------------------------------------
2106   |
2107   |  // int lj_vm_modi(int dividend, int divisor);
2108   |->vm_modi:
2109   |    eor CARG4w, CARG1w, CARG2w
2110   |    cmp CARG4w, #0
2111   |  eor CARG3w, CARG1w, CARG1w, asr #31
2112   |   eor CARG4w, CARG2w, CARG2w, asr #31
2113   |  sub CARG3w, CARG3w, CARG1w, asr #31
2114   |   sub CARG4w, CARG4w, CARG2w, asr #31
2115   |  udiv CARG1w, CARG3w, CARG4w
2116   |  msub CARG1w, CARG1w, CARG4w, CARG3w
2117   |    ccmp CARG1w, #0, #4, mi
2118   |    sub CARG3w, CARG1w, CARG4w
2119   |    csel CARG1w, CARG1w, CARG3w, eq
2120   |  eor CARG3w, CARG1w, CARG2w
2121   |  cmp CARG3w, #0
2122   |  cneg CARG1w, CARG1w, mi
2123   |  ret
2124   |
2125   |//-----------------------------------------------------------------------
2126   |//-- Miscellaneous functions --------------------------------------------
2127   |//-----------------------------------------------------------------------
2128   |
2129   |.define NEXT_TAB,            TAB:CARG1
2130   |.define NEXT_RES,            CARG1
2131   |.define NEXT_IDX,            CARG2w
2132   |.define NEXT_LIM,            CARG3w
2133   |.define NEXT_TMP0,           TMP0
2134   |.define NEXT_TMP0w,          TMP0w
2135   |.define NEXT_TMP1,           TMP1
2136   |.define NEXT_TMP1w,          TMP1w
2137   |.define NEXT_RES_PTR,        sp
2138   |.define NEXT_RES_VAL,        [sp]
2139   |.define NEXT_RES_KEY,        [sp, #8]
2140   |
2141   |// TValue *lj_vm_next(GCtab *t, uint32_t idx)
2142   |// Next idx returned in CRET2w.
2143   |->vm_next:
2144   |.if JIT
2145   |  ldr NEXT_LIM, NEXT_TAB->asize
2146   |   ldr NEXT_TMP1, NEXT_TAB->array
2147   |1:  // Traverse array part.
2148   |  subs NEXT_TMP0w, NEXT_IDX, NEXT_LIM
2149   |  bhs >5                             // Index points after array part?
2150   |  ldr NEXT_TMP0, [NEXT_TMP1, NEXT_IDX, uxtw #3]
2151   |  cmn NEXT_TMP0, #-LJ_TNIL
2152   |   cinc NEXT_IDX, NEXT_IDX, eq
2153   |  beq <1                             // Skip holes in array part.
2154   |  str NEXT_TMP0, NEXT_RES_VAL
2155   |   movz NEXT_TMP0w, #(LJ_TISNUM>>1)&0xffff, lsl #16
2156   |   stp NEXT_IDX, NEXT_TMP0w, NEXT_RES_KEY
2157   |  add NEXT_IDX, NEXT_IDX, #1
2158   |  mov NEXT_RES, NEXT_RES_PTR
2159   |4:
2160   |  ret
2161   |
2162   |5:  // Traverse hash part.
2163   |  ldr NEXT_TMP1w, NEXT_TAB->hmask
2164   |   ldr NODE:NEXT_RES, NEXT_TAB->node
2165   |   add NEXT_TMP0w, NEXT_TMP0w, NEXT_TMP0w, lsl #1
2166   |  add NEXT_LIM, NEXT_LIM, NEXT_TMP1w
2167   |   add NODE:NEXT_RES, NODE:NEXT_RES, NEXT_TMP0w, uxtw #3
2168   |6:
2169   |  cmp NEXT_IDX, NEXT_LIM
2170   |  bhi >9
2171   |  ldr NEXT_TMP0, NODE:NEXT_RES->val
2172   |  cmn NEXT_TMP0, #-LJ_TNIL
2173   |   add NEXT_IDX, NEXT_IDX, #1
2174   |  bne <4
2175   |  // Skip holes in hash part.
2176   |  add NODE:NEXT_RES, NODE:NEXT_RES, #sizeof(Node)
2177   |  b <6
2178   |
2179   |9:  // End of iteration. Set the key to nil (not the value).
2180   |  movn NEXT_TMP0, #0
2181   |  str NEXT_TMP0, NEXT_RES_KEY
2182   |  mov NEXT_RES, NEXT_RES_PTR
2183   |  ret
2184   |.endif
2185   |
2186   |//-----------------------------------------------------------------------
2187   |//-- FFI helper functions -----------------------------------------------
2188   |//-----------------------------------------------------------------------
2189   |
2190   |// Handler for callback functions.
2191   |// Saveregs already performed. Callback slot number in w9, g in x10.
2192   |->vm_ffi_callback:
2193   |.if FFI
2194   |.type CTSTATE, CTState, PC
2195   |  saveregs
2196   |  ldr CTSTATE, GL:x10->ctype_state
2197   |  mov GL, x10
2198   |    add x10, sp, # CFRAME_SPACE
2199   |  str w9, CTSTATE->cb.slot
2200   |  stp x0, x1, CTSTATE->cb.gpr[0]
2201   |   stp d0, d1, CTSTATE->cb.fpr[0]
2202   |  stp x2, x3, CTSTATE->cb.gpr[2]
2203   |   stp d2, d3, CTSTATE->cb.fpr[2]
2204   |  stp x4, x5, CTSTATE->cb.gpr[4]
2205   |   stp d4, d5, CTSTATE->cb.fpr[4]
2206   |  stp x6, x7, CTSTATE->cb.gpr[6]
2207   |   stp d6, d7, CTSTATE->cb.fpr[6]
2208   |    str x10, CTSTATE->cb.stack
2209   |  mov CARG1, CTSTATE
2210   |   str CTSTATE, SAVE_PC              // Any value outside of bytecode is ok.
2211   |  mov CARG2, sp
2212   |  bl extern lj_ccallback_enter       // (CTState *cts, void *cf)
2213   |  // Returns lua_State *.
2214   |  ldp BASE, RC, L:CRET1->base
2215   |   init_constants
2216   |   mov L, CRET1
2217   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
2218   |  sub RC, RC, BASE
2219   |   st_vmstate ST_INTERP
2220   |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2221   |  ins_callt
2222   |.endif
2223   |
2224   |->cont_ffi_callback:                 // Return from FFI callback.
2225   |.if FFI
2226   |  ldr CTSTATE, GL->ctype_state
2227   |   stp BASE, CARG4, L->base
2228   |  str L, CTSTATE->L
2229   |  mov CARG1, CTSTATE
2230   |  mov CARG2, RA
2231   |  bl extern lj_ccallback_leave       // (CTState *cts, TValue *o)
2232   |  ldp x0, x1, CTSTATE->cb.gpr[0]
2233   |   ldp d0, d1, CTSTATE->cb.fpr[0]
2234   |  b ->vm_leave_unw
2235   |.endif
2236   |
2237   |->vm_ffi_call:                       // Call C function via FFI.
2238   |  // Caveat: needs special frame unwinding, see below.
2239   |.if FFI
2240   |  .type CCSTATE, CCallState, x19
2241   |  sp_auth
2242   |  stp_unwind CCSTATE, x20, [sp, #-32]!
2243   |  stp fp, lr, [sp, #16]
2244   |  add fp, sp, #16
2245   |  mov CCSTATE, x0
2246   |  ldr TMP0w, CCSTATE:x0->spadj
2247   |   ldrb TMP1w, CCSTATE->nsp
2248   |    add TMP2, CCSTATE, #offsetof(CCallState, stack)
2249   |   subs TMP1, TMP1, #8
2250   |    ldr TMP3, CCSTATE->func
2251   |  sub sp, sp, TMP0
2252   |   bmi >2
2253   |1:  // Copy stack slots
2254   |  ldr TMP0, [TMP2, TMP1]
2255   |  str TMP0, [sp, TMP1]
2256   |  subs TMP1, TMP1, #8
2257   |  bpl <1
2258   |2:
2259   |  ldp x0, x1, CCSTATE->gpr[0]
2260   |   ldp d0, d1, CCSTATE->fpr[0]
2261   |  ldp x2, x3, CCSTATE->gpr[2]
2262   |   ldp d2, d3, CCSTATE->fpr[2]
2263   |  ldp x4, x5, CCSTATE->gpr[4]
2264   |   ldp d4, d5, CCSTATE->fpr[4]
2265   |  ldp x6, x7, CCSTATE->gpr[6]
2266   |   ldp d6, d7, CCSTATE->fpr[6]
2267   |  ldr x8, CCSTATE->retp
2268   |  blr_auth TMP3
2269   |  sub sp, fp, #16
2270   |  stp x0, x1, CCSTATE->gpr[0]
2271   |   stp d0, d1, CCSTATE->fpr[0]
2272   |   stp d2, d3, CCSTATE->fpr[2]
2273   |  ldp fp, lr, [sp, #16]
2274   |  ldp_unwind CCSTATE, x20, [sp], #32
2275   |  ret_auth
2276   |.endif
2277   |// Note: vm_ffi_call must be the last function in this object file!
2278   |
2279   |//-----------------------------------------------------------------------
2282 /* Generate the code for a single instruction. */
2283 static void build_ins(BuildCtx *ctx, BCOp op, int defop)
2285   int vk = 0;
2286   |=>defop:
2288   switch (op) {
2290   /* -- Comparison ops ---------------------------------------------------- */
2292   /* Remember: all ops branch for a true comparison, fall through otherwise. */
2294   case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT:
2295     |  // RA = src1, RC = src2, JMP with RC = target
2296     |  ldr CARG1, [BASE, RA, lsl #3]
2297     |    ldrh RBw, [PC, # OFS_RD]
2298     |   ldr CARG2, [BASE, RC, lsl #3]
2299     |    add PC, PC, #4
2300     |    add RB, PC, RB, lsl #2
2301     |    sub RB, RB, #0x20000
2302     |  checkint CARG1, >3
2303     |   checkint CARG2, >4
2304     |  cmp CARG1w, CARG2w
2305     if (op == BC_ISLT) {
2306       |  csel PC, RB, PC, lt
2307     } else if (op == BC_ISGE) {
2308       |  csel PC, RB, PC, ge
2309     } else if (op == BC_ISLE) {
2310       |  csel PC, RB, PC, le
2311     } else {
2312       |  csel PC, RB, PC, gt
2313     }
2314     |1:
2315     |  ins_next
2316     |
2317     |3:  // RA not int.
2318     |    ldr FARG1, [BASE, RA, lsl #3]
2319     |  blo ->vmeta_comp
2320     |    ldr FARG2, [BASE, RC, lsl #3]
2321     |   cmp TISNUMhi, CARG2, lsr #32
2322     |   bhi >5
2323     |   bne ->vmeta_comp
2324     |  // RA number, RC int.
2325     |  scvtf FARG2, CARG2w
2326     |  b >5
2327     |
2328     |4:  // RA int, RC not int
2329     |    ldr FARG2, [BASE, RC, lsl #3]
2330     |   blo ->vmeta_comp
2331     |  // RA int, RC number.
2332     |  scvtf FARG1, CARG1w
2333     |
2334     |5:  // RA number, RC number
2335     |  fcmp FARG1, FARG2
2336     |  // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't.
2337     if (op == BC_ISLT) {
2338       |  csel PC, RB, PC, lo
2339     } else if (op == BC_ISGE) {
2340       |  csel PC, RB, PC, hs
2341     } else if (op == BC_ISLE) {
2342       |  csel PC, RB, PC, ls
2343     } else {
2344       |  csel PC, RB, PC, hi
2345     }
2346     |  b <1
2347     break;
2349   case BC_ISEQV: case BC_ISNEV:
2350     vk = op == BC_ISEQV;
2351     |  // RA = src1, RC = src2, JMP with RC = target
2352     |  ldr CARG1, [BASE, RA, lsl #3]
2353     |   add RC, BASE, RC, lsl #3
2354     |    ldrh RBw, [PC, # OFS_RD]
2355     |   ldr CARG3, [RC]
2356     |    add PC, PC, #4
2357     |    add RB, PC, RB, lsl #2
2358     |    sub RB, RB, #0x20000
2359     |  asr ITYPE, CARG3, #47
2360     |  cmn ITYPE, #-LJ_TISNUM
2361     if (vk) {
2362       |  bls ->BC_ISEQN_Z
2363     } else {
2364       |  bls ->BC_ISNEN_Z
2365     }
2366     |  // RC is not a number.
2367     |   asr TMP0, CARG1, #47
2368     |.if FFI
2369     |  // Check if RC or RA is a cdata.
2370     |  cmn ITYPE, #-LJ_TCDATA
2371     |   ccmn TMP0, #-LJ_TCDATA, #4, ne
2372     |  beq ->vmeta_equal_cd
2373     |.endif
2374     |  cmp CARG1, CARG3
2375     |  bne >2
2376     |  // Tag and value are equal.
2377     if (vk) {
2378       |->BC_ISEQV_Z:
2379       |  mov PC, RB                     // Perform branch.
2380     }
2381     |1:
2382     |  ins_next
2383     |
2384     |2:  // Check if the tags are the same and it's a table or userdata.
2385     |  cmp ITYPE, TMP0
2386     |  ccmn ITYPE, #-LJ_TISTABUD, #2, eq
2387     if (vk) {
2388       |  bhi <1
2389     } else {
2390       |  bhi ->BC_ISEQV_Z               // Reuse code from opposite instruction.
2391     }
2392     |  // Different tables or userdatas. Need to check __eq metamethod.
2393     |  // Field metatable must be at same offset for GCtab and GCudata!
2394     |  and TAB:CARG2, CARG1, #LJ_GCVMASK
2395     |  ldr TAB:TMP2, TAB:CARG2->metatable
2396     if (vk) {
2397       |  cbz TAB:TMP2, <1               // No metatable?
2398       |  ldrb TMP1w, TAB:TMP2->nomm
2399       |   mov CARG4, #0                 // ne = 0
2400       |  tbnz TMP1w, #MM_eq, <1         // 'no __eq' flag set: done.
2401     } else {
2402       |  cbz TAB:TMP2, ->BC_ISEQV_Z     // No metatable?
2403       |  ldrb TMP1w, TAB:TMP2->nomm
2404       |   mov CARG4, #1                 // ne = 1.
2405       |  tbnz TMP1w, #MM_eq, ->BC_ISEQV_Z       // 'no __eq' flag set: done.
2406     }
2407     |  b ->vmeta_equal
2408     break;
2410   case BC_ISEQS: case BC_ISNES:
2411     vk = op == BC_ISEQS;
2412     |  // RA = src, RC = str_const (~), JMP with RC = target
2413     |  ldr CARG1, [BASE, RA, lsl #3]
2414     |   mvn RC, RC
2415     |    ldrh RBw, [PC, # OFS_RD]
2416     |   ldr CARG2, [KBASE, RC, lsl #3]
2417     |    add PC, PC, #4
2418     |   movn TMP0, #~LJ_TSTR
2419     |.if FFI
2420     |  asr ITYPE, CARG1, #47
2421     |.endif
2422     |    add RB, PC, RB, lsl #2
2423     |   add CARG2, CARG2, TMP0, lsl #47
2424     |    sub RB, RB, #0x20000
2425     |.if FFI
2426     |  cmn ITYPE, #-LJ_TCDATA
2427     |  beq ->vmeta_equal_cd
2428     |.endif
2429     |  cmp CARG1, CARG2
2430     if (vk) {
2431       |  csel PC, RB, PC, eq
2432     } else {
2433       |  csel PC, RB, PC, ne
2434     }
2435     |  ins_next
2436     break;
2438   case BC_ISEQN: case BC_ISNEN:
2439     vk = op == BC_ISEQN;
2440     |  // RA = src, RC = num_const (~), JMP with RC = target
2441     |  ldr CARG1, [BASE, RA, lsl #3]
2442     |   add RC, KBASE, RC, lsl #3
2443     |    ldrh RBw, [PC, # OFS_RD]
2444     |   ldr CARG3, [RC]
2445     |    add PC, PC, #4
2446     |    add RB, PC, RB, lsl #2
2447     |    sub RB, RB, #0x20000
2448     if (vk) {
2449       |->BC_ISEQN_Z:
2450     } else {
2451       |->BC_ISNEN_Z:
2452     }
2453     |  checkint CARG1, >4
2454     |   checkint CARG3, >6
2455     |  cmp CARG1w, CARG3w
2456     |1:
2457     if (vk) {
2458       |  csel PC, RB, PC, eq
2459       |2:
2460     } else {
2461       |2:
2462       |  csel PC, RB, PC, ne
2463     }
2464     |3:
2465     |  ins_next
2466     |
2467     |4:  // RA not int.
2468     |.if FFI
2469     |  blo >7
2470     |.else
2471     |  blo <2
2472     |.endif
2473     |    ldr FARG1, [BASE, RA, lsl #3]
2474     |    ldr FARG2, [RC]
2475     |   cmp TISNUMhi, CARG3, lsr #32
2476     |   bne >5
2477     |  // RA number, RC int.
2478     |  scvtf FARG2, CARG3w
2479     |5:
2480     |  // RA number, RC number.
2481     |  fcmp FARG1, FARG2
2482     |  b <1
2483     |
2484     |6:  // RA int, RC number
2485     |  ldr FARG2, [RC]
2486     |  scvtf FARG1, CARG1w
2487     |  fcmp FARG1, FARG2
2488     |  b <1
2489     |
2490     |.if FFI
2491     |7:
2492     |  asr ITYPE, CARG1, #47
2493     |  cmn ITYPE, #-LJ_TCDATA
2494     |  bne <2
2495     |  b ->vmeta_equal_cd
2496     |.endif
2497     break;
2499   case BC_ISEQP: case BC_ISNEP:
2500     vk = op == BC_ISEQP;
2501     |  // RA = src, RC = primitive_type (~), JMP with RC = target
2502     |  ldr TMP0, [BASE, RA, lsl #3]
2503     |   ldrh RBw, [PC, # OFS_RD]
2504     |   add PC, PC, #4
2505     |  add RC, RC, #1
2506     |   add RB, PC, RB, lsl #2
2507     |.if FFI
2508     |  asr ITYPE, TMP0, #47
2509     |  cmn ITYPE, #-LJ_TCDATA
2510     |  beq ->vmeta_equal_cd
2511     |  cmn RC, ITYPE
2512     |.else
2513     |  cmn RC, TMP0, asr #47
2514     |.endif
2515     |   sub RB, RB, #0x20000
2516     if (vk) {
2517       |  csel PC, RB, PC, eq
2518     } else {
2519       |  csel PC, RB, PC, ne
2520     }
2521     |  ins_next
2522     break;
2524   /* -- Unary test and copy ops ------------------------------------------- */
2526   case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF:
2527     |  // RA = dst or unused, RC = src, JMP with RC = target
2528     |   ldrh RBw, [PC, # OFS_RD]
2529     |  ldr TMP0, [BASE, RC, lsl #3]
2530     |   add PC, PC, #4
2531     |  mov_false TMP1
2532     |   add RB, PC, RB, lsl #2
2533     |  cmp TMP0, TMP1
2534     |   sub RB, RB, #0x20000
2535     if (op == BC_ISTC || op == BC_IST) {
2536       if (op == BC_ISTC) {
2537         |  csel RA, RA, RC, lo
2538       }
2539       |  csel PC, RB, PC, lo
2540     } else {
2541       if (op == BC_ISFC) {
2542         |  csel RA, RA, RC, hs
2543       }
2544       |  csel PC, RB, PC, hs
2545     }
2546     if (op == BC_ISTC || op == BC_ISFC) {
2547       |  str TMP0, [BASE, RA, lsl #3]
2548     }
2549     |  ins_next
2550     break;
2552   case BC_ISTYPE:
2553     |  // RA = src, RC = -type
2554     |  ldr TMP0, [BASE, RA, lsl #3]
2555     |  cmn RC, TMP0, asr #47
2556     |  bne ->vmeta_istype
2557     |  ins_next
2558     break;
2559   case BC_ISNUM:
2560     |  // RA = src, RC = -(TISNUM-1)
2561     |  ldr TMP0, [BASE, RA]
2562     |  checknum TMP0, ->vmeta_istype
2563     |  ins_next
2564     break;
2566   /* -- Unary ops --------------------------------------------------------- */
2568   case BC_MOV:
2569     |  // RA = dst, RC = src
2570     |  ldr TMP0, [BASE, RC, lsl #3]
2571     |  str TMP0, [BASE, RA, lsl #3]
2572     |  ins_next
2573     break;
2574   case BC_NOT:
2575     |  // RA = dst, RC = src
2576     |  ldr TMP0, [BASE, RC, lsl #3]
2577     |   mov_false TMP1
2578     |   mov_true TMP2
2579     |  cmp TMP0, TMP1
2580     |  csel TMP0, TMP1, TMP2, lo
2581     |  str TMP0, [BASE, RA, lsl #3]
2582     |  ins_next
2583     break;
2584   case BC_UNM:
2585     |  // RA = dst, RC = src
2586     |  ldr TMP0, [BASE, RC, lsl #3]
2587     |  asr ITYPE, TMP0, #47
2588     |  cmn ITYPE, #-LJ_TISNUM
2589     |  bhi ->vmeta_unm
2590     |  eor TMP0, TMP0, #U64x(80000000,00000000)
2591     |  bne >5
2592     |  negs TMP0w, TMP0w
2593     |   movz CARG3, #0x41e0, lsl #48    // 2^31.
2594     |   add_TISNUM TMP0, TMP0
2595     |  csel TMP0, TMP0, CARG3, vc
2596     |5:
2597     |  str TMP0, [BASE, RA, lsl #3]
2598     |  ins_next
2599     break;
2600   case BC_LEN:
2601     |  // RA = dst, RC = src
2602     |  ldr CARG1, [BASE, RC, lsl #3]
2603     |  asr ITYPE, CARG1, #47
2604     |  cmn ITYPE, #-LJ_TSTR
2605     |   and CARG1, CARG1, #LJ_GCVMASK
2606     |  bne >2
2607     |  ldr CARG1w, STR:CARG1->len
2608     |1:
2609     |  add_TISNUM CARG1, CARG1
2610     |  str CARG1, [BASE, RA, lsl #3]
2611     |  ins_next
2612     |
2613     |2:
2614     |  cmn ITYPE, #-LJ_TTAB
2615     |  bne ->vmeta_len
2616 #if LJ_52
2617     |  ldr TAB:CARG2, TAB:CARG1->metatable
2618     |  cbnz TAB:CARG2, >9
2619     |3:
2620 #endif
2621     |->BC_LEN_Z:
2622     |  bl extern lj_tab_len             // (GCtab *t)
2623     |  // Returns uint32_t (but less than 2^31).
2624     |  b <1
2625     |
2626 #if LJ_52
2627     |9:
2628     |  ldrb TMP1w, TAB:CARG2->nomm
2629     |  tbnz TMP1w, #MM_len, <3          // 'no __len' flag set: done.
2630     |  b ->vmeta_len
2631 #endif
2632     break;
2634   /* -- Binary ops -------------------------------------------------------- */
2636     |.macro ins_arithcheck_int, target
2637     |  checkint CARG1, target
2638     |  checkint CARG2, target
2639     |.endmacro
2640     |
2641     |.macro ins_arithcheck_num, target
2642     |  checknum CARG1, target
2643     |  checknum CARG2, target
2644     |.endmacro
2645     |
2646     |.macro ins_arithcheck_nzdiv, target
2647     |  cbz CARG2w, target
2648     |.endmacro
2649     |
2650     |.macro ins_arithhead
2651     ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN);
2652     ||if (vk == 1) {
2653     |   and RC, RC, #255
2654     |    decode_RB RB, INS
2655     ||} else {
2656     |   decode_RB RB, INS
2657     |    and RC, RC, #255
2658     ||}
2659     |.endmacro
2660     |
2661     |.macro ins_arithload, reg1, reg2
2662     |  // RA = dst, RB = src1, RC = src2 | num_const
2663     ||switch (vk) {
2664     ||case 0:
2665     |   ldr reg1, [BASE, RB, lsl #3]
2666     |    ldr reg2, [KBASE, RC, lsl #3]
2667     ||  break;
2668     ||case 1:
2669     |   ldr reg1, [KBASE, RC, lsl #3]
2670     |    ldr reg2, [BASE, RB, lsl #3]
2671     ||  break;
2672     ||default:
2673     |   ldr reg1, [BASE, RB, lsl #3]
2674     |    ldr reg2, [BASE, RC, lsl #3]
2675     ||  break;
2676     ||}
2677     |.endmacro
2678     |
2679     |.macro ins_arithfallback, ins
2680     ||switch (vk) {
2681     ||case 0:
2682     |   ins ->vmeta_arith_vn
2683     ||  break;
2684     ||case 1:
2685     |   ins ->vmeta_arith_nv
2686     ||  break;
2687     ||default:
2688     |   ins ->vmeta_arith_vv
2689     ||  break;
2690     ||}
2691     |.endmacro
2692     |
2693     |.macro ins_arithmod, res, reg1, reg2
2694     |  fdiv d2, reg1, reg2
2695     |  frintm d2, d2
2696     |  // Cannot use fmsub, because FMA is not enabled by default.
2697     |  fmul d2, d2, reg2
2698     |  fsub res, reg1, d2
2699     |.endmacro
2700     |
2701     |.macro ins_arithdn, intins, fpins
2702     |  ins_arithhead
2703     |  ins_arithload CARG1, CARG2
2704     |  ins_arithcheck_int >5
2705     |.if "intins" == "smull"
2706     |  smull CARG1, CARG1w, CARG2w
2707     |  cmp CARG1, CARG1, sxtw
2708     |   mov CARG1w, CARG1w
2709     |  ins_arithfallback bne
2710     |.elif "intins" == "ins_arithmodi"
2711     |  ins_arithfallback ins_arithcheck_nzdiv
2712     |  bl ->vm_modi
2713     |.else
2714     |  intins CARG1w, CARG1w, CARG2w
2715     |  ins_arithfallback bvs
2716     |.endif
2717     |  add_TISNUM CARG1, CARG1
2718     |  str CARG1, [BASE, RA, lsl #3]
2719     |4:
2720     |  ins_next
2721     |
2722     |5:  // FP variant.
2723     |  ins_arithload FARG1, FARG2
2724     |  ins_arithfallback ins_arithcheck_num
2725     |  fpins FARG1, FARG1, FARG2
2726     |  str FARG1, [BASE, RA, lsl #3]
2727     |  b <4
2728     |.endmacro
2729     |
2730     |.macro ins_arithfp, fpins
2731     |  ins_arithhead
2732     |  ins_arithload CARG1, CARG2
2733     |  ins_arithload FARG1, FARG2
2734     |  ins_arithfallback ins_arithcheck_num
2735     |.if "fpins" == "fpow"
2736     |  bl extern pow
2737     |.else
2738     |  fpins FARG1, FARG1, FARG2
2739     |.endif
2740     |  str FARG1, [BASE, RA, lsl #3]
2741     |  ins_next
2742     |.endmacro
2744   case BC_ADDVN: case BC_ADDNV: case BC_ADDVV:
2745     |  ins_arithdn adds, fadd
2746     break;
2747   case BC_SUBVN: case BC_SUBNV: case BC_SUBVV:
2748     |  ins_arithdn subs, fsub
2749     break;
2750   case BC_MULVN: case BC_MULNV: case BC_MULVV:
2751     |  ins_arithdn smull, fmul
2752     break;
2753   case BC_DIVVN: case BC_DIVNV: case BC_DIVVV:
2754     |  ins_arithfp fdiv
2755     break;
2756   case BC_MODVN: case BC_MODNV: case BC_MODVV:
2757     |  ins_arithdn ins_arithmodi, ins_arithmod
2758     break;
2759   case BC_POW:
2760     |  // NYI: (partial) integer arithmetic.
2761     |  ins_arithfp fpow
2762     break;
2764   case BC_CAT:
2765     |  decode_RB RB, INS
2766     |   and RC, RC, #255
2767     |  // RA = dst, RB = src_start, RC = src_end
2768     |   str BASE, L->base
2769     |  sub CARG3, RC, RB
2770     |  add CARG2, BASE, RC, lsl #3
2771     |->BC_CAT_Z:
2772     |  // RA = dst, CARG2 = top-1, CARG3 = left
2773     |  mov CARG1, L
2774     |   str PC, SAVE_PC
2775     |  bl extern lj_meta_cat            // (lua_State *L, TValue *top, int left)
2776     |  // Returns NULL (finished) or TValue * (metamethod).
2777     |  ldrb RBw, [PC, #-4+OFS_RB]
2778     |   ldr BASE, L->base
2779     |   cbnz CRET1, ->vmeta_binop
2780     |  ldr TMP0, [BASE, RB, lsl #3]
2781     |  str TMP0, [BASE, RA, lsl #3]     // Copy result to RA.
2782     |  ins_next
2783     break;
2785   /* -- Constant ops ------------------------------------------------------ */
2787   case BC_KSTR:
2788     |  // RA = dst, RC = str_const (~)
2789     |  mvn RC, RC
2790     |  ldr TMP0, [KBASE, RC, lsl #3]
2791     |   movn TMP1, #~LJ_TSTR
2792     |  add TMP0, TMP0, TMP1, lsl #47
2793     |  str TMP0, [BASE, RA, lsl #3]
2794     |  ins_next
2795     break;
2796   case BC_KCDATA:
2797     |.if FFI
2798     |  // RA = dst, RC = cdata_const (~)
2799     |  mvn RC, RC
2800     |  ldr TMP0, [KBASE, RC, lsl #3]
2801     |   movn TMP1, #~LJ_TCDATA
2802     |  add TMP0, TMP0, TMP1, lsl #47
2803     |  str TMP0, [BASE, RA, lsl #3]
2804     |  ins_next
2805     |.endif
2806     break;
2807   case BC_KSHORT:
2808     |  // RA = dst, RC = int16_literal
2809     |  sxth RCw, RCw
2810     |  add_TISNUM TMP0, RC
2811     |  str TMP0, [BASE, RA, lsl #3]
2812     |  ins_next
2813     break;
2814   case BC_KNUM:
2815     |  // RA = dst, RC = num_const
2816     |  ldr TMP0, [KBASE, RC, lsl #3]
2817     |  str TMP0, [BASE, RA, lsl #3]
2818     |  ins_next
2819     break;
2820   case BC_KPRI:
2821     |  // RA = dst, RC = primitive_type (~)
2822     |  mvn TMP0, RC, lsl #47
2823     |  str TMP0, [BASE, RA, lsl #3]
2824     |  ins_next
2825     break;
2826   case BC_KNIL:
2827     |  // RA = base, RC = end
2828     |  add RA, BASE, RA, lsl #3
2829     |   add RC, BASE, RC, lsl #3
2830     |  str TISNIL, [RA], #8
2831     |1:
2832     |   cmp RA, RC
2833     |  str TISNIL, [RA], #8
2834     |   blt <1
2835     |  ins_next_
2836     break;
2838   /* -- Upvalue and function ops ------------------------------------------ */
2840   case BC_UGET:
2841     |  // RA = dst, RC = uvnum
2842     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2843     |   add RC, RC, #offsetof(GCfuncL, uvptr)/8
2844     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2845     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RC, lsl #3]
2846     |  ldr CARG2, UPVAL:CARG2->v
2847     |  ldr TMP0, [CARG2]
2848     |  str TMP0, [BASE, RA, lsl #3]
2849     |  ins_next
2850     break;
2851   case BC_USETV:
2852     |  // RA = uvnum, RC = src
2853     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2854     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2855     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2856     |  ldr UPVAL:CARG1, [LFUNC:CARG2, RA, lsl #3]
2857     |   ldr CARG3, [BASE, RC, lsl #3]
2858     |    ldr CARG2, UPVAL:CARG1->v
2859     |  ldrb TMP2w, UPVAL:CARG1->marked
2860     |  ldrb TMP0w, UPVAL:CARG1->closed
2861     |    asr ITYPE, CARG3, #47
2862     |   str CARG3, [CARG2]
2863     |    add ITYPE, ITYPE, #-LJ_TISGCV
2864     |  tst TMP2w, #LJ_GC_BLACK          // isblack(uv)
2865     |  ccmp TMP0w, #0, #4, ne           // && uv->closed
2866     |    ccmn ITYPE, #-(LJ_TNUMX - LJ_TISGCV), #0, ne   // && tvisgcv(v)
2867     |  bhi >2
2868     |1:
2869     |  ins_next
2870     |
2871     |2:  // Check if new value is white.
2872     |  and GCOBJ:CARG3, CARG3, #LJ_GCVMASK
2873     |  ldrb TMP1w, GCOBJ:CARG3->gch.marked
2874     |  tst TMP1w, #LJ_GC_WHITES         // iswhite(str)
2875     |  beq <1
2876     |  // Crossed a write barrier. Move the barrier forward.
2877     |  mov CARG1, GL
2878     |  bl extern lj_gc_barrieruv        // (global_State *g, TValue *tv)
2879     |  b <1
2880     break;
2881   case BC_USETS:
2882     |  // RA = uvnum, RC = str_const (~)
2883     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2884     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2885     |    mvn RC, RC
2886     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2887     |  ldr UPVAL:CARG1, [LFUNC:CARG2, RA, lsl #3]
2888     |   ldr STR:CARG3, [KBASE, RC, lsl #3]
2889     |   movn TMP0, #~LJ_TSTR
2890     |    ldr CARG2, UPVAL:CARG1->v
2891     |  ldrb TMP2w, UPVAL:CARG1->marked
2892     |   add TMP0, STR:CARG3, TMP0, lsl #47
2893     |    ldrb TMP1w, STR:CARG3->marked
2894     |   str TMP0, [CARG2]
2895     |  tbnz TMP2w, #2, >2               // isblack(uv)
2896     |1:
2897     |  ins_next
2898     |
2899     |2:  // Check if string is white and ensure upvalue is closed.
2900     |  ldrb TMP0w, UPVAL:CARG1->closed
2901     |    tst TMP1w, #LJ_GC_WHITES       // iswhite(str)
2902     |  ccmp TMP0w, #0, #4, ne
2903     |  beq <1
2904     |  // Crossed a write barrier. Move the barrier forward.
2905     |  mov CARG1, GL
2906     |  bl extern lj_gc_barrieruv        // (global_State *g, TValue *tv)
2907     |  b <1
2908     break;
2909   case BC_USETN:
2910     |  // RA = uvnum, RC = num_const
2911     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2912     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2913     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2914     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA, lsl #3]
2915     |   ldr TMP0, [KBASE, RC, lsl #3]
2916     |  ldr CARG2, UPVAL:CARG2->v
2917     |   str TMP0, [CARG2]
2918     |  ins_next
2919     break;
2920   case BC_USETP:
2921     |  // RA = uvnum, RC = primitive_type (~)
2922     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2923     |   add RA, RA, #offsetof(GCfuncL, uvptr)/8
2924     |  and LFUNC:CARG2, CARG2, #LJ_GCVMASK
2925     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA, lsl #3]
2926     |   mvn TMP0, RC, lsl #47
2927     |  ldr CARG2, UPVAL:CARG2->v
2928     |   str TMP0, [CARG2]
2929     |  ins_next
2930     break;
2932   case BC_UCLO:
2933     |  // RA = level, RC = target
2934     |  ldr CARG3, L->openupval
2935     |   add RC, PC, RC, lsl #2
2936     |    str BASE, L->base
2937     |   sub PC, RC, #0x20000
2938     |  cbz CARG3, >1
2939     |  mov CARG1, L
2940     |  add CARG2, BASE, RA, lsl #3
2941     |  bl extern lj_func_closeuv        // (lua_State *L, TValue *level)
2942     |  ldr BASE, L->base
2943     |1:
2944     |  ins_next
2945     break;
2947   case BC_FNEW:
2948     |  // RA = dst, RC = proto_const (~) (holding function prototype)
2949     |  mvn RC, RC
2950     |   str BASE, L->base
2951     |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
2952     |    str PC, SAVE_PC
2953     |   ldr CARG2, [KBASE, RC, lsl #3]
2954     |    mov CARG1, L
2955     |  and LFUNC:CARG3, CARG3, #LJ_GCVMASK
2956     |  // (lua_State *L, GCproto *pt, GCfuncL *parent)
2957     |  bl extern lj_func_newL_gc
2958     |  // Returns GCfuncL *.
2959     |  ldr BASE, L->base
2960     |   movn TMP0, #~LJ_TFUNC
2961     |   add CRET1, CRET1, TMP0, lsl #47
2962     |  str CRET1, [BASE, RA, lsl #3]
2963     |  ins_next
2964     break;
2966   /* -- Table ops --------------------------------------------------------- */
2968   case BC_TNEW:
2969   case BC_TDUP:
2970     |  // RA = dst, RC = (hbits|asize) | tab_const (~)
2971     |  ldp CARG3, CARG4, GL->gc.total   // Assumes threshold follows total.
2972     |   str BASE, L->base
2973     |   str PC, SAVE_PC
2974     |   mov CARG1, L
2975     |  cmp CARG3, CARG4
2976     |  bhs >5
2977     |1:
2978     if (op == BC_TNEW) {
2979       |  and CARG2, RC, #0x7ff
2980       |   lsr CARG3, RC, #11
2981       |  cmp CARG2, #0x7ff
2982       |  mov TMP0, #0x801
2983       |  csel CARG2, CARG2, TMP0, ne
2984       |  bl extern lj_tab_new  // (lua_State *L, int32_t asize, uint32_t hbits)
2985       |  // Returns GCtab *.
2986     } else {
2987       |  mvn RC, RC
2988       |  ldr CARG2, [KBASE, RC, lsl #3]
2989       |  bl extern lj_tab_dup  // (lua_State *L, Table *kt)
2990       |  // Returns GCtab *.
2991     }
2992     |  ldr BASE, L->base
2993     |   movk CRET1, #(LJ_TTAB>>1)&0xffff, lsl #48
2994     |  str CRET1, [BASE, RA, lsl #3]
2995     |  ins_next
2996     |
2997     |5:
2998     |  bl extern lj_gc_step_fixtop  // (lua_State *L)
2999     |  mov CARG1, L
3000     |  b <1
3001     break;
3003   case BC_GGET:
3004     |  // RA = dst, RC = str_const (~)
3005   case BC_GSET:
3006     |  // RA = src, RC = str_const (~)
3007     |  ldr LFUNC:CARG1, [BASE, FRAME_FUNC]
3008     |   mvn RC, RC
3009     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3010     |  ldr TAB:CARG2, LFUNC:CARG1->env
3011     |   ldr STR:RC, [KBASE, RC, lsl #3]
3012     if (op == BC_GGET) {
3013       |  b ->BC_TGETS_Z
3014     } else {
3015       |  b ->BC_TSETS_Z
3016     }
3017     break;
3019   case BC_TGETV:
3020     |  decode_RB RB, INS
3021     |   and RC, RC, #255
3022     |  // RA = dst, RB = table, RC = key
3023     |  ldr CARG2, [BASE, RB, lsl #3]
3024     |   ldr TMP1, [BASE, RC, lsl #3]
3025     |  checktab CARG2, ->vmeta_tgetv
3026     |  checkint TMP1, >9                // Integer key?
3027     |  ldr CARG3, TAB:CARG2->array
3028     |   ldr CARG1w, TAB:CARG2->asize
3029     |  add CARG3, CARG3, TMP1, uxtw #3
3030     |   cmp TMP1w, CARG1w               // In array part?
3031     |   bhs ->vmeta_tgetv
3032     |  ldr TMP0, [CARG3]
3033     |  cmp_nil TMP0
3034     |  beq >5
3035     |1:
3036     |  str TMP0, [BASE, RA, lsl #3]
3037     |  ins_next
3038     |
3039     |5:  // Check for __index if table value is nil.
3040     |  ldr TAB:CARG1, TAB:CARG2->metatable
3041     |  cbz TAB:CARG1, <1                // No metatable: done.
3042     |  ldrb TMP1w, TAB:CARG1->nomm
3043     |  tbnz TMP1w, #MM_index, <1        // 'no __index' flag set: done.
3044     |  b ->vmeta_tgetv
3045     |
3046     |9:
3047     |  asr ITYPE, TMP1, #47
3048     |  cmn ITYPE, #-LJ_TSTR             // String key?
3049     |  bne ->vmeta_tgetv
3050     |   and STR:RC, TMP1, #LJ_GCVMASK
3051     |  b ->BC_TGETS_Z
3052     break;
3053   case BC_TGETS:
3054     |  decode_RB RB, INS
3055     |   and RC, RC, #255
3056     |  // RA = dst, RB = table, RC = str_const (~)
3057     |  ldr CARG2, [BASE, RB, lsl #3]
3058     |   mvn RC, RC
3059     |   ldr STR:RC, [KBASE, RC, lsl #3]
3060     |  checktab CARG2, ->vmeta_tgets1
3061     |->BC_TGETS_Z:
3062     |  // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = dst
3063     |  ldr TMP1w, TAB:CARG2->hmask
3064     |   ldr TMP2w, STR:RC->sid
3065     |    ldr NODE:CARG3, TAB:CARG2->node
3066     |  and TMP1w, TMP1w, TMP2w          // idx = str->sid & tab->hmask
3067     |  add TMP1, TMP1, TMP1, lsl #1
3068     |  movn CARG4, #~LJ_TSTR
3069     |    add NODE:CARG3, NODE:CARG3, TMP1, lsl #3  // node = tab->node + idx*3*8
3070     |  add CARG4, STR:RC, CARG4, lsl #47        // Tagged key to look for.
3071     |1:
3072     |  ldp TMP0, CARG1, NODE:CARG3->val
3073     |   ldr NODE:CARG3, NODE:CARG3->next
3074     |  cmp CARG1, CARG4
3075     |  bne >4
3076     |  cmp_nil TMP0
3077     |  beq >5
3078     |3:
3079     |  str TMP0, [BASE, RA, lsl #3]
3080     |  ins_next
3081     |
3082     |4:  // Follow hash chain.
3083     |  cbnz NODE:CARG3, <1
3084     |  // End of hash chain: key not found, nil result.
3085     |   mov_nil TMP0
3086     |
3087     |5:  // Check for __index if table value is nil.
3088     |  ldr TAB:CARG1, TAB:CARG2->metatable
3089     |  cbz TAB:CARG1, <3                // No metatable: done.
3090     |  ldrb TMP1w, TAB:CARG1->nomm
3091     |  tbnz TMP1w, #MM_index, <3        // 'no __index' flag set: done.
3092     |  b ->vmeta_tgets
3093     break;
3094   case BC_TGETB:
3095     |  decode_RB RB, INS
3096     |   and RC, RC, #255
3097     |  // RA = dst, RB = table, RC = index
3098     |  ldr CARG2, [BASE, RB, lsl #3]
3099     |  checktab CARG2, ->vmeta_tgetb
3100     |  ldr CARG3, TAB:CARG2->array
3101     |   ldr CARG1w, TAB:CARG2->asize
3102     |  add CARG3, CARG3, RC, lsl #3
3103     |   cmp RCw, CARG1w                 // In array part?
3104     |   bhs ->vmeta_tgetb
3105     |  ldr TMP0, [CARG3]
3106     |  cmp_nil TMP0
3107     |  beq >5
3108     |1:
3109     |  str TMP0, [BASE, RA, lsl #3]
3110     |  ins_next
3111     |
3112     |5:  // Check for __index if table value is nil.
3113     |  ldr TAB:CARG1, TAB:CARG2->metatable
3114     |  cbz TAB:CARG1, <1                // No metatable: done.
3115     |  ldrb TMP1w, TAB:CARG1->nomm
3116     |  tbnz TMP1w, #MM_index, <1        // 'no __index' flag set: done.
3117     |  b ->vmeta_tgetb
3118     break;
3119   case BC_TGETR:
3120     |  decode_RB RB, INS
3121     |   and RC, RC, #255
3122     |  // RA = dst, RB = table, RC = key
3123     |  ldr CARG1, [BASE, RB, lsl #3]
3124     |   ldr TMP1, [BASE, RC, lsl #3]
3125     |  and TAB:CARG1, CARG1, #LJ_GCVMASK
3126     |  ldr CARG3, TAB:CARG1->array
3127     |   ldr TMP2w, TAB:CARG1->asize
3128     |  add CARG3, CARG3, TMP1w, uxtw #3
3129     |   cmp TMP1w, TMP2w                // In array part?
3130     |   bhs ->vmeta_tgetr
3131     |  ldr TMP0, [CARG3]
3132     |->BC_TGETR_Z:
3133     |  str TMP0, [BASE, RA, lsl #3]
3134     |  ins_next
3135     break;
3137   case BC_TSETV:
3138     |  decode_RB RB, INS
3139     |   and RC, RC, #255
3140     |  // RA = src, RB = table, RC = key
3141     |  ldr CARG2, [BASE, RB, lsl #3]
3142     |   ldr TMP1, [BASE, RC, lsl #3]
3143     |  checktab CARG2, ->vmeta_tsetv
3144     |  checkint TMP1, >9                // Integer key?
3145     |  ldr CARG3, TAB:CARG2->array
3146     |   ldr CARG1w, TAB:CARG2->asize
3147     |  add CARG3, CARG3, TMP1, uxtw #3
3148     |   cmp TMP1w, CARG1w               // In array part?
3149     |   bhs ->vmeta_tsetv
3150     |  ldr TMP1, [CARG3]
3151     |   ldr TMP0, [BASE, RA, lsl #3]
3152     |    ldrb TMP2w, TAB:CARG2->marked
3153     |  cmp_nil TMP1                     // Previous value is nil?
3154     |  beq >5
3155     |1:
3156     |   str TMP0, [CARG3]
3157     |    tbnz TMP2w, #2, >7             // isblack(table)
3158     |2:
3159     |   ins_next
3160     |
3161     |5:  // Check for __newindex if previous value is nil.
3162     |  ldr TAB:CARG1, TAB:CARG2->metatable
3163     |  cbz TAB:CARG1, <1                // No metatable: done.
3164     |  ldrb TMP1w, TAB:CARG1->nomm
3165     |  tbnz TMP1w, #MM_newindex, <1     // 'no __newindex' flag set: done.
3166     |  b ->vmeta_tsetv
3167     |
3168     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3169     |  barrierback TAB:CARG2, TMP2w, TMP1
3170     |  b <2
3171     |
3172     |9:
3173     |  asr ITYPE, TMP1, #47
3174     |  cmn ITYPE, #-LJ_TSTR             // String key?
3175     |  bne ->vmeta_tsetv
3176     |   and STR:RC, TMP1, #LJ_GCVMASK
3177     |  b ->BC_TSETS_Z
3178     break;
3179   case BC_TSETS:
3180     |  decode_RB RB, INS
3181     |   and RC, RC, #255
3182     |  // RA = dst, RB = table, RC = str_const (~)
3183     |  ldr CARG2, [BASE, RB, lsl #3]
3184     |   mvn RC, RC
3185     |   ldr STR:RC, [KBASE, RC, lsl #3]
3186     |  checktab CARG2, ->vmeta_tsets1
3187     |->BC_TSETS_Z:
3188     |  // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = src
3189     |  ldr TMP1w, TAB:CARG2->hmask
3190     |   ldr TMP2w, STR:RC->sid
3191     |    ldr NODE:CARG3, TAB:CARG2->node
3192     |  and TMP1w, TMP1w, TMP2w          // idx = str->sid & tab->hmask
3193     |  add TMP1, TMP1, TMP1, lsl #1
3194     |  movn CARG4, #~LJ_TSTR
3195     |    add NODE:CARG3, NODE:CARG3, TMP1, lsl #3  // node = tab->node + idx*3*8
3196     |  add CARG4, STR:RC, CARG4, lsl #47        // Tagged key to look for.
3197     |   strb wzr, TAB:CARG2->nomm       // Clear metamethod cache.
3198     |1:
3199     |  ldp TMP1, CARG1, NODE:CARG3->val
3200     |   ldr NODE:TMP3, NODE:CARG3->next
3201     |    ldrb TMP2w, TAB:CARG2->marked
3202     |  cmp CARG1, CARG4
3203     |  bne >5
3204     |   ldr TMP0, [BASE, RA, lsl #3]
3205     |  cmp_nil TMP1                     // Previous value is nil?
3206     |  beq >4
3207     |2:
3208     |   str TMP0, NODE:CARG3->val
3209     |    tbnz TMP2w, #2, >7             // isblack(table)
3210     |3:
3211     |  ins_next
3212     |
3213     |4:  // Check for __newindex if previous value is nil.
3214     |  ldr TAB:CARG1, TAB:CARG2->metatable
3215     |  cbz TAB:CARG1, <2                // No metatable: done.
3216     |  ldrb TMP1w, TAB:CARG1->nomm
3217     |  tbnz TMP1w, #MM_newindex, <2     // 'no __newindex' flag set: done.
3218     |  b ->vmeta_tsets
3219     |
3220     |5:  // Follow hash chain.
3221     |  mov NODE:CARG3, NODE:TMP3
3222     |  cbnz NODE:TMP3, <1
3223     |  // End of hash chain: key not found, add a new one.
3224     |
3225     |  // But check for __newindex first.
3226     |  ldr TAB:CARG1, TAB:CARG2->metatable
3227     |  cbz TAB:CARG1, >6                // No metatable: continue.
3228     |  ldrb TMP1w, TAB:CARG1->nomm
3229     |  // 'no __newindex' flag NOT set: check.
3230     |  tbz TMP1w, #MM_newindex, ->vmeta_tsets
3231     |6:
3232     |  movn TMP1, #~LJ_TSTR
3233     |   str PC, SAVE_PC
3234     |  add TMP0, STR:RC, TMP1, lsl #47
3235     |   str BASE, L->base
3236     |   mov CARG1, L
3237     |  str TMP0, TMPD
3238     |   add CARG3, sp, TMPDofs
3239     |  bl extern lj_tab_newkey          // (lua_State *L, GCtab *t, TValue *k)
3240     |  // Returns TValue *.
3241     |  ldr BASE, L->base
3242     |  ldr TMP0, [BASE, RA, lsl #3]
3243     |  str TMP0, [CRET1]
3244     |  b <3                             // No 2nd write barrier needed.
3245     |
3246     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3247     |  barrierback TAB:CARG2, TMP2w, TMP1
3248     |  b <3
3249     break;
3250   case BC_TSETB:
3251     |  decode_RB RB, INS
3252     |   and RC, RC, #255
3253     |  // RA = src, RB = table, RC = index
3254     |  ldr CARG2, [BASE, RB, lsl #3]
3255     |  checktab CARG2, ->vmeta_tsetb
3256     |  ldr CARG3, TAB:CARG2->array
3257     |   ldr CARG1w, TAB:CARG2->asize
3258     |  add CARG3, CARG3, RC, lsl #3
3259     |   cmp RCw, CARG1w                 // In array part?
3260     |   bhs ->vmeta_tsetb
3261     |  ldr TMP1, [CARG3]
3262     |   ldr TMP0, [BASE, RA, lsl #3]
3263     |    ldrb TMP2w, TAB:CARG2->marked
3264     |  cmp_nil TMP1                     // Previous value is nil?
3265     |  beq >5
3266     |1:
3267     |   str TMP0, [CARG3]
3268     |    tbnz TMP2w, #2, >7             // isblack(table)
3269     |2:
3270     |   ins_next
3271     |
3272     |5:  // Check for __newindex if previous value is nil.
3273     |  ldr TAB:CARG1, TAB:CARG2->metatable
3274     |  cbz TAB:CARG1, <1                // No metatable: done.
3275     |  ldrb TMP1w, TAB:CARG1->nomm
3276     |  tbnz TMP1w, #MM_newindex, <1     // 'no __newindex' flag set: done.
3277     |  b ->vmeta_tsetb
3278     |
3279     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3280     |  barrierback TAB:CARG2, TMP2w, TMP1
3281     |  b <2
3282     break;
3283   case BC_TSETR:
3284     |  decode_RB RB, INS
3285     |   and RC, RC, #255
3286     |  // RA = src, RB = table, RC = key
3287     |  ldr CARG2, [BASE, RB, lsl #3]
3288     |   ldr TMP1, [BASE, RC, lsl #3]
3289     |  and TAB:CARG2, CARG2, #LJ_GCVMASK
3290     |  ldr CARG1, TAB:CARG2->array
3291     |    ldrb TMP2w, TAB:CARG2->marked
3292     |   ldr CARG4w, TAB:CARG2->asize
3293     |  add CARG1, CARG1, TMP1, uxtw #3
3294     |    tbnz TMP2w, #2, >7             // isblack(table)
3295     |2:
3296     |   cmp TMP1w, CARG4w               // In array part?
3297     |   bhs ->vmeta_tsetr
3298     |->BC_TSETR_Z:
3299     |   ldr TMP0, [BASE, RA, lsl #3]
3300     |   str TMP0, [CARG1]
3301     |   ins_next
3302     |
3303     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3304     |  barrierback TAB:CARG2, TMP2w, TMP0
3305     |  b <2
3306     break;
3308   case BC_TSETM:
3309     |  // RA = base (table at base-1), RC = num_const (start index)
3310     |  add RA, BASE, RA, lsl #3
3311     |1:
3312     |   ldr RBw, SAVE_MULTRES
3313     |  ldr TAB:CARG2, [RA, #-8]         // Guaranteed to be a table.
3314     |   ldr TMP1, [KBASE, RC, lsl #3]   // Integer constant is in lo-word.
3315     |    sub RB, RB, #8
3316     |    cbz RB, >4                     // Nothing to copy?
3317     |  and TAB:CARG2, CARG2, #LJ_GCVMASK
3318     |  ldr CARG1w, TAB:CARG2->asize
3319     |   add CARG3w, TMP1w, RBw, lsr #3
3320     |   ldr CARG4, TAB:CARG2->array
3321     |  cmp CARG3, CARG1
3322     |    add RB, RA, RB
3323     |  bhi >5
3324     |   add TMP1, CARG4, TMP1w, uxtw #3
3325     |    ldrb TMP2w, TAB:CARG2->marked
3326     |3:  // Copy result slots to table.
3327     |   ldr TMP0, [RA], #8
3328     |   str TMP0, [TMP1], #8
3329     |  cmp RA, RB
3330     |  blo <3
3331     |    tbnz TMP2w, #2, >7             // isblack(table)
3332     |4:
3333     |  ins_next
3334     |
3335     |5:  // Need to resize array part.
3336     |   str BASE, L->base
3337     |  mov CARG1, L
3338     |   str PC, SAVE_PC
3339     |  bl extern lj_tab_reasize         // (lua_State *L, GCtab *t, int nasize)
3340     |  // Must not reallocate the stack.
3341     |  b <1
3342     |
3343     |7:  // Possible table write barrier for any value. Skip valiswhite check.
3344     |  barrierback TAB:CARG2, TMP2w, TMP1
3345     |  b <4
3346     break;
3348   /* -- Calls and vararg handling ----------------------------------------- */
3350   case BC_CALLM:
3351     |  // RA = base, (RB = nresults+1,) RC = extra_nargs
3352     |  ldr TMP0w, SAVE_MULTRES
3353     |  decode_RC8RD NARGS8:RC, RC
3354     |  add NARGS8:RC, NARGS8:RC, TMP0
3355     |  b ->BC_CALL_Z
3356     break;
3357   case BC_CALL:
3358     |  decode_RC8RD NARGS8:RC, RC
3359     |  // RA = base, (RB = nresults+1,) RC = (nargs+1)*8
3360     |->BC_CALL_Z:
3361     |  mov RB, BASE                     // Save old BASE for vmeta_call.
3362     |  add BASE, BASE, RA, lsl #3
3363     |  ldr CARG3, [BASE], #16
3364     |   sub NARGS8:RC, NARGS8:RC, #8
3365     |  checkfunc CARG3, ->vmeta_call
3366     |  ins_call
3367     break;
3369   case BC_CALLMT:
3370     |  // RA = base, (RB = 0,) RC = extra_nargs
3371     |  ldr TMP0w, SAVE_MULTRES
3372     |  add NARGS8:RC, TMP0, RC, lsl #3
3373     |  b ->BC_CALLT1_Z
3374     break;
3375   case BC_CALLT:
3376     |  lsl NARGS8:RC, RC, #3
3377     |  // RA = base, (RB = 0,) RC = (nargs+1)*8
3378     |->BC_CALLT1_Z:
3379     |  add RA, BASE, RA, lsl #3
3380     |  ldr TMP1, [RA], #16
3381     |   sub NARGS8:RC, NARGS8:RC, #8
3382     |  checktp CARG3, TMP1, LJ_TFUNC, ->vmeta_callt
3383     |  ldr PC, [BASE, FRAME_PC]
3384     |->BC_CALLT2_Z:
3385     |   mov RB, #0
3386     |   ldrb TMP2w, LFUNC:CARG3->ffid
3387     |  tst PC, #FRAME_TYPE
3388     |  bne >7
3389     |1:
3390     |  str TMP1, [BASE, FRAME_FUNC]     // Copy function down, but keep PC.
3391     |  cbz NARGS8:RC, >3
3392     |2:
3393     |  ldr TMP0, [RA, RB]
3394     |   add TMP1, RB, #8
3395     |   cmp TMP1, NARGS8:RC
3396     |  str TMP0, [BASE, RB]
3397     |    mov RB, TMP1
3398     |   bne <2
3399     |3:
3400     |  cmp TMP2, #1                     // (> FF_C) Calling a fast function?
3401     |  bhi >5
3402     |4:
3403     |  ins_callt
3404     |
3405     |5:  // Tailcall to a fast function with a Lua frame below.
3406     |  ldrb RAw, [PC, #-4+OFS_RA]
3407     |  sub CARG1, BASE, RA, lsl #3
3408     |  ldr LFUNC:CARG1, [CARG1, #-32]
3409     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3410     |  ldr CARG1, LFUNC:CARG1->pc
3411     |  ldr KBASE, [CARG1, #PC2PROTO(k)]
3412     |  b <4
3413     |
3414     |7:  // Tailcall from a vararg function.
3415     |  eor PC, PC, #FRAME_VARG
3416     |  tst PC, #FRAME_TYPEP             // Vararg frame below?
3417     |  csel TMP2, RB, TMP2, ne          // Clear ffid if no Lua function below.
3418     |  bne <1
3419     |  sub BASE, BASE, PC
3420     |  ldr PC, [BASE, FRAME_PC]
3421     |  tst PC, #FRAME_TYPE
3422     |  csel TMP2, RB, TMP2, ne          // Clear ffid if no Lua function below.
3423     |  b <1
3424     break;
3426   case BC_ITERC:
3427     |  // RA = base, (RB = nresults+1, RC = nargs+1 (2+1))
3428     |  add RA, BASE, RA, lsl #3
3429     |  ldr CARG3, [RA, #-24]
3430     |    mov RB, BASE                   // Save old BASE for vmeta_call.
3431     |   ldp CARG1, CARG2, [RA, #-16]
3432     |    add BASE, RA, #16
3433     |    mov NARGS8:RC, #16             // Iterators get 2 arguments.
3434     |  str CARG3, [RA]                  // Copy callable.
3435     |   stp CARG1, CARG2, [RA, #16]     // Copy state and control var.
3436     |  checkfunc CARG3, ->vmeta_call
3437     |  ins_call
3438     break;
3440   case BC_ITERN:
3441     |.if JIT
3442     |  hotloop
3443     |.endif
3444     |->vm_IITERN:
3445     |  // RA = base, (RB = nresults+1, RC = nargs+1 (2+1))
3446     |  add RA, BASE, RA, lsl #3
3447     |  ldr TAB:RB, [RA, #-16]
3448     |    ldrh TMP3w, [PC, # OFS_RD]
3449     |  ldr CARG1w, [RA, #-8+LO]         // Get index from control var.
3450     |    add PC, PC, #4
3451     |    add TMP3, PC, TMP3, lsl #2
3452     |  and TAB:RB, RB, #LJ_GCVMASK
3453     |    sub TMP3, TMP3, #0x20000
3454     |  ldr TMP1w, TAB:RB->asize
3455     |   ldr CARG2, TAB:RB->array
3456     |1:  // Traverse array part.
3457     |  subs RC, CARG1, TMP1
3458     |   add CARG3, CARG2, CARG1, lsl #3
3459     |  bhs >5                           // Index points after array part?
3460     |   ldr TMP0, [CARG3]
3461     |   cmp_nil TMP0
3462     |   cinc CARG1, CARG1, eq           // Skip holes in array part.
3463     |   beq <1
3464     |   add_TISNUM CARG1, CARG1
3465     |   stp CARG1, TMP0, [RA]
3466     |    add CARG1, CARG1, #1
3467     |3:
3468     |    str CARG1w, [RA, #-8+LO]       // Update control var.
3469     |  mov PC, TMP3
3470     |4:
3471     |  ins_next
3472     |
3473     |5:  // Traverse hash part.
3474     |  ldr TMP2w, TAB:RB->hmask
3475     |   ldr NODE:RB, TAB:RB->node
3476     |6:
3477     |   add CARG1, RC, RC, lsl #1
3478     |  cmp RC, TMP2                     // End of iteration? Branch to ITERN+1.
3479     |   add NODE:CARG3, NODE:RB, CARG1, lsl #3  // node = tab->node + idx*3*8
3480     |  bhi <4
3481     |  ldp TMP0, CARG1, NODE:CARG3->val
3482     |  cmp_nil TMP0
3483     |   add RC, RC, #1
3484     |  beq <6                           // Skip holes in hash part.
3485     |  stp CARG1, TMP0, [RA]
3486     |  add CARG1, RC, TMP1
3487     |  b <3
3488     break;
3490   case BC_ISNEXT:
3491     |  // RA = base, RC = target (points to ITERN)
3492     |  add RA, BASE, RA, lsl #3
3493     |  ldr CFUNC:CARG1, [RA, #-24]
3494     |     add RC, PC, RC, lsl #2
3495     |   ldp TAB:CARG3, CARG4, [RA, #-16]
3496     |     sub RC, RC, #0x20000
3497     |  checkfunc CFUNC:CARG1, >5
3498     |   asr TMP0, TAB:CARG3, #47
3499     |  ldrb TMP1w, CFUNC:CARG1->ffid
3500     |   cmp_nil CARG4
3501     |   ccmn TMP0, #-LJ_TTAB, #0, eq
3502     |  ccmp TMP1w, #FF_next_N, #0, eq
3503     |  bne >5
3504     |  mov TMP0w, #0xfffe7fff           // LJ_KEYINDEX
3505     |  lsl TMP0, TMP0, #32
3506     |  str TMP0, [RA, #-8]              // Initialize control var.
3507     |1:
3508     |     mov PC, RC
3509     |  ins_next
3510     |
3511     |5:  // Despecialize bytecode if any of the checks fail.
3512     |.if JIT
3513     |  ldrb TMP2w, [RC, # OFS_OP]
3514     |.endif
3515     |  mov TMP0, #BC_JMP
3516     |   mov TMP1, #BC_ITERC
3517     |  strb TMP0w, [PC, #-4+OFS_OP]
3518     |.if JIT
3519     |  cmp TMP2w, #BC_ITERN
3520     |  bne >6
3521     |.endif
3522     |   strb TMP1w, [RC, # OFS_OP]
3523     |  b <1
3524     |.if JIT
3525     |6:  // Unpatch JLOOP.
3526     |  ldr RA, [GL, #GL_J(trace)]
3527     |  ldrh TMP2w, [RC, # OFS_RD]
3528     |  ldr TRACE:RA, [RA, TMP2, lsl #3]
3529     |  ldr TMP2w, TRACE:RA->startins
3530     |  bfxil TMP2w, TMP1w, #0, #8
3531     |  str TMP2w, [RC]
3532     |  b <1
3533     |.endif
3534     break;
3536   case BC_VARG:
3537     |  decode_RB RB, INS
3538     |   and RC, RC, #255
3539     |  // RA = base, RB = (nresults+1), RC = numparams
3540     |  ldr TMP1, [BASE, FRAME_PC]
3541     |  add TMP0, BASE, RC, lsl #3
3542     |   add RC, BASE, RA, lsl #3        // RC = destination
3543     |  add TMP0, TMP0, #FRAME_VARG
3544     |   add TMP2, RC, RB, lsl #3
3545     |  sub RA, TMP0, TMP1               // RA = vbase
3546     |  // Note: RA may now be even _above_ BASE if nargs was < numparams.
3547     |   sub TMP3, BASE, #16             // TMP3 = vtop
3548     |  cbz RB, >5
3549     |   sub TMP2, TMP2, #16
3550     |1:  // Copy vararg slots to destination slots.
3551     |  cmp RA, TMP3
3552     |  ldr TMP0, [RA], #8
3553     |  csinv TMP0, TMP0, xzr, lo        // TISNIL = ~xzr
3554     |   cmp RC, TMP2
3555     |  str TMP0, [RC], #8
3556     |   blo <1
3557     |2:
3558     |  ins_next
3559     |
3560     |5:  // Copy all varargs.
3561     |  ldr TMP0, L->maxstack
3562     |   subs TMP2, TMP3, RA
3563     |   csel RB, xzr, TMP2, le          // MULTRES = (max(vtop-vbase,0)+1)*8
3564     |   add RB, RB, #8
3565     |  add TMP1, RC, TMP2
3566     |   str RBw, SAVE_MULTRES
3567     |   ble <2                          // Nothing to copy.
3568     |  cmp TMP1, TMP0
3569     |  bhi >7
3570     |6:
3571     |  ldr TMP0, [RA], #8
3572     |  str TMP0, [RC], #8
3573     |  cmp RA, TMP3
3574     |  blo <6
3575     |  b <2
3576     |
3577     |7:  // Grow stack for varargs.
3578     |  lsr CARG2, TMP2, #3
3579     |   stp BASE, RC, L->base
3580     |  mov CARG1, L
3581     |  sub RA, RA, BASE                 // Need delta, because BASE may change.
3582     |   str PC, SAVE_PC
3583     |  bl extern lj_state_growstack     // (lua_State *L, int n)
3584     |  ldp BASE, RC, L->base
3585     |  add RA, BASE, RA
3586     |  sub TMP3, BASE, #16
3587     |  b <6
3588     break;
3590   /* -- Returns ----------------------------------------------------------- */
3592   case BC_RETM:
3593     |  // RA = results, RC = extra results
3594     |  ldr TMP0w, SAVE_MULTRES
3595     |   ldr PC, [BASE, FRAME_PC]
3596     |    add RA, BASE, RA, lsl #3
3597     |  add RC, TMP0, RC, lsl #3
3598     |  b ->BC_RETM_Z
3599     break;
3601   case BC_RET:
3602     |  // RA = results, RC = nresults+1
3603     |  ldr PC, [BASE, FRAME_PC]
3604     |   lsl RC, RC, #3
3605     |    add RA, BASE, RA, lsl #3
3606     |->BC_RETM_Z:
3607     |   str RCw, SAVE_MULTRES
3608     |1:
3609     |  ands CARG1, PC, #FRAME_TYPE
3610     |   eor CARG2, PC, #FRAME_VARG
3611     |  bne ->BC_RETV2_Z
3612     |
3613     |->BC_RET_Z:
3614     |  // BASE = base, RA = resultptr, RC = (nresults+1)*8, PC = return
3615     |  ldr INSw, [PC, #-4]
3616     |  subs TMP1, RC, #8
3617     |   sub CARG3, BASE, #16
3618     |  beq >3
3619     |2:
3620     |  ldr TMP0, [RA], #8
3621     |   add BASE, BASE, #8
3622     |   sub TMP1, TMP1, #8
3623     |  str TMP0, [BASE, #-24]
3624     |   cbnz TMP1, <2
3625     |3:
3626     |  decode_RA RA, INS
3627     |  sub CARG4, CARG3, RA, lsl #3
3628     |   decode_RB RB, INS
3629     |  ldr LFUNC:CARG1, [CARG4, FRAME_FUNC]
3630     |5:
3631     |  cmp RC, RB, lsl #3               // More results expected?
3632     |  blo >6
3633     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3634     |  mov BASE, CARG4
3635     |  ldr CARG2, LFUNC:CARG1->pc
3636     |  ldr KBASE, [CARG2, #PC2PROTO(k)]
3637     |   ins_next
3638     |
3639     |6:  // Fill up results with nil.
3640     |  add BASE, BASE, #8
3641     |   add RC, RC, #8
3642     |  str TISNIL, [BASE, #-24]
3643     |  b <5
3644     |
3645     |->BC_RETV1_Z:  // Non-standard return case.
3646     |  add RA, BASE, RA, lsl #3
3647     |->BC_RETV2_Z:
3648     |  tst CARG2, #FRAME_TYPEP
3649     |  bne ->vm_return
3650     |  // Return from vararg function: relocate BASE down.
3651     |  sub BASE, BASE, CARG2
3652     |  ldr PC, [BASE, FRAME_PC]
3653     |  b <1
3654     break;
3656   case BC_RET0: case BC_RET1:
3657     |  // RA = results, RC = nresults+1
3658     |  ldr PC, [BASE, FRAME_PC]
3659     |   lsl RC, RC, #3
3660     |   str RCw, SAVE_MULTRES
3661     |  ands CARG1, PC, #FRAME_TYPE
3662     |   eor CARG2, PC, #FRAME_VARG
3663     |  bne ->BC_RETV1_Z
3664     |   ldr INSw, [PC, #-4]
3665     if (op == BC_RET1) {
3666       |  ldr TMP0, [BASE, RA, lsl #3]
3667     }
3668     |  sub CARG4, BASE, #16
3669     |   decode_RA RA, INS
3670     |  sub BASE, CARG4, RA, lsl #3
3671     if (op == BC_RET1) {
3672       |  str TMP0, [CARG4], #8
3673     }
3674     |   decode_RB RB, INS
3675     |  ldr LFUNC:CARG1, [BASE, FRAME_FUNC]
3676     |5:
3677     |  cmp RC, RB, lsl #3
3678     |  blo >6
3679     |  and LFUNC:CARG1, CARG1, #LJ_GCVMASK
3680     |  ldr CARG2, LFUNC:CARG1->pc
3681     |  ldr KBASE, [CARG2, #PC2PROTO(k)]
3682     |  ins_next
3683     |
3684     |6:  // Fill up results with nil.
3685     |  add RC, RC, #8
3686     |  str TISNIL, [CARG4], #8
3687     |  b <5
3688     break;
3690   /* -- Loops and branches ------------------------------------------------ */
3692   |.define FOR_IDX,  [RA];      .define FOR_TIDX,  [RA, #4]
3693   |.define FOR_STOP, [RA, #8];  .define FOR_TSTOP, [RA, #12]
3694   |.define FOR_STEP, [RA, #16]; .define FOR_TSTEP, [RA, #20]
3695   |.define FOR_EXT,  [RA, #24]; .define FOR_TEXT,  [RA, #28]
3697   case BC_FORL:
3698     |.if JIT
3699     |  hotloop
3700     |.endif
3701     |  // Fall through. Assumes BC_IFORL follows.
3702     break;
3704   case BC_JFORI:
3705   case BC_JFORL:
3706 #if !LJ_HASJIT
3707     break;
3708 #endif
3709   case BC_FORI:
3710   case BC_IFORL:
3711     |  // RA = base, RC = target (after end of loop or start of loop)
3712     vk = (op == BC_IFORL || op == BC_JFORL);
3713     |  add RA, BASE, RA, lsl #3
3714     |  ldp CARG1, CARG2, FOR_IDX                // CARG1 = IDX, CARG2 = STOP
3715     |   ldr CARG3, FOR_STEP                     // CARG3 = STEP
3716     if (op != BC_JFORL) {
3717       |   add RC, PC, RC, lsl #2
3718       |   sub RC, RC, #0x20000
3719     }
3720     |  checkint CARG1, >5
3721     if (!vk) {
3722       |  checkint CARG2, ->vmeta_for
3723       |   checkint CARG3, ->vmeta_for
3724       |  tbnz CARG3w, #31, >4
3725       |  cmp CARG1w, CARG2w
3726     } else {
3727       |  adds CARG1w, CARG1w, CARG3w
3728       |  bvs >2
3729       |   add_TISNUM TMP0, CARG1
3730       |  tbnz CARG3w, #31, >4
3731       |  cmp CARG1w, CARG2w
3732     }
3733     |1:
3734     if (op == BC_FORI) {
3735       |  csel PC, RC, PC, gt
3736     } else if (op == BC_JFORI) {
3737       |  mov PC, RC
3738       |  ldrh RCw, [RC, #-4+OFS_RD]
3739     } else if (op == BC_IFORL) {
3740       |  csel PC, RC, PC, le
3741     }
3742     if (vk) {
3743       |   str TMP0, FOR_IDX
3744       |   str TMP0, FOR_EXT
3745     } else {
3746       |  str CARG1, FOR_EXT
3747     }
3748     if (op == BC_JFORI || op == BC_JFORL) {
3749       |  ble =>BC_JLOOP
3750     }
3751     |2:
3752     |   ins_next
3753     |
3754     |4:  // Invert check for negative step.
3755     |  cmp CARG2w, CARG1w
3756     |  b <1
3757     |
3758     |5:  // FP loop.
3759     |  ldp d0, d1, FOR_IDX
3760     |  blo ->vmeta_for
3761     if (!vk) {
3762       |  checknum CARG2, ->vmeta_for
3763       |   checknum CARG3, ->vmeta_for
3764       |  str d0, FOR_EXT
3765     } else {
3766       |  ldr d2, FOR_STEP
3767       |  fadd d0, d0, d2
3768     }
3769     |  tbnz CARG3, #63, >7
3770     |  fcmp d0, d1
3771     |6:
3772     if (vk) {
3773       |  str d0, FOR_IDX
3774       |  str d0, FOR_EXT
3775     }
3776     if (op == BC_FORI) {
3777       |  csel PC, RC, PC, hi
3778     } else if (op == BC_JFORI) {
3779       |  ldrh RCw, [RC, #-4+OFS_RD]
3780       |  bls =>BC_JLOOP
3781     } else if (op == BC_IFORL) {
3782       |  csel PC, RC, PC, ls
3783     } else {
3784       |  bls =>BC_JLOOP
3785     }
3786     |  b <2
3787     |
3788     |7:  // Invert check for negative step.
3789     |  fcmp d1, d0
3790     |  b <6
3791     break;
3793   case BC_ITERL:
3794     |.if JIT
3795     |  hotloop
3796     |.endif
3797     |  // Fall through. Assumes BC_IITERL follows.
3798     break;
3800   case BC_JITERL:
3801 #if !LJ_HASJIT
3802     break;
3803 #endif
3804   case BC_IITERL:
3805     |  // RA = base, RC = target
3806     |  ldr CARG1, [BASE, RA, lsl #3]
3807     |   add TMP1, BASE, RA, lsl #3
3808     |  cmp_nil CARG1
3809     |  beq >1                           // Stop if iterator returned nil.
3810     if (op == BC_JITERL) {
3811       |  str CARG1, [TMP1, #-8]
3812       |  b =>BC_JLOOP
3813     } else {
3814       |  add TMP0, PC, RC, lsl #2       // Otherwise save control var + branch.
3815       |  sub PC, TMP0, #0x20000
3816       |  str CARG1, [TMP1, #-8]
3817     }
3818     |1:
3819     |  ins_next
3820     break;
3822   case BC_LOOP:
3823     |  // RA = base, RC = target (loop extent)
3824     |  // Note: RA/RC is only used by trace recorder to determine scope/extent
3825     |  // This opcode does NOT jump, it's only purpose is to detect a hot loop.
3826     |.if JIT
3827     |  hotloop
3828     |.endif
3829     |  // Fall through. Assumes BC_ILOOP follows.
3830     break;
3832   case BC_ILOOP:
3833     |  // RA = base, RC = target (loop extent)
3834     |  ins_next
3835     break;
3837   case BC_JLOOP:
3838     |.if JIT
3839     |  // RA = base (ignored), RC = traceno
3840     |  ldr CARG1, [GL, #GL_J(trace)]
3841     |   st_vmstate wzr  // Traces on ARM64 don't store the trace #, so use 0.
3842     |  ldr TRACE:RC, [CARG1, RC, lsl #3]
3843     |.if PAUTH
3844     |  ldr RA, TRACE:RC->mcauth
3845     |.else
3846     |  ldr RA, TRACE:RC->mcode
3847     |.endif
3848     |   str BASE, GL->jit_base
3849     |   str L, GL->tmpbuf.L
3850     |  sub sp, sp, #16  // See SPS_FIXED. Avoids sp adjust in every root trace.
3851     |.if PAUTH
3852     |  braa RA, RC
3853     |.else
3854     |  br RA
3855     |.endif
3856     |.endif
3857     break;
3859   case BC_JMP:
3860     |  // RA = base (only used by trace recorder), RC = target
3861     |  add RC, PC, RC, lsl #2
3862     |  sub PC, RC, #0x20000
3863     |  ins_next
3864     break;
3866   /* -- Function headers -------------------------------------------------- */
3868   case BC_FUNCF:
3869     |.if JIT
3870     |  hotcall
3871     |.endif
3872   case BC_FUNCV:  /* NYI: compiled vararg functions. */
3873     |  // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow.
3874     break;
3876   case BC_JFUNCF:
3877 #if !LJ_HASJIT
3878     break;
3879 #endif
3880   case BC_IFUNCF:
3881     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8
3882     |  ldr CARG1, L->maxstack
3883     |   ldrb TMP1w, [PC, #-4+PC2PROTO(numparams)]
3884     |    ldr KBASE, [PC, #-4+PC2PROTO(k)]
3885     |  cmp RA, CARG1
3886     |  bhi ->vm_growstack_l
3887     |2:
3888     |  cmp NARGS8:RC, TMP1, lsl #3      // Check for missing parameters.
3889     |  blo >3
3890     if (op == BC_JFUNCF) {
3891       |  decode_RD RC, INS
3892       |  b =>BC_JLOOP
3893     } else {
3894       |  ins_next
3895     }
3896     |
3897     |3:  // Clear missing parameters.
3898     |  str TISNIL, [BASE, NARGS8:RC]
3899     |  add NARGS8:RC, NARGS8:RC, #8
3900     |  b <2
3901     break;
3903   case BC_JFUNCV:
3904 #if !LJ_HASJIT
3905     break;
3906 #endif
3907     |  NYI  // NYI: compiled vararg functions
3908     break;  /* NYI: compiled vararg functions. */
3910   case BC_IFUNCV:
3911     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8
3912     |  ldr CARG1, L->maxstack
3913     |   movn TMP0, #~LJ_TFUNC
3914     |   add TMP2, BASE, RC
3915     |   add LFUNC:CARG3, CARG3, TMP0, lsl #47
3916     |  add RA, RA, RC
3917     |  sub CARG1, CARG1, #8
3918     |   add TMP0, RC, #16+FRAME_VARG
3919     |   str LFUNC:CARG3, [TMP2], #8     // Store (tagged) copy of LFUNC.
3920     |    ldr KBASE, [PC, #-4+PC2PROTO(k)]
3921     |  cmp RA, CARG1
3922     |   str TMP0, [TMP2], #8            // Store delta + FRAME_VARG.
3923     |  bhs ->vm_growstack_l
3924     |   sub RC, TMP2, #16
3925     |  ldrb TMP1w, [PC, #-4+PC2PROTO(numparams)]
3926     |   mov RA, BASE
3927     |   mov BASE, TMP2
3928     |  cbz TMP1, >2
3929     |1:
3930     |  cmp RA, RC                       // Less args than parameters?
3931     |  bhs >3
3932     |   ldr TMP0, [RA]
3933     |  sub TMP1, TMP1, #1
3934     |    str TISNIL, [RA], #8           // Clear old fixarg slot (help the GC).
3935     |   str TMP0, [TMP2], #8
3936     |  cbnz TMP1, <1
3937     |2:
3938     |  ins_next
3939     |
3940     |3:
3941     |  sub TMP1, TMP1, #1
3942     |   str TISNIL, [TMP2], #8
3943     |  cbz TMP1, <2
3944     |  b <3
3945     break;
3947   case BC_FUNCC:
3948   case BC_FUNCCW:
3949     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = CFUNC, RC = nargs*8
3950     if (op == BC_FUNCC) {
3951       |  ldr CARG4, CFUNC:CARG3->f
3952     } else {
3953       |  ldr CARG4, GL->wrapf
3954     }
3955     |   add CARG2, RA, NARGS8:RC
3956     |   ldr CARG1, L->maxstack
3957     |  add RC, BASE, NARGS8:RC
3958     |   cmp CARG2, CARG1
3959     |  stp BASE, RC, L->base
3960     if (op == BC_FUNCCW) {
3961       |  ldr CARG2, CFUNC:CARG3->f
3962     }
3963     |    mv_vmstate TMP0w, C
3964     |  mov CARG1, L
3965     |   bhi ->vm_growstack_c            // Need to grow stack.
3966     |    st_vmstate TMP0w
3967     |  blr_auth CARG4                   // (lua_State *L [, lua_CFunction f])
3968     |  // Returns nresults.
3969     |  ldp BASE, TMP1, L->base
3970     |    str L, GL->cur_L
3971     |   sbfiz RC, CRET1, #3, #32
3972     |    st_vmstate ST_INTERP
3973     |  ldr PC, [BASE, FRAME_PC]
3974     |   sub RA, TMP1, RC                // RA = L->top - nresults*8
3975     |  b ->vm_returnc
3976     break;
3978   /* ---------------------------------------------------------------------- */
3980   default:
3981     fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]);
3982     exit(2);
3983     break;
3984   }
3987 static int build_backend(BuildCtx *ctx)
3989   int op;
3991   dasm_growpc(Dst, BC__MAX);
3993   build_subroutines(ctx);
3995   |.code_op
3996   for (op = 0; op < BC__MAX; op++)
3997     build_ins(ctx, (BCOp)op, op);
3999   return BC__MAX;
4002 /* Emit pseudo frame-info for all assembler functions. */
4003 static void emit_asm_debug(BuildCtx *ctx)
4005   int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code);
4006   int i;
4007   switch (ctx->mode) {
4008   case BUILD_elfasm:
4009     fprintf(ctx->fp, "\t.section .debug_frame,\"\",%%progbits\n");
4010     fprintf(ctx->fp,
4011         ".Lframe0:\n"
4012         "\t.long .LECIE0-.LSCIE0\n"
4013         ".LSCIE0:\n"
4014         "\t.long 0xffffffff\n"
4015         "\t.byte 0x1\n"
4016         "\t.string \"\"\n"
4017         "\t.uleb128 0x1\n"
4018         "\t.sleb128 -8\n"
4019         "\t.byte 30\n"                          /* Return address is in lr. */
4020         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4021         "\t.align 3\n"
4022         ".LECIE0:\n\n");
4023     fprintf(ctx->fp,
4024         ".LSFDE0:\n"
4025         "\t.long .LEFDE0-.LASFDE0\n"
4026         ".LASFDE0:\n"
4027         "\t.long .Lframe0\n"
4028         "\t.quad .Lbegin\n"
4029         "\t.quad %d\n"
4030         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4031         "\t.byte 0x9d\n\t.uleb128 2\n",         /* offset fp */
4032         fcofs);
4033     for (i = 19; i <= 28; i++)  /* offset x19-x28 */
4034       fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, i+(3-19));
4035     for (i = 8; i <= 15; i++)  /* offset d8-d15 */
4036       fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n",
4037               64+i, i+(3+(28-19+1)-8));
4038     fprintf(ctx->fp,
4039         "\t.align 3\n"
4040         ".LEFDE0:\n\n");
4041 #if LJ_HASFFI
4042     fprintf(ctx->fp,
4043         ".LSFDE1:\n"
4044         "\t.long .LEFDE1-.LASFDE1\n"
4045         ".LASFDE1:\n"
4046         "\t.long .Lframe0\n"
4047         "\t.quad lj_vm_ffi_call\n"
4048         "\t.quad %d\n"
4049         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4050         "\t.byte 0x9d\n\t.uleb128 2\n"          /* offset fp */
4051         "\t.byte 0x93\n\t.uleb128 3\n"          /* offset x19 */
4052         "\t.byte 0x94\n\t.uleb128 4\n"          /* offset x20 */
4053         "\t.align 3\n"
4054         ".LEFDE1:\n\n", (int)ctx->codesz - fcofs);
4055 #endif
4056 #if !LJ_NO_UNWIND
4057     fprintf(ctx->fp, "\t.section .eh_frame,\"a\",%%progbits\n");
4058     fprintf(ctx->fp,
4059         ".Lframe1:\n"
4060         "\t.long .LECIE1-.LSCIE1\n"
4061         ".LSCIE1:\n"
4062         "\t.long 0\n"
4063         "\t.byte 0x1\n"
4064         "\t.string \"zPR\"\n"
4065         "\t.uleb128 0x1\n"
4066         "\t.sleb128 -8\n"
4067         "\t.byte 30\n"                          /* Return address is in lr. */
4068         "\t.uleb128 6\n"                        /* augmentation length */
4069         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4070         "\t.long lj_err_unwind_dwarf-.\n"
4071         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4072         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4073         "\t.align 3\n"
4074         ".LECIE1:\n\n");
4075     fprintf(ctx->fp,
4076         ".LSFDE2:\n"
4077         "\t.long .LEFDE2-.LASFDE2\n"
4078         ".LASFDE2:\n"
4079         "\t.long .LASFDE2-.Lframe1\n"
4080         "\t.long .Lbegin-.\n"
4081         "\t.long %d\n"
4082         "\t.uleb128 0\n"                        /* augmentation length */
4083         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4084         "\t.byte 0x9d\n\t.uleb128 2\n",         /* offset fp */
4085         fcofs);
4086     for (i = 19; i <= 28; i++)  /* offset x19-x28 */
4087       fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, i+(3-19));
4088     for (i = 8; i <= 15; i++)  /* offset d8-d15 */
4089       fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n",
4090               64+i, i+(3+(28-19+1)-8));
4091     fprintf(ctx->fp,
4092         "\t.align 3\n"
4093         ".LEFDE2:\n\n");
4094 #if LJ_HASFFI
4095     fprintf(ctx->fp,
4096         ".Lframe2:\n"
4097         "\t.long .LECIE2-.LSCIE2\n"
4098         ".LSCIE2:\n"
4099         "\t.long 0\n"
4100         "\t.byte 0x1\n"
4101         "\t.string \"zR\"\n"
4102         "\t.uleb128 0x1\n"
4103         "\t.sleb128 -8\n"
4104         "\t.byte 30\n"                          /* Return address is in lr. */
4105         "\t.uleb128 1\n"                        /* augmentation length */
4106         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4107         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4108         "\t.align 3\n"
4109         ".LECIE2:\n\n");
4110     fprintf(ctx->fp,
4111         ".LSFDE3:\n"
4112         "\t.long .LEFDE3-.LASFDE3\n"
4113         ".LASFDE3:\n"
4114         "\t.long .LASFDE3-.Lframe2\n"
4115         "\t.long lj_vm_ffi_call-.\n"
4116         "\t.long %d\n"
4117         "\t.uleb128 0\n"                        /* augmentation length */
4118         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4119         "\t.byte 0x9d\n\t.uleb128 2\n"          /* offset fp */
4120         "\t.byte 0x93\n\t.uleb128 3\n"          /* offset x19 */
4121         "\t.byte 0x94\n\t.uleb128 4\n"          /* offset x20 */
4122         "\t.align 3\n"
4123         ".LEFDE3:\n\n", (int)ctx->codesz - fcofs);
4124 #endif
4125 #endif
4126     break;
4127 #if !LJ_NO_UNWIND
4128   case BUILD_machasm: {
4129 #if LJ_HASFFI
4130     int fcsize = 0;
4131 #endif
4132     int j;
4133     fprintf(ctx->fp, "\t.section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support\n");
4134     fprintf(ctx->fp,
4135         "EH_frame1:\n"
4136         "\t.set L$set$x,LECIEX-LSCIEX\n"
4137         "\t.long L$set$x\n"
4138         "LSCIEX:\n"
4139         "\t.long 0\n"
4140         "\t.byte 0x1\n"
4141         "\t.ascii \"zPR\\0\"\n"
4142         "\t.uleb128 0x1\n"
4143         "\t.sleb128 -8\n"
4144         "\t.byte 30\n"                          /* Return address is in lr. */
4145         "\t.uleb128 6\n"                        /* augmentation length */
4146         "\t.byte 0x9b\n"                        /* indirect|pcrel|sdata4 */
4147         "\t.long _lj_err_unwind_dwarf@GOT-.\n"
4148         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4149         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4150         "\t.align 3\n"
4151         "LECIEX:\n\n");
4152     for (j = 0; j < ctx->nsym; j++) {
4153       const char *name = ctx->sym[j].name;
4154       int32_t size = ctx->sym[j+1].ofs - ctx->sym[j].ofs;
4155       if (size == 0) continue;
4156 #if LJ_HASFFI
4157       if (!strcmp(name, "_lj_vm_ffi_call")) { fcsize = size; continue; }
4158 #endif
4159       fprintf(ctx->fp,
4160         "LSFDE%d:\n"
4161         "\t.set L$set$%d,LEFDE%d-LASFDE%d\n"
4162         "\t.long L$set$%d\n"
4163         "LASFDE%d:\n"
4164         "\t.long LASFDE%d-EH_frame1\n"
4165         "\t.long %s-.\n"
4166         "\t.long %d\n"
4167         "\t.uleb128 0\n"                        /* augmentation length */
4168         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4169         "\t.byte 0x9d\n\t.uleb128 2\n",         /* offset fp */
4170         j, j, j, j, j, j, j, name, size);
4171       for (i = 19; i <= 28; i++)  /* offset x19-x28 */
4172         fprintf(ctx->fp, "\t.byte 0x%x\n\t.uleb128 %d\n", 0x80+i, i+(3-19));
4173       for (i = 8; i <= 15; i++)  /* offset d8-d15 */
4174         fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 0x%x\n\t.uleb128 %d\n",
4175                 64+i, i+(3+(28-19+1)-8));
4176       fprintf(ctx->fp,
4177         "\t.align 3\n"
4178         "LEFDE%d:\n\n", j);
4179     }
4180 #if LJ_HASFFI
4181     if (fcsize) {
4182       fprintf(ctx->fp,
4183         "EH_frame2:\n"
4184         "\t.set L$set$y,LECIEY-LSCIEY\n"
4185         "\t.long L$set$y\n"
4186         "LSCIEY:\n"
4187         "\t.long 0\n"
4188         "\t.byte 0x1\n"
4189         "\t.ascii \"zR\\0\"\n"
4190         "\t.uleb128 0x1\n"
4191         "\t.sleb128 -8\n"
4192         "\t.byte 30\n"                          /* Return address is in lr. */
4193         "\t.uleb128 1\n"                        /* augmentation length */
4194         "\t.byte 0x1b\n"                        /* pcrel|sdata4 */
4195         "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 16\n"   /* def_cfa fp 16 */
4196         "\t.align 3\n"
4197         "LECIEY:\n\n");
4198       fprintf(ctx->fp,
4199         "LSFDEY:\n"
4200         "\t.set L$set$yy,LEFDEY-LASFDEY\n"
4201         "\t.long L$set$yy\n"
4202         "LASFDEY:\n"
4203         "\t.long LASFDEY-EH_frame2\n"
4204         "\t.long _lj_vm_ffi_call-.\n"
4205         "\t.long %d\n"
4206         "\t.uleb128 0\n"                        /* augmentation length */
4207         "\t.byte 0x9e\n\t.uleb128 1\n"          /* offset lr */
4208         "\t.byte 0x9d\n\t.uleb128 2\n"          /* offset fp */
4209         "\t.byte 0x93\n\t.uleb128 3\n"          /* offset x19 */
4210         "\t.byte 0x94\n\t.uleb128 4\n"          /* offset x20 */
4211         "\t.align 3\n"
4212         "LEFDEY:\n\n", fcsize);
4213     }
4214 #endif
4215     fprintf(ctx->fp, ".subsections_via_symbols\n");
4216     }
4217     break;
4218 #endif
4219   default:
4220     break;
4221   }