/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/vm/hhbc.h" #include "hphp/runtime/ext/ext_variable.h" #include "hphp/runtime/vm/unit.h" #include "hphp/runtime/base/stats.h" #include namespace HPHP { /////////////////////////////////////////////////////////////////////////////// int numImmediates(Op opcode) { assert(isValidOpcode(opcode)); static const int8_t values[] = { #define NA 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define O(name, imm, unusedPop, unusedPush, unusedFlags) imm, OPCODES #undef O #undef NA #undef ONE #undef TWO #undef THREE #undef FOUR }; return values[uint8_t(opcode)]; } ArgType immType(const Op opcode, int idx) { assert(isValidOpcode(opcode)); assert(idx >= 0 && idx < numImmediates(opcode)); always_assert(idx < 4); // No opcodes have more than four immediates static const int8_t arg0Types[] = { #define NA -1, #define ONE(a) a, #define TWO(a, b) a, #define THREE(a, b, c) a, #define FOUR(a, b, c, d) a, #define O(name, imm, unusedPop, unusedPush, unusedFlags) imm OPCODES // re-using definition of O below. #undef NA #undef ONE #undef TWO #undef THREE #undef FOUR }; static const int8_t arg1Types[] = { #define NA -1, #define ONE(a) -1, #define TWO(a, b) b, #define THREE(a, b, c) b, #define FOUR(a, b, c, d) b, OPCODES // re-using definition of O below. #undef NA #undef ONE #undef TWO #undef THREE #undef FOUR }; static const int8_t arg2Types[] = { #define NA -1, #define ONE(a) -1, #define TWO(a, b) -1, #define THREE(a, b, c) c, #define FOUR(a, b, c, d) c, OPCODES #undef NA #undef ONE #undef TWO #undef THREE #undef FOUR }; static const int8_t arg3Types[] = { #define NA -1, #define ONE(a) -1, #define TWO(a, b) -1, #define THREE(a, b, c) -1, #define FOUR(a, b, c, d) d, OPCODES #undef O #undef NA #undef ONE #undef TWO #undef THREE #undef FOUR }; auto opInt = uint8_t(opcode); switch (idx) { case 0: return (ArgType)arg0Types[opInt]; case 1: return (ArgType)arg1Types[opInt]; case 2: return (ArgType)arg2Types[opInt]; case 3: return (ArgType)arg3Types[opInt]; default: assert(false); return (ArgType)-1; } } int immSize(const Op* opcode, int idx) { assert(idx >= 0 && idx < numImmediates(*opcode)); always_assert(idx < 4); // No opcodes have more than four immediates static const int8_t argTypeToSizes[] = { #define ARGTYPE(nm, type) sizeof(type), #define ARGTYPEVEC(nm, type) 0, ARGTYPES #undef ARGTYPE #undef ARGTYPEVEC }; if (immType(*opcode, idx) == IVA || immType(*opcode, idx) == HA || immType(*opcode, idx) == IA) { intptr_t offset = 1; if (idx >= 1) offset += immSize(opcode, 0); if (idx >= 2) offset += immSize(opcode, 1); if (idx >= 3) offset += immSize(opcode, 2); // variable size unsigned char imm = *(unsigned char*)(opcode + offset); // Low order bit set => 4-byte. return (imm & 0x1 ? sizeof(int32_t) : sizeof(unsigned char)); } else if (immIsVector(*opcode, idx)) { intptr_t offset = 1; if (idx >= 1) offset += immSize(opcode, 0); if (idx >= 2) offset += immSize(opcode, 1); if (idx >= 3) offset += immSize(opcode, 2); int prefixes, vecElemSz; auto itype = immType(*opcode, idx); if (itype == MA) { prefixes = 2; vecElemSz = sizeof(uint8_t); } else if (itype == BLA) { prefixes = 1; vecElemSz = sizeof(Offset); } else if (itype == ILA) { prefixes = 1; vecElemSz = 2 * sizeof(uint32_t); } else { assert(itype == SLA); prefixes = 1; vecElemSz = sizeof(StrVecItem); } return prefixes * sizeof(int32_t) + vecElemSz * *(int32_t*)((int8_t*)opcode + offset); } else { ArgType type = immType(*opcode, idx); return (type >= 0) ? argTypeToSizes[type] : 0; } } bool immIsVector(Op opcode, int idx) { ArgType type = immType(opcode, idx); return (type == MA || type == BLA || type == SLA || type == ILA); } bool hasImmVector(Op opcode) { const int num = numImmediates(opcode); for (int i = 0; i < num; ++i) { if (immIsVector(opcode, i)) return true; } return false; } ArgUnion getImm(const Op* opcode, int idx) { const Op* p = opcode + 1; assert(idx >= 0 && idx < numImmediates(*opcode)); ArgUnion retval; retval.u_NA = 0; int cursor = 0; for (cursor = 0; cursor < idx; cursor++) { // Advance over this immediate. p += immSize(opcode, cursor); } always_assert(cursor == idx); ArgType type = immType(*opcode, idx); if (type == IVA || type == HA || type == IA) { retval.u_IVA = decodeVariableSizeImm((const uint8_t**)&p); } else if (!immIsVector(*opcode, cursor)) { memcpy(&retval.bytes, p, immSize(opcode, idx)); } always_assert(numImmediates(*opcode) > idx); return retval; } ArgUnion* getImmPtr(const Op* opcode, int idx) { assert(immType(*opcode, idx) != IVA); assert(immType(*opcode, idx) != HA); assert(immType(*opcode, idx) != IA); const Op* ptr = opcode + 1; for (int i = 0; i < idx; i++) { ptr += immSize(opcode, i); } return (ArgUnion*)ptr; } template T decodeImm(const unsigned char** immPtr) { T val = *(T*)*immPtr; *immPtr += sizeof(T); return val; } int64_t decodeMemberCodeImm(const unsigned char** immPtr, MemberCode mcode) { switch (mcode) { case MEL: case MPL: return decodeVariableSizeImm(immPtr); case MET: case MPT: return decodeImm(immPtr); case MEI: return decodeImm(immPtr); default: not_reached(); } } // TODO: merge with emitIVA in unit.h size_t encodeVariableSizeImm(int32_t n, unsigned char* buf) { if (LIKELY((n & 0x7f) == n)) { *buf = static_cast(n) << 1; return 1; } assert((n & 0x7fffffff) == n); *reinterpret_cast(buf) = (uint32_t(n) << 1) | 0x1; return 4; } void encodeIvaToVector(std::vector& out, int32_t val) { size_t currentLen = out.size(); out.resize(currentLen + 4); out.resize(currentLen + encodeVariableSizeImm(val, &out[currentLen])); } int instrLen(const Op* opcode) { auto op = *opcode; int len = 1; int nImm = numImmediates(op); for (int i = 0; i < nImm; i++) { len += immSize(opcode, i); } return len; } InstrFlags instrFlags(Op opcode) { static const InstrFlags instrFlagsData[] = { #define O(unusedName, unusedImm, unusedPop, unusedPush, flags) flags, OPCODES #undef O }; return instrFlagsData[uint8_t(opcode)]; } Offset* instrJumpOffset(Op* instr) { static const int8_t jumpMask[] = { #define NA 0 #define MA 0 #define IVA 0 #define I64A 0 #define DA 0 #define SA 0 #define AA 0 #define BA 1 #define HA 0 #define IA 0 #define OA 0 #define ONE(a) a #define TWO(a, b) (a + 2 * b) #define THREE(a, b, c) (a + 2 * b + 4 * c) #define FOUR(a, b, c, d) (a + 2 * b + 4 * c + 8 * d) #define O(name, imm, pop, push, flags) imm, OPCODES #undef NA #undef MA #undef IVA #undef I64A #undef DA #undef SA #undef AA #undef HA #undef IA #undef BA #undef OA #undef ONE #undef TWO #undef THREE #undef FOUR #undef O }; assert(!isSwitch(*instr)); if (Op(*instr) == OpIterBreak) { uint32_t veclen = *(uint32_t *)(instr + 1); assert(veclen > 0); Offset* target = (Offset *)((uint32_t *)(instr + 1) + 2 * veclen + 1); return target; } int mask = jumpMask[uint8_t(*instr)]; if (mask == 0) { return nullptr; } int immNum; switch (mask) { case 0: return nullptr; case 1: immNum = 0; break; case 2: immNum = 1; break; case 4: immNum = 2; break; case 8: immNum = 3; break; default: assert(false); return nullptr; } return &getImmPtr(instr, immNum)->u_BA; } Offset instrJumpTarget(const Op* instrs, Offset pos) { Offset* offset = instrJumpOffset(const_cast(instrs + pos)); if (!offset) { return InvalidAbsoluteOffset; } else { return *offset + pos; } } /** * Return the number of successor-edges including fall-through paths but not * implicit exception paths. */ int numSuccs(const Op* instr) { if (!instrIsControlFlow(*instr)) return 1; if ((instrFlags(*instr) & TF) != 0) { if (isSwitch(*instr)) { return *(int*)(instr + 1); } if (*instr == OpJmp || *instr == OpIterBreak) return 1; return 0; } if (instrJumpOffset(const_cast(instr))) return 2; return 1; } /** * instrNumPops() returns the number of values consumed from the stack * for a given push/pop instruction. For peek/poke instructions, this * function returns 0. */ int instrNumPops(const Op* opcode) { static const int8_t numberOfPops[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define LMANY(...) -1 #define C_LMANY(...) -2 #define V_LMANY(...) -2 #define R_LMANY(...) -2 #define FMANY -3 #define CVMANY -3 #define CMANY -3 #define O(name, imm, pop, push, flags) pop, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef LMANY #undef C_LMANY #undef V_LMANY #undef R_LMANY #undef FMANY #undef CVMANY #undef CMANY #undef O }; int n = numberOfPops[uint8_t(*opcode)]; // For most instructions, we know how many values are popped based // solely on the opcode if (n >= 0) return n; // FCall and NewTuple specify how many values are popped in their // first immediate if (n == -3) return getImm(opcode, 0).u_IVA; // For instructions with vector immediates, we have to scan the // contents of the vector immediate to determine how many values // are popped assert(n == -1 || n == -2); ImmVector iv = getImmVector(opcode); // Count the number of values on the stack accounted for by the // ImmVector's location and members int k = iv.numStackValues(); // If this instruction also takes a RHS, count that too if (n == -2) ++k; return k; } /** * instrNumPushes() returns the number of values pushed onto the stack * for a given push/pop instruction. For peek/poke instructions or * InsertMid instructions, this function returns 0. */ int instrNumPushes(const Op* opcode) { static const int8_t numberOfPushes[] = { #define NOV 0 #define ONE(...) 1 #define TWO(...) 2 #define THREE(...) 3 #define FOUR(...) 4 #define INS_1(...) 0 #define INS_2(...) 0 #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_1 #undef INS_2 #undef O }; return numberOfPushes[uint8_t(*opcode)]; } StackTransInfo instrStackTransInfo(const Op* opcode) { static const StackTransInfo::Kind transKind[] = { #define NOV StackTransInfo::Kind::PushPop #define ONE(...) StackTransInfo::Kind::PushPop #define TWO(...) StackTransInfo::Kind::PushPop #define THREE(...) StackTransInfo::Kind::PushPop #define FOUR(...) StackTransInfo::Kind::PushPop #define INS_1(...) StackTransInfo::Kind::InsertMid #define INS_2(...) StackTransInfo::Kind::InsertMid #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_1 #undef INS_2 #undef O }; static const int8_t peekPokeType[] = { #define NOV -1 #define ONE(...) -1 #define TWO(...) -1 #define THREE(...) -1 #define FOUR(...) -1 #define INS_1(...) 0 #define INS_2(...) 1 #define O(name, imm, pop, push, flags) push, OPCODES #undef NOV #undef ONE #undef TWO #undef THREE #undef FOUR #undef INS_2 #undef INS_1 #undef O }; StackTransInfo ret; ret.kind = transKind[uint8_t(*opcode)]; switch (ret.kind) { case StackTransInfo::Kind::PushPop: ret.pos = 0; ret.numPushes = instrNumPushes(opcode); ret.numPops = instrNumPops(opcode); return ret; case StackTransInfo::Kind::InsertMid: ret.numPops = 0; ret.numPushes = 0; ret.pos = peekPokeType[uint8_t(*opcode)]; return ret; default: NOT_REACHED(); } } bool pushesActRec(Op opcode) { switch (opcode) { case OpFPushFunc: case OpFPushFuncD: case OpFPushFuncU: case OpFPushObjMethod: case OpFPushObjMethodD: case OpFPushClsMethod: case OpFPushClsMethodF: case OpFPushClsMethodD: case OpFPushCtor: case OpFPushCtorD: case OpFPushCufIter: case OpFPushCuf: case OpFPushCufF: case OpFPushCufSafe: return true; default: return false; } } static void staticArrayStreamer(ArrayData* ad, std::stringstream& out) { out << "array("; if (!ad->empty()) { bool comma = false; for (ArrayIter it(ad); !it.end(); it.next()) { if (comma) { out << ","; } else { comma = true; } Variant key = it.first(); // Key. switch (key.getType()) { case KindOfInt64: { out << *key.getInt64Data(); break; } case KindOfStaticString: case KindOfString: { out << "\"" << Util::escapeStringForCPP(key.getStringData()->data(), key.getStringData()->size()) << "\""; break; } default: assert(false); } out << "=>"; // Value. Variant val = it.second(); switch (val.getType()) { case KindOfUninit: case KindOfNull: { out << "null"; break; } case KindOfBoolean: { out << (val.toBoolean() ? "true" : "false"); break; } case KindOfStaticString: case KindOfString: { out << "\"" << Util::escapeStringForCPP(val.getStringData()->data(), val.getStringData()->size()) << "\""; break; } case KindOfInt64: { out << *val.getInt64Data(); break; } case KindOfDouble: { out << *val.getDoubleData(); break; } case KindOfArray: { staticArrayStreamer(val.getArrayData(), out); break; } default: assert(false); } } } out << ")"; } void staticStreamer(const TypedValue* tv, std::stringstream& out) { switch (tv->m_type) { case KindOfUninit: case KindOfNull: { out << "null"; break; } case KindOfBoolean: { out << (tv->m_data.num ? "true" : "false"); break; } case KindOfStaticString: case KindOfString: { out << "\"" << tv->m_data.pstr->data() << "\""; break; } case KindOfInt64: { out << tv->m_data.num; break; } case KindOfDouble: { out << tv->m_data.dbl; break; } case KindOfArray: { staticArrayStreamer(tv->m_data.parr, out); break; } default: assert(false); } } const char* const locationNames[] = { "L", "C", "H", "GL", "GC", "NL", "NC", "SL", "SC", "R" }; const size_t locationNamesCount = sizeof(locationNames) / sizeof(*locationNames); static_assert(locationNamesCount == NumLocationCodes, "Location code missing for locationCodeString"); const char* locationCodeString(LocationCode lcode) { assert(lcode >= 0 && lcode < NumLocationCodes); return locationNames[lcode]; } LocationCode parseLocationCode(const char* s) { if (!*s) return InvalidLocationCode; switch (*s) { case 'L': return LL; case 'C': return LC; case 'H': return LH; case 'R': return LR; default: int incr = (s[1] == 'C'); switch (*s) { case 'G': return LocationCode(LGL + incr); case 'N': return LocationCode(LNL + incr); case 'S': return LocationCode(LSL + incr); } return InvalidLocationCode; } } const char* const memberNames[] = { "EC", "PC", "EL", "PL", "ET", "PT", "EI", "W" }; const size_t memberNamesCount = sizeof(memberNames) / sizeof(*memberNames); static_assert(memberNamesCount == NumMemberCodes, "Member code missing for memberCodeString"); const char* memberCodeString(MemberCode mcode) { assert(mcode >= 0 && mcode < NumMemberCodes); return memberNames[mcode]; } MemberCode parseMemberCode(const char* s) { int incr; switch (*s) { case 'W': return MW; case 'E': incr = 0; break; case 'P': incr = 1; break; default: return InvalidMemberCode; } switch (s[1]) { case 'C': return MemberCode(MEC + incr); case 'L': return MemberCode(MEL + incr); case 'T': return MemberCode(MET + incr); case 'I': return incr ? InvalidMemberCode : MEI; default: return InvalidMemberCode; } } std::string instrToString(const Op* it, const Unit* u /* = NULL */) { // IncDec names static const char* incdecNames[] = { "PreInc", "PostInc", "PreDec", "PostDec" }; static const int incdecNamesCount = (int)(sizeof(incdecNames)/sizeof(const char*)); // SetOp names static const char* setopNames[] = { #define SETOP_OP(setOpOp, bcOp) #bcOp, SETOP_OPS #undef SETOP_OP }; static const int setopNamesCount = (int)(sizeof(setopNames)/sizeof(const char*)); std::stringstream out; const Op* iStart = it; Op op = *it; ++it; switch (op) { #define READ(t) out << " " << *((t*)&*it); it += sizeof(t) #define READOFF() do { \ Offset _value = *(Offset*)it; \ out << " " << _value; \ if (u != nullptr && _value >= 0) { \ out << " (" << u->offsetOf(iStart + _value) << ")"; \ } \ it += sizeof(Offset); \ } while (false) #define READV() out << " " << decodeVariableSizeImm((const uint8_t**)&it); #define READIVA() do { \ out << " "; \ auto imm = decodeVariableSizeImm((const uint8_t**)&it); \ if (op == OpIncStat && immIdx == 0) { \ out << Stats::g_counterNames[imm]; \ } else { \ out << imm; \ } \ immIdx++; \ } while (false) #define READOA() do { \ int immVal = (int)*((uchar*)&*it); \ it += sizeof(uchar); \ out << " "; \ switch (op) { \ case OpIncDecL: case OpIncDecN: case OpIncDecG: case OpIncDecS: \ case OpIncDecM: \ out << ((immVal >= 0 && immVal < incdecNamesCount) ? \ incdecNames[immVal] : "?"); \ break; \ case OpSetOpL: case OpSetOpN: case OpSetOpG: case OpSetOpS: \ case OpSetOpM: \ out << ((immVal >=0 && immVal < setopNamesCount) ? \ setopNames[immVal] : "?"); \ break; \ default: \ out << immVal; \ break; \ } \ } while (false) #define READVEC() do { \ int sz = *((int*)&*it); \ it += sizeof(int) * 2; \ const uint8_t* const start = (uint8_t*)it; \ out << " <"; \ if (sz > 0) { \ int immVal = (int)*((uchar*)&*it); \ out << ((immVal >= 0 && size_t(immVal) < locationNamesCount) ? \ locationCodeString(LocationCode(immVal)) : "?"); \ it += sizeof(uchar); \ int numLocImms = numLocationCodeImms(LocationCode(immVal)); \ for (int i = 0; i < numLocImms; ++i) { \ out << ':' << decodeVariableSizeImm((const uint8_t**)&it); \ } \ while (reinterpret_cast(it) - start < sz) { \ immVal = (int)*((uchar*)&*it); \ out << " " << ((immVal >=0 && size_t(immVal) < memberNamesCount) ? \ memberCodeString(MemberCode(immVal)) : "?"); \ it += sizeof(uchar); \ if (memberCodeHasImm(MemberCode(immVal))) { \ int64_t imm = decodeMemberCodeImm((const uint8_t**)&it, \ MemberCode(immVal)); \ out << ':'; \ if (memberCodeImmIsString(MemberCode(immVal)) && u) { \ const StringData* str = u->lookupLitstrId(imm); \ int len = str->size(); \ char* escaped = string_addslashes(str->data(), len); \ out << '"' << escaped << '"'; \ free(escaped); \ } else { \ out << imm; \ } \ } \ } \ assert(reinterpret_cast(it) - start == sz); \ } \ out << ">"; \ } while (false) #define READLITSTR(sep) do { \ Id id = readData(it); \ if (id < 0) { \ assert(op == OpSSwitch); \ out << sep << "-"; \ } else if (u) { \ const StringData* sd = u->lookupLitstrId(id); \ out << sep << "\"" << \ Util::escapeStringForCPP(sd->data(), sd->size()) << "\""; \ } else { \ out << sep << id; \ } \ } while (false) #define READSVEC() do { \ int sz = readData(it); \ out << " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out << sep; \ if (op == OpSSwitch) { \ READLITSTR(""); \ out << ":"; \ } \ Offset o = readData(it); \ if (u != nullptr) { \ if (iStart + o == (Op*)u->entry() - 1) { \ out << "Invalid"; \ } else { \ out << u->offsetOf(iStart + o); \ } \ } else { \ out << o; \ } \ sep = " "; \ } \ out << ">"; \ } while (false) #define READIVEC() do { \ int sz = readData(it); \ out << " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out << sep; \ IterKind k = (IterKind)readData(it); \ switch(k) { \ case KindOfIter: out << "(Iter) "; break; \ case KindOfMIter: out << "(MIter) "; break; \ case KindOfCIter: out << "(CIter) "; break; \ } \ out << readData(it); \ sep = ", "; \ } \ out << ">"; \ } while (false) #define ONE(a) H_##a #define TWO(a, b) H_##a; H_##b #define THREE(a, b, c) H_##a; H_##b; H_##c; #define FOUR(a, b, c, d) H_##a; H_##b; H_##c; H_##d; #define NA #define H_MA READVEC() #define H_BLA READSVEC() #define H_SLA READSVEC() #define H_ILA READIVEC() #define H_IVA READIVA() #define H_I64A READ(int64_t) #define H_HA READV() #define H_IA READV() #define H_DA READ(double) #define H_BA READOFF() #define H_OA READOA() #define H_SA READLITSTR(" ") #define H_AA \ if (u) { \ out << " "; \ staticArrayStreamer(u->lookupArrayId(*((Id*)it)), out); \ } else { \ out << " " << *((Id*)it); \ } \ it += sizeof(Id) #define O(name, imm, push, pop, flags) \ case Op##name: { \ out << #name; \ UNUSED unsigned immIdx = 0; \ imm; \ break; \ } OPCODES #undef O #undef READ #undef ONE #undef TWO #undef THREE #undef FOUR #undef NA #undef H_MA #undef H_BLA #undef H_SLA #undef H_ILA #undef H_IVA #undef H_I64A #undef H_HA #undef H_IA #undef H_DA #undef H_BA #undef H_OA #undef H_SA #undef H_AA default: assert(false); }; return out.str(); } const char* opcodeToName(Op op) { const char* namesArr[] = { #define O(name, imm, inputs, outputs, flags) \ #name , OPCODES #undef O }; if (op >= Op::LowInvalid && op <= Op::HighInvalid) { return namesArr[uint8_t(op)]; } return "Invalid"; } bool instrIsControlFlow(Op opcode) { InstrFlags opFlags = instrFlags(opcode); return (opFlags & CF) != 0; } bool instrIsNonCallControlFlow(Op opcode) { if (!instrIsControlFlow(opcode) || isFCallStar(opcode)) return false; switch (opcode) { case OpContEnter: case OpFCallBuiltin: case OpIncl: case OpInclOnce: case OpReq: case OpReqOnce: case OpReqDoc: return false; default: return true; } } bool instrAllowsFallThru(Op opcode) { InstrFlags opFlags = instrFlags(opcode); return (opFlags & TF) == 0; } bool instrReadsCurrentFpi(Op opcode) { InstrFlags opFlags = instrFlags(opcode); return (opFlags & FF) != 0; } ImmVector getImmVector(const Op* opcode) { int numImm = numImmediates(*opcode); for (int k = 0; k < numImm; ++k) { ArgType t = immType(*opcode, k); if (t == MA) { void* vp = getImmPtr(opcode, k); return ImmVector::createFromStream( static_cast(vp)); } else if (t == BLA || t == SLA || t == ILA) { void* vp = getImmPtr(opcode, k); return ImmVector::createFromStream( static_cast(vp)); } } NOT_REACHED(); } const uint8_t* ImmVector::findLastMember() const { assert(m_length > 0); // Loop that does basically the same as numStackValues(), except // stop at the last. const uint8_t* vec = m_start; const LocationCode locCode = LocationCode(*vec++); const int numLocImms = numLocationCodeImms(locCode); for (int i = 0; i < numLocImms; ++i) { decodeVariableSizeImm(&vec); } for (;;) { const uint8_t* ret = vec; MemberCode code = MemberCode(*vec++); if (memberCodeHasImm(code)) { decodeMemberCodeImm(&vec, code); } if (vec - m_start == m_length) { return ret; } assert(vec - m_start < m_length); } NOT_REACHED(); } bool ImmVector::decodeLastMember(const Unit* u, StringData*& sdOut, MemberCode& membOut, int64_t* strIdOut /*=NULL*/) const { const uint8_t* vec = findLastMember(); membOut = MemberCode(*vec++); if (memberCodeImmIsString(membOut)) { int64_t strId = decodeMemberCodeImm(&vec, membOut); if (strIdOut) *strIdOut = strId; sdOut = u->lookupLitstrId(strId); return true; } return false; } int instrSpToArDelta(const Op* opcode) { // This function should only be called for instructions that read // the current FPI assert(instrReadsCurrentFpi(*opcode)); // The delta from sp to ar is equal to the number of values on the stack // that will be consumed by this instruction (numPops) plus the number of // parameters pushed onto the stack so far that are not being consumed by // this instruction (numExtra). For the FPass* instructions, numExtra will // be equal to the first immediate argument (param id). For the FCall // instructions, numExtra will be 0 because all of the parameters on the // stack are already accounted for by numPops. int numPops = instrNumPops(opcode); int numExtra = isFCallStar(*opcode) ? 0 : getImm(opcode, 0).u_IVA; int delta = numPops + numExtra; return delta; } const MInstrInfo& getMInstrInfo(Op op) { static const MInstrInfo mInstrInfo[] = { #define MII(instr, attrs, bS, iS, vC, fN) \ {MI_##instr##M, \ {MIA_none, MIA_none, MInstrAttr((attrs) & MIA_base), \ MInstrAttr((attrs) & MIA_base), MInstrAttr((attrs) & MIA_base), \ MInstrAttr((attrs) & MIA_base), MInstrAttr((attrs) & MIA_base), \ MIA_none, \ MIA_none}, \ {MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_intermediate), \ MInstrAttr((attrs) & MIA_final)}, \ unsigned(vC), bool((attrs) & MIA_new), bool((attrs) & MIA_final_get), \ #instr}, MINSTRS #undef MII }; switch (op) { #define MII(instr_, attrs, bS, iS, vC, fN) \ case Op##instr_##M: { \ const MInstrInfo& mii = mInstrInfo[MI_##instr_##M]; \ assert(mii.instr() == MI_##instr_##M); \ return mii; \ } MINSTRS #undef MII default: not_reached(); } } /////////////////////////////////////////////////////////////////////////////// }