[llvm] AMDGPU: Add NextUseAnalysis Pass (PR #178873)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 10 10:15:07 PDT 2026
================
@@ -0,0 +1,2614 @@
+//===---------------------- AMDGPUNextUseAnalysis.cpp ---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the AMDGPUNextUseAnalysis pass, a machine-level analysis
+// that computes the distance from each instruction to the "nearest" next use of
+// every live virtual register. These distances guide register spilling
+// decisions by identifying which live values are furthest from their next use
+// and are therefore the best candidates to spill.
+//
+// The analysis is based on the Braun & Hack CC'09 paper "Register Spilling and
+// Live-Range Splitting for SSA-Form Programs."
+//
+// Key concepts:
+//
+// NextUseDistance A loop-depth-weighted instruction count representing
+// how far away a register's next use is. Distances
+// through deeper loops are scaled by fromLoopDepth() so
+// that uses inside hot loops appear closer.
+//
+// Inter-block Pre-computed shortest weighted distances between all
+// distances pairs of basic blocks, used to efficiently answer
+// cross-block next-use queries. Each intermediate block
+// is weighted by fromLoopDepth() applied once per loop
+// boundary crossing relative to the destination.
+//
+// Configuration flags (see Config struct in the header):
+//
+// CountPhis Count PHI instructions toward distance and block size.
+// ForwardOnly Restrict inter-block distances to forward-reachable
+// paths.
+// PreciseUseModeling Model PHI uses at their incoming edge block and filter
+// uses with intermediate redefinitions.
+// PromoteToPreheader Route loop-entry and inner-loop uses to the preheader.
+//
+// This file contains:
+//
+// - Command-line options for configuration and debug output
+// - LiveRegUse / JSON helpers
+// - AMDGPUNextUseAnalysisImpl (the main analysis implementation)
+// - Instruction ID assignment and block size computation
+// - CFG path pre-computation (reachability, loop depth, back-edges)
+// - Inter-block distance computation
+// - Per-register next-use distance queries and caching
+// - AMDGPUNextUseAnalysis (public facade, pimpl)
+// - Legacy and new pass manager wrappers
+//
+//===----------------------------------------------------------------------===//
+
+#include "AMDGPUNextUseAnalysis.h"
+#include "AMDGPU.h"
+#include "GCNRegPressure.h"
+#include "GCNSubtarget.h"
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineLoopInfo.h"
+#include "llvm/IR/ModuleSlotTracker.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "amdgpu-next-use-analysis"
+
+//==============================================================================
+// Options etc
+//==============================================================================
+namespace {
+
+cl::opt<bool> DumpNextUseDistance("amdgpu-next-use-analysis-dump-distance",
+ cl::init(false), cl::Hidden);
+
+cl::opt<bool>
+ DistanceCacheEnabled("amdgpu-next-use-analysis-distance-cache",
+ cl::init(true), cl::Hidden,
+ cl::desc("Enable live-reg-use distance cache"));
+
+cl::opt<std::string>
+ DumpNextUseDistanceAsJson("amdgpu-next-use-analysis-dump-distance-as-json",
+ cl::Hidden);
+cl::opt<bool>
+ DumpNextUseDistanceVerbose("amdgpu-next-use-analysis-dump-distance-verbose",
+ cl::init(false), cl::Hidden);
+
+// 'graphics' and 'compute' modes arose due to initial competing implementations
+// of next-use analysis that emphasized different types of workloads. This
+// implementation is a compromise that combines aspects of both. Over time, the
+// hope is we will be able to remove some of these differences and settle on a
+// more unified implementation.
+cl::opt<std::string>
+ ConfigPresetOpt("amdgpu-next-use-analysis-config", cl::Hidden,
+ cl::init("graphics"),
+ cl::desc("Config preset: 'graphics' or 'compute'"));
+
+cl::opt<bool> ConfigCountPhisOpt(
+ "amdgpu-next-use-analysis-count-phis", cl::Hidden,
+ cl::desc("Count PHI instructions toward distance and block size"));
+cl::opt<bool> ConfigForwardOnlyOpt(
+ "amdgpu-next-use-analysis-forward-only", cl::Hidden,
+ cl::desc("Restrict inter-block distances to forward-reachable paths"));
+cl::opt<bool> ConfigPreciseUseModelingOpt(
+ "amdgpu-next-use-analysis-precise-use-modeling", cl::Hidden,
+ cl::desc("Model PHI uses via incoming edge block with loop-aware "
+ "reachability filtering"));
+cl::opt<bool> ConfigPromoteToPreheaderOpt(
+ "amdgpu-next-use-analysis-use-preheader-model", cl::Hidden,
+ cl::desc("Promote loop-entry and inner-loop uses to the loop preheader"));
+} // namespace
+
+//==============================================================================
+// LiveRegUse - Represents a live register use with its distance. Used for
+// tracking and sorting register uses by distance.
+//==============================================================================
+namespace {
+using UseDistancePair = AMDGPUNextUseAnalysis::UseDistancePair;
+struct LiveRegUse : public UseDistancePair {
+ // 'nullptr' indicates an unset/invalid state.
+ LiveRegUse() : UseDistancePair(nullptr, 0) {}
+ LiveRegUse(const MachineOperand *Use, NextUseDistance Dist)
+ : UseDistancePair(Use, Dist) {}
+ LiveRegUse(const UseDistancePair &P) : UseDistancePair(P) {}
+
+ bool isUnset() const { return Use == nullptr; }
+
+ Register getReg() const { return Use->getReg(); }
+ unsigned getSubReg() const { return Use->getSubReg(); }
+ LaneBitmask getLaneMask(const SIRegisterInfo *TRI) const {
+ return TRI->getSubRegIndexLaneMask(Use->getSubReg());
+ }
+
+ bool isCloserThan(const LiveRegUse &X) const {
+ if (Dist < X.Dist)
+ return true;
+
+ if (Dist > X.Dist)
+ return false;
+
+ if (Use == X.Use)
+ return false;
+
+ // Ugh. When !CountPhis, PHIs and the first non-PHI instruction have id
+ // 0. In this case, consider PHIs as less than the first non-PHI
+ // instruction.
+ const MachineInstr *ThisMI = Use->getParent();
+ const MachineInstr *XMI = X.Use->getParent();
+ const MachineBasicBlock *ThisMBB = ThisMI->getParent();
+ if (ThisMBB == XMI->getParent()) {
+ if (ThisMI->isPHI() && !XMI->isPHI() &&
+ XMI == &(*ThisMBB->getFirstNonPHI()))
+ return true;
+ }
+
+ // Ensure deterministic results
+ return X.getReg() < getReg();
+ }
+
+ void print(raw_ostream &OS) const {
+ if (isUnset()) {
+ OS << "<unset>";
+ return;
+ }
+ Dist.print(OS);
+ OS << " [" << printReg(getReg());
+ if (getSubReg())
+ OS << ":" << getSubReg();
+ OS << "]";
+ }
+
+ LLVM_DUMP_METHOD void dump() const {
+ print(dbgs());
+ dbgs() << '\n';
+ }
+};
+
+inline bool updateClosest(LiveRegUse &Closest, const LiveRegUse &X) {
+ if (!Closest.Use || X.isCloserThan(Closest)) {
+ Closest = X;
+ return true;
+ }
+ return false;
+}
+
+inline bool updateFurthest(LiveRegUse &Furthest, const LiveRegUse &X) {
+ if (!Furthest.Use || Furthest.isCloserThan(X)) {
+ Furthest = X;
+ return true;
+ }
+ return false;
+}
+} // namespace
+
+//==============================================================================
+// JSON helpers
+//==============================================================================
+namespace {
+template <typename Lambda>
+void printStringAttr(json::OStream &J, const char *Name, Lambda L) {
+ J.attributeBegin(Name);
+ raw_ostream &OS = J.rawValueBegin();
+ OS << '"';
+ L(OS);
+ OS << '"';
+ J.rawValueEnd();
+ J.attributeEnd();
+}
+void printStringAttr(json::OStream &J, const char *Name, Printable P) {
+ printStringAttr(J, Name, [&](raw_ostream &OS) { OS << P; });
+}
+
+void printStringAttr(json::OStream &J, const char *Name, const MachineInstr &MI,
+ ModuleSlotTracker &MST) {
+ printStringAttr(J, Name, [&](raw_ostream &OS) {
+ MI.print(OS, MST,
+ /* IsStandalone */ false,
+ /* SkipOpers */ false,
+ /* SkipDebugLoc */ false,
+ /* AddNewLine ---> */ false,
+ /* TargetInstrInfo */ nullptr);
+ });
+}
+
+void printMBBNameAttr(json::OStream &J, const char *Name,
+ const MachineBasicBlock &MBB, ModuleSlotTracker &MST) {
+ printStringAttr(J, Name, [&](raw_ostream &OS) {
+ MBB.printName(OS, MachineBasicBlock::PrintNameIr, &MST);
+ });
+}
+
+template <typename NameLambda, typename ValueT>
+void printAttr(json::OStream &J, NameLambda NL, ValueT V) {
+ std::string Name;
+ raw_string_ostream NameOS(Name);
+ NL(NameOS);
+ J.attribute(NameOS.str(), V);
+}
+
+template <typename ValueT>
+void printAttr(json::OStream &J, const Printable &P, ValueT V) {
+ printAttr(J, [&](raw_ostream &OS) { OS << P; }, V);
+}
+
+} // namespace
+
+//==============================================================================
+// AMDGPUNextUseAnalysisImpl
+//==============================================================================
+class llvm::AMDGPUNextUseAnalysisImpl {
+public:
+ struct CacheableNextUseDistance {
+ bool IsInstrRelative;
+ NextUseDistance Distance;
+ };
+ static constexpr bool InstrRelative = true;
+ static constexpr bool InstrInvariant = false;
+
+private:
+ const MachineFunction *MF = nullptr;
+ const SIRegisterInfo *TRI = nullptr;
+ const SIInstrInfo *TII = nullptr;
+ const MachineLoopInfo *MLI = nullptr;
+ const MachineRegisterInfo *MRI = nullptr;
+
+ using InstrIdTy = unsigned;
+ using InstrToIdMap = DenseMap<const MachineInstr *, InstrIdTy>;
+ InstrToIdMap InstrToId;
+ AMDGPUNextUseAnalysis::Config Cfg;
+
+ void initializeTables() {
+ for (const MachineBasicBlock &BB : *MF)
+ calcInstrIds(&BB, InstrToId);
+ initializeCfgPaths();
+ initializeInterBlockDistances();
+ }
+
+ void clearTables() {
+ InstrToId.clear();
+ RegUseMap.clear();
+ Paths.clear();
+
+ resetDistanceCache();
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Instruction Ids
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ unsigned sizeOf(const MachineInstr &MI) const {
+ // When !Cfg.CountPhis, PHIs do not contribute to distances/sizes since they
+ // generally don't result in the generation of a machine instruction.
+ // FIXME: Consider using MI.isPseudo()
+ return Cfg.CountPhis ? 1 : !MI.isPHI();
+ }
+
+ void calcInstrIds(const MachineBasicBlock *BB,
+ InstrToIdMap &MutableInstrToId) const {
+ InstrIdTy Id = 0;
+ for (auto &MI : BB->instrs()) {
+ MutableInstrToId[&MI] = Id;
+ Id += sizeOf(MI);
+ }
+ }
+
+ /// Returns MI's instruction Id. It renumbers (part of) the BB if MI is not
+ /// found in the map.
+ InstrIdTy getInstrId(const MachineInstr *MI) const {
+ auto It = InstrToId.find(MI);
+ if (It != InstrToId.end())
+ return It->second;
+
+ // Renumber the MBB.
+ // TODO: Renumber from MI onwards.
+ auto &MutableInstrToId = const_cast<InstrToIdMap &>(InstrToId);
+ calcInstrIds(MI->getParent(), MutableInstrToId);
+ return InstrToId.find(MI)->second;
+ }
+
+ // Length of the segment from MI (inclusive) to the first instruction of the
+ // basic block.
+ InstrIdTy getHeadLen(const MachineInstr *MI) const {
+ const MachineBasicBlock *MBB = MI->getParent();
+ return getInstrId(MI) + getInstrId(&MBB->instr_front()) + 1;
+ }
+
+ // Length of the segment from MI (exclusive) to the last instruction of the
+ // basic block.
+ InstrIdTy getTailLen(const MachineInstr *MI) const {
+ const MachineBasicBlock *MBB = MI->getParent();
+ return getInstrId(&MBB->instr_back()) - getInstrId(MI);
+ }
+
+ // Length of the segment from 'From' to 'To' (exclusive). Both instructions
+ // must be in the same basic block.
+ InstrIdTy getDistance(const MachineInstr *From,
+ const MachineInstr *To) const {
+ assert(From->getParent() == To->getParent());
+ return getInstrId(To) - getInstrId(From);
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // RegUses - cache of uses by register
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ DenseMap<Register, SmallVector<const MachineOperand *>> RegUseMap;
+
+ const SmallVector<const MachineOperand *> &
+ getRegisterUses(Register Reg) const {
+ auto I = RegUseMap.find(Reg);
+ if (I != RegUseMap.end())
+ return I->second;
+
+ auto *NonConstThis = const_cast<AMDGPUNextUseAnalysisImpl *>(this);
+ SmallVector<const MachineOperand *> &Uses = NonConstThis->RegUseMap[Reg];
+ for (const MachineOperand &UseMO : MRI->use_nodbg_operands(Reg)) {
+ if (!UseMO.isUndef())
+ Uses.push_back(&UseMO);
+ }
+ return Uses;
+ }
+
+ bool hasAtLeastOneUse(Register Reg) const {
+ return !getRegisterUses(Reg).empty();
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Paths
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ class Path
+ : public std::pair<const MachineBasicBlock *, const MachineBasicBlock *> {
+ public:
+ using Base =
+ std::pair<const MachineBasicBlock *, const MachineBasicBlock *>;
+ using Base::pair;
+ Path(const Base &Pair) : Base(Pair) {}
+
+ const MachineBasicBlock *src() const { return first; }
+ const MachineBasicBlock *dst() const { return second; }
+
+ using DenseMapInfo = llvm::DenseMapInfo<Base>;
+ };
+
+ enum EdgeKind { Back = -1, None = 0, Forward = 1 };
+ struct PathInfo {
+ EdgeKind EK;
+ bool Reachable;
+ int ForwardReachable;
+ unsigned RelativeLoopDepth;
+ std::optional<NextUseDistance> ShortestDistance;
+ std::optional<NextUseDistance> ShortestUnweightedDistance;
+ InstrIdTy Size;
+
+ PathInfo()
+ : EK(None), Reachable(false), ForwardReachable(-1),
+ RelativeLoopDepth(0), Size(0) {}
+
+ bool isBackedge() const { return EK == EdgeKind::Back; }
+
+ bool isForwardReachableSet() const { return 0 <= ForwardReachable; }
+ bool isForwardReachableUnset() const { return ForwardReachable < 0; }
+ bool isForwardReachable() const { return ForwardReachable == 1; }
+ bool isNotForwardReachable() const { return ForwardReachable == 0; }
+
+ void print(raw_ostream &OS) const {
+ const char *EKStr = EK == Back ? "back" : EK == Forward ? "fwd" : "none";
+ OS << "{ek=" << EKStr << " reach=" << Reachable
+ << " fwd-reach=" << ForwardReachable
+ << " loop-depth=" << RelativeLoopDepth << " size=" << Size;
+ if (ShortestDistance) {
+ OS << " shdist=";
+ ShortestDistance->print(OS);
+ }
+ if (ShortestUnweightedDistance) {
+ OS << " shudist=";
+ ShortestUnweightedDistance->print(OS);
+ }
+ OS << "}";
+ }
+
+ LLVM_DUMP_METHOD void dump() const {
+ print(dbgs());
+ dbgs() << '\n';
+ }
+ };
+
+ //----------------------------------------------------------------------------
+ // Path Storage - 'Paths' is lazily populated and some members are lazily
+ // computed. All mutations should go through one of the 'initializePathInfo*'
+ // flavors below.
+ //----------------------------------------------------------------------------
+ DenseMap<Path, PathInfo, Path::DenseMapInfo> Paths;
+
+ const PathInfo *maybePathInfoFor(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ auto I = Paths.find({From, To});
+ return I == Paths.end() ? nullptr : &I->second;
+ }
+
+ PathInfo &getOrInitPathInfo(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ auto *NonConstThis = const_cast<AMDGPUNextUseAnalysisImpl *>(this);
+ auto &MutablePaths = NonConstThis->Paths;
+
+ Path P(From, To);
+ auto [I, Inserted] = MutablePaths.try_emplace(P);
+ if (!Inserted)
+ return I->second;
+
+ bool Reachable = calcIsReachable(P.src(), P.dst());
+
+ // Iterator may have been invalidated by calcIsReachable, so get a fresh
+ // reference to the slot.
+ return NonConstThis->initializePathInfo(MutablePaths.at(P), P,
+ EdgeKind::None, Reachable);
+ }
+
+ const PathInfo &pathInfoFor(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return getOrInitPathInfo(From, To);
+ }
+
+ //----------------------------------------------------------------------------
+ // initializePathInfo* - various flavors of PathInfo initialization. They
+ // (should) always funnel to the first flavor below.
+ //----------------------------------------------------------------------------
+ PathInfo &initializePathInfo(PathInfo &Slot, Path P, EdgeKind EK,
+ bool Reachable) {
+ Slot.EK = EK;
+ Slot.Reachable = Reachable;
+ Slot.ForwardReachable = EK != EdgeKind::None ? (0 < EK) : -1;
+ Slot.RelativeLoopDepth =
+ Slot.Reachable ? calcRelativeLoopDepth(P.src(), P.dst()) : 0;
+ Slot.Size = P.src() == P.dst() ? calcSize(P.src()) : 0;
+ if (EK != EdgeKind::None)
+ Slot.ShortestUnweightedDistance = 0;
+ return Slot;
+ }
+
+ PathInfo &initializePathInfo(Path P, EdgeKind EK, bool Reachable) const {
+ auto *NonConstThis = const_cast<AMDGPUNextUseAnalysisImpl *>(this);
+ auto &MutablePaths = NonConstThis->Paths;
+ return NonConstThis->initializePathInfo(MutablePaths[P], P, EK, Reachable);
+ }
+
+ std::pair<PathInfo *, bool> maybeInitializePathInfo(Path P, EdgeKind EK,
+ bool Reachable) const {
+ auto *NonConstThis = const_cast<AMDGPUNextUseAnalysisImpl *>(this);
+ auto &MutablePaths = NonConstThis->Paths;
+ auto [I, Inserted] = MutablePaths.try_emplace(P);
+ if (Inserted)
+ NonConstThis->initializePathInfo(I->second, P, EK, Reachable);
+ return {&I->second, Inserted};
+ }
+
+ bool initializePathInfoForwardReachable(const MachineBasicBlock *From,
+ const MachineBasicBlock *To,
+ bool Value) const {
+ PathInfo &Slot = getOrInitPathInfo(From, To);
+ assert(Slot.isForwardReachableUnset());
+ Slot.ForwardReachable = Value;
+ return Value;
+ }
+
+ NextUseDistance
+ initializePathInfoShortestDistance(const MachineBasicBlock *From,
+ const MachineBasicBlock *To,
+ NextUseDistance Value) const {
+ PathInfo &Slot = getOrInitPathInfo(From, To);
+ assert(!Slot.ShortestDistance.has_value());
+ Slot.ShortestDistance = Value;
+ return Value;
+ }
+
+ NextUseDistance
+ initializePathInfoShortestUnweightedDistance(const MachineBasicBlock *From,
+ const MachineBasicBlock *To,
+ NextUseDistance Value) const {
+ PathInfo &Slot = getOrInitPathInfo(From, To);
+ assert(!Slot.ShortestUnweightedDistance.has_value());
+ Slot.ShortestUnweightedDistance = Value;
+ return Value;
+ }
+
+ //----------------------------------------------------------------------------
+ // initialize*Paths
+ //----------------------------------------------------------------------------
+private:
+ void initializePaths(const SmallVector<Path> &ReachablePaths,
+ const SmallVector<Path> &UnreachablePaths) const {
+ for (bool R : {true, false}) {
+ const auto &ToInit = R ? ReachablePaths : UnreachablePaths;
+ for (const Path &P : ToInit)
+ initializePathInfo(P, EdgeKind::None, R);
+ }
+ }
+
+ void
+ initializeForwardOnlyPaths(const SmallVector<Path> &ReachablePaths,
+ const SmallVector<Path> &UnreachablePaths) const {
+ for (bool R : {true, false}) {
+ const auto &ToInit = R ? ReachablePaths : UnreachablePaths;
+ for (const Path &P : ToInit) {
+ PathInfo &Slot = getOrInitPathInfo(P.src(), P.dst());
+ assert(Slot.isForwardReachableUnset() || Slot.ForwardReachable == R);
+ Slot.ForwardReachable = R;
+ }
+ }
+ }
+
+ // Follow the control flow graph starting at the entry block until all blocks
+ // have been visited. Along the way, initialize the PathInfo for each edge
+ // traversed.
+ void initializeCfgPaths() {
+ Paths.clear();
+
+ enum VisitState { Undiscovered, Visiting, Finished };
+ DenseMap<const MachineBasicBlock *, VisitState> State;
+
+ SmallVector<const MachineBasicBlock *> Work{&MF->front()};
+ State[&MF->front()] = Undiscovered;
+
+ while (!Work.empty()) {
+ const MachineBasicBlock *Src = Work.back();
+ VisitState &SrcState = State[Src];
+
+ // A block may already be 'Finished' if it is reachable from multiple
+ // predecessors causing it to be pushed more than once while still
+ // 'Undiscovered'.
+ if (SrcState == Visiting || SrcState == Finished) {
+ Work.pop_back();
+ SrcState = Finished;
+ continue;
+ }
+
+ SrcState = Visiting;
+ for (const MachineBasicBlock *Dst : Src->successors()) {
+ const VisitState DstState = State.lookup(Dst);
+
+ EdgeKind EK;
+ if (DstState == Undiscovered) {
+ EK = EdgeKind::Forward;
+ Work.push_back(Dst);
+ } else if (DstState == Visiting) {
+ EK = EdgeKind::Back;
+ } else {
+ EK = EdgeKind::Forward;
+ }
+
+ Path P(Src, Dst);
+ assert(!Paths.contains(P));
+ initializePathInfo(P, EK, /*Reachable*/ true);
+ }
+ }
+
+ LLVM_DEBUG(dumpPaths());
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Loop helpers
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ static bool isStandAloneLoop(const MachineLoop *Loop) {
+ return Loop->getSubLoops().empty() && Loop->isOutermost();
+ }
+
+ static MachineLoop *findChildLoop(MachineLoop *const Parent,
+ MachineLoop *Descendant) {
+ for (MachineLoop *L = Descendant; L != Parent; L = L->getParentLoop()) {
+ if (L->getParentLoop() == Parent)
+ return L;
+ }
+ return nullptr;
+ }
+
+ // If loops 'A' and 'B' share a common parent loop, return that loop and the
+ // depth of 'A' relative to it. Otherwise return nullptr and the loop depth of
+ // 'A'.
+ static std::pair<MachineLoop *, unsigned>
+ findCommonParent(MachineLoop *A, const MachineLoop *const B) {
+ unsigned Depth = 0;
+ for (; A != nullptr; A = A->getParentLoop(), ++Depth) {
+ if (A->contains(B))
+ break;
+ }
+ return {A, Depth};
+ }
+
+ static const MachineBasicBlock *
+ getOutermostPreheader(const MachineLoop *Loop) {
+ return Loop ? Loop->getOutermostLoop()->getLoopPreheader() : nullptr;
+ }
+
+ static MachineBasicBlock *findChildPreheader(MachineLoop *const Parent,
+ MachineLoop *Descendant) {
+ MachineLoop *ChildLoop = findChildLoop(Parent, Descendant);
+ return ChildLoop ? ChildLoop->getLoopPreheader() : nullptr;
+ }
+
+ static const MachineBasicBlock *mbbForPhiOp(const MachineInstr *MI,
+ const MachineOperand *MO) {
+ return MI->isPHI() ? MI->getOperand(MO->getOperandNo() + 1).getMBB()
+ : nullptr;
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Calculate features
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ InstrIdTy calcSize(const MachineBasicBlock *BB) const {
+ InstrIdTy Size = BB->size();
+ if (!Cfg.CountPhis)
+ Size -= std::distance(BB->begin(), BB->getFirstNonPHI());
+ return Size;
+ }
+
+ NextUseDistance calcWeightedSize(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return NextUseDistance::fromSize(getSize(From),
+ getRelativeLoopDepth(From, To));
+ }
+
+ // Return the loop depth of 'From' relative to 'To'.
+ unsigned calcRelativeLoopDepth(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ MachineLoop *LoopFrom = MLI->getLoopFor(From);
+ MachineLoop *LoopTo = MLI->getLoopFor(To);
+
+ if (!LoopFrom)
+ return 0;
+
+ if (!LoopTo)
+ return LoopFrom->getLoopDepth();
+
+ if (LoopFrom->contains(LoopTo)) // covers LoopFrom == LoopTo
+ return 0;
+
+ if (LoopTo->contains(LoopFrom))
+ return LoopFrom->getLoopDepth() - LoopTo->getLoopDepth();
+
+ // Loops are siblings of some sort.
+ return findCommonParent(LoopFrom, LoopTo).second;
+ }
+
+ // Attempt to find a path from 'From' to 'To' using a depth first search. If
+ // 'ForwardOnly' is true, do not follow backedges. As a performance
+ // improvement, this may initialize reachable intermediate paths or paths we
+ // determine are unreachable.
+ bool calcIsReachable(const MachineBasicBlock *From,
+ const MachineBasicBlock *To,
+ bool ForwardOnly = false) const {
+ if (From == To && !MLI->getLoopFor(From))
+ return false;
+
+ if (!ForwardOnly && interBlockDistanceExists(From, To))
+ return true;
+
+ enum { VisitOp, PopOp };
+ using MBBOpPair = std::pair<const MachineBasicBlock *, int>;
+ SmallVector<MBBOpPair> Work{{From, VisitOp}};
+ DenseSet<const MachineBasicBlock *> Visited{From};
+
+ SmallVector<Path> IntermediatePath;
+ SmallVector<Path> Unreachable;
+
+ // Should be run at every function exit point.
+ auto Finally = [&](bool Reachable) {
+ // This is an optimization. For intermediate paths we found while
+ // calculating reachability for 'From' --> 'To', remember their
+ // reachability.
+ if (!Reachable) {
+ IntermediatePath.clear();
+ for (const MachineBasicBlock *MBB : Visited) {
+ if (MBB != From)
+ Unreachable.emplace_back(MBB, To);
+ }
+ }
+
+ if (ForwardOnly)
+ initializeForwardOnlyPaths(IntermediatePath, Unreachable);
+ else
+ initializePaths(IntermediatePath, Unreachable);
+
+ return Reachable;
+ };
+
+ while (!Work.empty()) {
+ auto [Current, Op] = Work.pop_back_val();
+
+ // Backtracking
+ if (Op == PopOp) {
+ IntermediatePath.pop_back();
+ if (ForwardOnly)
+ Unreachable.emplace_back(Current, To);
+ continue;
+ }
+
+ if (Current->succ_empty())
+ continue;
+
+ if (Current != From) {
+ IntermediatePath.emplace_back(Current, To);
+ Work.emplace_back(Current, PopOp);
+ }
+
+ for (const MachineBasicBlock *Succ : Current->successors()) {
+ if (ForwardOnly && isBackedge(Current, Succ))
+ continue;
+
+ if (Succ == To)
+ return Finally(true);
+
+ if (auto CachedReachable = isMaybeReachable(Succ, To, ForwardOnly)) {
+ if (CachedReachable.value())
+ return Finally(true);
+ Visited.insert(Succ);
+ continue;
+ }
+
+ if (Visited.insert(Succ).second)
+ Work.emplace_back(Succ, VisitOp);
+ }
+ }
+
+ return Finally(false);
+ }
+
+ //----------------------------------------------------------------------------
+ // Inter-block distance - the weighted and unweighted cost (i.e. "distance")
+ // to travel from one MachineBasicBlock to another.
+ //
+ // Values are pre-computed and stored in 'InterBlockDistances' using a
+ // backwards data-flow algorithm similar to the one described in 4.1 of a
+ // "Register Spilling and Live-Range Splitting for SSA-Form Programs" by
+ // Matthias Braun and Sebastian Hack, CC'09. This replaced a prior
+ // implementation based on Dijkstra's shortest path algorithm.
+ //----------------------------------------------------------------------------
+private:
+ struct InterBlockDistance {
+ NextUseDistance Weighted;
+ NextUseDistance Unweighted;
+ InterBlockDistance() : Weighted(-1), Unweighted(-1) {}
+ InterBlockDistance(NextUseDistance W, NextUseDistance UW)
+ : Weighted(W), Unweighted(UW) {}
+ bool operator==(const InterBlockDistance &Other) const {
+ return Weighted == Other.Weighted && Unweighted == Other.Unweighted;
+ }
+ bool operator!=(const InterBlockDistance &Other) const {
+ return !(*this == Other);
+ }
+
+ void print(raw_ostream &OS) const {
+ OS << "{W=";
+ Weighted.print(OS);
+ OS << " U=";
+ Unweighted.print(OS);
+ OS << "}";
+ }
+
+ LLVM_DUMP_METHOD void dump() const {
+ print(dbgs());
+ dbgs() << '\n';
+ }
+ };
+ using InterBlockDistanceMap =
+ DenseMap<unsigned, DenseMap<unsigned, InterBlockDistance>>;
+ InterBlockDistanceMap InterBlockDistances;
+
+ void initializeInterBlockDistances() {
+ InterBlockDistanceMap Distances;
+
+ bool Changed;
+ do {
+ Changed = false;
+ for (const MachineBasicBlock *MBB : post_order(MF)) {
+ unsigned MBBNum = MBB->getNumber();
+
+ // Save previous state for convergence check
+ InterBlockDistanceMap::mapped_type Prev = std::move(Distances[MBBNum]);
+ InterBlockDistanceMap::mapped_type Curr;
+ Curr.reserve(Prev.size());
+
+ // Direct successors are distance 0 by definition: no instructions are
+ // executed between exiting MBB and entering Succ.
+ for (const MachineBasicBlock *Succ : MBB->successors())
+ Curr[Succ->getNumber()] = InterBlockDistance(0, 0);
+
+ // Propagate further destinations through each successor.
+ for (const MachineBasicBlock *Succ : MBB->successors()) {
+ unsigned SuccNum = Succ->getNumber();
+ const unsigned UnweightedSize{getSize(Succ)};
+
+ for (const auto &[DestBlockNum, DestDist] : Distances[SuccNum]) {
+ // MBB -> MBB is considered unreachable (getInterBlockDistance
+ // asserts From != To).
+ if (DestBlockNum == MBBNum)
+ continue;
+
+ const MachineBasicBlock *DestMBB =
+ MF->getBlockNumbered(DestBlockNum);
+
+ const NextUseDistance UnweightedDist{UnweightedSize +
+ DestDist.Unweighted};
+
+ unsigned SuccToDestLoopDepth = calcRelativeLoopDepth(Succ, DestMBB);
+
+ const NextUseDistance WeightedDist =
+ DestDist.Weighted +
+ NextUseDistance::fromSize(UnweightedSize, SuccToDestLoopDepth);
+
+ // Insert or update distances (take minimum)
+ auto [I, First] =
+ Curr.try_emplace(DestBlockNum, WeightedDist, UnweightedDist);
+ if (!First) {
+ InterBlockDistance &Slot = I->second;
+ Slot.Weighted = min(Slot.Weighted, WeightedDist);
+ Slot.Unweighted = min(Slot.Unweighted, UnweightedDist);
+ }
+ }
+ }
+ Changed |= (Prev != Curr);
+ Distances[MBBNum] = std::move(Curr);
+ }
+ } while (Changed);
+
+ InterBlockDistances = std::move(Distances);
+ LLVM_DEBUG(dumpInterBlockDistances());
+ }
+
+ const InterBlockDistance *
+ getInterBlockDistanceMapValue(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ auto I = InterBlockDistances.find(From->getNumber());
+ if (I == InterBlockDistances.end())
+ return nullptr;
+ const InterBlockDistanceMap::mapped_type &FromSlot = I->second;
+ auto J = FromSlot.find(To->getNumber());
+ return J == FromSlot.end() ? nullptr : &J->second;
+ }
+
+ bool interBlockDistanceExists(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return getInterBlockDistanceMapValue(From, To);
+ }
+
+ NextUseDistance getInterBlockDistance(const MachineBasicBlock *From,
+ const MachineBasicBlock *To,
+ bool Unweighted) const {
+
+ assert(From != To && "The basic blocks should be different.");
+ if (!From || !To)
+ return NextUseDistance::unreachable();
+
+ if (Cfg.ForwardOnly && !isForwardReachable(From, To))
+ return NextUseDistance::unreachable();
+
+ const InterBlockDistance *BD = getInterBlockDistanceMapValue(From, To);
+ if (!BD)
+ return NextUseDistance::unreachable();
+
+ return Unweighted ? BD->Unweighted : BD->Weighted;
+ }
+
+ NextUseDistance
+ getWeightedInterBlockDistance(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return getInterBlockDistance(From, To, false);
+ }
+
+ NextUseDistance
+ getUnweightedInterBlockDistance(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return getInterBlockDistance(From, To, true);
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Feature getters. Use cached results if available. If not calculate.
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ InstrIdTy getSize(const MachineBasicBlock *BB) const {
+ return pathInfoFor(BB, BB).Size;
+ }
+
+ bool isReachable(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return pathInfoFor(From, To).Reachable;
+ }
+
+ bool isReachableOrSame(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return From == To || pathInfoFor(From, To).Reachable;
+ }
+
+ bool isForwardReachable(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ const PathInfo &PI = pathInfoFor(From, To);
+ if (PI.isForwardReachableSet())
+ return PI.isForwardReachable();
+
+ return initializePathInfoForwardReachable(
+ From, To,
+ PI.Reachable && calcIsReachable(From, To, /*ForwardOnly*/ true));
+ }
+
+ // Return true/false if we know that 'To' is reachable or not from
+ // 'From'. Otherwise return 'std::nullopt'.
+ std::optional<bool> isMaybeReachable(const MachineBasicBlock *From,
+ const MachineBasicBlock *To,
+ bool ForwardOnly) const {
+ const PathInfo *PI = maybePathInfoFor(From, To);
+ if (!PI)
+ return std::nullopt;
+
+ if (ForwardOnly) {
+ if (PI->isForwardReachable())
+ return true;
+
+ if (PI->isNotForwardReachable())
+ return false;
+ return std::nullopt;
+ }
+ return PI->Reachable;
+ }
+
+ bool isBackedge(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return pathInfoFor(From, To).isBackedge();
+ }
+
+ // Can be used as a substitute for DT->dominates(A, B) if A and B are in the
+ // same basic block.
+ bool instrsAreInOrder(const MachineInstr *A, const MachineInstr *B) const {
+ assert(A->getParent() == B->getParent() &&
+ "instructions must be in the same basic block!");
+ if (A == B || getInstrId(A) < getInstrId(B))
+ return true;
+ if (!A->isPHI())
+ return false;
+ if (!B->isPHI())
+ return true;
+ for (auto &PHI : A->getParent()->phis()) {
+ if (&PHI == A)
+ return true;
+ if (&PHI == B)
+ return false;
+ }
+ return false;
+ }
+
+ unsigned getRelativeLoopDepth(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ return pathInfoFor(From, To).RelativeLoopDepth;
+ }
+
+ NextUseDistance getShortestPath(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ std::optional<NextUseDistance> MaybeD =
+ pathInfoFor(From, To).ShortestDistance;
+ if (MaybeD.has_value())
+ return MaybeD.value();
+
+ NextUseDistance Dist = getWeightedInterBlockDistance(From, To);
+ return initializePathInfoShortestDistance(From, To, Dist);
+ }
+
+ NextUseDistance getShortestUnweightedPath(const MachineBasicBlock *From,
+ const MachineBasicBlock *To) const {
+ std::optional<NextUseDistance> MaybeD =
+ pathInfoFor(From, To).ShortestUnweightedDistance;
+ if (MaybeD.has_value())
+ return MaybeD.value();
+
+ return initializePathInfoShortestUnweightedDistance(
+ From, To, getUnweightedInterBlockDistance(From, To));
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /// MBBDistPair - Represents the distance to a machine basic block.
+ /// Used for returning both the distance and the target block together.
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ struct MBBDistPair {
+ NextUseDistance Distance;
+ const MachineBasicBlock *MBB;
+ MBBDistPair() : Distance(NextUseDistance::unreachable()), MBB(nullptr) {}
+ MBBDistPair(NextUseDistance D, const MachineBasicBlock *B)
+ : Distance(D), MBB(B) {}
+
+ MBBDistPair operator+(NextUseDistance D) { return {Distance + D, MBB}; }
+ MBBDistPair &operator+=(NextUseDistance D) {
+ Distance += D;
+ return *this;
+ }
+
+ void print(raw_ostream &OS) const {
+ OS << "{";
+ Distance.print(OS);
+ if (MBB)
+ OS << " " << printMBBReference(*MBB);
+ else
+ OS << " <null>";
+ OS << "}";
+ }
+
+ LLVM_DUMP_METHOD void dump() const {
+ print(dbgs());
+ dbgs() << '\n';
+ }
+ };
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // CFG Helpers
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ // Return the shortest distance to a latch
+ MBBDistPair calcShortestDistanceToLatch(const MachineBasicBlock *CurMBB,
+ const MachineLoop *CurLoop) const {
+ SmallVector<MachineBasicBlock *, 2> Latches;
+ CurLoop->getLoopLatches(Latches);
+ MBBDistPair LD;
+
+ for (MachineBasicBlock *LMBB : Latches) {
+ if (LMBB == CurMBB)
+ return {0, CurMBB};
+
+ NextUseDistance Dst = getShortestPath(CurMBB, LMBB);
+ if (Dst < LD.Distance) {
+ LD.Distance = Dst;
+ LD.MBB = LMBB;
+ }
+ }
+ return LD;
+ }
+
+ // Return the shortest unweighted distance to a latch
+ MBBDistPair
+ calcShortestUnweightedDistanceToLatch(const MachineBasicBlock *CurMBB,
+ const MachineLoop *CurLoop) const {
+ SmallVector<MachineBasicBlock *, 2> Latches;
+ CurLoop->getLoopLatches(Latches);
+ MBBDistPair LD;
+
+ for (MachineBasicBlock *LMBB : Latches) {
+ if (LMBB == CurMBB)
+ return {0, CurMBB};
+
+ NextUseDistance Dst = getShortestUnweightedPath(CurMBB, LMBB);
+ if (Dst < LD.Distance) {
+ LD.Distance = Dst;
+ LD.MBB = LMBB;
+ }
+ }
+ return LD;
+ }
+
+ // Return the shortest distance to an exit
+ MBBDistPair calcShortestDistanceToExit(const MachineBasicBlock *CurMBB,
+ const MachineLoop *CurLoop) const {
+ SmallVector<std::pair<MachineBasicBlock *, MachineBasicBlock *>> ExitEdges;
+ CurLoop->getExitEdges(ExitEdges);
+ MBBDistPair LD;
+
+ for (auto [Exit, Dest] : ExitEdges) {
+ if (Exit == CurMBB)
+ return {0, CurMBB};
+
+ NextUseDistance Dst = getShortestPath(CurMBB, Exit);
+ if (Dst < LD.Distance) {
+ LD.Distance = Dst;
+ LD.MBB = Exit;
+ }
+ }
+ return LD;
+ }
+
+ // Return the shortest distance through a loop (header to latch) that goes
+ // through CurMBB.
+ MBBDistPair
+ calcShortestDistanceThroughInnermostLoop(const MachineBasicBlock *CurMBB,
+ MachineLoop *CurLoop) const {
+ assert(MLI->getLoopFor(CurMBB) == CurLoop);
+
+ // This is a hot spot. Check it before doing anything else.
+ if (CurLoop->getNumBlocks() == 1)
+ return {getSize(CurMBB), CurMBB};
+
+ MachineBasicBlock *LoopHeader = CurLoop->getHeader();
+ MBBDistPair LD{0, nullptr};
+
+ LD += getSize(LoopHeader);
+
+ if (CurMBB != LoopHeader)
+ LD += getShortestPath(LoopHeader, CurMBB);
+
+ if (CurLoop->isLoopExiting(CurMBB))
+ LD.MBB = CurMBB;
+ else
+ LD = calcShortestDistanceToExit(CurMBB, CurLoop) + LD.Distance;
+
+ if (CurMBB != LoopHeader && CurMBB != LD.MBB)
+ LD += getSize(CurMBB);
+
+ if (LD.MBB != LoopHeader)
+ LD += getSize(LD.MBB);
+
+ return LD;
+ }
+
+ // Return the shortest distance through a loop (header to latch) that goes
+ // through CurMBB.
+ MBBDistPair calcShortestDistanceThroughLoop(const MachineBasicBlock *CurMBB,
+ MachineLoop *OuterLoop) const {
+ MachineLoop *CurLoop = MLI->getLoopFor(CurMBB);
+ MBBDistPair CurLD =
+ calcShortestDistanceThroughInnermostLoop(CurMBB, CurLoop);
+
+ MachineBasicBlock *CurHdr = CurLoop->getHeader();
+ for (;;) {
+ if (OuterLoop == CurLoop)
+ return CurLD;
+
+ MachineLoop *ParentLoop = CurLoop->getParentLoop();
+ MachineBasicBlock *ParentHdr = ParentLoop->getHeader();
+
+ MBBDistPair LD{0, nullptr};
+ LD += getSize(ParentHdr);
+ LD += getShortestPath(ParentHdr, CurHdr);
+ LD += CurLD.Distance.applyLoopWeight();
+ LD = calcShortestDistanceToExit(CurLD.MBB, ParentLoop) + LD.Distance;
+ LD += getSize(LD.MBB);
+ CurLD = LD;
+ CurLoop = ParentLoop;
+ CurHdr = ParentHdr;
+ }
+ llvm_unreachable("CurMBB not contained in OuterLoop");
+ }
+
+ // Similar to calcShortestDistanceThroughLoop with LoopWeight applied to the
+ // returned distance.
+ MBBDistPair
+ calcWeightedDistanceThroughLoopViaMBB(const MachineBasicBlock *CurMBB,
+ MachineLoop *CurLoop) const {
+ MBBDistPair LD = calcShortestDistanceThroughLoop(CurMBB, CurLoop);
+ LD.Distance = LD.Distance.applyLoopWeight();
+ return LD;
+ }
+
+ // Return the weighted, shortest distance through a loop (header to latch).
+ // If ParentLoop is provided, use it to adjust the loop depth.
+ MBBDistPair calcWeightedDistanceThroughLoop(
+ MachineLoop *CurLoop, const MachineLoop *ParentLoop = nullptr) const {
+ const MachineBasicBlock *Hdr = CurLoop->getHeader();
+ if (CurLoop->getNumBlocks() != 1)
+ return calcWeightedDistanceThroughLoopViaMBB(Hdr, CurLoop);
+
+ unsigned LoopDepth = MLI->getLoopDepth(Hdr);
+ if (ParentLoop)
+ LoopDepth -= ParentLoop->getLoopDepth();
+
+ return {NextUseDistance::fromSize(getSize(Hdr), LoopDepth),
+ CurLoop->getLoopLatch()};
+ }
+
+ // Calculate total distance from exit point to use instruction
+ NextUseDistance appendDistanceToUse(const MBBDistPair &Exit,
+ const MachineInstr *UseMI,
+ const MachineBasicBlock *UseMBB) const {
+ return Exit.Distance + getShortestPath(Exit.MBB, UseMBB) +
+ getHeadLen(UseMI);
+ }
+
+ // Return the weighted, shortest distance through the CurLoop which is a
+ // sub-loop of UseLoop.
+ MBBDistPair calcDistanceThroughSubLoopUse(MachineLoop *CurLoop,
+ MachineLoop *UseLoop) const {
+ // All the sub-loops of the UseLoop will be executed before the use.
+ // Hence, we should take this into consideration in distance calculation.
+ MachineLoop *UseLoopSubLoop = findChildLoop(UseLoop, CurLoop);
+ assert(UseLoopSubLoop && "CurLoop should be nested in UseLoop");
+ return calcWeightedDistanceThroughLoop(UseLoopSubLoop, UseLoop);
+ }
+
+ // Similar to calcDistanceThroughSubLoopUse, adding the distance to 'UseMI'.
+ NextUseDistance calcDistanceThroughSubLoopToUseMI(
+ const MachineBasicBlock *CurMBB, MachineLoop *CurLoop,
+ const MachineInstr *UseMI, const MachineBasicBlock *UseMBB,
+ MachineLoop *UseLoop) const {
+ return appendDistanceToUse(calcDistanceThroughSubLoopUse(CurLoop, UseLoop),
+ UseMI, UseMBB);
+ }
+
+ // Return the weighted distance through a loop to an outside use loop.
+ // Differentiates between uses inside or outside of the current loop nest.
+ MBBDistPair calcDistanceThroughLoopToOutsideLoopUse(
+ const MachineBasicBlock *CurMBB, MachineLoop *CurLoop,
+ const MachineBasicBlock *UseMBB, MachineLoop *UseLoop) const {
+ assert(!CurLoop->contains(UseLoop));
+
+ if (isStandAloneLoop(CurLoop))
+ return calcWeightedDistanceThroughLoopViaMBB(CurMBB, CurLoop);
+
+ MachineLoop *OutermostLoop = CurLoop->getOutermostLoop();
+ if (!OutermostLoop->contains(UseLoop)) {
+ // We should take into consideration the whole loop nest in the
+ // calculation of the distance because we will reach the use after
+ // executing the whole loop nest.
+
+ // ... But make sure that we pick a route that goes through CurMBB
+ return calcWeightedDistanceThroughLoopViaMBB(CurMBB, OutermostLoop);
+ }
+
+ // At this point we know that CurLoop and UseLoop are independent and they
+ // are in the same loop nest.
+
+ if (MLI->getLoopDepth(CurMBB) <= MLI->getLoopDepth(UseMBB))
+ return calcWeightedDistanceThroughLoop(CurLoop);
+
+ assert(CurLoop != OutermostLoop && "The loop cannot be the outermost.");
+ const unsigned UseLoopDepth = MLI->getLoopDepth(UseMBB);
+ for (;;) {
+ if (CurLoop->getLoopDepth() == UseLoopDepth)
+ break;
+ CurLoop = CurLoop->getParentLoop();
+ if (CurLoop == OutermostLoop)
+ break;
+ }
+ return calcWeightedDistanceThroughLoop(CurLoop);
+ }
+
+ // Similar to calcDistanceThroughLoopToOutsideLoopUse but adds the distance to
+ // an instruction in the loop.
+ NextUseDistance calcDistanceThroughLoopToOutsideLoopUseMI(
+ const MachineBasicBlock *CurMBB, MachineLoop *CurLoop,
+ const MachineInstr *UseMI, const MachineBasicBlock *UseMBB,
+ MachineLoop *UseLoop) const {
+ return appendDistanceToUse(calcDistanceThroughLoopToOutsideLoopUse(
+ CurMBB, CurLoop, UseMBB, UseLoop),
+ UseMI, UseMBB);
+ }
+
+ // Return true if 'MO' is covered by 'LaneMask'
+ bool machineOperandCoveredBy(const MachineOperand &MO,
+ LaneBitmask LaneMask) const {
+ LaneBitmask Mask = TRI->getSubRegIndexLaneMask(MO.getSubReg());
+ return (Mask & LaneMask) == Mask;
+ }
+
+ // Returns true iff uses of LiveReg/LiveLaneMask in PHI UseMI are coming from
+ // a backedge when starting at CurMI.
+ bool isIncomingValFromBackedge(Register LiveReg, LaneBitmask LiveLaneMask,
+ const MachineInstr *CurMI,
+ const MachineInstr *UseMI) const {
+ if (!UseMI->isPHI())
+ return false;
+
+ MachineLoop *CurLoop = MLI->getLoopFor(CurMI->getParent());
+ MachineLoop *UseLoop = MLI->getLoopFor(UseMI->getParent());
+
+ // Not a backedge if ...
+ // A: not in a loop at all
+ // B: or CurMI is in a loop outside of UseLoop
+ // C: or UseMI is not in the UseLoop header
+ if (/*A:*/ !UseLoop ||
+ /*B:*/ (CurLoop && !UseLoop->contains(CurLoop)) ||
+ /*C:*/ UseMI->getParent() != UseLoop->getHeader())
+ return false;
+
+ SmallVector<MachineBasicBlock *, 2> Latches;
+ UseLoop->getLoopLatches(Latches);
+
+ auto Ops = UseMI->operands();
+ for (auto It = std::next(Ops.begin()), ItE = Ops.end(); It != ItE;
+ It = std::next(It, 2)) {
+ auto &RegMO = *It;
+ auto &MBBMO = *std::next(It);
+ assert(RegMO.isReg() && "Expected register operand of PHI");
+ assert(MBBMO.isMBB() && "Expected MBB operand of PHI");
+ if (RegMO.getReg() == LiveReg &&
+ machineOperandCoveredBy(RegMO, LiveLaneMask)) {
+ MachineBasicBlock *IncomingBB = MBBMO.getMBB();
+ if (llvm::is_contained(Latches, IncomingBB))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Return the distance from 'CurMI' through a parent loop backedge PHI Use
+ // ('UseMI').
+ CacheableNextUseDistance calcDistanceViaEnclosingBackedge(
+ const MachineInstr *CurMI, const MachineBasicBlock *CurMBB,
+ MachineLoop *CurLoop, const MachineInstr *UseMI,
+ const MachineBasicBlock *UseMBB, MachineLoop *UseLoop) const {
+ assert(UseLoop && "There is no backedge.");
+ assert(CurLoop && (UseLoop != CurLoop) && UseLoop->contains(CurLoop) &&
+ "Unexpected loop configuration");
+
+ InstrIdTy UseHeadLen = getHeadLen(UseMI);
+ MBBDistPair InnerLoopLD = calcDistanceThroughSubLoopUse(CurLoop, UseLoop);
+ MBBDistPair LD = calcShortestDistanceToLatch(InnerLoopLD.MBB, UseLoop);
+ return {InstrInvariant,
+ InnerLoopLD.Distance + LD.Distance + getSize(LD.MBB) + UseHeadLen};
+ }
+
+ // Optimized version of calcBackedgeDistance when we already know that CurMI
+ // and UseMI are in the same basic block
+ NextUseDistance calcBackedgeDistance(const MachineInstr *CurMI,
+ const MachineBasicBlock *CurMBB,
+ MachineLoop *CurLoop,
+ const MachineInstr *UseMI) const {
+ // use is in the next loop iteration
+ InstrIdTy CurTailLen = getTailLen(CurMI);
+ InstrIdTy UseHeadLen = getHeadLen(UseMI);
+ MBBDistPair LD = calcShortestUnweightedDistanceToLatch(CurMBB, CurLoop);
+ const MachineBasicBlock *HdrMBB = CurLoop->getHeader();
+ NextUseDistance Hdr = CurMBB == HdrMBB ? 0 : getSize(HdrMBB);
+ NextUseDistance Dst =
+ CurMBB == HdrMBB ? 0 : getShortestUnweightedPath(HdrMBB, CurMBB);
+
+ return CurTailLen + LD.Distance + getSize(LD.MBB) + Hdr + Dst + UseHeadLen;
+ }
+
+ //----------------------------------------------------------------------------
+ // Calculate inter-instruction distances
+ //----------------------------------------------------------------------------
+private:
+ // Calculate the shortest weighted path from MachineInstruction 'FromMI' to
+ // 'ToMI'. It is weighted distance in that paths that exit loops are made to
+ // look much further away.
+ NextUseDistance calcShortestDistance(const MachineInstr *FromMI,
+ const MachineInstr *ToMI) const {
+ const MachineBasicBlock *FromMBB = FromMI->getParent();
+ const MachineBasicBlock *ToMBB = ToMI->getParent();
+
+ if (FromMBB == ToMBB) {
+ NextUseDistance RV = getDistance(FromMI, ToMI);
+ assert(RV >= 0 && "unexpected negative distance from getDistance");
+ return RV;
+ }
+
+ InstrIdTy FromTailLen = getTailLen(FromMI);
+ InstrIdTy ToHeadLen = getHeadLen(ToMI);
+ NextUseDistance Dst = getShortestPath(FromMBB, ToMBB);
+ assert(Dst.isReachable() &&
+ "calcShortestDistance called for instructions in non-reachable"
+ " basic blocks!");
+ NextUseDistance RV = FromTailLen + Dst + ToHeadLen;
+ assert(RV >= 0 && "unexpected negative distance");
+ return RV;
+ }
+
+ // Calculate the shortest unweighted path from MachineInstruction 'FromMI' to
+ // 'ToMI'. In contrast with 'calcShortestDistance', distances are based solely
+ // on basic block instruction counts and traversing a loop exit does not
+ // affect the value.
+ NextUseDistance
+ calcShortestUnweightedDistance(const MachineInstr *FromMI,
+ const MachineInstr *ToMI) const {
+ const MachineBasicBlock *FromMBB = FromMI->getParent();
+ const MachineBasicBlock *ToMBB = ToMI->getParent();
+
+ if (FromMBB == ToMBB)
+ return getDistance(FromMI, ToMI);
+
+ InstrIdTy FromTailLen = getTailLen(FromMI);
+ InstrIdTy ToHeadLen = getHeadLen(ToMI);
+ NextUseDistance Dst = getShortestUnweightedPath(FromMBB, ToMBB);
+ assert(Dst.isReachable() &&
+ "calcShortestUnweightedDistance called for instructions in"
+ " non-reachable basic blocks!");
+ return FromTailLen + Dst + ToHeadLen;
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // calcDistanceToUse* - various flavors of calculating the distance from an
+ // instruction 'CurMI' to the use of a live [sub]register.
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ // Return the distance from 'CurMI' to a live [sub]register use ('UseMI').
+ //
+ // Cfg flags controlling behavior:
+ // PreciseUseModeling — rewrite PHI uses to their incoming edge block;
+ // also selects unweighted cross-block distance
+ // PromoteToPreheader — route loop-entry / inner-loop uses to the preheader
+ CacheableNextUseDistance
+ calcDistanceToUse(Register LiveReg, LaneBitmask LiveLaneMask,
+ const MachineInstr &CurMI,
+ const MachineOperand *UseMO) const {
+ const MachineInstr *UseMI = UseMO->getParent();
+ const MachineBasicBlock *CurMBB = CurMI.getParent();
+ const MachineBasicBlock *UseMBB = UseMI->getParent();
+ MachineLoop *CurLoop = MLI->getLoopFor(CurMBB);
+ MachineLoop *UseLoop = MLI->getLoopFor(UseMBB);
+
+ if (Cfg.PreciseUseModeling) {
+ // Map PHI use to the end of its incoming edge block.
+ if (const MachineBasicBlock *PhiUseEdge = mbbForPhiOp(UseMI, UseMO)) {
+ UseMI = &PhiUseEdge->back();
+ UseMBB = PhiUseEdge;
+ UseLoop = MLI->getLoopFor(PhiUseEdge);
+ }
+ }
+
+ enum class LoopConfig {
+ NoCur,
+ Same,
+ CurContainsUse,
+ UseContainsCur,
+ Siblings,
+ Unrelated
+ };
+ auto [LpCfg, PreHdr, CommonParent] = [&]()
+ -> std::tuple<LoopConfig, const MachineBasicBlock *, MachineLoop *> {
+ if (!CurLoop) {
+ return {LoopConfig::NoCur, getOutermostPreheader(UseLoop), nullptr};
+ }
+ if (CurLoop->contains(UseLoop)) {
+ return {CurMBB == UseMBB ? LoopConfig::Same
+ : LoopConfig::CurContainsUse,
+ findChildPreheader(CurLoop, UseLoop), nullptr};
+ }
+
+ if (MachineLoop *P = findCommonParent(UseLoop, CurLoop).first) {
+ if (P != UseLoop)
+ return {LoopConfig::Siblings, findChildPreheader(P, UseLoop), P};
+ return {LoopConfig::UseContainsCur, nullptr, nullptr};
+ }
+ return {LoopConfig::Unrelated, getOutermostPreheader(UseLoop), nullptr};
+ }();
+
+ //--------------------------------------------------------------------------
+ // Don't PromoteToPreheader
+ //--------------------------------------------------------------------------
+ if (!Cfg.PromoteToPreheader) {
+ switch (LpCfg) {
+ case LoopConfig::NoCur:
+ case LoopConfig::Same:
+ case LoopConfig::CurContainsUse:
+ return {InstrRelative, calcShortestDistance(&CurMI, UseMI)};
+
+ case LoopConfig::UseContainsCur: {
+ if (isIncomingValFromBackedge(LiveReg, LiveLaneMask, &CurMI, UseMI)) {
+ return calcDistanceViaEnclosingBackedge(&CurMI, CurMBB, CurLoop,
+ UseMI, UseMBB, UseLoop);
+ }
+
+ return {InstrInvariant, calcDistanceThroughSubLoopToUseMI(
+ CurMBB, CurLoop, UseMI, UseMBB, UseLoop)};
+ }
+ case LoopConfig::Siblings:
+ case LoopConfig::Unrelated:
+ return {InstrInvariant, calcDistanceThroughLoopToOutsideLoopUseMI(
+ CurMBB, CurLoop, UseMI, UseMBB, UseLoop)};
+ }
+ llvm_unreachable("unexpected loop configuration!");
+ }
+
+ //--------------------------------------------------------------------------
+ // PromoteToPreheader
+ //--------------------------------------------------------------------------
+ if (PreHdr) {
+ UseMI = &PreHdr->back();
+ UseMBB = PreHdr;
+ UseLoop = CommonParent;
+ }
+
+ switch (LpCfg) {
+ case LoopConfig::NoCur:
+ return {InstrRelative, calcShortestUnweightedDistance(&CurMI, UseMI) -
+ (sizeOf(*UseMI) ? 0 : 1)};
+
+ case LoopConfig::Same:
+ case LoopConfig::CurContainsUse:
+ if (CurMBB == UseMBB && !instrsAreInOrder(&CurMI, UseMI))
+ return {InstrRelative,
+ calcBackedgeDistance(&CurMI, CurMBB, CurLoop, UseMI)};
+
+ return {InstrRelative, calcShortestUnweightedDistance(&CurMI, UseMI)};
+
+ case LoopConfig::UseContainsCur:
+ case LoopConfig::Siblings:
+ return {InstrInvariant, calcDistanceThroughSubLoopToUseMI(
+ CurMBB, CurLoop, UseMI, UseMBB, UseLoop)};
+
+ case LoopConfig::Unrelated:
+ return {InstrInvariant, calcDistanceThroughLoopToOutsideLoopUseMI(
+ CurMBB, CurLoop, UseMI, UseMBB, UseLoop)};
+ }
+ llvm_unreachable("unexpected loop configuration!");
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // getUses helpers (compute mode)
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ // Returns true if Use is reachable from MI. Handles backedges and intervening
+ // defs.
+ bool isUseReachablePrecise(const MachineInstr &MI,
+ const MachineBasicBlock *MBB,
+ const MachineOperand *UseMO,
+ const MachineInstr *UseMI,
+ const MachineBasicBlock *UseMBB) const {
+
+ // Filter out uses that are clearly unreachable
+ if (MBB != UseMBB && !isReachable(MBB, UseMBB))
+ return false;
+
+ // PHI uses are considered part of the incoming BB. Check for reachability
+ // at the edge.
+ if (const MachineBasicBlock *EdgeMBB = mbbForPhiOp(UseMI, UseMO)) {
+ if (!isReachableOrSame(MBB, EdgeMBB))
+ return false;
+ }
+
+ // Filter out uses with an intermediate def.
+ const MachineInstr *DefMI = MRI->getUniqueVRegDef(UseMO->getReg());
+ const MachineBasicBlock *DefMBB = DefMI->getParent();
+ if (MBB == UseMBB) {
+ if (UseMI->isPHI() && MBB == DefMBB)
+ return true;
+
+ if (instrsAreInOrder(&MI, UseMI))
+ return true;
+
+ // A Def in the loop means that the value at MI will not survive through
+ // to this use.
+ MachineLoop *UseLoop = MLI->getLoopFor(UseMBB);
+ return UseLoop && !UseLoop->contains(DefMBB);
+ }
+
+ if (MBB == DefMBB)
+ return instrsAreInOrder(DefMI, &MI);
+
+ MachineLoop *Loop = MLI->getLoopFor(MBB);
+ if (!Loop)
+ return true;
+
+ MachineLoop *TopLoop = Loop->getOutermostLoop();
+ return !TopLoop->contains(DefMBB) || !isReachable(MBB, DefMBB) ||
+ !isForwardReachable(UseMBB, MBB);
+ }
+
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ // Debug/Developer Helpers
+ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+private:
+ /// Goes over all MBB pairs in \p MF, calculates the shortest path between
+ /// them.
+ void populatePathTable() {
+ for (const MachineBasicBlock &MBB1 : *MF) {
+ for (const MachineBasicBlock &MBB2 : *MF) {
+ if (&MBB1 == &MBB2)
+ continue;
+ getShortestPath(&MBB1, &MBB2);
+ }
+ }
+ }
+
+ void printPaths(raw_ostream &OS) const {
+ OS << "\n---------------- Paths --------------- {\n";
+ for (const auto &[P, PI] : Paths) {
+ OS << " " << printMBBReference(*P.src()) << " -> "
+ << printMBBReference(*P.dst()) << ": ";
+ PI.print(OS);
+ OS << '\n';
+ }
+ OS << "}\n";
+ }
+
+ LLVM_DUMP_METHOD void dumpPaths() const { printPaths(dbgs()); }
+
+ // Legacy alias kept for existing call sites.
+ void dumpShortestPaths() const {
+ for (const auto &P : Paths) {
+ const MachineBasicBlock *From = P.first.src();
+ const MachineBasicBlock *To = P.first.dst();
+ std::optional<NextUseDistance> Dist = P.second.ShortestDistance;
+ dbgs() << "From: " << printMBBReference(*From)
+ << "-> To:" << printMBBReference(*To) << " = "
+ << Dist.value_or(-1).fmt() << "\n";
+ }
+ }
+
+ void printInterBlockDistances(raw_ostream &OS) const {
+ using MBBPair = std::pair<unsigned, unsigned>;
+ using Elem = std::pair<NextUseDistance, MBBPair>;
+ std::vector<Elem> SortedDistances;
+
+ for (const auto &[FromNum, Dsts] : InterBlockDistances) {
+ for (const auto &[ToNum, Dist] : Dsts) {
+ SortedDistances.emplace_back(Dist.Weighted, MBBPair(FromNum, ToNum));
+ }
+ }
+ std::sort(SortedDistances.begin(), SortedDistances.end(),
+ [](const auto &A, const auto &B) {
+ if (A.first != B.first)
+ return A.first < B.first;
+
+ if (A.second.first != B.second.first)
+ return A.second.first < B.second.first;
+
+ return A.second.second < B.second.second;
+ });
+
+ OS << "\n--------- InterBlockDistances -------- {\n";
+ for (const Elem &E : SortedDistances) {
+
+ OS << " bb." << E.second.first << " -> bb." << E.second.second << ": ";
+ E.first.print(OS);
+ OS << '\n';
+ }
+ OS << "}\n";
+ }
+
+ LLVM_DUMP_METHOD void dumpInterBlockDistances() const {
+ printInterBlockDistances(dbgs());
+ }
+
+ void printAllDistances() {
+ auto getRegNextUseDistance =
+ [this](Register DefReg) -> std::optional<NextUseDistance> {
+ const MachineInstr &DefMI = *MRI->def_instr_begin(DefReg);
+
+ SmallVector<const MachineOperand *> Uses;
+ for (MachineOperand &UseMO : MRI->use_nodbg_operands(DefReg))
+ Uses.push_back(&UseMO);
+
+ return getShortestDistance(DefReg, DefMI, Uses);
+ };
+
+ for (const MachineBasicBlock &MBB : *MF) {
+ for (const MachineInstr &MI : MBB) {
+ for (const MachineOperand &MO : MI.operands()) {
+ if (!MO.isReg() || MO.isUse())
+ continue;
+
+ Register Reg = MO.getReg();
+ if (Reg.isPhysical() || TRI->isAGPR(*MRI, Reg))
----------------
arsenm wrote:
Shouldn't skip AGPR
https://github.com/llvm/llvm-project/pull/178873
More information about the llvm-commits
mailing list