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