Specialize metatables for method calls

This commit is contained in:
Jude Melton-Houghton 2022-09-19 08:05:00 -04:00
parent dad04f1754
commit b5783323ef
13 changed files with 85 additions and 17 deletions

View File

@ -144,6 +144,7 @@
_(GSET, var, ___, str, newindex) \
_(TGETV, dst, var, var, index) \
_(TGETS, dst, var, str, index) \
_(TGETSS, dst, var, str, index) \
_(TGETB, dst, var, lit, index) \
_(TGETR, dst, var, var, index) \
_(TSETV, var, var, var, newindex) \
@ -259,6 +260,8 @@ static LJ_AINLINE int bc_isret(BCOp op)
return (op == BC_RETM || op == BC_RET || op == BC_RET0 || op == BC_RET1);
}
#define bc_isalias(op) ((op) == BC_TGETSS)
LJ_DATA const uint16_t lj_bc_mode[];
LJ_DATA const uint16_t lj_bc_ofs[];

View File

@ -271,13 +271,10 @@ restart:
return "global";
case BC_TGETS:
*name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_c(ins))));
if (ip > proto_bc(pt)) {
BCIns insp = ip[-1];
if (bc_op(insp) == BC_MOV && bc_a(insp) == ra+1+LJ_FR2 &&
bc_d(insp) == bc_b(ins))
return "method";
}
return "field";
case BC_TGETSS:
*name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_c(ins))));
return "method";
case BC_UGET:
*name = lj_debug_uvname(pt, bc_d(ins));
return "upvalue";

View File

