Skip to content

Commit e6e39fa

Browse files
committed
move partial-order and sorting to utils
The partial ordering visitor is used for block sorting and structurizing, and the sorting is required for both structurizing and normal SPIR-V emission. The reason why we sort after structurizing, then all target in prepare functions is that LLVM requires the MIR to be correct after each pass. So if we don't sort after the structurizer, then we fail this requirement. But structurizing is only required for vulkan env, meaning we also need a path to sort non-vulkan SPIR-V, as the sorting is also required in the general SPIR-V spec. Signed-off-by: Nathan Gauër <[email protected]>
1 parent 69831f6 commit e6e39fa

9 files changed

+272
-246
lines changed

llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,10 @@ SPIRVPrepareFunctions::removeAggregateTypesFromSignature(Function *F) {
546546

547547
bool SPIRVPrepareFunctions::runOnModule(Module &M) {
548548
bool Changed = false;
549-
for (Function &F : M)
549+
for (Function &F : M) {
550550
Changed |= substituteIntrinsicCalls(&F);
551+
Changed |= sortBlocks(F);
552+
}
551553

552554
std::vector<Function *> FuncsWorklist;
553555
for (auto &F : M)

llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp

Lines changed: 1 addition & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
3030
#include <queue>
3131
#include <stack>
32+
#include <unordered_set>
3233

3334
using namespace llvm;
3435
using namespace SPIRV;
@@ -42,184 +43,6 @@ namespace {
4243
using BlockSet = std::unordered_set<BasicBlock *>;
4344
using Edge = std::pair<BasicBlock *, BasicBlock *>;
4445

45-
// This class implements a partial ordering visitor, which visits a cyclic graph
46-
// in natural topological-like ordering. Topological ordering is not defined for
47-
// directed graphs with cycles, so this assumes cycles are a single node, and
48-
// ignores back-edges. The cycle is visited from the entry in the same
49-
// topological-like ordering.
50-
//
51-
// This means once we visit a node, we know all the possible ancestors have been
52-
// visited.
53-
//
54-
// clang-format off
55-
//
56-
// Given this graph:
57-
//
58-
// ,-> B -\
59-
// A -+ +---> D ----> E -> F -> G -> H
60-
// `-> C -/ ^ |
61-
// +-----------------+
62-
//
63-
// Visit order is:
64-
// A, [B, C in any order], D, E, F, G, H
65-
//
66-
// clang-format on
67-
//
68-
// Changing the function CFG between the construction of the visitor and
69-
// visiting is undefined. The visitor can be reused, but if the CFG is updated,
70-
// the visitor must be rebuilt.
71-
class PartialOrderingVisitor {
72-
DomTreeBuilder::BBDomTree DT;
73-
LoopInfo LI;
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 = {};
86-
87-
// Get all basic-blocks reachable from Start.
88-
BlockSet getReachableFrom(BasicBlock *Start) {
89-
std::queue<BasicBlock *> ToVisit;
90-
ToVisit.push(Start);
91-
92-
BlockSet Output;
93-
while (ToVisit.size() != 0) {
94-
BasicBlock *BB = ToVisit.front();
95-
ToVisit.pop();
96-
97-
if (Output.count(BB) != 0)
98-
continue;
99-
Output.insert(BB);
100-
101-
for (BasicBlock *Successor : successors(BB)) {
102-
if (DT.dominates(Successor, BB))
103-
continue;
104-
ToVisit.push(Successor);
105-
}
106-
}
107-
108-
return Output;
109-
}
110-
111-
size_t visit(BasicBlock *BB, size_t Rank) {
112-
if (Visited.count(BB) != 0)
113-
return Rank;
114-
115-
Loop *L = LI.getLoopFor(BB);
116-
const bool isLoopHeader = LI.isLoopHeader(BB);
117-
118-
if (BlockToOrder.count(BB) == 0) {
119-
OrderInfo Info = {Rank, Visited.size()};
120-
BlockToOrder.emplace(BB, Info);
121-
} else {
122-
BlockToOrder[BB].Rank = std::max(BlockToOrder[BB].Rank, Rank);
123-
}
124-
125-
for (BasicBlock *Predecessor : predecessors(BB)) {
126-
if (isLoopHeader && L->contains(Predecessor)) {
127-
continue;
128-
}
129-
130-
if (BlockToOrder.count(Predecessor) == 0) {
131-
return Rank;
132-
}
133-
}
134-
135-
Visited.insert(BB);
136-
137-
SmallVector<BasicBlock *, 2> OtherSuccessors;
138-
SmallVector<BasicBlock *, 2> LoopSuccessors;
139-
140-
for (BasicBlock *Successor : successors(BB)) {
141-
// Ignoring back-edges.
142-
if (DT.dominates(Successor, BB))
143-
continue;
144-
145-
if (isLoopHeader && L->contains(Successor)) {
146-
LoopSuccessors.push_back(Successor);
147-
} else
148-
OtherSuccessors.push_back(Successor);
149-
}
150-
151-
for (BasicBlock *BB : LoopSuccessors)
152-
Rank = std::max(Rank, visit(BB, Rank + 1));
153-
154-
size_t OutputRank = Rank;
155-
for (BasicBlock *Item : OtherSuccessors)
156-
OutputRank = std::max(OutputRank, visit(Item, Rank + 1));
157-
return OutputRank;
158-
};
159-
160-
public:
161-
// Build the visitor to operate on the function F.
162-
PartialOrderingVisitor(Function &F) {
163-
DT.recalculate(F);
164-
LI = LoopInfo(DT);
165-
166-
visit(&*F.begin(), 0);
167-
168-
Order.reserve(F.size());
169-
for (auto &[BB, Info] : BlockToOrder)
170-
Order.emplace_back(BB);
171-
172-
std::sort(
173-
Order.begin(), Order.end(),
174-
[&](const auto &LHS, const auto &RHS) { return compare(LHS, RHS); });
175-
}
176-
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;
183-
}
184-
185-
// Visit the function starting from the basic block |Start|, and calling |Op|
186-
// on each visited BB. This traversal ignores back-edges, meaning this won't
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.
192-
void partialOrderVisit(BasicBlock &Start,
193-
std::function<bool(BasicBlock *)> Op) {
194-
BlockSet Reachable = getReachableFrom(&Start);
195-
assert(BlockToOrder.count(&Start) != 0);
196-
197-
// Skipping blocks with a rank inferior to |Start|'s rank.
198-
auto It = Order.begin();
199-
while (It != Order.end() && *It != &Start)
200-
++It;
201-
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());
205-
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) {
213-
continue;
214-
}
215-
216-
if (!Op(*It)) {
217-
EndRank = BlockToOrder[*It].Rank;
218-
}
219-
}
220-
}
221-
};
222-
22346
// Helper function to do a partial order visit from the block |Start|, calling
22447
// |Op| on each visited node.
22548
void partialOrderVisit(BasicBlock &Start,
@@ -1318,44 +1141,6 @@ class SPIRVStructurizer : public FunctionPass {
13181141
return Modified;
13191142
}
13201143

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-
13591144
public:
13601145
static char ID;
13611146

0 commit comments

Comments
 (0)