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