diff --git a/js/src/lirasm/lirasm.cpp b/js/src/lirasm/lirasm.cpp index 1a272059b75..735cc5bd475 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, repkind) \ +#define OPDEF(op, number, args) \ op_map[#op] = make_pair(LIR_##op, args); -#define OPDEF64(op, number, args, repkind) \ +#define OPDEF64(op, number, args) \ 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 a19ed1dc9c0..dde5bd198f6 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->paramArg()]); + findSpecificRegFor(p, savedRegs[p->imm8()]); } } @@ -1584,10 +1584,10 @@ namespace nanojit { LInsp state = _thisfrag->lirbuf->state; if (state) - findSpecificRegFor(state, argRegs[state->paramArg()]); + findSpecificRegFor(state, argRegs[state->imm8()]); LInsp param1 = _thisfrag->lirbuf->param1; if (param1) - findSpecificRegFor(param1, argRegs[param1->paramArg()]); + findSpecificRegFor(param1, argRegs[param1->imm8()]); } void Assembler::handleLoopCarriedExprs() diff --git a/js/src/nanojit/Fragmento.cpp b/js/src/nanojit/Fragmento.cpp index 97d91ddcce4..d440463a0cd 100644 --- a/js/src/nanojit/Fragmento.cpp +++ b/js/src/nanojit/Fragmento.cpp @@ -84,13 +84,12 @@ namespace nanojit /* Opcodes must be strictly increasing without holes. */ uint32_t count = 0; -#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 + #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 } #endif diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 22fb37de3ed..7d6cd65739e 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, repkind) \ +#define OPDEF(op, number, operands) \ operands, -#define OPDEF64(op, number, operands, repkind) \ +#define OPDEF64(op, number, operands) \ operands, #include "LIRopcode.tbl" #undef OPDEF @@ -61,35 +61,13 @@ 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, repkind) \ +#define OPDEF(op, number, operands) \ #op, -#define OPDEF64(op, number, operands, repkind) \ +#define OPDEF64(op, number, operands) \ #op, #include "LIRopcode.tbl" #undef OPDEF @@ -153,8 +131,7 @@ namespace nanojit int32_t LirBuffer::insCount() { - // A LIR_skip payload is considered part of the LIR_skip, and LIR_call - // arg slots are considered part of the LIR_call. + // Doesn't include LIR_skip payload or LIR_call arg slots. return _stats.lir; } @@ -188,10 +165,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. - LInsSk* insSk = (LInsSk*)_unused; - LIns* ins = insSk->getLIns(); - ins->initLInsSk((LInsp)addrOfLastLInsOnCurrentPage); - _unused += sizeof(LInsSk); + LInsp l = (LInsp)_unused; + l->setIns1(LIR_skip, (LInsp)addrOfLastLInsOnCurrentPage); + l->resv()->clear(); + _unused += sizeof(LIns); _stats.lir++; } @@ -231,42 +208,40 @@ 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; - LInsSti* insSti = (LInsSti*)_buf->makeRoom(sizeof(LInsSti)); - LIns* ins = insSti->getLIns(); - ins->initLInsSti(op, val, base, d); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setStorei(op, val, base, d); + l->resv()->clear(); + return l; } LInsp LirBufWriter::ins0(LOpcode op) { - LInsOp0* insOp0 = (LInsOp0*)_buf->makeRoom(sizeof(LInsOp0)); - LIns* ins = insOp0->getLIns(); - ins->initLInsOp0(op); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setIns0(op); + l->resv()->clear(); + return l; } LInsp LirBufWriter::ins1(LOpcode op, LInsp o1) { - LInsOp1* insOp1 = (LInsOp1*)_buf->makeRoom(sizeof(LInsOp1)); - LIns* ins = insOp1->getLIns(); - ins->initLInsOp1(op, o1); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setIns1(op, o1); + l->resv()->clear(); + return l; } LInsp LirBufWriter::ins2(LOpcode op, LInsp o1, LInsp o2) { - LInsOp2* insOp2 = (LInsOp2*)_buf->makeRoom(sizeof(LInsOp2)); - LIns* ins = insOp2->getLIns(); - ins->initLInsOp2(op, o1, o2); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setIns2(op, o1, o2); + l->resv()->clear(); + return l; } LInsp LirBufWriter::insLoad(LOpcode op, LInsp base, LInsp d) @@ -288,39 +263,39 @@ namespace nanojit LInsp LirBufWriter::insAlloc(int32_t size) { size = (size+3)>>2; // # of required 32bit words - LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); - LIns* ins = insI->getLIns(); - ins->initLInsI(LIR_alloc, size); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setAlloc(LIR_alloc, size); + l->resv()->clear(); + return l; } LInsp LirBufWriter::insParam(int32_t arg, int32_t kind) { - LInsP* insP = (LInsP*)_buf->makeRoom(sizeof(LInsP)); - LIns* ins = insP->getLIns(); - ins->initLInsP(arg, kind); + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setParam(LIR_param, arg, kind); + l->resv()->clear(); if (kind) { NanoAssert(arg < NumSavedRegs); - _buf->savedRegs[arg] = ins; + _buf->savedRegs[arg] = l; _buf->explicitSavedRegs = true; } - return ins; + return l; } LInsp LirBufWriter::insImm(int32_t imm) { - LInsI* insI = (LInsI*)_buf->makeRoom(sizeof(LInsI)); - LIns* ins = insI->getLIns(); - ins->initLInsI(LIR_int, imm); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setImm(LIR_int, imm); + l->resv()->clear(); + return l; } LInsp LirBufWriter::insImmq(uint64_t imm) { - LInsI64* insI64 = (LInsI64*)_buf->makeRoom(sizeof(LInsI64)); - LIns* ins = insI64->getLIns(); - ins->initLInsI64(LIR_quad, imm); - return ins; + LInsp l = (LInsp)_buf->makeRoom(sizeof(LIns)); + l->setImmq(LIR_quad, imm); + l->resv()->clear(); + return l; } LInsp LirBufWriter::insSkip(size_t payload_szB) @@ -333,14 +308,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(LInsSk)); + uintptr_t payload = _buf->makeRoom(payload_szB + sizeof(LIns)); // payload + skip uintptr_t prevLInsAddr = payload - sizeof(LIns); - LInsSk* insSk = (LInsSk*)(payload + payload_szB); - LIns* ins = insSk->getLIns(); + LInsp l = (LInsp)(payload + payload_szB); NanoAssert(prevLInsAddr >= pageDataStart(prevLInsAddr)); - NanoAssert(samepage(prevLInsAddr, insSk)); - ins->initLInsSk((LInsp)prevLInsAddr); - return ins; + NanoAssert(samepage(prevLInsAddr, l)); + l->setIns1(LIR_skip, (LInsp)prevLInsAddr); + l->resv()->clear(); + return l; } // Reads the next non-skip instruction. @@ -366,38 +341,33 @@ namespace nanojit do { - // 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; + switch (iop) + { + default: + i -= sizeof(LIns); + 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(); - i -= sizeof(LInsC); // step over the instruction - i -= argc*sizeof(LInsp); // step over the arguments - NanoAssert( samepage(i, _i) ); + uintptr_t prev = i - sizeof(LIns) - argc*sizeof(LInsp); + NanoAssert( samepage(i, prev) ); + i = prev; 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_skip: + NanoAssert(((LInsp)i)->oprnd1() != (LInsp)i); + i = uintptr_t(((LInsp)i)->oprnd1()); + break; - case LIR_start: - _i = 0; // this means the next call to this method will return 0 - return cur; - } + case LIR_start: + _i = 0; // start of trace + return cur; + } iop = ((LInsp)i)->opcode(); } while (iop==LIR_skip || iop==LIR_2); @@ -406,7 +376,7 @@ namespace nanojit } bool LIns::isFloat() const { - switch (opcode()) { + switch (firstWord.code) { default: return false; case LIR_fadd: @@ -422,69 +392,107 @@ namespace nanojit } #if defined(_DEBUG) - bool LIns::isLInsOp0() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Op0 == repKinds[opcode()]; + 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::isLInsOp1() const { - NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_Op1 == 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::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()]; + default: + return false; + } } #endif // defined(_DEBUG) bool LIns::isCmp() const { - LOpcode op = opcode(); + LOpcode op = firstWord.code; return (op >= LIR_eq && op <= LIR_uge) || (op >= LIR_feq && op <= LIR_fge); } bool LIns::isCond() const { - LOpcode op = opcode(); + LOpcode op = firstWord.code; 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 (opcode() & LIR64) != 0 || opcode() == LIR_callh; + return (firstWord.code & LIR64) != 0 || firstWord.code == LIR_callh; #else // callh in 32bit cpu's means the 32bit MSW of an int64 result in 2 registers - return (opcode() & LIR64) != 0; + return (firstWord.code & LIR64) != 0; #endif } @@ -495,7 +503,7 @@ namespace nanojit bool LIns::isconstq() const { - return opcode() == LIR_quad; + return firstWord.code == LIR_quad; } bool LIns::isconstp() const @@ -509,14 +517,14 @@ namespace nanojit bool LIns::isCse() const { - return nanojit::isCseOpcode(opcode()) || (isCall() && callInfo()->_cse); + return nanojit::isCseOpcode(firstWord.code) || (isCall() && callInfo()->_cse); } void LIns::setTarget(LInsp label) { NanoAssert(label && label->isop(LIR_label)); NanoAssert(isBranch()); - toLInsOp2()->oprnd_2 = label; + u.oprnd_2 = label; } LInsp LIns::getTarget() @@ -528,15 +536,15 @@ namespace nanojit void *LIns::payload() const { NanoAssert(isop(LIR_skip)); - // Operand 1 points to the previous LIns; we move past it to get to - // the payload. - return (void*) (uintptr_t(prevLIns()) + sizeof(LIns)); + // 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)); } uint64_t LIns::imm64() const { NanoAssert(isconstq()); - return (uint64_t(toLInsI64()->imm64_1) << 32) | uint32_t(toLInsI64()->imm64_0); + return (uint64_t(i64.imm64_1) << 32) | uint32_t(i64.imm64_0); } double LIns::imm64f() const @@ -552,7 +560,7 @@ namespace nanojit const CallInfo* LIns::callInfo() const { NanoAssert(isCall()); - return toLInsC()->ci; + return c.ci; } // Index args in r-l order. arg(0) is rightmost arg. @@ -561,9 +569,8 @@ namespace nanojit { NanoAssert(isCall()); NanoAssert(i < argc()); - // 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; + LInsp* offs = (LInsp*)this - (i+1); + return *offs; } LIns* LirWriter::ins2i(LOpcode v, LIns* oprnd1, int32_t imm) @@ -1004,23 +1011,38 @@ 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(LInsC)); // args + call + LInsp* newargs = (LInsp*)_buf->makeRoom(argc*sizeof(LInsp) + sizeof(LIns)); // args + call for (int32_t i = 0; i < argc; i++) newargs[argc - i - 1] = args[i]; // Write the call instruction itself. - LInsC* insC = (LInsC*)(uintptr_t(newargs) + argc*sizeof(LInsp)); - LIns* ins = insC->getLIns(); + LInsp l = (LInsp)(uintptr_t(newargs) + argc*sizeof(LInsp)); #ifndef NANOJIT_64BIT - ins->initLInsC(op==LIR_callh ? LIR_call : op, argc, ci); + l->setCall(op==LIR_callh ? LIR_call : op, argc, ci); #else - ins->initLInsC(op, argc, ci); + l->setCall(op, argc, ci); #endif - return ins; + l->resv()->clear(); + return l; } using namespace avmplus; @@ -1436,9 +1458,9 @@ namespace nanojit RetiredEntry *e = NJ_NEW(gc, RetiredEntry)(gc); e->i = i; for (int j=0, n=live.size(); j < n; j++) { - LInsp ins = live.keyAt(j); - if (!ins->isStore() && !ins->isGuard()) - e->live.add(ins); + LInsp l = live.keyAt(j); + if (!l->isStore() && !l->isGuard()) + e->live.add(l); } int size=0; if ((size = e->live.size()) > maxlive) @@ -1685,8 +1707,8 @@ namespace nanojit } case LIR_param: { - uint32_t arg = i->paramArg(); - if (!i->paramKind()) { + uint32_t arg = i->imm8(); + if (!i->imm8b()) { 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 ea3c6127599..59ceaf3c068 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, repkind) \ +#define OPDEF(op, number, args) \ LIR_##op = (number), -#define OPDEF64(op, number, args, repkind) \ +#define OPDEF64(op, number, args) \ LIR_##op = ((number) | LIR64), #include "LIRopcode.tbl" LIR_sentinel @@ -70,6 +70,7 @@ 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 @@ -77,6 +78,7 @@ 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 @@ -146,6 +148,13 @@ 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. @@ -154,7 +163,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 opcode:8; + LOpcode code:8; inline void init() { reg = UnknownReg; @@ -162,425 +171,107 @@ namespace nanojit used = 1; } - inline void clear() { + inline void clear() + { used = 0; } }; - //----------------------------------------------------------------------- - // 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 - { - }; - + // 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. class LIns { - private: - // Last word: fields shared by all LIns kinds. The reservation fields - // are read/written during assembly. - Reservation lastWord; + // 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-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) ); } + LIns* oprnd_2; + }; - // This is never called, but that's ok because it contains only static - // assertions. - void staticSanityCheck() + // Used for LIR_sti and LIR_stqi. + struct sti_type { - // LIns must be word-sized. - NanoStaticAssert(sizeof(LIns) == 1*sizeof(void*)); + // Nb: oprnd_1 and oprnd_2 layout must match that in u_type + // because oprnd1() and oprnd2() are used for both. + LIns* oprnd_1; - // 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 + LIns* oprnd_2; - // 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)) ); + int32_t disp; + }; - // 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)) ); - } + // 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 - 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()); - } + 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 + // are read/written during assembly. + Reservation firstWord; + + // 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; + }; + + public: LIns* oprnd1() const { - NanoAssert(isLInsOp1() || isLInsOp2() || isStore()); - return toLInsOp2()->oprnd_1; + NanoAssert(isOp1() || isOp2() || isLoad() || isStore()); + return u.oprnd_1; } LIns* oprnd2() const { - NanoAssert(isLInsOp2() || isStore()); - return toLInsOp2()->oprnd_2; + NanoAssert(isOp2() || isLoad() || isStore()); + return u.oprnd_2; } - LIns* prevLIns() const { - NanoAssert(isop(LIR_skip)); - return toLInsSk()->prevLIns; - } - - 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 { + 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 toLInsI()->imm32 << 2; + return i.imm32<<2; + } + inline void setSize(int32_t bytes) { + NanoAssert(isop(LIR_alloc) && (bytes&3)==0 && isU16(bytes>>2)); + i.imm32 = bytes>>2; } LIns* arg(uint32_t i); @@ -588,7 +279,7 @@ namespace nanojit inline int32_t immdisp() const { NanoAssert(isStore()); - return toLInsSti()->disp; + return sti.disp; } inline void* constvalp() const @@ -601,49 +292,36 @@ namespace nanojit } bool isCse() const; - bool isRet() const { return nanojit::isRetOpcode(opcode()); } - bool isop(LOpcode o) const { return opcode() == o; } + bool isRet() const { return nanojit::isRetOpcode(firstWord.code); } + bool isop(LOpcode o) const { return firstWord.code == o; } #if defined(_DEBUG) - // 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; + bool isOp1() const; // true for unary ops + bool isOp2() const; // true for binary ops #endif bool isQuad() const; bool isCond() const; bool isFloat() const; bool isCmp() const; bool isCall() const { - LOpcode op = LOpcode(opcode() & ~LIR64); + LOpcode op = LOpcode(firstWord.code & ~LIR64); return op == LIR_call; } bool isStore() const { - LOpcode op = LOpcode(opcode() & ~LIR64); + LOpcode op = LOpcode(firstWord.code & ~LIR64); return op == LIR_sti; } bool isLoad() const { - LOpcode op = opcode(); + LOpcode op = firstWord.code; return op == LIR_ldq || op == LIR_ld || op == LIR_ldc || op == LIR_ldqc || op == LIR_ldcs || op == LIR_ldcb; } bool isGuard() const { - LOpcode op = opcode(); + LOpcode op = firstWord.code; 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 opcode() == LIR_int; } + bool isconst() const { return firstWord.code == 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; @@ -655,6 +333,69 @@ 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(); @@ -662,17 +403,17 @@ namespace nanojit inline uint32_t argc() const { NanoAssert(isCall()); - return toLInsC()->argc; + return c.imm8b; } 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 @@ -749,12 +490,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(LInsSk)) + #define NJ_MAX_LINS_SZB (NJ_PAGE_CODE_AREA_SZB - sizeof(LIns)) // 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(LInsSk)) + #define NJ_MAX_SKIP_PAYLOAD_SZB (NJ_MAX_LINS_SZB - sizeof(LIns)) #ifdef NJ_VERBOSE diff --git a/js/src/nanojit/LIRopcode.tbl b/js/src/nanojit/LIRopcode.tbl index 4c4e6134ced..a98156f07e4 100644 --- a/js/src/nanojit/LIRopcode.tbl +++ b/js/src/nanojit/LIRopcode.tbl @@ -44,22 +44,15 @@ * * Includers must define OPDEF and OPDEF64 macros of the following forms: * - * #define OPDEF(op,val,operands,repkind) ... - * #define OPDEF64(op,val,operands,repkind) ... + * #define OPDEF(op,val,operands) ... + * #define OPDEF64(op,val,operands) ... * * 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, 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. + * 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 * * This file is best viewed with 128 columns: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 @@ -68,36 +61,37 @@ /* op val name operands */ /* special operations (must be 0..N) */ -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) +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) /* non-pure operations */ -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 +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 /* guards */ -OPDEF(loop, 17, 0, Op2) // loop fragment -OPDEF(x, 18, 0, Op2) // exit always +OPDEF(loop, 17, 0) // loop fragment +OPDEF(x, 18, 0) // exit always /* branches */ -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) +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 /* operators */ @@ -106,12 +100,12 @@ OPDEF(ji, 23,-1, None) // indirect jump (currently not implemented) * common-subexpression-elimination detection code. */ -OPDEF(int, 24, 0, I) // constant 32-bit integer -OPDEF(cmov, 25, 2, Op2) // conditional move (op1=cond, op2=LIR_2(iftrue,iffalse)) +OPDEF(int, 24, 0) // constant 32-bit integer +OPDEF(cmov, 25, 2) // conditional move (op1=cond, op2=cond(iftrue,iffalse)) #if defined(NANOJIT_64BIT) -OPDEF(callh, 26,-1, None) // unused on 64-bit machines +OPDEF(callh, 26, 0) #else -OPDEF(callh, 26, 1, Op1) // get the high 32 bits of a call returning a 64-bit value +OPDEF(callh, 26, 1) #endif /* @@ -123,43 +117,46 @@ OPDEF(callh, 26, 1, Op1) // get the high 32 bits of a call returning a 64 * with 3. NB: These opcodes must remain continuous so that comparison-opcode * detection works correctly. */ -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(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(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 +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(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 +// 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(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 (>>>) +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) // >>> // conditional guards, op^1 to complement. Only things that are // isCond() can be passed to these. -OPDEF(xt, 48, 1, Op2) // exit if true (0x30 0011 0000) -OPDEF(xf, 49, 1, Op2) // exit if false (0x31 0011 0001) +OPDEF(xt, 48, 1) // exit if true 0x30 0011 0000 +OPDEF(xf, 49, 1) // exit if false 0x31 0011 0001 -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 +// 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(unused52, 52,-1, None) +OPDEF(unused52, 52, 0) -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 +OPDEF(ov, 53, 1) +OPDEF(cs, 54, 1) // 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 @@ -168,96 +165,96 @@ OPDEF(cs, 54, 1, Op1) // test for carry; value must have just been // 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, 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) +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 -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(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(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(unused5_64, 5, 2) +OPDEF64(unused6_64, 6, 2) +OPDEF64(unused7_64, 7, 2) +OPDEF64(unused8_64, 8, 2) -OPDEF64(ldq, LIR_ld, 2, Op2) // 64-bit (quad) load +OPDEF64(unused9_64, 9, 2) +OPDEF64(ldq, LIR_ld, 2) // quad load -OPDEF64(unused11_64, 11,-1, None) +OPDEF64(unused11_64, 11, 2) -OPDEF64(stqi, LIR_sti, 2, Sti) // 64-bit (quad) store -OPDEF64(fret, LIR_ret, 1, Op1) +OPDEF64(stqi, LIR_sti, 2) // quad store +OPDEF64(fret, LIR_ret, 1) -OPDEF64(unused14_64, 14,-1, None) -OPDEF64(unused15_64, 15,-1, None) +OPDEF64(unused14_64, 14, 2) +OPDEF64(unused15_64, 15, 2) -OPDEF64(fcall, LIR_call, 0, C) // subroutine call returning 64-bit (quad) value +OPDEF64(fcall, LIR_call, 0) // subroutine call returning quad -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) +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) -// We strip off the 64 bit flag and compare that the opcode is between LIR_int +// We strip of the 64bit 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, I64) // 64-bit (quad) constant value -OPDEF64(qcmov, LIR_cmov, 2, Op2) // 64-bit conditional move +OPDEF64(quad, LIR_int, 0) // quad constant value +OPDEF64(qcmov, LIR_cmov, 2) +OPDEF64(unused26_64, 26, 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(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(ldqc, LIR_ldc, 2, Op2) // non-volatile 64-bit load +OPDEF64(ldqc, LIR_ldc, 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(?) +/* 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(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(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(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(i2f, 46, 1) // convert an integer to a float +OPDEF64(u2f, 47, 1) // convert an unsigned integer to a float -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) +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) diff --git a/js/src/nanojit/NativeARM.cpp b/js/src/nanojit/NativeARM.cpp index d8c0463ea0b..6387a582c93 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->paramArg())); + prefer = rmask(imm2register(i->imm8())); 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->paramArg(); - uint32_t kind = ins->paramKind(); + uint32_t a = ins->imm8(); + uint32_t kind = ins->imm8b(); 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 5a63b8efe25..7054b1c1dd6 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->paramArg()]); + findSpecificRegFor(state, argRegs[state->imm8()]); } void Assembler::asm_fcond(LInsp ins) @@ -817,8 +817,8 @@ namespace nanojit void Assembler::asm_param(LInsp ins) { - uint32_t a = ins->paramArg(); - uint32_t kind = ins->paramKind(); + uint32_t a = ins->imm8(); + uint32_t kind = ins->imm8b(); // 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 70953ef6c90..71132947b91 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->paramArg() < max_regs) - prefer &= rmask(Register(i->paramArg())); + if (i->imm8() < max_regs) + prefer &= rmask(Register(i->imm8())); } 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->paramArg(); - uint32_t kind = ins->paramKind(); + uint32_t a = ins->imm8(); + uint32_t kind = ins->imm8b(); if (kind == 0) { // ordinary param AbiKind abi = _thisfrag->lirbuf->abi;