Mercurial
view third_party/luajit/src/vm_mips.dasc @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 94705b5986b3 |
| children |
line wrap: on
line source
|// Low-level VM code for MIPS CPUs. |// Bytecode interpreter, fast functions and helper functions. |// Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h |// |// MIPS soft-float support contributed by Djordje Kovacevic and |// Stefan Pejic from RT-RK.com, sponsored by Cisco Systems, Inc. | |.arch mips |.section code_op, code_sub | |.actionlist build_actionlist |.globals GLOB_ |.globalnames globnames |.externnames extnames | |// Note: The ragged indentation of the instructions is intentional. |// The starting columns indicate data dependencies. | |//----------------------------------------------------------------------- | |// Fixed register assignments for the interpreter. |// Don't use: r0 = 0, r26/r27 = reserved, r28 = gp, r29 = sp, r31 = ra | |.macro .FPU, a, b |.if FPU | a, b |.endif |.endmacro | |// The following must be C callee-save (but BASE is often refetched). |.define BASE, r16 // Base of current Lua stack frame. |.define KBASE, r17 // Constants of current Lua function. |.define PC, r18 // Next PC. |.define DISPATCH, r19 // Opcode dispatch table. |.define LREG, r20 // Register holding lua_State (also in SAVE_L). |.define MULTRES, r21 // Size of multi-result: (nresults+1)*8. | |.define JGL, r30 // On-trace: global_State + 32768. | |// Constants for type-comparisons, stores and conversions. C callee-save. |.define TISNUM, r22 |.define TISNIL, r30 |.if FPU |.define TOBIT, f30 // 2^52 + 2^51. |.endif | |// The following temporaries are not saved across C calls, except for RA. |.define RA, r23 // Callee-save. |.define RB, r8 |.define RC, r9 |.define RD, r10 |.define INS, r11 | |.define AT, r1 // Assembler temporary. |.define TMP0, r12 |.define TMP1, r13 |.define TMP2, r14 |.define TMP3, r15 | |// MIPS o32 calling convention. |.define CFUNCADDR, r25 |.define CARG1, r4 |.define CARG2, r5 |.define CARG3, r6 |.define CARG4, r7 | |.define CRET1, r2 |.define CRET2, r3 | |.if ENDIAN_LE |.define SFRETLO, CRET1 |.define SFRETHI, CRET2 |.define SFARG1LO, CARG1 |.define SFARG1HI, CARG2 |.define SFARG2LO, CARG3 |.define SFARG2HI, CARG4 |.else |.define SFRETLO, CRET2 |.define SFRETHI, CRET1 |.define SFARG1LO, CARG2 |.define SFARG1HI, CARG1 |.define SFARG2LO, CARG4 |.define SFARG2HI, CARG3 |.endif | |.if FPU |.define FARG1, f12 |.define FARG2, f14 | |.define FRET1, f0 |.define FRET2, f2 |.endif | |// Stack layout while in interpreter. Must match with lj_frame.h. |.if FPU // MIPS32 hard-float. | |.define CFRAME_SPACE, 112 // Delta for sp. | |.define SAVE_ERRF, 124(sp) // 32 bit C frame info. |.define SAVE_NRES, 120(sp) |.define SAVE_CFRAME, 116(sp) |.define SAVE_L, 112(sp) |//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by interpreter. |.define SAVE_GPR_, 72 // .. 72+10*4: 32 bit GPR saves. |.define SAVE_FPR_, 24 // .. 24+6*8: 64 bit FPR saves. | |.else // MIPS32 soft-float | |.define CFRAME_SPACE, 64 // Delta for sp. | |.define SAVE_ERRF, 76(sp) // 32 bit C frame info. |.define SAVE_NRES, 72(sp) |.define SAVE_CFRAME, 68(sp) |.define SAVE_L, 64(sp) |//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by interpreter. |.define SAVE_GPR_, 24 // .. 24+10*4: 32 bit GPR saves. | |.endif | |.define SAVE_PC, 20(sp) |.define ARG5, 16(sp) |.define CSAVE_4, 12(sp) |.define CSAVE_3, 8(sp) |.define CSAVE_2, 4(sp) |.define CSAVE_1, 0(sp) |//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by callee. | |.define ARG5_OFS, 16 |.define SAVE_MULTRES, ARG5 | |//----------------------------------------------------------------------- | |.macro saveregs | addiu sp, sp, -CFRAME_SPACE | sw ra, SAVE_GPR_+9*4(sp) | sw r30, SAVE_GPR_+8*4(sp) | .FPU sdc1 f30, SAVE_FPR_+5*8(sp) | sw r23, SAVE_GPR_+7*4(sp) | sw r22, SAVE_GPR_+6*4(sp) | .FPU sdc1 f28, SAVE_FPR_+4*8(sp) | sw r21, SAVE_GPR_+5*4(sp) | sw r20, SAVE_GPR_+4*4(sp) | .FPU sdc1 f26, SAVE_FPR_+3*8(sp) | sw r19, SAVE_GPR_+3*4(sp) | sw r18, SAVE_GPR_+2*4(sp) | .FPU sdc1 f24, SAVE_FPR_+2*8(sp) | sw r17, SAVE_GPR_+1*4(sp) | sw r16, SAVE_GPR_+0*4(sp) | .FPU sdc1 f22, SAVE_FPR_+1*8(sp) | .FPU sdc1 f20, SAVE_FPR_+0*8(sp) |.endmacro | |.macro restoreregs_ret | lw ra, SAVE_GPR_+9*4(sp) | lw r30, SAVE_GPR_+8*4(sp) | .FPU ldc1 f30, SAVE_FPR_+5*8(sp) | lw r23, SAVE_GPR_+7*4(sp) | lw r22, SAVE_GPR_+6*4(sp) | .FPU ldc1 f28, SAVE_FPR_+4*8(sp) | lw r21, SAVE_GPR_+5*4(sp) | lw r20, SAVE_GPR_+4*4(sp) | .FPU ldc1 f26, SAVE_FPR_+3*8(sp) | lw r19, SAVE_GPR_+3*4(sp) | lw r18, SAVE_GPR_+2*4(sp) | .FPU ldc1 f24, SAVE_FPR_+2*8(sp) | lw r17, SAVE_GPR_+1*4(sp) | lw r16, SAVE_GPR_+0*4(sp) | .FPU ldc1 f22, SAVE_FPR_+1*8(sp) | .FPU ldc1 f20, SAVE_FPR_+0*8(sp) | jr ra | addiu sp, sp, CFRAME_SPACE |.endmacro | |// Type definitions. Some of these are only used for documentation. |.type L, lua_State, LREG |.type GL, global_State |.type TVALUE, TValue |.type GCOBJ, GCobj |.type STR, GCstr |.type TAB, GCtab |.type LFUNC, GCfuncL |.type CFUNC, GCfuncC |.type PROTO, GCproto |.type UPVAL, GCupval |.type NODE, Node |.type NARGS8, int |.type TRACE, GCtrace |.type SBUF, SBuf | |//----------------------------------------------------------------------- | |// Trap for not-yet-implemented parts. |.macro NYI; .long 0xec1cf0f0; .endmacro | |// Macros to mark delay slots. |.macro ., a; a; .endmacro |.macro ., a,b; a,b; .endmacro |.macro ., a,b,c; a,b,c; .endmacro | |//----------------------------------------------------------------------- | |// Endian-specific defines. |.if ENDIAN_LE |.define FRAME_PC, -4 |.define FRAME_FUNC, -8 |.define HI, 4 |.define LO, 0 |.define OFS_RD, 2 |.define OFS_RA, 1 |.define OFS_OP, 0 |.else |.define FRAME_PC, -8 |.define FRAME_FUNC, -4 |.define HI, 0 |.define LO, 4 |.define OFS_RD, 0 |.define OFS_RA, 2 |.define OFS_OP, 3 |.endif | |// Instruction decode. |.macro decode_OP1, dst, ins; andi dst, ins, 0xff; .endmacro |.macro decode_OP4a, dst, ins; andi dst, ins, 0xff; .endmacro |.macro decode_OP4b, dst; sll dst, dst, 2; .endmacro |.macro decode_RC4a, dst, ins; srl dst, ins, 14; .endmacro |.macro decode_RC4b, dst; andi dst, dst, 0x3fc; .endmacro |.macro decode_RD4b, dst; sll dst, dst, 2; .endmacro |.macro decode_RA8a, dst, ins; srl dst, ins, 5; .endmacro |.macro decode_RA8b, dst; andi dst, dst, 0x7f8; .endmacro |.macro decode_RB8a, dst, ins; srl dst, ins, 21; .endmacro |.macro decode_RB8b, dst; andi dst, dst, 0x7f8; .endmacro |.macro decode_RD8a, dst, ins; srl dst, ins, 16; .endmacro |.macro decode_RD8b, dst; sll dst, dst, 3; .endmacro |.macro decode_RDtoRC8, dst, src; andi dst, src, 0x7f8; .endmacro | |// Instruction fetch. |.macro ins_NEXT1 | lw INS, 0(PC) | addiu PC, PC, 4 |.endmacro |// Instruction decode+dispatch. |.macro ins_NEXT2 | decode_OP4a TMP1, INS | decode_OP4b TMP1 | addu TMP0, DISPATCH, TMP1 | decode_RD8a RD, INS | lw AT, 0(TMP0) | decode_RA8a RA, INS | decode_RD8b RD | jr AT | decode_RA8b RA |.endmacro |.macro ins_NEXT | ins_NEXT1 | ins_NEXT2 |.endmacro | |// Instruction footer. |.if 1 | // Replicated dispatch. Less unpredictable branches, but higher I-Cache use. | .define ins_next, ins_NEXT | .define ins_next_, ins_NEXT | .define ins_next1, ins_NEXT1 | .define ins_next2, ins_NEXT2 |.else | // Common dispatch. Lower I-Cache use, only one (very) unpredictable branch. | // Affects only certain kinds of benchmarks (and only with -j off). | .macro ins_next | b ->ins_next | .endmacro | .macro ins_next1 | .endmacro | .macro ins_next2 | b ->ins_next | .endmacro | .macro ins_next_ | ->ins_next: | ins_NEXT | .endmacro |.endif | |// Call decode and dispatch. |.macro ins_callt | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC | lw PC, LFUNC:RB->pc | lw INS, 0(PC) | addiu PC, PC, 4 | decode_OP4a TMP1, INS | decode_RA8a RA, INS | decode_OP4b TMP1 | decode_RA8b RA | addu TMP0, DISPATCH, TMP1 | lw TMP0, 0(TMP0) | jr TMP0 | addu RA, RA, BASE |.endmacro | |.macro ins_call | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, PC = caller PC | sw PC, FRAME_PC(BASE) | ins_callt |.endmacro | |//----------------------------------------------------------------------- | |.macro branch_RD | srl TMP0, RD, 1 | lui AT, (-(BCBIAS_J*4 >> 16) & 65535) | addu TMP0, TMP0, AT | addu PC, PC, TMP0 |.endmacro | |// Assumes DISPATCH is relative to GL. #define DISPATCH_GL(field) (GG_DISP2G + (int)offsetof(global_State, field)) #define DISPATCH_J(field) (GG_DISP2J + (int)offsetof(jit_State, field)) #define GG_DISP2GOT (GG_OFS(got) - GG_OFS(dispatch)) #define DISPATCH_GOT(name) (GG_DISP2GOT + 4*LJ_GOT_##name) | #define PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) | |.macro load_got, func | lw CFUNCADDR, DISPATCH_GOT(func)(DISPATCH) |.endmacro |// Much faster. Sadly, there's no easy way to force the required code layout. |// .macro call_intern, func; bal extern func; .endmacro |.macro call_intern, func; jalr CFUNCADDR; .endmacro |.macro call_extern; jalr CFUNCADDR; .endmacro |.macro jmp_extern; jr CFUNCADDR; .endmacro | |.macro hotcheck, delta, target | srl TMP1, PC, 1 | andi TMP1, TMP1, 126 | addu TMP1, TMP1, DISPATCH | lhu TMP2, GG_DISP2HOT(TMP1) | addiu TMP2, TMP2, -delta | bltz TMP2, target |. sh TMP2, GG_DISP2HOT(TMP1) |.endmacro | |.macro hotloop | hotcheck HOTCOUNT_LOOP, ->vm_hotloop |.endmacro | |.macro hotcall | hotcheck HOTCOUNT_CALL, ->vm_hotcall |.endmacro | |// Set current VM state. Uses TMP0. |.macro li_vmstate, st; li TMP0, ~LJ_VMST_..st; .endmacro |.macro st_vmstate; sw TMP0, DISPATCH_GL(vmstate)(DISPATCH); .endmacro | |// Move table write barrier back. Overwrites mark and tmp. |.macro barrierback, tab, mark, tmp, target | lw tmp, DISPATCH_GL(gc.grayagain)(DISPATCH) | andi mark, mark, ~LJ_GC_BLACK & 255 // black2gray(tab) | sw tab, DISPATCH_GL(gc.grayagain)(DISPATCH) | sb mark, tab->marked | b target |. sw tmp, tab->gclist |.endmacro | |//----------------------------------------------------------------------- /* Generate subroutines used by opcodes and other parts of the VM. */ /* The .code_sub section should be last to help static branch prediction. */ static void build_subroutines(BuildCtx *ctx) { |.code_sub | |//----------------------------------------------------------------------- |//-- Return handling ---------------------------------------------------- |//----------------------------------------------------------------------- | |->vm_returnp: | // See vm_return. Also: TMP2 = previous base. | andi AT, PC, FRAME_P | beqz AT, ->cont_dispatch |. li TMP1, LJ_TTRUE | | // Return from pcall or xpcall fast func. | lw PC, FRAME_PC(TMP2) // Fetch PC of previous frame. | move BASE, TMP2 // Restore caller base. | // Prepending may overwrite the pcall frame, so do it at the end. | sw TMP1, FRAME_PC(RA) // Prepend true to results. | addiu RA, RA, -8 | |->vm_returnc: | addiu RD, RD, 8 // RD = (nresults+1)*8. | andi TMP0, PC, FRAME_TYPE | beqz RD, ->vm_unwind_c_eh |. li CRET1, LUA_YIELD | beqz TMP0, ->BC_RET_Z // Handle regular return to Lua. |. move MULTRES, RD | |->vm_return: | // BASE = base, RA = resultptr, RD/MULTRES = (nresults+1)*8, PC = return | // TMP0 = PC & FRAME_TYPE | li TMP2, -8 | xori AT, TMP0, FRAME_C | and TMP2, PC, TMP2 | bnez AT, ->vm_returnp |. subu TMP2, BASE, TMP2 // TMP2 = previous base. | | addiu TMP1, RD, -8 | sw TMP2, L->base | li_vmstate C | lw TMP2, SAVE_NRES | addiu BASE, BASE, -8 | st_vmstate | beqz TMP1, >2 |. sll TMP2, TMP2, 3 |1: | addiu TMP1, TMP1, -8 | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | addiu RA, RA, 8 | sw SFRETHI, HI(BASE) | sw SFRETLO, LO(BASE) | bnez TMP1, <1 |. addiu BASE, BASE, 8 | |2: | bne TMP2, RD, >6 |3: |. sw BASE, L->top // Store new top. | |->vm_leave_cp: | lw TMP0, SAVE_CFRAME // Restore previous C frame. | move CRET1, r0 // Ok return status for vm_pcall. | sw TMP0, L->cframe | |->vm_leave_unw: | restoreregs_ret | |6: | lw TMP1, L->maxstack | slt AT, TMP2, RD | bnez AT, >7 // Less results wanted? | // More results wanted. Check stack size and fill up results with nil. |. slt AT, BASE, TMP1 | beqz AT, >8 |. nop | sw TISNIL, HI(BASE) | addiu RD, RD, 8 | b <2 |. addiu BASE, BASE, 8 | |7: // Less results wanted. | subu TMP0, RD, TMP2 | subu TMP0, BASE, TMP0 // Either keep top or shrink it. | b <3 |. movn BASE, TMP0, TMP2 // LUA_MULTRET+1 case? | |8: // Corner case: need to grow stack for filling up results. | // This can happen if: | // - A C function grows the stack (a lot). | // - The GC shrinks the stack in between. | // - A return back from a lua_call() with (high) nresults adjustment. | load_got lj_state_growstack | move MULTRES, RD | srl CARG2, TMP2, 3 | call_intern lj_state_growstack // (lua_State *L, int n) |. move CARG1, L | lw TMP2, SAVE_NRES | lw BASE, L->top // Need the (realloced) L->top in BASE. | move RD, MULTRES | b <2 |. sll TMP2, TMP2, 3 | |->vm_unwind_c: // Unwind C stack, return from vm_pcall. | // (void *cframe, int errcode) | move sp, CARG1 | move CRET1, CARG2 |->vm_unwind_c_eh: // Landing pad for external unwinder. | lw L, SAVE_L | li TMP0, ~LJ_VMST_C | lw GL:TMP1, L->glref | b ->vm_leave_unw |. sw TMP0, GL:TMP1->vmstate | |->vm_unwind_ff: // Unwind C stack, return from ff pcall. | // (void *cframe) | li AT, -4 | and sp, CARG1, AT |->vm_unwind_ff_eh: // Landing pad for external unwinder. | lw L, SAVE_L | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). | li TISNUM, LJ_TISNUM // Setup type comparison constants. | li TISNIL, LJ_TNIL | lw BASE, L->base | lw DISPATCH, L->glref // Setup pointer to dispatch table. | .FPU mtc1 TMP3, TOBIT | li TMP1, LJ_TFALSE | li_vmstate INTERP | lw PC, FRAME_PC(BASE) // Fetch PC of previous frame. | .FPU cvt.d.s TOBIT, TOBIT | addiu RA, BASE, -8 // Results start at BASE-8. | addiu DISPATCH, DISPATCH, GG_G2DISP | sw TMP1, HI(RA) // Prepend false to error message. | st_vmstate | b ->vm_returnc |. li RD, 16 // 2 results: false + error message. | |->vm_unwind_stub: // Jump to exit stub from unwinder. | jr CARG1 |. move ra, CARG2 | |//----------------------------------------------------------------------- |//-- Grow stack for calls ----------------------------------------------- |//----------------------------------------------------------------------- | |->vm_growstack_c: // Grow stack for C function. | b >2 |. li CARG2, LUA_MINSTACK | |->vm_growstack_l: // Grow stack for Lua function. | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC | addu RC, BASE, RC | subu RA, RA, BASE | sw BASE, L->base | addiu PC, PC, 4 // Must point after first instruction. | sw RC, L->top | srl CARG2, RA, 3 |2: | // L->base = new base, L->top = top | load_got lj_state_growstack | sw PC, SAVE_PC | call_intern lj_state_growstack // (lua_State *L, int n) |. move CARG1, L | lw BASE, L->base | lw RC, L->top | lw LFUNC:RB, FRAME_FUNC(BASE) | subu RC, RC, BASE | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, FRAME_PC(BASE) = PC | ins_callt // Just retry the call. | |//----------------------------------------------------------------------- |//-- Entry points into the assembler VM --------------------------------- |//----------------------------------------------------------------------- | |->vm_resume: // Setup C frame and resume thread. | // (lua_State *L, TValue *base, int nres1 = 0, ptrdiff_t ef = 0) | saveregs | move L, CARG1 | lw DISPATCH, L->glref // Setup pointer to dispatch table. | move BASE, CARG2 | lbu TMP1, L->status | sw L, SAVE_L | li PC, FRAME_CP | addiu TMP0, sp, CFRAME_RESUME | addiu DISPATCH, DISPATCH, GG_G2DISP | sw r0, SAVE_NRES | sw r0, SAVE_ERRF | sw CARG1, SAVE_PC // Any value outside of bytecode is ok. | sw r0, SAVE_CFRAME | beqz TMP1, >3 |. sw TMP0, L->cframe | | // Resume after yield (like a return). | sw L, DISPATCH_GL(cur_L)(DISPATCH) | move RA, BASE | lw BASE, L->base | li TISNUM, LJ_TISNUM // Setup type comparison constants. | lw TMP1, L->top | lw PC, FRAME_PC(BASE) | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). | subu RD, TMP1, BASE | .FPU mtc1 TMP3, TOBIT | sb r0, L->status | .FPU cvt.d.s TOBIT, TOBIT | li_vmstate INTERP | addiu RD, RD, 8 | st_vmstate | move MULTRES, RD | andi TMP0, PC, FRAME_TYPE | beqz TMP0, ->BC_RET_Z |. li TISNIL, LJ_TNIL | b ->vm_return |. nop | |->vm_pcall: // Setup protected C frame and enter VM. | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) | saveregs | sw CARG4, SAVE_ERRF | b >1 |. li PC, FRAME_CP | |->vm_call: // Setup C frame and enter VM. | // (lua_State *L, TValue *base, int nres1) | saveregs | li PC, FRAME_C | |1: // Entry point for vm_pcall above (PC = ftype). | lw TMP1, L:CARG1->cframe | move L, CARG1 | sw CARG3, SAVE_NRES | lw DISPATCH, L->glref // Setup pointer to dispatch table. | sw CARG1, SAVE_L | move BASE, CARG2 | addiu DISPATCH, DISPATCH, GG_G2DISP | sw CARG1, SAVE_PC // Any value outside of bytecode is ok. | sw TMP1, SAVE_CFRAME | sw sp, L->cframe // Add our C frame to cframe chain. | |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). | sw L, DISPATCH_GL(cur_L)(DISPATCH) | lw TMP2, L->base // TMP2 = old base (used in vmeta_call). | li TISNUM, LJ_TISNUM // Setup type comparison constants. | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). | lw TMP1, L->top | .FPU mtc1 TMP3, TOBIT | addu PC, PC, BASE | subu NARGS8:RC, TMP1, BASE | subu PC, PC, TMP2 // PC = frame delta + frame type | .FPU cvt.d.s TOBIT, TOBIT | li_vmstate INTERP | li TISNIL, LJ_TNIL | st_vmstate | |->vm_call_dispatch: | // TMP2 = old base, BASE = new base, RC = nargs*8, PC = caller PC | lw TMP0, FRAME_PC(BASE) | li AT, LJ_TFUNC | bne TMP0, AT, ->vmeta_call |. lw LFUNC:RB, FRAME_FUNC(BASE) | |->vm_call_dispatch_f: | ins_call | // BASE = new base, RB = func, RC = nargs*8, PC = caller PC | |->vm_cpcall: // Setup protected C frame, call C. | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) | saveregs | move L, CARG1 | lw TMP0, L:CARG1->stack | sw CARG1, SAVE_L | lw TMP1, L->top | lw DISPATCH, L->glref // Setup pointer to dispatch table. | sw CARG1, SAVE_PC // Any value outside of bytecode is ok. | subu TMP0, TMP0, TMP1 // Compute -savestack(L, L->top). | lw TMP1, L->cframe | addiu DISPATCH, DISPATCH, GG_G2DISP | sw TMP0, SAVE_NRES // Neg. delta means cframe w/o frame. | sw r0, SAVE_ERRF // No error function. | sw TMP1, SAVE_CFRAME | sw sp, L->cframe // Add our C frame to cframe chain. | sw L, DISPATCH_GL(cur_L)(DISPATCH) | jalr CARG4 // (lua_State *L, lua_CFunction func, void *ud) |. move CFUNCADDR, CARG4 | move BASE, CRET1 | bnez CRET1, <3 // Else continue with the call. |. li PC, FRAME_CP | b ->vm_leave_cp // No base? Just remove C frame. |. nop | |//----------------------------------------------------------------------- |//-- Metamethod handling ------------------------------------------------ |//----------------------------------------------------------------------- | |// The lj_meta_* functions (except for lj_meta_cat) don't reallocate the |// stack, so BASE doesn't need to be reloaded across these calls. | |//-- Continuation dispatch ---------------------------------------------- | |->cont_dispatch: | // BASE = meta base, RA = resultptr, RD = (nresults+1)*8 | lw TMP0, -16+LO(BASE) // Continuation. | move RB, BASE | move BASE, TMP2 // Restore caller BASE. | lw LFUNC:TMP1, FRAME_FUNC(TMP2) |.if FFI | sltiu AT, TMP0, 2 |.endif | lw PC, -16+HI(RB) // Restore PC from [cont|PC]. | addu TMP2, RA, RD |.if FFI | bnez AT, >1 |.endif |. sw TISNIL, -8+HI(TMP2) // Ensure one valid arg. | lw TMP1, LFUNC:TMP1->pc | // BASE = base, RA = resultptr, RB = meta base | jr TMP0 // Jump to continuation. |. lw KBASE, PC2PROTO(k)(TMP1) | |.if FFI |1: | bnez TMP0, ->cont_ffi_callback // cont = 1: return from FFI callback. | // cont = 0: tailcall from C function. |. addiu TMP1, RB, -16 | b ->vm_call_tail |. subu RC, TMP1, BASE |.endif | |->cont_cat: // RA = resultptr, RB = meta base | lw INS, -4(PC) | addiu CARG2, RB, -16 | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | decode_RB8a MULTRES, INS | decode_RA8a RA, INS | decode_RB8b MULTRES | decode_RA8b RA | addu TMP1, BASE, MULTRES | sw BASE, L->base | subu CARG3, CARG2, TMP1 | sw SFRETHI, HI(CARG2) | bne TMP1, CARG2, ->BC_CAT_Z |. sw SFRETLO, LO(CARG2) | addu RA, BASE, RA | sw SFRETHI, HI(RA) | b ->cont_nop |. sw SFRETLO, LO(RA) | |//-- Table indexing metamethods ----------------------------------------- | |->vmeta_tgets1: | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) | li TMP0, LJ_TSTR | sw STR:RC, LO(CARG3) | b >1 |. sw TMP0, HI(CARG3) | |->vmeta_tgets: | addiu CARG2, DISPATCH, DISPATCH_GL(tmptv) | li TMP0, LJ_TTAB | sw TAB:RB, LO(CARG2) | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv2) | sw TMP0, HI(CARG2) | li TMP1, LJ_TSTR | sw STR:RC, LO(CARG3) | b >1 |. sw TMP1, HI(CARG3) | |->vmeta_tgetb: // TMP0 = index | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) | sw TMP0, LO(CARG3) | sw TISNUM, HI(CARG3) | |->vmeta_tgetv: |1: | load_got lj_meta_tget | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_meta_tget // (lua_State *L, TValue *o, TValue *k) |. move CARG1, L | // Returns TValue * (finished) or NULL (metamethod). | beqz CRET1, >3 |. addiu TMP1, BASE, -FRAME_CONT | lw SFARG1HI, HI(CRET1) | lw SFARG2HI, LO(CRET1) | ins_next1 | sw SFARG1HI, HI(RA) | sw SFARG2HI, LO(RA) | ins_next2 | |3: // Call __index metamethod. | // BASE = base, L->top = new base, stack = cont/func/t/k | lw BASE, L->top | sw PC, -16+HI(BASE) // [cont|PC] | subu PC, BASE, TMP1 | lw LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. | b ->vm_call_dispatch_f |. li NARGS8:RC, 16 // 2 args for func(t, k). | |->vmeta_tgetr: | load_got lj_tab_getinth | call_intern lj_tab_getinth // (GCtab *t, int32_t key) |. nop | // Returns cTValue * or NULL. | beqz CRET1, ->BC_TGETR_Z |. move SFARG2HI, TISNIL | lw SFARG2HI, HI(CRET1) | b ->BC_TGETR_Z |. lw SFARG2LO, LO(CRET1) | |//----------------------------------------------------------------------- | |->vmeta_tsets1: | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) | li TMP0, LJ_TSTR | sw STR:RC, LO(CARG3) | b >1 |. sw TMP0, HI(CARG3) | |->vmeta_tsets: | addiu CARG2, DISPATCH, DISPATCH_GL(tmptv) | li TMP0, LJ_TTAB | sw TAB:RB, LO(CARG2) | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv2) | sw TMP0, HI(CARG2) | li TMP1, LJ_TSTR | sw STR:RC, LO(CARG3) | b >1 |. sw TMP1, HI(CARG3) | |->vmeta_tsetb: // TMP0 = index | addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) | sw TMP0, LO(CARG3) | sw TISNUM, HI(CARG3) | |->vmeta_tsetv: |1: | load_got lj_meta_tset | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_meta_tset // (lua_State *L, TValue *o, TValue *k) |. move CARG1, L | // Returns TValue * (finished) or NULL (metamethod). | lw SFARG1HI, HI(RA) | beqz CRET1, >3 |. lw SFARG1LO, LO(RA) | // NOBARRIER: lj_meta_tset ensures the table is not black. | ins_next1 | sw SFARG1HI, HI(CRET1) | sw SFARG1LO, LO(CRET1) | ins_next2 | |3: // Call __newindex metamethod. | // BASE = base, L->top = new base, stack = cont/func/t/k/(v) | addiu TMP1, BASE, -FRAME_CONT | lw BASE, L->top | sw PC, -16+HI(BASE) // [cont|PC] | subu PC, BASE, TMP1 | lw LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. | sw SFARG1HI, 16+HI(BASE) // Copy value to third argument. | sw SFARG1LO, 16+LO(BASE) | b ->vm_call_dispatch_f |. li NARGS8:RC, 24 // 3 args for func(t, k, v) | |->vmeta_tsetr: | load_got lj_tab_setinth | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_tab_setinth // (lua_State *L, GCtab *t, int32_t key) |. move CARG1, L | // Returns TValue *. | b ->BC_TSETR_Z |. nop | |//-- Comparison metamethods --------------------------------------------- | |->vmeta_comp: | // RA/RD point to o1/o2. | move CARG2, RA | move CARG3, RD | load_got lj_meta_comp | addiu PC, PC, -4 | sw BASE, L->base | sw PC, SAVE_PC | decode_OP1 CARG4, INS | call_intern lj_meta_comp // (lua_State *L, TValue *o1, *o2, int op) |. move CARG1, L | // Returns 0/1 or TValue * (metamethod). |3: | sltiu AT, CRET1, 2 | beqz AT, ->vmeta_binop | negu TMP2, CRET1 |4: | lhu RD, OFS_RD(PC) | addiu PC, PC, 4 | lui TMP1, (-(BCBIAS_J*4 >> 16) & 65535) | sll RD, RD, 2 | addu RD, RD, TMP1 | and RD, RD, TMP2 | addu PC, PC, RD |->cont_nop: | ins_next | |->cont_ra: // RA = resultptr | lbu TMP1, -4+OFS_RA(PC) | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | sll TMP1, TMP1, 3 | addu TMP1, BASE, TMP1 | sw SFRETHI, HI(TMP1) | b ->cont_nop |. sw SFRETLO, LO(TMP1) | |->cont_condt: // RA = resultptr | lw TMP0, HI(RA) | sltiu AT, TMP0, LJ_TISTRUECOND | b <4 |. negu TMP2, AT // Branch if result is true. | |->cont_condf: // RA = resultptr | lw TMP0, HI(RA) | sltiu AT, TMP0, LJ_TISTRUECOND | b <4 |. addiu TMP2, AT, -1 // Branch if result is false. | |->vmeta_equal: | // SFARG1LO/SFARG2LO point to o1/o2. TMP0 is set to 0/1. | load_got lj_meta_equal | move CARG2, SFARG1LO | move CARG3, SFARG2LO | move CARG4, TMP0 | addiu PC, PC, -4 | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_meta_equal // (lua_State *L, GCobj *o1, *o2, int ne) |. move CARG1, L | // Returns 0/1 or TValue * (metamethod). | b <3 |. nop | |->vmeta_equal_cd: |.if FFI | load_got lj_meta_equal_cd | move CARG2, INS | addiu PC, PC, -4 | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_meta_equal_cd // (lua_State *L, BCIns op) |. move CARG1, L | // Returns 0/1 or TValue * (metamethod). | b <3 |. nop |.endif | |->vmeta_istype: | load_got lj_meta_istype | addiu PC, PC, -4 | sw BASE, L->base | srl CARG2, RA, 3 | srl CARG3, RD, 3 | sw PC, SAVE_PC | call_intern lj_meta_istype // (lua_State *L, BCReg ra, BCReg tp) |. move CARG1, L | b ->cont_nop |. nop | |//-- Arithmetic metamethods --------------------------------------------- | |->vmeta_unm: | move RC, RB | |->vmeta_arith: | load_got lj_meta_arith | decode_OP1 TMP0, INS | sw BASE, L->base | move CARG2, RA | sw PC, SAVE_PC | move CARG3, RB | move CARG4, RC | sw TMP0, ARG5 | call_intern lj_meta_arith // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) |. move CARG1, L | // Returns NULL (finished) or TValue * (metamethod). | beqz CRET1, ->cont_nop |. nop | | // Call metamethod for binary op. |->vmeta_binop: | // BASE = old base, CRET1 = new base, stack = cont/func/o1/o2 | subu TMP1, CRET1, BASE | sw PC, -16+HI(CRET1) // [cont|PC] | move TMP2, BASE | addiu PC, TMP1, FRAME_CONT | move BASE, CRET1 | b ->vm_call_dispatch |. li NARGS8:RC, 16 // 2 args for func(o1, o2). | |->vmeta_len: | // CARG2 already set by BC_LEN. #if LJ_52 | move MULTRES, CARG1 #endif | load_got lj_meta_len | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_meta_len // (lua_State *L, TValue *o) |. move CARG1, L | // Returns NULL (retry) or TValue * (metamethod base). #if LJ_52 | bnez CRET1, ->vmeta_binop // Binop call for compatibility. |. nop | b ->BC_LEN_Z |. move CARG1, MULTRES #else | b ->vmeta_binop // Binop call for compatibility. |. nop #endif | |//-- Call metamethod ---------------------------------------------------- | |->vmeta_call: // Resolve and call __call metamethod. | // TMP2 = old base, BASE = new base, RC = nargs*8 | load_got lj_meta_call | sw TMP2, L->base // This is the callers base! | addiu CARG2, BASE, -8 | sw PC, SAVE_PC | addu CARG3, BASE, RC | move MULTRES, NARGS8:RC | call_intern lj_meta_call // (lua_State *L, TValue *func, TValue *top) |. move CARG1, L | lw LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. | addiu NARGS8:RC, MULTRES, 8 // Got one more argument now. | ins_call | |->vmeta_callt: // Resolve __call for BC_CALLT. | // BASE = old base, RA = new base, RC = nargs*8 | load_got lj_meta_call | sw BASE, L->base | addiu CARG2, RA, -8 | sw PC, SAVE_PC | addu CARG3, RA, RC | move MULTRES, NARGS8:RC | call_intern lj_meta_call // (lua_State *L, TValue *func, TValue *top) |. move CARG1, L | lw TMP1, FRAME_PC(BASE) | lw LFUNC:RB, FRAME_FUNC(RA) // Guaranteed to be a function here. | b ->BC_CALLT_Z |. addiu NARGS8:RC, MULTRES, 8 // Got one more argument now. | |//-- Argument coercion for 'for' statement ------------------------------ | |->vmeta_for: | load_got lj_meta_for | sw BASE, L->base | move CARG2, RA | sw PC, SAVE_PC | move MULTRES, INS | call_intern lj_meta_for // (lua_State *L, TValue *base) |. move CARG1, L |.if JIT | decode_OP1 TMP0, MULTRES | li AT, BC_JFORI |.endif | decode_RA8a RA, MULTRES | decode_RD8a RD, MULTRES | decode_RA8b RA |.if JIT | beq TMP0, AT, =>BC_JFORI |. decode_RD8b RD | b =>BC_FORI |. nop |.else | b =>BC_FORI |. decode_RD8b RD |.endif | |//----------------------------------------------------------------------- |//-- Fast functions ----------------------------------------------------- |//----------------------------------------------------------------------- | |.macro .ffunc, name |->ff_ .. name: |.endmacro | |.macro .ffunc_1, name |->ff_ .. name: | lw SFARG1HI, HI(BASE) | beqz NARGS8:RC, ->fff_fallback |. lw SFARG1LO, LO(BASE) |.endmacro | |.macro .ffunc_2, name |->ff_ .. name: | sltiu AT, NARGS8:RC, 16 | lw SFARG1HI, HI(BASE) | bnez AT, ->fff_fallback |. lw SFARG2HI, 8+HI(BASE) | lw SFARG1LO, LO(BASE) | lw SFARG2LO, 8+LO(BASE) |.endmacro | |.macro .ffunc_n, name // Caveat: has delay slot! |->ff_ .. name: | lw SFARG1HI, HI(BASE) |.if FPU | ldc1 FARG1, 0(BASE) |.else | lw SFARG1LO, LO(BASE) |.endif | beqz NARGS8:RC, ->fff_fallback |. sltiu AT, SFARG1HI, LJ_TISNUM | beqz AT, ->fff_fallback |.endmacro | |.macro .ffunc_nn, name // Caveat: has delay slot! |->ff_ .. name: | sltiu AT, NARGS8:RC, 16 | lw SFARG1HI, HI(BASE) | bnez AT, ->fff_fallback |. lw SFARG2HI, 8+HI(BASE) | sltiu TMP0, SFARG1HI, LJ_TISNUM |.if FPU | ldc1 FARG1, 0(BASE) |.else | lw SFARG1LO, LO(BASE) |.endif | sltiu TMP1, SFARG2HI, LJ_TISNUM |.if FPU | ldc1 FARG2, 8(BASE) |.else | lw SFARG2LO, 8+LO(BASE) |.endif | and TMP0, TMP0, TMP1 | beqz TMP0, ->fff_fallback |.endmacro | |// Inlined GC threshold check. Caveat: uses TMP0 and TMP1 and has delay slot! |.macro ffgccheck | lw TMP0, DISPATCH_GL(gc.total)(DISPATCH) | lw TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) | subu AT, TMP0, TMP1 | bgezal AT, ->fff_gcstep |.endmacro | |//-- Base library: checks ----------------------------------------------- | |.ffunc_1 assert | sltiu AT, SFARG1HI, LJ_TISTRUECOND | beqz AT, ->fff_fallback |. addiu RA, BASE, -8 | lw PC, FRAME_PC(BASE) | addiu RD, NARGS8:RC, 8 // Compute (nresults+1)*8. | addu TMP2, RA, NARGS8:RC | sw SFARG1HI, HI(RA) | addiu TMP1, BASE, 8 | beq BASE, TMP2, ->fff_res // Done if exactly 1 argument. |. sw SFARG1LO, LO(RA) |1: | lw SFRETHI, HI(TMP1) | lw SFRETLO, LO(TMP1) | sw SFRETHI, -8+HI(TMP1) | sw SFRETLO, -8+LO(TMP1) | bne TMP1, TMP2, <1 |. addiu TMP1, TMP1, 8 | b ->fff_res |. nop | |.ffunc type | lw SFARG1HI, HI(BASE) | beqz NARGS8:RC, ->fff_fallback |. sltiu TMP0, SFARG1HI, LJ_TISNUM | movn SFARG1HI, TISNUM, TMP0 | not TMP1, SFARG1HI | sll TMP1, TMP1, 3 | addu TMP1, CFUNC:RB, TMP1 | lw SFARG1HI, CFUNC:TMP1->upvalue[0].u32.hi | b ->fff_restv |. lw SFARG1LO, CFUNC:TMP1->upvalue[0].u32.lo | |//-- Base library: getters and setters --------------------------------- | |.ffunc_1 getmetatable | li AT, LJ_TTAB | bne SFARG1HI, AT, >6 |. li AT, LJ_TUDATA |1: // Field metatable must be at same offset for GCtab and GCudata! | lw TAB:SFARG1LO, TAB:SFARG1LO->metatable |2: | lw STR:RC, DISPATCH_GL(gcroot[GCROOT_MMNAME+MM_metatable])(DISPATCH) | beqz TAB:SFARG1LO, ->fff_restv |. li SFARG1HI, LJ_TNIL | lw TMP0, TAB:SFARG1LO->hmask | li SFARG1HI, LJ_TTAB // Use metatable as default result. | lw TMP1, STR:RC->sid | lw NODE:TMP2, TAB:SFARG1LO->node | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask | sll TMP0, TMP1, 5 | sll TMP1, TMP1, 3 | subu TMP1, TMP0, TMP1 | addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) | li AT, LJ_TSTR |3: // Rearranged logic, because we expect _not_ to find the key. | lw CARG4, offsetof(Node, key)+HI(NODE:TMP2) | lw TMP0, offsetof(Node, key)+LO(NODE:TMP2) | lw NODE:TMP3, NODE:TMP2->next | bne CARG4, AT, >4 |. lw CARG3, offsetof(Node, val)+HI(NODE:TMP2) | beq TMP0, STR:RC, >5 |. lw TMP1, offsetof(Node, val)+LO(NODE:TMP2) |4: | beqz NODE:TMP3, ->fff_restv // Not found, keep default result. |. move NODE:TMP2, NODE:TMP3 | b <3 |. nop |5: | beq CARG3, TISNIL, ->fff_restv // Ditto for nil value. |. nop | move SFARG1HI, CARG3 // Return value of mt.__metatable. | b ->fff_restv |. move SFARG1LO, TMP1 | |6: | beq SFARG1HI, AT, <1 |. sltu AT, TISNUM, SFARG1HI | movz SFARG1HI, TISNUM, AT | not TMP1, SFARG1HI | sll TMP1, TMP1, 2 | addu TMP1, DISPATCH, TMP1 | b <2 |. lw TAB:SFARG1LO, DISPATCH_GL(gcroot[GCROOT_BASEMT])(TMP1) | |.ffunc_2 setmetatable | // Fast path: no mt for table yet and not clearing the mt. | li AT, LJ_TTAB | bne SFARG1HI, AT, ->fff_fallback |. addiu SFARG2HI, SFARG2HI, -LJ_TTAB | lw TAB:TMP1, TAB:SFARG1LO->metatable | lbu TMP3, TAB:SFARG1LO->marked | or AT, SFARG2HI, TAB:TMP1 | bnez AT, ->fff_fallback |. andi AT, TMP3, LJ_GC_BLACK // isblack(table) | beqz AT, ->fff_restv |. sw TAB:SFARG2LO, TAB:SFARG1LO->metatable | barrierback TAB:SFARG1LO, TMP3, TMP0, ->fff_restv | |.ffunc rawget | lw CARG4, HI(BASE) | sltiu AT, NARGS8:RC, 16 | lw TAB:CARG2, LO(BASE) | load_got lj_tab_get | addiu CARG4, CARG4, -LJ_TTAB | or AT, AT, CARG4 | bnez AT, ->fff_fallback | addiu CARG3, BASE, 8 | call_intern lj_tab_get // (lua_State *L, GCtab *t, cTValue *key) |. move CARG1, L | // Returns cTValue *. | lw SFARG1HI, HI(CRET1) | b ->fff_restv |. lw SFARG1LO, LO(CRET1) | |//-- Base library: conversions ------------------------------------------ | |.ffunc tonumber | // Only handles the number case inline (without a base argument). | lw CARG1, HI(BASE) | xori AT, NARGS8:RC, 8 // Exactly one number argument. | sltu TMP0, TISNUM, CARG1 | or AT, AT, TMP0 | bnez AT, ->fff_fallback |. lw SFARG1HI, HI(BASE) | b ->fff_restv |. lw SFARG1LO, LO(BASE) | |.ffunc_1 tostring | // Only handles the string or number case inline. | li AT, LJ_TSTR | // A __tostring method in the string base metatable is ignored. | beq SFARG1HI, AT, ->fff_restv // String key? | // Handle numbers inline, unless a number base metatable is present. |. lw TMP1, DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])(DISPATCH) | sltu TMP0, TISNUM, SFARG1HI | or TMP0, TMP0, TMP1 | bnez TMP0, ->fff_fallback |. sw BASE, L->base // Add frame since C call can throw. | ffgccheck |. sw PC, SAVE_PC // Redundant (but a defined value). | load_got lj_strfmt_number | move CARG1, L | call_intern lj_strfmt_number // (lua_State *L, cTValue *o) |. move CARG2, BASE | // Returns GCstr *. | li SFARG1HI, LJ_TSTR | b ->fff_restv |. move SFARG1LO, CRET1 | |//-- Base library: iterators ------------------------------------------- | |.ffunc next | lw CARG2, HI(BASE) | lw TAB:CARG1, LO(BASE) | beqz NARGS8:RC, ->fff_fallback |. addu TMP2, BASE, NARGS8:RC | li AT, LJ_TTAB | sw TISNIL, HI(TMP2) // Set missing 2nd arg to nil. | bne CARG2, AT, ->fff_fallback |. lw PC, FRAME_PC(BASE) | load_got lj_tab_next | addiu CARG2, BASE, 8 | call_intern lj_tab_next // (GCtab *t, cTValue *key, TValue *o) |. addiu CARG3, BASE, -8 | // Returns 1=found, 0=end, -1=error. | addiu RA, BASE, -8 | bgtz CRET1, ->fff_res // Found key/value. |. li RD, (2+1)*8 | beqz CRET1, ->fff_restv // End of traversal: return nil. |. li SFARG1HI, LJ_TNIL | lw CFUNC:RB, FRAME_FUNC(BASE) | b ->fff_fallback // Invalid key. |. li RC, 2*8 | |.ffunc_1 pairs | li AT, LJ_TTAB | bne SFARG1HI, AT, ->fff_fallback |. lw PC, FRAME_PC(BASE) #if LJ_52 | lw TAB:TMP2, TAB:SFARG1LO->metatable | lw TMP0, CFUNC:RB->upvalue[0].u32.hi | lw TMP1, CFUNC:RB->upvalue[0].u32.lo | bnez TAB:TMP2, ->fff_fallback #else | lw TMP0, CFUNC:RB->upvalue[0].u32.hi | lw TMP1, CFUNC:RB->upvalue[0].u32.lo #endif |. addiu RA, BASE, -8 | sw TISNIL, 8+HI(BASE) | sw TMP0, HI(RA) | sw TMP1, LO(RA) | b ->fff_res |. li RD, (3+1)*8 | |.ffunc ipairs_aux | sltiu AT, NARGS8:RC, 16 | lw CARG3, HI(BASE) | lw TAB:CARG1, LO(BASE) | lw CARG4, 8+HI(BASE) | bnez AT, ->fff_fallback |. addiu CARG3, CARG3, -LJ_TTAB | xor CARG4, CARG4, TISNUM | and AT, CARG3, CARG4 | bnez AT, ->fff_fallback |. lw PC, FRAME_PC(BASE) | lw TMP2, 8+LO(BASE) | lw TMP0, TAB:CARG1->asize | lw TMP1, TAB:CARG1->array | addiu TMP2, TMP2, 1 | sw TISNUM, -8+HI(BASE) | sltu AT, TMP2, TMP0 | sw TMP2, -8+LO(BASE) | beqz AT, >2 // Not in array part? |. addiu RA, BASE, -8 | sll TMP3, TMP2, 3 | addu TMP3, TMP1, TMP3 | lw TMP1, HI(TMP3) | lw TMP2, LO(TMP3) |1: | beq TMP1, TISNIL, ->fff_res // End of iteration, return 0 results. |. li RD, (0+1)*8 | sw TMP1, 8+HI(RA) | sw TMP2, 8+LO(RA) | b ->fff_res |. li RD, (2+1)*8 | |2: // Check for empty hash part first. Otherwise call C function. | lw TMP0, TAB:CARG1->hmask | load_got lj_tab_getinth | beqz TMP0, ->fff_res |. li RD, (0+1)*8 | call_intern lj_tab_getinth // (GCtab *t, int32_t key) |. move CARG2, TMP2 | // Returns cTValue * or NULL. | beqz CRET1, ->fff_res |. li RD, (0+1)*8 | lw TMP1, HI(CRET1) | b <1 |. lw TMP2, LO(CRET1) | |.ffunc_1 ipairs | li AT, LJ_TTAB | bne SFARG1HI, AT, ->fff_fallback |. lw PC, FRAME_PC(BASE) #if LJ_52 | lw TAB:TMP2, TAB:SFARG1LO->metatable | lw TMP0, CFUNC:RB->upvalue[0].u32.hi | lw TMP1, CFUNC:RB->upvalue[0].u32.lo | bnez TAB:TMP2, ->fff_fallback #else | lw TMP0, CFUNC:RB->upvalue[0].u32.hi | lw TMP1, CFUNC:RB->upvalue[0].u32.lo #endif |. addiu RA, BASE, -8 | sw TISNUM, 8+HI(BASE) | sw r0, 8+LO(BASE) | sw TMP0, HI(RA) | sw TMP1, LO(RA) | b ->fff_res |. li RD, (3+1)*8 | |//-- Base library: catch errors ---------------------------------------- | |.ffunc pcall | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) | beqz NARGS8:RC, ->fff_fallback | move TMP2, BASE | addiu BASE, BASE, 8 | // Remember active hook before pcall. | srl TMP3, TMP3, HOOK_ACTIVE_SHIFT | andi TMP3, TMP3, 1 | addiu PC, TMP3, 8+FRAME_PCALL | b ->vm_call_dispatch |. addiu NARGS8:RC, NARGS8:RC, -8 | |.ffunc xpcall | sltiu AT, NARGS8:RC, 16 | lw CARG4, 8+HI(BASE) | bnez AT, ->fff_fallback |. lw CARG3, 8+LO(BASE) | lw CARG1, LO(BASE) | lw CARG2, HI(BASE) | lbu TMP1, DISPATCH_GL(hookmask)(DISPATCH) | li AT, LJ_TFUNC | move TMP2, BASE | bne CARG4, AT, ->fff_fallback // Traceback must be a function. | addiu BASE, BASE, 16 | // Remember active hook before pcall. | srl TMP3, TMP3, HOOK_ACTIVE_SHIFT | sw CARG3, LO(TMP2) // Swap function and traceback. | sw CARG4, HI(TMP2) | andi TMP3, TMP3, 1 | sw CARG1, 8+LO(TMP2) | sw CARG2, 8+HI(TMP2) | addiu PC, TMP3, 16+FRAME_PCALL | b ->vm_call_dispatch |. addiu NARGS8:RC, NARGS8:RC, -16 | |//-- Coroutine library -------------------------------------------------- | |.macro coroutine_resume_wrap, resume |.if resume |.ffunc coroutine_resume | lw CARG3, HI(BASE) | beqz NARGS8:RC, ->fff_fallback |. lw CARG1, LO(BASE) | li AT, LJ_TTHREAD | bne CARG3, AT, ->fff_fallback |.else |.ffunc coroutine_wrap_aux | lw L:CARG1, CFUNC:RB->upvalue[0].gcr |.endif | lbu TMP0, L:CARG1->status | lw TMP1, L:CARG1->cframe | lw CARG2, L:CARG1->top | lw TMP2, L:CARG1->base | addiu TMP3, TMP0, -LUA_YIELD | bgtz TMP3, ->fff_fallback // st > LUA_YIELD? |. xor TMP2, TMP2, CARG2 | bnez TMP1, ->fff_fallback // cframe != 0? |. or AT, TMP2, TMP0 | lw TMP0, L:CARG1->maxstack | beqz AT, ->fff_fallback // base == top && st == 0? |. lw PC, FRAME_PC(BASE) | addu TMP2, CARG2, NARGS8:RC | sltu AT, TMP0, TMP2 | bnez AT, ->fff_fallback // Stack overflow? |. sw PC, SAVE_PC | sw BASE, L->base |1: |.if resume | addiu BASE, BASE, 8 // Keep resumed thread in stack for GC. | addiu NARGS8:RC, NARGS8:RC, -8 | addiu TMP2, TMP2, -8 |.endif | sw TMP2, L:CARG1->top | addu TMP1, BASE, NARGS8:RC | move CARG3, CARG2 | sw BASE, L->top |2: // Move args to coroutine. | lw SFRETHI, HI(BASE) | lw SFRETLO, LO(BASE) | sltu AT, BASE, TMP1 | beqz AT, >3 |. addiu BASE, BASE, 8 | sw SFRETHI, HI(CARG3) | sw SFRETLO, LO(CARG3) | b <2 |. addiu CARG3, CARG3, 8 |3: | bal ->vm_resume // (lua_State *L, TValue *base, 0, 0) |. move L:RA, L:CARG1 | // Returns thread status. |4: | lw TMP2, L:RA->base | sltiu AT, CRET1, LUA_YIELD+1 | lw TMP3, L:RA->top | li_vmstate INTERP | lw BASE, L->base | sw L, DISPATCH_GL(cur_L)(DISPATCH) | st_vmstate | beqz AT, >8 |. subu RD, TMP3, TMP2 | lw TMP0, L->maxstack | beqz RD, >6 // No results? |. addu TMP1, BASE, RD | sltu AT, TMP0, TMP1 | bnez AT, >9 // Need to grow stack? |. addu TMP3, TMP2, RD | sw TMP2, L:RA->top // Clear coroutine stack. | move TMP1, BASE |5: // Move results from coroutine. | lw SFRETHI, HI(TMP2) | lw SFRETLO, LO(TMP2) | addiu TMP2, TMP2, 8 | sltu AT, TMP2, TMP3 | sw SFRETHI, HI(TMP1) | sw SFRETLO, LO(TMP1) | bnez AT, <5 |. addiu TMP1, TMP1, 8 |6: | andi TMP0, PC, FRAME_TYPE |.if resume | li TMP1, LJ_TTRUE | addiu RA, BASE, -8 | sw TMP1, -8+HI(BASE) // Prepend true to results. | addiu RD, RD, 16 |.else | move RA, BASE | addiu RD, RD, 8 |.endif |7: | sw PC, SAVE_PC | beqz TMP0, ->BC_RET_Z |. move MULTRES, RD | b ->vm_return |. nop | |8: // Coroutine returned with error (at co->top-1). |.if resume | addiu TMP3, TMP3, -8 | li TMP1, LJ_TFALSE | lw SFRETHI, HI(TMP3) | lw SFRETLO, LO(TMP3) | sw TMP3, L:RA->top // Remove error from coroutine stack. | li RD, (2+1)*8 | sw TMP1, -8+HI(BASE) // Prepend false to results. | addiu RA, BASE, -8 | sw SFRETHI, HI(BASE) // Copy error message. | sw SFRETLO, LO(BASE) | b <7 |. andi TMP0, PC, FRAME_TYPE |.else | load_got lj_ffh_coroutine_wrap_err | move CARG2, L:RA | call_intern lj_ffh_coroutine_wrap_err // (lua_State *L, lua_State *co) |. move CARG1, L |.endif | |9: // Handle stack expansion on return from yield. | load_got lj_state_growstack | srl CARG2, RD, 3 | call_intern lj_state_growstack // (lua_State *L, int n) |. move CARG1, L | b <4 |. li CRET1, 0 |.endmacro | | coroutine_resume_wrap 1 // coroutine.resume | coroutine_resume_wrap 0 // coroutine.wrap | |.ffunc coroutine_yield | lw TMP0, L->cframe | addu TMP1, BASE, NARGS8:RC | sw BASE, L->base | andi TMP0, TMP0, CFRAME_RESUME | sw TMP1, L->top | beqz TMP0, ->fff_fallback |. li CRET1, LUA_YIELD | sw r0, L->cframe | b ->vm_leave_unw |. sb CRET1, L->status | |//-- Math library ------------------------------------------------------- | |.ffunc_1 math_abs | bne SFARG1HI, TISNUM, >1 |. sra TMP0, SFARG1LO, 31 | xor TMP1, SFARG1LO, TMP0 | subu SFARG1LO, TMP1, TMP0 | bgez SFARG1LO, ->fff_restv |. nop | lui SFARG1HI, 0x41e0 // 2^31 as a double. | b ->fff_restv |. li SFARG1LO, 0 |1: | sltiu AT, SFARG1HI, LJ_TISNUM | beqz AT, ->fff_fallback |. sll SFARG1HI, SFARG1HI, 1 | srl SFARG1HI, SFARG1HI, 1 |// fallthrough | |->fff_restv: | // SFARG1LO/SFARG1HI = TValue result. | lw PC, FRAME_PC(BASE) | sw SFARG1HI, -8+HI(BASE) | addiu RA, BASE, -8 | sw SFARG1LO, -8+LO(BASE) |->fff_res1: | // RA = results, PC = return. | li RD, (1+1)*8 |->fff_res: | // RA = results, RD = (nresults+1)*8, PC = return. | andi TMP0, PC, FRAME_TYPE | bnez TMP0, ->vm_return |. move MULTRES, RD | lw INS, -4(PC) | decode_RB8a RB, INS | decode_RB8b RB |5: | sltu AT, RD, RB | bnez AT, >6 // More results expected? |. decode_RA8a TMP0, INS | decode_RA8b TMP0 | ins_next1 | // Adjust BASE. KBASE is assumed to be set for the calling frame. | subu BASE, RA, TMP0 | ins_next2 | |6: // Fill up results with nil. | addu TMP1, RA, RD | addiu RD, RD, 8 | b <5 |. sw TISNIL, -8+HI(TMP1) | |.macro math_extern, func | .ffunc math_ .. func | lw SFARG1HI, HI(BASE) | beqz NARGS8:RC, ->fff_fallback |. load_got func | sltiu AT, SFARG1HI, LJ_TISNUM | beqz AT, ->fff_fallback |.if FPU |. ldc1 FARG1, 0(BASE) |.else |. lw SFARG1LO, LO(BASE) |.endif | call_extern |. nop | b ->fff_resn |. nop |.endmacro | |.macro math_extern2, func | .ffunc_nn math_ .. func |. load_got func | call_extern |. nop | b ->fff_resn |. nop |.endmacro | |// TODO: Return integer type if result is integer (own sf implementation). |.macro math_round, func |->ff_math_ .. func: | lw SFARG1HI, HI(BASE) | beqz NARGS8:RC, ->fff_fallback |. lw SFARG1LO, LO(BASE) | beq SFARG1HI, TISNUM, ->fff_restv |. sltu AT, SFARG1HI, TISNUM | beqz AT, ->fff_fallback |.if FPU |. ldc1 FARG1, 0(BASE) | bal ->vm_ .. func |.else |. load_got func | call_extern |.endif |. nop | b ->fff_resn |. nop |.endmacro | | math_round floor | math_round ceil | |.ffunc math_log | li AT, 8 | bne NARGS8:RC, AT, ->fff_fallback // Exactly 1 argument. |. lw SFARG1HI, HI(BASE) | sltiu AT, SFARG1HI, LJ_TISNUM | beqz AT, ->fff_fallback |. load_got log |.if FPU | call_extern |. ldc1 FARG1, 0(BASE) |.else | call_extern |. lw SFARG1LO, LO(BASE) |.endif | b ->fff_resn |. nop | | math_extern log10 | math_extern exp | math_extern sin | math_extern cos | math_extern tan | math_extern asin | math_extern acos | math_extern atan | math_extern sinh | math_extern cosh | math_extern tanh | math_extern2 pow | math_extern2 atan2 | math_extern2 fmod | |.if FPU |.ffunc_n math_sqrt |. sqrt.d FRET1, FARG1 |// fallthrough to ->fff_resn |.else | math_extern sqrt |.endif | |->fff_resn: | lw PC, FRAME_PC(BASE) | addiu RA, BASE, -8 |.if FPU | b ->fff_res1 |. sdc1 FRET1, -8(BASE) |.else | sw SFRETHI, -8+HI(BASE) | b ->fff_res1 |. sw SFRETLO, -8+LO(BASE) |.endif | | |.ffunc math_ldexp | sltiu AT, NARGS8:RC, 16 | lw SFARG1HI, HI(BASE) | bnez AT, ->fff_fallback |. lw CARG4, 8+HI(BASE) | bne CARG4, TISNUM, ->fff_fallback | load_got ldexp |. sltu AT, SFARG1HI, TISNUM | beqz AT, ->fff_fallback |.if FPU |. ldc1 FARG1, 0(BASE) |.else |. lw SFARG1LO, LO(BASE) |.endif | call_extern |. lw CARG3, 8+LO(BASE) | b ->fff_resn |. nop | |.ffunc_n math_frexp | load_got frexp | lw PC, FRAME_PC(BASE) | call_extern |. addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) | lw TMP1, DISPATCH_GL(tmptv)(DISPATCH) | addiu RA, BASE, -8 |.if FPU | mtc1 TMP1, FARG2 | sdc1 FRET1, 0(RA) | cvt.d.w FARG2, FARG2 | sdc1 FARG2, 8(RA) |.else | sw SFRETLO, LO(RA) | sw SFRETHI, HI(RA) | sw TMP1, 8+LO(RA) | sw TISNUM, 8+HI(RA) |.endif | b ->fff_res |. li RD, (2+1)*8 | |.ffunc_n math_modf | load_got modf | lw PC, FRAME_PC(BASE) | call_extern |. addiu CARG3, BASE, -8 | addiu RA, BASE, -8 |.if FPU | sdc1 FRET1, 0(BASE) |.else | sw SFRETLO, LO(BASE) | sw SFRETHI, HI(BASE) |.endif | b ->fff_res |. li RD, (2+1)*8 | |.macro math_minmax, name, intins, ismax | .ffunc_1 name | addu TMP3, BASE, NARGS8:RC | bne SFARG1HI, TISNUM, >5 |. addiu TMP2, BASE, 8 |1: // Handle integers. |. lw SFARG2HI, HI(TMP2) | beq TMP2, TMP3, ->fff_restv |. lw SFARG2LO, LO(TMP2) | bne SFARG2HI, TISNUM, >3 |. slt AT, SFARG1LO, SFARG2LO | intins SFARG1LO, SFARG2LO, AT | b <1 |. addiu TMP2, TMP2, 8 | |3: // Convert intermediate result to number and continue with number loop. | sltiu AT, SFARG2HI, LJ_TISNUM | beqz AT, ->fff_fallback |.if FPU |. mtc1 SFARG1LO, FRET1 | cvt.d.w FRET1, FRET1 | b >7 |. ldc1 FARG1, 0(TMP2) |.else |. nop | bal ->vm_sfi2d_1 |. nop | b >7 |. nop |.endif | |5: |. sltiu AT, SFARG1HI, LJ_TISNUM | beqz AT, ->fff_fallback |.if FPU |. ldc1 FRET1, 0(BASE) |.endif | |6: // Handle numbers. |. lw SFARG2HI, HI(TMP2) |.if FPU | beq TMP2, TMP3, ->fff_resn |.else | beq TMP2, TMP3, ->fff_restv |.endif |. sltiu AT, SFARG2HI, LJ_TISNUM | beqz AT, >8 |.if FPU |. ldc1 FARG1, 0(TMP2) |.else |. lw SFARG2LO, LO(TMP2) |.endif |7: |.if FPU |.if ismax | c.olt.d FARG1, FRET1 |.else | c.olt.d FRET1, FARG1 |.endif | movf.d FRET1, FARG1 |.else |.if ismax | bal ->vm_sfcmpogt |.else | bal ->vm_sfcmpolt |.endif |. nop | movz SFARG1LO, SFARG2LO, CRET1 | movz SFARG1HI, SFARG2HI, CRET1 |.endif | b <6 |. addiu TMP2, TMP2, 8 | |8: // Convert integer to number and continue with number loop. | bne SFARG2HI, TISNUM, ->fff_fallback |.if FPU |. lwc1 FARG1, LO(TMP2) | b <7 |. cvt.d.w FARG1, FARG1 |.else |. nop | bal ->vm_sfi2d_2 |. nop | b <7 |. nop |.endif | |.endmacro | | math_minmax math_min, movz, 0 | math_minmax math_max, movn, 1 | |//-- String library ----------------------------------------------------- | |.ffunc string_byte // Only handle the 1-arg case here. | lw CARG3, HI(BASE) | lw STR:CARG1, LO(BASE) | xori AT, NARGS8:RC, 8 | addiu CARG3, CARG3, -LJ_TSTR | or AT, AT, CARG3 | bnez AT, ->fff_fallback // Need exactly 1 string argument. |. nop | lw TMP0, STR:CARG1->len | addiu RA, BASE, -8 | lw PC, FRAME_PC(BASE) | sltu RD, r0, TMP0 | lbu TMP1, STR:CARG1[1] // Access is always ok (NUL at end). | addiu RD, RD, 1 | sll RD, RD, 3 // RD = ((str->len != 0)+1)*8 | sw TISNUM, HI(RA) | b ->fff_res |. sw TMP1, LO(RA) | |.ffunc string_char // Only handle the 1-arg case here. | ffgccheck |. nop | lw CARG3, HI(BASE) | lw CARG1, LO(BASE) | li TMP1, 255 | xori AT, NARGS8:RC, 8 // Exactly 1 argument. | xor TMP0, CARG3, TISNUM // Integer. | sltu TMP1, TMP1, CARG1 // !(255 < n). | or AT, AT, TMP0 | or AT, AT, TMP1 | bnez AT, ->fff_fallback |. li CARG3, 1 | addiu CARG2, sp, ARG5_OFS | sb CARG1, ARG5 |->fff_newstr: | load_got lj_str_new | sw BASE, L->base | sw PC, SAVE_PC | call_intern lj_str_new // (lua_State *L, char *str, size_t l) |. move CARG1, L | // Returns GCstr *. | lw BASE, L->base |->fff_resstr: | move SFARG1LO, CRET1 | b ->fff_restv |. li SFARG1HI, LJ_TSTR | |.ffunc string_sub | ffgccheck |. nop | addiu AT, NARGS8:RC, -16 | lw CARG3, 16+HI(BASE) | lw TMP0, HI(BASE) | lw STR:CARG1, LO(BASE) | bltz AT, ->fff_fallback |. lw CARG2, 8+HI(BASE) | beqz AT, >1 |. li CARG4, -1 | bne CARG3, TISNUM, ->fff_fallback |. lw CARG4, 16+LO(BASE) |1: | bne CARG2, TISNUM, ->fff_fallback |. li AT, LJ_TSTR | bne TMP0, AT, ->fff_fallback |. lw CARG3, 8+LO(BASE) | lw CARG2, STR:CARG1->len | // STR:CARG1 = str, CARG2 = str->len, CARG3 = start, CARG4 = end | slt AT, CARG4, r0 | addiu TMP0, CARG2, 1 | addu TMP1, CARG4, TMP0 | slt TMP3, CARG3, r0 | movn CARG4, TMP1, AT // if (end < 0) end += len+1 | addu TMP1, CARG3, TMP0 | movn CARG3, TMP1, TMP3 // if (start < 0) start += len+1 | li TMP2, 1 | slt AT, CARG4, r0 | slt TMP3, r0, CARG3 | movn CARG4, r0, AT // if (end < 0) end = 0 | movz CARG3, TMP2, TMP3 // if (start < 1) start = 1 | slt AT, CARG2, CARG4 | movn CARG4, CARG2, AT // if (end > len) end = len | addu CARG2, STR:CARG1, CARG3 | subu CARG3, CARG4, CARG3 // len = end - start | addiu CARG2, CARG2, sizeof(GCstr)-1 | bgez CARG3, ->fff_newstr |. addiu CARG3, CARG3, 1 // len++ |->fff_emptystr: // Return empty string. | addiu STR:SFARG1LO, DISPATCH, DISPATCH_GL(strempty) | b ->fff_restv |. li SFARG1HI, LJ_TSTR | |.macro ffstring_op, name | .ffunc string_ .. name | ffgccheck |. nop | lw CARG3, HI(BASE) | lw STR:CARG2, LO(BASE) | beqz NARGS8:RC, ->fff_fallback |. li AT, LJ_TSTR | bne CARG3, AT, ->fff_fallback |. addiu SBUF:CARG1, DISPATCH, DISPATCH_GL(tmpbuf) | load_got lj_buf_putstr_ .. name | lw TMP0, SBUF:CARG1->b | sw L, SBUF:CARG1->L | sw BASE, L->base | sw TMP0, SBUF:CARG1->w | call_intern extern lj_buf_putstr_ .. name |. sw PC, SAVE_PC | load_got lj_buf_tostr | call_intern lj_buf_tostr |. move SBUF:CARG1, SBUF:CRET1 | b ->fff_resstr |. lw BASE, L->base |.endmacro | |ffstring_op reverse |ffstring_op lower |ffstring_op upper | |//-- Bit library -------------------------------------------------------- | |->vm_tobit_fb: | beqz TMP1, ->fff_fallback |.if FPU |. ldc1 FARG1, 0(BASE) | add.d FARG1, FARG1, TOBIT | jr ra |. mfc1 CRET1, FARG1 |.else |// FP number to bit conversion for soft-float. |->vm_tobit: | sll TMP0, SFARG1HI, 1 | lui AT, 0x0020 | addu TMP0, TMP0, AT | slt AT, TMP0, r0 | movz SFARG1LO, r0, AT | beqz AT, >2 |. li TMP1, 0x3e0 | not TMP1, TMP1 | sra TMP0, TMP0, 21 | subu TMP0, TMP1, TMP0 | slt AT, TMP0, r0 | bnez AT, >1 |. sll TMP1, SFARG1HI, 11 | lui AT, 0x8000 | or TMP1, TMP1, AT | srl AT, SFARG1LO, 21 | or TMP1, TMP1, AT | slt AT, SFARG1HI, r0 | beqz AT, >2 |. srlv SFARG1LO, TMP1, TMP0 | subu SFARG1LO, r0, SFARG1LO |2: | jr ra |. move CRET1, SFARG1LO |1: | addiu TMP0, TMP0, 21 | srlv TMP1, SFARG1LO, TMP0 | li AT, 20 | subu TMP0, AT, TMP0 | sll SFARG1LO, SFARG1HI, 12 | sllv AT, SFARG1LO, TMP0 | or SFARG1LO, TMP1, AT | slt AT, SFARG1HI, r0 | beqz AT, <2 |. nop | jr ra |. subu CRET1, r0, SFARG1LO |.endif | |.macro .ffunc_bit, name | .ffunc_1 bit_..name | beq SFARG1HI, TISNUM, >6 |. move CRET1, SFARG1LO | bal ->vm_tobit_fb |. sltu TMP1, SFARG1HI, TISNUM |6: |.endmacro | |.macro .ffunc_bit_op, name, ins | .ffunc_bit name | addiu TMP2, BASE, 8 | addu TMP3, BASE, NARGS8:RC |1: | lw SFARG1HI, HI(TMP2) | beq TMP2, TMP3, ->fff_resi |. lw SFARG1LO, LO(TMP2) |.if FPU | bne SFARG1HI, TISNUM, >2 |. addiu TMP2, TMP2, 8 | b <1 |. ins CRET1, CRET1, SFARG1LO |2: | ldc1 FARG1, -8(TMP2) | sltu TMP1, SFARG1HI, TISNUM | beqz TMP1, ->fff_fallback |. add.d FARG1, FARG1, TOBIT | mfc1 SFARG1LO, FARG1 | b <1 |. ins CRET1, CRET1, SFARG1LO |.else | beq SFARG1HI, TISNUM, >2 |. move CRET2, CRET1 | bal ->vm_tobit_fb |. sltu TMP1, SFARG1HI, TISNUM | move SFARG1LO, CRET2 |2: | ins CRET1, CRET1, SFARG1LO | b <1 |. addiu TMP2, TMP2, 8 |.endif |.endmacro | |.ffunc_bit_op band, and |.ffunc_bit_op bor, or |.ffunc_bit_op bxor, xor | |.ffunc_bit bswap | srl TMP0, CRET1, 24 | srl TMP2, CRET1, 8 | sll TMP1, CRET1, 24 | andi TMP2, TMP2, 0xff00 | or TMP0, TMP0, TMP1 | andi CRET1, CRET1, 0xff00 | or TMP0, TMP0, TMP2 | sll CRET1, CRET1, 8 | b ->fff_resi |. or CRET1, TMP0, CRET1 | |.ffunc_bit bnot | b ->fff_resi |. not CRET1, CRET1 | |.macro .ffunc_bit_sh, name, ins, shmod | .ffunc_2 bit_..name | beq SFARG1HI, TISNUM, >1 |. nop | bal ->vm_tobit_fb |. sltu TMP1, SFARG1HI, TISNUM | move SFARG1LO, CRET1 |1: | bne SFARG2HI, TISNUM, ->fff_fallback |. nop |.if shmod == 1 | li AT, 32 | subu TMP0, AT, SFARG2LO | sllv SFARG2LO, SFARG1LO, SFARG2LO | srlv SFARG1LO, SFARG1LO, TMP0 |.elif shmod == 2 | li AT, 32 | subu TMP0, AT, SFARG2LO | srlv SFARG2LO, SFARG1LO, SFARG2LO | sllv SFARG1LO, SFARG1LO, TMP0 |.endif | b ->fff_resi |. ins CRET1, SFARG1LO, SFARG2LO |.endmacro | |.ffunc_bit_sh lshift, sllv, 0 |.ffunc_bit_sh rshift, srlv, 0 |.ffunc_bit_sh arshift, srav, 0 |// Can't use rotrv, since it's only in MIPS32R2. |.ffunc_bit_sh rol, or, 1 |.ffunc_bit_sh ror, or, 2 | |.ffunc_bit tobit |->fff_resi: | lw PC, FRAME_PC(BASE) | addiu RA, BASE, -8 | sw TISNUM, -8+HI(BASE) | b ->fff_res1 |. sw CRET1, -8+LO(BASE) | |//----------------------------------------------------------------------- | |->fff_fallback: // Call fast function fallback handler. | // BASE = new base, RB = CFUNC, RC = nargs*8 | lw TMP3, CFUNC:RB->f | addu TMP1, BASE, NARGS8:RC | lw PC, FRAME_PC(BASE) // Fallback may overwrite PC. | addiu TMP0, TMP1, 8*LUA_MINSTACK | lw TMP2, L->maxstack | sw PC, SAVE_PC // Redundant (but a defined value). | sltu AT, TMP2, TMP0 | sw BASE, L->base | sw TMP1, L->top | bnez AT, >5 // Need to grow stack. |. move CFUNCADDR, TMP3 | jalr TMP3 // (lua_State *L) |. move CARG1, L | // Either throws an error, or recovers and returns -1, 0 or nresults+1. | lw BASE, L->base | sll RD, CRET1, 3 | bgtz CRET1, ->fff_res // Returned nresults+1? |. addiu RA, BASE, -8 |1: // Returned 0 or -1: retry fast path. | lw TMP0, L->top | lw LFUNC:RB, FRAME_FUNC(BASE) | bnez CRET1, ->vm_call_tail // Returned -1? |. subu NARGS8:RC, TMP0, BASE | ins_callt // Returned 0: retry fast path. | |// Reconstruct previous base for vmeta_call during tailcall. |->vm_call_tail: | andi TMP0, PC, FRAME_TYPE | li AT, -4 | bnez TMP0, >3 |. and TMP1, PC, AT | lbu TMP1, OFS_RA(PC) | sll TMP1, TMP1, 3 | addiu TMP1, TMP1, 8 |3: | b ->vm_call_dispatch // Resolve again for tailcall. |. subu TMP2, BASE, TMP1 | |5: // Grow stack for fallback handler. | load_got lj_state_growstack | li CARG2, LUA_MINSTACK | call_intern lj_state_growstack // (lua_State *L, int n) |. move CARG1, L | lw BASE, L->base | b <1 |. li CRET1, 0 // Force retry. | |->fff_gcstep: // Call GC step function. | // BASE = new base, RC = nargs*8 | move MULTRES, ra | load_got lj_gc_step | sw BASE, L->base | addu TMP0, BASE, NARGS8:RC | sw PC, SAVE_PC // Redundant (but a defined value). | sw TMP0, L->top | call_intern lj_gc_step // (lua_State *L) |. move CARG1, L | lw BASE, L->base | move ra, MULTRES | lw TMP0, L->top | lw CFUNC:RB, FRAME_FUNC(BASE) | jr ra |. subu NARGS8:RC, TMP0, BASE | |//----------------------------------------------------------------------- |//-- Special dispatch targets ------------------------------------------- |//----------------------------------------------------------------------- | |->vm_record: // Dispatch target for recording phase. |.if JIT | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) | andi AT, TMP3, HOOK_VMEVENT // No recording while in vmevent. | bnez AT, >5 | // Decrement the hookcount for consistency, but always do the call. |. lw TMP2, DISPATCH_GL(hookcount)(DISPATCH) | andi AT, TMP3, HOOK_ACTIVE | bnez AT, >1 |. addiu TMP2, TMP2, -1 | andi AT, TMP3, LUA_MASKLINE|LUA_MASKCOUNT | beqz AT, >1 |. nop | b >1 |. sw TMP2, DISPATCH_GL(hookcount)(DISPATCH) |.endif | |->vm_rethook: // Dispatch target for return hooks. | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) | andi AT, TMP3, HOOK_ACTIVE // Hook already active? | beqz AT, >1 |5: // Re-dispatch to static ins. |. lw AT, GG_DISP2STATIC(TMP0) // Assumes TMP0 holds DISPATCH+OP*4. | jr AT |. nop | |->vm_inshook: // Dispatch target for instr/line hooks. | lbu TMP3, DISPATCH_GL(hookmask)(DISPATCH) | lw TMP2, DISPATCH_GL(hookcount)(DISPATCH) | andi AT, TMP3, HOOK_ACTIVE // Hook already active? | bnez AT, <5 |. andi AT, TMP3, LUA_MASKLINE|LUA_MASKCOUNT | beqz AT, <5 |. addiu TMP2, TMP2, -1 | beqz TMP2, >1 |. sw TMP2, DISPATCH_GL(hookcount)(DISPATCH) | andi AT, TMP3, LUA_MASKLINE | beqz AT, <5 |1: |. load_got lj_dispatch_ins | sw MULTRES, SAVE_MULTRES | move CARG2, PC | sw BASE, L->base | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. | call_intern lj_dispatch_ins // (lua_State *L, const BCIns *pc) |. move CARG1, L |3: | lw BASE, L->base |4: // Re-dispatch to static ins. | lw INS, -4(PC) | decode_OP4a TMP1, INS | decode_OP4b TMP1 | addu TMP0, DISPATCH, TMP1 | decode_RD8a RD, INS | lw AT, GG_DISP2STATIC(TMP0) | decode_RA8a RA, INS | decode_RD8b RD | jr AT | decode_RA8b RA | |->cont_hook: // Continue from hook yield. | addiu PC, PC, 4 | b <4 |. lw MULTRES, -24+LO(RB) // Restore MULTRES for *M ins. | |->vm_hotloop: // Hot loop counter underflow. |.if JIT | lw LFUNC:TMP1, FRAME_FUNC(BASE) | addiu CARG1, DISPATCH, GG_DISP2J | sw PC, SAVE_PC | lw TMP1, LFUNC:TMP1->pc | move CARG2, PC | sw L, DISPATCH_J(L)(DISPATCH) | lbu TMP1, PC2PROTO(framesize)(TMP1) | load_got lj_trace_hot | sw BASE, L->base | sll TMP1, TMP1, 3 | addu TMP1, BASE, TMP1 | call_intern lj_trace_hot // (jit_State *J, const BCIns *pc) |. sw TMP1, L->top | b <3 |. nop |.endif | |->vm_callhook: // Dispatch target for call hooks. |.if JIT | b >1 |.endif |. move CARG2, PC | |->vm_hotcall: // Hot call counter underflow. |.if JIT | ori CARG2, PC, 1 |1: |.endif | load_got lj_dispatch_call | addu TMP0, BASE, RC | sw PC, SAVE_PC | sw BASE, L->base | subu RA, RA, BASE | sw TMP0, L->top | call_intern lj_dispatch_call // (lua_State *L, const BCIns *pc) |. move CARG1, L | // Returns ASMFunction. | lw BASE, L->base | lw TMP0, L->top | sw r0, SAVE_PC // Invalidate for subsequent line hook. | subu NARGS8:RC, TMP0, BASE | addu RA, BASE, RA | lw LFUNC:RB, FRAME_FUNC(BASE) | jr CRET1 |. lw INS, -4(PC) | |->cont_stitch: // Trace stitching. |.if JIT | // RA = resultptr, RB = meta base | lw INS, -4(PC) | lw TMP2, -24+LO(RB) // Save previous trace. | decode_RA8a RC, INS | addiu AT, MULTRES, -8 | decode_RA8b RC | beqz AT, >2 |. addu RC, BASE, RC // Call base. |1: // Move results down. | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | addiu AT, AT, -8 | addiu RA, RA, 8 | sw SFRETHI, HI(RC) | sw SFRETLO, LO(RC) | bnez AT, <1 |. addiu RC, RC, 8 |2: | decode_RA8a RA, INS | decode_RB8a RB, INS | decode_RA8b RA | decode_RB8b RB | addu RA, RA, RB | addu RA, BASE, RA |3: | sltu AT, RC, RA | bnez AT, >9 // More results wanted? |. nop | | lhu TMP3, TRACE:TMP2->traceno | lhu RD, TRACE:TMP2->link | beq RD, TMP3, ->cont_nop // Blacklisted. |. load_got lj_dispatch_stitch | bnez RD, =>BC_JLOOP // Jump to stitched trace. |. sll RD, RD, 3 | | // Stitch a new trace to the previous trace. | sw TMP3, DISPATCH_J(exitno)(DISPATCH) | sw L, DISPATCH_J(L)(DISPATCH) | sw BASE, L->base | addiu CARG1, DISPATCH, GG_DISP2J | call_intern lj_dispatch_stitch // (jit_State *J, const BCIns *pc) |. move CARG2, PC | b ->cont_nop |. lw BASE, L->base | |9: | sw TISNIL, HI(RC) | b <3 |. addiu RC, RC, 8 |.endif | |->vm_profhook: // Dispatch target for profiler hook. #if LJ_HASPROFILE | load_got lj_dispatch_profile | sw MULTRES, SAVE_MULTRES | move CARG2, PC | sw BASE, L->base | call_intern lj_dispatch_profile // (lua_State *L, const BCIns *pc) |. move CARG1, L | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. | addiu PC, PC, -4 | b ->cont_nop |. lw BASE, L->base #endif | |//----------------------------------------------------------------------- |//-- Trace exit handler ------------------------------------------------- |//----------------------------------------------------------------------- | |.macro savex_, a, b |.if FPU | sdc1 f..a, 16+a*8(sp) | sw r..a, 16+32*8+a*4(sp) | sw r..b, 16+32*8+b*4(sp) |.else | sw r..a, 16+a*4(sp) | sw r..b, 16+b*4(sp) |.endif |.endmacro | |->vm_exit_handler: |.if JIT |.if FPU | addiu sp, sp, -(16+32*8+32*4) |.else | addiu sp, sp, -(16+32*4) |.endif | savex_ 0, 1 | savex_ 2, 3 | savex_ 4, 5 | savex_ 6, 7 | savex_ 8, 9 | savex_ 10, 11 | savex_ 12, 13 | savex_ 14, 15 | savex_ 16, 17 | savex_ 18, 19 | savex_ 20, 21 | savex_ 22, 23 | savex_ 24, 25 | savex_ 26, 27 |.if FPU | sdc1 f28, 16+28*8(sp) | sdc1 f30, 16+30*8(sp) | sw r28, 16+32*8+28*4(sp) | sw r30, 16+32*8+30*4(sp) | sw r0, 16+32*8+31*4(sp) // Clear RID_TMP. | addiu TMP2, sp, 16+32*8+32*4 // Recompute original value of sp. | sw TMP2, 16+32*8+29*4(sp) // Store sp in RID_SP |.else | sw r28, 16+28*4(sp) | sw r30, 16+30*4(sp) | sw r0, 16+31*4(sp) // Clear RID_TMP. | addiu TMP2, sp, 16+32*4 // Recompute original value of sp. | sw TMP2, 16+29*4(sp) // Store sp in RID_SP |.endif | li_vmstate EXIT | addiu DISPATCH, JGL, -GG_DISP2G-32768 | lw TMP1, 0(TMP2) // Load exit number. | st_vmstate | lw L, DISPATCH_GL(cur_L)(DISPATCH) | lw BASE, DISPATCH_GL(jit_base)(DISPATCH) | load_got lj_trace_exit | sw L, DISPATCH_J(L)(DISPATCH) | sw ra, DISPATCH_J(parent)(DISPATCH) // Store trace number. | sw BASE, L->base | sw TMP1, DISPATCH_J(exitno)(DISPATCH) // Store exit number. | addiu CARG1, DISPATCH, GG_DISP2J | sw r0, DISPATCH_GL(jit_base)(DISPATCH) | call_intern lj_trace_exit // (jit_State *J, ExitState *ex) |. addiu CARG2, sp, 16 | // Returns MULTRES (unscaled) or negated error code. | lw TMP1, L->cframe | li AT, -4 | lw BASE, L->base | and sp, TMP1, AT | lw PC, SAVE_PC // Get SAVE_PC. | b >1 |. sw L, SAVE_L // Set SAVE_L (on-trace resume/yield). |.endif |->vm_exit_interp: |.if JIT | // CRET1 = MULTRES or negated error code, BASE, PC and JGL set. | lw L, SAVE_L | addiu DISPATCH, JGL, -GG_DISP2G-32768 | sw BASE, L->base |1: | sltiu TMP0, CRET1, -LUA_ERRERR // Check for error from exit. | beqz TMP0, >9 |. lw LFUNC:RB, FRAME_FUNC(BASE) | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). | sll MULTRES, CRET1, 3 | li TISNIL, LJ_TNIL | li TISNUM, LJ_TISNUM // Setup type comparison constants. | sw MULTRES, SAVE_MULTRES | .FPU mtc1 TMP3, TOBIT | lw TMP1, LFUNC:RB->pc | sw r0, DISPATCH_GL(jit_base)(DISPATCH) | lw KBASE, PC2PROTO(k)(TMP1) | .FPU cvt.d.s TOBIT, TOBIT | // Modified copy of ins_next which handles function header dispatch, too. | lw INS, 0(PC) | addiu CRET1, CRET1, 17 // Static dispatch? | // Assumes TISNIL == ~LJ_VMST_INTERP == -1 | sw TISNIL, DISPATCH_GL(vmstate)(DISPATCH) | decode_RD8a RD, INS | beqz CRET1, >5 |. addiu PC, PC, 4 | decode_OP4a TMP1, INS | decode_OP4b TMP1 | addu TMP0, DISPATCH, TMP1 | sltiu TMP2, TMP1, BC_FUNCF*4 | lw AT, 0(TMP0) | decode_RA8a RA, INS | beqz TMP2, >2 |. decode_RA8b RA | jr AT |. decode_RD8b RD |2: | sltiu TMP2, TMP1, (BC_FUNCC+2)*4 // Fast function? | bnez TMP2, >3 |. lw TMP1, FRAME_PC(BASE) | // Check frame below fast function. | andi TMP0, TMP1, FRAME_TYPE | bnez TMP0, >3 // Trace stitching continuation? |. nop | // Otherwise set KBASE for Lua function below fast function. | lw TMP2, -4(TMP1) | decode_RA8a TMP0, TMP2 | decode_RA8b TMP0 | subu TMP1, BASE, TMP0 | lw LFUNC:TMP2, -8+FRAME_FUNC(TMP1) | lw TMP1, LFUNC:TMP2->pc | lw KBASE, PC2PROTO(k)(TMP1) |3: | addiu RC, MULTRES, -8 | jr AT |. addu RA, RA, BASE | |5: // Dispatch to static entry of original ins replaced by BC_JLOOP. | lw TMP0, DISPATCH_J(trace)(DISPATCH) | decode_RD4b RD | addu TMP0, TMP0, RD | lw TRACE:TMP2, 0(TMP0) | lw INS, TRACE:TMP2->startins | decode_OP4a TMP1, INS | decode_OP4b TMP1 | addu TMP0, DISPATCH, TMP1 | decode_RD8a RD, INS | lw AT, GG_DISP2STATIC(TMP0) | decode_RA8a RA, INS | decode_RD8b RD | jr AT |. decode_RA8b RA | |9: // Rethrow error from the right C frame. | load_got lj_err_trace | sub CARG2, r0, CRET1 | call_intern lj_err_trace // (lua_State *L, int errcode) |. move CARG1, L |.endif | |//----------------------------------------------------------------------- |//-- Math helper functions ---------------------------------------------- |//----------------------------------------------------------------------- | |// Hard-float round to integer. |// Modifies AT, TMP0, FRET1, FRET2, f4. Keeps all others incl. FARG1. |.macro vm_round_hf, func | lui TMP0, 0x4330 // Hiword of 2^52 (double). | mtc1 r0, f4 | mtc1 TMP0, f5 | abs.d FRET2, FARG1 // |x| | mfc1 AT, f13 | c.olt.d 0, FRET2, f4 | add.d FRET1, FRET2, f4 // (|x| + 2^52) - 2^52 | bc1f 0, >1 // Truncate only if |x| < 2^52. |. sub.d FRET1, FRET1, f4 | slt AT, AT, r0 |.if "func" == "ceil" | lui TMP0, 0xbff0 // Hiword of -1 (double). Preserves -0. |.else | lui TMP0, 0x3ff0 // Hiword of +1 (double). |.endif |.if "func" == "trunc" | mtc1 TMP0, f5 | c.olt.d 0, FRET2, FRET1 // |x| < result? | sub.d FRET2, FRET1, f4 | movt.d FRET1, FRET2, 0 // If yes, subtract +1. | neg.d FRET2, FRET1 | jr ra |. movn.d FRET1, FRET2, AT // Merge sign bit back in. |.else | neg.d FRET2, FRET1 | mtc1 TMP0, f5 | movn.d FRET1, FRET2, AT // Merge sign bit back in. |.if "func" == "ceil" | c.olt.d 0, FRET1, FARG1 // x > result? |.else | c.olt.d 0, FARG1, FRET1 // x < result? |.endif | sub.d FRET2, FRET1, f4 // If yes, subtract +-1. | jr ra |. movt.d FRET1, FRET2, 0 |.endif |1: | jr ra |. mov.d FRET1, FARG1 |.endmacro | |.macro vm_round, func |.if FPU | vm_round_hf, func |.endif |.endmacro | |->vm_floor: | vm_round floor |->vm_ceil: | vm_round ceil |->vm_trunc: |.if JIT | vm_round trunc |.endif | |// Soft-float integer to number conversion. |.macro sfi2d, AHI, ALO |.if not FPU | beqz ALO, >9 // Handle zero first. |. sra TMP0, ALO, 31 | xor TMP1, ALO, TMP0 | subu TMP1, TMP1, TMP0 // Absolute value in TMP1. | clz AHI, TMP1 | andi TMP0, TMP0, 0x800 // Mask sign bit. | li AT, 0x3ff+31-1 | sllv TMP1, TMP1, AHI // Align mantissa left with leading 1. | subu AHI, AT, AHI // Exponent - 1 in AHI. | sll ALO, TMP1, 21 | or AHI, AHI, TMP0 // Sign | Exponent. | srl TMP1, TMP1, 11 | sll AHI, AHI, 20 // Align left. | jr ra |. addu AHI, AHI, TMP1 // Add mantissa, increment exponent. |9: | jr ra |. li AHI, 0 |.endif |.endmacro | |// Input SFARG1LO. Output: SFARG1*. Temporaries: AT, TMP0, TMP1. |->vm_sfi2d_1: | sfi2d SFARG1HI, SFARG1LO | |// Input SFARG2LO. Output: SFARG2*. Temporaries: AT, TMP0, TMP1. |->vm_sfi2d_2: | sfi2d SFARG2HI, SFARG2LO | |// Soft-float comparison. Equivalent to c.eq.d. |// Input: SFARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1. |->vm_sfcmpeq: |.if not FPU | sll AT, SFARG1HI, 1 | sll TMP0, SFARG2HI, 1 | or CRET1, SFARG1LO, SFARG2LO | or TMP1, AT, TMP0 | or TMP1, TMP1, CRET1 | beqz TMP1, >8 // Both args +-0: return 1. |. sltu CRET1, r0, SFARG1LO | lui TMP1, 0xffe0 | addu AT, AT, CRET1 | sltu CRET1, r0, SFARG2LO | sltu AT, TMP1, AT | addu TMP0, TMP0, CRET1 | sltu TMP0, TMP1, TMP0 | or TMP1, AT, TMP0 | bnez TMP1, >9 // Either arg is NaN: return 0; |. xor TMP0, SFARG1HI, SFARG2HI | xor TMP1, SFARG1LO, SFARG2LO | or AT, TMP0, TMP1 | jr ra |. sltiu CRET1, AT, 1 // Same values: return 1. |8: | jr ra |. li CRET1, 1 |9: | jr ra |. li CRET1, 0 |.endif | |// Soft-float comparison. Equivalent to c.ult.d and c.olt.d. |// Input: SFARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1, CRET2. |->vm_sfcmpult: |.if not FPU | b >1 |. li CRET2, 1 |.endif | |->vm_sfcmpolt: |.if not FPU | li CRET2, 0 |1: | sll AT, SFARG1HI, 1 | sll TMP0, SFARG2HI, 1 | or CRET1, SFARG1LO, SFARG2LO | or TMP1, AT, TMP0 | or TMP1, TMP1, CRET1 | beqz TMP1, >8 // Both args +-0: return 0. |. sltu CRET1, r0, SFARG1LO | lui TMP1, 0xffe0 | addu AT, AT, CRET1 | sltu CRET1, r0, SFARG2LO | sltu AT, TMP1, AT | addu TMP0, TMP0, CRET1 | sltu TMP0, TMP1, TMP0 | or TMP1, AT, TMP0 | bnez TMP1, >9 // Either arg is NaN: return 0 or 1; |. and AT, SFARG1HI, SFARG2HI | bltz AT, >5 // Both args negative? |. nop | beq SFARG1HI, SFARG2HI, >8 |. sltu CRET1, SFARG1LO, SFARG2LO | jr ra |. slt CRET1, SFARG1HI, SFARG2HI |5: // Swap conditions if both operands are negative. | beq SFARG1HI, SFARG2HI, >8 |. sltu CRET1, SFARG2LO, SFARG1LO | jr ra |. slt CRET1, SFARG2HI, SFARG1HI |8: | jr ra |. nop |9: | jr ra |. move CRET1, CRET2 |.endif | |->vm_sfcmpogt: |.if not FPU | sll AT, SFARG2HI, 1 | sll TMP0, SFARG1HI, 1 | or CRET1, SFARG2LO, SFARG1LO | or TMP1, AT, TMP0 | or TMP1, TMP1, CRET1 | beqz TMP1, >8 // Both args +-0: return 0. |. sltu CRET1, r0, SFARG2LO | lui TMP1, 0xffe0 | addu AT, AT, CRET1 | sltu CRET1, r0, SFARG1LO | sltu AT, TMP1, AT | addu TMP0, TMP0, CRET1 | sltu TMP0, TMP1, TMP0 | or TMP1, AT, TMP0 | bnez TMP1, >9 // Either arg is NaN: return 0 or 1; |. and AT, SFARG2HI, SFARG1HI | bltz AT, >5 // Both args negative? |. nop | beq SFARG2HI, SFARG1HI, >8 |. sltu CRET1, SFARG2LO, SFARG1LO | jr ra |. slt CRET1, SFARG2HI, SFARG1HI |5: // Swap conditions if both operands are negative. | beq SFARG2HI, SFARG1HI, >8 |. sltu CRET1, SFARG1LO, SFARG2LO | jr ra |. slt CRET1, SFARG1HI, SFARG2HI |8: | jr ra |. nop |9: | jr ra |. li CRET1, 0 |.endif | |// Soft-float comparison. Equivalent to c.ole.d a, b or c.ole.d b, a. |// Input: SFARG*, TMP3. Output: CRET1. Temporaries: AT, TMP0, TMP1. |->vm_sfcmpolex: |.if not FPU | sll AT, SFARG1HI, 1 | sll TMP0, SFARG2HI, 1 | or CRET1, SFARG1LO, SFARG2LO | or TMP1, AT, TMP0 | or TMP1, TMP1, CRET1 | beqz TMP1, >8 // Both args +-0: return 1. |. sltu CRET1, r0, SFARG1LO | lui TMP1, 0xffe0 | addu AT, AT, CRET1 | sltu CRET1, r0, SFARG2LO | sltu AT, TMP1, AT | addu TMP0, TMP0, CRET1 | sltu TMP0, TMP1, TMP0 | or TMP1, AT, TMP0 | bnez TMP1, >9 // Either arg is NaN: return 0; |. and AT, SFARG1HI, SFARG2HI | xor AT, AT, TMP3 | bltz AT, >5 // Both args negative? |. nop | beq SFARG1HI, SFARG2HI, >6 |. sltu CRET1, SFARG2LO, SFARG1LO | jr ra |. slt CRET1, SFARG2HI, SFARG1HI |5: // Swap conditions if both operands are negative. | beq SFARG1HI, SFARG2HI, >6 |. sltu CRET1, SFARG1LO, SFARG2LO | slt CRET1, SFARG1HI, SFARG2HI |6: | jr ra |. nop |8: | jr ra |. li CRET1, 1 |9: | jr ra |. li CRET1, 0 |.endif | |.macro sfmin_max, name, fpcall |->vm_sf .. name: |.if JIT and not FPU | move TMP2, ra | bal ->fpcall |. nop | move TMP0, CRET1 | move SFRETHI, SFARG1HI | move SFRETLO, SFARG1LO | move ra, TMP2 | movz SFRETHI, SFARG2HI, TMP0 | jr ra |. movz SFRETLO, SFARG2LO, TMP0 |.endif |.endmacro | | sfmin_max min, vm_sfcmpolt | sfmin_max max, vm_sfcmpogt | |//----------------------------------------------------------------------- |//-- Miscellaneous functions -------------------------------------------- |//----------------------------------------------------------------------- | |.define NEXT_TAB, TAB:CARG1 |.define NEXT_IDX, CARG2 |.define NEXT_ASIZE, CARG3 |.define NEXT_NIL, CARG4 |.define NEXT_TMP0, r12 |.define NEXT_TMP1, r13 |.define NEXT_TMP2, r14 |.define NEXT_RES_VK, CRET1 |.define NEXT_RES_IDX, CRET2 |.define NEXT_RES_PTR, sp |.define NEXT_RES_VAL_I, 0(sp) |.define NEXT_RES_VAL_IT, 4(sp) |.define NEXT_RES_KEY_I, 8(sp) |.define NEXT_RES_KEY_IT, 12(sp) | |// TValue *lj_vm_next(GCtab *t, uint32_t idx) |// Next idx returned in CRET2. |->vm_next: |.if JIT and ENDIAN_LE | lw NEXT_ASIZE, NEXT_TAB->asize | lw NEXT_TMP0, NEXT_TAB->array | li NEXT_NIL, LJ_TNIL |1: // Traverse array part. | sltu AT, NEXT_IDX, NEXT_ASIZE | sll NEXT_TMP1, NEXT_IDX, 3 | beqz AT, >5 |. addu NEXT_TMP1, NEXT_TMP0, NEXT_TMP1 | lw NEXT_TMP2, 4(NEXT_TMP1) | sw NEXT_IDX, NEXT_RES_KEY_I | beq NEXT_TMP2, NEXT_NIL, <1 |. addiu NEXT_IDX, NEXT_IDX, 1 | lw NEXT_TMP0, 0(NEXT_TMP1) | li AT, LJ_TISNUM | sw NEXT_TMP2, NEXT_RES_VAL_IT | sw AT, NEXT_RES_KEY_IT | sw NEXT_TMP0, NEXT_RES_VAL_I | move NEXT_RES_VK, NEXT_RES_PTR | jr ra |. move NEXT_RES_IDX, NEXT_IDX | |5: // Traverse hash part. | subu NEXT_RES_IDX, NEXT_IDX, NEXT_ASIZE | lw NODE:NEXT_RES_VK, NEXT_TAB->node | sll NEXT_TMP2, NEXT_RES_IDX, 5 | lw NEXT_TMP0, NEXT_TAB->hmask | sll AT, NEXT_RES_IDX, 3 | subu AT, NEXT_TMP2, AT | addu NODE:NEXT_RES_VK, NODE:NEXT_RES_VK, AT |6: | sltu AT, NEXT_TMP0, NEXT_RES_IDX | bnez AT, >8 |. nop | lw NEXT_TMP2, NODE:NEXT_RES_VK->val.it | bne NEXT_TMP2, NEXT_NIL, >9 |. addiu NEXT_RES_IDX, NEXT_RES_IDX, 1 | // Skip holes in hash part. | b <6 |. addiu NODE:NEXT_RES_VK, NODE:NEXT_RES_VK, sizeof(Node) | |8: // End of iteration. Set the key to nil (not the value). | sw NEXT_NIL, NEXT_RES_KEY_IT | move NEXT_RES_VK, NEXT_RES_PTR |9: | jr ra |. addu NEXT_RES_IDX, NEXT_RES_IDX, NEXT_ASIZE |.endif | |//----------------------------------------------------------------------- |//-- FFI helper functions ----------------------------------------------- |//----------------------------------------------------------------------- | |// Handler for callback functions. Callback slot number in r1, g in r2. |->vm_ffi_callback: |.if FFI |.type CTSTATE, CTState, PC | saveregs | lw CTSTATE, GL:r2->ctype_state | addiu DISPATCH, r2, GG_G2DISP | load_got lj_ccallback_enter | sw r1, CTSTATE->cb.slot | sw CARG1, CTSTATE->cb.gpr[0] | sw CARG2, CTSTATE->cb.gpr[1] | .FPU sdc1 FARG1, CTSTATE->cb.fpr[0] | sw CARG3, CTSTATE->cb.gpr[2] | sw CARG4, CTSTATE->cb.gpr[3] | .FPU sdc1 FARG2, CTSTATE->cb.fpr[1] | addiu TMP0, sp, CFRAME_SPACE+16 | sw TMP0, CTSTATE->cb.stack | sw r0, SAVE_PC // Any value outside of bytecode is ok. | move CARG2, sp | call_intern lj_ccallback_enter // (CTState *cts, void *cf) |. move CARG1, CTSTATE | // Returns lua_State *. | lw BASE, L:CRET1->base | lw RC, L:CRET1->top | li TISNUM, LJ_TISNUM // Setup type comparison constants. | move L, CRET1 | .FPU lui TMP3, 0x59c0 // TOBIT = 2^52 + 2^51 (float). | lw LFUNC:RB, FRAME_FUNC(BASE) | .FPU mtc1 TMP3, TOBIT | li_vmstate INTERP | li TISNIL, LJ_TNIL | subu RC, RC, BASE | st_vmstate | .FPU cvt.d.s TOBIT, TOBIT | ins_callt |.endif | |->cont_ffi_callback: // Return from FFI callback. |.if FFI | load_got lj_ccallback_leave | lw CTSTATE, DISPATCH_GL(ctype_state)(DISPATCH) | sw BASE, L->base | sw RB, L->top | sw L, CTSTATE->L | move CARG2, RA | call_intern lj_ccallback_leave // (CTState *cts, TValue *o) |. move CARG1, CTSTATE | .FPU ldc1 FRET1, CTSTATE->cb.fpr[0] | lw CRET1, CTSTATE->cb.gpr[0] | .FPU ldc1 FRET2, CTSTATE->cb.fpr[1] | b ->vm_leave_unw |. lw CRET2, CTSTATE->cb.gpr[1] |.endif | |->vm_ffi_call: // Call C function via FFI. | // Caveat: needs special frame unwinding, see below. |.if FFI | .type CCSTATE, CCallState, CARG1 | lw TMP1, CCSTATE->spadj | lbu CARG2, CCSTATE->nsp | move TMP2, sp | subu sp, sp, TMP1 | sw ra, -4(TMP2) | sll CARG2, CARG2, 2 | sw r16, -8(TMP2) | sw CCSTATE, -12(TMP2) | move r16, TMP2 | addiu TMP1, CCSTATE, offsetof(CCallState, stack) | addiu TMP2, sp, 16 | beqz CARG2, >2 |. addu TMP3, TMP1, CARG2 |1: | lw TMP0, 0(TMP1) | addiu TMP1, TMP1, 4 | sltu AT, TMP1, TMP3 | sw TMP0, 0(TMP2) | bnez AT, <1 |. addiu TMP2, TMP2, 4 |2: | lw CFUNCADDR, CCSTATE->func | lw CARG2, CCSTATE->gpr[1] | lw CARG3, CCSTATE->gpr[2] | lw CARG4, CCSTATE->gpr[3] | .FPU ldc1 FARG1, CCSTATE->fpr[0] | .FPU ldc1 FARG2, CCSTATE->fpr[1] | jalr CFUNCADDR |. lw CARG1, CCSTATE->gpr[0] // Do this last, since CCSTATE is CARG1. | lw CCSTATE:TMP1, -12(r16) | lw TMP2, -8(r16) | lw ra, -4(r16) | sw CRET1, CCSTATE:TMP1->gpr[0] | sw CRET2, CCSTATE:TMP1->gpr[1] |.if FPU | sdc1 FRET1, CCSTATE:TMP1->fpr[0] | sdc1 FRET2, CCSTATE:TMP1->fpr[1] |.else | sw CARG1, CCSTATE:TMP1->gpr[2] // Soft-float: complex double .im part. | sw CARG2, CCSTATE:TMP1->gpr[3] |.endif | move sp, r16 | jr ra |. move r16, TMP2 |.endif |// Note: vm_ffi_call must be the last function in this object file! | |//----------------------------------------------------------------------- } /* Generate the code for a single instruction. */ static void build_ins(BuildCtx *ctx, BCOp op, int defop) { int vk = 0; |=>defop: switch (op) { /* -- Comparison ops ---------------------------------------------------- */ /* Remember: all ops branch for a true comparison, fall through otherwise. */ case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT: | // RA = src1*8, RD = src2*8, JMP with RD = target |.macro bc_comp, FRA, FRD, RAHI, RALO, RDHI, RDLO, movop, fmovop, fcomp, sfcomp | addu RA, BASE, RA | addu RD, BASE, RD | lw RAHI, HI(RA) | lw RDHI, HI(RD) | lhu TMP2, OFS_RD(PC) | addiu PC, PC, 4 | bne RAHI, TISNUM, >2 |. lw RALO, LO(RA) | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | lw RDLO, LO(RD) | bne RDHI, TISNUM, >5 |. decode_RD4b TMP2 | slt AT, SFARG1LO, SFARG2LO | addu TMP2, TMP2, TMP3 | movop TMP2, r0, AT |1: | addu PC, PC, TMP2 | ins_next | |2: // RA is not an integer. | sltiu AT, RAHI, LJ_TISNUM | beqz AT, ->vmeta_comp |. lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | sltiu AT, RDHI, LJ_TISNUM |.if FPU | ldc1 FRA, 0(RA) | ldc1 FRD, 0(RD) |.else | lw RDLO, LO(RD) |.endif | beqz AT, >4 |. decode_RD4b TMP2 |3: // RA and RD are both numbers. |.if FPU | fcomp f20, f22 | addu TMP2, TMP2, TMP3 | b <1 |. fmovop TMP2, r0 |.else | bal sfcomp |. addu TMP2, TMP2, TMP3 | b <1 |. movop TMP2, r0, CRET1 |.endif | |4: // RA is a number, RD is not a number. | bne RDHI, TISNUM, ->vmeta_comp | // RA is a number, RD is an integer. Convert RD to a number. |.if FPU |. lwc1 FRD, LO(RD) | b <3 |. cvt.d.w FRD, FRD |.else |. nop |.if "RDHI" == "SFARG1HI" | bal ->vm_sfi2d_1 |.else | bal ->vm_sfi2d_2 |.endif |. nop | b <3 |. nop |.endif | |5: // RA is an integer, RD is not an integer | sltiu AT, RDHI, LJ_TISNUM | beqz AT, ->vmeta_comp | // RA is an integer, RD is a number. Convert RA to a number. |.if FPU |. mtc1 RALO, FRA | ldc1 FRD, 0(RD) | b <3 | cvt.d.w FRA, FRA |.else |. nop |.if "RAHI" == "SFARG1HI" | bal ->vm_sfi2d_1 |.else | bal ->vm_sfi2d_2 |.endif |. nop | b <3 |. nop |.endif |.endmacro | if (op == BC_ISLT) { | bc_comp f20, f22, SFARG1HI, SFARG1LO, SFARG2HI, SFARG2LO, movz, movf, c.olt.d, ->vm_sfcmpolt } else if (op == BC_ISGE) { | bc_comp f20, f22, SFARG1HI, SFARG1LO, SFARG2HI, SFARG2LO, movn, movt, c.olt.d, ->vm_sfcmpolt } else if (op == BC_ISLE) { | bc_comp f22, f20, SFARG2HI, SFARG2LO, SFARG1HI, SFARG1LO, movn, movt, c.ult.d, ->vm_sfcmpult } else { | bc_comp f22, f20, SFARG2HI, SFARG2LO, SFARG1HI, SFARG1LO, movz, movf, c.ult.d, ->vm_sfcmpult } break; case BC_ISEQV: case BC_ISNEV: vk = op == BC_ISEQV; | // RA = src1*8, RD = src2*8, JMP with RD = target | addu RA, BASE, RA | addiu PC, PC, 4 | addu RD, BASE, RD | lw SFARG1HI, HI(RA) | lhu TMP2, -4+OFS_RD(PC) | lw SFARG2HI, HI(RD) | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | sltu AT, TISNUM, SFARG1HI | sltu TMP0, TISNUM, SFARG2HI | or AT, AT, TMP0 if (vk) { | beqz AT, ->BC_ISEQN_Z } else { | beqz AT, ->BC_ISNEN_Z } |. decode_RD4b TMP2 | // Either or both types are not numbers. | lw SFARG1LO, LO(RA) | lw SFARG2LO, LO(RD) | addu TMP2, TMP2, TMP3 |.if FFI | li TMP3, LJ_TCDATA | beq SFARG1HI, TMP3, ->vmeta_equal_cd |.endif |. sltiu AT, SFARG1HI, LJ_TISPRI // Not a primitive? |.if FFI | beq SFARG2HI, TMP3, ->vmeta_equal_cd |.endif |. xor TMP3, SFARG1LO, SFARG2LO // Same tv? | xor SFARG2HI, SFARG2HI, SFARG1HI // Same type? | sltiu TMP0, SFARG1HI, LJ_TISTABUD+1 // Table or userdata? | movz TMP3, r0, AT // Ignore tv if primitive. | movn TMP0, r0, SFARG2HI // Tab/ud and same type? | or AT, SFARG2HI, TMP3 // Same type && (pri||same tv). | movz TMP0, r0, AT | beqz TMP0, >1 // Done if not tab/ud or not same type or same tv. if (vk) { |. movn TMP2, r0, AT } else { |. movz TMP2, r0, AT } | // Different tables or userdatas. Need to check __eq metamethod. | // Field metatable must be at same offset for GCtab and GCudata! | lw TAB:TMP1, TAB:SFARG1LO->metatable | beqz TAB:TMP1, >1 // No metatable? |. nop | lbu TMP1, TAB:TMP1->nomm | andi TMP1, TMP1, 1<<MM_eq | bnez TMP1, >1 // Or 'no __eq' flag set? |. nop | b ->vmeta_equal // Handle __eq metamethod. |. li TMP0, 1-vk // ne = 0 or 1. |1: | addu PC, PC, TMP2 | ins_next break; case BC_ISEQS: case BC_ISNES: vk = op == BC_ISEQS; | // RA = src*8, RD = str_const*8 (~), JMP with RD = target | addu RA, BASE, RA | addiu PC, PC, 4 | lw TMP0, HI(RA) | srl RD, RD, 1 | lw STR:TMP3, LO(RA) | subu RD, KBASE, RD | lhu TMP2, -4+OFS_RD(PC) |.if FFI | li AT, LJ_TCDATA | beq TMP0, AT, ->vmeta_equal_cd |.endif |. lw STR:TMP1, -4(RD) // KBASE-4-str_const*4 | addiu TMP0, TMP0, -LJ_TSTR | decode_RD4b TMP2 | xor TMP1, STR:TMP1, STR:TMP3 | or TMP0, TMP0, TMP1 | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | addu TMP2, TMP2, TMP3 if (vk) { | movn TMP2, r0, TMP0 } else { | movz TMP2, r0, TMP0 } | addu PC, PC, TMP2 | ins_next break; case BC_ISEQN: case BC_ISNEN: vk = op == BC_ISEQN; | // RA = src*8, RD = num_const*8, JMP with RD = target | addu RA, BASE, RA | addu RD, KBASE, RD | lw SFARG1HI, HI(RA) | lw SFARG2HI, HI(RD) | lhu TMP2, OFS_RD(PC) | addiu PC, PC, 4 | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | decode_RD4b TMP2 if (vk) { |->BC_ISEQN_Z: } else { |->BC_ISNEN_Z: } | bne SFARG1HI, TISNUM, >3 |. lw SFARG1LO, LO(RA) | lw SFARG2LO, LO(RD) | addu TMP2, TMP2, TMP3 | bne SFARG2HI, TISNUM, >6 |. xor AT, SFARG1LO, SFARG2LO if (vk) { | movn TMP2, r0, AT |1: | addu PC, PC, TMP2 |2: } else { | movz TMP2, r0, AT |1: |2: | addu PC, PC, TMP2 } | ins_next | |3: // RA is not an integer. | sltiu AT, SFARG1HI, LJ_TISNUM |.if FFI | beqz AT, >8 |.else | beqz AT, <2 |.endif |. addu TMP2, TMP2, TMP3 | sltiu AT, SFARG2HI, LJ_TISNUM |.if FPU | ldc1 f20, 0(RA) | ldc1 f22, 0(RD) |.endif | beqz AT, >5 |. lw SFARG2LO, LO(RD) |4: // RA and RD are both numbers. |.if FPU | c.eq.d f20, f22 | b <1 if (vk) { |. movf TMP2, r0 } else { |. movt TMP2, r0 } |.else | bal ->vm_sfcmpeq |. nop | b <1 if (vk) { |. movz TMP2, r0, CRET1 } else { |. movn TMP2, r0, CRET1 } |.endif | |5: // RA is a number, RD is not a number. |.if FFI | bne SFARG2HI, TISNUM, >9 |.else | bne SFARG2HI, TISNUM, <2 |.endif | // RA is a number, RD is an integer. Convert RD to a number. |.if FPU |. lwc1 f22, LO(RD) | b <4 |. cvt.d.w f22, f22 |.else |. nop | bal ->vm_sfi2d_2 |. nop | b <4 |. nop |.endif | |6: // RA is an integer, RD is not an integer | sltiu AT, SFARG2HI, LJ_TISNUM |.if FFI | beqz AT, >9 |.else | beqz AT, <2 |.endif | // RA is an integer, RD is a number. Convert RA to a number. |.if FPU |. mtc1 SFARG1LO, f20 | ldc1 f22, 0(RD) | b <4 | cvt.d.w f20, f20 |.else |. nop | bal ->vm_sfi2d_1 |. nop | b <4 |. nop |.endif | |.if FFI |8: | li AT, LJ_TCDATA | bne SFARG1HI, AT, <2 |. nop | b ->vmeta_equal_cd |. nop |9: | li AT, LJ_TCDATA | bne SFARG2HI, AT, <2 |. nop | b ->vmeta_equal_cd |. nop |.endif break; case BC_ISEQP: case BC_ISNEP: vk = op == BC_ISEQP; | // RA = src*8, RD = primitive_type*8 (~), JMP with RD = target | addu RA, BASE, RA | srl TMP1, RD, 3 | lw TMP0, HI(RA) | lhu TMP2, OFS_RD(PC) | not TMP1, TMP1 | addiu PC, PC, 4 |.if FFI | li AT, LJ_TCDATA | beq TMP0, AT, ->vmeta_equal_cd |.endif |. xor TMP0, TMP0, TMP1 | decode_RD4b TMP2 | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | addu TMP2, TMP2, TMP3 if (vk) { | movn TMP2, r0, TMP0 } else { | movz TMP2, r0, TMP0 } | addu PC, PC, TMP2 | ins_next break; /* -- Unary test and copy ops ------------------------------------------- */ case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: | // RA = dst*8 or unused, RD = src*8, JMP with RD = target | addu RD, BASE, RD | lhu TMP2, OFS_RD(PC) | lw TMP0, HI(RD) | addiu PC, PC, 4 if (op == BC_IST || op == BC_ISF) { | sltiu TMP0, TMP0, LJ_TISTRUECOND | decode_RD4b TMP2 | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | addu TMP2, TMP2, TMP3 if (op == BC_IST) { | movz TMP2, r0, TMP0 } else { | movn TMP2, r0, TMP0 } | addu PC, PC, TMP2 } else { | sltiu TMP0, TMP0, LJ_TISTRUECOND | lw SFRETHI, HI(RD) | lw SFRETLO, LO(RD) if (op == BC_ISTC) { | beqz TMP0, >1 } else { | bnez TMP0, >1 } |. addu RA, BASE, RA | decode_RD4b TMP2 | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | addu TMP2, TMP2, TMP3 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | addu PC, PC, TMP2 |1: } | ins_next break; case BC_ISTYPE: | // RA = src*8, RD = -type*8 | addu TMP2, BASE, RA | srl TMP1, RD, 3 | lw TMP0, HI(TMP2) | ins_next1 | addu AT, TMP0, TMP1 | bnez AT, ->vmeta_istype |. ins_next2 break; case BC_ISNUM: | // RA = src*8, RD = -(TISNUM-1)*8 | addu TMP2, BASE, RA | lw TMP0, HI(TMP2) | ins_next1 | sltiu AT, TMP0, LJ_TISNUM | beqz AT, ->vmeta_istype |. ins_next2 break; /* -- Unary ops --------------------------------------------------------- */ case BC_MOV: | // RA = dst*8, RD = src*8 | addu RD, BASE, RD | addu RA, BASE, RA | lw SFRETHI, HI(RD) | lw SFRETLO, LO(RD) | ins_next1 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 break; case BC_NOT: | // RA = dst*8, RD = src*8 | addu RD, BASE, RD | addu RA, BASE, RA | lw TMP0, HI(RD) | li TMP1, LJ_TFALSE | sltiu TMP0, TMP0, LJ_TISTRUECOND | addiu TMP1, TMP0, LJ_TTRUE | ins_next1 | sw TMP1, HI(RA) | ins_next2 break; case BC_UNM: | // RA = dst*8, RD = src*8 | addu RB, BASE, RD | lw SFARG1HI, HI(RB) | addu RA, BASE, RA | bne SFARG1HI, TISNUM, >2 |. lw SFARG1LO, LO(RB) | lui TMP1, 0x8000 | beq SFARG1LO, TMP1, ->vmeta_unm // Meta handler deals with -2^31. |. negu SFARG1LO, SFARG1LO |1: | ins_next1 | sw SFARG1HI, HI(RA) | sw SFARG1LO, LO(RA) | ins_next2 |2: | sltiu AT, SFARG1HI, LJ_TISNUM | beqz AT, ->vmeta_unm |. lui TMP1, 0x8000 | b <1 |. xor SFARG1HI, SFARG1HI, TMP1 break; case BC_LEN: | // RA = dst*8, RD = src*8 | addu CARG2, BASE, RD | addu RA, BASE, RA | lw TMP0, HI(CARG2) | lw CARG1, LO(CARG2) | li AT, LJ_TSTR | bne TMP0, AT, >2 |. li AT, LJ_TTAB | lw CRET1, STR:CARG1->len |1: | ins_next1 | sw TISNUM, HI(RA) | sw CRET1, LO(RA) | ins_next2 |2: | bne TMP0, AT, ->vmeta_len |. nop #if LJ_52 | lw TAB:TMP2, TAB:CARG1->metatable | bnez TAB:TMP2, >9 |. nop |3: #endif |->BC_LEN_Z: | load_got lj_tab_len | call_intern lj_tab_len // (GCtab *t) |. nop | // Returns uint32_t (but less than 2^31). | b <1 |. nop #if LJ_52 |9: | lbu TMP0, TAB:TMP2->nomm | andi TMP0, TMP0, 1<<MM_len | bnez TMP0, <3 // 'no __len' flag set: done. |. nop | b ->vmeta_len |. nop #endif break; /* -- Binary ops -------------------------------------------------------- */ |.macro fpmod, a, b, c | bal ->vm_floor // floor(b/c) |. div.d FARG1, b, c | mul.d a, FRET1, c | sub.d a, b, a // b - floor(b/c)*c |.endmacro |.macro sfpmod | addiu sp, sp, -16 | | load_got __divdf3 | sw SFARG1HI, HI(sp) | sw SFARG1LO, LO(sp) | sw SFARG2HI, 8+HI(sp) | call_extern |. sw SFARG2LO, 8+LO(sp) | | load_got floor | move SFARG1HI, SFRETHI | call_extern |. move SFARG1LO, SFRETLO | | load_got __muldf3 | move SFARG1HI, SFRETHI | move SFARG1LO, SFRETLO | lw SFARG2HI, 8+HI(sp) | call_extern |. lw SFARG2LO, 8+LO(sp) | | load_got __subdf3 | lw SFARG1HI, HI(sp) | lw SFARG1LO, LO(sp) | move SFARG2HI, SFRETHI | call_extern |. move SFARG2LO, SFRETLO | | addiu sp, sp, 16 |.endmacro |.macro ins_arithpre, label ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN); | // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8 ||switch (vk) { ||case 0: | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | // RA = dst*8, RB = src1*8, RC = num_const*8 | addu RB, BASE, RB |.if "label" ~= "none" | b label |.endif |. addu RC, KBASE, RC || break; ||case 1: | decode_RB8a RC, INS | decode_RB8b RC | decode_RDtoRC8 RB, RD | // RA = dst*8, RB = num_const*8, RC = src1*8 | addu RC, BASE, RC |.if "label" ~= "none" | b label |.endif |. addu RB, KBASE, RB || break; ||default: | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | // RA = dst*8, RB = src1*8, RC = src2*8 | addu RB, BASE, RB |.if "label" ~= "none" | b label |.endif |. addu RC, BASE, RC || break; ||} |.endmacro | |.macro ins_arith, intins, fpins, fpcall, label | ins_arithpre none | |.if "label" ~= "none" |label: |.endif | | lw SFARG1HI, HI(RB) | lw SFARG2HI, HI(RC) | |.if "intins" ~= "div" | | // Check for two integers. | lw SFARG1LO, LO(RB) | bne SFARG1HI, TISNUM, >5 |. lw SFARG2LO, LO(RC) | bne SFARG2HI, TISNUM, >5 | |.if "intins" == "addu" |. intins CRET1, SFARG1LO, SFARG2LO | xor TMP1, CRET1, SFARG1LO // ((y^a) & (y^b)) < 0: overflow. | xor TMP2, CRET1, SFARG2LO | and TMP1, TMP1, TMP2 | bltz TMP1, ->vmeta_arith |. addu RA, BASE, RA |.elif "intins" == "subu" |. intins CRET1, SFARG1LO, SFARG2LO | xor TMP1, CRET1, SFARG1LO // ((y^a) & (a^b)) < 0: overflow. | xor TMP2, SFARG1LO, SFARG2LO | and TMP1, TMP1, TMP2 | bltz TMP1, ->vmeta_arith |. addu RA, BASE, RA |.elif "intins" == "mult" |. intins SFARG1LO, SFARG2LO | mflo CRET1 | mfhi TMP2 | sra TMP1, CRET1, 31 | bne TMP1, TMP2, ->vmeta_arith |. addu RA, BASE, RA |.else |. load_got lj_vm_modi | beqz SFARG2LO, ->vmeta_arith |. addu RA, BASE, RA |.if ENDIAN_BE | move CARG1, SFARG1LO |.endif | call_extern |. move CARG2, SFARG2LO |.endif | | ins_next1 | sw TISNUM, HI(RA) | sw CRET1, LO(RA) |3: | ins_next2 | |.elif not FPU | | lw SFARG1LO, LO(RB) | lw SFARG2LO, LO(RC) | |.endif | |5: // Check for two numbers. | .FPU ldc1 f20, 0(RB) | sltiu AT, SFARG1HI, LJ_TISNUM | sltiu TMP0, SFARG2HI, LJ_TISNUM | .FPU ldc1 f22, 0(RC) | and AT, AT, TMP0 | beqz AT, ->vmeta_arith |. addu RA, BASE, RA | |.if FPU | fpins FRET1, f20, f22 |.elif "fpcall" == "sfpmod" | sfpmod |.else | load_got fpcall | call_extern |. nop |.endif | | ins_next1 |.if not FPU | sw SFRETHI, HI(RA) |.endif |.if "intins" ~= "div" | b <3 |.endif |.if FPU |. sdc1 FRET1, 0(RA) |.else |. sw SFRETLO, LO(RA) |.endif |.if "intins" == "div" | ins_next2 |.endif | |.endmacro case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: | ins_arith addu, add.d, __adddf3, none break; case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: | ins_arith subu, sub.d, __subdf3, none break; case BC_MULVN: case BC_MULNV: case BC_MULVV: | ins_arith mult, mul.d, __muldf3, none break; case BC_DIVVN: | ins_arith div, div.d, __divdf3, ->BC_DIVVN_Z break; case BC_DIVNV: case BC_DIVVV: | ins_arithpre ->BC_DIVVN_Z break; case BC_MODVN: | ins_arith modi, fpmod, sfpmod, ->BC_MODVN_Z break; case BC_MODNV: case BC_MODVV: | ins_arithpre ->BC_MODVN_Z break; case BC_POW: | ins_arithpre none | lw SFARG1HI, HI(RB) | lw SFARG2HI, HI(RC) | sltiu AT, SFARG1HI, LJ_TISNUM | sltiu TMP0, SFARG2HI, LJ_TISNUM | and AT, AT, TMP0 | load_got pow | beqz AT, ->vmeta_arith |. addu RA, BASE, RA |.if FPU | ldc1 FARG1, 0(RB) | ldc1 FARG2, 0(RC) |.else | lw SFARG1LO, LO(RB) | lw SFARG2LO, LO(RC) |.endif | call_extern |. nop | ins_next1 |.if FPU | sdc1 FRET1, 0(RA) |.else | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) |.endif | ins_next2 break; case BC_CAT: | // RA = dst*8, RB = src_start*8, RC = src_end*8 | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | subu CARG3, RC, RB | sw BASE, L->base | addu CARG2, BASE, RC | move MULTRES, RB |->BC_CAT_Z: | load_got lj_meta_cat | srl CARG3, CARG3, 3 | sw PC, SAVE_PC | call_intern lj_meta_cat // (lua_State *L, TValue *top, int left) |. move CARG1, L | // Returns NULL (finished) or TValue * (metamethod). | bnez CRET1, ->vmeta_binop |. lw BASE, L->base | addu RB, BASE, MULTRES | lw SFRETHI, HI(RB) | lw SFRETLO, LO(RB) | addu RA, BASE, RA | ins_next1 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 break; /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: | // RA = dst*8, RD = str_const*8 (~) | srl TMP1, RD, 1 | subu TMP1, KBASE, TMP1 | ins_next1 | lw TMP0, -4(TMP1) // KBASE-4-str_const*4 | addu RA, BASE, RA | li TMP2, LJ_TSTR | sw TMP0, LO(RA) | sw TMP2, HI(RA) | ins_next2 break; case BC_KCDATA: |.if FFI | // RA = dst*8, RD = cdata_const*8 (~) | srl TMP1, RD, 1 | subu TMP1, KBASE, TMP1 | ins_next1 | lw TMP0, -4(TMP1) // KBASE-4-cdata_const*4 | addu RA, BASE, RA | li TMP2, LJ_TCDATA | sw TMP0, LO(RA) | sw TMP2, HI(RA) | ins_next2 |.endif break; case BC_KSHORT: | // RA = dst*8, RD = int16_literal*8 | sra RD, INS, 16 | addu RA, BASE, RA | ins_next1 | sw TISNUM, HI(RA) | sw RD, LO(RA) | ins_next2 break; case BC_KNUM: | // RA = dst*8, RD = num_const*8 | addu RD, KBASE, RD | addu RA, BASE, RA | lw SFRETHI, HI(RD) | lw SFRETLO, LO(RD) | ins_next1 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 break; case BC_KPRI: | // RA = dst*8, RD = primitive_type*8 (~) | srl TMP1, RD, 3 | addu RA, BASE, RA | not TMP0, TMP1 | ins_next1 | sw TMP0, HI(RA) | ins_next2 break; case BC_KNIL: | // RA = base*8, RD = end*8 | addu RA, BASE, RA | sw TISNIL, HI(RA) | addiu RA, RA, 8 | addu RD, BASE, RD |1: | sw TISNIL, HI(RA) | slt AT, RA, RD | bnez AT, <1 |. addiu RA, RA, 8 | ins_next_ break; /* -- Upvalue and function ops ------------------------------------------ */ case BC_UGET: | // RA = dst*8, RD = uvnum*8 | lw LFUNC:RB, FRAME_FUNC(BASE) | srl RD, RD, 1 | addu RD, RD, LFUNC:RB | lw UPVAL:RB, LFUNC:RD->uvptr | ins_next1 | lw TMP1, UPVAL:RB->v | lw SFRETHI, HI(TMP1) | lw SFRETLO, LO(TMP1) | addu RA, BASE, RA | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 break; case BC_USETV: | // RA = uvnum*8, RD = src*8 | lw LFUNC:RB, FRAME_FUNC(BASE) | srl RA, RA, 1 | addu RD, BASE, RD | addu RA, RA, LFUNC:RB | lw UPVAL:RB, LFUNC:RA->uvptr | lw SFRETHI, HI(RD) | lw SFRETLO, LO(RD) | lbu TMP3, UPVAL:RB->marked | lw CARG2, UPVAL:RB->v | andi TMP3, TMP3, LJ_GC_BLACK // isblack(uv) | lbu TMP0, UPVAL:RB->closed | sw SFRETHI, HI(CARG2) | sw SFRETLO, LO(CARG2) | li AT, LJ_GC_BLACK|1 | or TMP3, TMP3, TMP0 | beq TMP3, AT, >2 // Upvalue is closed and black? |. addiu TMP2, SFRETHI, -(LJ_TNUMX+1) |1: | ins_next | |2: // Check if new value is collectable. | sltiu AT, TMP2, LJ_TISGCV - (LJ_TNUMX+1) | beqz AT, <1 // tvisgcv(v) |. nop | lbu TMP3, GCOBJ:SFRETLO->gch.marked | andi TMP3, TMP3, LJ_GC_WHITES // iswhite(v) | beqz TMP3, <1 |. load_got lj_gc_barrieruv | // Crossed a write barrier. Move the barrier forward. | call_intern lj_gc_barrieruv // (global_State *g, TValue *tv) |. addiu CARG1, DISPATCH, GG_DISP2G | b <1 |. nop break; case BC_USETS: | // RA = uvnum*8, RD = str_const*8 (~) | lw LFUNC:RB, FRAME_FUNC(BASE) | srl RA, RA, 1 | srl TMP1, RD, 1 | addu RA, RA, LFUNC:RB | subu TMP1, KBASE, TMP1 | lw UPVAL:RB, LFUNC:RA->uvptr | lw STR:TMP1, -4(TMP1) // KBASE-4-str_const*4 | lbu TMP2, UPVAL:RB->marked | lw CARG2, UPVAL:RB->v | lbu TMP3, STR:TMP1->marked | andi AT, TMP2, LJ_GC_BLACK // isblack(uv) | lbu TMP2, UPVAL:RB->closed | li TMP0, LJ_TSTR | sw STR:TMP1, LO(CARG2) | bnez AT, >2 |. sw TMP0, HI(CARG2) |1: | ins_next | |2: // Check if string is white and ensure upvalue is closed. | beqz TMP2, <1 |. andi AT, TMP3, LJ_GC_WHITES // iswhite(str) | beqz AT, <1 |. load_got lj_gc_barrieruv | // Crossed a write barrier. Move the barrier forward. | call_intern lj_gc_barrieruv // (global_State *g, TValue *tv) |. addiu CARG1, DISPATCH, GG_DISP2G | b <1 |. nop break; case BC_USETN: | // RA = uvnum*8, RD = num_const*8 | lw LFUNC:RB, FRAME_FUNC(BASE) | srl RA, RA, 1 | addu RD, KBASE, RD | addu RA, RA, LFUNC:RB | lw UPVAL:RB, LFUNC:RA->uvptr | lw SFRETHI, HI(RD) | lw SFRETLO, LO(RD) | lw TMP1, UPVAL:RB->v | ins_next1 | sw SFRETHI, HI(TMP1) | sw SFRETLO, LO(TMP1) | ins_next2 break; case BC_USETP: | // RA = uvnum*8, RD = primitive_type*8 (~) | lw LFUNC:RB, FRAME_FUNC(BASE) | srl RA, RA, 1 | srl TMP0, RD, 3 | addu RA, RA, LFUNC:RB | not TMP0, TMP0 | lw UPVAL:RB, LFUNC:RA->uvptr | ins_next1 | lw TMP1, UPVAL:RB->v | sw TMP0, HI(TMP1) | ins_next2 break; case BC_UCLO: | // RA = level*8, RD = target | lw TMP2, L->openupval | branch_RD // Do this first since RD is not saved. | load_got lj_func_closeuv | sw BASE, L->base | beqz TMP2, >1 |. move CARG1, L | call_intern lj_func_closeuv // (lua_State *L, TValue *level) |. addu CARG2, BASE, RA | lw BASE, L->base |1: | ins_next break; case BC_FNEW: | // RA = dst*8, RD = proto_const*8 (~) (holding function prototype) | srl TMP1, RD, 1 | load_got lj_func_newL_gc | subu TMP1, KBASE, TMP1 | lw CARG3, FRAME_FUNC(BASE) | lw CARG2, -4(TMP1) // KBASE-4-tab_const*4 | sw BASE, L->base | sw PC, SAVE_PC | // (lua_State *L, GCproto *pt, GCfuncL *parent) | call_intern lj_func_newL_gc |. move CARG1, L | // Returns GCfuncL *. | lw BASE, L->base | li TMP0, LJ_TFUNC | ins_next1 | addu RA, BASE, RA | sw LFUNC:CRET1, LO(RA) | sw TMP0, HI(RA) | ins_next2 break; /* -- Table ops --------------------------------------------------------- */ case BC_TNEW: case BC_TDUP: | // RA = dst*8, RD = (hbits|asize)*8 | tab_const*8 (~) | lw TMP0, DISPATCH_GL(gc.total)(DISPATCH) | lw TMP1, DISPATCH_GL(gc.threshold)(DISPATCH) | sw BASE, L->base | sw PC, SAVE_PC | sltu AT, TMP0, TMP1 | beqz AT, >5 |1: if (op == BC_TNEW) { | load_got lj_tab_new | srl CARG2, RD, 3 | andi CARG2, CARG2, 0x7ff | li TMP0, 0x801 | addiu AT, CARG2, -0x7ff | srl CARG3, RD, 14 | movz CARG2, TMP0, AT | // (lua_State *L, int32_t asize, uint32_t hbits) | call_intern lj_tab_new |. move CARG1, L | // Returns Table *. } else { | load_got lj_tab_dup | srl TMP1, RD, 1 | subu TMP1, KBASE, TMP1 | move CARG1, L | call_intern lj_tab_dup // (lua_State *L, Table *kt) |. lw CARG2, -4(TMP1) // KBASE-4-str_const*4 | // Returns Table *. } | lw BASE, L->base | ins_next1 | addu RA, BASE, RA | li TMP0, LJ_TTAB | sw TAB:CRET1, LO(RA) | sw TMP0, HI(RA) | ins_next2 |5: | load_got lj_gc_step_fixtop | move MULTRES, RD | call_intern lj_gc_step_fixtop // (lua_State *L) |. move CARG1, L | b <1 |. move RD, MULTRES break; case BC_GGET: | // RA = dst*8, RD = str_const*8 (~) case BC_GSET: | // RA = src*8, RD = str_const*8 (~) | lw LFUNC:TMP2, FRAME_FUNC(BASE) | srl TMP1, RD, 1 | subu TMP1, KBASE, TMP1 | lw TAB:RB, LFUNC:TMP2->env | lw STR:RC, -4(TMP1) // KBASE-4-str_const*4 if (op == BC_GGET) { | b ->BC_TGETS_Z } else { | b ->BC_TSETS_Z } |. addu RA, BASE, RA break; case BC_TGETV: | // RA = dst*8, RB = table*8, RC = key*8 | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | addu CARG2, BASE, RB | addu CARG3, BASE, RC | lw TMP1, HI(CARG2) | lw TMP2, HI(CARG3) | lw TAB:RB, LO(CARG2) | li AT, LJ_TTAB | bne TMP1, AT, ->vmeta_tgetv |. addu RA, BASE, RA | bne TMP2, TISNUM, >5 |. lw RC, LO(CARG3) | lw TMP0, TAB:RB->asize | lw TMP1, TAB:RB->array | sltu AT, RC, TMP0 | sll TMP2, RC, 3 | beqz AT, ->vmeta_tgetv // Integer key and in array part? |. addu TMP2, TMP1, TMP2 | lw SFRETHI, HI(TMP2) | beq SFRETHI, TISNIL, >2 |. lw SFRETLO, LO(TMP2) |1: | ins_next1 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 | |2: // Check for __index if table value is nil. | lw TAB:TMP2, TAB:RB->metatable | beqz TAB:TMP2, <1 // No metatable: done. |. nop | lbu TMP0, TAB:TMP2->nomm | andi TMP0, TMP0, 1<<MM_index | bnez TMP0, <1 // 'no __index' flag set: done. |. nop | b ->vmeta_tgetv |. nop | |5: | li AT, LJ_TSTR | bne TMP2, AT, ->vmeta_tgetv |. nop | b ->BC_TGETS_Z // String key? |. nop break; case BC_TGETS: | // RA = dst*8, RB = table*8, RC = str_const*4 (~) | decode_RB8a RB, INS | decode_RB8b RB | addu CARG2, BASE, RB | decode_RC4a RC, INS | lw TMP0, HI(CARG2) | decode_RC4b RC | li AT, LJ_TTAB | lw TAB:RB, LO(CARG2) | subu CARG3, KBASE, RC | lw STR:RC, -4(CARG3) // KBASE-4-str_const*4 | bne TMP0, AT, ->vmeta_tgets1 |. addu RA, BASE, RA |->BC_TGETS_Z: | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 | lw TMP0, TAB:RB->hmask | lw TMP1, STR:RC->sid | lw NODE:TMP2, TAB:RB->node | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask | sll TMP0, TMP1, 5 | sll TMP1, TMP1, 3 | subu TMP1, TMP0, TMP1 | addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) |1: | lw CARG1, offsetof(Node, key)+HI(NODE:TMP2) | lw TMP0, offsetof(Node, key)+LO(NODE:TMP2) | lw NODE:TMP1, NODE:TMP2->next | lw SFRETHI, offsetof(Node, val)+HI(NODE:TMP2) | addiu CARG1, CARG1, -LJ_TSTR | xor TMP0, TMP0, STR:RC | or AT, CARG1, TMP0 | bnez AT, >4 |. lw TAB:TMP3, TAB:RB->metatable | beq SFRETHI, TISNIL, >5 // Key found, but nil value? |. lw SFRETLO, offsetof(Node, val)+LO(NODE:TMP2) |3: | ins_next1 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 | |4: // Follow hash chain. | bnez NODE:TMP1, <1 |. move NODE:TMP2, NODE:TMP1 | // End of hash chain: key not found, nil result. | |5: // Check for __index if table value is nil. | beqz TAB:TMP3, <3 // No metatable: done. |. li SFRETHI, LJ_TNIL | lbu TMP0, TAB:TMP3->nomm | andi TMP0, TMP0, 1<<MM_index | bnez TMP0, <3 // 'no __index' flag set: done. |. nop | b ->vmeta_tgets |. nop break; case BC_TGETB: | // RA = dst*8, RB = table*8, RC = index*8 | decode_RB8a RB, INS | decode_RB8b RB | addu CARG2, BASE, RB | decode_RDtoRC8 RC, RD | lw CARG1, HI(CARG2) | li AT, LJ_TTAB | lw TAB:RB, LO(CARG2) | addu RA, BASE, RA | bne CARG1, AT, ->vmeta_tgetb |. srl TMP0, RC, 3 | lw TMP1, TAB:RB->asize | lw TMP2, TAB:RB->array | sltu AT, TMP0, TMP1 | beqz AT, ->vmeta_tgetb |. addu RC, TMP2, RC | lw SFRETHI, HI(RC) | beq SFRETHI, TISNIL, >5 |. lw SFRETLO, LO(RC) |1: | ins_next1 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | ins_next2 | |5: // Check for __index if table value is nil. | lw TAB:TMP2, TAB:RB->metatable | beqz TAB:TMP2, <1 // No metatable: done. |. nop | lbu TMP1, TAB:TMP2->nomm | andi TMP1, TMP1, 1<<MM_index | bnez TMP1, <1 // 'no __index' flag set: done. |. nop | b ->vmeta_tgetb // Caveat: preserve TMP0 and CARG2! |. nop break; case BC_TGETR: | // RA = dst*8, RB = table*8, RC = key*8 | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | addu RB, BASE, RB | addu RC, BASE, RC | lw TAB:CARG1, LO(RB) | lw CARG2, LO(RC) | addu RA, BASE, RA | lw TMP0, TAB:CARG1->asize | lw TMP1, TAB:CARG1->array | sltu AT, CARG2, TMP0 | sll TMP2, CARG2, 3 | beqz AT, ->vmeta_tgetr // In array part? |. addu CRET1, TMP1, TMP2 | lw SFARG2HI, HI(CRET1) | lw SFARG2LO, LO(CRET1) |->BC_TGETR_Z: | ins_next1 | sw SFARG2HI, HI(RA) | sw SFARG2LO, LO(RA) | ins_next2 break; case BC_TSETV: | // RA = src*8, RB = table*8, RC = key*8 | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | addu CARG2, BASE, RB | addu CARG3, BASE, RC | lw TMP1, HI(CARG2) | lw TMP2, HI(CARG3) | lw TAB:RB, LO(CARG2) | li AT, LJ_TTAB | bne TMP1, AT, ->vmeta_tsetv |. addu RA, BASE, RA | bne TMP2, TISNUM, >5 |. lw RC, LO(CARG3) | lw TMP0, TAB:RB->asize | lw TMP1, TAB:RB->array | sltu AT, RC, TMP0 | sll TMP2, RC, 3 | beqz AT, ->vmeta_tsetv // Integer key and in array part? |. addu TMP1, TMP1, TMP2 | lw TMP0, HI(TMP1) | lbu TMP3, TAB:RB->marked | lw SFRETHI, HI(RA) | beq TMP0, TISNIL, >3 |. lw SFRETLO, LO(RA) |1: | andi AT, TMP3, LJ_GC_BLACK // isblack(table) | sw SFRETHI, HI(TMP1) | bnez AT, >7 |. sw SFRETLO, LO(TMP1) |2: | ins_next | |3: // Check for __newindex if previous value is nil. | lw TAB:TMP2, TAB:RB->metatable | beqz TAB:TMP2, <1 // No metatable: done. |. nop | lbu TMP2, TAB:TMP2->nomm | andi TMP2, TMP2, 1<<MM_newindex | bnez TMP2, <1 // 'no __newindex' flag set: done. |. nop | b ->vmeta_tsetv |. nop | |5: | li AT, LJ_TSTR | bne TMP2, AT, ->vmeta_tsetv |. nop | b ->BC_TSETS_Z // String key? |. nop | |7: // Possible table write barrier for the value. Skip valiswhite check. | barrierback TAB:RB, TMP3, TMP0, <2 break; case BC_TSETS: | // RA = src*8, RB = table*8, RC = str_const*8 (~) | decode_RB8a RB, INS | decode_RB8b RB | addu CARG2, BASE, RB | decode_RC4a RC, INS | lw TMP0, HI(CARG2) | decode_RC4b RC | li AT, LJ_TTAB | subu CARG3, KBASE, RC | lw TAB:RB, LO(CARG2) | lw STR:RC, -4(CARG3) // KBASE-4-str_const*4 | bne TMP0, AT, ->vmeta_tsets1 |. addu RA, BASE, RA |->BC_TSETS_Z: | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8 | lw TMP0, TAB:RB->hmask | lw TMP1, STR:RC->sid | lw NODE:TMP2, TAB:RB->node | sb r0, TAB:RB->nomm // Clear metamethod cache. | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask | sll TMP0, TMP1, 5 | sll TMP1, TMP1, 3 | subu TMP1, TMP0, TMP1 | addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8) |.if FPU | ldc1 f20, 0(RA) |.else | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) |.endif |1: | lw CARG1, offsetof(Node, key)+HI(NODE:TMP2) | lw TMP0, offsetof(Node, key)+LO(NODE:TMP2) | li AT, LJ_TSTR | lw NODE:TMP1, NODE:TMP2->next | bne CARG1, AT, >5 |. lw CARG2, offsetof(Node, val)+HI(NODE:TMP2) | bne TMP0, STR:RC, >5 |. lbu TMP3, TAB:RB->marked | beq CARG2, TISNIL, >4 // Key found, but nil value? |. lw TAB:TMP0, TAB:RB->metatable |2: | andi AT, TMP3, LJ_GC_BLACK // isblack(table) |.if FPU | bnez AT, >7 |. sdc1 f20, NODE:TMP2->val |.else | sw SFRETHI, NODE:TMP2->val.u32.hi | bnez AT, >7 |. sw SFRETLO, NODE:TMP2->val.u32.lo |.endif |3: | ins_next | |4: // Check for __newindex if previous value is nil. | beqz TAB:TMP0, <2 // No metatable: done. |. nop | lbu TMP0, TAB:TMP0->nomm | andi TMP0, TMP0, 1<<MM_newindex | bnez TMP0, <2 // 'no __newindex' flag set: done. |. nop | b ->vmeta_tsets |. nop | |5: // Follow hash chain. | bnez NODE:TMP1, <1 |. move NODE:TMP2, NODE:TMP1 | // End of hash chain: key not found, add a new one | | // But check for __newindex first. | lw TAB:TMP2, TAB:RB->metatable | beqz TAB:TMP2, >6 // No metatable: continue. |. addiu CARG3, DISPATCH, DISPATCH_GL(tmptv) | lbu TMP0, TAB:TMP2->nomm | andi TMP0, TMP0, 1<<MM_newindex | beqz TMP0, ->vmeta_tsets // 'no __newindex' flag NOT set: check. |. li AT, LJ_TSTR |6: | load_got lj_tab_newkey | sw STR:RC, LO(CARG3) | sw AT, HI(CARG3) | sw BASE, L->base | move CARG2, TAB:RB | sw PC, SAVE_PC | call_intern lj_tab_newkey // (lua_State *L, GCtab *t, TValue *k |. move CARG1, L | // Returns TValue *. | lw BASE, L->base |.if FPU | b <3 // No 2nd write barrier needed. |. sdc1 f20, 0(CRET1) |.else | lw SFARG1HI, HI(RA) | lw SFARG1LO, LO(RA) | sw SFARG1HI, HI(CRET1) | b <3 // No 2nd write barrier needed. |. sw SFARG1LO, LO(CRET1) |.endif | |7: // Possible table write barrier for the value. Skip valiswhite check. | barrierback TAB:RB, TMP3, TMP0, <3 break; case BC_TSETB: | // RA = src*8, RB = table*8, RC = index*8 | decode_RB8a RB, INS | decode_RB8b RB | addu CARG2, BASE, RB | decode_RDtoRC8 RC, RD | lw CARG1, HI(CARG2) | li AT, LJ_TTAB | lw TAB:RB, LO(CARG2) | addu RA, BASE, RA | bne CARG1, AT, ->vmeta_tsetb |. srl TMP0, RC, 3 | lw TMP1, TAB:RB->asize | lw TMP2, TAB:RB->array | sltu AT, TMP0, TMP1 | beqz AT, ->vmeta_tsetb |. addu RC, TMP2, RC | lw TMP1, HI(RC) | lbu TMP3, TAB:RB->marked | beq TMP1, TISNIL, >5 |1: |. lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | andi AT, TMP3, LJ_GC_BLACK // isblack(table) | sw SFRETHI, HI(RC) | bnez AT, >7 |. sw SFRETLO, LO(RC) |2: | ins_next | |5: // Check for __newindex if previous value is nil. | lw TAB:TMP2, TAB:RB->metatable | beqz TAB:TMP2, <1 // No metatable: done. |. nop | lbu TMP1, TAB:TMP2->nomm | andi TMP1, TMP1, 1<<MM_newindex | bnez TMP1, <1 // 'no __newindex' flag set: done. |. nop | b ->vmeta_tsetb // Caveat: preserve TMP0 and CARG2! |. nop | |7: // Possible table write barrier for the value. Skip valiswhite check. | barrierback TAB:RB, TMP3, TMP0, <2 break; case BC_TSETR: | // RA = dst*8, RB = table*8, RC = key*8 | decode_RB8a RB, INS | decode_RB8b RB | decode_RDtoRC8 RC, RD | addu CARG1, BASE, RB | addu CARG3, BASE, RC | lw TAB:CARG2, LO(CARG1) | lw CARG3, LO(CARG3) | lbu TMP3, TAB:CARG2->marked | lw TMP0, TAB:CARG2->asize | lw TMP1, TAB:CARG2->array | andi AT, TMP3, LJ_GC_BLACK // isblack(table) | bnez AT, >7 |. addu RA, BASE, RA |2: | sltu AT, CARG3, TMP0 | sll TMP2, CARG3, 3 | beqz AT, ->vmeta_tsetr // In array part? |. addu CRET1, TMP1, TMP2 |->BC_TSETR_Z: | lw SFARG1HI, HI(RA) | lw SFARG1LO, LO(RA) | ins_next1 | sw SFARG1HI, HI(CRET1) | sw SFARG1LO, LO(CRET1) | ins_next2 | |7: // Possible table write barrier for the value. Skip valiswhite check. | barrierback TAB:CARG2, TMP3, CRET1, <2 break; case BC_TSETM: | // RA = base*8 (table at base-1), RD = num_const*8 (start index) | addu RA, BASE, RA |1: | addu TMP3, KBASE, RD | lw TAB:CARG2, -8+LO(RA) // Guaranteed to be a table. | addiu TMP0, MULTRES, -8 | lw TMP3, LO(TMP3) // Integer constant is in lo-word. | beqz TMP0, >4 // Nothing to copy? |. srl CARG3, TMP0, 3 | addu CARG3, CARG3, TMP3 | lw TMP2, TAB:CARG2->asize | sll TMP1, TMP3, 3 | lbu TMP3, TAB:CARG2->marked | lw CARG1, TAB:CARG2->array | sltu AT, TMP2, CARG3 | bnez AT, >5 |. addu TMP2, RA, TMP0 | addu TMP1, TMP1, CARG1 | andi TMP0, TMP3, LJ_GC_BLACK // isblack(table) |3: // Copy result slots to table. | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | addiu RA, RA, 8 | sltu AT, RA, TMP2 | sw SFRETHI, HI(TMP1) | sw SFRETLO, LO(TMP1) | bnez AT, <3 |. addiu TMP1, TMP1, 8 | bnez TMP0, >7 |. nop |4: | ins_next | |5: // Need to resize array part. | load_got lj_tab_reasize | sw BASE, L->base | sw PC, SAVE_PC | move BASE, RD | call_intern lj_tab_reasize // (lua_State *L, GCtab *t, int nasize) |. move CARG1, L | // Must not reallocate the stack. | move RD, BASE | b <1 |. lw BASE, L->base // Reload BASE for lack of a saved register. | |7: // Possible table write barrier for any value. Skip valiswhite check. | barrierback TAB:CARG2, TMP3, TMP0, <4 break; /* -- Calls and vararg handling ----------------------------------------- */ case BC_CALLM: | // RA = base*8, (RB = (nresults+1)*8,) RC = extra_nargs*8 | decode_RDtoRC8 NARGS8:RC, RD | b ->BC_CALL_Z |. addu NARGS8:RC, NARGS8:RC, MULTRES break; case BC_CALL: | // RA = base*8, (RB = (nresults+1)*8,) RC = (nargs+1)*8 | decode_RDtoRC8 NARGS8:RC, RD |->BC_CALL_Z: | move TMP2, BASE | addu BASE, BASE, RA | li AT, LJ_TFUNC | lw TMP0, HI(BASE) | lw LFUNC:RB, LO(BASE) | addiu BASE, BASE, 8 | bne TMP0, AT, ->vmeta_call |. addiu NARGS8:RC, NARGS8:RC, -8 | ins_call break; case BC_CALLMT: | // RA = base*8, (RB = 0,) RC = extra_nargs*8 | addu NARGS8:RD, NARGS8:RD, MULTRES // BC_CALLT gets RC from RD. | // Fall through. Assumes BC_CALLT follows. break; case BC_CALLT: | // RA = base*8, (RB = 0,) RC = (nargs+1)*8 | addu RA, BASE, RA | li AT, LJ_TFUNC | lw TMP0, HI(RA) | lw LFUNC:RB, LO(RA) | move NARGS8:RC, RD | lw TMP1, FRAME_PC(BASE) | addiu RA, RA, 8 | bne TMP0, AT, ->vmeta_callt |. addiu NARGS8:RC, NARGS8:RC, -8 |->BC_CALLT_Z: | andi TMP0, TMP1, FRAME_TYPE // Caveat: preserve TMP0 until the 'or'. | lbu TMP3, LFUNC:RB->ffid | bnez TMP0, >7 |. xori TMP2, TMP1, FRAME_VARG |1: | sw LFUNC:RB, FRAME_FUNC(BASE) // Copy function down, but keep PC. | sltiu AT, TMP3, 2 // (> FF_C) Calling a fast function? | move TMP2, BASE | beqz NARGS8:RC, >3 |. move TMP3, NARGS8:RC |2: | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | addiu RA, RA, 8 | addiu TMP3, TMP3, -8 | sw SFRETHI, HI(TMP2) | sw SFRETLO, LO(TMP2) | bnez TMP3, <2 |. addiu TMP2, TMP2, 8 |3: | or TMP0, TMP0, AT | beqz TMP0, >5 |. nop |4: | ins_callt | |5: // Tailcall to a fast function with a Lua frame below. | lw INS, -4(TMP1) | decode_RA8a RA, INS | decode_RA8b RA | subu TMP1, BASE, RA | lw LFUNC:TMP1, -8+FRAME_FUNC(TMP1) | lw TMP1, LFUNC:TMP1->pc | b <4 |. lw KBASE, PC2PROTO(k)(TMP1) // Need to prepare KBASE. | |7: // Tailcall from a vararg function. | andi AT, TMP2, FRAME_TYPEP | bnez AT, <1 // Vararg frame below? |. subu TMP2, BASE, TMP2 // Relocate BASE down. | move BASE, TMP2 | lw TMP1, FRAME_PC(TMP2) | b <1 |. andi TMP0, TMP1, FRAME_TYPE break; case BC_ITERC: | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 ((2+1)*8)) | move TMP2, BASE | addu BASE, BASE, RA | li AT, LJ_TFUNC | lw TMP1, -24+HI(BASE) | lw LFUNC:RB, -24+LO(BASE) | lw SFARG1HI, -16+HI(BASE) | lw SFARG1LO, -16+LO(BASE) | lw SFARG2HI, -8+HI(BASE) | lw SFARG2LO, -8+LO(BASE) | sw TMP1, HI(BASE) // Copy callable. | sw LFUNC:RB, LO(BASE) | sw SFARG1HI, 8+HI(BASE) // Copy state. | sw SFARG1LO, 8+LO(BASE) | sw SFARG2HI, 16+HI(BASE) // Copy control var. | sw SFARG2LO, 16+LO(BASE) | addiu BASE, BASE, 8 | bne TMP1, AT, ->vmeta_call |. li NARGS8:RC, 16 // Iterators get 2 arguments. | ins_call break; case BC_ITERN: |.if JIT and ENDIAN_LE | hotloop |.endif |->vm_IITERN: | // RA = base*8, (RB = (nresults+1)*8, RC = (nargs+1)*8 (2+1)*8) | addu RA, BASE, RA | lw TAB:RB, -16+LO(RA) | lw RC, -8+LO(RA) // Get index from control var. | lw TMP0, TAB:RB->asize | lw TMP1, TAB:RB->array | addiu PC, PC, 4 |1: // Traverse array part. | sltu AT, RC, TMP0 | beqz AT, >5 // Index points after array part? |. sll TMP3, RC, 3 | addu TMP3, TMP1, TMP3 | lw SFARG1HI, HI(TMP3) | lw SFARG1LO, LO(TMP3) | lhu RD, -4+OFS_RD(PC) | sw TISNUM, HI(RA) | sw RC, LO(RA) | beq SFARG1HI, TISNIL, <1 // Skip holes in array part. |. addiu RC, RC, 1 | sw SFARG1HI, 8+HI(RA) | sw SFARG1LO, 8+LO(RA) | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | decode_RD4b RD | addu RD, RD, TMP3 | sw RC, -8+LO(RA) // Update control var. | addu PC, PC, RD |3: | ins_next | |5: // Traverse hash part. | lw TMP1, TAB:RB->hmask | subu RC, RC, TMP0 | lw TMP2, TAB:RB->node |6: | sltu AT, TMP1, RC // End of iteration? Branch to ITERL+1. | bnez AT, <3 |. sll TMP3, RC, 5 | sll RB, RC, 3 | subu TMP3, TMP3, RB | addu NODE:TMP3, TMP3, TMP2 | lw SFARG1HI, NODE:TMP3->val.u32.hi | lw SFARG1LO, NODE:TMP3->val.u32.lo | lhu RD, -4+OFS_RD(PC) | beq SFARG1HI, TISNIL, <6 // Skip holes in hash part. |. addiu RC, RC, 1 | lw SFARG2HI, NODE:TMP3->key.u32.hi | lw SFARG2LO, NODE:TMP3->key.u32.lo | lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535) | sw SFARG1HI, 8+HI(RA) | sw SFARG1LO, 8+LO(RA) | addu RC, RC, TMP0 | decode_RD4b RD | addu RD, RD, TMP3 | sw SFARG2HI, HI(RA) | sw SFARG2LO, LO(RA) | addu PC, PC, RD | b <3 |. sw RC, -8+LO(RA) // Update control var. break; case BC_ISNEXT: | // RA = base*8, RD = target (points to ITERN) | addu RA, BASE, RA | srl TMP0, RD, 1 | lw CARG1, -24+HI(RA) | lw CFUNC:CARG2, -24+LO(RA) | addu TMP0, PC, TMP0 | lw CARG3, -16+HI(RA) | lw CARG4, -8+HI(RA) | li AT, LJ_TFUNC | bne CARG1, AT, >5 |. lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535) | lbu CARG2, CFUNC:CARG2->ffid | addiu CARG3, CARG3, -LJ_TTAB | addiu CARG4, CARG4, -LJ_TNIL | or CARG3, CARG3, CARG4 | addiu CARG2, CARG2, -FF_next_N | or CARG2, CARG2, CARG3 | bnez CARG2, >5 |. lui TMP1, (LJ_KEYINDEX >> 16) | addu PC, TMP0, TMP2 | ori TMP1, TMP1, (LJ_KEYINDEX & 0xffff) | sw r0, -8+LO(RA) // Initialize control var. | sw TMP1, -8+HI(RA) |1: | ins_next |5: // Despecialize bytecode if any of the checks fail. | li TMP3, BC_JMP | li TMP1, BC_ITERC | sb TMP3, -4+OFS_OP(PC) | addu PC, TMP0, TMP2 |.if JIT | lb TMP0, OFS_OP(PC) | li AT, BC_ITERN | bne TMP0, AT, >6 |. lhu TMP2, OFS_RD(PC) |.endif | b <1 |. sb TMP1, OFS_OP(PC) |.if JIT |6: // Unpatch JLOOP. | lw TMP0, DISPATCH_J(trace)(DISPATCH) | sll TMP2, TMP2, 2 | addu TMP0, TMP0, TMP2 | lw TRACE:TMP2, 0(TMP0) | lw TMP0, TRACE:TMP2->startins | li AT, -256 | and TMP0, TMP0, AT | or TMP0, TMP0, TMP1 | b <1 |. sw TMP0, 0(PC) |.endif break; case BC_VARG: | // RA = base*8, RB = (nresults+1)*8, RC = numparams*8 | lw TMP0, FRAME_PC(BASE) | decode_RDtoRC8 RC, RD | decode_RB8a RB, INS | addu RC, BASE, RC | decode_RB8b RB | addu RA, BASE, RA | addiu RC, RC, FRAME_VARG | addu TMP2, RA, RB | addiu TMP3, BASE, -8 // TMP3 = vtop | subu RC, RC, TMP0 // RC = vbase | // Note: RC may now be even _above_ BASE if nargs was < numparams. | beqz RB, >5 // Copy all varargs? |. subu TMP1, TMP3, RC | addiu TMP2, TMP2, -16 |1: // Copy vararg slots to destination slots. | lw CARG1, HI(RC) | sltu AT, RC, TMP3 | lw CARG2, LO(RC) | addiu RC, RC, 8 | movz CARG1, TISNIL, AT | sw CARG1, HI(RA) | sw CARG2, LO(RA) | sltu AT, RA, TMP2 | bnez AT, <1 |. addiu RA, RA, 8 |3: | ins_next | |5: // Copy all varargs. | lw TMP0, L->maxstack | blez TMP1, <3 // No vararg slots? |. li MULTRES, 8 // MULTRES = (0+1)*8 | addu TMP2, RA, TMP1 | sltu AT, TMP0, TMP2 | bnez AT, >7 |. addiu MULTRES, TMP1, 8 |6: | lw SFRETHI, HI(RC) | lw SFRETLO, LO(RC) | addiu RC, RC, 8 | sw SFRETHI, HI(RA) | sw SFRETLO, LO(RA) | sltu AT, RC, TMP3 | bnez AT, <6 // More vararg slots? |. addiu RA, RA, 8 | b <3 |. nop | |7: // Grow stack for varargs. | load_got lj_state_growstack | sw RA, L->top | subu RA, RA, BASE | sw BASE, L->base | subu BASE, RC, BASE // Need delta, because BASE may change. | sw PC, SAVE_PC | srl CARG2, TMP1, 3 | call_intern lj_state_growstack // (lua_State *L, int n) |. move CARG1, L | move RC, BASE | lw BASE, L->base | addu RA, BASE, RA | addu RC, BASE, RC | b <6 |. addiu TMP3, BASE, -8 break; /* -- Returns ----------------------------------------------------------- */ case BC_RETM: | // RA = results*8, RD = extra_nresults*8 | addu RD, RD, MULTRES // MULTRES >= 8, so RD >= 8. | // Fall through. Assumes BC_RET follows. break; case BC_RET: | // RA = results*8, RD = (nresults+1)*8 | lw PC, FRAME_PC(BASE) | addu RA, BASE, RA | move MULTRES, RD |1: | andi TMP0, PC, FRAME_TYPE | bnez TMP0, ->BC_RETV_Z |. xori TMP1, PC, FRAME_VARG | |->BC_RET_Z: | // BASE = base, RA = resultptr, RD = (nresults+1)*8, PC = return | lw INS, -4(PC) | addiu TMP2, BASE, -8 | addiu RC, RD, -8 | decode_RA8a TMP0, INS | decode_RB8a RB, INS | decode_RA8b TMP0 | decode_RB8b RB | addu TMP3, TMP2, RB | beqz RC, >3 |. subu BASE, TMP2, TMP0 |2: | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) | addiu RA, RA, 8 | addiu RC, RC, -8 | sw SFRETHI, HI(TMP2) | sw SFRETLO, LO(TMP2) | bnez RC, <2 |. addiu TMP2, TMP2, 8 |3: | addiu TMP3, TMP3, -8 |5: | sltu AT, TMP2, TMP3 | bnez AT, >6 |. lw LFUNC:TMP1, FRAME_FUNC(BASE) | ins_next1 | lw TMP1, LFUNC:TMP1->pc | lw KBASE, PC2PROTO(k)(TMP1) | ins_next2 | |6: // Fill up results with nil. | sw TISNIL, HI(TMP2) | b <5 |. addiu TMP2, TMP2, 8 | |->BC_RETV_Z: // Non-standard return case. | andi TMP2, TMP1, FRAME_TYPEP | bnez TMP2, ->vm_return |. nop | // Return from vararg function: relocate BASE down. | subu BASE, BASE, TMP1 | b <1 |. lw PC, FRAME_PC(BASE) break; case BC_RET0: case BC_RET1: | // RA = results*8, RD = (nresults+1)*8 | lw PC, FRAME_PC(BASE) | addu RA, BASE, RA | move MULTRES, RD | andi TMP0, PC, FRAME_TYPE | bnez TMP0, ->BC_RETV_Z |. xori TMP1, PC, FRAME_VARG | | lw INS, -4(PC) | addiu TMP2, BASE, -8 if (op == BC_RET1) { | lw SFRETHI, HI(RA) | lw SFRETLO, LO(RA) } | decode_RB8a RB, INS | decode_RA8a RA, INS | decode_RB8b RB | decode_RA8b RA if (op == BC_RET1) { | sw SFRETHI, HI(TMP2) | sw SFRETLO, LO(TMP2) } | subu BASE, TMP2, RA |5: | sltu AT, RD, RB | bnez AT, >6 |. lw LFUNC:TMP1, FRAME_FUNC(BASE) | ins_next1 | lw TMP1, LFUNC:TMP1->pc | lw KBASE, PC2PROTO(k)(TMP1) | ins_next2 | |6: // Fill up results with nil. | addiu TMP2, TMP2, 8 | addiu RD, RD, 8 | b <5 if (op == BC_RET1) { |. sw TISNIL, HI(TMP2) } else { |. sw TISNIL, -8+HI(TMP2) } break; /* -- Loops and branches ------------------------------------------------ */ case BC_FORL: |.if JIT | hotloop |.endif | // Fall through. Assumes BC_IFORL follows. break; case BC_JFORI: case BC_JFORL: #if !LJ_HASJIT break; #endif case BC_FORI: case BC_IFORL: | // RA = base*8, RD = target (after end of loop or start of loop) vk = (op == BC_IFORL || op == BC_JFORL); | addu RA, BASE, RA | lw SFARG1HI, FORL_IDX*8+HI(RA) | lw SFARG1LO, FORL_IDX*8+LO(RA) if (op != BC_JFORL) { | srl RD, RD, 1 | lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535) | addu TMP2, RD, TMP2 } if (!vk) { | lw SFARG2HI, FORL_STOP*8+HI(RA) | lw SFARG2LO, FORL_STOP*8+LO(RA) | bne SFARG1HI, TISNUM, >5 |. lw SFRETHI, FORL_STEP*8+HI(RA) | xor AT, SFARG2HI, TISNUM | lw SFRETLO, FORL_STEP*8+LO(RA) | xor TMP0, SFRETHI, TISNUM | or AT, AT, TMP0 | bnez AT, ->vmeta_for |. slt AT, SFRETLO, r0 | slt CRET1, SFARG2LO, SFARG1LO | slt TMP1, SFARG1LO, SFARG2LO | movn CRET1, TMP1, AT } else { | bne SFARG1HI, TISNUM, >5 |. lw SFARG2LO, FORL_STEP*8+LO(RA) | lw SFRETLO, FORL_STOP*8+LO(RA) | move TMP3, SFARG1LO | addu SFARG1LO, SFARG1LO, SFARG2LO | xor TMP0, SFARG1LO, TMP3 | xor TMP1, SFARG1LO, SFARG2LO | and TMP0, TMP0, TMP1 | slt TMP1, SFARG1LO, SFRETLO | slt CRET1, SFRETLO, SFARG1LO | slt AT, SFARG2LO, r0 | slt TMP0, TMP0, r0 // ((y^a) & (y^b)) < 0: overflow. | movn CRET1, TMP1, AT | or CRET1, CRET1, TMP0 } |1: if (op == BC_FORI) { | movz TMP2, r0, CRET1 | addu PC, PC, TMP2 } else if (op == BC_JFORI) { | addu PC, PC, TMP2 | lhu RD, -4+OFS_RD(PC) } else if (op == BC_IFORL) { | movn TMP2, r0, CRET1 | addu PC, PC, TMP2 } if (vk) { | sw SFARG1HI, FORL_IDX*8+HI(RA) | sw SFARG1LO, FORL_IDX*8+LO(RA) } | ins_next1 | sw SFARG1HI, FORL_EXT*8+HI(RA) | sw SFARG1LO, FORL_EXT*8+LO(RA) |2: if (op == BC_JFORI) { | beqz CRET1, =>BC_JLOOP |. decode_RD8b RD } else if (op == BC_JFORL) { | beqz CRET1, =>BC_JLOOP } | ins_next2 | |5: // FP loop. |.if FPU if (!vk) { | ldc1 f0, FORL_IDX*8(RA) | ldc1 f2, FORL_STOP*8(RA) | sltiu TMP0, SFARG1HI, LJ_TISNUM | sltiu TMP1, SFARG2HI, LJ_TISNUM | sltiu AT, SFRETHI, LJ_TISNUM | and TMP0, TMP0, TMP1 | and AT, AT, TMP0 | beqz AT, ->vmeta_for |. slt TMP3, SFRETHI, r0 | c.ole.d 0, f0, f2 | c.ole.d 1, f2, f0 | li CRET1, 1 | movt CRET1, r0, 0 | movt AT, r0, 1 | b <1 |. movn CRET1, AT, TMP3 } else { | ldc1 f0, FORL_IDX*8(RA) | ldc1 f4, FORL_STEP*8(RA) | ldc1 f2, FORL_STOP*8(RA) | lw SFARG2HI, FORL_STEP*8+HI(RA) | add.d f0, f0, f4 | c.ole.d 0, f0, f2 | c.ole.d 1, f2, f0 | slt TMP3, SFARG2HI, r0 | li CRET1, 1 | li AT, 1 | movt CRET1, r0, 0 | movt AT, r0, 1 | movn CRET1, AT, TMP3 if (op == BC_IFORL) { | movn TMP2, r0, CRET1 | addu PC, PC, TMP2 } | sdc1 f0, FORL_IDX*8(RA) | ins_next1 | b <2 |. sdc1 f0, FORL_EXT*8(RA) } |.else if (!vk) { | sltiu TMP0, SFARG1HI, LJ_TISNUM | sltiu TMP1, SFARG2HI, LJ_TISNUM | sltiu AT, SFRETHI, LJ_TISNUM | and TMP0, TMP0, TMP1 | and AT, AT, TMP0 | beqz AT, ->vmeta_for |. nop | bal ->vm_sfcmpolex |. move TMP3, SFRETHI | b <1 |. nop } else { | lw SFARG2HI, FORL_STEP*8+HI(RA) | load_got __adddf3 | call_extern |. sw TMP2, ARG5 | lw SFARG2HI, FORL_STOP*8+HI(RA) | lw SFARG2LO, FORL_STOP*8+LO(RA) | move SFARG1HI, SFRETHI | move SFARG1LO, SFRETLO | bal ->vm_sfcmpolex |. lw TMP3, FORL_STEP*8+HI(RA) if ( op == BC_JFORL ) { | lhu RD, -4+OFS_RD(PC) | lw TMP2, ARG5 | b <1 |. decode_RD8b RD } else { | b <1 |. lw TMP2, ARG5 } } |.endif break; case BC_ITERL: |.if JIT | hotloop |.endif | // Fall through. Assumes BC_IITERL follows. break; case BC_JITERL: #if !LJ_HASJIT break; #endif case BC_IITERL: | // RA = base*8, RD = target | addu RA, BASE, RA | lw TMP1, HI(RA) | beq TMP1, TISNIL, >1 // Stop if iterator returned nil. |. lw TMP2, LO(RA) if (op == BC_JITERL) { | sw TMP1, -8+HI(RA) | b =>BC_JLOOP |. sw TMP2, -8+LO(RA) } else { | branch_RD // Otherwise save control var + branch. | sw TMP1, -8+HI(RA) | sw TMP2, -8+LO(RA) } |1: | ins_next break; case BC_LOOP: | // RA = base*8, RD = target (loop extent) | // Note: RA/RD is only used by trace recorder to determine scope/extent | // This opcode does NOT jump, it's only purpose is to detect a hot loop. |.if JIT | hotloop |.endif | // Fall through. Assumes BC_ILOOP follows. break; case BC_ILOOP: | // RA = base*8, RD = target (loop extent) | ins_next break; case BC_JLOOP: |.if JIT | // RA = base*8 (ignored), RD = traceno*8 | lw TMP1, DISPATCH_J(trace)(DISPATCH) | srl RD, RD, 1 | li AT, 0 | addu TMP1, TMP1, RD | // Traces on MIPS don't store the trace number, so use 0. | sw AT, DISPATCH_GL(vmstate)(DISPATCH) | lw TRACE:TMP2, 0(TMP1) | sw BASE, DISPATCH_GL(jit_base)(DISPATCH) | lw TMP2, TRACE:TMP2->mcode | sw L, DISPATCH_GL(tmpbuf.L)(DISPATCH) | jr TMP2 |. addiu JGL, DISPATCH, GG_DISP2G+32768 |.endif break; case BC_JMP: | // RA = base*8 (only used by trace recorder), RD = target | branch_RD | ins_next break; /* -- Function headers -------------------------------------------------- */ case BC_FUNCF: |.if JIT | hotcall |.endif case BC_FUNCV: /* NYI: compiled vararg functions. */ | // Fall through. Assumes BC_IFUNCF/BC_IFUNCV follow. break; case BC_JFUNCF: #if !LJ_HASJIT break; #endif case BC_IFUNCF: | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 | lw TMP2, L->maxstack | lbu TMP1, -4+PC2PROTO(numparams)(PC) | lw KBASE, -4+PC2PROTO(k)(PC) | sltu AT, TMP2, RA | bnez AT, ->vm_growstack_l |. sll TMP1, TMP1, 3 if (op != BC_JFUNCF) { | ins_next1 } |2: | sltu AT, NARGS8:RC, TMP1 // Check for missing parameters. | bnez AT, >3 |. addu AT, BASE, NARGS8:RC if (op == BC_JFUNCF) { | decode_RD8a RD, INS | b =>BC_JLOOP |. decode_RD8b RD } else { | ins_next2 } | |3: // Clear missing parameters. | sw TISNIL, HI(AT) | b <2 |. addiu NARGS8:RC, NARGS8:RC, 8 break; case BC_JFUNCV: #if !LJ_HASJIT break; #endif | NYI // NYI: compiled vararg functions break; /* NYI: compiled vararg functions. */ case BC_IFUNCV: | // BASE = new base, RA = BASE+framesize*8, RB = LFUNC, RC = nargs*8 | addu TMP1, BASE, RC | lw TMP2, L->maxstack | addu TMP0, RA, RC | sw LFUNC:RB, LO(TMP1) // Store copy of LFUNC. | addiu TMP3, RC, 8+FRAME_VARG | sltu AT, TMP0, TMP2 | lw KBASE, -4+PC2PROTO(k)(PC) | beqz AT, ->vm_growstack_l |. sw TMP3, HI(TMP1) // Store delta + FRAME_VARG. | lbu TMP2, -4+PC2PROTO(numparams)(PC) | move RA, BASE | move RC, TMP1 | ins_next1 | beqz TMP2, >3 |. addiu BASE, TMP1, 8 |1: | lw TMP0, HI(RA) | lw TMP3, LO(RA) | sltu AT, RA, RC // Less args than parameters? | move CARG1, TMP0 | movz TMP0, TISNIL, AT // Clear missing parameters. | movn CARG1, TISNIL, AT // Clear old fixarg slot (help the GC). | sw TMP3, 8+LO(TMP1) | addiu TMP2, TMP2, -1 | sw TMP0, 8+HI(TMP1) | addiu TMP1, TMP1, 8 | sw CARG1, HI(RA) | bnez TMP2, <1 |. addiu RA, RA, 8 |3: | ins_next2 break; case BC_FUNCC: case BC_FUNCCW: | // BASE = new base, RA = BASE+framesize*8, RB = CFUNC, RC = nargs*8 if (op == BC_FUNCC) { | lw CFUNCADDR, CFUNC:RB->f } else { | lw CFUNCADDR, DISPATCH_GL(wrapf)(DISPATCH) } | addu TMP1, RA, NARGS8:RC | lw TMP2, L->maxstack | addu RC, BASE, NARGS8:RC | sw BASE, L->base | sltu AT, TMP2, TMP1 | sw RC, L->top | li_vmstate C if (op == BC_FUNCCW) { | lw CARG2, CFUNC:RB->f } | bnez AT, ->vm_growstack_c // Need to grow stack. |. move CARG1, L | jalr CFUNCADDR // (lua_State *L [, lua_CFunction f]) |. st_vmstate | // Returns nresults. | lw BASE, L->base | sll RD, CRET1, 3 | lw TMP1, L->top | li_vmstate INTERP | lw PC, FRAME_PC(BASE) // Fetch PC of caller. | subu RA, TMP1, RD // RA = L->top - nresults*8 | sw L, DISPATCH_GL(cur_L)(DISPATCH) | b ->vm_returnc |. st_vmstate break; /* ---------------------------------------------------------------------- */ default: fprintf(stderr, "Error: undefined opcode BC_%s\n", bc_names[op]); exit(2); break; } } static int build_backend(BuildCtx *ctx) { int op; dasm_growpc(Dst, BC__MAX); build_subroutines(ctx); |.code_op for (op = 0; op < BC__MAX; op++) build_ins(ctx, (BCOp)op, op); return BC__MAX; } /* Emit pseudo frame-info for all assembler functions. */ static void emit_asm_debug(BuildCtx *ctx) { int fcofs = (int)((uint8_t *)ctx->glob[GLOB_vm_ffi_call] - ctx->code); int i; switch (ctx->mode) { case BUILD_elfasm: fprintf(ctx->fp, "\t.section .debug_frame,\"\",@progbits\n"); fprintf(ctx->fp, ".Lframe0:\n" "\t.4byte .LECIE0-.LSCIE0\n" ".LSCIE0:\n" "\t.4byte 0xffffffff\n" "\t.byte 0x1\n" "\t.string \"\"\n" "\t.uleb128 0x1\n" "\t.sleb128 -4\n" "\t.byte 31\n" "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" "\t.align 2\n" ".LECIE0:\n\n"); fprintf(ctx->fp, ".LSFDE0:\n" "\t.4byte .LEFDE0-.LASFDE0\n" ".LASFDE0:\n" "\t.4byte .Lframe0\n" "\t.4byte .Lbegin\n" "\t.4byte %d\n" "\t.byte 0xe\n\t.uleb128 %d\n" "\t.byte 0x9f\n\t.sleb128 1\n" "\t.byte 0x9e\n\t.sleb128 2\n", fcofs, CFRAME_SIZE); for (i = 23; i >= 16; i--) fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 26-i); #if !LJ_SOFTFP for (i = 30; i >= 20; i -= 2) fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+32+i, 42-i); #endif fprintf(ctx->fp, "\t.align 2\n" ".LEFDE0:\n\n"); #if LJ_HASFFI fprintf(ctx->fp, ".LSFDE1:\n" "\t.4byte .LEFDE1-.LASFDE1\n" ".LASFDE1:\n" "\t.4byte .Lframe0\n" "\t.4byte lj_vm_ffi_call\n" "\t.4byte %d\n" "\t.byte 0x9f\n\t.uleb128 1\n" "\t.byte 0x90\n\t.uleb128 2\n" "\t.byte 0xd\n\t.uleb128 0x10\n" "\t.align 2\n" ".LEFDE1:\n\n", (int)ctx->codesz - fcofs); #endif #if !LJ_NO_UNWIND fprintf(ctx->fp, "\t.section .eh_frame,\"aw\",@progbits\n"); fprintf(ctx->fp, "\t.globl lj_err_unwind_dwarf\n" ".Lframe1:\n" "\t.4byte .LECIE1-.LSCIE1\n" ".LSCIE1:\n" "\t.4byte 0\n" "\t.byte 0x1\n" "\t.string \"zPR\"\n" "\t.uleb128 0x1\n" "\t.sleb128 -4\n" "\t.byte 31\n" "\t.uleb128 6\n" /* augmentation length */ "\t.byte 0\n" "\t.4byte lj_err_unwind_dwarf\n" "\t.byte 0\n" "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" "\t.align 2\n" ".LECIE1:\n\n"); fprintf(ctx->fp, ".LSFDE2:\n" "\t.4byte .LEFDE2-.LASFDE2\n" ".LASFDE2:\n" "\t.4byte .LASFDE2-.Lframe1\n" "\t.4byte .Lbegin\n" "\t.4byte %d\n" "\t.uleb128 0\n" /* augmentation length */ "\t.byte 0xe\n\t.uleb128 %d\n" "\t.byte 0x9f\n\t.sleb128 1\n" "\t.byte 0x9e\n\t.sleb128 2\n", fcofs, CFRAME_SIZE); for (i = 23; i >= 16; i--) fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 26-i); #if !LJ_SOFTFP for (i = 30; i >= 20; i -= 2) fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+32+i, 42-i); #endif fprintf(ctx->fp, "\t.align 2\n" ".LEFDE2:\n\n"); #if LJ_HASFFI fprintf(ctx->fp, ".Lframe2:\n" "\t.4byte .LECIE2-.LSCIE2\n" ".LSCIE2:\n" "\t.4byte 0\n" "\t.byte 0x1\n" "\t.string \"zR\"\n" "\t.uleb128 0x1\n" "\t.sleb128 -4\n" "\t.byte 31\n" "\t.uleb128 1\n" /* augmentation length */ "\t.byte 0\n" "\t.byte 0xc\n\t.uleb128 29\n\t.uleb128 0\n" "\t.align 2\n" ".LECIE2:\n\n"); fprintf(ctx->fp, ".LSFDE3:\n" "\t.4byte .LEFDE3-.LASFDE3\n" ".LASFDE3:\n" "\t.4byte .LASFDE3-.Lframe2\n" "\t.4byte lj_vm_ffi_call\n" "\t.4byte %d\n" "\t.uleb128 0\n" /* augmentation length */ "\t.byte 0x9f\n\t.uleb128 1\n" "\t.byte 0x90\n\t.uleb128 2\n" "\t.byte 0xd\n\t.uleb128 0x10\n" "\t.align 2\n" ".LEFDE3:\n\n", (int)ctx->codesz - fcofs); #endif #endif break; default: break; } }