RegionDescPtr selectHotRegion(TransID transId, MCGenerator* mcg) { auto const profData = jit::profData(); assertx(profData); auto const& func = *profData->transRec(transId)->func(); FuncId funcId = func.getFuncId(); TransCFG cfg(funcId, profData, mcg->srcDB()); assertx(regionMode() != RegionMode::Method); RegionDescPtr region; HotTransContext ctx; ctx.cfg = &cfg; ctx.profData = profData; ctx.tid = transId; ctx.maxBCInstrs = RuntimeOption::EvalJitMaxRegionInstrs; switch (pgoRegionMode(func)) { case PGORegionMode::Hottrace: region = selectHotTrace(ctx); break; case PGORegionMode::Hotblock: region = selectHotBlock(transId, profData, cfg); break; case PGORegionMode::WholeCFG: case PGORegionMode::HotCFG: region = selectHotCFG(ctx); break; } assertx(region); if (Trace::moduleEnabled(HPHP::Trace::pgo, 5)) { std::string dotFileName = std::string("/tmp/trans-cfg-") + folly::to<std::string>(transId) + ".dot"; std::ofstream outFile(dotFileName); if (outFile.is_open()) { cfg.print(outFile, funcId, profData); outFile.close(); } FTRACE(5, "selectHotRegion: New Translation (file: {}) {}\n", dotFileName, region ? show(*region) : std::string("empty region")); } always_assert(region->instrSize() <= RuntimeOption::EvalJitMaxRegionInstrs); if (region->empty()) return nullptr; return region; }
RegionDescPtr selectHotRegion(TransID transId, MCGenerator* mcg) { assertx(RuntimeOption::EvalJitPGO); const ProfData* profData = mcg->tx().profData(); auto const& func = *(profData->transFunc(transId)); FuncId funcId = func.getFuncId(); TransCFG cfg(funcId, profData, mcg->tx().getSrcDB(), mcg->getJmpToTransIDMap()); TransIDSet selectedTIDs; assertx(regionMode() != RegionMode::Method); RegionDescPtr region; HotTransContext ctx; ctx.cfg = &cfg; ctx.profData = profData; ctx.tid = transId; ctx.maxBCInstrs = RuntimeOption::EvalJitMaxRegionInstrs; switch (pgoRegionMode(func)) { case PGORegionMode::Hottrace: region = selectHotTrace(ctx, selectedTIDs); break; case PGORegionMode::Hotblock: region = selectHotBlock(transId, profData, cfg); break; case PGORegionMode::WholeCFG: case PGORegionMode::HotCFG: region = selectHotCFG(ctx, selectedTIDs); break; } assertx(region); if (Trace::moduleEnabled(HPHP::Trace::pgo, 5)) { std::string dotFileName = std::string("/tmp/trans-cfg-") + folly::to<std::string>(transId) + ".dot"; cfg.print(dotFileName, funcId, profData, &selectedTIDs); FTRACE(5, "selectHotRegion: New Translation {} (file: {}) {}\n", mcg->tx().profData()->curTransID(), dotFileName, region ? show(*region) : std::string("empty region")); } always_assert(region->instrSize() <= RuntimeOption::EvalJitMaxRegionInstrs); return region; }
RegionDescPtr selectHotTrace(TransID triggerId, const ProfData* profData, TransCFG& cfg, TransIDSet& selectedSet, TransIDVec* selectedVec) { auto region = std::make_shared<RegionDesc>(); TransID tid = triggerId; TransID prevId = kInvalidTransID; selectedSet.clear(); if (selectedVec) selectedVec->clear(); PostConditions 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, PostConditions> blockPostConds; uint32_t numBCInstrs = 0; while (!selectedSet.count(tid)) { RegionDescPtr blockRegion = profData->transRegion(tid); if (blockRegion == nullptr) break; // Break if region would be larger than the specified limit. auto newInstrSize = numBCInstrs + blockRegion->instrSize(); if (newInstrSize > RuntimeOption::EvalJitMaxRegionInstrs) { FTRACE(2, "selectHotTrace: breaking region at Translation {} because " "size ({}) would exceed of maximum translation limit\n", tid, newInstrSize); 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) { const Func* func = profData->transFunc(tid); Offset bcOffset = profData->transStartBcOff(tid); 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 = profData->transSrcKey(tid); if (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(); auto newLastBlockId = blockRegion->blocks().back()->id(); // Add blockRegion's blocks and arcs to region. region->append(*blockRegion); numBCInstrs += blockRegion->instrSize(); if (hasPredBlock) { region->addArc(predBlockId, newFirstBlockId); } // When Eval.JitLoops is set, insert back-edges in the region if // they exist in the TransCFG. if (RuntimeOption::EvalJitLoops) { assertx(hasTransId(newFirstBlockId)); auto newTransId = getTransId(newFirstBlockId); // Don't add the arc if the last opcode in the source block ends // the region. if (!breaksRegion(*profData->transLastInstr(newTransId))) { auto& blocks = region->blocks(); for (auto iOther = 0; iOther < blocks.size(); iOther++) { auto other = blocks[iOther]; auto otherFirstBlockId = other.get()->id(); if (!hasTransId(otherFirstBlockId)) continue; auto otherTransId = getTransId(otherFirstBlockId); if (cfg.hasArc(newTransId, otherTransId)) { region->addArc(newLastBlockId, otherFirstBlockId); } } } } if (cfg.outArcs(tid).size() > 1) { region->setSideExitingBlock(blockRegion->entry()->id()); } selectedSet.insert(tid); if (selectedVec) selectedVec->push_back(tid); Op lastOp = *(profData->transLastInstr(tid)); if (breaksRegion(lastOp)) { FTRACE(2, "selectHotTrace: breaking region because of last instruction " "in Translation {}: {}\n", tid, opcodeToName(lastOp)); break; } auto outArcs = 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) { RegionDesc::BlockPtr possibleNext = profData->transRegion(arc->dst())->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; }
RegionDescPtr selectHotTrace(TransID triggerId, const ProfData* profData, TransCFG& cfg, TransIDSet& selectedSet, TransIDVec* selectedVec) { auto region = std::make_shared<RegionDesc>(); TransID tid = triggerId; TransID prevId = kInvalidTransID; selectedSet.clear(); if (selectedVec) selectedVec->clear(); PostConditions accumPostConds; // Maps BlockIds to the set of BC offsets for its successor blocks. // Used to prevent multiple successors with the same SrcKey for now. // This can go away once task #4157613 is done. hphp_hash_map<RegionDesc::BlockId, SrcKeySet> succSKSet; // 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, PostConditions> blockPostConds; uint32_t numBCInstrs = 0; while (!selectedSet.count(tid)) { RegionDescPtr blockRegion = profData->transRegion(tid); if (blockRegion == nullptr) break; // Break if region would be larger than the specified limit. auto newInstrSize = numBCInstrs + blockRegion->instrSize(); if (newInstrSize > RuntimeOption::EvalJitMaxRegionInstrs) { FTRACE(2, "selectHotTrace: breaking region at Translation {} because " "size ({}) would exceed of maximum translation limit\n", tid, newInstrSize); 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) { const Func* func = profData->transFunc(tid); Offset bcOffset = profData->transStartBcOff(tid); 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 = profData->transSrcKey(tid); if (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(); auto newFirstBlockSk = newFirstBlock->start(); auto newLastBlockId = blockRegion->blocks().back()->id(); // Make sure we don't end up with multiple successors for the same // SrcKey. Task #4157613 will allow the following check to go away. // This needs to be done before we insert blockRegion into region, // to avoid creating unreachable blocks. if (RuntimeOption::EvalHHIRBytecodeControlFlow && hasPredBlock && succSKSet[predBlockId].count(newFirstBlockSk)) { break; } // Add blockRegion's blocks and arcs to region. region->append(*blockRegion); numBCInstrs += blockRegion->instrSize(); if (hasPredBlock) { if (RuntimeOption::EvalHHIRBytecodeControlFlow) { // This is checked above. assert(succSKSet[predBlockId].count(newFirstBlockSk) == 0); succSKSet[predBlockId].insert(newFirstBlockSk); } region->addArc(predBlockId, newFirstBlockId); } // With bytecode control-flow, we add all forward arcs in the TransCFG // that are induced by the blocks in the region, as a simple way // to expose control-flow for now. // This can go away once Task #4075822 is done. if (RuntimeOption::EvalHHIRBytecodeControlFlow) { assert(hasTransId(newFirstBlockId)); auto newTransId = getTransId(newFirstBlockId); auto& blocks = region->blocks(); for (auto iOther = 0; iOther < blocks.size(); iOther++) { auto other = blocks[iOther]; auto otherFirstBlockId = other.get()->id(); if (!hasTransId(otherFirstBlockId)) continue; auto otherTransId = getTransId(otherFirstBlockId); auto otherFirstBlockSk = other.get()->start(); auto otherRegion = profData->transRegion(otherTransId); auto otherLastBlockId = otherRegion->blocks().back()->id(); // When loops are off, stop once we hit the newTransId we just inserted. if (!RuntimeOption::EvalJitLoops && otherTransId == newTransId) break; if (cfg.hasArc(otherTransId, newTransId) && // Task #4157613 will allow the following check to go away !succSKSet[otherLastBlockId].count(newFirstBlockSk) && preCondsAreSatisfied(newFirstBlock, blockPostConds[otherLastBlockId])) { region->addArc(otherLastBlockId, newFirstBlockId); succSKSet[otherLastBlockId].insert(newFirstBlockSk); } // When Eval.JitLoops is set, insert back-edges in the // region if they exist in the TransCFG. if (RuntimeOption::EvalJitLoops && cfg.hasArc(newTransId, otherTransId) && // Task #4157613 will allow the following check to go away !succSKSet[newLastBlockId].count(otherFirstBlockSk)) { region->addArc(newLastBlockId, otherFirstBlockId); succSKSet[newLastBlockId].insert(otherFirstBlockSk); } } } if (cfg.outArcs(tid).size() > 1) { region->setSideExitingBlock(blockRegion->entry()->id()); } selectedSet.insert(tid); if (selectedVec) selectedVec->push_back(tid); Op lastOp = *(profData->transLastInstr(tid)); if (breaksRegion(lastOp)) { FTRACE(2, "selectHotTrace: breaking region because of last instruction " "in Translation {}: {}\n", tid, opcodeToName(lastOp)); break; } auto outArcs = 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) { RegionDesc::BlockPtr possibleNext = profData->transRegion(arc->dst())->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; } } assert(maxArc != nullptr); prevId = tid; tid = maxArc->dst(); } return region; }