File indexing completed on 2026-05-10 08:43:33
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
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
0048 inline unsigned getSpillOptionIdx() { return 0; }
0049
0050
0051
0052
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
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
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
0169 class NodeMetadata {
0170 public:
0171 using AllowedRegVector = RegAlloc::AllowedRegVector;
0172
0173
0174
0175
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
0223
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
0336
0337 const MatrixMetadata& OldMMd = G.getEdgeCosts(EId).getMetadata();
0338 N1Md.handleRemoveEdge(OldMMd, Transpose);
0339 N2Md.handleRemoveEdge(OldMMd, !Transpose);
0340
0341
0342 const MatrixMetadata& MMd = NewCosts.getMetadata();
0343 N1Md.handleAddEdge(MMd, Transpose);
0344 N2Md.handleAddEdge(MMd, !Transpose);
0345
0346
0347
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
0356 moveToOptimallyReducibleNodes(NId);
0357 } else if (NMd.getReductionState() ==
0358 NodeMetadata::NotProvablyAllocatable &&
0359 NMd.isConservativelyAllocatable()) {
0360
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
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
0423
0424
0425
0426
0427
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
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
0454
0455
0456
0457
0458
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
0509 void dump() const;
0510
0511
0512
0513 void dump(raw_ostream &OS) const;
0514
0515
0516
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 }
0528 }
0529
0530
0531 FunctionPass *
0532 createPBQPRegisterAllocator(char *customPassID = nullptr);
0533
0534 }
0535
0536 #endif