void RegionDesc::Block::checkInstruction(Op op) const { if (instrFlags(op) & TF) { FTRACE(1, "Bad block: {}\n", show(*this)); assertx(!"Block may not contain non-fallthrough instruction unless " "they are last"); } if (instrIsNonCallControlFlow(op)) { FTRACE(1, "Bad block: {}\n", show(*this)); assertx(!"Block may not contain control flow instructions unless " "they are last"); } }
bool instrBreaksProfileBB(const NormalizedInstruction* inst) { if (instrIsNonCallControlFlow(inst->op()) || inst->op() == OpAwait || // may branch to scheduler and suspend execution inst->op() == OpClsCnsD) { // side exits if misses in the RDS return true; } // In profiling mode, don't trace through a control flow merge point, // however, allow inlining of default parameter funclets if (mcg->tx().profData()->anyBlockEndsAt(inst->func(), inst->offset()) && !inst->func()->isEntry(inst->nextSk().offset())) { return true; } return false; }
/* * Check invariants on a RegionDesc::Block. * * 1. Single entry, single exit (aside from exceptions). I.e. no * non-fallthrough instructions mid-block and no control flow (not * counting calls as control flow). * * 2. The type prediction list is ordered by increasing SrcKey. * * 3. Each prediction in the type prediction list is inside the range * of this block. * * 4. Each local id referred to in the type prediction list is valid. * * 5. (Unchecked) each stack offset in the type prediction list is * valid. */ void RegionDesc::Block::checkInvariants() const { smart::vector<SrcKey> keysInRange; keysInRange.reserve(length()); keysInRange.push_back(start()); for (int i = 1; i < length(); ++i) { if (i != length() - 1) { auto const pc = unit()->at(keysInRange.back().offset()); if (instrFlags(*pc) & TF) { FTRACE(1, "Bad block: {}\n", show(*this)); assert(!"Block may not contain non-fallthrough instruction unless " "they are last"); } if (instrIsNonCallControlFlow(*pc)) { FTRACE(1, "Bad block: {}\n", show(*this)); assert(!"Block may not contain control flow instructions unless " "they are last"); } } keysInRange.push_back(keysInRange.back().advanced(unit())); } assert(keysInRange.size() == length()); assert(std::is_sorted(m_predTypes.begin(), m_predTypes.end(), typePredListCmp)); assert(std::is_sorted(keysInRange.begin(), keysInRange.end())); auto rangeIt = keysInRange.begin(); for (auto& tpred : m_predTypes) { while (rangeIt != keysInRange.end() && *rangeIt < tpred.first) ++rangeIt; assert(rangeIt != keysInRange.end() && tpred.first == *rangeIt && "RegionDesc::Block contained an out-of-range prediction"); auto& loc = tpred.second.location; switch (loc.tag()) { case Location::Tag::Local: assert(loc.localId() < m_func->numLocals()); break; case Location::Tag::Stack: // Unchecked break; } } }
RegionDescPtr selectTraceletLegacy(const RegionContext& rCtx, const Transl::Tracelet& tlet) { typedef RegionDesc::Block Block; auto region = std::make_shared<RegionDesc>(); SrcKey sk(tlet.m_sk); auto unit = tlet.func()->unit(); const Func* topFunc = nullptr; Block* curBlock = nullptr; auto newBlock = [&](const Func* func, SrcKey start, Offset spOff) { assert(curBlock == nullptr || curBlock->length() > 0); region->blocks.push_back( std::make_shared<Block>(func, start.offset(), 0, spOff)); curBlock = region->blocks.back().get(); }; newBlock(tlet.func(), sk, rCtx.spOffset); for (auto ni = tlet.m_instrStream.first; ni; ni = ni->next) { assert(sk == ni->source); assert(ni->unit() == unit); Offset curSpOffset = rCtx.spOffset + ni->stackOffset; curBlock->addInstruction(); if ((curBlock->length() == 1 && ni->funcd != nullptr) || ni->funcd != topFunc) { topFunc = ni->funcd; curBlock->setKnownFunc(sk, topFunc); } if (ni->calleeTrace && !ni->calleeTrace->m_inliningFailed) { assert(ni->op() == OpFCall); assert(ni->funcd == ni->calleeTrace->func()); // This should be translated as an inlined call. Insert the blocks of the // callee in the region. auto const& callee = *ni->calleeTrace; curBlock->setInlinedCallee(ni->funcd); SrcKey cSk = callee.m_sk; Unit* cUnit = callee.func()->unit(); newBlock(callee.func(), cSk, curSpOffset); for (auto cni = callee.m_instrStream.first; cni; cni = cni->next) { assert(cSk == cni->source); assert(cni->op() == OpRetC || cni->op() == OpContRetC || cni->op() == OpNativeImpl || !instrIsNonCallControlFlow(cni->op())); curBlock->addInstruction(); cSk.advance(cUnit); } if (ni->next) { sk.advance(unit); newBlock(tlet.func(), sk, curSpOffset); } continue; } if (!ni->noOp && isFPassStar(ni->op())) { curBlock->setParamByRef(sk, ni->preppedByRef); } if (ni->next && ni->op() == OpJmp) { // A Jmp that isn't the final instruction in a Tracelet means we traced // through a forward jump in analyze. Update sk to point to the next NI // in the stream. auto dest = ni->offset() + ni->imm[0].u_BA; assert(dest > sk.offset()); // We only trace for forward Jmps for now. sk.setOffset(dest); // The Jmp terminates this block. newBlock(tlet.func(), sk, curSpOffset); } else { sk.advance(unit); } } auto& frontBlock = *region->blocks.front(); // Add tracelet guards as predictions on the first instruction. Predictions // and known types from static analysis will be applied by // Translator::translateRegion. for (auto const& dep : tlet.m_dependencies) { if (dep.second->rtt.isVagueValue() || dep.second->location.isThis()) continue; typedef RegionDesc R; auto addPred = [&](const R::Location& loc) { auto type = Type(dep.second->rtt); frontBlock.addPredicted(tlet.m_sk, {loc, type}); }; switch (dep.first.space) { case Transl::Location::Stack: { uint32_t offsetFromSp = uint32_t(-dep.first.offset - 1); uint32_t offsetFromFp = rCtx.spOffset - offsetFromSp; addPred(R::Location::Stack{offsetFromSp, offsetFromFp}); break; } case Transl::Location::Local: addPred(R::Location::Local{uint32_t(dep.first.offset)}); break; default: not_reached(); } } // Add reffiness dependencies as predictions on the first instruction. for (auto const& dep : tlet.m_refDeps.m_arMap) { RegionDesc::ReffinessPred pred{dep.second.m_mask, dep.second.m_vals, dep.first}; frontBlock.addReffinessPred(tlet.m_sk, pred); } FTRACE(2, "Converted Tracelet:\n{}\nInto RegionDesc:\n{}\n", tlet.toString(), show(*region)); return region; }