RegionDescPtr selectRegion(const RegionContext& context, TransKind kind) { auto const mode = regionMode(); FTRACE(1, "Select region: mode={} context:\n{}", static_cast<int>(mode), show(context) ); auto region = [&]{ try { switch (mode) { case RegionMode::None: return RegionDescPtr{nullptr}; case RegionMode::Method: return selectMethod(context); case RegionMode::Tracelet: return selectTracelet(context, kind == TransKind::Profile); } not_reached(); } catch (const std::exception& e) { FTRACE(1, "region selector threw: {}\n", e.what()); return RegionDescPtr{nullptr}; } }(); if (region) { FTRACE(3, "{}", show(*region)); always_assert(region->instrSize() <= RuntimeOption::EvalJitMaxRegionInstrs); } else { FTRACE(1, "no region selectable; using tracelet compiler\n"); } return region; }
void visit(TransID tid) { auto tidRegion = m_profData->transRegion(tid); auto tidInstrs = tidRegion->instrSize(); if (m_numBCInstrs + tidInstrs > RuntimeOption::EvalJitMaxRegionInstrs) { return; } if (m_visited.count(tid)) return; m_visited.insert(tid); m_visiting.insert(tid); if (!breaksRegion(*(m_profData->transLastInstr(tid)))) { auto srcBlockId = tidRegion->blocks().back().get()->id(); for (auto const arc : m_cfg.outArcs(tid)) { auto dst = arc->dst(); // If dst is in the visiting set then this arc forms a cycle. Don't // include it unless we've asked for loops. if (!RuntimeOption::EvalJitLoops && m_visiting.count(dst)) continue; // Skip dst if we already generated a region starting at that SrcKey. auto dstSK = m_profData->transSrcKey(dst); if (m_profData->optimized(dstSK)) continue; auto dstBlockId = m_profData->transRegion(dst)->entry()->id(); m_arcs.push_back({srcBlockId, dstBlockId}); visit(dst); } } // Now insert the region for tid in the front of m_region. We do // this last so that the region ends up in (quasi-)topological order // (it'll be in topological order for acyclic regions). m_region->prepend(*tidRegion); m_selectedSet.insert(tid); if (m_selectedVec) m_selectedVec->push_back(tid); m_numBCInstrs += tidRegion->instrSize(); always_assert(m_numBCInstrs <= RuntimeOption::EvalJitMaxRegionInstrs); m_visiting.erase(tid); }
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; }
bool RegionFormer::tryInline(uint32_t& instrSize) { assertx(m_inst.source == m_sk); assertx(m_inst.func() == curFunc()); assertx(m_sk.resumed() == resumed()); instrSize = 0; if (!m_inl.canInlineAt(m_inst.source, m_inst.funcd, *m_region)) { return false; } auto refuse = [this](const std::string& str) { FTRACE(2, "selectTracelet not inlining {}: {}\n", m_inst.toString(), str); return false; }; auto callee = m_inst.funcd; // Make sure the FPushOp wasn't interpreted. if (m_irgs.fpiStack.empty()) { return refuse("fpistack empty; fpush was in a different region"); } auto spillFrame = m_irgs.fpiStack.top().spillFrame; if (!spillFrame) { return refuse("couldn't find SpillFrame for FPushOp"); } auto numArgs = m_inst.imm[0].u_IVA; auto numParams = callee->numParams(); // Set up the region context, mapping stack slots in the caller to locals in // the callee. RegionContext ctx; ctx.func = callee; ctx.bcOffset = callee->getEntryForNumArgs(numArgs); ctx.spOffset = FPInvOffset{safe_cast<int32_t>(callee->numSlotsInFrame())}; ctx.resumed = false; for (int i = 0; i < numArgs; ++i) { auto type = irgen::publicTopType(m_irgs, BCSPOffset{i}); uint32_t paramIdx = numArgs - 1 - i; ctx.liveTypes.push_back({RegionDesc::Location::Local{paramIdx}, type}); } for (unsigned i = numArgs; i < numParams; ++i) { // These locals will be populated by DV init funclets but they'll start // out as Uninit. ctx.liveTypes.push_back({RegionDesc::Location::Local{i}, TUninit}); } FTRACE(1, "selectTracelet analyzing callee {} with context:\n{}", callee->fullName()->data(), show(ctx)); auto region = selectTracelet(ctx, m_profiling, false /* noinline */); if (!region) { return refuse("failed to select region in callee"); } instrSize = region->instrSize(); auto newInstrSize = instrSize + m_numBCInstrs + m_pendingInlinedInstrs; if (newInstrSize > RuntimeOption::EvalJitMaxRegionInstrs) { return refuse("new region would be too large"); } if (!m_inl.shouldInline(callee, *region)) { return refuse("shouldIRInline failed"); } return true; }