Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:33

0001 //===- RegAllocPBQP.h -------------------------------------------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file defines the PBQPBuilder interface, for classes which build PBQP
0010 // instances to represent register allocation problems, and the RegAllocPBQP
0011 // interface.
0012 //
0013 //===----------------------------------------------------------------------===//
0014 
0015 #ifndef LLVM_CODEGEN_REGALLOCPBQP_H
0016 #define LLVM_CODEGEN_REGALLOCPBQP_H
0017 
0018 #include "llvm/ADT/DenseMap.h"
0019 #include "llvm/ADT/Hashing.h"
0020 #include "llvm/CodeGen/PBQP/CostAllocator.h"
0021 #include "llvm/CodeGen/PBQP/Graph.h"
0022 #include "llvm/CodeGen/PBQP/Math.h"
0023 #include "llvm/CodeGen/PBQP/ReductionRules.h"
0024 #include "llvm/CodeGen/PBQP/Solution.h"
0025 #include "llvm/CodeGen/Register.h"
0026 #include "llvm/MC/MCRegister.h"
0027 #include "llvm/Support/ErrorHandling.h"
0028 #include <algorithm>
0029 #include <cassert>
0030 #include <cstddef>
0031 #include <limits>
0032 #include <memory>
0033 #include <set>
0034 #include <vector>
0035 
0036 namespace llvm {
0037 
0038 class FunctionPass;
0039 class LiveIntervals;
0040 class MachineBlockFrequencyInfo;
0041 class MachineFunction;
0042 class raw_ostream;
0043 
0044 namespace PBQP {
0045 namespace RegAlloc {
0046 
0047 /// Spill option index.
0048 inline unsigned getSpillOptionIdx() { return 0; }
0049 
0050 /// Metadata to speed allocatability test.
0051 ///
0052 /// Keeps track of the number of infinities in each row and column.
0053 class MatrixMetadata {
0054 public:
0055   MatrixMetadata(const Matrix& M)
0056     : UnsafeRows(new bool[M.getRows() - 1]()),
0057       UnsafeCols(new bool[M.getCols() - 1]()) {
0058     unsigned* ColCounts = new unsigned[M.getCols() - 1]();
0059 
0060     for (unsigned i = 1; i < M.getRows(); ++i) {
0061       unsigned RowCount = 0;
0062       for (unsigned j = 1; j < M.getCols(); ++j) {
0063         if (M[i][j] == std::numeric_limits<PBQPNum>::infinity()) {
0064           ++RowCount;
0065           ++ColCounts[j - 1];
0066           UnsafeRows[i - 1] = true;
0067           UnsafeCols[j - 1] = true;
0068         }
0069       }
0070       WorstRow = std::max(WorstRow, RowCount);
0071     }
0072     unsigned WorstColCountForCurRow =
0073       *std::max_element(ColCounts, ColCounts + M.getCols() - 1);
0074     WorstCol = std::max(WorstCol, WorstColCountForCurRow);
0075     delete[] ColCounts;
0076   }
0077 
0078   MatrixMetadata(const MatrixMetadata &) = delete;
0079   MatrixMetadata &operator=(const MatrixMetadata &) = delete;
0080 
0081   unsigned getWorstRow() const { return WorstRow; }
0082   unsigned getWorstCol() const { return WorstCol; }
0083   const bool* getUnsafeRows() const { return UnsafeRows.get(); }
0084   const bool* getUnsafeCols() const { return UnsafeCols.get(); }
0085 
0086 private:
0087   unsigned WorstRow = 0;
0088   unsigned WorstCol = 0;
0089   std::unique_ptr<bool[]> UnsafeRows;
0090   std::unique_ptr<bool[]> UnsafeCols;
0091 };
0092 
0093 /// Holds a vector of the allowed physical regs for a vreg.
0094 class AllowedRegVector {
0095   friend hash_code hash_value(const AllowedRegVector &);
0096 
0097 public:
0098   AllowedRegVector() = default;
0099   AllowedRegVector(AllowedRegVector &&) = default;
0100 
0101   AllowedRegVector(const std::vector<MCRegister> &OptVec)
0102       : NumOpts(OptVec.size()), Opts(new MCRegister[NumOpts]) {
0103     std::copy(OptVec.begin(), OptVec.end(), Opts.get());
0104   }
0105 
0106   unsigned size() const { return NumOpts; }
0107   MCRegister operator[](size_t I) const { return Opts[I]; }
0108 
0109   bool operator==(const AllowedRegVector &Other) const {
0110     if (NumOpts != Other.NumOpts)
0111       return false;
0112     return std::equal(Opts.get(), Opts.get() + NumOpts, Other.Opts.get());
0113   }
0114 
0115   bool operator!=(const AllowedRegVector &Other) const {
0116     return !(*this == Other);
0117   }
0118 
0119 private:
0120   unsigned NumOpts = 0;
0121   std::unique_ptr<MCRegister[]> Opts;
0122 };
0123 
0124 inline hash_code hash_value(const AllowedRegVector &OptRegs) {
0125   MCRegister *OStart = OptRegs.Opts.get();
0126   MCRegister *OEnd = OptRegs.Opts.get() + OptRegs.NumOpts;
0127   return hash_combine(OptRegs.NumOpts,
0128                       hash_combine_range(OStart, OEnd));
0129 }
0130 
0131 /// Holds graph-level metadata relevant to PBQP RA problems.
0132 class GraphMetadata {
0133 private:
0134   using AllowedRegVecPool = ValuePool<AllowedRegVector>;
0135 
0136 public:
0137   using AllowedRegVecRef = AllowedRegVecPool::PoolRef;
0138 
0139   GraphMetadata(MachineFunction &MF,
0140                 LiveIntervals &LIS,
0141                 MachineBlockFrequencyInfo &MBFI)
0142     : MF(MF), LIS(LIS), MBFI(MBFI) {}
0143 
0144   MachineFunction &MF;
0145   LiveIntervals &LIS;
0146   MachineBlockFrequencyInfo &MBFI;
0147 
0148   void setNodeIdForVReg(Register VReg, GraphBase::NodeId NId) {
0149     VRegToNodeId[VReg.id()] = NId;
0150   }
0151 
0152   GraphBase::NodeId getNodeIdForVReg(Register VReg) const {
0153     auto VRegItr = VRegToNodeId.find(VReg);
0154     if (VRegItr == VRegToNodeId.end())
0155       return GraphBase::invalidNodeId();
0156     return VRegItr->second;
0157   }
0158 
0159   AllowedRegVecRef getAllowedRegs(AllowedRegVector Allowed) {
0160     return AllowedRegVecs.getValue(std::move(Allowed));
0161   }
0162 
0163 private:
0164   DenseMap<Register, GraphBase::NodeId> VRegToNodeId;
0165   AllowedRegVecPool AllowedRegVecs;
0166 };
0167 
0168 /// Holds solver state and other metadata relevant to each PBQP RA node.
0169 class NodeMetadata {
0170 public:
0171   using AllowedRegVector = RegAlloc::AllowedRegVector;
0172 
0173   // The node's reduction state. The order in this enum is important,
0174   // as it is assumed nodes can only progress up (i.e. towards being
0175   // optimally reducible) when reducing the graph.
0176   using ReductionState = enum {
0177     Unprocessed,
0178     NotProvablyAllocatable,
0179     ConservativelyAllocatable,
0180     OptimallyReducible
0181   };
0182 
0183   NodeMetadata() = default;
0184 
0185   NodeMetadata(const NodeMetadata &Other)
0186       : RS(Other.RS), NumOpts(Other.NumOpts), DeniedOpts(Other.DeniedOpts),
0187         OptUnsafeEdges(new unsigned[NumOpts]), VReg(Other.VReg),
0188         AllowedRegs(Other.AllowedRegs)
0189 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
0190         ,
0191         everConservativelyAllocatable(Other.everConservativelyAllocatable)
0192 #endif
0193   {
0194     if (NumOpts > 0) {
0195       std::copy(&Other.OptUnsafeEdges[0], &Other.OptUnsafeEdges[NumOpts],
0196                 &OptUnsafeEdges[0]);
0197     }
0198   }
0199 
0200   NodeMetadata(NodeMetadata &&) = default;
0201   NodeMetadata& operator=(NodeMetadata &&) = default;
0202 
0203   void setVReg(Register VReg) { this->VReg = VReg; }
0204   Register getVReg() const { return VReg; }
0205 
0206   void setAllowedRegs(GraphMetadata::AllowedRegVecRef AllowedRegs) {
0207     this->AllowedRegs = std::move(AllowedRegs);
0208   }
0209   const AllowedRegVector& getAllowedRegs() const { return *AllowedRegs; }
0210 
0211   void setup(const Vector& Costs) {
0212     NumOpts = Costs.getLength() - 1;
0213     OptUnsafeEdges = std::unique_ptr<unsigned[]>(new unsigned[NumOpts]());
0214   }
0215 
0216   ReductionState getReductionState() const { return RS; }
0217   void setReductionState(ReductionState RS) {
0218     assert(RS >= this->RS && "A node's reduction state can not be downgraded");
0219     this->RS = RS;
0220 
0221 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
0222     // Remember this state to assert later that a non-infinite register
0223     // option was available.
0224     if (RS == ConservativelyAllocatable)
0225       everConservativelyAllocatable = true;
0226 #endif
0227   }
0228 
0229   void handleAddEdge(const MatrixMetadata& MD, bool Transpose) {
0230     DeniedOpts += Transpose ? MD.getWorstRow() : MD.getWorstCol();
0231     const bool* UnsafeOpts =
0232       Transpose ? MD.getUnsafeCols() : MD.getUnsafeRows();
0233     for (unsigned i = 0; i < NumOpts; ++i)
0234       OptUnsafeEdges[i] += UnsafeOpts[i];
0235   }
0236 
0237   void handleRemoveEdge(const MatrixMetadata& MD, bool Transpose) {
0238     DeniedOpts -= Transpose ? MD.getWorstRow() : MD.getWorstCol();
0239     const bool* UnsafeOpts =
0240       Transpose ? MD.getUnsafeCols() : MD.getUnsafeRows();
0241     for (unsigned i = 0; i < NumOpts; ++i)
0242       OptUnsafeEdges[i] -= UnsafeOpts[i];
0243   }
0244 
0245   bool isConservativelyAllocatable() const {
0246     return (DeniedOpts < NumOpts) ||
0247       (std::find(&OptUnsafeEdges[0], &OptUnsafeEdges[NumOpts], 0) !=
0248        &OptUnsafeEdges[NumOpts]);
0249   }
0250 
0251 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
0252   bool wasConservativelyAllocatable() const {
0253     return everConservativelyAllocatable;
0254   }
0255 #endif
0256 
0257 private:
0258   ReductionState RS = Unprocessed;
0259   unsigned NumOpts = 0;
0260   unsigned DeniedOpts = 0;
0261   std::unique_ptr<unsigned[]> OptUnsafeEdges;
0262   Register VReg;
0263   GraphMetadata::AllowedRegVecRef AllowedRegs;
0264 
0265 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
0266   bool everConservativelyAllocatable = false;
0267 #endif
0268 };
0269 
0270 class RegAllocSolverImpl {
0271 private:
0272   using RAMatrix = MDMatrix<MatrixMetadata>;
0273 
0274 public:
0275   using RawVector = PBQP::Vector;
0276   using RawMatrix = PBQP::Matrix;
0277   using Vector = PBQP::Vector;
0278   using Matrix = RAMatrix;
0279   using CostAllocator = PBQP::PoolCostAllocator<Vector, Matrix>;
0280 
0281   using NodeId = GraphBase::NodeId;
0282   using EdgeId = GraphBase::EdgeId;
0283 
0284   using NodeMetadata = RegAlloc::NodeMetadata;
0285   struct EdgeMetadata {};
0286   using GraphMetadata = RegAlloc::GraphMetadata;
0287 
0288   using Graph = PBQP::Graph<RegAllocSolverImpl>;
0289 
0290   RegAllocSolverImpl(Graph &G) : G(G) {}
0291 
0292   Solution solve() {
0293     G.setSolver(*this);
0294     Solution S;
0295     setup();
0296     S = backpropagate(G, reduce());
0297     G.unsetSolver();
0298     return S;
0299   }
0300 
0301   void handleAddNode(NodeId NId) {
0302     assert(G.getNodeCosts(NId).getLength() > 1 &&
0303            "PBQP Graph should not contain single or zero-option nodes");
0304     G.getNodeMetadata(NId).setup(G.getNodeCosts(NId));
0305   }
0306 
0307   void handleRemoveNode(NodeId NId) {}
0308   void handleSetNodeCosts(NodeId NId, const Vector& newCosts) {}
0309 
0310   void handleAddEdge(EdgeId EId) {
0311     handleReconnectEdge(EId, G.getEdgeNode1Id(EId));
0312     handleReconnectEdge(EId, G.getEdgeNode2Id(EId));
0313   }
0314 
0315   void handleDisconnectEdge(EdgeId EId, NodeId NId) {
0316     NodeMetadata& NMd = G.getNodeMetadata(NId);
0317     const MatrixMetadata& MMd = G.getEdgeCosts(EId).getMetadata();
0318     NMd.handleRemoveEdge(MMd, NId == G.getEdgeNode2Id(EId));
0319     promote(NId, NMd);
0320   }
0321 
0322   void handleReconnectEdge(EdgeId EId, NodeId NId) {
0323     NodeMetadata& NMd = G.getNodeMetadata(NId);
0324     const MatrixMetadata& MMd = G.getEdgeCosts(EId).getMetadata();
0325     NMd.handleAddEdge(MMd, NId == G.getEdgeNode2Id(EId));
0326   }
0327 
0328   void handleUpdateCosts(EdgeId EId, const Matrix& NewCosts) {
0329     NodeId N1Id = G.getEdgeNode1Id(EId);
0330     NodeId N2Id = G.getEdgeNode2Id(EId);
0331     NodeMetadata& N1Md = G.getNodeMetadata(N1Id);
0332     NodeMetadata& N2Md = G.getNodeMetadata(N2Id);
0333     bool Transpose = N1Id != G.getEdgeNode1Id(EId);
0334 
0335     // Metadata are computed incrementally. First, update them
0336     // by removing the old cost.
0337     const MatrixMetadata& OldMMd = G.getEdgeCosts(EId).getMetadata();
0338     N1Md.handleRemoveEdge(OldMMd, Transpose);
0339     N2Md.handleRemoveEdge(OldMMd, !Transpose);
0340 
0341     // And update now the metadata with the new cost.
0342     const MatrixMetadata& MMd = NewCosts.getMetadata();
0343     N1Md.handleAddEdge(MMd, Transpose);
0344     N2Md.handleAddEdge(MMd, !Transpose);
0345 
0346     // As the metadata may have changed with the update, the nodes may have
0347     // become ConservativelyAllocatable or OptimallyReducible.
0348     promote(N1Id, N1Md);
0349     promote(N2Id, N2Md);
0350   }
0351 
0352 private:
0353   void promote(NodeId NId, NodeMetadata& NMd) {
0354     if (G.getNodeDegree(NId) == 3) {
0355       // This node is becoming optimally reducible.
0356       moveToOptimallyReducibleNodes(NId);
0357     } else if (NMd.getReductionState() ==
0358                NodeMetadata::NotProvablyAllocatable &&
0359                NMd.isConservativelyAllocatable()) {
0360       // This node just became conservatively allocatable.
0361       moveToConservativelyAllocatableNodes(NId);
0362     }
0363   }
0364 
0365   void removeFromCurrentSet(NodeId NId) {
0366     switch (G.getNodeMetadata(NId).getReductionState()) {
0367     case NodeMetadata::Unprocessed: break;
0368     case NodeMetadata::OptimallyReducible:
0369       assert(OptimallyReducibleNodes.find(NId) !=
0370              OptimallyReducibleNodes.end() &&
0371              "Node not in optimally reducible set.");
0372       OptimallyReducibleNodes.erase(NId);
0373       break;
0374     case NodeMetadata::ConservativelyAllocatable:
0375       assert(ConservativelyAllocatableNodes.find(NId) !=
0376              ConservativelyAllocatableNodes.end() &&
0377              "Node not in conservatively allocatable set.");
0378       ConservativelyAllocatableNodes.erase(NId);
0379       break;
0380     case NodeMetadata::NotProvablyAllocatable:
0381       assert(NotProvablyAllocatableNodes.find(NId) !=
0382              NotProvablyAllocatableNodes.end() &&
0383              "Node not in not-provably-allocatable set.");
0384       NotProvablyAllocatableNodes.erase(NId);
0385       break;
0386     }
0387   }
0388 
0389   void moveToOptimallyReducibleNodes(NodeId NId) {
0390     removeFromCurrentSet(NId);
0391     OptimallyReducibleNodes.insert(NId);
0392     G.getNodeMetadata(NId).setReductionState(
0393       NodeMetadata::OptimallyReducible);
0394   }
0395 
0396   void moveToConservativelyAllocatableNodes(NodeId NId) {
0397     removeFromCurrentSet(NId);
0398     ConservativelyAllocatableNodes.insert(NId);
0399     G.getNodeMetadata(NId).setReductionState(
0400       NodeMetadata::ConservativelyAllocatable);
0401   }
0402 
0403   void moveToNotProvablyAllocatableNodes(NodeId NId) {
0404     removeFromCurrentSet(NId);
0405     NotProvablyAllocatableNodes.insert(NId);
0406     G.getNodeMetadata(NId).setReductionState(
0407       NodeMetadata::NotProvablyAllocatable);
0408   }
0409 
0410   void setup() {
0411     // Set up worklists.
0412     for (auto NId : G.nodeIds()) {
0413       if (G.getNodeDegree(NId) < 3)
0414         moveToOptimallyReducibleNodes(NId);
0415       else if (G.getNodeMetadata(NId).isConservativelyAllocatable())
0416         moveToConservativelyAllocatableNodes(NId);
0417       else
0418         moveToNotProvablyAllocatableNodes(NId);
0419     }
0420   }
0421 
0422   // Compute a reduction order for the graph by iteratively applying PBQP
0423   // reduction rules. Locally optimal rules are applied whenever possible (R0,
0424   // R1, R2). If no locally-optimal rules apply then any conservatively
0425   // allocatable node is reduced. Finally, if no conservatively allocatable
0426   // node exists then the node with the lowest spill-cost:degree ratio is
0427   // selected.
0428   std::vector<GraphBase::NodeId> reduce() {
0429     assert(!G.empty() && "Cannot reduce empty graph.");
0430 
0431     using NodeId = GraphBase::NodeId;
0432     std::vector<NodeId> NodeStack;
0433 
0434     // Consume worklists.
0435     while (true) {
0436       if (!OptimallyReducibleNodes.empty()) {
0437         NodeSet::iterator NItr = OptimallyReducibleNodes.begin();
0438         NodeId NId = *NItr;
0439         OptimallyReducibleNodes.erase(NItr);
0440         NodeStack.push_back(NId);
0441         switch (G.getNodeDegree(NId)) {
0442         case 0:
0443           break;
0444         case 1:
0445           applyR1(G, NId);
0446           break;
0447         case 2:
0448           applyR2(G, NId);
0449           break;
0450         default: llvm_unreachable("Not an optimally reducible node.");
0451         }
0452       } else if (!ConservativelyAllocatableNodes.empty()) {
0453         // Conservatively allocatable nodes will never spill. For now just
0454         // take the first node in the set and push it on the stack. When we
0455         // start optimizing more heavily for register preferencing, it may
0456         // would be better to push nodes with lower 'expected' or worst-case
0457         // register costs first (since early nodes are the most
0458         // constrained).
0459         NodeSet::iterator NItr = ConservativelyAllocatableNodes.begin();
0460         NodeId NId = *NItr;
0461         ConservativelyAllocatableNodes.erase(NItr);
0462         NodeStack.push_back(NId);
0463         G.disconnectAllNeighborsFromNode(NId);
0464       } else if (!NotProvablyAllocatableNodes.empty()) {
0465         NodeSet::iterator NItr = llvm::min_element(NotProvablyAllocatableNodes,
0466                                                    SpillCostComparator(G));
0467         NodeId NId = *NItr;
0468         NotProvablyAllocatableNodes.erase(NItr);
0469         NodeStack.push_back(NId);
0470         G.disconnectAllNeighborsFromNode(NId);
0471       } else
0472         break;
0473     }
0474 
0475     return NodeStack;
0476   }
0477 
0478   class SpillCostComparator {
0479   public:
0480     SpillCostComparator(const Graph& G) : G(G) {}
0481 
0482     bool operator()(NodeId N1Id, NodeId N2Id) {
0483       PBQPNum N1SC = G.getNodeCosts(N1Id)[0];
0484       PBQPNum N2SC = G.getNodeCosts(N2Id)[0];
0485       if (N1SC == N2SC)
0486         return G.getNodeDegree(N1Id) < G.getNodeDegree(N2Id);
0487       return N1SC < N2SC;
0488     }
0489 
0490   private:
0491     const Graph& G;
0492   };
0493 
0494   Graph& G;
0495   using NodeSet = std::set<NodeId>;
0496   NodeSet OptimallyReducibleNodes;
0497   NodeSet ConservativelyAllocatableNodes;
0498   NodeSet NotProvablyAllocatableNodes;
0499 };
0500 
0501 class PBQPRAGraph : public PBQP::Graph<RegAllocSolverImpl> {
0502 private:
0503   using BaseT = PBQP::Graph<RegAllocSolverImpl>;
0504 
0505 public:
0506   PBQPRAGraph(GraphMetadata Metadata) : BaseT(std::move(Metadata)) {}
0507 
0508   /// Dump this graph to dbgs().
0509   void dump() const;
0510 
0511   /// Dump this graph to an output stream.
0512   /// @param OS Output stream to print on.
0513   void dump(raw_ostream &OS) const;
0514 
0515   /// Print a representation of this graph in DOT format.
0516   /// @param OS Output stream to print on.
0517   void printDot(raw_ostream &OS) const;
0518 };
0519 
0520 inline Solution solve(PBQPRAGraph& G) {
0521   if (G.empty())
0522     return Solution();
0523   RegAllocSolverImpl RegAllocSolver(G);
0524   return RegAllocSolver.solve();
0525 }
0526 
0527 } // end namespace RegAlloc
0528 } // end namespace PBQP
0529 
0530 /// Create a PBQP register allocator instance.
0531 FunctionPass *
0532 createPBQPRegisterAllocator(char *customPassID = nullptr);
0533 
0534 } // end namespace llvm
0535 
0536 #endif // LLVM_CODEGEN_REGALLOCPBQP_H