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)) { decodeVariableSizeImm(&vec); } if (vec - m_start == m_length) { return ret; } ASSERT(vec - m_start < m_length); } NOT_REACHED(); }
ArgUnion getImm(PC const origPC, int idx, const Unit* unit) { auto pc = origPC; auto const UNUSED op = decode_op(pc); assert(idx >= 0 && idx < numImmediates(op)); ArgUnion retval; retval.u_NA = 0; int cursor = 0; for (cursor = 0; cursor < idx; cursor++) { // Advance over this immediate. pc += immSize(origPC, cursor); } always_assert(cursor == idx); auto const type = immType(op, idx); if (type == IVA || type == LA || type == IA) { retval.u_IVA = decodeVariableSizeImm(&pc); } else if (type == KA) { assert(unit != nullptr); retval.u_KA = decode_member_key(pc, unit); } else if (!immIsVector(op, cursor)) { always_assert(type != RATA); // Decode RATAs with a different function. memcpy(&retval.bytes, pc, immSize(origPC, idx)); } always_assert(numImmediates(op) > idx); return retval; }
MInstrLocation getMLocation(const Op* opcode) { auto immVec = getImmVector(opcode); auto vec = immVec.vec(); auto const lcode = LocationCode(*vec++); auto const imm = numLocationCodeImms(lcode) ? decodeVariableSizeImm(&vec) : 0; return {lcode, imm}; }
bool Unit::MetaHandle::nextArg(MetaInfo& info) { ASSERT(index && cur && ptr); uint8* end = (uint8*)index + index[*index + cur + 2]; ASSERT(ptr <= end); if (ptr == end) return false; info.m_kind = (Unit::MetaInfo::Kind)*ptr++; info.m_arg = *ptr++; info.m_data = decodeVariableSizeImm(&ptr); return true; }
// A ContSuspend marks a return point from a generator or async // function. Execution will resume at this function later, and the // Continuation associated with this function can predict where. void CmdNext::setupStepCont(ActRec* fp, PC pc) { // ContSuspend is followed by the label where execution will continue. DEBUG_ONLY auto ops = reinterpret_cast<const Op*>(pc); assert(ops[0] == OpContSuspend || ops[0] == OpContSuspendK); ++pc; int32_t label = decodeVariableSizeImm(&pc); c_Continuation* cont = frame_continuation(fp); Offset nextInst = cont->getExecutionOffset(label); assert(nextInst != InvalidAbsoluteOffset); m_stepContTag = cont->actRec(); TRACE(2, "CmdNext: patch for cont step at '%s' offset %d\n", fp->m_func->fullName()->data(), nextInst); m_stepCont = StepDestination(fp->m_func->unit(), nextInst); }
// Await / Yield opcodes mark a suspend points of async functions and // generators. Execution will resume at this function later after the // opcode. void CmdNext::setupStepSuspend(ActRec* fp, PC pc) { // Yield is followed by the label where execution will continue. auto const op = decode_op(pc); assert(op == OpAwait || op == OpYield || op == OpYieldK); if (op == OpAwait) { decodeVariableSizeImm(&pc); } Offset nextInst = fp->func()->unit()->offsetOf(pc); assert(nextInst != InvalidAbsoluteOffset); m_stepResumableId = fp; TRACE(2, "CmdNext: patch for resumable step at '%s' offset %d\n", fp->m_func->fullName()->data(), nextInst); m_stepResumable = StepDestination(fp->m_func->unit(), nextInst); }
int64 decodeMemberCodeImm(const unsigned char** immPtr, MemberCode mcode) { switch (mcode) { case MEL: case MPL: return decodeVariableSizeImm(immPtr); case MET: case MPT: return decodeImm<int32>(immPtr); case MEI: return decodeImm<int64>(immPtr); default: not_reached(); } }
std::vector<MVectorItem> getMVector(const Op* opcode) { auto immVec = getImmVector(opcode); std::vector<MVectorItem> result; auto it = immVec.vec(); auto end = it + immVec.size(); // Skip the LocationCode and its immediate auto const lcode = LocationCode(*it++); if (numLocationCodeImms(lcode)) decodeVariableSizeImm(&it); while (it < end) { auto const mcode = MemberCode(*it++); auto const imm = memberCodeHasImm(mcode) ? decodeMemberCodeImm(&it, mcode) : 0; result.push_back({mcode, imm}); } return result; }
ArgUnion getImm(const Opcode* opcode, int idx) { const Opcode* 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(&p); } else if (!immIsVector(*opcode, cursor)) { memcpy(&retval.bytes, p, immSize(opcode, idx)); } always_assert(numImmediates(*opcode) > idx); return retval; }
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); auto const type = immType(*opcode, idx); if (type == IVA || type == LA || type == IA) { retval.u_IVA = decodeVariableSizeImm((const uint8_t**)&p); } else if (!immIsVector(*opcode, cursor)) { always_assert(type != RATA); // Decode RATAs with a different function. memcpy(&retval.bytes, p, immSize(opcode, idx)); } always_assert(numImmediates(*opcode) > idx); return retval; }
int64_t decodeMemberCodeImm(const unsigned char** immPtr, MemberCode mcode) { switch (mcode) { case MEL: case MPL: return decodeVariableSizeImm(immPtr); case MET: case MPT: case MQT: return decodeImm<int32_t>(immPtr); case MEI: return decodeImm<int64_t>(immPtr); case MEC: case MPC: case MW: case InvalidMemberCode: break; } not_reached(); }
void print_instr(Output& out, const FuncInfo& finfo, PC pc) { auto const startPc = pc; auto rel_label = [&] (Offset off) { auto const tgt = startPc - finfo.unit->at(0) + off; return jmp_label(finfo, tgt); }; auto print_minstr = [&] { auto const immVec = ImmVector::createFromStream(pc); pc += immVec.size() + sizeof(int32_t) + sizeof(int32_t); auto vec = immVec.vec(); auto const lcode = static_cast<LocationCode>(*vec++); out.fmt(" <{}", locationCodeString(lcode)); if (numLocationCodeImms(lcode)) { always_assert(numLocationCodeImms(lcode) == 1); out.fmt(":${}", loc_name(finfo, decodeVariableSizeImm(&vec))); } while (vec < pc) { auto const mcode = static_cast<MemberCode>(*vec++); out.fmt(" {}", memberCodeString(mcode)); auto const imm = [&] { return decodeMemberCodeImm(&vec, mcode); }; switch (memberCodeImmType(mcode)) { case MCodeImm::None: break; case MCodeImm::Local: out.fmt(":${}", loc_name(finfo, imm())); break; case MCodeImm::String: out.fmt(":{}", escaped(finfo.unit->lookupLitstrId(imm()))); break; case MCodeImm::Int: out.fmt(":{}", imm()); break; } } assert(vec == pc); out.fmt(">"); }; auto print_switch = [&] { auto const vecLen = decode<int32_t>(pc); out.fmt(" <"); for (auto i = int32_t{0}; i < vecLen; ++i) { auto const off = decode<Offset>(pc); FTRACE(1, "sw label: {}\n", off); out.fmt("{}{}", i != 0 ? " " : "", rel_label(off)); } out.fmt(">"); }; auto print_sswitch = [&] { auto const vecLen = decode<int32_t>(pc); out.fmt(" <"); for (auto i = int32_t{0}; i < vecLen; ++i) { auto const strId = decode<Id>(pc); auto const offset = decode<Offset>(pc); out.fmt("{}{}:{}", i != 0 ? " " : "", strId == -1 ? "-" : escaped(finfo.unit->lookupLitstrId(strId)), rel_label(offset) ); } out.fmt(">"); }; auto print_itertab = [&] { auto const vecLen = decode<int32_t>(pc); out.fmt(" <"); for (auto i = int32_t{0}; i < vecLen; ++i) { auto const kind = static_cast<IterKind>(decode<int32_t>(pc)); auto const id = decode<int32_t>(pc); auto const kindStr = [&]() -> const char* { switch (kind) { case KindOfIter: return "(Iter)"; case KindOfMIter: return "(MIter)"; case KindOfCIter: return "(CIter)"; } not_reached(); }(); out.fmt("{}{} {}", i != 0 ? ", " : "", kindStr, id); } out.fmt(">"); }; auto print_stringvec = [&] { auto const vecLen = decode<int32_t>(pc); out.fmt(" <"); for (auto i = uint32_t{0}; i < vecLen; ++i) { auto const str = finfo.unit->lookupLitstrId(decode<int32_t>(pc)); out.fmt("{}{}", i != 0 ? " " : "", escaped(str)); } out.fmt(">"); }; #define IMM_MA print_minstr(); #define IMM_BLA print_switch(); #define IMM_SLA print_sswitch(); #define IMM_ILA print_itertab(); #define IMM_IVA out.fmt(" {}", decodeVariableSizeImm(&pc)); #define IMM_I64A out.fmt(" {}", decode<int64_t>(pc)); #define IMM_LA out.fmt(" ${}", loc_name(finfo, decodeVariableSizeImm(&pc))); #define IMM_IA out.fmt(" {}", decodeVariableSizeImm(&pc)); #define IMM_DA out.fmt(" {}", decode<double>(pc)); #define IMM_SA out.fmt(" {}", \ escaped(finfo.unit->lookupLitstrId(decode<Id>(pc)))); #define IMM_AA out.fmt(" @A_{}", decode<Id>(pc)); #define IMM_BA out.fmt(" {}", rel_label(decode<Offset>(pc))); #define IMM_OA(ty) out.fmt(" {}", \ subopToName(static_cast<ty>(decode<uint8_t>(pc)))); #define IMM_VSA print_stringvec(); #define IMM_NA #define IMM_ONE(x) IMM_##x #define IMM_TWO(x,y) IMM_ONE(x) IMM_ONE(y) #define IMM_THREE(x,y,z) IMM_TWO(x,y) IMM_ONE(z) #define IMM_FOUR(x,y,z,l) IMM_THREE(x,y,z) IMM_ONE(l) out.indent(); #define O(opcode, imms, ...) \ case Op::opcode: \ ++pc; \ out.fmt("{}", #opcode); \ IMM_##imms \ break; switch (*reinterpret_cast<const Op*>(pc)) { OPCODES } #undef O assert(pc == startPc + instrLen(reinterpret_cast<const Op*>(startPc))); #undef IMM_NA #undef IMM_ONE #undef IMM_TWO #undef IMM_THREE #undef IMM_FOUR #undef IMM_MA #undef IMM_BLA #undef IMM_SLA #undef IMM_ILA #undef IMM_IVA #undef IMM_I64A #undef IMM_LA #undef IMM_IA #undef IMM_DA #undef IMM_SA #undef IMM_AA #undef IMM_BA #undef IMM_OA #undef IMM_VSA out.nl(); }
void emitAwait(IRGS& env, int32_t numIters) { auto const resumeOffset = nextBcOff(env); assertx(curFunc(env)->isAsync()); if (curFunc(env)->isAsyncGenerator()) PUNT(Await-AsyncGenerator); auto const exitSlow = makeExitSlow(env); if (!topC(env)->isA(TObj)) PUNT(Await-NonObject); auto const child = popC(env); gen(env, JmpZero, exitSlow, gen(env, IsWaitHandle, child)); // cns() would ODR-use these auto const kSucceeded = c_WaitHandle::STATE_SUCCEEDED; auto const kFailed = c_WaitHandle::STATE_FAILED; auto const state = gen(env, LdWHState, child); /* * HHBBC may have proven something about the inner type of this wait handle. * * So, we may have an assertion on the type of the top of the stack after * this instruction. We know the next bytecode instruction is reachable from * fallthrough on the Await, so if it is an AssertRATStk 0, anything coming * out of the wait handle must be a subtype of that type, so this is a safe * and conservative way to do this optimization (even if our successor * bytecode offset is a jump target from things we aren't thinking about * here). */ auto const knownTy = [&] { auto pc = curUnit(env)->at(resumeOffset); if (*reinterpret_cast<const Op*>(pc) != Op::AssertRATStk) return TInitCell; ++pc; auto const stkLoc = decodeVariableSizeImm(&pc); if (stkLoc != 0) return TInitCell; auto const rat = decodeRAT(curUnit(env), pc); auto const ty = ratToAssertType(env, rat); return ty ? *ty : TInitCell; }(); ifThenElse( env, [&] (Block* taken) { auto const succeeded = gen(env, EqInt, state, cns(env, kSucceeded)); gen(env, JmpNZero, taken, succeeded); }, [&] { // Next: the wait handle is not finished, we need to suspend auto const failed = gen(env, EqInt, state, cns(env, kFailed)); gen(env, JmpNZero, exitSlow, failed); if (resumed(env)) { implAwaitR(env, child, resumeOffset); } else { implAwaitE(env, child, resumeOffset, numIters); } }, [&] { // Taken: retrieve the result from the wait handle auto const res = gen(env, LdWHResult, knownTy, child); gen(env, IncRef, res); gen(env, DecRef, child); push(env, res); } ); }