Add LJ_TARGET_UNALIGNED.
[luajit-2.0/celess22.git] / src / vm_arm.dasc
blobf00b302812837d3034e3d7178131e4e33f373b6a
1 |// Low-level VM code for ARM CPUs.
2 |// Bytecode interpreter, fast functions and helper functions.
3 |// Copyright (C) 2005-2012 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   |  mov CARG2, KBASE
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 CARG3, 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   |.if HFABI
1372   |  vmov d0, CARG1, CARG2
1373   |  bl ->vm_..func.._hf
1374   |  b ->fff_resd
1375   |.else
1376   |  bl ->vm_..func
1377   |  b ->fff_restv
1378   |.endif
1379   |.endmacro
1380   |
1381   |  math_round floor
1382   |  math_round ceil
1383   |
1384   |.align 8
1385   |9:
1386   |  .long 0x00000000, 0x41e00000       // 2^31.
1387   |
1388   |.ffunc_1 math_abs
1389   |  checktp CARG2, LJ_TISNUM
1390   |  bhi ->fff_fallback
1391   |  bicne CARG2, CARG2, #0x80000000
1392   |  bne ->fff_restv
1393   |  cmp CARG1, #0
1394   |  rsbslt CARG1, CARG1, #0
1395   |  ldrdvs CARG12, <9
1396   |  // Fallthrough.
1397   |
1398   |->fff_restv:
1399   |  // CARG12 = TValue result.
1400   |  ldr PC, [BASE, FRAME_PC]
1401   |  strd CARG12, [BASE, #-8]
1402   |->fff_res1:
1403   |  // PC = return.
1404   |  mov RC, #(1+1)*8
1405   |->fff_res:
1406   |  // RC = (nresults+1)*8, PC = return.
1407   |  ands CARG1, PC, #FRAME_TYPE
1408   |  ldreq INS, [PC, #-4]
1409   |   str RC, SAVE_MULTRES
1410   |  sub RA, BASE, #8
1411   |  bne ->vm_return
1412   |  decode_RB8 RB, INS
1413   |5:
1414   |  cmp RB, RC                         // More results expected?
1415   |  bhi >6
1416   |  decode_RA8 CARG1, INS
1417   |   ins_next1
1418   |   ins_next2
1419   |  // Adjust BASE. KBASE is assumed to be set for the calling frame.
1420   |  sub BASE, RA, CARG1
1421   |   ins_next3
1422   |
1423   |6:  // Fill up results with nil.
1424   |  add CARG2, RA, RC
1425   |  mvn CARG1, #~LJ_TNIL
1426   |   add RC, RC, #8
1427   |  str CARG1, [CARG2, #-4]
1428   |  b <5
1429   |
1430   |.macro math_extern, func
1431   |.if HFABI
1432   |  .ffunc_d math_ .. func
1433   |.else
1434   |  .ffunc_n math_ .. func
1435   |.endif
1436   |  .IOS mov RA, BASE
1437   |  bl extern func
1438   |  .IOS mov BASE, RA
1439   |.if HFABI
1440   |  b ->fff_resd
1441   |.else
1442   |  b ->fff_restv
1443   |.endif
1444   |.endmacro
1445   |
1446   |.macro math_extern2, func
1447   |.if HFABI
1448   |  .ffunc_dd math_ .. func
1449   |.else
1450   |  .ffunc_nn math_ .. func
1451   |.endif
1452   |  .IOS mov RA, BASE
1453   |  bl extern func
1454   |  .IOS mov BASE, RA
1455   |.if HFABI
1456   |  b ->fff_resd
1457   |.else
1458   |  b ->fff_restv
1459   |.endif
1460   |.endmacro
1461   |
1462   |.if FPU
1463   |  .ffunc_d math_sqrt
1464   |  vsqrt.f64 d0, d0
1465   |->fff_resd:
1466   |  ldr PC, [BASE, FRAME_PC]
1467   |  vstr d0, [BASE, #-8]
1468   |  b ->fff_res1
1469   |.else
1470   |  math_extern sqrt
1471   |.endif
1472   |
1473   |.ffunc math_log
1474   |.if HFABI
1475   |  ldr CARG2, [BASE, #4]
1476   |   cmp NARGS8:RC, #8                 // Need exactly 1 argument.
1477   |  vldr d0, [BASE]
1478   |   bne ->fff_fallback
1479   |.else
1480   |  ldrd CARG12, [BASE]
1481   |   cmp NARGS8:RC, #8                 // Need exactly 1 argument.
1482   |   bne ->fff_fallback
1483   |.endif
1484   |  checktp CARG2, LJ_TISNUM
1485   |  bhs ->fff_fallback
1486   |  .IOS mov RA, BASE
1487   |  bl extern log
1488   |  .IOS mov BASE, RA
1489   |.if HFABI
1490   |  b ->fff_resd
1491   |.else
1492   |  b ->fff_restv
1493   |.endif
1494   |
1495   |  math_extern log10
1496   |  math_extern exp
1497   |  math_extern sin
1498   |  math_extern cos
1499   |  math_extern tan
1500   |  math_extern asin
1501   |  math_extern acos
1502   |  math_extern atan
1503   |  math_extern sinh
1504   |  math_extern cosh
1505   |  math_extern tanh
1506   |  math_extern2 pow
1507   |  math_extern2 atan2
1508   |  math_extern2 fmod
1509   |
1510   |->ff_math_deg:
1511   |.if FPU
1512   |  .ffunc_d math_rad
1513   |  vldr d1, CFUNC:CARG3->upvalue[0]
1514   |  vmul.f64 d0, d0, d1
1515   |  b ->fff_resd
1516   |.else
1517   |  .ffunc_n math_rad
1518   |  ldrd CARG34, CFUNC:CARG3->upvalue[0]
1519   |  bl extern __aeabi_dmul
1520   |  b ->fff_restv
1521   |.endif
1522   |
1523   |.if HFABI
1524   |  .ffunc math_ldexp
1525   |  ldr CARG4, [BASE, #4]
1526   |  ldrd CARG12, [BASE, #8]
1527   |   cmp NARGS8:RC, #16
1528   |   blo ->fff_fallback
1529   |  vldr d0, [BASE]
1530   |  checktp CARG4, LJ_TISNUM
1531   |  bhs ->fff_fallback
1532   |  checktp CARG2, LJ_TISNUM
1533   |  bne ->fff_fallback
1534   |  .IOS mov RA, BASE
1535   |  bl extern ldexp                    // (double x, int exp)
1536   |  .IOS mov BASE, RA
1537   |  b ->fff_resd
1538   |.else
1539   |.ffunc_2 math_ldexp
1540   |  checktp CARG2, LJ_TISNUM
1541   |  bhs ->fff_fallback
1542   |  checktp CARG4, LJ_TISNUM
1543   |  bne ->fff_fallback
1544   |  .IOS mov RA, BASE
1545   |  bl extern ldexp                    // (double x, int exp)
1546   |  .IOS mov BASE, RA
1547   |  b ->fff_restv
1548   |.endif
1549   |
1550   |.if HFABI
1551   |.ffunc_d math_frexp
1552   |  mov CARG1, sp
1553   |  .IOS mov RA, BASE
1554   |  bl extern frexp
1555   |  .IOS mov BASE, RA
1556   |   ldr CARG3, [sp]
1557   |   mvn CARG4, #~LJ_TISNUM
1558   |    ldr PC, [BASE, FRAME_PC]
1559   |  vstr d0, [BASE, #-8]
1560   |    mov RC, #(2+1)*8
1561   |   strd CARG34, [BASE]
1562   |  b ->fff_res
1563   |.else
1564   |.ffunc_n math_frexp
1565   |  mov CARG3, sp
1566   |  .IOS mov RA, BASE
1567   |  bl extern frexp
1568   |  .IOS mov BASE, RA
1569   |   ldr CARG3, [sp]
1570   |   mvn CARG4, #~LJ_TISNUM
1571   |    ldr PC, [BASE, FRAME_PC]
1572   |  strd CARG12, [BASE, #-8]
1573   |    mov RC, #(2+1)*8
1574   |   strd CARG34, [BASE]
1575   |  b ->fff_res
1576   |.endif
1577   |
1578   |.if HFABI
1579   |.ffunc_d math_modf
1580   |  sub CARG1, BASE, #8
1581   |   ldr PC, [BASE, FRAME_PC]
1582   |  .IOS mov RA, BASE
1583   |  bl extern modf
1584   |  .IOS mov BASE, RA
1585   |   mov RC, #(2+1)*8
1586   |  vstr d0, [BASE]
1587   |  b ->fff_res
1588   |.else
1589   |.ffunc_n math_modf
1590   |  sub CARG3, BASE, #8
1591   |   ldr PC, [BASE, FRAME_PC]
1592   |  .IOS mov RA, BASE
1593   |  bl extern modf
1594   |  .IOS mov BASE, RA
1595   |   mov RC, #(2+1)*8
1596   |  strd CARG12, [BASE]
1597   |  b ->fff_res
1598   |.endif
1599   |
1600   |.macro math_minmax, name, cond, fcond
1601   |.if FPU
1602   |  .ffunc_1 name
1603   |   add RB, BASE, RC
1604   |  checktp CARG2, LJ_TISNUM
1605   |   add RA, BASE, #8
1606   |  bne >4
1607   |1:  // Handle integers.
1608   |  ldrd CARG34, [RA]
1609   |   cmp RA, RB
1610   |   bhs ->fff_restv
1611   |  checktp CARG4, LJ_TISNUM
1612   |  bne >3
1613   |  cmp CARG1, CARG3
1614   |   add RA, RA, #8
1615   |  mov..cond CARG1, CARG3
1616   |  b <1
1617   |3:  // Convert intermediate result to number and continue below.
1618   |  vmov s4, CARG1
1619   |  bhi ->fff_fallback
1620   |  vldr d1, [RA]
1621   |  vcvt.f64.s32 d0, s4
1622   |  b >6
1623   |
1624   |4:
1625   |  vldr d0, [BASE]
1626   |  bhi ->fff_fallback
1627   |5:  // Handle numbers.
1628   |  ldrd CARG34, [RA]
1629   |  vldr d1, [RA]
1630   |   cmp RA, RB
1631   |   bhs ->fff_resd
1632   |  checktp CARG4, LJ_TISNUM
1633   |  bhs >7
1634   |6:
1635   |  vcmp.f64 d0, d1
1636   |  vmrs
1637   |   add RA, RA, #8
1638   |  vmov..fcond.f64 d0, d1
1639   |  b <5
1640   |7:  // Convert integer to number and continue above.
1641   |  vmov s4, CARG3
1642   |  bhi ->fff_fallback
1643   |  vcvt.f64.s32 d1, s4
1644   |  b <6
1645   |
1646   |.else
1647   |
1648   |  .ffunc_1 name
1649   |  checktp CARG2, LJ_TISNUM
1650   |   mov RA, #8
1651   |  bne >4
1652   |1:  // Handle integers.
1653   |  ldrd CARG34, [BASE, RA]
1654   |   cmp RA, RC
1655   |   bhs ->fff_restv
1656   |  checktp CARG4, LJ_TISNUM
1657   |  bne >3
1658   |  cmp CARG1, CARG3
1659   |   add RA, RA, #8
1660   |  mov..cond CARG1, CARG3
1661   |  b <1
1662   |3:  // Convert intermediate result to number and continue below.
1663   |  bhi ->fff_fallback
1664   |  bl extern __aeabi_i2d
1665   |  ldrd CARG34, [BASE, RA]
1666   |  b >6
1667   |
1668   |4:
1669   |  bhi ->fff_fallback
1670   |5:  // Handle numbers.
1671   |  ldrd CARG34, [BASE, RA]
1672   |   cmp RA, RC
1673   |   bhs ->fff_restv
1674   |  checktp CARG4, LJ_TISNUM
1675   |  bhs >7
1676   |6:
1677   |  bl extern __aeabi_cdcmple
1678   |   add RA, RA, #8
1679   |  mov..fcond CARG1, CARG3
1680   |  mov..fcond CARG2, CARG4
1681   |  b <5
1682   |7:  // Convert integer to number and continue above.
1683   |  bhi ->fff_fallback
1684   |  strd CARG12, TMPD
1685   |  mov CARG1, CARG3
1686   |  bl extern __aeabi_i2d
1687   |  ldrd CARG34, TMPD
1688   |  b <6
1689   |.endif
1690   |.endmacro
1691   |
1692   |  math_minmax math_min, gt, hi
1693   |  math_minmax math_max, lt, lo
1694   |
1695   |//-- String library -----------------------------------------------------
1696   |
1697   |.ffunc_1 string_len
1698   |  checkstr CARG2, ->fff_fallback
1699   |  ldr CARG1, STR:CARG1->len
1700   |  mvn CARG2, #~LJ_TISNUM
1701   |  b ->fff_restv
1702   |
1703   |.ffunc string_byte                   // Only handle the 1-arg case here.
1704   |  ldrd CARG12, [BASE]
1705   |    ldr PC, [BASE, FRAME_PC]
1706   |   cmp NARGS8:RC, #8
1707   |   checktpeq CARG2, LJ_TSTR          // Need exactly 1 argument.
1708   |   bne ->fff_fallback
1709   |  ldr CARG3, STR:CARG1->len
1710   |   ldrb CARG1, STR:CARG1[1]          // Access is always ok (NUL at end).
1711   |   mvn CARG2, #~LJ_TISNUM
1712   |  cmp CARG3, #0
1713   |  moveq RC, #(0+1)*8
1714   |  movne RC, #(1+1)*8
1715   |   strd CARG12, [BASE, #-8]
1716   |  b ->fff_res
1717   |
1718   |.ffunc string_char                   // Only handle the 1-arg case here.
1719   |  ffgccheck
1720   |  ldrd CARG12, [BASE]
1721   |    ldr PC, [BASE, FRAME_PC]
1722   |   cmp NARGS8:RC, #8                 // Need exactly 1 argument.
1723   |   checktpeq CARG2, LJ_TISNUM
1724   |   bicseq CARG4, CARG1, #255
1725   |  mov CARG3, #1
1726   |   bne ->fff_fallback
1727   |  str CARG1, TMPD
1728   |  mov CARG2, TMPDp                   // Points to stack. Little-endian.
1729   |->fff_newstr:
1730   |  // CARG2 = str, CARG3 = len.
1731   |   str BASE, L->base
1732   |  mov CARG1, L
1733   |   str PC, SAVE_PC
1734   |  bl extern lj_str_new               // (lua_State *L, char *str, size_t l)
1735   |  // Returns GCstr *.
1736   |  ldr BASE, L->base
1737   |   mvn CARG2, #~LJ_TSTR
1738   |  b ->fff_restv
1739   |
1740   |.ffunc string_sub
1741   |  ffgccheck
1742   |  ldrd CARG12, [BASE]
1743   |   ldrd CARG34, [BASE, #16]
1744   |    cmp NARGS8:RC, #16
1745   |     mvn RB, #0
1746   |    beq >1
1747   |    blo ->fff_fallback
1748   |   checktp CARG4, LJ_TISNUM
1749   |    mov RB, CARG3
1750   |   bne ->fff_fallback
1751   |1:
1752   |  ldrd CARG34, [BASE, #8]
1753   |  checktp CARG2, LJ_TSTR
1754   |   ldreq CARG2, STR:CARG1->len
1755   |  checktpeq CARG4, LJ_TISNUM
1756   |  bne ->fff_fallback
1757   |  // CARG1 = str, CARG2 = str->len, CARG3 = start, RB = end
1758   |  add CARG4, CARG2, #1
1759   |  cmp CARG3, #0                      // if (start < 0) start += len+1
1760   |  addlt CARG3, CARG3, CARG4
1761   |  cmp CARG3, #1                      // if (start < 1) start = 1
1762   |  movlt CARG3, #1
1763   |  cmp RB, #0                         // if (end < 0) end += len+1
1764   |  addlt RB, RB, CARG4
1765   |  bic RB, RB, RB, asr #31            // if (end < 0) end = 0
1766   |  cmp RB, CARG2                      // if (end > len) end = len
1767   |   add CARG1, STR:CARG1, #sizeof(GCstr)-1
1768   |  movgt RB, CARG2
1769   |   add CARG2, CARG1, CARG3
1770   |  subs CARG3, RB, CARG3              // len = end - start
1771   |   add CARG3, CARG3, #1              // len += 1
1772   |  bge ->fff_newstr
1773   |->fff_emptystr:
1774   |  sub STR:CARG1, DISPATCH, #-DISPATCH_GL(strempty)
1775   |  mvn CARG2, #~LJ_TSTR
1776   |  b ->fff_restv
1777   |
1778   |.ffunc string_rep                    // Only handle the 1-char case inline.
1779   |  ffgccheck
1780   |  ldrd CARG12, [BASE]
1781   |   ldrd CARG34, [BASE, #8]
1782   |    cmp NARGS8:RC, #16
1783   |    bne ->fff_fallback               // Exactly 2 arguments
1784   |  checktp CARG2, LJ_TSTR
1785   |   checktpeq CARG4, LJ_TISNUM
1786   |   bne ->fff_fallback
1787   |  subs CARG4, CARG3, #1
1788   |   ldr CARG2, STR:CARG1->len
1789   |  blt ->fff_emptystr                 // Count <= 0?
1790   |   cmp CARG2, #1
1791   |   blo ->fff_emptystr                // Zero-length string?
1792   |   bne ->fff_fallback                // Fallback for > 1-char strings.
1793   |  ldr RB, [DISPATCH, #DISPATCH_GL(tmpbuf.sz)]
1794   |   ldr CARG2, [DISPATCH, #DISPATCH_GL(tmpbuf.buf)]
1795   |   ldr CARG1, STR:CARG1[1]
1796   |  cmp RB, CARG3
1797   |  blo ->fff_fallback
1798   |1:  // Fill buffer with char.
1799   |   strb CARG1, [CARG2, CARG4]
1800   |  subs CARG4, CARG4, #1
1801   |  bge <1
1802   |  b ->fff_newstr
1803   |
1804   |.ffunc string_reverse
1805   |  ffgccheck
1806   |  ldrd CARG12, [BASE]
1807   |   cmp NARGS8:RC, #8
1808   |   blo ->fff_fallback
1809   |  checkstr CARG2, ->fff_fallback
1810   |  ldr CARG3, STR:CARG1->len
1811   |   ldr RB, [DISPATCH, #DISPATCH_GL(tmpbuf.sz)]
1812   |    ldr CARG2, [DISPATCH, #DISPATCH_GL(tmpbuf.buf)]
1813   |  mov CARG4, CARG3
1814   |  add CARG1, STR:CARG1, #sizeof(GCstr)
1815   |   cmp RB, CARG3
1816   |   blo ->fff_fallback
1817   |1:  // Reverse string copy.
1818   |  ldrb RB, [CARG1], #1
1819   |   subs CARG4, CARG4, #1
1820   |   blt ->fff_newstr
1821   |  strb RB, [CARG2, CARG4]
1822   |  b <1
1823   |
1824   |.macro ffstring_case, name, lo
1825   |  .ffunc name
1826   |  ffgccheck
1827   |  ldrd CARG12, [BASE]
1828   |   cmp NARGS8:RC, #8
1829   |   blo ->fff_fallback
1830   |  checkstr CARG2, ->fff_fallback
1831   |  ldr CARG3, STR:CARG1->len
1832   |   ldr RB, [DISPATCH, #DISPATCH_GL(tmpbuf.sz)]
1833   |    ldr CARG2, [DISPATCH, #DISPATCH_GL(tmpbuf.buf)]
1834   |  mov CARG4, #0
1835   |  add CARG1, STR:CARG1, #sizeof(GCstr)
1836   |   cmp RB, CARG3
1837   |   blo ->fff_fallback
1838   |1:  // ASCII case conversion.
1839   |  ldrb RB, [CARG1, CARG4]
1840   |   cmp CARG4, CARG3
1841   |   bhs ->fff_newstr
1842   |  sub RC, RB, #lo
1843   |  cmp RC, #26
1844   |  eorlo RB, RB, #0x20
1845   |  strb RB, [CARG2, CARG4]
1846   |   add CARG4, CARG4, #1
1847   |  b <1
1848   |.endmacro
1849   |
1850   |ffstring_case string_lower, 65
1851   |ffstring_case string_upper, 97
1852   |
1853   |//-- Table library ------------------------------------------------------
1854   |
1855   |.ffunc_1 table_getn
1856   |  checktab CARG2, ->fff_fallback
1857   |  .IOS mov RA, BASE
1858   |  bl extern lj_tab_len               // (GCtab *t)
1859   |  // Returns uint32_t (but less than 2^31).
1860   |  .IOS mov BASE, RA
1861   |  mvn CARG2, #~LJ_TISNUM
1862   |  b ->fff_restv
1863   |
1864   |//-- Bit library --------------------------------------------------------
1865   |
1866   |// FP number to bit conversion for soft-float. Clobbers r0-r3.
1867   |->vm_tobit_fb:
1868   |  bhi ->fff_fallback
1869   |->vm_tobit:
1870   |  lsl RB, CARG2, #1
1871   |  adds RB, RB, #0x00200000
1872   |  movpl CARG1, #0                    // |x| < 1?
1873   |  bxpl lr
1874   |  mvn CARG4, #0x3e0
1875   |  subs RB, CARG4, RB, asr #21
1876   |  bmi >1                             // |x| >= 2^32?
1877   |  lsl CARG4, CARG2, #11
1878   |  orr CARG4, CARG4, #0x80000000
1879   |  orr CARG4, CARG4, CARG1, lsr #21
1880   |   cmp CARG2, #0
1881   |  lsr CARG1, CARG4, RB
1882   |   rsblt CARG1, CARG1, #0
1883   |  bx lr
1884   |1:
1885   |  add RB, RB, #21
1886   |  lsr CARG4, CARG1, RB
1887   |  rsb RB, RB, #20
1888   |  lsl CARG1, CARG2, #12
1889   |   cmp CARG2, #0
1890   |  orr CARG1, CARG4, CARG1, lsl RB
1891   |   rsblt CARG1, CARG1, #0
1892   |  bx lr
1893   |
1894   |.macro .ffunc_bit, name
1895   |  .ffunc_1 bit_..name
1896   |  checktp CARG2, LJ_TISNUM
1897   |  blne ->vm_tobit_fb
1898   |.endmacro
1899   |
1900   |.ffunc_bit tobit
1901   |  mvn CARG2, #~LJ_TISNUM
1902   |  b ->fff_restv
1903   |
1904   |.macro .ffunc_bit_op, name, ins
1905   |  .ffunc_bit name
1906   |  mov CARG3, CARG1
1907   |  mov RA, #8
1908   |1:
1909   |  ldrd CARG12, [BASE, RA]
1910   |   cmp RA, NARGS8:RC
1911   |    add RA, RA, #8
1912   |   bge >2
1913   |  checktp CARG2, LJ_TISNUM
1914   |  blne ->vm_tobit_fb
1915   |  ins CARG3, CARG3, CARG1
1916   |  b <1
1917   |.endmacro
1918   |
1919   |.ffunc_bit_op band, and
1920   |.ffunc_bit_op bor, orr
1921   |.ffunc_bit_op bxor, eor
1922   |
1923   |2:
1924   |  mvn CARG4, #~LJ_TISNUM
1925   |   ldr PC, [BASE, FRAME_PC]
1926   |  strd CARG34, [BASE, #-8]
1927   |  b ->fff_res1
1928   |
1929   |.ffunc_bit bswap
1930   |  eor CARG3, CARG1, CARG1, ror #16
1931   |  bic CARG3, CARG3, #0x00ff0000
1932   |  ror CARG1, CARG1, #8
1933   |   mvn CARG2, #~LJ_TISNUM
1934   |  eor CARG1, CARG1, CARG3, lsr #8
1935   |  b ->fff_restv
1936   |
1937   |.ffunc_bit bnot
1938   |  mvn CARG1, CARG1
1939   |  mvn CARG2, #~LJ_TISNUM
1940   |  b ->fff_restv
1941   |
1942   |.macro .ffunc_bit_sh, name, ins, shmod
1943   |  .ffunc bit_..name
1944   |  ldrd CARG12, [BASE, #8]
1945   |   cmp NARGS8:RC, #16
1946   |   blo ->fff_fallback
1947   |  checktp CARG2, LJ_TISNUM
1948   |  blne ->vm_tobit_fb
1949   |.if shmod == 0
1950   |  and RA, CARG1, #31
1951   |.else
1952   |  rsb RA, CARG1, #0
1953   |.endif
1954   |  ldrd CARG12, [BASE]
1955   |  checktp CARG2, LJ_TISNUM
1956   |  blne ->vm_tobit_fb
1957   |  ins CARG1, CARG1, RA
1958   |  mvn CARG2, #~LJ_TISNUM
1959   |  b ->fff_restv
1960   |.endmacro
1961   |
1962   |.ffunc_bit_sh lshift, lsl, 0
1963   |.ffunc_bit_sh rshift, lsr, 0
1964   |.ffunc_bit_sh arshift, asr, 0
1965   |.ffunc_bit_sh rol, ror, 1
1966   |.ffunc_bit_sh ror, ror, 0
1967   |
1968   |//-----------------------------------------------------------------------
1969   |
1970   |->fff_fallback:                      // Call fast function fallback handler.
1971   |  // BASE = new base, RC = nargs*8
1972   |   ldr CARG3, [BASE, FRAME_FUNC]
1973   |  ldr CARG2, L->maxstack
1974   |  add CARG1, BASE, NARGS8:RC
1975   |    ldr PC, [BASE, FRAME_PC]         // Fallback may overwrite PC.
1976   |  str CARG1, L->top
1977   |   ldr CARG3, CFUNC:CARG3->f
1978   |    str BASE, L->base
1979   |  add CARG1, CARG1, #8*LUA_MINSTACK
1980   |    str PC, SAVE_PC                  // Redundant (but a defined value).
1981   |  cmp CARG1, CARG2
1982   |   mov CARG1, L
1983   |  bhi >5                             // Need to grow stack.
1984   |   blx CARG3                         // (lua_State *L)
1985   |  // Either throws an error, or recovers and returns -1, 0 or nresults+1.
1986   |   ldr BASE, L->base
1987   |  cmp CRET1, #0
1988   |   lsl RC, CRET1, #3
1989   |   sub RA, BASE, #8
1990   |  bgt ->fff_res                      // Returned nresults+1?
1991   |1:  // Returned 0 or -1: retry fast path.
1992   |   ldr CARG1, L->top
1993   |    ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
1994   |   sub NARGS8:RC, CARG1, BASE
1995   |  bne ->vm_call_tail                 // Returned -1?
1996   |  ins_callt                          // Returned 0: retry fast path.
1997   |
1998   |// Reconstruct previous base for vmeta_call during tailcall.
1999   |->vm_call_tail:
2000   |  ands CARG1, PC, #FRAME_TYPE
2001   |   bic CARG2, PC, #FRAME_TYPEP
2002   |  ldreq INS, [PC, #-4]
2003   |  andeq CARG2, MASKR8, INS, lsr #5   // Conditional decode_RA8.
2004   |  addeq CARG2, CARG2, #8
2005   |  sub RB, BASE, CARG2
2006   |  b ->vm_call_dispatch               // Resolve again for tailcall.
2007   |
2008   |5:  // Grow stack for fallback handler.
2009   |  mov CARG2, #LUA_MINSTACK
2010   |  bl extern lj_state_growstack       // (lua_State *L, int n)
2011   |  ldr BASE, L->base
2012   |  cmp CARG1, CARG1                   // Set zero-flag to force retry.
2013   |  b <1
2014   |
2015   |->fff_gcstep:                        // Call GC step function.
2016   |  // BASE = new base, RC = nargs*8
2017   |  mov RA, lr
2018   |   str BASE, L->base
2019   |  add CARG2, BASE, NARGS8:RC
2020   |   str PC, SAVE_PC                   // Redundant (but a defined value).
2021   |  str CARG2, L->top
2022   |  mov CARG1, L
2023   |  bl extern lj_gc_step               // (lua_State *L)
2024   |   ldr BASE, L->base
2025   |  mov lr, RA                         // Help return address predictor.
2026   |   ldr CFUNC:CARG3, [BASE, FRAME_FUNC]
2027   |  bx lr
2028   |
2029   |//-----------------------------------------------------------------------
2030   |//-- Special dispatch targets -------------------------------------------
2031   |//-----------------------------------------------------------------------
2032   |
2033   |->vm_record:                         // Dispatch target for recording phase.
2034   |.if JIT
2035   |  ldrb CARG1, [DISPATCH, #DISPATCH_GL(hookmask)]
2036   |  tst CARG1, #HOOK_VMEVENT           // No recording while in vmevent.
2037   |  bne >5
2038   |  // Decrement the hookcount for consistency, but always do the call.
2039   |   ldr CARG2, [DISPATCH, #DISPATCH_GL(hookcount)]
2040   |  tst CARG1, #HOOK_ACTIVE
2041   |  bne >1
2042   |   sub CARG2, CARG2, #1
2043   |  tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT
2044   |   strne CARG2, [DISPATCH, #DISPATCH_GL(hookcount)]
2045   |  b >1
2046   |.endif
2047   |
2048   |->vm_rethook:                        // Dispatch target for return hooks.
2049   |  ldrb CARG1, [DISPATCH, #DISPATCH_GL(hookmask)]
2050   |  tst CARG1, #HOOK_ACTIVE            // Hook already active?
2051   |  beq >1
2052   |5:  // Re-dispatch to static ins.
2053   |  decode_OP OP, INS
2054   |  add OP, DISPATCH, OP, lsl #2
2055   |  ldr pc, [OP, #GG_DISP2STATIC]
2056   |
2057   |->vm_inshook:                        // Dispatch target for instr/line hooks.
2058   |  ldrb CARG1, [DISPATCH, #DISPATCH_GL(hookmask)]
2059   |   ldr CARG2, [DISPATCH, #DISPATCH_GL(hookcount)]
2060   |  tst CARG1, #HOOK_ACTIVE            // Hook already active?
2061   |  bne <5
2062   |  tst CARG1, #LUA_MASKLINE|LUA_MASKCOUNT
2063   |  beq <5
2064   |   subs CARG2, CARG2, #1
2065   |   str CARG2, [DISPATCH, #DISPATCH_GL(hookcount)]
2066   |   beq >1
2067   |  tst CARG1, #LUA_MASKLINE
2068   |  beq <5
2069   |1:
2070   |  mov CARG1, L
2071   |   str BASE, L->base
2072   |  mov CARG2, PC
2073   |  // SAVE_PC must hold the _previous_ PC. The callee updates it with PC.
2074   |  bl extern lj_dispatch_ins          // (lua_State *L, const BCIns *pc)
2075   |3:
2076   |  ldr BASE, L->base
2077   |4:  // Re-dispatch to static ins.
2078   |  ldrb OP, [PC, #-4]
2079   |   ldr INS, [PC, #-4]
2080   |  add OP, DISPATCH, OP, lsl #2
2081   |  ldr OP, [OP, #GG_DISP2STATIC]
2082   |   decode_RA8 RA, INS
2083   |   decode_RD RC, INS
2084   |  bx OP
2085   |
2086   |->cont_hook:                         // Continue from hook yield.
2087   |  ldr CARG1, [CARG4, #-24]
2088   |   add PC, PC, #4
2089   |  str CARG1, SAVE_MULTRES            // Restore MULTRES for *M ins.
2090   |  b <4
2091   |
2092   |->vm_hotloop:                        // Hot loop counter underflow.
2093   |.if JIT
2094   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]  // Same as curr_topL(L).
2095   |   sub CARG1, DISPATCH, #-GG_DISP2J
2096   |   str PC, SAVE_PC
2097   |  ldr CARG3, LFUNC:CARG3->field_pc
2098   |   mov CARG2, PC
2099   |   str L, [DISPATCH, #DISPATCH_J(L)]
2100   |  ldrb CARG3, [CARG3, #PC2PROTO(framesize)]
2101   |   str BASE, L->base
2102   |  add CARG3, BASE, CARG3, lsl #3
2103   |  str CARG3, L->top
2104   |  bl extern lj_trace_hot             // (jit_State *J, const BCIns *pc)
2105   |  b <3
2106   |.endif
2107   |
2108   |->vm_callhook:                       // Dispatch target for call hooks.
2109   |  mov CARG2, PC
2110   |.if JIT
2111   |  b >1
2112   |.endif
2113   |
2114   |->vm_hotcall:                        // Hot call counter underflow.
2115   |.if JIT
2116   |  orr CARG2, PC, #1
2117   |1:
2118   |.endif
2119   |  add CARG4, BASE, RC
2120   |   str PC, SAVE_PC
2121   |    mov CARG1, L
2122   |   str BASE, L->base
2123   |    sub RA, RA, BASE
2124   |  str CARG4, L->top
2125   |  bl extern lj_dispatch_call         // (lua_State *L, const BCIns *pc)
2126   |  // Returns ASMFunction.
2127   |  ldr BASE, L->base
2128   |   ldr CARG4, L->top
2129   |    mov CARG2, #0
2130   |  add RA, BASE, RA
2131   |   sub NARGS8:RC, CARG4, BASE
2132   |    str CARG2, SAVE_PC               // Invalidate for subsequent line hook.
2133   |  ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
2134   |   ldr INS, [PC, #-4]
2135   |  bx CRET1
2136   |
2137   |//-----------------------------------------------------------------------
2138   |//-- Trace exit handler -------------------------------------------------
2139   |//-----------------------------------------------------------------------
2140   |
2141   |->vm_exit_handler:
2142   |.if JIT
2143   |  sub sp, sp, #12
2144   |  push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12}
2145   |  ldr CARG1, [sp, #64]       // Load original value of lr.
2146   |   ldr DISPATCH, [lr]        // Load DISPATCH.
2147   |    add CARG3, sp, #64       // Recompute original value of sp.
2148   |   mv_vmstate CARG4, EXIT
2149   |    str CARG3, [sp, #52]     // Store sp in RID_SP
2150   |   st_vmstate CARG4
2151   |  ldr CARG2, [CARG1, #-4]!   // Get exit instruction.
2152   |   str CARG1, [sp, #56]      // Store exit pc in RID_LR and RID_PC.
2153   |   str CARG1, [sp, #60]
2154   |.if FPU
2155   |  vpush {d0-d15}
2156   |.endif
2157   |  lsl CARG2, CARG2, #8
2158   |  add CARG1, CARG1, CARG2, asr #6
2159   |   ldr CARG2, [lr, #4]       // Load exit stub group offset.
2160   |   sub CARG1, CARG1, lr
2161   |  ldr L, [DISPATCH, #DISPATCH_GL(jit_L)]
2162   |   add CARG1, CARG2, CARG1, lsr #2   // Compute exit number.
2163   |    ldr BASE, [DISPATCH, #DISPATCH_GL(jit_base)]
2164   |   str CARG1, [DISPATCH, #DISPATCH_J(exitno)]
2165   |   mov CARG4, #0
2166   |  str L, [DISPATCH, #DISPATCH_J(L)]
2167   |    str BASE, L->base
2168   |   str CARG4, [DISPATCH, #DISPATCH_GL(jit_L)]
2169   |  sub CARG1, DISPATCH, #-GG_DISP2J
2170   |  mov CARG2, sp
2171   |  bl extern lj_trace_exit            // (jit_State *J, ExitState *ex)
2172   |  // Returns MULTRES (unscaled) or negated error code.
2173   |  ldr CARG2, L->cframe
2174   |   ldr BASE, L->base
2175   |  bic CARG2, CARG2, #~CFRAME_RAWMASK // Use two steps: bic sp is deprecated.
2176   |  mov sp, CARG2
2177   |   ldr PC, SAVE_PC                   // Get SAVE_PC.
2178   |  str L, SAVE_L                      // Set SAVE_L (on-trace resume/yield).
2179   |  b >1
2180   |.endif
2181   |->vm_exit_interp:
2182   |  // CARG1 = MULTRES or negated error code, BASE, PC and DISPATCH set.
2183   |.if JIT
2184   |  ldr L, SAVE_L
2185   |1:
2186   |  cmp CARG1, #0
2187   |  blt >3                             // Check for error from exit.
2188   |   lsl RC, CARG1, #3
2189   |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
2190   |   str RC, SAVE_MULTRES
2191   |   mov CARG3, #0
2192   |  ldr CARG2, LFUNC:CARG2->field_pc
2193   |   str CARG3, [DISPATCH, #DISPATCH_GL(jit_L)]
2194   |    mv_vmstate CARG4, INTERP
2195   |  ldr KBASE, [CARG2, #PC2PROTO(k)]
2196   |  // Modified copy of ins_next which handles function header dispatch, too.
2197   |  ldrb OP, [PC]
2198   |     mov MASKR8, #255
2199   |   ldr INS, [PC], #4
2200   |     lsl MASKR8, MASKR8, #3          // MASKR8 = 255*8.
2201   |    st_vmstate CARG4
2202   |  cmp OP, #BC_FUNCF                  // Function header?
2203   |  ldr OP, [DISPATCH, OP, lsl #2]
2204   |   decode_RA8 RA, INS
2205   |   lsrlo RC, INS, #16        // No: Decode operands A*8 and D.
2206   |   subhs RC, RC, #8
2207   |   addhs RA, RA, BASE        // Yes: RA = BASE+framesize*8, RC = nargs*8
2208   |  bx OP
2209   |
2210   |3:  // Rethrow error from the right C frame.
2211   |  rsb CARG2, CARG1, #0
2212   |  mov CARG1, L
2213   |  bl extern lj_err_throw             // (lua_State *L, int errcode)
2214   |.endif
2215   |
2216   |//-----------------------------------------------------------------------
2217   |//-- Math helper functions ----------------------------------------------
2218   |//-----------------------------------------------------------------------
2219   |
2220   |// FP value rounding. Called from JIT code.
2221   |//
2222   |// double lj_vm_floor/ceil/trunc(double x);
2223   |.macro vm_round, func, hf
2224   |.if FPU
2225   |.if hf == 0
2226   |  vmov d0, CARG1, CARG2
2227   |  vldr d2, <8                        // 2^52
2228   |.else
2229   |  vldr d2, <8                        // 2^52
2230   |  vmov CARG1, CARG2, d0
2231   |.endif
2232   |  vabs.f64 d1, d0
2233   |  vcmp.f64 d1, d2                    // |x| >= 2^52 or NaN?
2234   |  vmrs
2235   |.if "func" == "trunc"
2236   |  bxpl lr                            // Return argument unchanged.
2237   |  vadd.f64 d0, d1, d2
2238   |  vsub.f64 d0, d0, d2                // (|x| + 2^52) - 2^52
2239   |  vldr d2, <9                        // +1.0
2240   |  vcmp.f64 d1, d0                    // |x| < result: subtract +1.0
2241   |  vmrs
2242   |  vsubmi.f64 d0, d0, d2
2243   |  cmp CARG2, #0
2244   |  vnegmi.f64 d0, d0                  // Merge sign bit back in.
2245   |.else
2246   |  vadd.f64 d1, d1, d2
2247   |  bxpl lr                            // Return argument unchanged.
2248   |  cmp CARG2, #0
2249   |  vsub.f64 d1, d1, d2                // (|x| + 2^52) - 2^52
2250   |  vldr d2, <9                        // +1.0
2251   |  vnegmi.f64 d1, d1                  // Merge sign bit back in.
2252   |.if "func" == "floor"
2253   |  vcmp.f64 d0, d1                    // x < result: subtract +1.0.
2254   |  vmrs
2255   |  vsubmi.f64 d0, d1, d2
2256   |.else
2257   |  vcmp.f64 d1, d0                    // x > result: add +1.0.
2258   |  vmrs
2259   |  vaddmi.f64 d0, d1, d2
2260   |.endif
2261   |  vmovpl.f64 d0, d1
2262   |.endif
2263   |.if hf == 0
2264   |  vmov CARG1, CARG2, d0
2265   |.endif
2266   |  bx lr
2267   |
2268   |.else
2269   |
2270   |  lsl CARG3, CARG2, #1
2271   |  adds RB, CARG3, #0x00200000
2272   |  bpl >2                             // |x| < 1?
2273   |  mvn CARG4, #0x3cc
2274   |  subs RB, CARG4, RB, asr #21        // 2^0: RB = 51, 2^51: RB = 0.
2275   |  bxlo lr                            // |x| >= 2^52: done.
2276   |  mvn CARG4, #1
2277   |   bic CARG3, CARG1, CARG4, lsl RB   // ztest = lo & ~lomask
2278   |  and CARG1, CARG1, CARG4, lsl RB    // lo &= lomask
2279   |  subs RB, RB, #32
2280   |   bicpl CARG4, CARG2, CARG4, lsl RB // |x| <= 2^20: ztest |= hi & ~himask
2281   |   orrpl CARG3, CARG3, CARG4
2282   |   mvnpl CARG4, #1
2283   |  andpl CARG2, CARG2, CARG4, lsl RB  // |x| <= 2^20: hi &= himask
2284   |.if "func" == "floor"
2285   |   tst CARG3, CARG2, asr #31         // iszero = ((ztest & signmask) == 0)
2286   |.else
2287   |   bics CARG3, CARG3, CARG2, asr #31 // iszero = ((ztest & ~signmask) == 0)
2288   |.endif
2289   |  bxeq lr                            // iszero: done.
2290   |  mvn CARG4, #1
2291   |  cmp RB, #0
2292   |  lslpl CARG3, CARG4, RB
2293   |  mvnmi CARG3, #0
2294   |  add RB, RB, #32
2295   |  subs CARG1, CARG1, CARG4, lsl RB   // lo = lo-lomask
2296   |  sbc CARG2, CARG2, CARG3            // hi = hi-himask+carry
2297   |  bx lr
2298   |
2299   |2:  // |x| < 1:
2300   |  bxcs lr                            // |x| is not finite.
2301   |  orr CARG3, CARG3, CARG1            // ztest = (2*hi) | lo
2302   |.if "func" == "floor"
2303   |  tst CARG3, CARG2, asr #31          // iszero = ((ztest & signmask) == 0)
2304   |.else
2305   |  bics CARG3, CARG3, CARG2, asr #31  // iszero = ((ztest & ~signmask) == 0)
2306   |.endif
2307   |  mov CARG1, #0                      // lo = 0
2308   |  and CARG2, CARG2, #0x80000000
2309   |  ldrne CARG4, <9                    // hi = sign(x) | (iszero ? 0.0 : 1.0)
2310   |  orrne CARG2, CARG2, CARG4
2311   |  bx lr
2312   |.endif
2313   |.endmacro
2314   |
2315   |.if FPU
2316   |.align 8
2317   |9:
2318   |  .long 0, 0x3ff00000                // +1.0
2319   |8:
2320   |  .long 0, 0x43300000                // 2^52
2321   |.else
2322   |9:
2323   |  .long 0x3ff00000                   // hiword(+1.0)
2324   |.endif
2325   |
2326   |->vm_floor:
2327   |.if not HFABI
2328   |  vm_round floor, 0
2329   |.endif
2330   |->vm_floor_hf:
2331   |.if FPU
2332   |  vm_round floor, 1
2333   |.endif
2334   |
2335   |->vm_ceil:
2336   |.if not HFABI
2337   |  vm_round ceil, 0
2338   |.endif
2339   |->vm_ceil_hf:
2340   |.if FPU
2341   |  vm_round ceil, 1
2342   |.endif
2343   |
2344   |->vm_trunc:
2345   |.if JIT and not HFABI
2346   |  lsl CARG3, CARG2, #1
2347   |  adds RB, CARG3, #0x00200000
2348   |  andpl CARG2, CARG2, #0x80000000    // |x| < 1? hi = sign(x), lo = 0.
2349   |  movpl CARG1, #0
2350   |  bxpl lr
2351   |  mvn CARG4, #0x3cc
2352   |  subs RB, CARG4, RB, asr #21        // 2^0: RB = 51, 2^51: RB = 0.
2353   |  bxlo lr                            // |x| >= 2^52: already done.
2354   |  mvn CARG4, #1
2355   |  and CARG1, CARG1, CARG4, lsl RB    // lo &= lomask
2356   |  subs RB, RB, #32
2357   |  andpl CARG2, CARG2, CARG4, lsl RB  // |x| <= 2^20: hi &= himask
2358   |  bx lr
2359   |.endif
2360   |
2361   |->vm_trunc_hf:
2362   |.if JIT and FPU
2363   |  vm_round trunc, 1
2364   |.endif
2365   |
2366   |  // double lj_vm_mod(double dividend, double divisor);
2367   |->vm_mod:
2368   |.if FPU
2369   |  // Special calling convention. Also, RC (r11) is not preserved.
2370   |  vdiv.f64 d0, d6, d7
2371   |   mov RC, lr
2372   |  bl ->vm_floor_hf
2373   |  vmul.f64 d0, d0, d7
2374   |   mov lr, RC
2375   |  vsub.f64 d6, d6, d0
2376   |  bx lr
2377   |.else
2378   |  push {r0, r1, r2, r3, r4, lr}
2379   |  bl extern __aeabi_ddiv
2380   |  bl ->vm_floor
2381   |  ldrd CARG34, [sp, #8]
2382   |  bl extern __aeabi_dmul
2383   |  ldrd CARG34, [sp]
2384   |  eor CARG2, CARG2, #0x80000000
2385   |  bl extern __aeabi_dadd
2386   |  add sp, sp, #20
2387   |  pop {pc}
2388   |.endif
2389   |
2390   |  // int lj_vm_modi(int dividend, int divisor);
2391   |->vm_modi:
2392   |  ands RB, CARG1, #0x80000000
2393   |  rsbmi CARG1, CARG1, #0             // a = |dividend|
2394   |  eor RB, RB, CARG2, asr #1          // Keep signdiff and sign(divisor).
2395   |  cmp CARG2, #0
2396   |  rsbmi CARG2, CARG2, #0             // b = |divisor|
2397   |  subs CARG4, CARG2, #1
2398   |  cmpne CARG1, CARG2
2399   |  moveq CARG1, #0                    // if (b == 1 || a == b) a = 0
2400   |  tsthi CARG2, CARG4
2401   |  andeq CARG1, CARG1, CARG4          // else if ((b & (b-1)) == 0) a &= b-1
2402   |  bls >1
2403   |  // Use repeated subtraction to get the remainder.
2404   |  clz CARG3, CARG1
2405   |  clz CARG4, CARG2
2406   |  sub CARG4, CARG4, CARG3
2407   |  rsbs CARG3, CARG4, #31             // entry = (31-(clz(b)-clz(a)))*8
2408   |  addne pc, pc, CARG3, lsl #3        // Duff's device.
2409   |  nop
2410   {
2411     int i;
2412     for (i = 31; i >= 0; i--) {
2413       |  cmp CARG1, CARG2, lsl #i
2414       |  subhs CARG1, CARG1, CARG2, lsl #i
2415     }
2416   }
2417   |1:
2418   |  cmp CARG1, #0
2419   |  cmpne RB, #0
2420   |  submi CARG1, CARG1, CARG2          // if (y != 0 && signdiff) y = y - b
2421   |  eors CARG2, CARG1, RB, lsl #1
2422   |  rsbmi CARG1, CARG1, #0             // if (sign(divisor) != sign(y)) y = -y
2423   |  bx lr
2424   |
2425   |//-----------------------------------------------------------------------
2426   |//-- Miscellaneous functions --------------------------------------------
2427   |//-----------------------------------------------------------------------
2428   |
2429   |//-----------------------------------------------------------------------
2430   |//-- FFI helper functions -----------------------------------------------
2431   |//-----------------------------------------------------------------------
2432   |
2433   |// Handler for callback functions.
2434   |// Saveregs already performed. Callback slot number in [sp], g in r12.
2435   |->vm_ffi_callback:
2436   |.if FFI
2437   |.type CTSTATE, CTState, PC
2438   |  ldr CTSTATE, GL:r12->ctype_state
2439   |   add DISPATCH, r12, #GG_G2DISP
2440   |.if FPU
2441   |  str r4, SAVE_R4
2442   |  add r4, sp, CFRAME_SPACE+4+8*8
2443   |  vstmdb r4!, {d8-d15}
2444   |.endif
2445   |.if HFABI
2446   |  add r12, CTSTATE, #offsetof(CTState, cb.fpr[8])
2447   |.endif
2448   |  strd CARG34, CTSTATE->cb.gpr[2]
2449   |  strd CARG12, CTSTATE->cb.gpr[0]
2450   |.if HFABI
2451   |  vstmdb r12!, {d0-d7}
2452   |.endif
2453   |  ldr CARG4, [sp]
2454   |   add CARG3, sp, #CFRAME_SIZE
2455   |    mov CARG1, CTSTATE
2456   |  lsr CARG4, CARG4, #3
2457   |   str CARG3, CTSTATE->cb.stack
2458   |    mov CARG2, sp
2459   |  str CARG4, CTSTATE->cb.slot
2460   |  str CTSTATE, SAVE_PC               // Any value outside of bytecode is ok.
2461   |  bl extern lj_ccallback_enter       // (CTState *cts, void *cf)
2462   |  // Returns lua_State *.
2463   |  ldr BASE, L:CRET1->base
2464   |    mv_vmstate CARG2, INTERP
2465   |  ldr RC, L:CRET1->top
2466   |    mov MASKR8, #255
2467   |   ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
2468   |    mov L, CRET1
2469   |  sub RC, RC, BASE
2470   |    lsl MASKR8, MASKR8, #3           // MASKR8 = 255*8.
2471   |    st_vmstate CARG2
2472   |  ins_callt
2473   |.endif
2474   |
2475   |->cont_ffi_callback:                 // Return from FFI callback.
2476   |.if FFI
2477   |  ldr CTSTATE, [DISPATCH, #DISPATCH_GL(ctype_state)]
2478   |   str BASE, L->base
2479   |   str CARG4, L->top
2480   |  str L, CTSTATE->L
2481   |  mov CARG1, CTSTATE
2482   |  mov CARG2, RA
2483   |  bl extern lj_ccallback_leave       // (CTState *cts, TValue *o)
2484   |  ldrd CARG12, CTSTATE->cb.gpr[0]
2485   |.if HFABI
2486   |  vldr d0, CTSTATE->cb.fpr[0]
2487   |.endif
2488   |  b ->vm_leave_unw
2489   |.endif
2490   |
2491   |->vm_ffi_call:                       // Call C function via FFI.
2492   |  // Caveat: needs special frame unwinding, see below.
2493   |.if FFI
2494   |  .type CCSTATE, CCallState, r4
2495   |  push {CCSTATE, r5, r11, lr}
2496   |  mov CCSTATE, CARG1
2497   |  ldr CARG1, CCSTATE:CARG1->spadj
2498   |   ldrb CARG2, CCSTATE->nsp
2499   |    add CARG3, CCSTATE, #offsetof(CCallState, stack)
2500   |.if HFABI
2501   |  add RB, CCSTATE, #offsetof(CCallState, fpr[0])
2502   |.endif
2503   |  mov r11, sp
2504   |  sub sp, sp, CARG1                  // Readjust stack.
2505   |   subs CARG2, CARG2, #1
2506   |.if HFABI
2507   |  vldm RB, {d0-d7}
2508   |.endif
2509   |    ldr RB, CCSTATE->func
2510   |   bmi >2
2511   |1:  // Copy stack slots.
2512   |  ldr CARG4, [CARG3, CARG2, lsl #2]
2513   |  str CARG4, [sp, CARG2, lsl #2]
2514   |  subs CARG2, CARG2, #1
2515   |  bpl <1
2516   |2:
2517   |  ldrd CARG12, CCSTATE->gpr[0]
2518   |  ldrd CARG34, CCSTATE->gpr[2]
2519   |  blx RB
2520   |  mov sp, r11
2521   |.if HFABI
2522   |  add r12, CCSTATE, #offsetof(CCallState, fpr[4])
2523   |.endif
2524   |  strd CRET1, CCSTATE->gpr[0]
2525   |.if HFABI
2526   |  vstmdb r12!, {d0-d3}
2527   |.endif
2528   |  pop {CCSTATE, r5, r11, pc}
2529   |.endif
2530   |// Note: vm_ffi_call must be the last function in this object file!
2531   |
2532   |//-----------------------------------------------------------------------
2535 /* Generate the code for a single instruction. */
2536 static void build_ins(BuildCtx *ctx, BCOp op, int defop)
2538   int vk = 0;
2539   |=>defop:
2541   switch (op) {
2543   /* -- Comparison ops ---------------------------------------------------- */
2545   /* Remember: all ops branch for a true comparison, fall through otherwise. */
2547   case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT:
2548     |  // RA = src1*8, RC = src2, JMP with RC = target
2549     |   lsl RC, RC, #3
2550     |  ldrd CARG12, [RA, BASE]!
2551     |    ldrh RB, [PC, #2]
2552     |   ldrd CARG34, [RC, BASE]!
2553     |    add PC, PC, #4
2554     |    add RB, PC, RB, lsl #2
2555     |  checktp CARG2, LJ_TISNUM
2556     |  bne >3
2557     |  checktp CARG4, LJ_TISNUM
2558     |  bne >4
2559     |  cmp CARG1, CARG3
2560     if (op == BC_ISLT) {
2561       |  sublt PC, RB, #0x20000
2562     } else if (op == BC_ISGE) {
2563       |  subge PC, RB, #0x20000
2564     } else if (op == BC_ISLE) {
2565       |  suble PC, RB, #0x20000
2566     } else {
2567       |  subgt PC, RB, #0x20000
2568     }
2569     |1:
2570     |  ins_next
2571     |
2572     |3: // CARG12 is not an integer.
2573     |.if FPU
2574     |   vldr d0, [RA]
2575     |  bhi ->vmeta_comp
2576     |  // d0 is a number.
2577     |  checktp CARG4, LJ_TISNUM
2578     |   vldr d1, [RC]
2579     |  blo >5
2580     |  // d0 is a number, CARG3 is an integer.
2581     |  vmov s4, CARG3
2582     |  vcvt.f64.s32 d1, s4
2583     |  b >5
2584     |4:  // CARG1 is an integer, CARG34 is not an integer.
2585     |   vldr d1, [RC]
2586     |  bhi ->vmeta_comp
2587     |  // CARG1 is an integer, d1 is a number.
2588     |  vmov s4, CARG1
2589     |  vcvt.f64.s32 d0, s4
2590     |5:  // d0 and d1 are numbers.
2591     |  vcmp.f64 d0, d1
2592     |  vmrs
2593     |  // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't.
2594     if (op == BC_ISLT) {
2595       |  sublo PC, RB, #0x20000
2596     } else if (op == BC_ISGE) {
2597       |  subhs PC, RB, #0x20000
2598     } else if (op == BC_ISLE) {
2599       |  subls PC, RB, #0x20000
2600     } else {
2601       |  subhi PC, RB, #0x20000
2602     }
2603     |  b <1
2604     |.else
2605     |  bhi ->vmeta_comp
2606     |  // CARG12 is a number.
2607     |  checktp CARG4, LJ_TISNUM
2608     |  movlo RA, RB                     // Save RB.
2609     |  blo >5
2610     |  // CARG12 is a number, CARG3 is an integer.
2611     |  mov CARG1, CARG3
2612     |  mov RC, RA
2613     |  mov RA, RB                       // Save RB.
2614     |  bl extern __aeabi_i2d
2615     |  mov CARG3, CARG1
2616     |  mov CARG4, CARG2
2617     |  ldrd CARG12, [RC]                // Restore first operand.
2618     |  b >5
2619     |4:  // CARG1 is an integer, CARG34 is not an integer.
2620     |  bhi ->vmeta_comp
2621     |  // CARG1 is an integer, CARG34 is a number.
2622     |  mov RA, RB                       // Save RB.
2623     |  bl extern __aeabi_i2d
2624     |  ldrd CARG34, [RC]                // Restore second operand.
2625     |5:  // CARG12 and CARG34 are numbers.
2626     |  bl extern __aeabi_cdcmple
2627     |  // To preserve NaN semantics GE/GT branch on unordered, but LT/LE don't.
2628     if (op == BC_ISLT) {
2629       |  sublo PC, RA, #0x20000
2630     } else if (op == BC_ISGE) {
2631       |  subhs PC, RA, #0x20000
2632     } else if (op == BC_ISLE) {
2633       |  subls PC, RA, #0x20000
2634     } else {
2635       |  subhi PC, RA, #0x20000
2636     }
2637     |  b <1
2638     |.endif
2639     break;
2641   case BC_ISEQV: case BC_ISNEV:
2642     vk = op == BC_ISEQV;
2643     |  // RA = src1*8, RC = src2, JMP with RC = target
2644     |   lsl RC, RC, #3
2645     |  ldrd CARG12, [RA, BASE]!
2646     |    ldrh RB, [PC, #2]
2647     |   ldrd CARG34, [RC, BASE]!
2648     |    add PC, PC, #4
2649     |    add RB, PC, RB, lsl #2
2650     |  checktp CARG2, LJ_TISNUM
2651     |  cmnls CARG4, #-LJ_TISNUM
2652     if (vk) {
2653       |  bls ->BC_ISEQN_Z
2654     } else {
2655       |  bls ->BC_ISNEN_Z
2656     }
2657     |  // Either or both types are not numbers.
2658     |.if FFI
2659     |  checktp CARG2, LJ_TCDATA
2660     |  checktpne CARG4, LJ_TCDATA
2661     |  beq ->vmeta_equal_cd
2662     |.endif
2663     |  cmp CARG2, CARG4                 // Compare types.
2664     |  bne >2                           // Not the same type?
2665     |  checktp CARG2, LJ_TISPRI
2666     |  bhs >1                           // Same type and primitive type?
2667     |
2668     |  // Same types and not a primitive type. Compare GCobj or pvalue.
2669     |  cmp CARG1, CARG3
2670     if (vk) {
2671       |  bne >3                         // Different GCobjs or pvalues?
2672       |1:  // Branch if same.
2673       |  sub PC, RB, #0x20000
2674       |2:  // Different.
2675       |  ins_next
2676       |3:
2677       |  checktp CARG2, LJ_TISTABUD
2678       |  bhi <2                         // Different objects and not table/ud?
2679     } else {
2680       |  beq >1                         // Same GCobjs or pvalues?
2681       |  checktp CARG2, LJ_TISTABUD
2682       |  bhi >2                         // Different objects and not table/ud?
2683     }
2684     |  // Different tables or userdatas. Need to check __eq metamethod.
2685     |  // Field metatable must be at same offset for GCtab and GCudata!
2686     |  ldr TAB:RA, TAB:CARG1->metatable
2687     |  cmp TAB:RA, #0
2688     if (vk) {
2689       |  beq <2                 // No metatable?
2690     } else {
2691       |  beq >2                 // No metatable?
2692     }
2693     |  ldrb RA, TAB:RA->nomm
2694     |   mov CARG4, #1-vk                // ne = 0 or 1.
2695     |   mov CARG2, CARG1
2696     |  tst RA, #1<<MM_eq
2697     |  beq ->vmeta_equal                // 'no __eq' flag not set?
2698     if (vk) {
2699       |  b <2
2700     } else {
2701       |2:  // Branch if different.
2702       |  sub PC, RB, #0x20000
2703       |1:  // Same.
2704       |  ins_next
2705     }
2706     break;
2708   case BC_ISEQS: case BC_ISNES:
2709     vk = op == BC_ISEQS;
2710     |  // RA = src*8, RC = str_const (~), JMP with RC = target
2711     |   mvn RC, RC
2712     |  ldrd CARG12, [BASE, RA]
2713     |    ldrh RB, [PC, #2]
2714     |   ldr STR:CARG3, [KBASE, RC, lsl #2]
2715     |    add PC, PC, #4
2716     |    add RB, PC, RB, lsl #2
2717     |  checktp CARG2, LJ_TSTR
2718     |.if FFI
2719     |  bne >7
2720     |  cmp CARG1, CARG3
2721     |.else
2722     |  cmpeq CARG1, CARG3
2723     |.endif
2724     if (vk) {
2725       |  subeq PC, RB, #0x20000
2726       |1:
2727     } else {
2728       |1:
2729       |  subne PC, RB, #0x20000
2730     }
2731     |  ins_next
2732     |
2733     |.if FFI
2734     |7:
2735     |  checktp CARG2, LJ_TCDATA
2736     |  bne <1
2737     |  b ->vmeta_equal_cd
2738     |.endif
2739     break;
2741   case BC_ISEQN: case BC_ISNEN:
2742     vk = op == BC_ISEQN;
2743     |  // RA = src*8, RC = num_const (~), JMP with RC = target
2744     |   lsl RC, RC, #3
2745     |  ldrd CARG12, [RA, BASE]!
2746     |    ldrh RB, [PC, #2]
2747     |   ldrd CARG34, [RC, KBASE]!
2748     |    add PC, PC, #4
2749     |    add RB, PC, RB, lsl #2
2750     if (vk) {
2751       |->BC_ISEQN_Z:
2752     } else {
2753       |->BC_ISNEN_Z:
2754     }
2755     |  checktp CARG2, LJ_TISNUM
2756     |  bne >3
2757     |  checktp CARG4, LJ_TISNUM
2758     |  bne >4
2759     |  cmp CARG1, CARG3
2760     if (vk) {
2761       |  subeq PC, RB, #0x20000
2762       |1:
2763     } else {
2764       |1:
2765       |  subne PC, RB, #0x20000
2766     }
2767     |2:
2768     |  ins_next
2769     |
2770     |3:  // CARG12 is not an integer.
2771     |.if FFI
2772     |  bhi >7
2773     |.else
2774     if (!vk) {
2775       |  subhi PC, RB, #0x20000
2776     }
2777     |  bhi <2
2778     |.endif
2779     |.if FPU
2780     |  checktp CARG4, LJ_TISNUM
2781     |  vmov s4, CARG3
2782     |   vldr d0, [RA]
2783     |  vldrlo d1, [RC]
2784     |  vcvths.f64.s32 d1, s4
2785     |  b >5
2786     |4:  // CARG1 is an integer, d1 is a number.
2787     |  vmov s4, CARG1
2788     |   vldr d1, [RC]
2789     |  vcvt.f64.s32 d0, s4
2790     |5:  // d0 and d1 are numbers.
2791     |  vcmp.f64 d0, d1
2792     |  vmrs
2793     if (vk) {
2794       |  subeq PC, RB, #0x20000
2795     } else {
2796       |  subne PC, RB, #0x20000
2797     }
2798     |  b <2
2799     |.else
2800     |  // CARG12 is a number.
2801     |  checktp CARG4, LJ_TISNUM
2802     |  movlo RA, RB                     // Save RB.
2803     |  blo >5
2804     |  // CARG12 is a number, CARG3 is an integer.
2805     |  mov CARG1, CARG3
2806     |  mov RC, RA
2807     |4:  // CARG1 is an integer, CARG34 is a number.
2808     |  mov RA, RB                       // Save RB.
2809     |  bl extern __aeabi_i2d
2810     |  ldrd CARG34, [RC]                // Restore other operand.
2811     |5:  // CARG12 and CARG34 are numbers.
2812     |  bl extern __aeabi_cdcmpeq
2813     if (vk) {
2814       |  subeq PC, RA, #0x20000
2815     } else {
2816       |  subne PC, RA, #0x20000
2817     }
2818     |  b <2
2819     |.endif
2820     |
2821     |.if FFI
2822     |7:
2823     |  checktp CARG2, LJ_TCDATA
2824     |  bne <1
2825     |  b ->vmeta_equal_cd
2826     |.endif
2827     break;
2829   case BC_ISEQP: case BC_ISNEP:
2830     vk = op == BC_ISEQP;
2831     |  // RA = src*8, RC = primitive_type (~), JMP with RC = target
2832     |  ldrd CARG12, [BASE, RA]
2833     |   ldrh RB, [PC, #2]
2834     |   add PC, PC, #4
2835     |  mvn RC, RC
2836     |   add RB, PC, RB, lsl #2
2837     |.if FFI
2838     |  checktp CARG2, LJ_TCDATA
2839     |  beq ->vmeta_equal_cd
2840     |.endif
2841     |  cmp CARG2, RC
2842     if (vk) {
2843       |  subeq PC, RB, #0x20000
2844     } else {
2845       |  subne PC, RB, #0x20000
2846     }
2847     |  ins_next
2848     break;
2850   /* -- Unary test and copy ops ------------------------------------------- */
2852   case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF:
2853     |  // RA = dst*8 or unused, RC = src, JMP with RC = target
2854     |  add RC, BASE, RC, lsl #3
2855     |   ldrh RB, [PC, #2]
2856     |  ldrd CARG12, [RC]
2857     |   add PC, PC, #4
2858     |   add RB, PC, RB, lsl #2
2859     |  checktp CARG2, LJ_TTRUE
2860     if (op == BC_ISTC || op == BC_IST) {
2861       |  subls PC, RB, #0x20000
2862       if (op == BC_ISTC) {
2863         |  strdls CARG12, [BASE, RA]
2864       }
2865     } else {
2866       |  subhi PC, RB, #0x20000
2867       if (op == BC_ISFC) {
2868         |  strdhi CARG12, [BASE, RA]
2869       }
2870     }
2871     |  ins_next
2872     break;
2874   /* -- Unary ops --------------------------------------------------------- */
2876   case BC_MOV:
2877     |  // RA = dst*8, RC = src
2878     |  lsl RC, RC, #3
2879     |   ins_next1
2880     |  ldrd CARG12, [BASE, RC]
2881     |   ins_next2
2882     |  strd CARG12, [BASE, RA]
2883     |   ins_next3
2884     break;
2885   case BC_NOT:
2886     |  // RA = dst*8, RC = src
2887     |  add RC, BASE, RC, lsl #3
2888     |   ins_next1
2889     |  ldr CARG1, [RC, #4]
2890     |   add RA, BASE, RA
2891     |   ins_next2
2892     |  checktp CARG1, LJ_TTRUE
2893     |  mvnls CARG2, #~LJ_TFALSE
2894     |  mvnhi CARG2, #~LJ_TTRUE
2895     |  str CARG2, [RA, #4]
2896     |   ins_next3
2897     break;
2898   case BC_UNM:
2899     |  // RA = dst*8, RC = src
2900     |  lsl RC, RC, #3
2901     |  ldrd CARG12, [BASE, RC]
2902     |   ins_next1
2903     |   ins_next2
2904     |  checktp CARG2, LJ_TISNUM
2905     |  bhi ->vmeta_unm
2906     |  eorne CARG2, CARG2, #0x80000000
2907     |  bne >5
2908     |  rsbseq CARG1, CARG1, #0
2909     |  ldrdvs CARG12, >9
2910     |5:
2911     |  strd CARG12, [BASE, RA]
2912     |   ins_next3
2913     |
2914     |.align 8
2915     |9:
2916     |  .long 0x00000000, 0x41e00000     // 2^31.
2917     break;
2918   case BC_LEN:
2919     |  // RA = dst*8, RC = src
2920     |  lsl RC, RC, #3
2921     |  ldrd CARG12, [BASE, RC]
2922     |  checkstr CARG2, >2
2923     |  ldr CARG1, STR:CARG1->len
2924     |1:
2925     |  mvn CARG2, #~LJ_TISNUM
2926     |   ins_next1
2927     |   ins_next2
2928     |  strd CARG12, [BASE, RA]
2929     |   ins_next3
2930     |2:
2931     |  checktab CARG2, ->vmeta_len
2932 #if LJ_52
2933     |  ldr TAB:CARG3, TAB:CARG1->metatable
2934     |  cmp TAB:CARG3, #0
2935     |  bne >9
2936     |3:
2937 #endif
2938     |->BC_LEN_Z:
2939     |  .IOS mov RC, BASE
2940     |  bl extern lj_tab_len             // (GCtab *t)
2941     |  // Returns uint32_t (but less than 2^31).
2942     |  .IOS mov BASE, RC
2943     |  b <1
2944 #if LJ_52
2945     |9:
2946     |  ldrb CARG4, TAB:CARG3->nomm
2947     |  tst CARG4, #1<<MM_len
2948     |  bne <3                           // 'no __len' flag set: done.
2949     |  b ->vmeta_len
2950 #endif
2951     break;
2953   /* -- Binary ops -------------------------------------------------------- */
2955     |.macro ins_arithcheck, cond, ncond, target
2956     ||if (vk == 1) {
2957     |   cmn CARG4, #-LJ_TISNUM
2958     |    cmn..cond CARG2, #-LJ_TISNUM
2959     ||} else {
2960     |   cmn CARG2, #-LJ_TISNUM
2961     |    cmn..cond CARG4, #-LJ_TISNUM
2962     ||}
2963     |  b..ncond target
2964     |.endmacro
2965     |.macro ins_arithcheck_int, target
2966     |  ins_arithcheck eq, ne, target
2967     |.endmacro
2968     |.macro ins_arithcheck_num, target
2969     |  ins_arithcheck lo, hs, target
2970     |.endmacro
2971     |
2972     |.macro ins_arithpre
2973     |  decode_RB8 RB, INS
2974     |   decode_RC8 RC, INS
2975     |  // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8
2976     ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN);
2977     ||switch (vk) {
2978     ||case 0:
2979     |   .if FPU
2980     |   ldrd CARG12, [RB, BASE]!
2981     |    ldrd CARG34, [RC, KBASE]!
2982     |   .else
2983     |   ldrd CARG12, [BASE, RB]
2984     |    ldrd CARG34, [KBASE, RC]
2985     |   .endif
2986     ||  break;
2987     ||case 1:
2988     |   .if FPU
2989     |   ldrd CARG34, [RB, BASE]!
2990     |    ldrd CARG12, [RC, KBASE]!
2991     |   .else
2992     |   ldrd CARG34, [BASE, RB]
2993     |    ldrd CARG12, [KBASE, RC]
2994     |   .endif
2995     ||  break;
2996     ||default:
2997     |   .if FPU
2998     |   ldrd CARG12, [RB, BASE]!
2999     |    ldrd CARG34, [RC, BASE]!
3000     |   .else
3001     |   ldrd CARG12, [BASE, RB]
3002     |    ldrd CARG34, [BASE, RC]
3003     |   .endif
3004     ||  break;
3005     ||}
3006     |.endmacro
3007     |
3008     |.macro ins_arithpre_fpu, reg1, reg2
3009     |.if FPU
3010     ||if (vk == 1) {
3011     |  vldr reg2, [RB]
3012     |  vldr reg1, [RC]
3013     ||} else {
3014     |  vldr reg1, [RB]
3015     |  vldr reg2, [RC]
3016     ||}
3017     |.endif
3018     |.endmacro
3019     |
3020     |.macro ins_arithpost_fpu, reg
3021     |   ins_next1
3022     |  add RA, BASE, RA
3023     |   ins_next2
3024     |  vstr reg, [RA]
3025     |   ins_next3
3026     |.endmacro
3027     |
3028     |.macro ins_arithfallback, ins
3029     ||switch (vk) {
3030     ||case 0:
3031     |   ins ->vmeta_arith_vn
3032     ||  break;
3033     ||case 1:
3034     |   ins ->vmeta_arith_nv
3035     ||  break;
3036     ||default:
3037     |   ins ->vmeta_arith_vv
3038     ||  break;
3039     ||}
3040     |.endmacro
3041     |
3042     |.macro ins_arithdn, intins, fpins, fpcall
3043     |  ins_arithpre
3044     |.if "intins" ~= "vm_modi" and not FPU
3045     |   ins_next1
3046     |.endif
3047     |  ins_arithcheck_int >5
3048     |.if "intins" == "smull"
3049     |  smull CARG1, RC, CARG3, CARG1
3050     |  cmp RC, CARG1, asr #31
3051     |  ins_arithfallback bne
3052     |.elif "intins" == "vm_modi"
3053     |  movs CARG2, CARG3
3054     |  ins_arithfallback beq
3055     |  bl ->vm_modi
3056     |  mvn CARG2, #~LJ_TISNUM
3057     |.else
3058     |  intins CARG1, CARG1, CARG3
3059     |  ins_arithfallback bvs
3060     |.endif
3061     |4:
3062     |.if "intins" == "vm_modi" or FPU
3063     |   ins_next1
3064     |.endif
3065     |   ins_next2
3066     |  strd CARG12, [BASE, RA]
3067     |   ins_next3
3068     |5:  // FP variant.
3069     |  ins_arithpre_fpu d6, d7
3070     |  ins_arithfallback ins_arithcheck_num
3071     |.if FPU
3072     |.if "intins" == "vm_modi"
3073     |  bl fpcall
3074     |.else
3075     |  fpins d6, d6, d7
3076     |.endif
3077     |  ins_arithpost_fpu d6
3078     |.else
3079     |  bl fpcall
3080     |.if "intins" ~= "vm_modi"
3081     |  ins_next1
3082     |.endif
3083     |  b <4
3084     |.endif
3085     |.endmacro
3086     |
3087     |.macro ins_arithfp, fpins, fpcall
3088     |  ins_arithpre
3089     |.if "fpins" ~= "extern" or HFABI
3090     |  ins_arithpre_fpu d0, d1
3091     |.endif
3092     |  ins_arithfallback ins_arithcheck_num
3093     |.if "fpins" == "extern"
3094     |  .IOS mov RC, BASE
3095     |  bl fpcall
3096     |  .IOS mov BASE, RC
3097     |.elif FPU
3098     |  fpins d0, d0, d1
3099     |.else
3100     |  bl fpcall
3101     |.endif
3102     |.if ("fpins" ~= "extern" or HFABI) and FPU
3103     |  ins_arithpost_fpu d0
3104     |.else
3105     |   ins_next1
3106     |   ins_next2
3107     |  strd CARG12, [BASE, RA]
3108     |   ins_next3
3109     |.endif
3110     |.endmacro
3112   case BC_ADDVN: case BC_ADDNV: case BC_ADDVV:
3113     |  ins_arithdn adds, vadd.f64, extern __aeabi_dadd
3114     break;
3115   case BC_SUBVN: case BC_SUBNV: case BC_SUBVV:
3116     |  ins_arithdn subs, vsub.f64, extern __aeabi_dsub
3117     break;
3118   case BC_MULVN: case BC_MULNV: case BC_MULVV:
3119     |  ins_arithdn smull, vmul.f64, extern __aeabi_dmul
3120     break;
3121   case BC_DIVVN: case BC_DIVNV: case BC_DIVVV:
3122     |  ins_arithfp vdiv.f64, extern __aeabi_ddiv
3123     break;
3124   case BC_MODVN: case BC_MODNV: case BC_MODVV:
3125     |  ins_arithdn vm_modi, vm_mod, ->vm_mod
3126     break;
3127   case BC_POW:
3128     |  // NYI: (partial) integer arithmetic.
3129     |  ins_arithfp extern, extern pow
3130     break;
3132   case BC_CAT:
3133     |  decode_RB8 RC, INS
3134     |   decode_RC8 RB, INS
3135     |  // RA = dst*8, RC = src_start*8, RB = src_end*8  (note: RB/RC swapped!)
3136     |  sub CARG3, RB, RC
3137     |   str BASE, L->base
3138     |  add CARG2, BASE, RB
3139     |->BC_CAT_Z:
3140     |  // RA = dst*8, RC = src_start*8, CARG2 = top-1
3141     |  mov CARG1, L
3142     |   str PC, SAVE_PC
3143     |  lsr CARG3, CARG3, #3
3144     |  bl extern lj_meta_cat            // (lua_State *L, TValue *top, int left)
3145     |  // Returns NULL (finished) or TValue * (metamethod).
3146     |  ldr BASE, L->base
3147     |  cmp CRET1, #0
3148     |  bne ->vmeta_binop
3149     |  ldrd CARG34, [BASE, RC]
3150     |   ins_next1
3151     |   ins_next2
3152     |  strd CARG34, [BASE, RA]          // Copy result to RA.
3153     |   ins_next3
3154     break;
3156   /* -- Constant ops ------------------------------------------------------ */
3158   case BC_KSTR:
3159     |  // RA = dst*8, RC = str_const (~)
3160     |  mvn RC, RC
3161     |   ins_next1
3162     |  ldr CARG1, [KBASE, RC, lsl #2]
3163     |  mvn CARG2, #~LJ_TSTR
3164     |   ins_next2
3165     |  strd CARG12, [BASE, RA]
3166     |   ins_next3
3167     break;
3168   case BC_KCDATA:
3169     |.if FFI
3170     |  // RA = dst*8, RC = cdata_const (~)
3171     |  mvn RC, RC
3172     |   ins_next1
3173     |  ldr CARG1, [KBASE, RC, lsl #2]
3174     |  mvn CARG2, #~LJ_TCDATA
3175     |   ins_next2
3176     |  strd CARG12, [BASE, RA]
3177     |   ins_next3
3178     |.endif
3179     break;
3180   case BC_KSHORT:
3181     |  // RA = dst*8, (RC = int16_literal)
3182     |  mov CARG1, INS, asr #16                  // Refetch sign-extended reg.
3183     |  mvn CARG2, #~LJ_TISNUM
3184     |   ins_next1
3185     |   ins_next2
3186     |  strd CARG12, [BASE, RA]
3187     |   ins_next3
3188     break;
3189   case BC_KNUM:
3190     |  // RA = dst*8, RC = num_const
3191     |  lsl RC, RC, #3
3192     |   ins_next1
3193     |  ldrd CARG12, [KBASE, RC]
3194     |   ins_next2
3195     |  strd CARG12, [BASE, RA]
3196     |   ins_next3
3197     break;
3198   case BC_KPRI:
3199     |  // RA = dst*8, RC = primitive_type (~)
3200     |  add RA, BASE, RA
3201     |  mvn RC, RC
3202     |   ins_next1
3203     |   ins_next2
3204     |  str RC, [RA, #4]
3205     |   ins_next3
3206     break;
3207   case BC_KNIL:
3208     |  // RA = base*8, RC = end
3209     |  add RA, BASE, RA
3210     |   add RC, BASE, RC, lsl #3
3211     |  mvn CARG1, #~LJ_TNIL
3212     |  str CARG1, [RA, #4]
3213     |   add RA, RA, #8
3214     |1:
3215     |  str CARG1, [RA, #4]
3216     |  cmp RA, RC
3217     |   add RA, RA, #8
3218     |  blt <1
3219     |  ins_next_
3220     break;
3222   /* -- Upvalue and function ops ------------------------------------------ */
3224   case BC_UGET:
3225     |  // RA = dst*8, RC = uvnum
3226     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
3227     |   lsl RC, RC, #2
3228     |   add RC, RC, #offsetof(GCfuncL, uvptr)
3229     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RC]
3230     |  ldr CARG2, UPVAL:CARG2->v
3231     |  ldrd CARG34, [CARG2]
3232     |   ins_next1
3233     |   ins_next2
3234     |  strd CARG34, [BASE, RA]
3235     |   ins_next3
3236     break;
3237   case BC_USETV:
3238     |  // RA = uvnum*8, RC = src
3239     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
3240     |   lsr RA, RA, #1
3241     |   add RA, RA, #offsetof(GCfuncL, uvptr)
3242     |    lsl RC, RC, #3
3243     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA]
3244     |    ldrd CARG34, [BASE, RC]
3245     |  ldrb RB, UPVAL:CARG2->marked
3246     |  ldrb RC, UPVAL:CARG2->closed
3247     |    ldr CARG2, UPVAL:CARG2->v
3248     |  tst RB, #LJ_GC_BLACK             // isblack(uv)
3249     |   add RB, CARG4, #-LJ_TISGCV
3250     |  cmpne RC, #0
3251     |   strd CARG34, [CARG2]
3252     |  bne >2                           // Upvalue is closed and black?
3253     |1:
3254     |   ins_next
3255     |
3256     |2:  // Check if new value is collectable.
3257     |  cmn RB, #-(LJ_TISNUM - LJ_TISGCV)
3258     |   ldrbhi RC, GCOBJ:CARG3->gch.marked
3259     |  bls <1                           // tvisgcv(v)
3260     |    sub CARG1, DISPATCH, #-GG_DISP2G
3261     |   tst RC, #LJ_GC_WHITES
3262     |  // Crossed a write barrier. Move the barrier forward.
3263     |.if IOS
3264     |  beq <1
3265     |  mov RC, BASE
3266     |  bl extern lj_gc_barrieruv        // (global_State *g, TValue *tv)
3267     |  mov BASE, RC
3268     |.else
3269     |  blne extern lj_gc_barrieruv      // (global_State *g, TValue *tv)
3270     |.endif
3271     |  b <1
3272     break;
3273   case BC_USETS:
3274     |  // RA = uvnum*8, RC = str_const (~)
3275     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
3276     |   lsr RA, RA, #1
3277     |   add RA, RA, #offsetof(GCfuncL, uvptr)
3278     |    mvn RC, RC
3279     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA]
3280     |    ldr STR:CARG3, [KBASE, RC, lsl #2]
3281     |    mvn CARG4, #~LJ_TSTR
3282     |  ldrb RB, UPVAL:CARG2->marked
3283     |   ldr CARG2, UPVAL:CARG2->v
3284     |     ldrb RC, UPVAL:CARG2->closed
3285     |  tst RB, #LJ_GC_BLACK             // isblack(uv)
3286     |    ldrb RB, STR:CARG3->marked
3287     |   strd CARG34, [CARG2]
3288     |  bne >2
3289     |1:
3290     |   ins_next
3291     |
3292     |2:  // Check if string is white and ensure upvalue is closed.
3293     |  tst RB, #LJ_GC_WHITES            // iswhite(str)
3294     |  cmpne RC, #0
3295     |   sub CARG1, DISPATCH, #-GG_DISP2G
3296     |  // Crossed a write barrier. Move the barrier forward.
3297     |.if IOS
3298     |  beq <1
3299     |  mov RC, BASE
3300     |  bl extern lj_gc_barrieruv        // (global_State *g, TValue *tv)
3301     |  mov BASE, RC
3302     |.else
3303     |  blne extern lj_gc_barrieruv      // (global_State *g, TValue *tv)
3304     |.endif
3305     |  b <1
3306     break;
3307   case BC_USETN:
3308     |  // RA = uvnum*8, RC = num_const
3309     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
3310     |   lsr RA, RA, #1
3311     |   add RA, RA, #offsetof(GCfuncL, uvptr)
3312     |    lsl RC, RC, #3
3313     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA]
3314     |    ldrd CARG34, [KBASE, RC]
3315     |  ldr CARG2, UPVAL:CARG2->v
3316     |   ins_next1
3317     |   ins_next2
3318     |  strd CARG34, [CARG2]
3319     |   ins_next3
3320     break;
3321   case BC_USETP:
3322     |  // RA = uvnum*8, RC = primitive_type (~)
3323     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
3324     |   lsr RA, RA, #1
3325     |   add RA, RA, #offsetof(GCfuncL, uvptr)
3326     |  ldr UPVAL:CARG2, [LFUNC:CARG2, RA]
3327     |   mvn RC, RC
3328     |  ldr CARG2, UPVAL:CARG2->v
3329     |   ins_next1
3330     |   ins_next2
3331     |  str RC, [CARG2, #4]
3332     |   ins_next3
3333     break;
3335   case BC_UCLO:
3336     |  // RA = level*8, RC = target
3337     |  ldr CARG3, L->openupval
3338     |   add RC, PC, RC, lsl #2
3339     |   str BASE, L->base
3340     |  cmp CARG3, #0
3341     |   sub PC, RC, #0x20000
3342     |  beq >1
3343     |   mov CARG1, L
3344     |   add CARG2, BASE, RA
3345     |  bl extern lj_func_closeuv        // (lua_State *L, TValue *level)
3346     |  ldr BASE, L->base
3347     |1:
3348     |  ins_next
3349     break;
3351   case BC_FNEW:
3352     |  // RA = dst*8, RC = proto_const (~) (holding function prototype)
3353     |  mvn RC, RC
3354     |   str BASE, L->base
3355     |  ldr CARG2, [KBASE, RC, lsl #2]
3356     |   str PC, SAVE_PC
3357     |  ldr CARG3, [BASE, FRAME_FUNC]
3358     |   mov CARG1, L
3359     |  // (lua_State *L, GCproto *pt, GCfuncL *parent)
3360     |  bl extern lj_func_newL_gc
3361     |  // Returns GCfuncL *.
3362     |  ldr BASE, L->base
3363     |  mvn CARG2, #~LJ_TFUNC
3364     |   ins_next1
3365     |   ins_next2
3366     |  strd CARG12, [BASE, RA]
3367     |   ins_next3
3368     break;
3370   /* -- Table ops --------------------------------------------------------- */
3372   case BC_TNEW:
3373   case BC_TDUP:
3374     |  // RA = dst*8, RC = (hbits|asize) | tab_const (~)
3375     if (op == BC_TDUP) {
3376       |  mvn RC, RC
3377     }
3378     |  ldr CARG3, [DISPATCH, #DISPATCH_GL(gc.total)]
3379     |   ldr CARG4, [DISPATCH, #DISPATCH_GL(gc.threshold)]
3380     |    str BASE, L->base
3381     |    str PC, SAVE_PC
3382     |  cmp CARG3, CARG4
3383     |   mov CARG1, L
3384     |  bhs >5
3385     |1:
3386     if (op == BC_TNEW) {
3387       |  lsl CARG2, RC, #21
3388       |   lsr CARG3, RC, #11
3389       |  asr RC, CARG2, #21
3390       |  lsr CARG2, CARG2, #21
3391       |  cmn RC, #1
3392       |  addeq CARG2, CARG2, #2
3393       |  bl extern lj_tab_new  // (lua_State *L, int32_t asize, uint32_t hbits)
3394       |  // Returns GCtab *.
3395     } else {
3396       |  ldr CARG2, [KBASE, RC, lsl #2]
3397       |  bl extern lj_tab_dup  // (lua_State *L, Table *kt)
3398       |  // Returns GCtab *.
3399     }
3400     |  ldr BASE, L->base
3401     |  mvn CARG2, #~LJ_TTAB
3402     |   ins_next1
3403     |   ins_next2
3404     |  strd CARG12, [BASE, RA]
3405     |   ins_next3
3406     |5:
3407     |  bl extern lj_gc_step_fixtop  // (lua_State *L)
3408     |  mov CARG1, L
3409     |  b <1
3410     break;
3412   case BC_GGET:
3413     |  // RA = dst*8, RC = str_const (~)
3414   case BC_GSET:
3415     |  // RA = dst*8, RC = str_const (~)
3416     |  ldr LFUNC:CARG2, [BASE, FRAME_FUNC]
3417     |   mvn RC, RC
3418     |  ldr TAB:CARG1, LFUNC:CARG2->env
3419     |   ldr STR:RC, [KBASE, RC, lsl #2]
3420     if (op == BC_GGET) {
3421       |  b ->BC_TGETS_Z
3422     } else {
3423       |  b ->BC_TSETS_Z
3424     }
3425     break;
3427   case BC_TGETV:
3428     |  decode_RB8 RB, INS
3429     |   decode_RC8 RC, INS
3430     |  // RA = dst*8, RB = table*8, RC = key*8
3431     |  ldrd TAB:CARG12, [BASE, RB]
3432     |   ldrd CARG34, [BASE, RC]
3433     |  checktab CARG2, ->vmeta_tgetv  // STALL: load CARG12.
3434     |   checktp CARG4, LJ_TISNUM        // Integer key?
3435     |  ldreq CARG4, TAB:CARG1->array
3436     |    ldreq CARG2, TAB:CARG1->asize
3437     |   bne >9
3438     |
3439     |  add CARG4, CARG4, CARG3, lsl #3
3440     |    cmp CARG3, CARG2               // In array part?
3441     |  ldrdlo CARG34, [CARG4]
3442     |    bhs ->vmeta_tgetv
3443     |   ins_next1  // Overwrites RB!
3444     |  checktp CARG4, LJ_TNIL
3445     |  beq >5
3446     |1:
3447     |   ins_next2
3448     |  strd CARG34, [BASE, RA]
3449     |   ins_next3
3450     |
3451     |5:  // Check for __index if table value is nil.
3452     |  ldr TAB:CARG2, TAB:CARG1->metatable
3453     |  cmp TAB:CARG2, #0
3454     |  beq <1                           // No metatable: done.
3455     |  ldrb CARG2, TAB:CARG2->nomm
3456     |  tst CARG2, #1<<MM_index
3457     |  bne <1                           // 'no __index' flag set: done.
3458     |  decode_RB8 RB, INS               // Restore RB.
3459     |  b ->vmeta_tgetv
3460     |
3461     |9:
3462     |  checktp CARG4, LJ_TSTR           // String key?
3463     |   moveq STR:RC, CARG3
3464     |  beq ->BC_TGETS_Z
3465     |  b ->vmeta_tgetv
3466     break;
3467   case BC_TGETS:
3468     |  decode_RB8 RB, INS
3469     |   and RC, RC, #255
3470     |  // RA = dst*8, RB = table*8, RC = str_const (~)
3471     |  ldrd CARG12, [BASE, RB]
3472     |   mvn RC, RC
3473     |   ldr STR:RC, [KBASE, RC, lsl #2]  // STALL: early RC.
3474     |  checktab CARG2, ->vmeta_tgets1
3475     |->BC_TGETS_Z:
3476     |  // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8
3477     |  ldr CARG3, TAB:CARG1->hmask
3478     |   ldr CARG4, STR:RC->hash
3479     |    ldr NODE:INS, TAB:CARG1->node
3480     |     mov TAB:RB, TAB:CARG1
3481     |  and CARG3, CARG3, CARG4                  // idx = str->hash & tab->hmask
3482     |  add CARG3, CARG3, CARG3, lsl #1
3483     |    add NODE:INS, NODE:INS, CARG3, lsl #3  // node = tab->node + idx*3*8
3484     |1:
3485     |  ldrd CARG12, NODE:INS->key  // STALL: early NODE:INS.
3486     |   ldrd CARG34, NODE:INS->val
3487     |    ldr NODE:INS, NODE:INS->next
3488     |  checktp CARG2, LJ_TSTR
3489     |  cmpeq CARG1, STR:RC
3490     |  bne >4
3491     |   checktp CARG4, LJ_TNIL
3492     |   beq >5
3493     |3:
3494     |   ins_next1
3495     |   ins_next2
3496     |  strd CARG34, [BASE, RA]
3497     |   ins_next3
3498     |
3499     |4:  // Follow hash chain.
3500     |  cmp NODE:INS, #0
3501     |  bne <1
3502     |  // End of hash chain: key not found, nil result.
3503     |
3504     |5:  // Check for __index if table value is nil.
3505     |  ldr TAB:CARG1, TAB:RB->metatable
3506     |   mov CARG3, #0  // Optional clear of undef. value (during load stall).
3507     |   mvn CARG4, #~LJ_TNIL
3508     |  cmp TAB:CARG1, #0
3509     |  beq <3                           // No metatable: done.
3510     |  ldrb CARG2, TAB:CARG1->nomm
3511     |  tst CARG2, #1<<MM_index
3512     |  bne <3                           // 'no __index' flag set: done.
3513     |  b ->vmeta_tgets
3514     break;
3515   case BC_TGETB:
3516     |  decode_RB8 RB, INS
3517     |   and RC, RC, #255
3518     |  // RA = dst*8, RB = table*8, RC = index
3519     |  ldrd CARG12, [BASE, RB]
3520     |  checktab CARG2, ->vmeta_tgetb  // STALL: load CARG12.
3521     |   ldr CARG3, TAB:CARG1->asize
3522     |  ldr CARG4, TAB:CARG1->array
3523     |  lsl CARG2, RC, #3
3524     |   cmp RC, CARG3
3525     |  ldrdlo CARG34, [CARG4, CARG2]
3526     |   bhs ->vmeta_tgetb
3527     |   ins_next1  // Overwrites RB!
3528     |  checktp CARG4, LJ_TNIL
3529     |  beq >5
3530     |1:
3531     |   ins_next2
3532     |  strd CARG34, [BASE, RA]
3533     |   ins_next3
3534     |
3535     |5:  // Check for __index if table value is nil.
3536     |  ldr TAB:CARG2, TAB:CARG1->metatable
3537     |  cmp TAB:CARG2, #0
3538     |  beq <1                           // No metatable: done.
3539     |  ldrb CARG2, TAB:CARG2->nomm
3540     |  tst CARG2, #1<<MM_index
3541     |  bne <1                           // 'no __index' flag set: done.
3542     |  b ->vmeta_tgetb
3543     break;
3545   case BC_TSETV:
3546     |  decode_RB8 RB, INS
3547     |   decode_RC8 RC, INS
3548     |  // RA = src*8, RB = table*8, RC = key*8
3549     |  ldrd TAB:CARG12, [BASE, RB]
3550     |   ldrd CARG34, [BASE, RC]
3551     |  checktab CARG2, ->vmeta_tsetv  // STALL: load CARG12.
3552     |   checktp CARG4, LJ_TISNUM        // Integer key?
3553     |  ldreq CARG2, TAB:CARG1->array
3554     |    ldreq CARG4, TAB:CARG1->asize
3555     |   bne >9
3556     |
3557     |  add CARG2, CARG2, CARG3, lsl #3
3558     |    cmp CARG3, CARG4               // In array part?
3559     |  ldrlo INS, [CARG2, #4]
3560     |    bhs ->vmeta_tsetv
3561     |   ins_next1  // Overwrites RB!
3562     |  checktp INS, LJ_TNIL
3563     |  ldrb INS, TAB:CARG1->marked
3564     |   ldrd CARG34, [BASE, RA]
3565     |  beq >5
3566     |1:
3567     |  tst INS, #LJ_GC_BLACK            // isblack(table)
3568     |   strd CARG34, [CARG2]
3569     |  bne >7
3570     |2:
3571     |   ins_next2
3572     |   ins_next3
3573     |
3574     |5:  // Check for __newindex if previous value is nil.
3575     |  ldr TAB:RA, TAB:CARG1->metatable
3576     |  cmp TAB:RA, #0
3577     |  beq <1                           // No metatable: done.
3578     |  ldrb RA, TAB:RA->nomm
3579     |  tst RA, #1<<MM_newindex
3580     |  bne <1                           // 'no __newindex' flag set: done.
3581     |  ldr INS, [PC, #-4]               // Restore RA and RB.
3582     |  decode_RB8 RB, INS
3583     |  decode_RA8 RA, INS
3584     |  b ->vmeta_tsetv
3585     |
3586     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3587     |  barrierback TAB:CARG1, INS, CARG3
3588     |  b <2
3589     |
3590     |9:
3591     |  checktp CARG4, LJ_TSTR           // String key?
3592     |   moveq STR:RC, CARG3
3593     |  beq ->BC_TSETS_Z
3594     |  b ->vmeta_tsetv
3595     break;
3596   case BC_TSETS:
3597     |  decode_RB8 RB, INS
3598     |   and RC, RC, #255
3599     |  // RA = src*8, RB = table*8, RC = str_const (~)
3600     |  ldrd CARG12, [BASE, RB]
3601     |   mvn RC, RC
3602     |   ldr STR:RC, [KBASE, RC, lsl #2]  // STALL: early RC.
3603     |  checktab CARG2, ->vmeta_tsets1
3604     |->BC_TSETS_Z:
3605     |  // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8
3606     |  ldr CARG3, TAB:CARG1->hmask
3607     |   ldr CARG4, STR:RC->hash
3608     |    ldr NODE:INS, TAB:CARG1->node
3609     |     mov TAB:RB, TAB:CARG1
3610     |  and CARG3, CARG3, CARG4                  // idx = str->hash & tab->hmask
3611     |  add CARG3, CARG3, CARG3, lsl #1
3612     |   mov CARG4, #0
3613     |    add NODE:INS, NODE:INS, CARG3, lsl #3  // node = tab->node + idx*3*8
3614     |   strb CARG4, TAB:RB->nomm                // Clear metamethod cache.
3615     |1:
3616     |  ldrd CARG12, NODE:INS->key
3617     |   ldr CARG4, NODE:INS->val.it
3618     |    ldr NODE:CARG3, NODE:INS->next
3619     |  checktp CARG2, LJ_TSTR
3620     |  cmpeq CARG1, STR:RC
3621     |  bne >5
3622     |  ldrb CARG2, TAB:RB->marked
3623     |   checktp CARG4, LJ_TNIL          // Key found, but nil value?
3624     |    ldrd CARG34, [BASE, RA]
3625     |   beq >4
3626     |2:
3627     |  tst CARG2, #LJ_GC_BLACK          // isblack(table)
3628     |    strd CARG34, NODE:INS->val
3629     |  bne >7
3630     |3:
3631     |   ins_next
3632     |
3633     |4:  // Check for __newindex if previous value is nil.
3634     |  ldr TAB:CARG1, TAB:RB->metatable
3635     |  cmp TAB:CARG1, #0
3636     |  beq <2                           // No metatable: done.
3637     |  ldrb CARG1, TAB:CARG1->nomm
3638     |  tst CARG1, #1<<MM_newindex
3639     |  bne <2                           // 'no __newindex' flag set: done.
3640     |  b ->vmeta_tsets
3641     |
3642     |5:  // Follow hash chain.
3643     |  movs NODE:INS, NODE:CARG3
3644     |  bne <1
3645     |  // End of hash chain: key not found, add a new one.
3646     |
3647     |  // But check for __newindex first.
3648     |  ldr TAB:CARG1, TAB:RB->metatable
3649     |   mov CARG3, TMPDp
3650     |   str PC, SAVE_PC
3651     |  cmp TAB:CARG1, #0                // No metatable: continue.
3652     |   str BASE, L->base
3653     |  ldrbne CARG2, TAB:CARG1->nomm
3654     |   mov CARG1, L
3655     |  beq >6
3656     |  tst CARG2, #1<<MM_newindex
3657     |  beq ->vmeta_tsets                // 'no __newindex' flag NOT set: check.
3658     |6:
3659     |  mvn CARG4, #~LJ_TSTR
3660     |   str STR:RC, TMPDlo
3661     |   mov CARG2, TAB:RB
3662     |  str CARG4, TMPDhi
3663     |  bl extern lj_tab_newkey          // (lua_State *L, GCtab *t, TValue *k)
3664     |  // Returns TValue *.
3665     |  ldr BASE, L->base
3666     |  ldrd CARG34, [BASE, RA]
3667     |  strd CARG34, [CRET1]
3668     |  b <3                             // No 2nd write barrier needed.
3669     |
3670     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3671     |  barrierback TAB:RB, CARG2, CARG3
3672     |  b <3
3673     break;
3674   case BC_TSETB:
3675     |  decode_RB8 RB, INS
3676     |   and RC, RC, #255
3677     |  // RA = src*8, RB = table*8, RC = index
3678     |  ldrd CARG12, [BASE, RB]
3679     |  checktab CARG2, ->vmeta_tsetb  // STALL: load CARG12.
3680     |   ldr CARG3, TAB:CARG1->asize
3681     |  ldr RB, TAB:CARG1->array
3682     |  lsl CARG2, RC, #3
3683     |   cmp RC, CARG3
3684     |  ldrdlo CARG34, [CARG2, RB]!
3685     |   bhs ->vmeta_tsetb
3686     |   ins_next1  // Overwrites RB!
3687     |  checktp CARG4, LJ_TNIL
3688     |  ldrb INS, TAB:CARG1->marked
3689     |   ldrd CARG34, [BASE, RA]
3690     |  beq >5
3691     |1:
3692     |  tst INS, #LJ_GC_BLACK            // isblack(table)
3693     |    strd CARG34, [CARG2]
3694     |  bne >7
3695     |2:
3696     |   ins_next2
3697     |   ins_next3
3698     |
3699     |5:  // Check for __newindex if previous value is nil.
3700     |  ldr TAB:RA, TAB:CARG1->metatable
3701     |  cmp TAB:RA, #0
3702     |  beq <1                           // No metatable: done.
3703     |  ldrb RA, TAB:RA->nomm
3704     |  tst RA, #1<<MM_newindex
3705     |  bne <1                           // 'no __newindex' flag set: done.
3706     |  ldr INS, [PC, #-4]               // Restore INS.
3707     |  decode_RA8 RA, INS
3708     |  b ->vmeta_tsetb
3709     |
3710     |7:  // Possible table write barrier for the value. Skip valiswhite check.
3711     |  barrierback TAB:CARG1, INS, CARG3
3712     |  b <2
3713     break;
3715   case BC_TSETM:
3716     |  // RA = base*8 (table at base-1), RC = num_const (start index)
3717     |  add RA, BASE, RA
3718     |1:
3719     |   ldr RB, SAVE_MULTRES
3720     |  ldr TAB:CARG2, [RA, #-8]         // Guaranteed to be a table.
3721     |  ldr CARG1, [KBASE, RC, lsl #3]   // Integer constant is in lo-word.
3722     |   subs RB, RB, #8
3723     |  ldr CARG4, TAB:CARG2->asize
3724     |   beq >4                          // Nothing to copy?
3725     |  add CARG3, CARG1, RB, lsr #3
3726     |  cmp CARG3, CARG4
3727     |   ldr CARG4, TAB:CARG2->array
3728     |    add RB, RA, RB
3729     |  bhi >5
3730     |   add INS, CARG4, CARG1, lsl #3
3731     |    ldrb CARG1, TAB:CARG2->marked
3732     |3:  // Copy result slots to table.
3733     |   ldrd CARG34, [RA], #8
3734     |   strd CARG34, [INS], #8
3735     |  cmp RA, RB
3736     |  blo <3
3737     |    tst CARG1, #LJ_GC_BLACK        // isblack(table)
3738     |    bne >7
3739     |4:
3740     |  ins_next
3741     |
3742     |5:  // Need to resize array part.
3743     |   str BASE, L->base
3744     |  mov CARG1, L
3745     |   str PC, SAVE_PC
3746     |  bl extern lj_tab_reasize         // (lua_State *L, GCtab *t, int nasize)
3747     |  // Must not reallocate the stack.
3748     |  .IOS ldr BASE, L->base
3749     |  b <1
3750     |
3751     |7:  // Possible table write barrier for any value. Skip valiswhite check.
3752     |  barrierback TAB:CARG2, CARG1, CARG3
3753     |  b <4
3754     break;
3756   /* -- Calls and vararg handling ----------------------------------------- */
3758   case BC_CALLM:
3759     |  // RA = base*8, (RB = nresults+1,) RC = extra_nargs
3760     |  ldr CARG1, SAVE_MULTRES
3761     |  decode_RC8 NARGS8:RC, INS
3762     |  add NARGS8:RC, NARGS8:RC, CARG1
3763     |  b ->BC_CALL_Z
3764     break;
3765   case BC_CALL:
3766     |  decode_RC8 NARGS8:RC, INS
3767     |  // RA = base*8, (RB = nresults+1,) RC = (nargs+1)*8
3768     |->BC_CALL_Z:
3769     |  mov RB, BASE                     // Save old BASE for vmeta_call.
3770     |  ldrd CARG34, [BASE, RA]!
3771     |   sub NARGS8:RC, NARGS8:RC, #8
3772     |   add BASE, BASE, #8
3773     |  checkfunc CARG4, ->vmeta_call
3774     |  ins_call
3775     break;
3777   case BC_CALLMT:
3778     |  // RA = base*8, (RB = 0,) RC = extra_nargs
3779     |  ldr CARG1, SAVE_MULTRES
3780     |  add NARGS8:RC, CARG1, RC, lsl #3
3781     |  b ->BC_CALLT1_Z
3782     break;
3783   case BC_CALLT:
3784     |  lsl NARGS8:RC, RC, #3
3785     |  // RA = base*8, (RB = 0,) RC = (nargs+1)*8
3786     |->BC_CALLT1_Z:
3787     |  ldrd LFUNC:CARG34, [RA, BASE]!
3788     |   sub NARGS8:RC, NARGS8:RC, #8
3789     |   add RA, RA, #8
3790     |  checkfunc CARG4, ->vmeta_callt
3791     |  ldr PC, [BASE, FRAME_PC]
3792     |->BC_CALLT2_Z:
3793     |   mov RB, #0
3794     |   ldrb CARG4, LFUNC:CARG3->ffid
3795     |  tst PC, #FRAME_TYPE
3796     |  bne >7
3797     |1:
3798     |  str LFUNC:CARG3, [BASE, FRAME_FUNC]  // Copy function down, but keep PC.
3799     |  cmp NARGS8:RC, #0
3800     |  beq >3
3801     |2:
3802     |  ldrd CARG12, [RA, RB]
3803     |   add INS, RB, #8
3804     |   cmp INS, NARGS8:RC
3805     |  strd CARG12, [BASE, RB]
3806     |    mov RB, INS
3807     |   bne <2
3808     |3:
3809     |  cmp CARG4, #1                    // (> FF_C) Calling a fast function?
3810     |  bhi >5
3811     |4:
3812     |  ins_callt
3813     |
3814     |5:  // Tailcall to a fast function with a Lua frame below.
3815     |  ldr INS, [PC, #-4]
3816     |  decode_RA8 RA, INS
3817     |  sub CARG1, BASE, RA
3818     |  ldr LFUNC:CARG1, [CARG1, #-16]
3819     |  ldr CARG1, LFUNC:CARG1->field_pc
3820     |  ldr KBASE, [CARG1, #PC2PROTO(k)]
3821     |  b <4
3822     |
3823     |7:  // Tailcall from a vararg function.
3824     |  eor PC, PC, #FRAME_VARG
3825     |  tst PC, #FRAME_TYPEP             // Vararg frame below?
3826     |  movne CARG4, #0                  // Clear ffid if no Lua function below.
3827     |  bne <1
3828     |  sub BASE, BASE, PC
3829     |  ldr PC, [BASE, FRAME_PC]
3830     |  tst PC, #FRAME_TYPE
3831     |  movne CARG4, #0                  // Clear ffid if no Lua function below.
3832     |  b <1
3833     break;
3835   case BC_ITERC:
3836     |  // RA = base*8, (RB = nresults+1, RC = nargs+1 (2+1))
3837     |  add RA, BASE, RA
3838     |   mov RB, BASE                    // Save old BASE for vmeta_call.
3839     |  ldrd CARG34, [RA, #-16]
3840     |   ldrd CARG12, [RA, #-8]
3841     |    add BASE, RA, #8
3842     |  strd CARG34, [RA, #8]            // Copy state.
3843     |   strd CARG12, [RA, #16]          // Copy control var.
3844     |  // STALL: locked CARG34.
3845     |  ldrd LFUNC:CARG34, [RA, #-24]
3846     |    mov NARGS8:RC, #16             // Iterators get 2 arguments.
3847     |  // STALL: load CARG34.
3848     |  strd LFUNC:CARG34, [RA]          // Copy callable.
3849     |  checkfunc CARG4, ->vmeta_call
3850     |  ins_call
3851     break;
3853   case BC_ITERN:
3854     |  // RA = base*8, (RB = nresults+1, RC = nargs+1 (2+1))
3855     |.if JIT
3856     |  // NYI: add hotloop, record BC_ITERN.
3857     |.endif
3858     |  add RA, BASE, RA
3859     |  ldr TAB:RB, [RA, #-16]
3860     |  ldr CARG1, [RA, #-8]             // Get index from control var.
3861     |  ldr INS, TAB:RB->asize
3862     |   ldr CARG2, TAB:RB->array
3863     |    add PC, PC, #4
3864     |1:  // Traverse array part.
3865     |  subs RC, CARG1, INS
3866     |   add CARG3, CARG2, CARG1, lsl #3
3867     |  bhs >5                           // Index points after array part?
3868     |   ldrd CARG34, [CARG3]
3869     |   checktp CARG4, LJ_TNIL
3870     |   addeq CARG1, CARG1, #1          // Skip holes in array part.
3871     |   beq <1
3872     |  ldrh RC, [PC, #-2]
3873     |   mvn CARG2, #~LJ_TISNUM
3874     |    strd CARG34, [RA, #8]
3875     |  add RC, PC, RC, lsl #2
3876     |    add RB, CARG1, #1
3877     |   strd CARG12, [RA]
3878     |  sub PC, RC, #0x20000
3879     |    str RB, [RA, #-8]              // Update control var.
3880     |3:
3881     |  ins_next
3882     |
3883     |5:  // Traverse hash part.
3884     |  ldr CARG4, TAB:RB->hmask
3885     |   ldr NODE:RB, TAB:RB->node
3886     |6:
3887     |   add CARG1, RC, RC, lsl #1
3888     |  cmp RC, CARG4                    // End of iteration? Branch to ITERL+1.
3889     |   add NODE:CARG3, NODE:RB, CARG1, lsl #3  // node = tab->node + idx*3*8
3890     |  bhi <3
3891     |   ldrd CARG12, NODE:CARG3->val
3892     |   checktp CARG2, LJ_TNIL
3893     |   add RC, RC, #1
3894     |   beq <6                          // Skip holes in hash part.
3895     |  ldrh RB, [PC, #-2]
3896     |   add RC, RC, INS
3897     |    ldrd CARG34, NODE:CARG3->key
3898     |   str RC, [RA, #-8]               // Update control var.
3899     |   strd CARG12, [RA, #8]
3900     |  add RC, PC, RB, lsl #2
3901     |  sub PC, RC, #0x20000
3902     |    strd CARG34, [RA]
3903     |  b <3
3904     break;
3906   case BC_ISNEXT:
3907     |  // RA = base*8, RC = target (points to ITERN)
3908     |  add RA, BASE, RA
3909     |     add RC, PC, RC, lsl #2
3910     |  ldrd CFUNC:CARG12, [RA, #-24]
3911     |   ldr CARG3, [RA, #-12]
3912     |    ldr CARG4, [RA, #-4]
3913     |  checktp CARG2, LJ_TFUNC
3914     |  ldrbeq CARG1, CFUNC:CARG1->ffid
3915     |   checktpeq CARG3, LJ_TTAB
3916     |    checktpeq CARG4, LJ_TNIL
3917     |  cmpeq CARG1, #FF_next_N
3918     |     subeq PC, RC, #0x20000
3919     |  bne >5
3920     |   ins_next1
3921     |   ins_next2
3922     |  mov CARG1, #0
3923     |  mvn CARG2, #0x00018000
3924     |  strd CARG1, [RA, #-8]            // Initialize control var.
3925     |1:
3926     |   ins_next3
3927     |5:  // Despecialize bytecode if any of the checks fail.
3928     |  mov CARG1, #BC_JMP
3929     |   mov OP, #BC_ITERC
3930     |  strb CARG1, [PC, #-4]
3931     |   sub PC, RC, #0x20000
3932     |   strb OP, [PC]                   // Subsumes ins_next1.
3933     |   ins_next2
3934     |  b <1
3935     break;
3937   case BC_VARG:
3938     |  decode_RB8 RB, INS
3939     |   decode_RC8 RC, INS
3940     |  // RA = base*8, RB = (nresults+1)*8, RC = numparams*8
3941     |  ldr CARG1, [BASE, FRAME_PC]
3942     |  add RC, BASE, RC
3943     |   add RA, BASE, RA
3944     |  add RC, RC, #FRAME_VARG
3945     |   add CARG4, RA, RB
3946     |  sub CARG3, BASE, #8              // CARG3 = vtop
3947     |  sub RC, RC, CARG1                // RC = vbase
3948     |  // Note: RC may now be even _above_ BASE if nargs was < numparams.
3949     |  cmp RB, #0
3950     |   sub CARG1, CARG3, RC
3951     |  beq >5                           // Copy all varargs?
3952     |   sub CARG4, CARG4, #16
3953     |1:  // Copy vararg slots to destination slots.
3954     |  cmp RC, CARG3
3955     |  ldrdlo CARG12, [RC], #8
3956     |  mvnhs CARG2, #~LJ_TNIL
3957     |   cmp RA, CARG4
3958     |  strd CARG12, [RA], #8
3959     |   blo <1
3960     |2:
3961     |  ins_next
3962     |
3963     |5:  // Copy all varargs.
3964     |  ldr CARG4, L->maxstack
3965     |   cmp CARG1, #0
3966     |   movle RB, #8                    // MULTRES = (0+1)*8
3967     |   addgt RB, CARG1, #8
3968     |  add CARG2, RA, CARG1
3969     |   str RB, SAVE_MULTRES
3970     |   ble <2
3971     |  cmp CARG2, CARG4
3972     |  bhi >7
3973     |6:
3974     |   ldrd CARG12, [RC], #8
3975     |   strd CARG12, [RA], #8
3976     |  cmp RC, CARG3
3977     |  blo <6
3978     |  b <2
3979     |
3980     |7:  // Grow stack for varargs.
3981     |  lsr CARG2, CARG1, #3
3982     |   str RA, L->top
3983     |  mov CARG1, L
3984     |   str BASE, L->base
3985     |  sub RC, RC, BASE                 // Need delta, because BASE may change.
3986     |   str PC, SAVE_PC
3987     |  sub RA, RA, BASE
3988     |  bl extern lj_state_growstack     // (lua_State *L, int n)
3989     |  ldr BASE, L->base
3990     |  add RA, BASE, RA
3991     |  add RC, BASE, RC
3992     |  sub CARG3, BASE, #8
3993     |  b <6
3994     break;
3996   /* -- Returns ----------------------------------------------------------- */
3998   case BC_RETM:
3999     |  // RA = results*8, RC = extra results
4000     |  ldr CARG1, SAVE_MULTRES
4001     |   ldr PC, [BASE, FRAME_PC]
4002     |    add RA, BASE, RA
4003     |  add RC, CARG1, RC, lsl #3
4004     |  b ->BC_RETM_Z
4005     break;
4007   case BC_RET:
4008     |  // RA = results*8, RC = nresults+1
4009     |  ldr PC, [BASE, FRAME_PC]
4010     |   lsl RC, RC, #3
4011     |    add RA, BASE, RA
4012     |->BC_RETM_Z:
4013     |   str RC, SAVE_MULTRES
4014     |1:
4015     |  ands CARG1, PC, #FRAME_TYPE
4016     |   eor CARG2, PC, #FRAME_VARG
4017     |  bne ->BC_RETV2_Z
4018     |
4019     |->BC_RET_Z:
4020     |  // BASE = base, RA = resultptr, RC = (nresults+1)*8, PC = return
4021     |  ldr INS, [PC, #-4]
4022     |  subs CARG4, RC, #8
4023     |   sub CARG3, BASE, #8
4024     |  beq >3
4025     |2:
4026     |  ldrd CARG12, [RA], #8
4027     |   add BASE, BASE, #8
4028     |   subs CARG4, CARG4, #8
4029     |  strd CARG12, [BASE, #-16]
4030     |   bne <2
4031     |3:
4032     |  decode_RA8 RA, INS
4033     |  sub CARG4, CARG3, RA
4034     |   decode_RB8 RB, INS
4035     |  ldr LFUNC:CARG1, [CARG4, FRAME_FUNC]
4036     |5:
4037     |  cmp RB, RC                       // More results expected?
4038     |  bhi >6
4039     |  mov BASE, CARG4
4040     |  ldr CARG2, LFUNC:CARG1->field_pc
4041     |   ins_next1
4042     |   ins_next2
4043     |  ldr KBASE, [CARG2, #PC2PROTO(k)]
4044     |   ins_next3
4045     |
4046     |6:  // Fill up results with nil.
4047     |  mvn CARG2, #~LJ_TNIL
4048     |  add BASE, BASE, #8
4049     |   add RC, RC, #8
4050     |  str CARG2, [BASE, #-12]
4051     |  b <5
4052     |
4053     |->BC_RETV1_Z:  // Non-standard return case.
4054     |  add RA, BASE, RA
4055     |->BC_RETV2_Z:
4056     |  tst CARG2, #FRAME_TYPEP
4057     |  bne ->vm_return
4058     |  // Return from vararg function: relocate BASE down.
4059     |  sub BASE, BASE, CARG2
4060     |  ldr PC, [BASE, FRAME_PC]
4061     |  b <1
4062     break;
4064   case BC_RET0: case BC_RET1:
4065     |  // RA = results*8, RC = nresults+1
4066     |  ldr PC, [BASE, FRAME_PC]
4067     |   lsl RC, RC, #3
4068     |   str RC, SAVE_MULTRES
4069     |  ands CARG1, PC, #FRAME_TYPE
4070     |   eor CARG2, PC, #FRAME_VARG
4071     |   ldreq INS, [PC, #-4]
4072     |  bne ->BC_RETV1_Z
4073     if (op == BC_RET1) {
4074       |  ldrd CARG12, [BASE, RA]
4075     }
4076     |  sub CARG4, BASE, #8
4077     |   decode_RA8 RA, INS
4078     if (op == BC_RET1) {
4079       |  strd CARG12, [CARG4]
4080     }
4081     |  sub BASE, CARG4, RA
4082     |   decode_RB8 RB, INS
4083     |  ldr LFUNC:CARG1, [BASE, FRAME_FUNC]
4084     |5:
4085     |  cmp RB, RC
4086     |  bhi >6
4087     |  ldr CARG2, LFUNC:CARG1->field_pc
4088     |   ins_next1
4089     |   ins_next2
4090     |  ldr KBASE, [CARG2, #PC2PROTO(k)]
4091     |   ins_next3
4092     |
4093     |6:  // Fill up results with nil.
4094     |  sub CARG2, CARG4, #4
4095     |  mvn CARG3, #~LJ_TNIL
4096     |  str CARG3, [CARG2, RC]
4097     |  add RC, RC, #8
4098     |  b <5
4099     break;
4101   /* -- Loops and branches ------------------------------------------------ */
4103   |.define FOR_IDX,  [RA];      .define FOR_TIDX,  [RA, #4]
4104   |.define FOR_STOP, [RA, #8];  .define FOR_TSTOP, [RA, #12]
4105   |.define FOR_STEP, [RA, #16]; .define FOR_TSTEP, [RA, #20]
4106   |.define FOR_EXT,  [RA, #24]; .define FOR_TEXT,  [RA, #28]
4108   case BC_FORL:
4109     |.if JIT
4110     |  hotloop
4111     |.endif
4112     |  // Fall through. Assumes BC_IFORL follows.
4113     break;
4115   case BC_JFORI:
4116   case BC_JFORL:
4117 #if !LJ_HASJIT
4118     break;
4119 #endif
4120   case BC_FORI:
4121   case BC_IFORL:
4122     |  // RA = base*8, RC = target (after end of loop or start of loop)
4123     vk = (op == BC_IFORL || op == BC_JFORL);
4124     |  ldrd CARG12, [RA, BASE]!
4125     if (op != BC_JFORL) {
4126       |   add RC, PC, RC, lsl #2
4127     }
4128     if (!vk) {
4129       |  ldrd CARG34, FOR_STOP
4130       |   checktp CARG2, LJ_TISNUM
4131       |  ldr RB, FOR_TSTEP
4132       |   bne >5
4133       |  checktp CARG4, LJ_TISNUM
4134       |   ldr CARG4, FOR_STEP
4135       |  checktpeq RB, LJ_TISNUM
4136       |  bne ->vmeta_for
4137       |  cmp CARG4, #0
4138       |  blt >4
4139       |  cmp CARG1, CARG3
4140     } else {
4141       |  ldrd CARG34, FOR_STEP
4142       |   checktp CARG2, LJ_TISNUM
4143       |   bne >5
4144       |  adds CARG1, CARG1, CARG3
4145       |   ldr CARG4, FOR_STOP
4146       if (op == BC_IFORL) {
4147         |  addvs RC, PC, #0x20000               // Overflow: prevent branch.
4148       } else {
4149         |  bvs >2                               // Overflow: do not enter mcode.
4150       }
4151       |  cmp CARG3, #0
4152       |  blt >4
4153       |  cmp CARG1, CARG4
4154     }
4155     |1:
4156     if (op == BC_FORI) {
4157       |  subgt PC, RC, #0x20000
4158     } else if (op == BC_JFORI) {
4159       |  sub PC, RC, #0x20000
4160       |  ldrhle RC, [PC, #-2]
4161     } else if (op == BC_IFORL) {
4162       |  suble PC, RC, #0x20000
4163     }
4164     if (vk) {
4165       |  strd CARG12, FOR_IDX
4166     }
4167     |2:
4168     |   ins_next1
4169     |   ins_next2
4170     |  strd CARG12, FOR_EXT
4171     if (op == BC_JFORI || op == BC_JFORL) {
4172       |  ble =>BC_JLOOP
4173     }
4174     |3:
4175     |   ins_next3
4176     |
4177     |4:  // Invert check for negative step.
4178     if (!vk) {
4179       |  cmp CARG3, CARG1
4180     } else {
4181       |  cmp CARG4, CARG1
4182     }
4183     |  b <1
4184     |
4185     |5:  // FP loop.
4186     if (!vk) {
4187       |  cmnlo CARG4, #-LJ_TISNUM
4188       |  cmnlo RB, #-LJ_TISNUM
4189       |  bhs ->vmeta_for
4190       |.if FPU
4191       |  vldr d0, FOR_IDX
4192       |  vldr d1, FOR_STOP
4193       |  cmp RB, #0
4194       |  vstr d0, FOR_EXT
4195       |.else
4196       |  cmp RB, #0
4197       |   strd CARG12, FOR_EXT
4198       |  blt >8
4199       |.endif
4200     } else {
4201       |.if FPU
4202       |  vldr d0, FOR_IDX
4203       |  vldr d2, FOR_STEP
4204       |  vldr d1, FOR_STOP
4205       |  cmp CARG4, #0
4206       |  vadd.f64 d0, d0, d2
4207       |.else
4208       |  cmp CARG4, #0
4209       |  blt >8
4210       |  bl extern __aeabi_dadd
4211       |   strd CARG12, FOR_IDX
4212       |  ldrd CARG34, FOR_STOP
4213       |   strd CARG12, FOR_EXT
4214       |.endif
4215     }
4216     |6:
4217     |.if FPU
4218     |  vcmpge.f64 d0, d1
4219     |  vcmplt.f64 d1, d0
4220     |  vmrs
4221     |.else
4222     |  bl extern __aeabi_cdcmple
4223     |.endif
4224     if (vk) {
4225       |.if FPU
4226       |  vstr d0, FOR_IDX
4227       |  vstr d0, FOR_EXT
4228       |.endif
4229     }
4230     if (op == BC_FORI) {
4231       |  subhi PC, RC, #0x20000
4232     } else if (op == BC_JFORI) {
4233       |  sub PC, RC, #0x20000
4234       |  ldrhls RC, [PC, #-2]
4235       |  bls =>BC_JLOOP
4236     } else if (op == BC_IFORL) {
4237       |  subls PC, RC, #0x20000
4238     } else {
4239       |  bls =>BC_JLOOP
4240     }
4241     |  ins_next1
4242     |  ins_next2
4243     |  b <3
4244     |
4245     |.if not FPU
4246     |8:  // Invert check for negative step.
4247     if (vk) {
4248       |  bl extern __aeabi_dadd
4249       |  strd CARG12, FOR_IDX
4250       |  strd CARG12, FOR_EXT
4251     }
4252     |  mov CARG3, CARG1
4253     |  mov CARG4, CARG2
4254     |  ldrd CARG12, FOR_STOP
4255     |  b <6
4256     |.endif
4257     break;
4259   case BC_ITERL:
4260     |.if JIT
4261     |  hotloop
4262     |.endif
4263     |  // Fall through. Assumes BC_IITERL follows.
4264     break;
4266   case BC_JITERL:
4267 #if !LJ_HASJIT
4268     break;
4269 #endif
4270   case BC_IITERL:
4271     |  // RA = base*8, RC = target
4272     |  ldrd CARG12, [RA, BASE]!
4273     if (op == BC_JITERL) {
4274       |  cmn CARG2, #-LJ_TNIL           // Stop if iterator returned nil.
4275       |  strdne CARG12, [RA, #-8]
4276       |  bne =>BC_JLOOP
4277     } else {
4278       |   add RC, PC, RC, lsl #2
4279       |  // STALL: load CARG12.
4280       |  cmn CARG2, #-LJ_TNIL           // Stop if iterator returned nil.
4281       |  subne PC, RC, #0x20000         // Otherwise save control var + branch.
4282       |  strdne CARG12, [RA, #-8]
4283     }
4284     |  ins_next
4285     break;
4287   case BC_LOOP:
4288     |  // RA = base*8, RC = target (loop extent)
4289     |  // Note: RA/RC is only used by trace recorder to determine scope/extent
4290     |  // This opcode does NOT jump, it's only purpose is to detect a hot loop.
4291     |.if JIT
4292     |  hotloop
4293     |.endif
4294     |  // Fall through. Assumes BC_ILOOP follows.
4295     break;
4297   case BC_ILOOP:
4298     |  // RA = base*8, RC = target (loop extent)
4299     |  ins_next
4300     break;
4302   case BC_JLOOP:
4303     |.if JIT
4304     |  // RA = base (ignored), RC = traceno
4305     |  ldr CARG1, [DISPATCH, #DISPATCH_J(trace)]
4306     |   mov CARG2, #0  // Traces on ARM don't store the trace number, so use 0.
4307     |  ldr TRACE:RC, [CARG1, RC, lsl #2]
4308     |   st_vmstate CARG2
4309     |  ldr RA, TRACE:RC->mcode
4310     |   str BASE, [DISPATCH, #DISPATCH_GL(jit_base)]
4311     |   str L, [DISPATCH, #DISPATCH_GL(jit_L)]
4312     |  bx RA
4313     |.endif
4314     break;
4316   case BC_JMP:
4317     |  // RA = base*8 (only used by trace recorder), RC = target
4318     |  add RC, PC, RC, lsl #2
4319     |  sub PC, RC, #0x20000
4320     |  ins_next
4321     break;
4323   /* -- Function headers -------------------------------------------------- */
4325   case BC_FUNCF:
4326     |.if JIT
4327     |  hotcall
4328     |.endif
4329   case BC_FUNCV:  /* NYI: compiled vararg functions. */
4330     |  // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow.
4331     break;
4333   case BC_JFUNCF:
4334 #if !LJ_HASJIT
4335     break;
4336 #endif
4337   case BC_IFUNCF:
4338     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8
4339     |  ldr CARG1, L->maxstack
4340     |   ldrb CARG2, [PC, #-4+PC2PROTO(numparams)]
4341     |    ldr KBASE, [PC, #-4+PC2PROTO(k)]
4342     |  cmp RA, CARG1
4343     |  bhi ->vm_growstack_l
4344     if (op != BC_JFUNCF) {
4345       |  ins_next1
4346       |  ins_next2
4347     }
4348     |2:
4349     |  cmp NARGS8:RC, CARG2, lsl #3     // Check for missing parameters.
4350     |   mvn CARG4, #~LJ_TNIL
4351     |  blo >3
4352     if (op == BC_JFUNCF) {
4353       |  decode_RD RC, INS
4354       |  b =>BC_JLOOP
4355     } else {
4356       |  ins_next3
4357     }
4358     |
4359     |3:  // Clear missing parameters.
4360     |  strd CARG34, [BASE, NARGS8:RC]
4361     |  add NARGS8:RC, NARGS8:RC, #8
4362     |  b <2
4363     break;
4365   case BC_JFUNCV:
4366 #if !LJ_HASJIT
4367     break;
4368 #endif
4369     |  NYI  // NYI: compiled vararg functions
4370     break;  /* NYI: compiled vararg functions. */
4372   case BC_IFUNCV:
4373     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = LFUNC, RC = nargs*8
4374     |  ldr CARG1, L->maxstack
4375     |   add CARG4, BASE, RC
4376     |  add RA, RA, RC
4377     |   str LFUNC:CARG3, [CARG4]        // Store copy of LFUNC.
4378     |   add CARG2, RC, #8+FRAME_VARG
4379     |    ldr KBASE, [PC, #-4+PC2PROTO(k)]
4380     |  cmp RA, CARG1
4381     |   str CARG2, [CARG4, #4]          // Store delta + FRAME_VARG.
4382     |  bhs ->vm_growstack_l
4383     |  ldrb RB, [PC, #-4+PC2PROTO(numparams)]
4384     |   mov RA, BASE
4385     |   mov RC, CARG4
4386     |  cmp RB, #0
4387     |   add BASE, CARG4, #8
4388     |  beq >3
4389     |  mvn CARG3, #~LJ_TNIL
4390     |1:
4391     |  cmp RA, RC                       // Less args than parameters?
4392     |   ldrdlo CARG12, [RA], #8
4393     |   movhs CARG2, CARG3
4394     |    strlo CARG3, [RA, #-4]         // Clear old fixarg slot (help the GC).
4395     |2:
4396     |  subs RB, RB, #1
4397     |   strd CARG12, [CARG4, #8]!
4398     |  bne <1
4399     |3:
4400     |  ins_next
4401     break;
4403   case BC_FUNCC:
4404   case BC_FUNCCW:
4405     |  // BASE = new base, RA = BASE+framesize*8, CARG3 = CFUNC, RC = nargs*8
4406     if (op == BC_FUNCC) {
4407       |  ldr CARG4, CFUNC:CARG3->f
4408     } else {
4409       |  ldr CARG4, [DISPATCH, #DISPATCH_GL(wrapf)]
4410     }
4411     |   add CARG2, RA, NARGS8:RC
4412     |   ldr CARG1, L->maxstack
4413     |  add RC, BASE, NARGS8:RC
4414     |    str BASE, L->base
4415     |   cmp CARG2, CARG1
4416     |  str RC, L->top
4417     if (op == BC_FUNCCW) {
4418       |  ldr CARG2, CFUNC:CARG3->f
4419     }
4420     |    mv_vmstate CARG3, C
4421     |  mov CARG1, L
4422     |   bhi ->vm_growstack_c            // Need to grow stack.
4423     |    st_vmstate CARG3
4424     |  blx CARG4                        // (lua_State *L [, lua_CFunction f])
4425     |  // Returns nresults.
4426     |  ldr BASE, L->base
4427     |    mv_vmstate CARG3, INTERP
4428     |   ldr CRET2, L->top
4429     |   lsl RC, CRET1, #3
4430     |    st_vmstate CARG3
4431     |  ldr PC, [BASE, FRAME_PC]
4432     |   sub RA, CRET2, RC               // RA = L->top - nresults*8
4433     |  b ->vm_returnc
4434     break;
4436   /* ---------------------------------------------------------------------- */
4438   default:
4439     fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]);
4440     exit(2);
4441     break;
4442   }
4445 static int build_backend(BuildCtx *ctx)
4447   int op;
4449   dasm_growpc(Dst, BC__MAX);
4451   build_subroutines(ctx);
4453   |.code_op
4454   for (op = 0; op < BC__MAX; op++)
4455     build_ins(ctx, (BCOp)op, op);
4457   return BC__MAX;
4460 /* Emit pseudo frame-info for all assembler functions. */
4461 static void emit_asm_debug(BuildCtx *ctx)
4463   int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code);
4464   int i;
4465   switch (ctx->mode) {
4466   case BUILD_elfasm:
4467     fprintf(ctx->fp, "\t.section .debug_frame,\"\",%%progbits\n");
4468     fprintf(ctx->fp,
4469         ".Lframe0:\n"
4470         "\t.long .LECIE0-.LSCIE0\n"
4471         ".LSCIE0:\n"
4472         "\t.long 0xffffffff\n"
4473         "\t.byte 0x1\n"
4474         "\t.string \"\"\n"
4475         "\t.uleb128 0x1\n"
4476         "\t.sleb128 -4\n"
4477         "\t.byte 0xe\n"                         /* Return address is in lr. */
4478         "\t.byte 0xc\n\t.uleb128 0xd\n\t.uleb128 0\n"   /* def_cfa sp */
4479         "\t.align 2\n"
4480         ".LECIE0:\n\n");
4481     fprintf(ctx->fp,
4482         ".LSFDE0:\n"
4483         "\t.long .LEFDE0-.LASFDE0\n"
4484         ".LASFDE0:\n"
4485         "\t.long .Lframe0\n"
4486         "\t.long .Lbegin\n"
4487         "\t.long %d\n"
4488         "\t.byte 0xe\n\t.uleb128 %d\n"          /* def_cfa_offset */
4489         "\t.byte 0x8e\n\t.uleb128 1\n",         /* offset lr */
4490         fcofs, CFRAME_SIZE);
4491     for (i = 11; i >= (LJ_ARCH_HASFPU ? 5 : 4); i--)  /* offset r4-r11 */
4492       fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 2+(11-i));
4493 #if LJ_ARCH_HASFPU
4494     for (i = 15; i >= 8; i--)  /* offset d8-d15 */
4495       fprintf(ctx->fp, "\t.byte 5\n\t.uleb128 %d, %d\n",
4496         64+2*i, 10+2*(15-i));
4497     fprintf(ctx->fp, "\t.byte 0x84\n\t.uleb128 %d\n", 25);  /* offset r4 */
4498 #endif
4499     fprintf(ctx->fp,
4500         "\t.align 2\n"
4501         ".LEFDE0:\n\n");
4502 #if LJ_HASFFI
4503     fprintf(ctx->fp,
4504         ".LSFDE1:\n"
4505         "\t.long .LEFDE1-.LASFDE1\n"
4506         ".LASFDE1:\n"
4507         "\t.long .Lframe0\n"
4508         "\t.long lj_vm_ffi_call\n"
4509         "\t.long %d\n"
4510         "\t.byte 0xe\n\t.uleb128 16\n"          /* def_cfa_offset */
4511         "\t.byte 0x8e\n\t.uleb128 1\n"          /* offset lr */
4512         "\t.byte 0x8b\n\t.uleb128 2\n"          /* offset r11 */
4513         "\t.byte 0x85\n\t.uleb128 3\n"          /* offset r5 */
4514         "\t.byte 0x84\n\t.uleb128 4\n"          /* offset r4 */
4515         "\t.byte 0xd\n\t.uleb128 0xb\n"         /* def_cfa_register r11 */
4516         "\t.align 2\n"
4517         ".LEFDE1:\n\n", (int)ctx->codesz - fcofs);
4518 #endif
4519     break;
4520   default:
4521     break;
4522   }