📄 ljit_x86.dasc
字号:
| call &luaH_newkey, L, TABLE:edi, TVALUE:eax | add esp, FRAME_OFFSET | jmp <2 // Copy to the returned value. See Node/TValue assumption above. | |6: // Key found, but previous value is nil. | mov TABLE:ecx, TABLE:edi->metatable | test TABLE:ecx, TABLE:ecx | jz <2 // No metatable? | test byte TABLE:ecx->flags, 1<<TM_NEWINDEX | jnz <2 // Or 'no __newindex' flag set? | |7: // Otherwise chain to C code which eventually calls luaV_settable. | setsvalue L->env, TSTRING:edx // Use L->env as temp key. | mov ecx, [esp] | sub esp, FRAME_OFFSET | mov L->savedpc, ecx | call &jit_settable_fb, L, TABLE:edi, BASE | add esp, FRAME_OFFSET | mov BASE, L->base | ret |.endjsub | |//----------------------------------------------------------------------- |.jsub SETTABLE_STR // Set string entry in table. |// Call with: TOP (tab), TVALUE:ecx (key), BASE (val) | mov eax, TOP->tt; shl eax, 4; or eax, TVALUE:ecx->tt | cmp eax, LUA_TTABLE_STR | mov TABLE:edi, TOP->value | mov TSTRING:edx, TVALUE:ecx->value | je <9 // Types ok? Continue above. | jmp ->DEOPTIMIZE_CALLER // Otherwise deoptimize. |.endjsub}/* ------------------------------------------------------------------------ */static void jit_op_newtable(jit_State *J, int dest, int lnarray, int lnhash){ | call &luaH_new, L, luaO_fb2int(lnarray), luaO_fb2int(lnhash) | sethvalue BASE[dest], eax jit_checkGC(J);}static void jit_op_getglobal(jit_State *J, int dest, int kidx){ const TValue *kk = &J->pt->k[kidx]; jit_assert(ttisstring(kk)); | mov TSTRING:edx, &&kk->value.gc->ts | addidx BASE, dest | call ->GETGLOBAL}static void jit_op_setglobal(jit_State *J, int rval, int kidx){ const TValue *kk = &J->pt->k[kidx]; jit_assert(ttisstring(kk)); | mov TSTRING:edx, &&kk->value.gc->ts | addidx BASE, rval | call ->SETGLOBAL}enum { TKEY_KSTR = -2, TKEY_STR = -1, TKEY_ANY = 0 };/* Optimize key lookup depending on consts or hints type. */static int jit_keylookup(jit_State *J, int tab, int rkey){ const TValue *tabt = hint_get(J, TYPE); const TValue *key; if (!ttistable(tabt)) return TKEY_ANY; /* Not a table? Use fallback. */ key = ISK(rkey) ? &J->pt->k[INDEXK(rkey)] : hint_get(J, TYPEKEY); if (ttisstring(key)) { /* String key? */ if (ISK(rkey)) { | lea TOP, BASE[tab] | mov TSTRING:edx, &&key->value.gc->ts return TKEY_KSTR; /* Const string key. */ } else { | lea TOP, BASE[tab] | lea TVALUE:ecx, BASE[rkey] return TKEY_STR; /* Var string key. */ } } else if (ttisnumber(key)) { /* Number key? */ lua_Number n = nvalue(key); int k; lua_number2int(k, n); if (!(k >= 1 && k < (1 << 26) && (lua_Number)k == n)) return TKEY_ANY; /* Not a proper array key? Use fallback. */ if (ISK(rkey)) { | istable tab | mov TABLE:edi, BASE[tab].value | jne >9 // TYPE hint was wrong? | mov ecx, k // Needed for hash fallback. | mov TVALUE:eax, TABLE:edi->array | cmp ecx, TABLE:edi->sizearray; ja >5 // Not in array part? return k; /* Const array key (>= 1). */ } else { | mov eax, BASE[tab].tt; shl eax, 4; or eax, BASE[rkey].tt | cmp eax, LUA_TTABLE_NUM; jne >9 // TYPE/TYPEKEY hint was wrong? if (J->flags & JIT_F_CPU_SSE2) { | movsd xmm0, qword BASE[rkey] | cvttsd2si eax, xmm0 | cvtsi2sd xmm1, eax | dec eax | ucomisd xmm1, xmm0 | mov TABLE:edi, BASE[tab].value | jne >9; jp >9 // Not an integer? Deoptimize. } else { |// Annoying x87 stuff: check whether a number is an integer. |// The latency of fist/fild is the real problem here. | fld qword BASE[rkey].value | fist dword TMP1 | fild dword TMP1 | fcomparepp // eax may be modified. | jne >9; jp >9 // Not an integer? Deoptimize. | mov eax, TMP1 | mov TABLE:edi, BASE[tab].value | dec eax } | cmp eax, TABLE:edi->sizearray; jae >5 // Not in array part? | TValuemul eax | add eax, TABLE:edi->array return 1; /* Variable array key. */ } } return TKEY_ANY; /* Use fallback. */}static void jit_op_gettable(jit_State *J, int dest, int tab, int rkey){ int k = jit_keylookup(J, tab, rkey); switch (k) { case TKEY_KSTR: /* Const string key. */ | addidx BASE, dest | call ->GETTABLE_KSTR break; case TKEY_STR: /* Variable string key. */ | addidx BASE, dest | call ->GETTABLE_STR break; case TKEY_ANY: /* Generic gettable fallback. */ if (ISK(rkey)) { | mov ecx, &&J->pt->k[INDEXK(rkey)] } else { | lea ecx, BASE[rkey] } | lea edx, BASE[tab] | addidx BASE, dest | mov L->savedpc, &J->nextins | call &luaV_gettable, L, edx, ecx, BASE | mov BASE, L->base break; default: /* Array key. */ |// This is really copyslot BASE[dest], TVALUE:eax[k-1] mixed with compare. |1: | mov edx, TVALUE:eax[k-1].tt | test edx, edx; je >6 // Array has nil value? if (J->flags & JIT_F_CPU_SSE2) { | movq xmm0, qword TVALUE:eax[k-1].value | movq qword BASE[dest].value, xmm0 } else { | mov ecx, TVALUE:eax[k-1].value | mov eax, TVALUE:eax[k-1].value.na[1] | mov BASE[dest].value, ecx | mov BASE[dest].value.na[1], eax } |2: | mov BASE[dest].tt, edx |.tail |5: // Fallback to hash part. TABLE:edi is callee-saved. if (ISK(rkey)) { | call ->GETTABLE_KNUM } else { | call ->GETTABLE_NUM } | jmp <1 // Slot is at TVALUE:eax[k-1]. | |6: // Shortcut for tables without an __index metamethod. | mov TABLE:ecx, TABLE:edi->metatable | test TABLE:ecx, TABLE:ecx | jz <2 // No metatable? | test byte TABLE:ecx->flags, 1<<TM_INDEX | jnz <2 // Or 'no __index' flag set? | |9: // Otherwise deoptimize. | mov edx, &J->nextins | jmp ->DEOPTIMIZE |.code break; } |.jsub GETTABLE_KNUM // Gettable fallback for const numeric keys. | mov TMP2, ecx // Save k. | sub esp, FRAME_OFFSET | call &luaH_getnum, TABLE:edi, ecx | add esp, FRAME_OFFSET | mov ecx, TMP2 // Restore k. | TValuemul ecx | sub TVALUE:eax, ecx // Compensate for TVALUE:eax[k-1]. | add TVALUE:eax, #TVALUE | ret |.endjsub | |.jsub GETTABLE_NUM // Gettable fallback for variable numeric keys. | inc eax | mov ARG2, TABLE:edi // Really ARG1 and ARG2. | mov ARG3, eax | jmp &luaH_getnum // Chain to C code. |.endjsub}static void jit_op_settable(jit_State *J, int tab, int rkey, int rval){ const TValue *val = ISK(rval) ? &J->pt->k[INDEXK(rval)] : NULL; int k = jit_keylookup(J, tab, rkey); switch (k) { case TKEY_KSTR: /* Const string key. */ case TKEY_STR: /* Variable string key. */ if (ISK(rval)) { | mov BASE, &val } else { | addidx BASE, rval } if (k == TKEY_KSTR) { | call ->SETTABLE_KSTR } else { | call ->SETTABLE_STR } break; case TKEY_ANY: /* Generic settable fallback. */ if (ISK(rkey)) { | mov ecx, &&J->pt->k[INDEXK(rkey)] } else { | lea ecx, BASE[rkey] } if (ISK(rval)) { | mov edx, &val } else { | lea edx, BASE[rval] } | addidx BASE, tab | mov L->savedpc, &J->nextins | call &luaV_settable, L, BASE, ecx, edx | mov BASE, L->base break; default: /* Array key. */ |1: | tvisnil TVALUE:eax[k-1]; je >6 // Previous value is nil? |2: |.tail |5: // Fallback to hash part. TABLE:edi is callee-saved. if (ISK(rkey)) { | call ->SETTABLE_KNUM } else { | call ->SETTABLE_NUM } | jmp <1 // Slot is at TVALUE:eax[k-1]. | |6: // Shortcut for tables without a __newindex metamethod. | mov TABLE:ecx, TABLE:edi->metatable | test TABLE:ecx, TABLE:ecx | jz <2 // No metatable? | test byte TABLE:ecx->flags, 1<<TM_NEWINDEX | jnz <2 // Or 'no __newindex' flag set? | |9: // Otherwise deoptimize. | mov edx, &J->nextins | jmp ->DEOPTIMIZE |.code if (!ISK(rval) || iscollectable(val)) { | test byte TABLE:edi->marked, bitmask(BLACKBIT) // isblack(table) | jnz >7 // Unlikely, but set barrier back. |3: |.tail |7: // Avoid valiswhite() check -- black2gray(table) is ok. | call ->BARRIERBACK | jmp <3 |.code } if (ISK(rval)) { | copyconst TVALUE:eax[k-1], val } else { | copyslot TVALUE:eax[k-1], BASE[rval], ecx, edx, TOP } break; } |.jsub SETTABLE_KNUM // Settable fallback for const numeric keys. | mov TMP2, ecx // Save k. | sub esp, FRAME_OFFSET | call &luaH_setnum, L, TABLE:edi, ecx | add esp, FRAME_OFFSET | mov ecx, TMP2 // Restore k. | TValuemul ecx | sub TVALUE:eax, ecx // Compensate for TVALUE:eax[k-1]. | add TVALUE:eax, #TVALUE | ret |.endjsub | |.jsub SETTABLE_NUM // Settable fallback for variable numeric keys. | inc eax | mov ARG2, L // Really ARG1, ARG2 and ARG3. | mov ARG3, TABLE:edi | mov ARG4, eax | jmp &luaH_setnum // Chain to C code. |.endjsub}static void jit_op_self(jit_State *J, int dest, int tab, int rkey){ | copyslot BASE[dest+1], BASE[tab] jit_op_gettable(J, dest, tab, rkey);}/* ------------------------------------------------------------------------ */static void jit_op_setlist(jit_State *J, int ra, int num, int batch){ if (batch == 0) { batch = (int)(*J->nextins); J->combine++; } batch = (batch-1)*LFIELDS_PER_FLUSH; if (num == 0) { /* Previous op was open and set TOP: {f()} or {...}. */ | mov L->env.value, TOP // Need to save TOP (edi). | lea eax, BASE[ra+1] | sub eax, TOP | neg eax | TValuediv eax // num = (TOP-ra-1)/sizeof(TValue). | mov TABLE:edi, BASE[ra].value | jz >4 // Nothing to set? if (batch > 0) { | add eax, batch } | cmp dword TABLE:edi->sizearray, eax | jae >1 // Skip resize if not needed. | // A resize is likely, so inline it. | call &luaH_resizearray, L, TABLE:edi, eax |1: | test byte TABLE:edi->marked, bitmask(BLACKBIT) // isblack(table) | mov edx, TABLE:edi->array | jnz >6 // Unlikely, but set barrier back. | mov TOP, L->env.value | |.tail |6: // Avoid lots of valiswhite() checks -- black2gray(table) is ok. | call ->BARRIERBACK | jmp <1 // Need to reload edx. |.code } else { /* Set fixed number of args. */ | mov TABLE:edi, BASE[ra].value // edi is callee-save. | cmp dword TABLE:edi->sizearray, batch+num | jb >5 // Need to resize array? |1: | test byte TABLE:edi->marked, bitmask(BLACKBIT) // isblack(table) | mov edx, TABLE:edi->array | jnz >6 // Unlikely, but set barrier back. | lea TOP, BASE[ra+1+num] // Careful: TOP is edi. | |.tail |5: // A resize is unlikely (impossible?). NEWTABLE should've done it. | call &luaH_resizearray, L, TABLE:edi, batch+num | jmp <1 |6: // Avoid lots of valiswhite() checks -- black2gray(table) is ok. | call ->BARRIERBACK | jmp <1 // Need to reload edx. |.code } if (batch > 0) { | add edx, batch*#TVALUE // edx = &t->array[(batch+1)-1] } | lea ecx, BASE[ra+1] |3: // Copy stack slots to array. | mov eax, [ecx] | add ecx, aword*1 | mov [edx], eax | add edx, aword*1 | cmp ecx, TOP | jb <3 | |4: if (num == 0) { /* Previous op was open. Restore L->top. */ | lea TOP, BASE[J->pt->maxstacksize] // Faster than getting L->ci->top. | mov L->top, TOP }}/* ------------------------------------------------------------------------ */static void jit_op_arith(jit_State *J, int dest, int rkb, int rkc, int ev){ const TValue *kkb = ISK(rkb) ? &J->pt->k[INDEXK(rkb)] : NULL; const TValue *kkc = ISK(rkc) ? &J->pt->k[INDEXK(rkc)] : NULL; const Value *kval; int idx, rev; int target = (ev == TM_LT || ev == TM_LE) ? jit_jmp_target(J) : 0; int hastail = 0; /* The bytecode compiler already folds constants except for: k/0, k%0, */ /* NaN results, k1<k2, k1<=k2. No point in optimizing these cases. */ if (ISK(rkb&rkc)) goto fallback; /* Avoid optimization when non-numeric constants are present. */ if (kkb ? !ttisnumber(kkb) : (kkc && !ttisnumber(kkc))) goto fallback; /* The TYPE hint selects numeric inlining and/or fallback encoding. */ switch (ttype(hint_get(J, TYPE))) { case LUA_TNIL: hastail = 1; break; /* No hint: numeric + fallback. */ case LUA_TNUMBER: break; /* Numbers: numeric + deoptimization. */ default: goto fallback; /* Mixed/other types: fallback only. */ } /* The checks above ensure: at most one of the operands is a constant. */ /* Reverse operation and swap operands so the 2nd operand is a variable. */ if (kkc) { kval = &kkc->value; idx = rkb; rev = 1; } else { kval = kkb ? &kkb->value : NULL; idx = rkc; rev = 0; } /* Special handling for some operators. */ switch (ev) { case TM_MOD: /* Check for modulo with positive numbers, so we can use fprem. */ if (kval) { if (kval->na[1] < 0) { hastail = 0; goto fallback; } /* x%-k, -k%x */ | isnumber idx | mov eax, BASE[idx].value.na[1] | jne L_DEOPTIMIZEF | test eax, eax; js L_DEOPTIMIZEF |// This will trigger deoptimization in some benchmarks (pidigits). |// But it's still a win. if (kkb) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -