diff --git a/js/src/lirasm/lirasm.cpp b/js/src/lirasm/lirasm.cpp index 735cc5bd4755..1a272059b755 100644 --- a/js/src/lirasm/lirasm.cpp +++ b/js/src/lirasm/lirasm.cpp @@ -643,9 +643,9 @@ assemble(istream &in, map labels; map > op_map; -#define OPDEF(op, number, args) \ +#define OPDEF(op, number, args, repkind) \ op_map[#op] = make_pair(LIR_##op, args); -#define OPDEF64(op, number, args) \ +#define OPDEF64(op, number, args, repkind) \ op_map[#op] = make_pair(LIR_##op, args); #include "nanojit/LIRopcode.tbl" #undef OPDEF diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index dde5bd198f61..a19ed1dc9c05 100644 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -1565,7 +1565,7 @@ namespace nanojit for (int i=0, n = NumSavedRegs; i < n; i++) { LIns *p = b->savedRegs[i]; if (p) - findSpecificRegFor(p, savedRegs[p->imm8()]); + findSpecificRegFor(p, savedRegs[p->paramArg()]); } } @@ -1584,10 +1584,10 @@ namespace nanojit { LInsp state = _thisfrag->lirbuf->state; if (state) - findSpecificRegFor(state, argRegs[state->imm8()]); + findSpecificRegFor(state, argRegs[state->paramArg()]); LInsp param1 = _thisfrag->lirbuf->param1; if (param1) - findSpecificRegFor(param1, argRegs[param1->imm8()]); + findSpecificRegFor(param1, argRegs[param1->paramArg()]); } void Assembler::handleLoopCarriedExprs() diff --git a/js/src/nanojit/Fragmento.cpp b/js/src/nanojit/Fragmento.cpp index d440463a0cda..97d91ddcce4d 100644 --- a/js/src/nanojit/Fragmento.cpp +++ b/js/src/nanojit/Fragmento.cpp @@ -84,12 +84,13 @@ namespace nanojit /* Opcodes must be strictly increasing without holes. */ uint32_t count = 0; - #define OPDEF(op, number, operands) \ - NanoAssertMsg(LIR_##op == count++, "misnumbered opcode"); - #define OPDEF64(op, number, operands) OPDEF(op, number, operands) - #include "LIRopcode.tbl" - #undef OPDEF - #undef OPDEF64 +#define OPDEF(op, number, operands, repkind) \ + NanoAssertMsg(LIR_##op == count++, "misnumbered opcode"); +#define OPDEF64(op, number, operands, repkind) \ + OPDEF(op, number, operands, repkind) +#include "LIRopcode.tbl" +#undef OPDEF +#undef OPDEF64 } #endif diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 7d6cd65739ed..22fb37de3ed2 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -51,9 +51,9 @@ namespace nanojit #ifdef FEATURE_NANOJIT const uint8_t operandCount[] = { -#define OPDEF(op, number, operands) \ +#define OPDEF(op, number, operands, repkind) \ operands, -#define OPDEF64(op, number, operands) \ +#define OPDEF64(op, number, operands, repkind) \ operands, #include "LIRopcode.tbl" #undef OPDEF @@ -61,13 +61,35 @@ namespace nanojit 0 }; + const uint8_t repKinds[] = { +#define OPDEF(op, number, operands, repkind) \ + LRK_##repkind, +#define OPDEF64(op, number, operands, repkind) \ + OPDEF(op, number, operands, repkind) +#include "LIRopcode.tbl" +#undef OPDEF +#undef OPDEF64 + 0 + }; + + const uint8_t insSizes[] = { +#define OPDEF(op, number, operands, repkind) \ + sizeof(LIns##repkind), +#define OPDEF64(op, number, operands, repkind) \ + OPDEF(op, number, operands, repkind) +#include "LIRopcode.tbl" +#undef OPDEF +#undef OPDEF64 + 0 + }; + // LIR verbose specific #ifdef NJ_VERBOSE const char* lirNames[] = { -#define OPDEF(op, number, operands) \ +#define OPDEF(op, number, operands, repkind) \ #op, -#define OPDEF64(op, number, operands) \ +#define OPDEF64(op, number, operands, repkind) \ #op, #include "LIRopcode.tbl" #undef OPDEF @@ -131,7 +153,8 @@ namespace nanojit int32_t LirBuffer::insCount() { - // Doesn't include LIR_skip payload or LIR_call arg slots. + // A LIR_skip payload is considered part of the LIR_skip, and LIR_call + // arg slots are considered part of the LIR_call. return _stats.lir; } @@ -165,10 +188,10 @@ namespace nanojit // Unlike all the ins*() functions, we don't call makeRoom() here // because we know we have enough space, having just started a new // page. - LInsp l = (LInsp)_unused; - l->setIns1(LIR_skip, (LInsp)addrOfLastLInsOnCurrentPage); - l->resv()->clear(); - _unused += sizeof(LIns); + LInsSk* insSk = (LInsSk*)_unused; + LIns* ins = insSk->getLIns(); + ins->initLInsSk((LInsp)addrOfLastLInsOnCurrentPage); + _unused += sizeof(LInsSk); _stats.lir++; } @@ -208,40 +231,42 @@ namespace nanojit moveToNewPage(addrOfLastLInsOnPage); } + // Make sure it's word-aligned. + NanoAssert(0 == startOfRoom % sizeof(void*)); return startOfRoom; } LInsp LirBufWriter::insStorei(LInsp val, LInsp base, int32_t d) { LOpcode op = val->isQuad() ? LIR_stqi : LIR_sti; - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setStorei(op, val, base, d); - l->resv()->clear(); - return l; + LInsSti* insSti = (LInsSti*)_buf->makeRoom(sizeof(LInsSti)); + LIns* ins = insSti->getLIns(); + ins->initLInsSti(op, val, base, d); + return ins; } LInsp LirBufWriter::ins0(LOpcode op) { - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setIns0(op); - l->resv()->clear(); - return l; + LInsOp0* insOp0 = (LInsOp0*)_buf->makeRoom(sizeof(LInsOp0)); + LIns* ins = insOp0->getLIns(); + ins->initLInsOp0(op); + return ins; } LInsp LirBufWriter::ins1(LOpcode op, LInsp o1) { - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setIns1(op, o1); - l->resv()->clear(); - return l; + LInsOp1* insOp1 = (LInsOp1*)_buf->makeRoom(sizeof(LInsOp1)); + LIns* ins = insOp1->getLIns(); + ins->initLInsOp1(op, o1); + return ins; } LInsp LirBufWriter::ins2(LOpcode op, LInsp o1, LInsp o2) { - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setIns2(op, o1, o2); - l->resv()->clear(); - return l; + LInsOp2* insOp2 = (LInsOp2*)_buf->makeRoom(sizeof(LInsOp2)); + LIns* ins = insOp2->getLIns(); + ins->initLInsOp2(op, o1, o2); + return ins; } LInsp LirBufWriter::insLoad(LOpcode op, LInsp base, LInsp d) @@ -263,39 +288,39 @@ namespace nanojit LInsp LirBufWriter::insAlloc(int32_t size) { size = (size+3)>>2; // # of required 32bit words - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setAlloc(LIR_alloc, size); - l->resv()->clear(); - return l; + LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); + LIns* ins = insI->getLIns(); + ins->initLInsI(LIR_alloc, size); + return ins; } LInsp LirBufWriter::insParam(int32_t arg, int32_t kind) { - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setParam(LIR_param, arg, kind); - l->resv()->clear(); + LInsP* insP = (LInsP*)_buf->makeRoom(sizeof(LInsP)); + LIns* ins = insP->getLIns(); + ins->initLInsP(arg, kind); if (kind) { NanoAssert(arg < NumSavedRegs); - _buf->savedRegs[arg] = l; + _buf->savedRegs[arg] = ins; _buf->explicitSavedRegs = true; } - return l; + return ins; } LInsp LirBufWriter::insImm(int32_t imm) { - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setImm(LIR_int, imm); - l->resv()->clear(); - return l; + LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); + LIns* ins = insI->getLIns(); + ins->initLInsI(LIR_int, imm); + return ins; } LInsp LirBufWriter::insImmq(uint64_t imm) { - LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); - l->setImmq(LIR_quad, imm); - l->resv()->clear(); - return l; + LInsI64* insI64 = (LInsI64*)_buf->makeRoom(sizeof(LInsI64)); + LIns* ins = insI64->getLIns(); + ins->initLInsI64(LIR_quad, imm); + return ins; } LInsp LirBufWriter::insSkip(size_t payload_szB) @@ -308,14 +333,14 @@ namespace nanojit NanoAssert(0 == NJ_MAX_SKIP_PAYLOAD_SZB % sizeof(void*)); NanoAssert(sizeof(void*) <= payload_szB && payload_szB <= NJ_MAX_SKIP_PAYLOAD_SZB); - uintptr_t payload = _buf->makeRoom(payload_szB + sizeof(LIns)); // payload + skip + uintptr_t payload = _buf->makeRoom(payload_szB + sizeof(LInsSk)); uintptr_t prevLInsAddr = payload - sizeof(LIns); - LInsp l = (LInsp)(payload + payload_szB); + LInsSk* insSk = (LInsSk*)(payload + payload_szB); + LIns* ins = insSk->getLIns(); NanoAssert(prevLInsAddr >= pageDataStart(prevLInsAddr)); - NanoAssert(samepage(prevLInsAddr, l)); - l->setIns1(LIR_skip, (LInsp)prevLInsAddr); - l->resv()->clear(); - return l; + NanoAssert(samepage(prevLInsAddr, insSk)); + ins->initLInsSk((LInsp)prevLInsAddr); + return ins; } // Reads the next non-skip instruction. @@ -341,33 +366,38 @@ namespace nanojit do { - switch (iop) - { - default: - i -= sizeof(LIns); - break; + // Nb: this switch is table-driven (because sizeof_LInsXYZ() is + // table-driven) in most cases to avoid branch mispredictions -- + // if we do a vanilla switch on the iop or LInsRepKind the extra + // branch mispredictions cause a small but noticeable slowdown. + switch (iop) + { + default: + i -= insSizes[((LInsp)i)->opcode()]; + break; #if defined NANOJIT_64BIT - case LIR_callh: + case LIR_callh: #endif - case LIR_call: - case LIR_fcall: { + case LIR_call: + case LIR_fcall: { int argc = ((LInsp)i)->argc(); - uintptr_t prev = i - sizeof(LIns) - argc*sizeof(LInsp); - NanoAssert( samepage(i, prev) ); - i = prev; + i -= sizeof(LInsC); // step over the instruction + i -= argc*sizeof(LInsp); // step over the arguments + NanoAssert( samepage(i, _i) ); break; } - case LIR_skip: - NanoAssert(((LInsp)i)->oprnd1() != (LInsp)i); - i = uintptr_t(((LInsp)i)->oprnd1()); - break; + case LIR_skip: + // Ignore the skip, move onto its predecessor. + NanoAssert(((LInsp)i)->prevLIns() != (LInsp)i); + i = uintptr_t(((LInsp)i)->prevLIns()); + break; - case LIR_start: - _i = 0; // start of trace - return cur; - } + case LIR_start: + _i = 0; // this means the next call to this method will return 0 + return cur; + } iop = ((LInsp)i)->opcode(); } while (iop==LIR_skip || iop==LIR_2); @@ -376,7 +406,7 @@ namespace nanojit } bool LIns::isFloat() const { - switch (firstWord.code) { + switch (opcode()) { default: return false; case LIR_fadd: @@ -392,107 +422,69 @@ namespace nanojit } #if defined(_DEBUG) - bool LIns::isOp1() const { - switch (firstWord.code) { - case LIR_skip: - case LIR_ret: - case LIR_live: - case LIR_neg: -#if !defined NANOJIT_64BIT - case LIR_callh: -#endif - case LIR_not: - case LIR_qlo: - case LIR_qhi: - case LIR_ov: - case LIR_cs: - case LIR_file: - case LIR_line: - case LIR_fret: - case LIR_fneg: - case LIR_i2f: - case LIR_u2f: - case LIR_mod: - return true; - - default: - return false; - } + bool LIns::isLInsOp0() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op0 == repKinds[opcode()]; } - // Nb: this excludes loads and stores, which are covered by isLoad() and - // isStore(). - bool LIns::isOp2() const { - switch (firstWord.code) { - case LIR_loop: - case LIR_x: - case LIR_jt: - case LIR_jf: - case LIR_feq: - case LIR_flt: - case LIR_fgt: - case LIR_fle: - case LIR_fge: - case LIR_cmov: - case LIR_add: - case LIR_sub: - case LIR_mul: - case LIR_div: - case LIR_and: - case LIR_or: - case LIR_xor: - case LIR_lsh: - case LIR_rsh: - case LIR_ush: - case LIR_xt: - case LIR_xf: - case LIR_eq: - case LIR_lt: - case LIR_gt: - case LIR_le: - case LIR_ge: - case LIR_ult: - case LIR_ugt: - case LIR_ule: - case LIR_uge: - case LIR_2: - case LIR_xbarrier: - case LIR_xtbl: - case LIR_qiand: - case LIR_qiadd: - case LIR_qjoin: - case LIR_qcmov: - case LIR_fadd: - case LIR_fsub: - case LIR_fmul: - case LIR_fdiv: - case LIR_qior: - case LIR_qilsh: - return true; + bool LIns::isLInsOp1() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op1 == repKinds[opcode()]; + } - default: - return false; - } + bool LIns::isLInsOp2() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Op2 == repKinds[opcode()]; + } + + bool LIns::isLInsSti() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Sti == repKinds[opcode()]; + } + + bool LIns::isLInsSk() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_Sk == repKinds[opcode()]; + } + + bool LIns::isLInsC() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_C == repKinds[opcode()]; + } + + bool LIns::isLInsP() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_P == repKinds[opcode()]; + } + + bool LIns::isLInsI() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_I == repKinds[opcode()]; + } + + bool LIns::isLInsI64() const { + NanoAssert(LRK_None != repKinds[opcode()]); + return LRK_I64 == repKinds[opcode()]; } #endif // defined(_DEBUG) bool LIns::isCmp() const { - LOpcode op = firstWord.code; + LOpcode op = opcode(); return (op >= LIR_eq && op <= LIR_uge) || (op >= LIR_feq && op <= LIR_fge); } bool LIns::isCond() const { - LOpcode op = firstWord.code; + LOpcode op = opcode(); return (op == LIR_ov) || (op == LIR_cs) || isCmp(); } bool LIns::isQuad() const { #ifdef AVMPLUS_64BIT // callh in 64bit cpu's means a call that returns an int64 in a single register - return (firstWord.code & LIR64) != 0 || firstWord.code == LIR_callh; + return (opcode() & LIR64) != 0 || opcode() == LIR_callh; #else // callh in 32bit cpu's means the 32bit MSW of an int64 result in 2 registers - return (firstWord.code & LIR64) != 0; + return (opcode() & LIR64) != 0; #endif } @@ -503,7 +495,7 @@ namespace nanojit bool LIns::isconstq() const { - return firstWord.code == LIR_quad; + return opcode() == LIR_quad; } bool LIns::isconstp() const @@ -517,14 +509,14 @@ namespace nanojit bool LIns::isCse() const { - return nanojit::isCseOpcode(firstWord.code) || (isCall() && callInfo()->_cse); + return nanojit::isCseOpcode(opcode()) || (isCall() && callInfo()->_cse); } void LIns::setTarget(LInsp label) { NanoAssert(label && label->isop(LIR_label)); NanoAssert(isBranch()); - u.oprnd_2 = label; + toLInsOp2()->oprnd_2 = label; } LInsp LIns::getTarget() @@ -536,15 +528,15 @@ namespace nanojit void *LIns::payload() const { NanoAssert(isop(LIR_skip)); - // Operand 1 points to the previous instruction; we move one - // instruction past it to get to the payload. - return (void*) (intptr_t(oprnd1()) + sizeof(LIns)); + // Operand 1 points to the previous LIns; we move past it to get to + // the payload. + return (void*) (uintptr_t(prevLIns()) + sizeof(LIns)); } uint64_t LIns::imm64() const { NanoAssert(isconstq()); - return (uint64_t(i64.imm64_1) << 32) | uint32_t(i64.imm64_0); + return (uint64_t(toLInsI64()->imm64_1) << 32) | uint32_t(toLInsI64()->imm64_0); } double LIns::imm64f() const @@ -560,7 +552,7 @@ namespace nanojit const CallInfo* LIns::callInfo() const { NanoAssert(isCall()); - return c.ci; + return toLInsC()->ci; } // Index args in r-l order. arg(0) is rightmost arg. @@ -569,8 +561,9 @@ namespace nanojit { NanoAssert(isCall()); NanoAssert(i < argc()); - LInsp* offs = (LInsp*)this - (i+1); - return *offs; + // Move to the start of the LInsC, then move back one word per argument. + LInsp* argSlot = (LInsp*)(uintptr_t(toLInsC()) - (i+1)*sizeof(void*)); + return *argSlot; } LIns* LirWriter::ins2i(LOpcode v, LIns* oprnd1, int32_t imm) @@ -1011,38 +1004,23 @@ namespace nanojit op = LIR_callh; } - // An example of what we're trying to serialize (for a 32-bit machine): - // - // byte - // ---- - // N+0 [ arg operand #2 ---------------------- - // N+4 arg operand #1 ---------------------- - // N+8 arg operand #0 ---------------------- ] - // N+12 [ resv + code=LIR_call - // N+16 imm8a | imm8b | (pad16) ------------- - // N+20 ci ---------------------------------- - // N+24 (pad32) ----------------------------- ] - // - // In this example: - // 'argc' = 3 - NanoAssert(argc <= (int)MAXARGS); // Lay the call parameters out (in reverse order). // Nb: this must be kept in sync with arg(). - LInsp* newargs = (LInsp*)_buf->makeRoom(argc*sizeof(LInsp) + sizeof(LIns)); // args + call + LInsp* newargs = (LInsp*)_buf->makeRoom(argc*sizeof(LInsp) + sizeof(LInsC)); // args + call for (int32_t i = 0; i < argc; i++) newargs[argc - i - 1] = args[i]; // Write the call instruction itself. - LInsp l = (LInsp)(uintptr_t(newargs) + argc*sizeof(LInsp)); + LInsC* insC = (LInsC*)(uintptr_t(newargs) + argc*sizeof(LInsp)); + LIns* ins = insC->getLIns(); #ifndef NANOJIT_64BIT - l->setCall(op==LIR_callh ? LIR_call : op, argc, ci); + ins->initLInsC(op==LIR_callh ? LIR_call : op, argc, ci); #else - l->setCall(op, argc, ci); + ins->initLInsC(op, argc, ci); #endif - l->resv()->clear(); - return l; + return ins; } using namespace avmplus; @@ -1458,9 +1436,9 @@ namespace nanojit RetiredEntry *e = NJ_NEW(gc, RetiredEntry)(gc); e->i = i; for (int j=0, n=live.size(); j < n; j++) { - LInsp l = live.keyAt(j); - if (!l->isStore() && !l->isGuard()) - e->live.add(l); + LInsp ins = live.keyAt(j); + if (!ins->isStore() && !ins->isGuard()) + e->live.add(ins); } int size=0; if ((size = e->live.size()) > maxlive) @@ -1707,8 +1685,8 @@ namespace nanojit } case LIR_param: { - uint32_t arg = i->imm8(); - if (!i->imm8b()) { + uint32_t arg = i->paramArg(); + if (!i->paramKind()) { if (arg < sizeof(Assembler::argRegs)/sizeof(Assembler::argRegs[0])) { sprintf(s, "%s = %s %d %s", formatRef(i), lirNames[op], arg, gpn(Assembler::argRegs[arg])); diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index 59ceaf3c068c..ea3c6127599b 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -58,9 +58,9 @@ namespace nanojit // flags; upper bits reserved LIR64 = 0x40, // result is double or quad -#define OPDEF(op, number, args) \ +#define OPDEF(op, number, args, repkind) \ LIR_##op = (number), -#define OPDEF64(op, number, args) \ +#define OPDEF64(op, number, args, repkind) \ LIR_##op = ((number) | LIR64), #include "LIRopcode.tbl" LIR_sentinel @@ -70,7 +70,6 @@ namespace nanojit #if defined NANOJIT_64BIT #define LIR_ldp LIR_ldq - #define LIR_stp LIR_stq #define LIR_piadd LIR_qiadd #define LIR_piand LIR_qiand #define LIR_pilsh LIR_qilsh @@ -78,7 +77,6 @@ namespace nanojit #define LIR_pior LIR_qior #else #define LIR_ldp LIR_ld - #define LIR_stp LIR_st #define LIR_piadd LIR_add #define LIR_piand LIR_and #define LIR_pilsh LIR_lsh @@ -148,13 +146,6 @@ namespace nanojit return (op & ~LIR64) == LIR_ret; } - // Sun Studio requires explicitly declaring signed int bit-field - #if defined(__SUNPRO_C) || defined(__SUNPRO_CC) - #define _sign_int signed int - #else - #define _sign_int int32_t - #endif - // The opcode is not logically part of the Reservation, but we include it // in this struct to ensure that opcode plus the Reservation fits in a // single word. Yuk. @@ -163,7 +154,7 @@ namespace nanojit uint32_t arIndex:16; // index into stack frame. displ is -4*arIndex Register reg:7; // register UnknownReg implies not in register uint32_t used:1; // when set, the reservation is active - LOpcode code:8; + LOpcode opcode:8; inline void init() { reg = UnknownReg; @@ -171,107 +162,425 @@ namespace nanojit used = 1; } - inline void clear() - { + inline void clear() { used = 0; } }; - // Low-level Instruction. 4 words per instruction -- it's important this - // doesn't change unintentionally, so it is checked in LIR.cpp by an - // assertion in initOpcodeAndClearResv(). - // The first word is the same for all LIns kinds; the last three differ. + //----------------------------------------------------------------------- + // Low-level instructions. This is a bit complicated, because we have a + // variable-width representation to minimise space usage. + // + // - Instruction size is always an integral multiple of word size. + // + // - Every instruction has at least one word, holding the opcode and the + // reservation info. That word is in class LIns. + // + // - Beyond that, most instructions have 1, 2 or 3 extra words. These + // extra words are in classes LInsOp1, LInsOp2, etc (collectively called + // "LInsXYZ" in what follows). Each LInsXYZ class also contains a word, + // accessible by the 'ins' member, which holds the LIns data; its type + // is void* (which is the same size as LIns) rather than LIns to avoid a + // recursive dependency between LIns and LInsXYZ. + // + // - LIR is written forward, but read backwards. When reading backwards, + // in order to find the opcode, it must be in a predictable place in the + // LInsXYZ isn't affected by instruction width. Therefore, the LIns + // word (which contains the opcode) is always the *last* word in an + // instruction. + // + // - Each instruction is created by casting pre-allocated bytes from a + // LirBuffer to the LInsXYZ type. Therefore there are no constructors + // for LIns or LInsXYZ. + // + // - The standard handle for an instruction is a LIns*. This actually + // points to the LIns word, ie. to the final word in the instruction. + // This is a bit odd, but it allows the instruction's opcode to be + // easily accessed. Once you've looked at the opcode and know what kind + // of instruction it is, if you want to access any of the other words, + // you need to use toLInsXYZ(), which takes the LIns* and gives you an + // LInsXYZ*, ie. the pointer to the actual start of the instruction's + // bytes. From there you can access the instruction-specific extra + // words. + // + // - However, from outside class LIns, LInsXYZ isn't visible, nor is + // toLInsXYZ() -- from outside LIns, all LIR instructions are handled + // via LIns pointers and get/set methods are used for all LIns/LInsXYZ + // accesses. In fact, all data members in LInsXYZ are private and can + // only be accessed by LIns, which is a friend class. The only thing + // anyone outside LIns can do with a LInsXYZ is call getLIns(). + // + // - An example Op2 instruction and the likely pointers to it (each line + // represents a word, and pointers to a line point to the start of the + // word on that line): + // + // [ oprnd_2 <-- LInsOp2* insOp2 == toLInsOp2(ins) + // oprnd_1 + // opcode + resv ] <-- LIns* ins + // + // - LIR_skip instructions are more complicated. They allow an arbitrary + // blob of data (the "payload") to be placed in the LIR stream. The + // size of the payload is always a multiple of the word size. A skip + // instruction's operand points to the previous instruction, which lets + // the payload be skipped over when reading backwards. Here's an + // example of a skip instruction with a 3-word payload preceded by an + // LInsOp1: + // + // [ oprnd_1 + // +-> opcode + resv ] + // | [ data + // | data + // | data + // +---- prevLIns <-- LInsSk* insSk == toLInsSk(ins) + // opcode==LIR_skip + resv ] <-- LIns* ins + // + // Skips are also used to link code pages. If the first instruction on + // a page isn't a LIR_start, it will be a skip, and the skip's operand + // will point to the last LIns on the previous page. In this case there + // isn't a payload as such; in fact, the previous page might be at a + // higher address, ie. the operand might point forward rather than + // backward. + // + // LInsSk has the same layout as LInsOp1, but we represent it as a + // different class because there are some places where we treat + // skips specially and so having it separate seems like a good idea. + // + // - Call instructions (LIR_call, LIR_fcall, LIR_calli, LIR_fcalli) are + // also more complicated. They are preceded by the arguments to the + // call, which are laid out in reverse order. For example, a call with + // 3 args will look like this: + // + // [ arg #2 + // arg #1 + // arg #0 + // argc <-- LInsC insC == toLInsC(ins) + // ci + // opcode + resv ] <-- LIns* ins + // + // - Various things about the size and layout of LIns and LInsXYZ are + // statically checked in staticSanityCheck(). In particular, this is + // worthwhile because there's nothing that guarantees that all the + // LInsXYZ classes have a size that is a multiple of word size (but in + // practice all sane compilers use a layout that results in this). We + // also check that every LInsXYZ is word-aligned in + // LirBuffer::makeRoom(); this seems sensible to avoid potential + // slowdowns due to misalignment. It relies on pages themselves being + // word-aligned, which is extremely likely. + // + // - There is an enum, LInsRepKind, with one member for each of the + // LInsXYZ kinds. Each opcode is categorised with its LInsRepKind value + // in LIRopcode.tbl, and this is used in various places. + //----------------------------------------------------------------------- + + enum LInsRepKind { + // LRK_XYZ corresponds to class LInsXYZ. + LRK_Op0, + LRK_Op1, + LRK_Op2, + LRK_Sti, + LRK_Sk, + LRK_C, + LRK_P, + LRK_I, + LRK_I64, + LRK_None // this one is used for unused opcode numbers + }; + + // 0-operand form. Used for LIR_start and LIR_label. + class LInsOp0 + { + private: + friend class LIns; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // 1-operand form. Used for LIR_ret, LIR_ov, unary arithmetic/logic ops, + // etc. + class LInsOp1 + { + private: + friend class LIns; + + // Nb: oprnd_1 position relative to 'ins' must match that in + // LIns{Op2,Sti}. Checked in LirBufWriter::LirBufWriter(). + LIns* oprnd_1; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // 2-operand form. Used for loads, guards, branches, comparisons, binary + // arithmetic/logic ops, etc. + class LInsOp2 + { + private: + friend class LIns; + + // Nb: oprnd_{1,2} position relative to 'ins' must match that in + // LIns{Op1,Sti}. Checked in LirBufWriter::LirBufWriter(). + LIns* oprnd_2; + + LIns* oprnd_1; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used for LIR_sti and LIR_stqi. + class LInsSti + { + private: + friend class LIns; + + int32_t disp; + + // Nb: oprnd_{1,2} position relative to 'ins' must match that in + // LIns{Op1,Op2}. Checked in LIns::staticSanityCheck(). + LIns* oprnd_2; + + LIns* oprnd_1; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used for LIR_skip. + class LInsSk + { + private: + friend class LIns; + + LIns* prevLIns; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used for all variants of LIR_call. + class LInsC + { + private: + friend class LIns; + + uintptr_t argc:8; + + const CallInfo* ci; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used for LIR_param. + class LInsP + { + private: + friend class LIns; + + uintptr_t arg:8; + uintptr_t kind:8; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used for LIR_int and LIR_alloc. + class LInsI + { + private: + friend class LIns; + + int32_t imm32; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used for LIR_quad. + class LInsI64 + { + private: + friend class LIns; + + int32_t imm64_0; + + int32_t imm64_1; + + void* ins; + + public: + LIns* getLIns() { return (LIns*)&ins; }; + }; + + // Used only as a placeholder for OPDEF macros for unused opcodes in + // LIRopcode.tbl. + class LInsNone + { + }; + class LIns { - // 2-operand form. Used for most LIns kinds, including LIR_skip (for - // which oprnd_1 is the target). - struct u_type - { - // Nb: oprnd_1 and oprnd_2 layout must match that in sti_type - // because oprnd1() and oprnd2() are used for both. - LIns* oprnd_1; - - LIns* oprnd_2; - }; - - // Used for LIR_sti and LIR_stqi. - struct sti_type - { - // Nb: oprnd_1 and oprnd_2 layout must match that in u_type - // because oprnd1() and oprnd2() are used for both. - LIns* oprnd_1; - - LIns* oprnd_2; - - int32_t disp; - }; - - // Used for LIR_call and LIR_param. - struct c_type - { - uintptr_t imm8a:8; // call: 0 (not used); param: arg - uintptr_t imm8b:8; // call: argc; param: kind - - const CallInfo* ci; // call: callInfo; param: NULL (not used) - }; - - // Used for LIR_int. - struct i_type - { - int32_t imm32; - }; - - // Used for LIR_quad. - struct i64_type - { - int32_t imm64_0; - int32_t imm64_1; - }; - - #undef _sign_int - - // 1st word: fields shared by all LIns kinds. The reservation fields + private: + // Last word: fields shared by all LIns kinds. The reservation fields // are read/written during assembly. - Reservation firstWord; + Reservation lastWord; - // 2nd, 3rd and 4th words: differ depending on the LIns kind. - union - { - u_type u; - c_type c; - i_type i; - i64_type i64; - sti_type sti; - }; + // LIns-to-LInsXYZ converters. + LInsOp0* toLInsOp0() const { return (LInsOp0*)( uintptr_t(this+1) - sizeof(LInsOp0) ); } + LInsOp1* toLInsOp1() const { return (LInsOp1*)( uintptr_t(this+1) - sizeof(LInsOp1) ); } + LInsOp2* toLInsOp2() const { return (LInsOp2*)( uintptr_t(this+1) - sizeof(LInsOp2) ); } + LInsSti* toLInsSti() const { return (LInsSti*)( uintptr_t(this+1) - sizeof(LInsSti) ); } + LInsSk* toLInsSk() const { return (LInsSk* )( uintptr_t(this+1) - sizeof(LInsSk ) ); } + LInsC* toLInsC() const { return (LInsC* )( uintptr_t(this+1) - sizeof(LInsC ) ); } + LInsP* toLInsP() const { return (LInsP* )( uintptr_t(this+1) - sizeof(LInsP ) ); } + LInsI* toLInsI() const { return (LInsI* )( uintptr_t(this+1) - sizeof(LInsI ) ); } + LInsI64* toLInsI64() const { return (LInsI64*)( uintptr_t(this+1) - sizeof(LInsI64) ); } + + // This is never called, but that's ok because it contains only static + // assertions. + void staticSanityCheck() + { + // LIns must be word-sized. + NanoStaticAssert(sizeof(LIns) == 1*sizeof(void*)); + + // LInsXYZ have expected sizes too. + NanoStaticAssert(sizeof(LInsOp0) == 1*sizeof(void*)); + NanoStaticAssert(sizeof(LInsOp1) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsOp2) == 3*sizeof(void*)); + NanoStaticAssert(sizeof(LInsSti) == 4*sizeof(void*)); + NanoStaticAssert(sizeof(LInsSk) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsC) == 3*sizeof(void*)); + NanoStaticAssert(sizeof(LInsP) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsI) == 2*sizeof(void*)); + #if defined NANOJIT_64BIT + NanoStaticAssert(sizeof(LInsI64) == 2*sizeof(void*)); + #else + NanoStaticAssert(sizeof(LInsI64) == 3*sizeof(void*)); + #endif + + // oprnd_1 must be in the same position in LIns{Op1,Op2,Sti} + // because oprnd1() is used for all of them. + NanoStaticAssert( (offsetof(LInsOp1, ins) - offsetof(LInsOp1, oprnd_1)) == + (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_1)) ); + NanoStaticAssert( (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_1)) == + (offsetof(LInsSti, ins) - offsetof(LInsSti, oprnd_1)) ); + + // oprnd_2 must be in the same position in LIns{Op2,Sti} + // because oprnd2() is used for both of them. + NanoStaticAssert( (offsetof(LInsOp2, ins) - offsetof(LInsOp2, oprnd_2)) == + (offsetof(LInsSti, ins) - offsetof(LInsSti, oprnd_2)) ); + } + + public: + void initLInsOp0(LOpcode opcode) { + lastWord.clear(); + lastWord.opcode = opcode; + NanoAssert(isLInsOp0()); + } + void initLInsOp1(LOpcode opcode, LIns* oprnd1) { + lastWord.clear(); + lastWord.opcode = opcode; + toLInsOp1()->oprnd_1 = oprnd1; + NanoAssert(isLInsOp1()); + } + void initLInsOp2(LOpcode opcode, LIns* oprnd1, LIns* oprnd2) { + lastWord.clear(); + lastWord.opcode = opcode; + toLInsOp2()->oprnd_1 = oprnd1; + toLInsOp2()->oprnd_2 = oprnd2; + NanoAssert(isLInsOp2()); + } + void initLInsSti(LOpcode opcode, LIns* val, LIns* base, int32_t d) { + lastWord.clear(); + lastWord.opcode = opcode; + toLInsSti()->oprnd_1 = val; + toLInsSti()->oprnd_2 = base; + toLInsSti()->disp = d; + NanoAssert(isLInsSti()); + } + void initLInsSk(LIns* prevLIns) { + lastWord.clear(); + lastWord.opcode = LIR_skip; + toLInsSk()->prevLIns = prevLIns; + NanoAssert(isLInsSk()); + } + // Nb: this does NOT initialise the arguments. That must be done + // separately. + void initLInsC(LOpcode opcode, int32_t argc, const CallInfo* ci) { + NanoAssert(isU8(argc)); + lastWord.clear(); + lastWord.opcode = opcode; + toLInsC()->argc = argc; + toLInsC()->ci = ci; + NanoAssert(isLInsC()); + } + void initLInsP(int32_t arg, int32_t kind) { + lastWord.clear(); + lastWord.opcode = LIR_param; + NanoAssert(isU8(arg) && isU8(kind)); + toLInsP()->arg = arg; + toLInsP()->kind = kind; + NanoAssert(isLInsP()); + } + void initLInsI(LOpcode opcode, int32_t imm32) { + lastWord.clear(); + lastWord.opcode = opcode; + toLInsI()->imm32 = imm32; + NanoAssert(isLInsI()); + } + void initLInsI64(LOpcode opcode, int64_t imm64) { + lastWord.clear(); + lastWord.opcode = opcode; + toLInsI64()->imm64_0 = int32_t(imm64); + toLInsI64()->imm64_1 = int32_t(imm64 >> 32); + NanoAssert(isLInsI64()); + } - public: LIns* oprnd1() const { - NanoAssert(isOp1() || isOp2() || isLoad() || isStore()); - return u.oprnd_1; + NanoAssert(isLInsOp1() || isLInsOp2() || isStore()); + return toLInsOp2()->oprnd_1; } LIns* oprnd2() const { - NanoAssert(isOp2() || isLoad() || isStore()); - return u.oprnd_2; + NanoAssert(isLInsOp2() || isStore()); + return toLInsOp2()->oprnd_2; } - inline LOpcode opcode() const { return firstWord.code; } - inline uint8_t imm8() const { NanoAssert(isop(LIR_param)); return c.imm8a; } - inline uint8_t imm8b() const { NanoAssert(isop(LIR_param)); return c.imm8b; } - inline int32_t imm32() const { NanoAssert(isconst()); return i.imm32; } - inline int32_t imm64_0() const { NanoAssert(isconstq()); return i64.imm64_0; } - inline int32_t imm64_1() const { NanoAssert(isconstq()); return i64.imm64_1; } - uint64_t imm64() const; - double imm64f() const; - Reservation* resv() { return &firstWord; } - void* payload() const; - inline Page* page() { return (Page*) alignTo(this,NJ_PAGE_SIZE); } - inline int32_t size() const { - NanoAssert(isop(LIR_alloc)); - return i.imm32<<2; + LIns* prevLIns() const { + NanoAssert(isop(LIR_skip)); + return toLInsSk()->prevLIns; } - inline void setSize(int32_t bytes) { - NanoAssert(isop(LIR_alloc) && (bytes&3)==0 && isU16(bytes>>2)); - i.imm32 = bytes>>2; + + inline LOpcode opcode() const { return lastWord.opcode; } + inline uint8_t paramArg() const { NanoAssert(isop(LIR_param)); return toLInsP()->arg; } + inline uint8_t paramKind() const { NanoAssert(isop(LIR_param)); return toLInsP()->kind; } + inline int32_t imm32() const { NanoAssert(isconst()); return toLInsI()->imm32; } + inline int32_t imm64_0() const { NanoAssert(isconstq()); return toLInsI64()->imm64_0; } + inline int32_t imm64_1() const { NanoAssert(isconstq()); return toLInsI64()->imm64_1; } + uint64_t imm64() const; + double imm64f() const; + Reservation* resv() { return &lastWord; } + void* payload() const; + inline Page* page() { return (Page*) alignTo(this,NJ_PAGE_SIZE); } + inline int32_t size() const { + NanoAssert(isop(LIR_alloc)); + return toLInsI()->imm32 << 2; } LIns* arg(uint32_t i); @@ -279,7 +588,7 @@ namespace nanojit inline int32_t immdisp() const { NanoAssert(isStore()); - return sti.disp; + return toLInsSti()->disp; } inline void* constvalp() const @@ -292,36 +601,49 @@ namespace nanojit } bool isCse() const; - bool isRet() const { return nanojit::isRetOpcode(firstWord.code); } - bool isop(LOpcode o) const { return firstWord.code == o; } + bool isRet() const { return nanojit::isRetOpcode(opcode()); } + bool isop(LOpcode o) const { return opcode() == o; } #if defined(_DEBUG) - bool isOp1() const; // true for unary ops - bool isOp2() const; // true for binary ops + // isLInsXYZ() returns true if the instruction has the LInsXYZ form. + // Note that there is some overlap with other predicates, eg. + // isStore()==isLInsSti(), isCall()==isLInsC(), but that's ok; these + // ones are used only to check that opcodes are appropriate for + // instruction layouts, the others are used for non-debugging + // purposes. + bool isLInsOp0() const; + bool isLInsOp1() const; + bool isLInsOp2() const; + bool isLInsSti() const; + bool isLInsSk() const; + bool isLInsC() const; + bool isLInsP() const; + bool isLInsI() const; + bool isLInsI64() const; #endif bool isQuad() const; bool isCond() const; bool isFloat() const; bool isCmp() const; bool isCall() const { - LOpcode op = LOpcode(firstWord.code & ~LIR64); + LOpcode op = LOpcode(opcode() & ~LIR64); return op == LIR_call; } bool isStore() const { - LOpcode op = LOpcode(firstWord.code & ~LIR64); + LOpcode op = LOpcode(opcode() & ~LIR64); return op == LIR_sti; } bool isLoad() const { - LOpcode op = firstWord.code; + LOpcode op = opcode(); return op == LIR_ldq || op == LIR_ld || op == LIR_ldc || op == LIR_ldqc || op == LIR_ldcs || op == LIR_ldcb; } bool isGuard() const { - LOpcode op = firstWord.code; + LOpcode op = opcode(); return op == LIR_x || op == LIR_xf || op == LIR_xt || op == LIR_loop || op == LIR_xbarrier || op == LIR_xtbl; } // True if the instruction is a 32-bit or smaller constant integer. - bool isconst() const { return firstWord.code == LIR_int; } + bool isconst() const { return opcode() == LIR_int; } // True if the instruction is a 32-bit or smaller constant integer and // has the value val when treated as a 32-bit signed integer. bool isconstval(int32_t val) const; @@ -333,69 +655,6 @@ namespace nanojit return isop(LIR_jt) || isop(LIR_jf) || isop(LIR_j); } - void setIns0(LOpcode op) { - firstWord.code = op; - } - void setIns1(LOpcode op, LIns* oprnd1) { - firstWord.code = op; - u.oprnd_1 = oprnd1; - NanoAssert(isOp1()); - } - void setIns2(LOpcode op, LIns* oprnd1, LIns* oprnd2) { - firstWord.code = op; - u.oprnd_1 = oprnd1; - u.oprnd_2 = oprnd2; - NanoAssert(isOp2() || isLoad() || isGuard() || isBranch()); - } - void setLoad(LOpcode op, LIns* base, LIns* d) { - setIns2(op, base, d); - } - void setGuard(LOpcode op, LIns* cond, LIns* data) { - setIns2(op, cond, data); - } - void setBranch(LOpcode op, LIns* cond, LIns* target) { - setIns2(op, cond, target); - } - void setStorei(LOpcode op, LIns* val, LIns* base, int32_t d) { - firstWord.code = op; - u.oprnd_1 = val; - u.oprnd_2 = base; - sti.disp = d; - NanoAssert(isStore()); - } - void setImm(LOpcode op, int32_t imm32) { - firstWord.code = op; - i.imm32 = imm32; - NanoAssert(op == LIR_alloc || op == LIR_int); - } - void setAlloc(LOpcode op, int32_t size) { - setImm(op, size); - } - void setParam(LOpcode op, int32_t arg, int32_t kind) - { - firstWord.code = op; - NanoAssert(isU8(arg) && isU8(kind)); - c.imm8a = arg; - c.imm8b = kind; - c.ci = NULL; - NanoAssert(op == LIR_param); - } - void setCall(LOpcode op, int32_t argc, const CallInfo* ci) - { - firstWord.code = op; - NanoAssert(isU8(argc)); - c.imm8a = 0; - c.imm8b = argc; - c.ci = ci; - NanoAssert(op == LIR_call || op == LIR_fcall); - } - void setImmq(LOpcode op, int64_t imm64) { - firstWord.code = op; - i64.imm64_0 = int32_t(imm64); - i64.imm64_1 = int32_t(imm64>>32); - NanoAssert(op == LIR_quad); - } - void setTarget(LIns* t); LIns* getTarget(); @@ -403,17 +662,17 @@ namespace nanojit inline uint32_t argc() const { NanoAssert(isCall()); - return c.imm8b; + return toLInsC()->argc; } const CallInfo *callInfo() const; }; - typedef LIns* LInsp; + + typedef LIns* LInsp; LIns* FASTCALL callArgN(LInsp i, uint32_t n); extern const uint8_t operandCount[]; class Fragmento; // @todo remove this ; needed for minbuild for some reason?!? Should not be compiling this code at all - class LirFilter; // make it a GCObject so we can explicitly delete it early class LirWriter : public avmplus::GCObject @@ -490,12 +749,12 @@ namespace nanojit // The first instruction on a page is always a start instruction, or a // payload-less skip instruction linking to the previous page. The // biggest possible instruction would take up the entire rest of the page. - #define NJ_MAX_LINS_SZB (NJ_PAGE_CODE_AREA_SZB - sizeof(LIns)) + #define NJ_MAX_LINS_SZB (NJ_PAGE_CODE_AREA_SZB - sizeof(LInsSk)) // The maximum skip payload size is determined by the maximum instruction // size. We require that a skip's payload be adjacent to the skip LIns // itself. - #define NJ_MAX_SKIP_PAYLOAD_SZB (NJ_MAX_LINS_SZB - sizeof(LIns)) + #define NJ_MAX_SKIP_PAYLOAD_SZB (NJ_MAX_LINS_SZB - sizeof(LInsSk)) #ifdef NJ_VERBOSE diff --git a/js/src/nanojit/LIRopcode.tbl b/js/src/nanojit/LIRopcode.tbl index a98156f07e4b..4c4e6134cedb 100644 --- a/js/src/nanojit/LIRopcode.tbl +++ b/js/src/nanojit/LIRopcode.tbl @@ -44,15 +44,22 @@ * * Includers must define OPDEF and OPDEF64 macros of the following forms: * - * #define OPDEF(op,val,operands) ... - * #define OPDEF64(op,val,operands) ... + * #define OPDEF(op,val,operands,repkind) ... + * #define OPDEF64(op,val,operands,repkind) ... * * Selected arguments can then be used within the macro expansions. * * Field Description - * op Bytecode name, token-pasted after "LIR_" to form an LOpcode - * val Bytecode value, which is the LOpcode enumerator value - * operands Number of operands for this instruction + * op Bytecode name, token-pasted after "LIR_" to form an LOpcode. + * val Bytecode value, which is the LOpcode enumerator value. + * operands Number of operands for this instruction, where an "operand" is + * a LIns* argument. Eg. LIR_sti has 3 fields, but the last is an + * immediate, so it only has two operands. Call instructions are + * considered to have 0 operands -- the call args aren't counted. + * The value is set to -1 for unused opcodes to make it obvious + * that it needs changing if the opcode becomes used. + * repkind Indicates how the instruction is represented in memory; XYZ + * corresponds to LInsXYZ and LRK_XYZ. * * This file is best viewed with 128 columns: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 @@ -61,37 +68,36 @@ /* op val name operands */ /* special operations (must be 0..N) */ -OPDEF(start, 0, 0) -OPDEF(unused1, 1, 0) -OPDEF(skip, 2, 0) -OPDEF(unused3, 3, 0) -OPDEF(unused4, 4, 0) - -OPDEF(unused5, 5, 2) -OPDEF(unused6, 6, 2) +OPDEF(start, 0, 0, Op0) // start of a fragment +OPDEF(unused1, 1,-1, None) +OPDEF(skip, 2, 1, Sk) // holds blobs ("payloads") of data; also links pages +OPDEF(unused3, 3,-1, None) +OPDEF(unused4, 4,-1, None) +OPDEF(unused5, 5,-1, None) +OPDEF(unused6, 6,-1, None) /* non-pure operations */ -OPDEF(addp, 7, 2) -OPDEF(param, 8, 0) -OPDEF(unused9, 9, 2) -OPDEF(ld, 10, 2) // 32-bit load -OPDEF(alloc, 11, 0) // alloca some stack space -OPDEF(sti, 12, 2) // 32-bit store -OPDEF(ret, 13, 1) -OPDEF(live, 14, 1) // extend live range of reference -OPDEF(unused15, 15, 0) // indirect call -OPDEF(call, 16, 0) // subroutine call returning a 32-bit value +OPDEF(addp, 7, 2, Op2) // integer addition for temporary pointer calculations +OPDEF(param, 8, 0, P) // load a parameter +OPDEF(unused9, 9,-1, None) +OPDEF(ld, 10, 2, Op2) // 32-bit load +OPDEF(alloc, 11, 0, I) // alloca some stack space +OPDEF(sti, 12, 2, Sti) // 32-bit store +OPDEF(ret, 13, 1, Op1) // return a word-sized value +OPDEF(live, 14, 1, Op1) // extend live range of reference +OPDEF(unused15, 15, 0, C) +OPDEF(call, 16, 0, C) // subroutine call returning a 32-bit value /* guards */ -OPDEF(loop, 17, 0) // loop fragment -OPDEF(x, 18, 0) // exit always +OPDEF(loop, 17, 0, Op2) // loop fragment +OPDEF(x, 18, 0, Op2) // exit always /* branches */ -OPDEF(j, 19, 0) // jump always -OPDEF(jt, 20, 1) // jump true -OPDEF(jf, 21, 1) // jump false -OPDEF(label, 22, 0) // a jump target -OPDEF(ji, 23, 2) // jump indirect +OPDEF(j, 19, 0, Op2) // jump always +OPDEF(jt, 20, 1, Op2) // jump if true +OPDEF(jf, 21, 1, Op2) // jump if false +OPDEF(label, 22, 0, Op0) // a jump target (no machine code is emitted for this) +OPDEF(ji, 23,-1, None) // indirect jump (currently not implemented) /* operators */ @@ -100,12 +106,12 @@ OPDEF(ji, 23, 2) // jump indirect * common-subexpression-elimination detection code. */ -OPDEF(int, 24, 0) // constant 32-bit integer -OPDEF(cmov, 25, 2) // conditional move (op1=cond, op2=cond(iftrue,iffalse)) +OPDEF(int, 24, 0, I) // constant 32-bit integer +OPDEF(cmov, 25, 2, Op2) // conditional move (op1=cond, op2=LIR_2(iftrue,iffalse)) #if defined(NANOJIT_64BIT) -OPDEF(callh, 26, 0) +OPDEF(callh, 26,-1, None) // unused on 64-bit machines #else -OPDEF(callh, 26, 1) +OPDEF(callh, 26, 1, Op1) // get the high 32 bits of a call returning a 64-bit value #endif /* @@ -117,46 +123,43 @@ OPDEF(callh, 26, 1) * with 3. NB: These opcodes must remain continuous so that comparison-opcode * detection works correctly. */ -OPDEF(feq, 27, 2) // floating-point equality [2 float inputs] -OPDEF(flt, 28, 2) // floating-point less than: arg1 < arg2 -OPDEF(fgt, 29, 2) // floating-point greater than: arg1 > arg2 -OPDEF(fle, 30, 2) // arg1 <= arg2, both floating-point -OPDEF(fge, 31, 2) // arg1 >= arg2, both floating-point +OPDEF(feq, 27, 2, Op2) // floating-point equality +OPDEF(flt, 28, 2, Op2) // floating-point less-than +OPDEF(fgt, 29, 2, Op2) // floating-point greater-than +OPDEF(fle, 30, 2, Op2) // floating-point less-than-or-equal +OPDEF(fge, 31, 2, Op2) // floating-point greater-than-or-equal -OPDEF(ldcb, 32, 2) // non-volatile 8-bit load -OPDEF(ldcs, 33, 2) // non-volatile 16-bit load -OPDEF(ldc, 34, 2) // non-volatile 32-bit load +OPDEF(ldcb, 32, 2, Op2) // non-volatile 8-bit load +OPDEF(ldcs, 33, 2, Op2) // non-volatile 16-bit load +OPDEF(ldc, 34, 2, Op2) // non-volatile 32-bit load -// neg through ush are all integer operations -OPDEF(neg, 35, 1) // numeric negation [ 1 integer input / integer output ] -OPDEF(add, 36, 2) // integer addition [ 2 operand integer intputs / integer output ] -OPDEF(sub, 37, 2) // integer subtraction -OPDEF(mul, 38, 2) // integer multiplication -OPDEF(div, 39, 2) -OPDEF(mod, 40, 1) +OPDEF(neg, 35, 1, Op1) // integer negation +OPDEF(add, 36, 2, Op2) // integer addition +OPDEF(sub, 37, 2, Op2) // integer subtraction +OPDEF(mul, 38, 2, Op2) // integer multiplication +OPDEF(div, 39, 2, Op2) // integer division +OPDEF(mod, 40, 1, Op1) // hack: get the modulus from a LIR_div result, for x86 only -OPDEF(and, 41, 2) -OPDEF(or, 42, 2) -OPDEF(xor, 43, 2) -OPDEF(not, 44, 1) -OPDEF(lsh, 45, 2) -OPDEF(rsh, 46, 2) // >> -OPDEF(ush, 47, 2) // >>> +OPDEF(and, 41, 2, Op2) // 32-bit bitwise AND +OPDEF(or, 42, 2, Op2) // 32-bit bitwise OR +OPDEF(xor, 43, 2, Op2) // 32-bit bitwise XOR +OPDEF(not, 44, 1, Op1) // 32-bit bitwise NOT +OPDEF(lsh, 45, 2, Op2) // 32-bit left shift +OPDEF(rsh, 46, 2, Op2) // 32-bit right shift with sign-extend (>>) +OPDEF(ush, 47, 2, Op2) // 32-bit unsigned right shift (>>>) // conditional guards, op^1 to complement. Only things that are // isCond() can be passed to these. -OPDEF(xt, 48, 1) // exit if true 0x30 0011 0000 -OPDEF(xf, 49, 1) // exit if false 0x31 0011 0001 +OPDEF(xt, 48, 1, Op2) // exit if true (0x30 0011 0000) +OPDEF(xf, 49, 1, Op2) // exit if false (0x31 0011 0001) -// qlo and qhi take a single quad argument and return its low and high -// 32 bits respectively as 32-bit integers. -OPDEF(qlo, 50, 1) -OPDEF(qhi, 51, 1) +OPDEF(qlo, 50, 1, Op1) // get the low 32 bits of a 64-bit value +OPDEF(qhi, 51, 1, Op1) // get the high 32 bits of a 64-bit value -OPDEF(unused52, 52, 0) +OPDEF(unused52, 52,-1, None) -OPDEF(ov, 53, 1) -OPDEF(cs, 54, 1) +OPDEF(ov, 53, 1, Op1) // test for overflow; value must have just been computed +OPDEF(cs, 54, 1, Op1) // test for carry; value must have just been computed // Integer (all sizes) relational operators. (op ^ 1) is the op which flips the // left and right sides of the comparison, so (lt ^ 1) == gt, or the operator @@ -165,96 +168,96 @@ OPDEF(cs, 54, 1) // with 3. 'u' prefix indicates the unsigned integer variant. // NB: These opcodes must remain continuous so that comparison-opcode detection // works correctly. -OPDEF(eq, 55, 2) // integer equality -OPDEF(lt, 56, 2) // 0x38 0011 1000 -OPDEF(gt, 57, 2) // 0x39 0011 1001 -OPDEF(le, 58, 2) // 0x3A 0011 1010 -OPDEF(ge, 59, 2) // 0x3B 0011 1011 -OPDEF(ult, 60, 2) // 0x3C 0011 1100 -OPDEF(ugt, 61, 2) // 0x3D 0011 1101 -OPDEF(ule, 62, 2) // 0x3E 0011 1110 -OPDEF(uge, 63, 2) // 0x3F 0011 1111 +OPDEF(eq, 55, 2, Op2) // integer equality +OPDEF(lt, 56, 2, Op2) // signed integer less-than (0x38 0011 1000) +OPDEF(gt, 57, 2, Op2) // signed integer greater-than (0x39 0011 1001) +OPDEF(le, 58, 2, Op2) // signed integer less-than-or-equal (0x3A 0011 1010) +OPDEF(ge, 59, 2, Op2) // signed integer greater-than-or-equal (0x3B 0011 1011) +OPDEF(ult, 60, 2, Op2) // unsigned integer less-than (0x3C 0011 1100) +OPDEF(ugt, 61, 2, Op2) // unsigned integer greater-than (0x3D 0011 1101) +OPDEF(ule, 62, 2, Op2) // unsigned integer less-than-or-equal (0x3E 0011 1110) +OPDEF(uge, 63, 2, Op2) // unsigned integer greater-than-or-equal (0x3F 0011 1111) -OPDEF64(2, 0, 2) // wraps a pair of refs -OPDEF64(file, 1, 2) -OPDEF64(line, 2, 2) -OPDEF64(xbarrier, 3, 1) // memory barrier (dummy guard) -OPDEF64(xtbl, 4, 1) // exit via indirect jump +OPDEF64(2, 0, 2, Op2) // wraps a pair of refs, for LIR_cmov or LIR_qcmov +OPDEF64(file, 1, 2, Op1) // source filename for debug symbols +OPDEF64(line, 2, 2, Op1) // source line number for debug symbols +OPDEF64(xbarrier, 3, 1, Op2) // memory barrier; doesn't exit, but flushes all values to the stack +OPDEF64(xtbl, 4, 1, Op2) // exit via indirect jump -OPDEF64(unused5_64, 5, 2) -OPDEF64(unused6_64, 6, 2) -OPDEF64(unused7_64, 7, 2) -OPDEF64(unused8_64, 8, 2) +OPDEF64(unused5_64, 5,-1, None) +OPDEF64(unused6_64, 6,-1, None) +OPDEF64(unused7_64, 7,-1, None) +OPDEF64(unused8_64, 8,-1, None) +OPDEF64(unused9_64, 9,-1, None) -OPDEF64(unused9_64, 9, 2) -OPDEF64(ldq, LIR_ld, 2) // quad load +OPDEF64(ldq, LIR_ld, 2, Op2) // 64-bit (quad) load -OPDEF64(unused11_64, 11, 2) +OPDEF64(unused11_64, 11,-1, None) -OPDEF64(stqi, LIR_sti, 2) // quad store -OPDEF64(fret, LIR_ret, 1) +OPDEF64(stqi, LIR_sti, 2, Sti) // 64-bit (quad) store +OPDEF64(fret, LIR_ret, 1, Op1) -OPDEF64(unused14_64, 14, 2) -OPDEF64(unused15_64, 15, 2) +OPDEF64(unused14_64, 14,-1, None) +OPDEF64(unused15_64, 15,-1, None) -OPDEF64(fcall, LIR_call, 0) // subroutine call returning quad +OPDEF64(fcall, LIR_call, 0, C) // subroutine call returning 64-bit (quad) value -OPDEF64(unused17_64, 17, 2) -OPDEF64(unused18_64, 18, 2) -OPDEF64(unused19_64, 19, 2) -OPDEF64(unused20_64, 20, 2) -OPDEF64(unused21_64, 21, 2) -OPDEF64(unused22_64, 22, 2) -OPDEF64(unused23_64, 23, 2) +OPDEF64(unused17_64, 17,-1, None) +OPDEF64(unused18_64, 18,-1, None) +OPDEF64(unused19_64, 19,-1, None) +OPDEF64(unused20_64, 20,-1, None) +OPDEF64(unused21_64, 21,-1, None) +OPDEF64(unused22_64, 22,-1, None) +OPDEF64(unused23_64, 23,-1, None) -// We strip of the 64bit flag and compare that the opcode is between LIR_int +// We strip off the 64 bit flag and compare that the opcode is between LIR_int // and LIR_uge to decide whether we can CSE the opcode. All opcodes below // this marker are subject to CSE. -OPDEF64(quad, LIR_int, 0) // quad constant value -OPDEF64(qcmov, LIR_cmov, 2) -OPDEF64(unused26_64, 26, 2) +OPDEF64(quad, LIR_int, 0, I64) // 64-bit (quad) constant value +OPDEF64(qcmov, LIR_cmov, 2, Op2) // 64-bit conditional move -OPDEF64(unused27_64, 27, 2) -OPDEF64(unused28_64, 28, 2) -OPDEF64(unused29_64, 29, 2) -OPDEF64(unused30_64, 30, 2) -OPDEF64(unused31_64, 31, 2) -OPDEF64(unused32_64, 32, 2) -OPDEF64(unused33_64, 33, 2) +OPDEF64(unused26_64, 26,-1, None) +OPDEF64(unused27_64, 27,-1, None) +OPDEF64(unused28_64, 28,-1, None) +OPDEF64(unused29_64, 29,-1, None) +OPDEF64(unused30_64, 30,-1, None) +OPDEF64(unused31_64, 31,-1, None) +OPDEF64(unused32_64, 32,-1, None) +OPDEF64(unused33_64, 33,-1, None) -OPDEF64(ldqc, LIR_ldc, 2) +OPDEF64(ldqc, LIR_ldc, 2, Op2) // non-volatile 64-bit load -/* floating-point arithmetic operations */ -OPDEF64(fneg, LIR_neg, 1) -OPDEF64(fadd, LIR_add, 2) -OPDEF64(fsub, LIR_sub, 2) -OPDEF64(fmul, LIR_mul, 2) -OPDEF64(fdiv, LIR_div, 2) -OPDEF64(fmod, LIR_mod, 2) +OPDEF64(fneg, LIR_neg, 1, Op1) // floating-point negation +OPDEF64(fadd, LIR_add, 2, Op2) // floating-point addition +OPDEF64(fsub, LIR_sub, 2, Op2) // floating-point subtraction +OPDEF64(fmul, LIR_mul, 2, Op2) // floating-point multiplication +OPDEF64(fdiv, LIR_div, 2, Op2) // floating-point division +OPDEF64(fmod, LIR_mod, 2, Op2) // floating-point modulus(?) -OPDEF64(qiand, 41, 2) -OPDEF64(qiadd, 42, 2) -OPDEF64(qior, 43, 2) -OPDEF64(qilsh, 44, 2) -OPDEF64(qjoin, 45, 2) // 1st arg is low 32 bits, 2nd arg is high 32 bits +OPDEF64(qiand, 41, 2, Op2) // 64-bit bitwise AND +OPDEF64(qiadd, 42, 2, Op2) // 64-bit bitwise ADD +OPDEF64(qior, 43, 2, Op2) // 64-bit bitwise OR -OPDEF64(i2f, 46, 1) // convert an integer to a float -OPDEF64(u2f, 47, 1) // convert an unsigned integer to a float +OPDEF64(qilsh, 44, 2, Op2) // 64-bit left shift +OPDEF64(qjoin, 45, 2, Op2) // join two 32-bit values (1st arg is low bits, 2nd is high) -OPDEF64(unused48_64, 48, 2) -OPDEF64(unused49_64, 49, 2) -OPDEF64(unused50_64, 50, 2) -OPDEF64(unused51_64, 51, 2) -OPDEF64(unused52_64, 52, 2) -OPDEF64(unused53_64, 53, 2) -OPDEF64(unused54_64, 54, 2) -OPDEF64(unused55_64, 55, 2) -OPDEF64(unused56_64, 56, 2) -OPDEF64(unused57_64, 57, 2) -OPDEF64(unused58_64, 58, 2) -OPDEF64(unused59_64, 59, 2) -OPDEF64(unused60_64, 60, 2) -OPDEF64(unused61_64, 61, 2) -OPDEF64(unused62_64, 62, 2) -OPDEF64(unused63_64, 63, 2) +OPDEF64(i2f, 46, 1, Op1) // convert a signed 32-bit integer to a float +OPDEF64(u2f, 47, 1, Op1) // convert an unsigned 32-bit integer to a float + +OPDEF64(unused48_64, 48,-1, None) +OPDEF64(unused49_64, 49,-1, None) +OPDEF64(unused50_64, 50,-1, None) +OPDEF64(unused51_64, 51,-1, None) +OPDEF64(unused52_64, 52,-1, None) +OPDEF64(unused53_64, 53,-1, None) +OPDEF64(unused54_64, 54,-1, None) +OPDEF64(unused55_64, 55,-1, None) +OPDEF64(unused56_64, 56,-1, None) +OPDEF64(unused57_64, 57,-1, None) +OPDEF64(unused58_64, 58,-1, None) +OPDEF64(unused59_64, 59,-1, None) +OPDEF64(unused60_64, 60,-1, None) +OPDEF64(unused61_64, 61,-1, None) +OPDEF64(unused62_64, 62,-1, None) +OPDEF64(unused63_64, 63,-1, None) diff --git a/js/src/nanojit/NativeARM.cpp b/js/src/nanojit/NativeARM.cpp index 6387a582c932..d8c0463ea0b6 100644 --- a/js/src/nanojit/NativeARM.cpp +++ b/js/src/nanojit/NativeARM.cpp @@ -615,7 +615,7 @@ Assembler::hint(LIns* i, RegisterMask allow /* = ~0 */) else if (op == LIR_callh) prefer = rmask(R1); else if (op == LIR_param) - prefer = rmask(imm2register(i->imm8())); + prefer = rmask(imm2register(i->paramArg())); if (_allocator.free & allow & prefer) allow &= prefer; @@ -1918,8 +1918,8 @@ Assembler::asm_qlo(LInsp ins) void Assembler::asm_param(LInsp ins) { - uint32_t a = ins->imm8(); - uint32_t kind = ins->imm8b(); + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); if (kind == 0) { // ordinary param AbiKind abi = _thisfrag->lirbuf->abi; diff --git a/js/src/nanojit/NativeSparc.cpp b/js/src/nanojit/NativeSparc.cpp index 7054b1c1dd6c..5a63b8efe250 100644 --- a/js/src/nanojit/NativeSparc.cpp +++ b/js/src/nanojit/NativeSparc.cpp @@ -591,7 +591,7 @@ namespace nanojit // restore first parameter, the only one we use LInsp state = _thisfrag->lirbuf->state; - findSpecificRegFor(state, argRegs[state->imm8()]); + findSpecificRegFor(state, argRegs[state->paramArg()]); } void Assembler::asm_fcond(LInsp ins) @@ -817,8 +817,8 @@ namespace nanojit void Assembler::asm_param(LInsp ins) { - uint32_t a = ins->imm8(); - uint32_t kind = ins->imm8b(); + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); // prepResultReg(ins, rmask(argRegs[a])); if (kind == 0) { prepResultReg(ins, rmask(argRegs[a])); diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index 71132947b919..70953ef6c90b 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -343,8 +343,8 @@ namespace nanojit } else if (op == LIR_param) { uint32_t max_regs = max_abi_regs[_thisfrag->lirbuf->abi]; - if (i->imm8() < max_regs) - prefer &= rmask(Register(i->imm8())); + if (i->paramArg() < max_regs) + prefer &= rmask(Register(i->paramArg())); } else if (op == LIR_callh || (op == LIR_rsh && i->oprnd1()->opcode()==LIR_callh)) { prefer &= rmask(retRegs[1]); @@ -1114,8 +1114,8 @@ namespace nanojit void Assembler::asm_param(LInsp ins) { - uint32_t a = ins->imm8(); - uint32_t kind = ins->imm8b(); + uint32_t a = ins->paramArg(); + uint32_t kind = ins->paramKind(); if (kind == 0) { // ordinary param AbiKind abi = _thisfrag->lirbuf->abi;