|// Low-level VM code for PowerPC CPUs. |// Bytecode interpreter, fast functions and helper functions. |// Copyright (C) 2005-2010 Mike Pall. See Copyright Notice in luajit.h | |.arch ppc |.section code_op, code_sub | |.actionlist build_actionlist |.globals GLOB_ |.globalnames globnames |.externnames extnames | |.if not SPE |.error "No support for plain PowerPC CPUs (yet)" |.endif | |// Note: The ragged indentation of the instructions is intentional. |// The starting columns indicate data dependencies. | |//----------------------------------------------------------------------- | |// Fixed register assignments for the interpreter. |// Don't use: r1 = sp, r2 and r13 = reserved and/or small data area ptr | |// The following must be C callee-save (but BASE is often refetched). |.define BASE, r14 // Base of current Lua stack frame. |.define KBASE, r15 // Constants of current Lua function. |.define PC, r16 // Next PC. |.define DISPATCH, r17 // Opcode dispatch table. |.define LREG, r18 // Register holding lua_State (also in SAVE_L). | |// Constants for vectorized type-comparisons (hi+low GPR). C callee-save. |.define TISNUM, r21 |.if SPE |.define TISSTR, r22 |.define TISTAB, r23 |.define TISFUNC, r24 |.define TISNIL, r25 |.endif | |// The following temporaries are not saved across C calls, except for RA. |.define RA, r19 // Callee-save. |.define RB, r10 |.define RC, r11 |.define RD, r12 |.define INS, r7 // Overlaps CARG5. | |.define TMP0, r0 |.define TMP1, r8 |.define TMP2, r9 |.define TMP3, r6 // Overlaps CARG4. | |// Saved temporaries. |.define SAVE0, r20 | |// Calling conventions. |.define CARG1, r3 |.define CARG2, r4 |.define CARG3, r5 |.define CARG4, r6 // Overlaps TMP3. |.define CARG5, r7 // Overlaps INS. | |.define CRET1, r3 |.define CRET2, r4 | |// Stack layout while in interpreter. Must match with lj_frame.h. |.if SPE |.define SAVE_LR, 180(sp) |.define CFRAME_SPACE, 176 // Delta for sp. |// Back chain for sp: 176(sp) <-- sp entering interpreter |.define SAVE_r31, 168(sp) // 64 bit register saves. |.define SAVE_r30, 160(sp) |.define SAVE_r29, 152(sp) |.define SAVE_r28, 144(sp) |.define SAVE_r27, 136(sp) |.define SAVE_r26, 128(sp) |.define SAVE_r25, 120(sp) |.define SAVE_r24, 112(sp) |.define SAVE_r23, 104(sp) |.define SAVE_r22, 96(sp) |.define SAVE_r21, 88(sp) |.define SAVE_r20, 80(sp) |.define SAVE_r19, 72(sp) |.define SAVE_r18, 64(sp) |.define SAVE_r17, 56(sp) |.define SAVE_r16, 48(sp) |.define SAVE_r15, 40(sp) |.define SAVE_r14, 32(sp) |.define SAVE_ERRF, 28(sp) // 32 bit C frame info. |.define SAVE_NRES, 24(sp) |.define SAVE_CFRAME, 20(sp) |.define SAVE_L, 16(sp) |.define SAVE_PC, 12(sp) |.define SAVE_MULTRES, 8(sp) |// Next frame lr: 4(sp) |// Back chain for sp: 0(sp) <-- sp while in interpreter | |.macro save_, reg; evstdd reg, SAVE_..reg; .endmacro |.macro rest_, reg; evldd reg, SAVE_..reg; .endmacro |.endif | |.macro saveregs | stwu sp, -CFRAME_SPACE(sp) | save_ r14; save_ r15; save_ r16; save_ r17; save_ r18; save_ r19 | mflr r0 | save_ r20; save_ r21; save_ r22; save_ r23; save_ r24; save_ r25 | stw r0, SAVE_LR | save_ r26; save_ r27; save_ r28; save_ r29; save_ r30; save_ r31 |.endmacro | |.macro restoreregs | lwz r0, SAVE_LR | rest_ r14; rest_ r15; rest_ r16; rest_ r17; rest_ r18; rest_ r19 | mtlr r0 | rest_ r20; rest_ r21; rest_ r22; rest_ r23; rest_ r24; rest_ r25 | rest_ r26; rest_ r27; rest_ r28; rest_ r29; rest_ r30; rest_ r31 | addi 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 | |//----------------------------------------------------------------------- | |// These basic macros should really be part of DynASM. |.macro srwi, rx, ry, n; rlwinm rx, ry, 32-n, n, 31; .endmacro |.macro slwi, rx, ry, n; rlwinm rx, ry, n, 0, 31-n; .endmacro |.macro subi, rx, ry, i; addi rx, ry, -i; .endmacro | |// Trap for not-yet-implemented parts. |.macro NYI; tw 4, sp, sp; .endmacro | |//----------------------------------------------------------------------- | |// Access to frame relative to BASE. |.define FRAME_PC, -8 |.define FRAME_FUNC, -4 | |// Instruction decode. |.macro decode_OP4, dst, ins; rlwinm dst, ins, 2, 22, 29; .endmacro |.macro decode_RA8, dst, ins; rlwinm dst, ins, 27, 21, 28; .endmacro |.macro decode_RB8, dst, ins; rlwinm dst, ins, 11, 21, 28; .endmacro |.macro decode_RC8, dst, ins; rlwinm dst, ins, 19, 21, 28; .endmacro |.macro decode_RD8, dst, ins; rlwinm dst, ins, 19, 13, 28; .endmacro | |.macro decode_OP1, dst, ins; rlwinm dst, ins, 0, 24, 31; .endmacro |.macro decode_RD4, dst, ins; rlwinm dst, ins, 18, 14, 29; .endmacro | |// Instruction decode+dispatch. |.macro ins_NEXT | lwz INS, 0(PC) | addi PC, PC, 4 | decode_OP4 TMP0, INS | decode_RB8 RB, INS | lwzx TMP0, DISPATCH, TMP0 | decode_RD8 RD, INS | decode_RC8 RC, INS | mtctr TMP0 | decode_RA8 RA, INS | bctr |.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 |.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_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 | lwz PC, LFUNC:RB->pc | lwz INS, 0(PC) | addi PC, PC, 4 | decode_OP4 TMP0, INS | decode_RA8 RA, INS | lwzx TMP0, DISPATCH, TMP0 | add RA, RA, BASE | mtctr TMP0 | bctr |.endmacro | |.macro ins_call | // BASE = new base, RB = LFUNC/CFUNC, RC = nargs*8, PC = caller PC | stw PC, FRAME_PC(BASE) | ins_callt |.endmacro | |//----------------------------------------------------------------------- | |// Macros to test operand types. |.if SPE |.macro checknum, reg; evcmpltu reg, TISNUM; .endmacro |.macro checkstr, reg; evcmpeq reg, TISSTR; .endmacro |.macro checktab, reg; evcmpeq reg, TISTAB; .endmacro |.macro checkfunc, reg; evcmpeq reg, TISFUNC; .endmacro |.macro checknil, reg; evcmpeq reg, TISNIL; .endmacro |.macro checkok, label; blt label; .endmacro |.macro checkfail, label; bge label; .endmacro |.macro checkanyfail, label; bns label; .endmacro |.endif | |// 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 PC2PROTO(field) ((int)offsetof(GCproto, field)-(int)sizeof(GCproto)) | |.macro hotloop | NYI |.endmacro | |.macro hotcall | NYI |.endmacro | |// Set current VM state. Uses TMP0. |.macro li_vmstate, st; li TMP0, ~LJ_VMST_..st; .endmacro |.macro st_vmstate; stw TMP0, DISPATCH_GL(vmstate)(DISPATCH); .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. TMP0, PC, FRAME_P | evsplati TMP1, LJ_TTRUE | beq ->cont_dispatch | | // Return from pcall or xpcall fast func. | lwz PC, FRAME_PC(TMP2) // Fetch PC of previous frame. | mr BASE, TMP2 // Restore caller base. | // Prepending may overwrite the pcall frame, so do it at the end. | stwu TMP1, FRAME_PC(RA) // Prepend true to results. | |->vm_returnc: | andi. TMP0, PC, FRAME_TYPE | addi RD, RD, 8 // RD = (nresults+1)*8. | stw RD, SAVE_MULTRES | beq ->BC_RET_Z // Handle regular return to Lua. | |->vm_return: | // BASE = base, RA = resultptr, RD/MULTRES = (nresults+1)*8, PC = return | // TMP0 = PC & FRAME_TYPE | cmpwi TMP0, FRAME_C | rlwinm TMP2, PC, 0, 0, 28 | li_vmstate C | sub TMP2, BASE, TMP2 // TMP2 = previous base. | bne ->vm_returnp | | addic. TMP1, RD, -8 | stw TMP2, L->base | lwz TMP2, SAVE_NRES | subi BASE, BASE, 8 | st_vmstate | slwi TMP2, TMP2, 3 | beq >2 |1: | addic. TMP1, TMP1, -8 | evldd TMP0, 0(RA) | addi RA, RA, 8 | evstdd TMP0, 0(BASE) | addi BASE, BASE, 8 | bne <1 | |2: | cmpw TMP2, RD // More/less results wanted? | bne >6 |3: | stw BASE, L->top // Store new top. | |->vm_leave_cp: | lwz TMP0, SAVE_CFRAME // Restore previous C frame. | li CRET1, 0 // Ok return status for vm_pcall. | stw TMP0, L->cframe | |->vm_leave_unw: | restoreregs | blr | |6: | ble >7 // Less results wanted? | // More results wanted. Check stack size and fill up results with nil. | lwz TMP1, L->maxstack | cmplw BASE, TMP1 | bge >8 | evstdd TISNIL, 0(BASE) | addi RD, RD, 8 | addi BASE, BASE, 8 | b <2 | |7: // Less results wanted. | sub TMP0, RD, TMP2 | cmpwi TMP2, 0 // LUA_MULTRET+1 case? | sub TMP0, BASE, TMP0 // Subtract the difference. | iseleq BASE, BASE, TMP0 // Either keep top or shrink it. | b <3 | |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. | stw BASE, L->top // Save current top held in BASE (yes). | mr SAVE0, RD | mr CARG2, TMP2 | mr CARG1, L | bl extern lj_state_growstack // (lua_State *L, int n) | lwz TMP2, SAVE_NRES | mr RD, SAVE0 | slwi TMP2, TMP2, 3 | lwz BASE, L->top // Need the (realloced) L->top in BASE. | b <2 | |->vm_unwind_c: // Unwind C stack, return from vm_pcall. | NYI |->vm_unwind_c_eh: // Landing pad for external unwinder. | NYI | |->vm_unwind_ff: // Unwind C stack, return from ff pcall. | NYI |->vm_unwind_ff_eh: // Landing pad for external unwinder. | NYI | |//----------------------------------------------------------------------- |//-- Grow stack for calls ----------------------------------------------- |//----------------------------------------------------------------------- | |->vm_growstack_c: // Grow stack for C function. | li CARG2, LUA_MINSTACK | b >2 | |->vm_growstack_l: // Grow stack for Lua function. | // BASE = new base, RA = BASE+framesize*8, RC = nargs*8, PC = first PC | add RC, BASE, RC | sub RA, RA, BASE | stw BASE, L->base | addi PC, PC, 4 // Must point after first instruction. | stw RC, L->top | srwi CARG2, RA, 3 |2: | // L->base = new base, L->top = top | mr CARG1, L | bl extern lj_state_growstack // (lua_State *L, int n) | lwz BASE, L->base | lwz RC, L->top | lwz LFUNC:RB, FRAME_FUNC(BASE) | sub 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. | NYI | |->vm_pcall: // Setup protected C frame and enter VM. | // (lua_State *L, TValue *base, int nres1, ptrdiff_t ef) | saveregs | li PC, FRAME_CP | stw CARG4, SAVE_ERRF | b >1 | |->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). | lwz TMP1, L:CARG1->cframe | stw CARG3, SAVE_NRES | mr L, CARG1 | stw CARG1, SAVE_L | mr BASE, CARG2 | stw sp, L->cframe // Add our C frame to cframe chain. | lwz DISPATCH, L->glref // Setup pointer to dispatch table. | stw CARG1, SAVE_PC // Any value outside of bytecode is ok. | stw TMP1, SAVE_CFRAME | addi DISPATCH, DISPATCH, GG_G2DISP | |3: // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype). | lwz TMP2, L->base // TMP2 = old base (used in vmeta_call). | evsplati TISNUM, LJ_TISNUM+1 // Setup type comparison constants. | lwz TMP1, L->top | evsplati TISFUNC, LJ_TFUNC | add PC, PC, BASE | li_vmstate INTERP | evsplati TISTAB, LJ_TTAB | sub PC, PC, TMP2 // PC = frame delta + frame type | st_vmstate | evsplati TISSTR, LJ_TSTR | sub NARGS8:RC, TMP1, BASE | evsplati TISNIL, LJ_TNIL | |->vm_call_dispatch: | // TMP2 = old base, BASE = new base, RC = nargs*8, PC = caller PC | // NYI: reschedule. | li TMP0, -8 | evlddx LFUNC:RB, BASE, TMP0 | checkfunc LFUNC:RB | checkfail ->vmeta_call // Ensure KBASE defined and != BASE. | |->vm_call_dispatch_f: | ins_call | // BASE = new base, RC = nargs*8 | |->vm_cpcall: // Setup protected C frame, call C. | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) | saveregs | mr L, CARG1 | mtctr CARG4 | lwz TMP0, L:CARG1->stack | stw CARG1, SAVE_L | lwz TMP1, L->top | stw CARG1, SAVE_PC // Any value outside of bytecode is ok. | sub TMP0, TMP0, TMP1 // Compute -savestack(L, L->top). | lwz TMP1, L->cframe | stw sp, L->cframe // Add our C frame to cframe chain. | li TMP2, 0 | stw TMP0, SAVE_NRES // Neg. delta means cframe w/o frame. | stw TMP2, SAVE_ERRF // No error function. | stw TMP1, SAVE_CFRAME | bctrl // (lua_State *L, lua_CFunction func, void *ud) | mr. BASE, CRET1 | lwz DISPATCH, L->glref // Setup pointer to dispatch table. | li PC, FRAME_CP | addi DISPATCH, DISPATCH, GG_G2DISP | bne <3 // Else continue with the call. | b ->vm_leave_cp // No base? Just remove C frame. | |//----------------------------------------------------------------------- |//-- Metamethod handling ------------------------------------------------ |//----------------------------------------------------------------------- | |//-- Continuation dispatch ---------------------------------------------- | |->cont_dispatch: | NYI | |->cont_cat: | NYI | |//-- Table indexing metamethods ----------------------------------------- | |->vmeta_tgets: | NYI | |->vmeta_tgetb: | NYI | |->vmeta_tgetv: | NYI | |//----------------------------------------------------------------------- | |->vmeta_tsets: | NYI | |->vmeta_tsetb: | NYI | |->vmeta_tsetv: | NYI | |//-- Comparison metamethods --------------------------------------------- | |->vmeta_comp: | NYI |->cont_nop: | NYI | |->cont_ra: | NYI | |->cont_condt: | NYI | |->cont_condf: | NYI | |->vmeta_equal: | NYI | |//-- Arithmetic metamethods --------------------------------------------- | |->vmeta_arith_vn: | NYI | |->vmeta_arith_nv: | NYI | |->vmeta_unm: | NYI | |->vmeta_arith_vv: | NYI | |->vmeta_binop: | NYI | |->vmeta_len: | NYI | |//-- Call metamethod ---------------------------------------------------- | |->vmeta_call_ra: | NYI | |->vmeta_call: // Resolve and call __call metamethod. | NYI | |//-- Argument coercion for 'for' statement ------------------------------ | |->vmeta_for: | NYI | |//----------------------------------------------------------------------- |//-- Fast functions ----------------------------------------------------- |//----------------------------------------------------------------------- | |.macro .ffunc, name |->ff_ .. name: |.endmacro | |.macro .ffunc_1, name |->ff_ .. name: | NYI |.endmacro | |.macro .ffunc_2, name |->ff_ .. name: | NYI |.endmacro | |.macro .ffunc_n, name | .ffunc_1 name | NYI |.endmacro | |.macro .ffunc_nn, name | .ffunc_2 name | NYI |.endmacro | |.macro ffgccheck | NYI |.endmacro | |//-- Base library: checks ----------------------------------------------- | |.ffunc assert | NYI | |.ffunc type | NYI | |//-- Base library: getters and setters --------------------------------- | |.ffunc_1 getmetatable | NYI | |.ffunc_2 setmetatable | NYI | |.ffunc_2 rawget | NYI | |//-- Base library: conversions ------------------------------------------ | |.ffunc tonumber | NYI | |.ffunc_1 tostring | NYI | |//-- Base library: iterators ------------------------------------------- | |.ffunc_1 next | NYI | |->fff_res2: | NYI | |.ffunc_1 pairs | NYI | |.ffunc_1 ipairs_aux | NYI | |->fff_res0: | NYI | |.ffunc_1 ipairs | NYI | |//-- Base library: catch errors ---------------------------------------- | |.ffunc_1 pcall | NYI | |.ffunc_2 xpcall | NYI | |//-- Coroutine library -------------------------------------------------- | |.macro coroutine_resume_wrap, resume |.if resume |.ffunc_1 coroutine_resume |.else |.ffunc coroutine_wrap_aux |.endif | NYI |.endmacro | | coroutine_resume_wrap 1 // coroutine.resume | coroutine_resume_wrap 0 // coroutine.wrap | |.ffunc coroutine_yield | NYI | |//-- Math library ------------------------------------------------------- | |.ffunc_n math_abs | NYI | // Fallthrough. | |->fff_res1: | NYI |->fff_res: | NYI | |.macro math_extern, func | .ffunc math_ .. func | NYI |.endmacro | |.macro math_extern2, func | .ffunc math_ .. func | NYI |.endmacro | | math_extern floor | math_extern ceil | | math_extern sqrt | math_extern log | 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 | |->ff_math_deg: |.ffunc_1 math_rad | NYI | |.ffunc_nn math_ldexp; NYI |.ffunc_n math_frexp; NYI |.ffunc_n math_modf; NYI | |.macro math_minmax, name, cmpop | .ffunc_1 name | NYI |.endmacro | | math_minmax math_min, efdtstlt | math_minmax math_max, efdtstgt | |//-- String library ----------------------------------------------------- | |.ffunc_1 string_len | NYI | |.ffunc string_byte // Only handle the 1-arg case here. | NYI | |.ffunc string_char // Only handle the 1-arg case here. | NYI | |->fff_newstr: | NYI | |.ffunc string_sub | NYI | |->fff_emptystr: // Range underflow. | NYI | |.ffunc_2 string_rep // Only handle the 1-char case inline. | NYI | |.ffunc_1 string_reverse | NYI | |.macro ffstring_case, name, lo, hi | .ffunc_1 name | NYI |.endmacro | |ffstring_case string_lower, 0x41, 0x5a |ffstring_case string_upper, 0x61, 0x7a | |//-- Table library ------------------------------------------------------ | |.ffunc_1 table_getn | NYI | |//-- Bit library -------------------------------------------------------- | |.ffunc_n bit_tobit | NYI | |.macro .ffunc_bit, name | .ffunc_n name | NYI |.endmacro | |.macro .ffunc_bit_op, name, ins | .ffunc_bit name | NYI |.endmacro | |.ffunc_bit_op bit_band, and |.ffunc_bit_op bit_bor, or |.ffunc_bit_op bit_bxor, xor | |.ffunc_bit bit_bswap | NYI | |.ffunc_bit bit_bnot | NYI | |->fff_resbit: | NYI | |->fff_fallback_bit_op: | NYI | |.macro .ffunc_bit_sh, name, ins | .ffunc_nn name | NYI |.endmacro | |.ffunc_bit_sh bit_lshift, shl |.ffunc_bit_sh bit_rshift, shr |.ffunc_bit_sh bit_arshift, sar |.ffunc_bit_sh bit_rol, rol |.ffunc_bit_sh bit_ror, ror | |//----------------------------------------------------------------------- | |->fff_fallback: // Call fast function fallback handler. | NYI | |->fff_gcstep: // Call GC step function. | NYI | |//----------------------------------------------------------------------- |//-- Special dispatch targets ------------------------------------------- |//----------------------------------------------------------------------- | |->vm_record: // Dispatch target for recording phase. #if LJ_HASJIT | NYI #endif | |->vm_rethook: // Dispatch target for return hooks. | NYI | |->vm_inshook: // Dispatch target for instr/line hooks. | NYI | |->cont_hook: // Continue from hook yield. | NYI | |->vm_hotloop: // Hot loop counter underflow. #if LJ_HASJIT | NYI #endif | |->vm_callhook: // Dispatch target for call hooks. | NYI | |->vm_hotcall: // Hot call counter underflow. #if LJ_HASJIT | NYI #endif | |//----------------------------------------------------------------------- |//-- Trace exit handler ------------------------------------------------- |//----------------------------------------------------------------------- | |->vm_exit_handler: #if LJ_HASJIT | NYI #endif |->vm_exit_interp: #if LJ_HASJIT | NYI #endif | |//----------------------------------------------------------------------- |//-- Math helper functions ---------------------------------------------- |//----------------------------------------------------------------------- | |// FP value rounding. Called by math.floor/math.ceil fast functions |// and from JIT code. | |.macro vm_round, name, mode |->name: | NYI |.endmacro | | vm_round vm_floor, 0 | vm_round vm_ceil, 1 #if LJ_HASJIT | vm_round vm_trunc, 2 #else |->vm_trunc: #endif | |->vm_powi: #if LJ_HASJIT | NYI #endif | |->vm_foldfpm: #if LJ_HASJIT | NYI #endif | |// Callable from C: double lj_vm_foldarith(double x, double y, int op) |// Compute x op y for basic arithmetic operators (+ - * / % ^ and unary -) |// and basic math functions. ORDER ARITH |->vm_foldarith: | NYI | |//----------------------------------------------------------------------- |//-- Miscellaneous functions -------------------------------------------- |//----------------------------------------------------------------------- | |//----------------------------------------------------------------------- } /* Generate the code for a single instruction. */ static void build_ins(BuildCtx *ctx, BCOp op, int defop) { |=>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: | NYI break; case BC_ISEQV: case BC_ISNEV: | NYI break; case BC_ISEQS: case BC_ISNES: | NYI break; case BC_ISEQN: case BC_ISNEN: | NYI break; case BC_ISEQP: case BC_ISNEP: | NYI break; /* -- Unary test and copy ops ------------------------------------------- */ case BC_ISTC: case BC_ISFC: case BC_IST: case BC_ISF: | NYI break; /* -- Unary ops --------------------------------------------------------- */ case BC_MOV: | // RA = dst*8, RD = src*8 | evlddx TMP0, BASE, RD | evstddx TMP0, BASE, RA | ins_next_ break; case BC_NOT: | NYI break; case BC_UNM: | NYI break; case BC_LEN: | NYI break; /* -- Binary ops -------------------------------------------------------- */ case BC_ADDVN: case BC_ADDNV: case BC_ADDVV: | NYI break; case BC_SUBVN: case BC_SUBNV: case BC_SUBVV: | NYI break; case BC_MULVN: case BC_MULNV: case BC_MULVV: | NYI break; case BC_DIVVN: case BC_DIVNV: case BC_DIVVV: | NYI break; case BC_MODVN: | NYI break; case BC_MODNV: case BC_MODVV: | NYI break; case BC_POW: | NYI break; case BC_CAT: | NYI break; /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: | // RA = dst*8, RD = str_const*8 (~) | srwi TMP1, RD, 1 | subfic TMP1, TMP1, -4 | lwzx TMP0, KBASE, TMP1 // KBASE-4-str_const*4 | evmergelo TMP0, TISSTR, TMP0 | evstddx TMP0, BASE, RA | ins_next break; case BC_KSHORT: | // RA = dst*8, RD = int16_literal*8 | srwi TMP1, RD, 3 | extsh TMP1, TMP1 | efdcfsi TMP0, TMP1 | evstddx TMP0, BASE, RA | ins_next break; case BC_KNUM: | // RA = dst*8, RD = num_const*8 | evlddx TMP0, KBASE, RD | evstddx TMP0, BASE, RA | ins_next break; case BC_KPRI: | // RA = dst*8, RD = primitive_type*8 (~) | srwi TMP1, RD, 3 | not TMP0, TMP1 | stwx TMP0, BASE, RA | ins_next break; case BC_KNIL: | // RA = base*8, RD = end*8 | evstddx TISNIL, BASE, RA | addi RA, RA, 8 |1: | evstddx TISNIL, BASE, RA | cmpw RA, RD | addi RA, RA, 8 | blt <1 | ins_next break; /* -- Upvalue and function ops ------------------------------------------ */ case BC_UGET: | NYI break; case BC_USETV: | NYI break; case BC_USETS: | NYI break; case BC_USETN: | NYI break; case BC_USETP: | NYI break; case BC_UCLO: | NYI break; case BC_FNEW: | NYI break; /* -- Table ops --------------------------------------------------------- */ case BC_TNEW: | NYI break; case BC_TDUP: | NYI break; case BC_GGET: case BC_GSET: | NYI break; case BC_TGETV: | NYI break; case BC_TGETS: | NYI break; case BC_TGETB: | NYI break; case BC_TSETV: | NYI break; case BC_TSETS: | NYI break; case BC_TSETB: | NYI break; case BC_TSETM: | NYI break; /* -- Calls and vararg handling ----------------------------------------- */ case BC_CALLM: | NYI break; case BC_CALL: | NYI break; case BC_CALLMT: | NYI break; case BC_CALLT: | NYI break; case BC_ITERC: | NYI break; case BC_VARG: | NYI break; /* -- Returns ----------------------------------------------------------- */ case BC_RETM: | // RA = results*8, RD = extra_nresults*8 | lwz TMP0, SAVE_MULTRES | add RD, RD, TMP0 // SAVE_MULTRES >= 8, so RD >= 8. | // Fall through. Assumes BC_RET follows. break; case BC_RET: | // RA = results*8, RD = (nresults+1)*8 | lwz PC, FRAME_PC(BASE) | add RA, BASE, RA | stw RD, SAVE_MULTRES |1: | andi. TMP0, PC, FRAME_TYPE | xori TMP1, PC, FRAME_VARG | bne ->BC_RETV_Z | |->BC_RET_Z: | // BASE = base, RA = resultptr, RD = (nresults+1)*8, PC = return | lwz INS, -4(PC) | cmpwi RD, 8 | subi TMP2, BASE, 8 | decode_RB8 RB, INS | beq >3 | li TMP1, 0 |2: | addi TMP3, TMP1, 8 | evlddx TMP0, RA, TMP1 | cmpw TMP3, RD | evstddx TMP0, TMP2, TMP1 | beq >3 | addi TMP1, TMP3, 8 | evlddx TMP0, RA, TMP3 | cmpw TMP1, RD | evstddx TMP0, TMP2, TMP3 | bne <2 |3: |5: | cmplw RB, RD | decode_RA8 RA, INS | bgt >6 | sub BASE, TMP2, RA | lwz LFUNC:TMP1, FRAME_FUNC(BASE) | lwz TMP1, LFUNC:TMP1->pc | lwz KBASE, PC2PROTO(k)(TMP1) | ins_next | |6: // Fill up results with nil. | subi TMP1, RD, 8 | addi RD, RD, 8 | evstddx TISNIL, TMP2, TMP1 | b <5 | |->BC_RETV_Z: // Non-standard return case. | andi. TMP2, TMP1, FRAME_TYPEP | bne ->vm_return | // Return from vararg function: relocate BASE down. | sub BASE, BASE, TMP1 | lwz PC, FRAME_PC(BASE) | b <1 break; case BC_RET0: case BC_RET1: | // RA = results*8, RD = (nresults+1)*8 | lwz PC, FRAME_PC(BASE) | add RA, BASE, RA | stw RD, SAVE_MULTRES | andi. TMP0, PC, FRAME_TYPE | xori TMP1, PC, FRAME_VARG | bne ->BC_RETV_Z | | lwz INS, -4(PC) | subi TMP2, BASE, 8 | decode_RB8 RB, INS if (op == BC_RET1) { | evldd TMP0, 0(RA) | evstdd TMP0, 0(TMP2) } |5: | cmplw RB, RD | decode_RA8 RA, INS | bgt >6 | sub BASE, TMP2, RA | lwz LFUNC:TMP1, FRAME_FUNC(BASE) | lwz TMP1, LFUNC:TMP1->pc | lwz KBASE, PC2PROTO(k)(TMP1) | ins_next | |6: // Fill up results with nil. | subi TMP1, RD, 8 | addi RD, RD, 8 | evstddx TISNIL, TMP2, TMP1 | b <5 break; /* -- Loops and branches ------------------------------------------------ */ case BC_FORL: #if LJ_HASJIT | 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: | NYI break; case BC_ITERL: #if LJ_HASJIT | hotloop #endif | // Fall through. Assumes BC_IITERL follows. break; case BC_JITERL: #if !LJ_HASJIT break; #endif case BC_IITERL: | NYI break; case BC_LOOP: | NYI #if LJ_HASJIT | // Fall through. Assumes BC_ILOOP follows. #endif break; case BC_ILOOP: | NYI break; case BC_JLOOP: | NYI break; case BC_JMP: | hotcall break; /* -- Function headers -------------------------------------------------- */ case BC_FUNCF: #if LJ_HASJIT | 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 | lwz TMP2, L->maxstack | lbz TMP1, -4+PC2PROTO(numparams)(PC) | lwz KBASE, -4+PC2PROTO(k)(PC) | cmplw RA, TMP2 | slwi TMP1, TMP1, 3 | bgt ->vm_growstack_l |2: | cmplw NARGS8:RC, TMP1 // Check for missing parameters. | ble >3 if (op == BC_JFUNCF) { | NYI } else { | ins_next } | |3: // Clear missing parameters. | evstddx TISNIL, BASE, NARGS8:RC | addi NARGS8:RC, NARGS8:RC, 8 | b <2 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 | lwz TMP2, L->maxstack | add TMP1, BASE, RC | add TMP0, RA, RC | stw LFUNC:RB, 4(TMP1) // Store copy of LFUNC. | addi TMP3, RC, 8+FRAME_VARG | lwz KBASE, -4+PC2PROTO(k)(PC) | cmplw TMP0, TMP2 | stw TMP3, 0(TMP1) // Store delta + FRAME_VARG. | bge ->vm_growstack_l | lbz TMP2, -4+PC2PROTO(numparams)(PC) | mr RA, BASE | mr RC, TMP1 | cmpwi TMP2, 0 | addi BASE, TMP1, 8 | beq >3 |1: | cmplw RA, RC // Less args than parameters? | evldd TMP0, 0(RA) | bge >4 | evstdd TISNIL, 0(RA) // Clear old fixarg slot (help the GC). | addi RA, RA, 8 |2: | addic. TMP2, TMP2, -1 | evstdd TMP0, 8(TMP1) | addi TMP1, TMP1, 8 | bne <1 |3: | ins_next | |4: // Clear missing parameters. | evmr TMP0, TISNIL | b <2 break; case BC_FUNCC: case BC_FUNCCW: | // BASE = new base, RA = BASE+framesize*8, RB = CFUNC, RC = nargs*8 if (op == BC_FUNCC) { | lwz TMP0, CFUNC:RB->f } else { | lwz TMP0, DISPATCH_GL(wrapf)(DISPATCH) } | add TMP1, RA, NARGS8:RC | lwz TMP2, L->maxstack | add RC, BASE, NARGS8:RC | stw BASE, L->base | mtctr TMP0 | cmplw TMP1, TMP2 | stw RC, L->top | li_vmstate C if (op == BC_FUNCCW) { | lwz CARG2, CFUNC:RB->f } | mr CARG1, L | bgt ->vm_growstack_c // Need to grow stack. | st_vmstate | bctrl // (lua_State *L [, lua_CFunction f]) | // Returns nresults. | lwz TMP1, L->top | slwi RD, CRET1, 3 | lwz BASE, L->base | li_vmstate INTERP | lwz PC, FRAME_PC(BASE) // Fetch PC of caller. | sub RA, TMP1, RD // RA = L->top - nresults*8 | st_vmstate | b ->vm_returnc 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) { /* NYI */ UNUSED(ctx); }