@@ -71,9 +71,18 @@ using Edge = std::pair<BasicBlock *, BasicBlock *>;
71
71
class PartialOrderingVisitor {
72
72
DomTreeBuilder::BBDomTree DT;
73
73
LoopInfo LI;
74
- BlockSet Visited;
75
- std::unordered_map<BasicBlock *, size_t > B2R;
76
- std::vector<std::pair<BasicBlock *, size_t >> Order;
74
+ BlockSet Visited = {};
75
+
76
+ struct OrderInfo {
77
+ size_t Rank;
78
+ size_t TraversalIndex;
79
+ };
80
+
81
+ using BlockToOrderInfoMap = std::unordered_map<BasicBlock *, OrderInfo>;
82
+ BlockToOrderInfoMap BlockToOrder;
83
+
84
+ // std::unordered_map<BasicBlock *, std::pair<size_t, size_t>> B2R = {};
85
+ std::vector<BasicBlock *> Order = {};
77
86
78
87
// Get all basic-blocks reachable from Start.
79
88
BlockSet getReachableFrom (BasicBlock *Start) {
@@ -106,18 +115,19 @@ class PartialOrderingVisitor {
106
115
Loop *L = LI.getLoopFor (BB);
107
116
const bool isLoopHeader = LI.isLoopHeader (BB);
108
117
109
- if (B2R.count (BB) == 0 ) {
110
- B2R.emplace (BB, Rank);
118
+ if (BlockToOrder.count (BB) == 0 ) {
119
+ OrderInfo Info = {Rank, Visited.size ()};
120
+ BlockToOrder.emplace (BB, Info);
111
121
} else {
112
- B2R [BB] = std::max (B2R [BB], Rank);
122
+ BlockToOrder [BB]. Rank = std::max (BlockToOrder [BB]. Rank , Rank);
113
123
}
114
124
115
125
for (BasicBlock *Predecessor : predecessors (BB)) {
116
126
if (isLoopHeader && L->contains (Predecessor)) {
117
127
continue ;
118
128
}
119
129
120
- if (B2R .count (Predecessor) == 0 ) {
130
+ if (BlockToOrder .count (Predecessor) == 0 ) {
121
131
return Rank;
122
132
}
123
133
}
@@ -155,45 +165,56 @@ class PartialOrderingVisitor {
155
165
156
166
visit (&*F.begin (), 0 );
157
167
158
- for (auto &[BB, Rank] : B2R)
159
- Order.emplace_back (BB, Rank);
168
+ Order.reserve (F.size ());
169
+ for (auto &[BB, Info] : BlockToOrder)
170
+ Order.emplace_back (BB);
160
171
161
- std::sort (Order.begin (), Order.end (), [](const auto &LHS, const auto &RHS) {
162
- return LHS.second < RHS.second ;
163
- });
164
-
165
- for (size_t i = 0 ; i < Order.size (); i++)
166
- B2R[Order[i].first ] = i;
172
+ std::sort (
173
+ Order.begin (), Order.end (),
174
+ [&](const auto &LHS, const auto &RHS) { return compare (LHS, RHS); });
167
175
}
168
176
169
- size_t getRank (BasicBlock *BB) {
170
- return B2R[BB];
177
+ bool compare (const BasicBlock *LHS, const BasicBlock *RHS) const {
178
+ const OrderInfo &InfoLHS = BlockToOrder.at (const_cast <BasicBlock *>(LHS));
179
+ const OrderInfo &InfoRHS = BlockToOrder.at (const_cast <BasicBlock *>(RHS));
180
+ if (InfoLHS.Rank != InfoRHS.Rank )
181
+ return InfoLHS.Rank < InfoRHS.Rank ;
182
+ return InfoLHS.TraversalIndex < InfoRHS.TraversalIndex ;
171
183
}
172
184
173
185
// Visit the function starting from the basic block |Start|, and calling |Op|
174
186
// on each visited BB. This traversal ignores back-edges, meaning this won't
175
187
// visit a node to which |Start| is not an ancestor.
188
+ // If Op returns |true|, the visitor continues. If |Op| returns false, the
189
+ // visitor will stop at that rank. This means if 2 nodes share the same rank,
190
+ // and Op returns false when visiting the first, the second will be visited
191
+ // afterwards. But none of their successors will.
176
192
void partialOrderVisit (BasicBlock &Start,
177
193
std::function<bool (BasicBlock *)> Op) {
178
194
BlockSet Reachable = getReachableFrom (&Start);
179
- assert (B2R.count (&Start) != 0 );
180
- size_t Rank = Order[B2R[&Start]].second ;
195
+ assert (BlockToOrder.count (&Start) != 0 );
181
196
197
+ // Skipping blocks with a rank inferior to |Start|'s rank.
182
198
auto It = Order.begin ();
183
- while (It != Order.end () && It-> second < Rank )
199
+ while (It != Order.end () && *It != &Start )
184
200
++It;
185
201
186
- if (It == Order.end ())
187
- return ;
202
+ // This is unexpected. Worst case |Start| is the last block,
203
+ // so It should point to the last block, not past-end.
204
+ assert (It != Order.end ());
188
205
189
- size_t EndRank = Order.rbegin ()->second + 1 ;
190
- for (; It != Order.end () && It->second <= EndRank; ++It) {
191
- if (Reachable.count (It->first ) == 0 ) {
206
+ // By default, there is no rank limit. Setting it to the maximum value.
207
+ std::optional<size_t > EndRank = std::nullopt;
208
+ for (; It != Order.end (); ++It) {
209
+ if (EndRank.has_value () && BlockToOrder[*It].Rank > *EndRank)
210
+ break ;
211
+
212
+ if (Reachable.count (*It) == 0 ) {
192
213
continue ;
193
214
}
194
215
195
- if (!Op (It-> first )) {
196
- EndRank = It-> second ;
216
+ if (!Op (*It )) {
217
+ EndRank = BlockToOrder[*It]. Rank ;
197
218
}
198
219
}
199
220
}
@@ -641,7 +662,6 @@ class SPIRVStructurizer : public FunctionPass {
641
662
auto NewExit = BasicBlock::Create (F.getContext (), " new.exit" , &F);
642
663
IRBuilder<> ExitBuilder (NewExit);
643
664
644
- BlockSet SeenDst;
645
665
std::vector<BasicBlock *> Dsts;
646
666
std::unordered_map<BasicBlock *, ConstantInt *> DstToIndex;
647
667
@@ -846,10 +866,10 @@ class SPIRVStructurizer : public FunctionPass {
846
866
std::sort (MergeInstructions.begin (), MergeInstructions.end (),
847
867
[&Visitor](Instruction *Left, Instruction *Right) {
848
868
if (Left == Right)
849
- return true ;
869
+ return false ;
850
870
BasicBlock *RightMerge = getDesignatedMergeBlock (Right);
851
871
BasicBlock *LeftMerge = getDesignatedMergeBlock (Left);
852
- return Visitor.getRank (RightMerge) >= Visitor. getRank ( LeftMerge);
872
+ return ! Visitor.compare (RightMerge, LeftMerge);
853
873
});
854
874
855
875
for (Instruction *I : MergeInstructions) {
@@ -1041,8 +1061,6 @@ class SPIRVStructurizer : public FunctionPass {
1041
1061
assert (Node->Parent ->Header && Node->Parent ->Merge );
1042
1062
1043
1063
BlockSet ConstructBlocks = getConstructBlocks (S, Node);
1044
- BlockSet ParentBlocks = getConstructBlocks (S, Node->Parent );
1045
-
1046
1064
auto Edges = getExitsFrom (ConstructBlocks, *Node->Header );
1047
1065
1048
1066
// No edges exiting the construct.
@@ -1300,6 +1318,44 @@ class SPIRVStructurizer : public FunctionPass {
1300
1318
return Modified;
1301
1319
}
1302
1320
1321
+ // Sort blocks in a partial ordering, so each block is after all its
1322
+ // dominators. This should match both the SPIR-V and the MIR requirements.
1323
+ bool sortBlocks (Function &F) {
1324
+ if (F.size () == 0 )
1325
+ return false ;
1326
+
1327
+ bool Modified = false ;
1328
+
1329
+ std::vector<BasicBlock *> Order;
1330
+ Order.reserve (F.size ());
1331
+
1332
+ PartialOrderingVisitor Visitor (F);
1333
+ Visitor.partialOrderVisit (*F.begin (), [&Order](BasicBlock *Block) {
1334
+ Order.push_back (Block);
1335
+ return true ;
1336
+ });
1337
+
1338
+ assert (&*F.begin () == Order[0 ]);
1339
+ BasicBlock *LastBlock = &*F.begin ();
1340
+ for (BasicBlock *BB : Order) {
1341
+ if (BB != LastBlock && &*LastBlock->getNextNode () != BB) {
1342
+ Modified = true ;
1343
+ BB->moveAfter (LastBlock);
1344
+ }
1345
+ LastBlock = BB;
1346
+ }
1347
+ #if 0
1348
+ for (auto It = Order.begin() + 1; It != Order.end(); ++It) {
1349
+ if (*It != &*LastBlock->getNextNode()) {
1350
+ Modified = true;
1351
+ (*It)->moveAfter(LastBlock);
1352
+ }
1353
+ LastBlock = *It;
1354
+ }
1355
+ #endif
1356
+ return Modified;
1357
+ }
1358
+
1303
1359
public:
1304
1360
static char ID;
1305
1361
@@ -1367,6 +1423,9 @@ class SPIRVStructurizer : public FunctionPass {
1367
1423
// branches with 1 or 2 returning edges. Adding a header for those.
1368
1424
Modified |= addHeaderToRemainingDivergentDAG (F);
1369
1425
1426
+ // STEP 9: sort basic blocks to match both the LLVM & SPIR-V requirements.
1427
+ Modified |= sortBlocks (F);
1428
+
1370
1429
return Modified;
1371
1430
}
1372
1431
0 commit comments