TCA genFuncBodyDispatch(Func* func, const DVFuncletsVec& dvs, CodeCache::View code) { auto context = prologue_context(kInvalidTransID, TransKind::Live, func, func->base()); IRUnit unit{context}; irgen::IRGS env{unit}; irgen::emitFuncBodyDispatch(env, dvs); irgen::sealUnit(env); CGMeta fixups; auto vunit = irlower::lowerUnit(env.unit, CodeKind::CrossTrace); auto& main = code.main(); auto const start = main.frontier(); emitVunit(*vunit, env.unit, code, fixups); if (RuntimeOption::EvalPerfRelocate) { GrowableVector<IncomingBranch> ibs; auto& frozen = code.frozen(); tc::recordPerfRelocMap(start, main.frontier(), frozen.frontier(), frozen.frontier(), context.srcKey(), 0, ibs, fixups); } fixups.process(nullptr); return start; }
static TransIDSet findPredTrans(TransID dstID, const ProfData* profData) { auto const dstRec = profData->transRec(dstID); auto const dstSK = dstRec->srcKey(); const SrcRec* dstSR = tc::findSrcRec(dstSK); assertx(dstSR); TransIDSet predSet; for (auto& inBr : dstSR->incomingBranches()) { auto const srcID = profData->jmpTransID(inBr.toSmash()); if (srcID == kInvalidTransID) continue; auto const srcRec = profData->transRec(srcID); if (!srcRec || !srcRec->isProfile()) continue; FTRACE(5, "findPredTrans: toSmash = {} srcID = {}\n", inBr.toSmash(), srcID); auto srcSuccOffsets = srcRec->lastSrcKey().succOffsets(); if (srcSuccOffsets.count(dstSK.offset())) { predSet.insert(srcID); } else { FTRACE(5, "findPredTrans: WARNING: incoming branch with impossible " "control flow between translations: {} -> {}" "(probably due to side exit)\n", srcID, dstID); } } return predSet; }
RegionDescPtr selectHotTrace(HotTransContext& ctx, TransIDSet& selectedSet, TransIDVec* selectedVec /* = nullptr */) { auto region = std::make_shared<RegionDesc>(); TransID tid = ctx.tid; TransID prevId = kInvalidTransID; selectedSet.clear(); if (selectedVec) selectedVec->clear(); TypedLocations accumPostConds; // Maps from BlockIds to accumulated post conditions for that block. // Used to determine if we can add branch-over edges by checking the // pre-conditions of the successor block. hphp_hash_map<RegionDesc::BlockId, TypedLocations> blockPostConds; auto numBCInstrs = ctx.maxBCInstrs; FTRACE(1, "selectHotTrace: starting with maxBCInstrs = {}\n", numBCInstrs); while (!selectedSet.count(tid)) { auto rec = ctx.profData->transRec(tid); auto blockRegion = rec->region(); if (blockRegion == nullptr) break; // Break if region would be larger than the specified limit. if (blockRegion->instrSize() > numBCInstrs) { FTRACE(2, "selectHotTrace: breaking region at Translation {} because " "size would exceed of maximum translation limit\n", tid); break; } // If the debugger is attached, only allow single-block regions. if (prevId != kInvalidTransID && isDebuggerAttachedProcess()) { FTRACE(2, "selectHotTrace: breaking region at Translation {} " "because of debugger is attached\n", tid); break; } // Break if block is not the first and it corresponds to the main // function body entry. This is to prevent creating multiple // large regions containing the function body (starting at various // DV funclets). if (prevId != kInvalidTransID) { auto const func = rec->func(); auto const bcOffset = rec->startBcOff(); if (func->base() == bcOffset) { FTRACE(2, "selectHotTrace: breaking region because reached the main " "function body entry at Translation {} (BC offset {})\n", tid, bcOffset); break; } } if (prevId != kInvalidTransID) { auto sk = rec->srcKey(); if (ctx.profData->optimized(sk)) { FTRACE(2, "selectHotTrace: breaking region because next sk already " "optimized, for Translation {}\n", tid); break; } } bool hasPredBlock = !region->empty(); RegionDesc::BlockId predBlockId = (hasPredBlock ? region->blocks().back().get()->id() : 0); auto const& newFirstBlock = blockRegion->entry(); auto newFirstBlockId = newFirstBlock->id(); // Add blockRegion's blocks and arcs to region. region->append(*blockRegion); numBCInstrs -= blockRegion->instrSize(); assertx(numBCInstrs >= 0); if (hasPredBlock) { region->addArc(predBlockId, newFirstBlockId); } selectedSet.insert(tid); if (selectedVec) selectedVec->push_back(tid); const auto lastSk = rec->lastSrcKey(); if (breaksRegion(lastSk)) { FTRACE(2, "selectHotTrace: breaking region because of last instruction " "in Translation {}: {}\n", tid, opcodeToName(lastSk.op())); break; } auto outArcs = ctx.cfg->outArcs(tid); if (outArcs.size() == 0) { FTRACE(2, "selectHotTrace: breaking region because there's no successor " "for Translation {}\n", tid); break; } auto newLastBlock = blockRegion->blocks().back(); discardPoppedTypes(accumPostConds, blockRegion->entry()->initialSpOffset()); mergePostConds(accumPostConds, newLastBlock->postConds()); blockPostConds[newLastBlock->id()] = accumPostConds; TransCFG::ArcPtrVec possibleOutArcs; for (auto arc : outArcs) { auto dstRec = ctx.profData->transRec(arc->dst()); auto possibleNext = dstRec->region()->entry(); if (preCondsAreSatisfied(possibleNext, accumPostConds)) { possibleOutArcs.emplace_back(arc); } } if (possibleOutArcs.size() == 0) { FTRACE(2, "selectHotTrace: breaking region because postcondition check " "pruned all successors of Translation {}\n", tid); break; } auto maxWeight = std::numeric_limits<int64_t>::min(); TransCFG::Arc* maxArc = nullptr; for (auto arc : possibleOutArcs) { if (arc->weight() >= maxWeight) { maxWeight = arc->weight(); maxArc = arc; } } assertx(maxArc != nullptr); prevId = tid; tid = maxArc->dst(); } FTRACE(3, "selectHotTrace: before chainRetransBlocks:\n{}\n", show(*region)); region->chainRetransBlocks(); FTRACE(3, "selectHotTrace: after chainRetransBlocks:\n{}\n", show(*region)); return region; }
TransCFG::TransCFG(FuncId funcId, const ProfData* profData, bool inlining /* = false */) { assertx(profData); // add nodes for (auto const tid : profData->funcProfTransIDs(funcId)) { auto const rec = profData->transRec(tid); assertx(rec->region() != nullptr); // This will skip DV Funclets if they were already // retranslated w/ the prologues: if (inlining || !profData->optimized(rec->srcKey())) { int64_t weight = profData->transCounter(tid); addNode(tid, weight); } } // add arcs for (auto const dstId : nodes()) { auto const rec = profData->transRec(dstId); auto const dstSK = rec->srcKey(); auto const dstBlock = rec->region()->entry(); FTRACE(5, "TransCFG: adding incoming arcs in dstId = {}\n", dstId); TransIDSet predIDs = findPredTrans(dstId, profData); for (auto predId : predIDs) { if (hasNode(predId)) { auto const predRec = profData->transRec(predId); auto const predBlock = predRec->region()->blocks().back(); auto const& postConds = predBlock->postConds(); auto predPostConds = postConds.changed; predPostConds.insert(predPostConds.end(), postConds.refined.begin(), postConds.refined.end()); auto const predSK = predRec->srcKey(); if (preCondsAreSatisfied(dstBlock, predPostConds) && predSK.resumed() == dstSK.resumed()) { FTRACE(5, "TransCFG: adding arc {} -> {} ({} -> {})\n", predId, dstId, showShort(predSK), showShort(dstSK)); addArc(predId, dstId, TransCFG::Arc::kUnknownWeight); } } } } // infer arc weights bool changed; do { changed = false; for (auto const tid : nodes()) { auto const nodeWeight = weight(tid); if (inferredArcWeight(inArcs(tid), nodeWeight)) changed = true; if (inferredArcWeight(outArcs(tid), nodeWeight)) changed = true; } } while (changed); // guess weight for non-inferred arcs for (auto const tid : nodes()) { for (auto arc : outArcs(tid)) { if (arc->weight() == Arc::kUnknownWeight) { arc->setGuessed(); auto arcWgt = std::min(weight(arc->src()), weight(arc->dst())) / 2; arc->setWeight(arcWgt); } } } }