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