ArgUnion getImm(const PC origPC, int idx, const Unit* unit) { auto pc = origPC; auto const op = decode_op(pc); assertx(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(immType(op, cursor), pc); } always_assert(cursor == idx); auto const type = immType(op, idx); if (type == IVA || type == LA || type == IA || type == CAR || type == CAW) { retval.u_IVA = decode_iva(pc); } else if (type == KA) { assertx(unit != nullptr); retval.u_KA = decode_member_key(pc, unit); } else if (type == LAR) { retval.u_LAR = decodeLocalRange(pc); } else if (type == FCA) { retval.u_FCA = decodeFCallArgs(pc); } else if (type == RATA) { assertx(unit != nullptr); retval.u_RATA = decodeRAT(unit, pc); } else if (!argTypeIsVector(type)) { memcpy(&retval.bytes, pc, immSize(type, pc)); } always_assert(numImmediates(op) > idx); return retval; }
std::string instrToString(const Op* it, const Unit* u /* = NULL */) { std::stringstream out; PC iStart = reinterpret_cast<PC>(it); Op op = *it; ++it; auto readRATA = [&] { if (!u) { auto const pc = reinterpret_cast<const unsigned char*>(it); it += encodedRATSize(pc); out << " <RepoAuthType>"; return; } auto pc = reinterpret_cast<const unsigned char*>(it); auto const rat = decodeRAT(u, pc); it = reinterpret_cast<const Op*>(pc); out << ' ' << show(rat); }; switch (op) { #define READ(t) out << " " << *((t*)&*it); it += sizeof(t) #define READOFF() do { \ Offset _value = *(Offset*)it; \ out << " " << _value; \ if (u != nullptr) { \ 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(type) do { \ auto const immVal = static_cast<type>( \ *reinterpret_cast<const uint8_t*>(it) \ ); \ it += sizeof(unsigned char); \ out << " " << subopToName(immVal); \ } 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)*((unsigned char*)&*it); \ out << ((immVal >= 0 && size_t(immVal) < locationNamesCount) ? \ locationCodeString(LocationCode(immVal)) : "?"); \ it += sizeof(unsigned char); \ int numLocImms = numLocationCodeImms(LocationCode(immVal)); \ for (int i = 0; i < numLocImms; ++i) { \ out << ':' << decodeVariableSizeImm((const uint8_t**)&it); \ } \ while (reinterpret_cast<const uint8_t*>(it) - start < sz) { \ immVal = (int)*((unsigned char*)&*it); \ out << " " << ((immVal >=0 && size_t(immVal) < memberNamesCount) ? \ memberCodeString(MemberCode(immVal)) : "?"); \ it += sizeof(unsigned char); \ 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(); \ String escaped = string_addslashes(str->data(), len); \ out << '"' << escaped.data() << '"'; \ } else { \ out << imm; \ } \ } \ } \ assert(reinterpret_cast<const uint8_t*>(it) - start == sz); \ } \ out << ">"; \ } while (false) #define READLITSTR(sep) do { \ Id id = readData<Id>(it); \ if (id < 0) { \ assert(op == OpSSwitch); \ out << sep << "-"; \ } else if (u) { \ const StringData* sd = u->lookupLitstrId(id); \ out << sep << "\"" << \ escapeStringForCPP(sd->data(), sd->size()) << "\""; \ } else { \ out << sep << id; \ } \ } while (false) #define READSVEC() do { \ int sz = readData<int>(it); \ out << " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out << sep; \ if (op == OpSSwitch) { \ READLITSTR(""); \ out << ":"; \ } \ Offset o = readData<Offset>(it); \ if (u != nullptr) { \ if (iStart + o == u->entry() - 1) { \ out << "Invalid"; \ } else { \ out << u->offsetOf(iStart + o); \ } \ } else { \ out << o; \ } \ sep = " "; \ } \ out << ">"; \ } while (false) #define READIVEC() do { \ int sz = readData<int>(it); \ out << " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out << sep; \ IterKind k = (IterKind)readData<Id>(it); \ switch(k) { \ case KindOfIter: out << "(Iter) "; break; \ case KindOfMIter: out << "(MIter) "; break; \ case KindOfCIter: out << "(CIter) "; break; \ } \ out << readData<Id>(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_LA READV() #define H_IA READV() #define H_DA READ(double) #define H_BA READOFF() #define H_OA(type) READOA(type) #define H_SA READLITSTR(" ") #define H_RATA readRATA() #define H_AA \ if (u) { \ out << " "; \ staticArrayStreamer(u->lookupArrayId(*((Id*)it)), out); \ } else { \ out << " " << *((Id*)it); \ } \ it += sizeof(Id) #define H_VSA do { \ int sz = readData<int32_t>(it); \ out << " <"; \ for (int i = 0; i < sz; ++i) { \ H_SA; \ } \ out << " >"; \ } while (false) #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_LA #undef H_IA #undef H_DA #undef H_BA #undef H_OA #undef H_SA #undef H_AA #undef H_VSA default: assert(false); }; return out.str(); }
std::string instrToString(PC it, Either<const Unit*, const UnitEmitter*> u) { std::stringstream out; PC iStart = it; Op op = decode_op(it); auto readRATA = [&] { if (auto unit = u.left()) { auto const rat = decodeRAT(unit, it); out << ' ' << show(rat); return; } auto const pc = it; it += encodedRATSize(pc); out << " <RepoAuthType>"; }; auto offsetOf = [u](PC pc) { return u.match( [pc](const Unit* u) { return u->offsetOf(pc); }, [pc](const UnitEmitter* ue) { return ue->offsetOf(pc); } ); }; auto lookupLitstrId = [u](Id id) { return u.match( [id](const Unit* u) { return u->lookupLitstrId(id); }, [id](const UnitEmitter* ue) { return ue->lookupLitstr(id); } ); }; auto lookupArrayId = [u](Id id) { return u.match( [id](const Unit* u) { return u->lookupArrayId(id); }, [id](const UnitEmitter* ue) { return ue->lookupArray(id); } ); }; switch (op) { #define READ(t) out << " " << *((t*)&*it); it += sizeof(t) #define READOFF() do { \ Offset _value = *(Offset*)it; \ out << " " << _value; \ if (u != nullptr) { \ out << " (" << offsetOf(iStart + _value) << ")"; \ } \ it += sizeof(Offset); \ } while (false) #define READV() out << " " << decodeVariableSizeImm(&it); #define READLA() out << " L:" << decodeVariableSizeImm(&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(type) do { \ auto const immVal = static_cast<type>( \ *reinterpret_cast<const uint8_t*>(it) \ ); \ it += sizeof(unsigned char); \ out << " " << subopToName(immVal); \ } while (false) #define READLITSTR(sep) do { \ Id id = decode_raw<Id>(it); \ if (id < 0) { \ assert(op == OpSSwitch); \ out << sep << "-"; \ } else { \ auto const sd = lookupLitstrId(id); \ out << sep << "\"" << \ escapeStringForCPP(sd->data(), sd->size()) << "\""; \ } \ } while (false) #define READSVEC() do { \ int sz = decode_raw<int>(it); \ out << " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out << sep; \ if (op == OpSSwitch) { \ READLITSTR(""); \ out << ":"; \ } \ Offset o = decode_raw<Offset>(it); \ out << offsetOf(iStart + o); \ sep = " "; \ } \ out << ">"; \ } while (false) #define READIVEC() do { \ int sz = decode_raw<int>(it); \ out << " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out << sep; \ IterKind k = (IterKind)decode_raw<Id>(it); \ switch(k) { \ case KindOfIter: out << "(Iter) "; break; \ case KindOfMIter: out << "(MIter) "; break; \ case KindOfCIter: out << "(CIter) "; break; \ } \ out << decode_raw<Id>(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_BLA READSVEC() #define H_SLA READSVEC() #define H_ILA READIVEC() #define H_IVA READIVA() #define H_I64A READ(int64_t) #define H_LA READLA() #define H_IA READV() #define H_DA READ(double) #define H_BA READOFF() #define H_OA(type) READOA(type) #define H_SA READLITSTR(" ") #define H_RATA readRATA() #define H_AA do { \ out << ' '; \ staticArrayStreamer(lookupArrayId(decode_raw<Id>(it)), out); \ } while (false) #define H_VSA do { \ int sz = decode_raw<int32_t>(it); \ out << " <"; \ for (int i = 0; i < sz; ++i) { \ H_SA; \ } \ out << " >"; \ } while (false) #define H_KA out << ' ' << show(decode_member_key(it, u)) #define O(name, imm, push, pop, flags) \ case Op##name: { \ out << #name; \ UNUSED unsigned immIdx = 0; \ imm; \ break; \ } OPCODES #undef O #undef READ #undef READV #undef READLA #undef ONE #undef TWO #undef THREE #undef FOUR #undef NA #undef H_BLA #undef H_SLA #undef H_ILA #undef H_IVA #undef H_I64A #undef H_LA #undef H_IA #undef H_DA #undef H_BA #undef H_OA #undef H_SA #undef H_AA #undef H_VSA #undef H_KA default: assert(false); }; return out.str(); }
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); } ); }
std::string instrToString(PC it, Either<const Unit*, const UnitEmitter*> u) { std::string out; PC iStart = it; Op op = decode_op(it); auto readRATA = [&] { if (auto unit = u.left()) { auto const rat = decodeRAT(unit, it); folly::format(&out, " {}", show(rat)); return; } auto const pc = it; it += encodedRATSize(pc); out += " <RepoAuthType>"; }; auto offsetOf = [u](PC pc) { return u.match( [pc](const Unit* u) { return u->offsetOf(pc); }, [pc](const UnitEmitter* ue) { return ue->offsetOf(pc); } ); }; auto lookupLitstrId = [u](Id id) { return u.match( [id](const Unit* u) { return u->lookupLitstrId(id); }, [id](const UnitEmitter* ue) { return ue->lookupLitstr(id); } ); }; auto lookupArrayId = [u](Id id) { return u.match( [id](const Unit* u) { return u->lookupArrayId(id); }, [id](const UnitEmitter* ue) { return ue->lookupArray(id); } ); }; switch (op) { #define READ(t) folly::format(&out, " {}", *((t*)&*it)); it += sizeof(t) #define READOFF() do { \ Offset _value = *(Offset*)it; \ folly::format(&out, " {}", _value); \ if (u != nullptr) { \ folly::format(&out, " ({})", offsetOf(iStart + _value)); \ } \ it += sizeof(Offset); \ } while (false) #define READV() folly::format(&out, " {}", decode_iva(it)); #define READLA() folly::format(&out, " L:{}", decode_iva(it)); #define READIVA() do { \ auto imm = decode_iva(it); \ folly::format(&out, " {}", imm); \ immIdx++; \ } while (false) #define READOA(type) do { \ auto const immVal = static_cast<type>( \ *reinterpret_cast<const uint8_t*>(it) \ ); \ it += sizeof(unsigned char); \ folly::format(&out, " {}", subopToName(immVal)); \ } while (false) #define READLITSTR(sep) do { \ Id id = decode_raw<Id>(it); \ if (id < 0) { \ assertx(op == OpSSwitch); \ folly::format(&out, "{}-", sep); \ } else { \ auto const sd = lookupLitstrId(id); \ folly::format(&out, "{}\"{}\"", sep, \ escapeStringForCPP(sd->data(), sd->size())); \ } \ } while (false) #define READSVEC() do { \ int sz = decode_iva(it); \ out += " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out += sep; \ if (op == OpSSwitch) { \ READLITSTR(""); \ out += ":"; \ } \ Offset o = decode_raw<Offset>(it); \ folly::format(&out, "{}", offsetOf(iStart + o)); \ sep = " "; \ } \ out += ">"; \ } while (false) #define READI32VEC() do { \ int sz = decode_iva(it); \ out += " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ folly::format(&out, "{}{}", sep, decode_raw<uint32_t>(it));\ sep = ", "; \ } \ out += ">"; \ } while (false) #define READBOOLVEC() do { \ int sz = decode_iva(it); \ uint8_t tmp = 0; \ out += " \""; \ for (int i = 0; i < sz; ++i) { \ if (i % 8 == 0) tmp = decode_raw<uint8_t>(it); \ out += ((tmp >> (i % 8)) & 1) ? "1" : "0"; \ } \ out += "\""; \ } while (false) #define READITERTAB() do { \ auto const sz = decode_iva(it); \ out += " <"; \ const char* sep = ""; \ for (int i = 0; i < sz; ++i) { \ out += sep; \ auto const k = (IterKind)decode_iva(it); \ switch (k) { \ case KindOfIter: out += "(Iter) "; break; \ case KindOfMIter: out += "(MIter) "; break; \ case KindOfCIter: out += "(CIter) "; break; \ case KindOfLIter: out += "(LIter) "; break; \ } \ folly::format(&out, "{}", decode_iva(it)); \ if (k == KindOfLIter) { \ folly::format(&out, " L:{}", decode_iva(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 FIVE(a, b, c, d, e) H_##a; H_##b; H_##c; H_##d; H_##e; #define NA #define H_BLA READSVEC() #define H_SLA READSVEC() #define H_ILA READITERTAB() #define H_I32LA READI32VEC() #define H_BLLA READBOOLVEC() #define H_IVA READIVA() #define H_I64A READ(int64_t) #define H_LA READLA() #define H_IA READV() #define H_CAR READV() #define H_CAW READV() #define H_DA READ(double) #define H_BA READOFF() #define H_OA(type) READOA(type) #define H_SA READLITSTR(" ") #define H_RATA readRATA() #define H_AA do { \ out += ' '; \ staticArrayStreamer(lookupArrayId(decode_raw<Id>(it)), out); \ } while (false) #define H_VSA do { \ int sz = decode_iva(it); \ out += " <"; \ for (int i = 0; i < sz; ++i) { \ H_SA; \ } \ out += " >"; \ } while (false) #define H_KA (out += ' ', out += show(decode_member_key(it, u))) #define H_LAR (out += ' ', out += show(decodeLocalRange(it))) #define H_FCA (out += ' ', out += show(decodeFCallArgs(it))) #define O(name, imm, push, pop, flags) \ case Op##name: { \ out += #name; \ UNUSED unsigned immIdx = 0; \ imm; \ break; \ } OPCODES #undef O #undef READ #undef READV #undef READLA #undef ONE #undef TWO #undef THREE #undef FOUR #undef FIVE #undef NA #undef H_BLA #undef H_SLA #undef H_ILA #undef H_I32LA #undef H_BLLA #undef H_IVA #undef H_I64A #undef H_LA #undef H_IA #undef H_CAR #undef H_CAW #undef H_DA #undef H_BA #undef H_OA #undef H_SA #undef H_AA #undef H_VSA #undef H_KA #undef H_LAR #undef H_FCA default: assertx(false); }; return out; }