Add support for tailcalls from internal C functions.

PPC: Fix __call metamethod for tailcalls.
This commit is contained in:
Mike Pall 2011-04-12 19:12:29 +02:00
parent 23f847f4ed
commit fa5cd010e8
12 changed files with 4180 additions and 4103 deletions

View File

@ -114,7 +114,8 @@ lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h lj_dispatch.h lj_bc.h \ lj_gc.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h lj_dispatch.h lj_bc.h \
lj_traceerr.h lj_vm.h lj_traceerr.h lj_vm.h
lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_bc.h lj_vm.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \
lj_vm.h
lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h
lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_ir.h lj_jit.h lj_iropt.h lj_ir.h lj_jit.h lj_iropt.h

View File

@ -411,7 +411,7 @@ static void build_subroutines(BuildCtx *ctx)
| |
|->vm_call_dispatch_f: |->vm_call_dispatch_f:
| ins_call | ins_call
| // BASE = new base, RC = nargs*8 | // BASE = new base, CARG3 = func, RC = nargs*8, PC = caller PC
| |
|->vm_cpcall: // Setup protected C frame, call C. |->vm_cpcall: // Setup protected C frame, call C.
| // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp)
@ -445,18 +445,26 @@ static void build_subroutines(BuildCtx *ctx)
|->cont_dispatch: |->cont_dispatch:
| // BASE = meta base, RA = resultptr, RC = (nresults+1)*8 | // BASE = meta base, RA = resultptr, RC = (nresults+1)*8
| ldr LFUNC:CARG3, [RB, FRAME_FUNC] | ldr LFUNC:CARG3, [RB, FRAME_FUNC]
| ldr CARG1, [BASE, #-16] // Get continuation.
| mov CARG4, BASE | mov CARG4, BASE
| mov BASE, RB // Restore caller BASE. | mov BASE, RB // Restore caller BASE.
| cmp CARG1, #0
| ldr PC, [CARG4, #-12] // Restore PC from [cont|PC]. | ldr PC, [CARG4, #-12] // Restore PC from [cont|PC].
| beq >1
| ldr CARG3, LFUNC:CARG3->field_pc | ldr CARG3, LFUNC:CARG3->field_pc
| mvn INS, #~LJ_TNIL | mvn INS, #~LJ_TNIL
| add CARG2, RA, RC | add CARG2, RA, RC
| ldr CARG1, [CARG4, #-16] // Get continuation.
| str INS, [CARG2, #-4] // Ensure one valid arg. | str INS, [CARG2, #-4] // Ensure one valid arg.
| ldr KBASE, [CARG3, #PC2PROTO(k)] | ldr KBASE, [CARG3, #PC2PROTO(k)]
| // BASE = base, RA = resultptr, CARG4 = meta base | // BASE = base, RA = resultptr, CARG4 = meta base
| bx CARG1 | bx CARG1
| |
|1: // Tail call from C function.
| ldr CARG3, [BASE, FRAME_FUNC]
| sub CARG4, CARG4, #16
| sub RC, CARG4, BASE
| b ->vm_call_tail
|
|->cont_cat: // RA = resultptr, CARG4 = meta base |->cont_cat: // RA = resultptr, CARG4 = meta base
| ldr INS, [PC, #-4] | ldr INS, [PC, #-4]
| sub CARG2, CARG4, #16 | sub CARG2, CARG4, #16
@ -714,7 +722,7 @@ static void build_subroutines(BuildCtx *ctx)
| str PC, SAVE_PC | str PC, SAVE_PC
| add CARG3, RA, NARGS8:RC | add CARG3, RA, NARGS8:RC
| bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top)
| ldr LFUNC:CARG3, [BASE, FRAME_FUNC] // Guaranteed to be a function here. | ldr LFUNC:CARG3, [RA, FRAME_FUNC] // Guaranteed to be a function here.
| ldr PC, [BASE, FRAME_PC] | ldr PC, [BASE, FRAME_PC]
| add NARGS8:RC, NARGS8:RC, #8 // Got one more argument now. | add NARGS8:RC, NARGS8:RC, #8 // Got one more argument now.
| b ->BC_CALLT2_Z | b ->BC_CALLT2_Z
@ -1514,10 +1522,11 @@ static void build_subroutines(BuildCtx *ctx)
| ldr CARG1, L->top | ldr CARG1, L->top
| ldr LFUNC:CARG3, [BASE, FRAME_FUNC] | ldr LFUNC:CARG3, [BASE, FRAME_FUNC]
| sub NARGS8:RC, CARG1, BASE | sub NARGS8:RC, CARG1, BASE
| bne >2 // Returned -1? | bne ->vm_call_tail // Returned -1?
| ins_callt // Returned 0: retry fast path. | ins_callt // Returned 0: retry fast path.
| |
|2: // Reconstruct previous base for vmeta_call during tailcall. |// Reconstruct previous base for vmeta_call during tailcall.
|->vm_call_tail:
| ands CARG1, PC, #FRAME_TYPE | ands CARG1, PC, #FRAME_TYPE
| bic CARG2, PC, #FRAME_TYPEP | bic CARG2, PC, #FRAME_TYPEP
| ldreq INS, [PC, #-4] | ldreq INS, [PC, #-4]

View File

@ -540,7 +540,7 @@ static void build_subroutines(BuildCtx *ctx)
| |
|->vm_call_dispatch_f: |->vm_call_dispatch_f:
| ins_call | ins_call
| // BASE = new base, RC = nargs*8 | // BASE = new base, RB = func, RC = nargs*8, PC = caller PC
| |
|->vm_cpcall: // Setup protected C frame, call C. |->vm_cpcall: // Setup protected C frame, call C.
| // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp)
@ -581,8 +581,10 @@ static void build_subroutines(BuildCtx *ctx)
| mr RB, BASE | mr RB, BASE
| mr BASE, TMP2 // Restore caller BASE. | mr BASE, TMP2 // Restore caller BASE.
| lwz LFUNC:TMP1, FRAME_FUNC(TMP2) | lwz LFUNC:TMP1, FRAME_FUNC(TMP2)
| subi TMP2, RD, 8 | cmplwi TMP0, 0
| lwz PC, -16(RB) // Restore PC from [cont|PC]. | lwz PC, -16(RB) // Restore PC from [cont|PC].
| beq >1
| subi TMP2, RD, 8
| lwz TMP1, LFUNC:TMP1->pc | lwz TMP1, LFUNC:TMP1->pc
| evstddx TISNIL, RA, TMP2 // Ensure one valid arg. | evstddx TISNIL, RA, TMP2 // Ensure one valid arg.
| lwz KBASE, PC2PROTO(k)(TMP1) | lwz KBASE, PC2PROTO(k)(TMP1)
@ -590,6 +592,11 @@ static void build_subroutines(BuildCtx *ctx)
| mtctr TMP0 | mtctr TMP0
| bctr // Jump to continuation. | bctr // Jump to continuation.
| |
|1: // Tail call from C function.
| subi TMP1, RB, 16
| sub RC, TMP1, BASE
| b ->vm_call_tail
|
|->cont_cat: // RA = resultptr, RB = meta base |->cont_cat: // RA = resultptr, RB = meta base
| lwz INS, -4(PC) | lwz INS, -4(PC)
| subi CARG2, RB, 16 | subi CARG2, RB, 16
@ -845,7 +852,7 @@ static void build_subroutines(BuildCtx *ctx)
| bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top) | bl extern lj_meta_call // (lua_State *L, TValue *func, TValue *top)
| lwz TMP1, FRAME_PC(BASE) | lwz TMP1, FRAME_PC(BASE)
| addi NARGS8:RC, SAVE0, 8 // Got one more argument now. | addi NARGS8:RC, SAVE0, 8 // Got one more argument now.
| lwz LFUNC:RB, FRAME_FUNC(BASE) // Guaranteed to be a function here. | lwz LFUNC:RB, FRAME_FUNC(RA) // Guaranteed to be a function here.
| b ->BC_CALLT_Z | b ->BC_CALLT_Z
| |
|//-- Argument coercion for 'for' statement ------------------------------ |//-- Argument coercion for 'for' statement ------------------------------
@ -1797,10 +1804,11 @@ static void build_subroutines(BuildCtx *ctx)
| lwz TMP0, L->top | lwz TMP0, L->top
| lwz LFUNC:RB, FRAME_FUNC(BASE) | lwz LFUNC:RB, FRAME_FUNC(BASE)
| sub NARGS8:RC, TMP0, BASE | sub NARGS8:RC, TMP0, BASE
| bne >2 // Returned -1? | bne ->vm_call_tail // Returned -1?
| ins_callt // Returned 0: retry fast path. | ins_callt // Returned 0: retry fast path.
| |
|2: // Reconstruct previous base for vmeta_call during tailcall. |// Reconstruct previous base for vmeta_call during tailcall.
|->vm_call_tail:
| andi. TMP0, PC, FRAME_TYPE | andi. TMP0, PC, FRAME_TYPE
| rlwinm TMP1, PC, 0, 0, 28 | rlwinm TMP1, PC, 0, 0, 28
| bne >3 | bne >3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -696,7 +696,7 @@ static void build_subroutines(BuildCtx *ctx, int cmov, int sse)
|->vm_call_dispatch_f: |->vm_call_dispatch_f:
| mov BASE, RA | mov BASE, RA
| ins_call | ins_call
| // BASE = new base, RD = nargs+1 | // BASE = new base, RB = func, RD = nargs+1, PC = caller PC
| |
|->vm_cpcall: // Setup protected C frame, call C. |->vm_cpcall: // Setup protected C frame, call C.
| // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp) | // (lua_State *L, lua_CFunction func, void *ud, lua_CPFunction cp)
@ -760,10 +760,14 @@ static void build_subroutines(BuildCtx *ctx, int cmov, int sse)
| mov PC, [RB-12] // Restore PC from [cont|PC]. | mov PC, [RB-12] // Restore PC from [cont|PC].
|.if X64 |.if X64
| movsxd RAa, dword [RB-16] // May be negative on WIN64 with debug. | movsxd RAa, dword [RB-16] // May be negative on WIN64 with debug.
| test RA, RA
| jz >1
| lea KBASEa, qword [=>0] | lea KBASEa, qword [=>0]
| add RAa, KBASEa | add RAa, KBASEa
|.else |.else
| mov RA, dword [RB-16] | mov RA, dword [RB-16]
| test RA, RA
| jz >1
|.endif |.endif
| mov LFUNC:KBASE, [BASE-8] | mov LFUNC:KBASE, [BASE-8]
| mov KBASE, LFUNC:KBASE->pc | mov KBASE, LFUNC:KBASE->pc
@ -771,6 +775,12 @@ static void build_subroutines(BuildCtx *ctx, int cmov, int sse)
| // BASE = base, RC = result, RB = meta base | // BASE = base, RC = result, RB = meta base
| jmp RAa // Jump to continuation. | jmp RAa // Jump to continuation.
| |
|1: // Tail call from C function.
| sub RB, BASE
| shr RB, 3
| lea RD, [RB-1]
| jmp ->vm_call_tail
|
|->cont_cat: // BASE = base, RC = result, RB = mbase |->cont_cat: // BASE = base, RC = result, RB = mbase
| movzx RA, PC_RB | movzx RA, PC_RB
| sub RB, 16 | sub RB, 16
@ -2735,10 +2745,11 @@ static void build_subroutines(BuildCtx *ctx, int cmov, int sse)
| test RD, RD | test RD, RD
| lea NARGS:RD, [RA+1] | lea NARGS:RD, [RA+1]
| mov LFUNC:RB, [BASE-8] | mov LFUNC:RB, [BASE-8]
| jne >2 // Returned -1? | jne ->vm_call_tail // Returned -1?
| ins_callt // Returned 0: retry fast path. | ins_callt // Returned 0: retry fast path.
| |
|2: // Reconstruct previous base for vmeta_call during tailcall. |// Reconstruct previous base for vmeta_call during tailcall.
|->vm_call_tail:
| mov RA, BASE | mov RA, BASE
| test PC, FRAME_TYPE | test PC, FRAME_TYPE
| jnz >3 | jnz >3

File diff suppressed because it is too large Load Diff

View File

@ -1104,7 +1104,7 @@ LUA_API int lua_yield(lua_State *L, int nresults)
setcont(top+1, lj_cont_hook); setcont(top+1, lj_cont_hook);
setframe_pc(top+1, cframe_pc(cf)-1); setframe_pc(top+1, cframe_pc(cf)-1);
setframe_gc(top+2, obj2gco(L)); setframe_gc(top+2, obj2gco(L));
top[2].fr.tp.ftsz = (int)((char *)(top+3)-(char *)L->base)+FRAME_CONT; setframe_ftsz(top+2, (int)((char *)(top+3)-(char *)L->base)+FRAME_CONT);
L->top = L->base = top+3; L->top = L->base = top+3;
} }
#if LJ_TARGET_X64 #if LJ_TARGET_X64

View File

@ -51,6 +51,7 @@ enum {
/* Note: this macro does not skip over FRAME_VARG. */ /* Note: this macro does not skip over FRAME_VARG. */
#define setframe_pc(f, pc) (setmref((f)->fr.tp.pcr, (pc))) #define setframe_pc(f, pc) (setmref((f)->fr.tp.pcr, (pc)))
#define setframe_ftsz(f, sz) ((f)->fr.tp.ftsz = (sz))
#define setframe_gc(f, p) (setgcref((f)->fr.func, (p))) #define setframe_gc(f, p) (setgcref((f)->fr.func, (p)))
/* -- C stack frame ------------------------------------------------------- */ /* -- C stack frame ------------------------------------------------------- */

View File

@ -15,6 +15,7 @@
#include "lj_str.h" #include "lj_str.h"
#include "lj_tab.h" #include "lj_tab.h"
#include "lj_meta.h" #include "lj_meta.h"
#include "lj_frame.h"
#include "lj_bc.h" #include "lj_bc.h"
#include "lj_vm.h" #include "lj_vm.h"
@ -68,6 +69,29 @@ cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm)
return niltv(L); return niltv(L);
} }
/* Tailcall from C function. */
int lj_meta_tailcall(lua_State *L, cTValue *tv)
{
TValue *base = L->base;
TValue *top = L->top;
const BCIns *pc = frame_pc(base-1); /* Preserve old PC from frame. */
copyTV(L, base-1, tv); /* Replace frame with new object. */
top->u64 = 0;
setframe_pc(top, pc);
setframe_gc(top+1, obj2gco(L)); /* Dummy frame object. */
setframe_ftsz(top+1, (int)((char *)(top+2) - (char *)base) + FRAME_CONT);
L->base = L->top = top+2;
/*
** before: [old_mo|PC] [... ...]
** ^base ^top
** after: [new_mo|itype] [... ...] [NULL|PC] [dummy|delta]
** ^base/top
** tailcall: [new_mo|PC] [... ...]
** ^base ^top
*/
return 0;
}
/* Setup call to metamethod to be run by Assembler VM. */ /* Setup call to metamethod to be run by Assembler VM. */
static TValue *mmcall(lua_State *L, ASMFunction cont, cTValue *mo, static TValue *mmcall(lua_State *L, ASMFunction cont, cTValue *mo,
cTValue *a, cTValue *b) cTValue *a, cTValue *b)

View File

@ -12,6 +12,7 @@
LJ_FUNC void lj_meta_init(lua_State *L); LJ_FUNC void lj_meta_init(lua_State *L);
LJ_FUNC cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name); LJ_FUNC cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name);
LJ_FUNC cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm); LJ_FUNC cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm);
LJ_FUNC int lj_meta_tailcall(lua_State *L, cTValue *tv);
#define lj_meta_fastg(g, mt, mm) \ #define lj_meta_fastg(g, mt, mm) \
((mt) == NULL ? NULL : ((mt)->nomm & (1u<<(mm))) ? NULL : \ ((mt) == NULL ? NULL : ((mt)->nomm & (1u<<(mm))) ? NULL : \