@ -225,6 +225,7 @@ static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd)
RecordIndex ix;
ix.tab = tr;
copyTV(J->L, &ix.tabv, &rd->argv[0]);
ix.mtspec = 0;
if (lj_record_mm_lookup(J, &ix, MM_metatable))
J->base[0] = ix.mobj;
else
@ -241,6 +242,7 @@ static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd)
RecordIndex ix;
ix.tab = tr;
copyTV(J->L, &ix.tabv, &rd->argv[0]);
ix.mtspec = 0;
lj_record_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable. */
fref = emitir(IRT(IR_FREF, IRT_PGC), tr, IRFL_TAB_META);
mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt;
@ -389,6 +391,7 @@ static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm)
RecordIndex ix;
ix.tab = J->base[0];
copyTV(J->L, &ix.tabv, &rd->argv[0]);
ix.mtspec = 1;
if (lj_record_mm_lookup(J, &ix, mm)) { /* Has metamethod? */
int errcode;
TValue argv0;
@ -548,6 +551,7 @@ static void LJ_FASTCALL recff_next(jit_State *J, RecordFFData *rd)
ix.idxchain = (J->framedepth && frame_islua(J->L->base-1) &&
bc_b(frame_pc(J->L->base-1)[-1])-1 < 2);
ix.mobj = 0; /* We don't need the next index. */
ix.mtspec = 0;
rd->nres = lj_record_next(J, &ix);
J->base[0] = ix.key;
J->base[1] = ix.val;

View File

@ -675,7 +675,7 @@ static void bcemit_method(FuncState *fs, ExpDesc *e, ExpDesc *key)
idx = const_str(fs, key);
if (idx <= BCMAX_C) {
bcreg_reserve(fs, 2+LJ_FR2);
bcemit_ABC(fs, BC_TGETS, func, obj, idx);
bcemit_ABC(fs, BC_TGETSS, func, obj, idx);
} else {
bcreg_reserve(fs, 3+LJ_FR2);
bcemit_AD(fs, BC_KSTR, func+2+LJ_FR2, idx);

View File

@ -686,6 +686,7 @@ static LoopEvent rec_itern(jit_State *J, BCReg ra, BCReg rb)
copyTV(J->L, &ix.keyv, &J->L->base[ra-1]);
ix.idxchain = (rb < 3); /* Omit value type check, if unused. */
ix.mobj = 1; /* We need the next index, too. */
ix.mtspec = 0;
J->maxslot = ra + lj_record_next(J, &ix);
J->needsnap = 1;
if (!tref_isnil(ix.key)) { /* Looping back? */
@ -818,6 +819,7 @@ static void rec_call_setup(jit_State *J, BCReg func, ptrdiff_t nargs)
if (!tref_isfunc(fbase[0])) { /* Resolve __call metamethod. */
ix.tab = fbase[0];
copyTV(J->L, &ix.tabv, functv);
ix.mtspec = 1;
if (!lj_record_mm_lookup(J, &ix, MM_call) || !tref_isfunc(ix.mobj))
lj_trace_err(J, LJ_TRERR_NOMM);
for (i = ++nargs; i > LJ_FR2; i--) /* Shift arguments up. */
@ -1103,7 +1105,14 @@ int lj_record_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm)
goto nocheck;
}
ix->mt = mt ? mix.tab : TREF_NIL;
emitir(IRTG(mt ? IR_NE : IR_EQ, IRT_TAB), mix.tab, lj_ir_knull(J, IRT_TAB));
if (ix->mtspec && mt) {
TRef kmt = lj_ir_ktab(J, mt);
emitir(IRTG(IR_EQ, IRT_TAB), mix.tab, kmt);
mix.tab = kmt;
ix->mt = kmt;
} else {
emitir(IRTG(mt ? IR_NE : IR_EQ, IRT_TAB), mix.tab, lj_ir_knull(J, IRT_TAB));
}
nocheck:
if (mt) {
GCstr *mmstr = mmname_str(J2G(J), mm);
@ -1157,6 +1166,7 @@ static TRef rec_mm_len(jit_State *J, TRef tr, TValue *tv)
RecordIndex ix;
ix.tab = tr;
copyTV(J->L, &ix.tabv, tv);
ix.mtspec = 1;
if (lj_record_mm_lookup(J, &ix, MM_len)) {
BCReg func = rec_mm_prep(J, lj_cont_ra);
TRef *base = J->base + func;
@ -2086,6 +2096,7 @@ static TRef rec_cat(jit_State *J, BCReg baseslot, BCReg topslot)
ix.tab = top[-1];
ix.key = top[0];
memcpy(savetv, &J->L->base[topslot-1], sizeof(savetv)); /* Save slots. */
ix.mtspec = 1;
rec_mm_arith(J, &ix, MM_concat); /* Call __concat metamethod. */
memcpy(&J->L->base[topslot-1], savetv, sizeof(savetv)); /* Restore slots. */
return 0; /* No result yet. */
@ -2290,6 +2301,7 @@ void lj_record_ins(jit_State *J)
rc = lj_ir_kint(J, 0);
ta = IRT_INT;
} else {
ix.mtspec = 1;
rec_mm_comp(J, &ix, (int)op);
break;
}
@ -2313,10 +2325,12 @@ void lj_record_ins(jit_State *J)
int diff;
rec_comp_prep(J);
diff = lj_record_objcmp(J, ra, rc, rav, rcv);
if (diff == 2 || !(tref_istab(ra) || tref_isudata(ra)))
if (diff == 2 || !(tref_istab(ra) || tref_isudata(ra))) {
rec_comp_fixup(J, J->pc, ((int)op & 1) == !diff);
else if (diff == 1) /* Only check __eq if different, but same type. */
} else if (diff == 1) { /* Only check __eq if different, but same type. */
ix.mtspec = 1;
rec_mm_equal(J, &ix, (int)op);
}
}
break;
@ -2367,6 +2381,7 @@ void lj_record_ins(jit_State *J)
} else {
ix.tab = rc;
copyTV(J->L, &ix.tabv, rcv);
ix.mtspec = 1;
rc = rec_mm_arith(J, &ix, MM_unm);
}
break;
@ -2383,27 +2398,33 @@ void lj_record_ins(jit_State *J)
case BC_ADDVN: case BC_SUBVN: case BC_MULVN: case BC_DIVVN:
case BC_ADDVV: case BC_SUBVV: case BC_MULVV: case BC_DIVVV: {
MMS mm = bcmode_mm(op);
if (tref_isnumber_str(rb) && tref_isnumber_str(rc))
if (tref_isnumber_str(rb) && tref_isnumber_str(rc)) {
rc = lj_opt_narrow_arith(J, rb, rc, rbv, rcv,
(int)mm - (int)MM_add + (int)IR_ADD);
else
} else {
ix.mtspec = 1;
rc = rec_mm_arith(J, &ix, mm);
}
break;
}
case BC_MODVN: case BC_MODVV:
recmod:
if (tref_isnumber_str(rb) && tref_isnumber_str(rc))
if (tref_isnumber_str(rb) && tref_isnumber_str(rc)) {
rc = lj_opt_narrow_mod(J, rb, rc, rbv, rcv);
else
} else {
ix.mtspec = 1;
rc = rec_mm_arith(J, &ix, MM_mod);
}
break;
case BC_POW:
if (tref_isnumber_str(rb) && tref_isnumber_str(rc))
if (tref_isnumber_str(rb) && tref_isnumber_str(rc)) {
rc = lj_opt_narrow_arith(J, rb, rc, rbv, rcv, IR_POW);
else
} else {
ix.mtspec = 1;
rc = rec_mm_arith(J, &ix, MM_pow);
}
break;
/* -- Miscellaneous ops ------------------------------------------------- */
@ -2457,6 +2478,7 @@ void lj_record_ins(jit_State *J)
settabV(J->L, &ix.tabv, tabref(J->fn->l.env));
ix.tab = emitir(IRT(IR_FLOAD, IRT_TAB), getcurrf(J), IRFL_FUNC_ENV);
ix.idxchain = LJ_MAX_IDXCHAIN;
ix.mtspec = 0;
rc = lj_record_idx(J, &ix);
break;
@ -2466,6 +2488,12 @@ void lj_record_ins(jit_State *J)
/* fallthrough */
case BC_TGETV: case BC_TGETS: case BC_TSETV: case BC_TSETS:
ix.idxchain = LJ_MAX_IDXCHAIN;
ix.mtspec = 0;
rc = lj_record_idx(J, &ix);
break;
case BC_TGETSS:
ix.idxchain = LJ_MAX_IDXCHAIN;
ix.mtspec = 1;
rc = lj_record_idx(J, &ix);
break;
case BC_TGETR: case BC_TSETR:

View File

@ -23,7 +23,8 @@ typedef struct RecordIndex {
TRef val; /* Value reference for a store or 0 for a load. */
TRef mt; /* Metatable reference. */
TRef mobj; /* Metamethod object reference. */
int idxchain; /* Index indirections left or 0 for raw lookup. */
short idxchain; /* Index indirections left or 0 for raw lookup. */
short mtspec; /* Specialize to metatable. */
} RecordIndex;
LJ_FUNC int lj_record_objcmp(jit_State *J, TRef a, TRef b,

View File

@ -2592,6 +2592,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|=>defop:
switch (op) {
@ -3542,6 +3546,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
| b ->vmeta_tgetv
break;
case BC_TGETS:
|=>BC_TGETSS:
| decode_RB8 RB, INS
| and RC, RC, #255
| // RA = dst*8, RB = table*8, RC = str_const (~)

View File

@ -2226,6 +2226,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|=>defop:
switch (op) {
@ -2992,6 +2996,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
| b ->BC_TGETS_Z
break;
case BC_TGETS:
|=>BC_TGETSS:
| decode_RB RB, INS
| and RC, RC, #255
| // RA = dst, RB = table, RC = str_const (~)

View File

@ -2981,6 +2981,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|=>defop:
switch (op) {
@ -4076,6 +4080,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
|. nop
break;
case BC_TGETS:
|=>BC_TGETSS:
| // RA = dst*8, RB = table*8, RC = str_const*4 (~)
| decode_RB8a RB, INS
| decode_RB8b RB

View File

@ -3104,6 +3104,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|=>defop:
switch (op) {
@ -4290,6 +4294,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
|. nop
break;
case BC_TGETS:
|=>BC_TGETSS:
| // RA = dst*8, RB = table*8, RC = str_const*8 (~)
| decode_RB8a RB, INS
| decode_RB8b RB

View File

@ -3310,6 +3310,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|=>defop:
switch (op) {
@ -4563,6 +4567,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
| b ->BC_TGETS_Z // String key?
break;
case BC_TGETS:
|=>BC_TGETSS:
| // RA = dst*8, RB = table*8, RC = str_const*8 (~)
| lwzux CARG1, RB, BASE
| srwi TMP1, RC, 1

View File

@ -2796,6 +2796,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|// Note: aligning all instructions does not pay off.
|=>defop:
@ -3678,6 +3682,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
| jmp ->BC_TGETS_Z
break;
case BC_TGETS:
|=>BC_TGETSS:
| ins_ABC // RA = dst, RB = table, RC = str const (~)
| mov TAB:RB, [BASE+RB*8]
| not RC

View File

@ -3381,6 +3381,10 @@ static void build_subroutines(BuildCtx *ctx)
static void build_ins(BuildCtx *ctx, BCOp op, int defop)
{
int vk = 0;
if (bc_isalias(op))
return;
|// Note: aligning all instructions does not pay off.
|=>defop:
@ -4309,6 +4313,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
| jmp ->BC_TGETS_Z
break;
case BC_TGETS:
|=>BC_TGETSS:
| ins_ABC // RA = dst, RB = table, RC = str const (~)
| not RCa
| mov STR:RC, [KBASE+RC*4]