void optimize(IRUnit& unit, IRBuilder& irBuilder, TransKind kind) { Timer timer(Timer::optimize); assertx(checkEverything(unit)); auto const hasLoop = RuntimeOption::EvalJitLoops && cfgHasLoop(unit); auto const func = unit.entry()->front().marker().func(); auto const regionMode = pgoRegionMode(*func); auto const traceMode = kind != TransKind::Optimize || regionMode == PGORegionMode::Hottrace; // TODO (#5792564): Guard relaxation doesn't work with loops. // TODO (#6599498): Guard relaxation is broken in wholecfg mode. if (shouldHHIRRelaxGuards() && !hasLoop && traceMode) { Timer _t(Timer::optimize_relaxGuards); const bool simple = kind == TransKind::Profile && (RuntimeOption::EvalJitRegionSelector == "tracelet" || RuntimeOption::EvalJitRegionSelector == "method"); RelaxGuardsFlags flags = (RelaxGuardsFlags) (RelaxReflow | (simple ? RelaxSimple : RelaxNormal)); auto changed = relaxGuards(unit, *irBuilder.guards(), flags); if (changed) { printUnit(6, unit, "after guard relaxation"); mandatoryDCE(unit); // relaxGuards can leave unreachable preds. } if (RuntimeOption::EvalHHIRSimplification) { doPass(unit, simplifyPass, DCE::Minimal); doPass(unit, cleanCfg, DCE::None); } } fullDCE(unit); printUnit(6, unit, " after initial DCE "); assertx(checkEverything(unit)); if (RuntimeOption::EvalHHIRTypeCheckHoisting) { doPass(unit, hoistTypeChecks, DCE::None); } if (RuntimeOption::EvalHHIRPredictionOpts) { doPass(unit, optimizePredictions, DCE::None); } if (RuntimeOption::EvalHHIRSimplification) { doPass(unit, simplifyPass, DCE::Full); doPass(unit, cleanCfg, DCE::None); } if (RuntimeOption::EvalHHIRGlobalValueNumbering) { doPass(unit, gvn, DCE::Full); } if (kind != TransKind::Profile && RuntimeOption::EvalHHIRMemoryOpts) { doPass(unit, optimizeLoads, DCE::Full); } if (kind != TransKind::Profile && RuntimeOption::EvalHHIRMemoryOpts) { doPass(unit, optimizeStores, DCE::Full); } if (kind != TransKind::Profile && RuntimeOption::EvalHHIRRefcountOpts) { doPass(unit, optimizeRefcounts2, DCE::Full); } if (RuntimeOption::EvalHHIRLICM) { if (kind != TransKind::Profile && hasLoop) { // The clean pass is just to stress lack of pre_headers for now, since // LICM is a disabled prototype pass. doPass(unit, cleanCfg, DCE::None); doPass(unit, optimizeLoopInvariantCode, DCE::Minimal); } } doPass(unit, removeExitPlaceholders, DCE::Full); if (RuntimeOption::EvalHHIRGenerateAsserts) { doPass(unit, insertAsserts, DCE::None); } }
void optimize(IRUnit& unit, IRBuilder& irBuilder, TransKind kind) { Timer _t(Timer::optimize); auto const finishPass = [&] (const char* msg) { if (msg) { printUnit(6, unit, folly::format("after {}", msg).str().c_str()); } assertx(checkCfg(unit)); assertx(checkTmpsSpanningCalls(unit)); if (debug) { forEachInst(rpoSortCfg(unit), [&](IRInstruction* inst) { assertx(checkOperandTypes(inst, &unit)); }); } }; auto const doPass = [&] (void (*fn)(IRUnit&), const char* msg = nullptr) { fn(unit); finishPass(msg); }; auto const dce = [&] (const char* which) { if (!RuntimeOption::EvalHHIRDeadCodeElim) return; eliminateDeadCode(unit); finishPass(folly::format("{} DCE", which).str().c_str()); }; auto const simplifyPass = [] (IRUnit& unit) { boost::dynamic_bitset<> reachable(unit.numBlocks()); reachable.set(unit.entry()->id()); auto const blocks = rpoSortCfg(unit); for (auto block : blocks) { // Skip unreachable blocks, or simplify() cries. if (!reachable.test(block->id())) continue; for (auto& inst : *block) simplify(unit, &inst); if (auto const b = block->back().next()) reachable.set(b->id()); if (auto const b = block->back().taken()) reachable.set(b->id()); } }; auto const doSimplify = RuntimeOption::EvalHHIRExtraOptPass && RuntimeOption::EvalHHIRSimplification; auto const hasLoop = RuntimeOption::EvalJitLoops && cfgHasLoop(unit); auto const traceMode = kind != TransKind::Optimize || RuntimeOption::EvalJitPGORegionSelector == "hottrace"; // TODO (#5792564): Guard relaxation doesn't work with loops. // TODO (#6599498): Guard relaxation is broken in wholecfg mode. if (shouldHHIRRelaxGuards() && !hasLoop && traceMode) { Timer _t(Timer::optimize_relaxGuards); const bool simple = kind == TransKind::Profile && (RuntimeOption::EvalJitRegionSelector == "tracelet" || RuntimeOption::EvalJitRegionSelector == "method"); RelaxGuardsFlags flags = (RelaxGuardsFlags) (RelaxReflow | (simple ? RelaxSimple : RelaxNormal)); auto changed = relaxGuards(unit, *irBuilder.guards(), flags); if (changed) finishPass("guard relaxation"); if (doSimplify) { doPass(simplifyPass, "guard relaxation simplify"); } } // This is vestigial (it removes some instructions needed by the old refcount // opts pass), and will be removed soon. eliminateTakes(unit); dce("initial"); if (RuntimeOption::EvalHHIRPredictionOpts) { doPass(optimizePredictions, "prediction opts"); } if (doSimplify) { doPass(simplifyPass, "simplify"); dce("simplify"); } if (RuntimeOption::EvalHHIRGlobalValueNumbering) { doPass(gvn); dce("gvn"); } if (kind != TransKind::Profile && RuntimeOption::EvalHHIRMemoryOpts) { doPass(optimizeLoads); dce("loadelim"); } /* * Note: doing this pass this late might not be ideal, in particular because * we've already turned some StLoc instructions into StLocNT. * * But right now there are assumptions preventing us from doing it before * refcount opts. (Refcount opts needs to see all the StLocs explicitly * because it makes assumptions about whether references are consumed based * on that.) */ if (kind != TransKind::Profile && RuntimeOption::EvalHHIRMemoryOpts) { doPass(optimizeStores); dce("storeelim"); } if (kind != TransKind::Profile && RuntimeOption::EvalHHIRRefcountOpts) { doPass(optimizeRefcounts2); dce("refcount"); } if (RuntimeOption::EvalHHIRGenerateAsserts) { doPass(insertAsserts); } }
void optimize(IRUnit& unit, IRBuilder& irBuilder, TransKind kind) { Timer _t(Timer::optimize); auto finishPass = [&](const char* msg) { if (msg) { printUnit(6, unit, folly::format("after {}", msg).str().c_str()); } assert(checkCfg(unit)); assert(checkTmpsSpanningCalls(unit)); if (debug) { forEachInst(rpoSortCfg(unit), [&](IRInstruction* inst) { assert(checkOperandTypes(inst, &unit)); }); } }; auto doPass = [&](void (*fn)(IRUnit&), const char* msg = nullptr) { fn(unit); finishPass(msg); }; auto dce = [&](const char* which) { if (!RuntimeOption::EvalHHIRDeadCodeElim) return; eliminateDeadCode(unit); finishPass(folly::format("{} DCE", which).str().c_str()); }; auto const doReoptimize = RuntimeOption::EvalHHIRExtraOptPass && (RuntimeOption::EvalHHIRCse || RuntimeOption::EvalHHIRSimplification); auto const hasLoop = RuntimeOption::EvalJitLoops && cfgHasLoop(unit); // TODO(#5792564): Guard relaxation doesn't work with loops. if (shouldHHIRRelaxGuards() && !hasLoop) { Timer _t(Timer::optimize_relaxGuards); const bool simple = kind == TransKind::Profile && (RuntimeOption::EvalJitRegionSelector == "tracelet" || RuntimeOption::EvalJitRegionSelector == "method"); RelaxGuardsFlags flags = (RelaxGuardsFlags) (RelaxReflow | (simple ? RelaxSimple : RelaxNormal)); auto changed = relaxGuards(unit, *irBuilder.guards(), flags); if (changed) finishPass("guard relaxation"); if (doReoptimize) { irBuilder.reoptimize(); finishPass("guard relaxation reoptimize"); } } if (RuntimeOption::EvalHHIRRefcountOpts) { optimizeRefcounts(unit, FrameStateMgr{unit.entry()->front().marker()}); finishPass("refcount opts"); } dce("initial"); if (RuntimeOption::EvalHHIRPredictionOpts) { doPass(optimizePredictions, "prediction opts"); } if (doReoptimize) { irBuilder.reoptimize(); finishPass("reoptimize"); dce("reoptimize"); } if (RuntimeOption::EvalHHIRGlobalValueNumbering) { doPass(gvn); dce("gvn"); } if (kind != TransKind::Profile && RuntimeOption::EvalHHIRMemoryOpts) { doPass(optimizeLoads); dce("loadelim"); } /* * Note: doing this pass this late might not be ideal, in particular because * we've already turned some StLoc instructions into StLocNT. * * But right now there are assumptions preventing us from doing it before * refcount opts. (Refcount opts needs to see all the StLocs explicitly * because it makes assumptions about whether references are consumed based * on that.) */ if (kind != TransKind::Profile && RuntimeOption::EvalHHIRMemoryOpts) { doPass(optimizeStores); dce("storeelim"); } if (RuntimeOption::EvalHHIRGenerateAsserts) { doPass(insertAsserts, "RefCnt asserts"); } }