2626#include " mlir/Dialect/SPIRV/SPIRVOps.h"
2727#include " mlir/Dialect/SPIRV/SPIRVTypes.h"
2828#include " mlir/IR/Builders.h"
29+ #include " mlir/IR/RegionGraphTraits.h"
2930#include " mlir/Support/LogicalResult.h"
3031#include " mlir/Support/StringExtras.h"
32+ #include " llvm/ADT/DepthFirstIterator.h"
3133#include " llvm/ADT/Sequence.h"
3234#include " llvm/ADT/SmallPtrSet.h"
3335#include " llvm/ADT/SmallVector.h"
@@ -52,76 +54,35 @@ LogicalResult encodeInstructionInto(SmallVectorImpl<uint32_t> &binary,
5254 return success ();
5355}
5456
55- namespace {
56- // / A pre-order depth-first vistor for processing basic blocks.
57+ // / A pre-order depth-first visitor function for processing basic blocks.
5758// /
58- // / SPIR-V spec "2.16.1. Universal Validation Rules" requires that "the order
59- // / of blocks in a function must satisfy the rule that blocks appear before all
60- // / blocks they dominate." This can be achieved by a pre-order CFG traversal
61- // / algorithm. To make the serialization output more logical and readable to
62- // / human, we perform depth-first CFG traversal and delay the serialization of
63- // / the merge block and the continue block, if exists, until after all other
64- // / blocks have been processed.
59+ // / Visits the basic blocks starting from the given `headerBlock` in pre-order
60+ // / depth-first manner and calls `blockHandler` on each block. Skips handling
61+ // / blocks in the `skipBlocks` list. If `skipHeader` is true, `blockHandler`
62+ // / will not be invoked in `headerBlock` but still handles all `headerBlock`'s
63+ // / successors.
6564// /
66- // / This visitor is special tailored for SPIR-V functions, spv.selection or
67- // / spv.loop block serialization to satisfy SPIR-V validation rules. It should
68- // / not be used as a general depth-first block visitor.
69- class PrettyBlockOrderVisitor {
70- public:
71- using BlockHandlerType = llvm::function_ref<LogicalResult(Block *)>;
72-
73- // / Visits the basic blocks starting from the given `headerBlock`'s successors
74- // / in pre-order depth-first manner and calls `blockHandler` on each block.
75- // / Skips handling blocks in the `skipBlocks` list. If `headerBlock` is also
76- // / in `skipBlocks` list, still handles all its successors.
77- static LogicalResult visit (Block *headerBlock, BlockHandlerType blockHandler,
78- ArrayRef<Block *> skipBlocks = {}) {
79- return PrettyBlockOrderVisitor (blockHandler, skipBlocks)
80- .visitHeaderBlock (headerBlock);
81- }
82-
83- private:
84- PrettyBlockOrderVisitor (BlockHandlerType blockHandler,
85- ArrayRef<Block *> skipBlocks)
86- : blockHandler(blockHandler),
87- doneBlocks (skipBlocks.begin(), skipBlocks.end()) {}
88-
89- LogicalResult visitHeaderBlock (Block *header) {
90- // Skip processing the header block if requested.
91- if (!llvm::is_contained (doneBlocks, header)) {
92- if (failed (blockHandler (header)))
93- return failure ();
94- doneBlocks.insert (header);
95- }
96-
97- for (auto *successor : header->getSuccessors ()) {
98- if (failed (visitNormalBlock (successor)))
99- return failure ();
100- }
101-
102- return success ();
103- }
104-
105- LogicalResult visitNormalBlock (Block *block) {
106- if (doneBlocks.count (block))
107- return success ();
108-
65+ // / SPIR-V spec "2.16.1. Universal Validation Rules" requires that "the order
66+ // / of blocks in a function must satisfy the rule that blocks appear before
67+ // / all blocks they dominate." This can be achieved by a pre-order CFG
68+ // / traversal algorithm. To make the serialization output more logical and
69+ // / readable to human, we perform depth-first CFG traversal and delay the
70+ // / serialization of the merge block and the continue block, if exists, until
71+ // / after all other blocks have been processed.
72+ static LogicalResult visitInPrettyBlockOrder (
73+ Block *headerBlock, llvm::function_ref<LogicalResult(Block *)> blockHandler,
74+ bool skipHeader = false, ArrayRef<Block *> skipBlocks = {}) {
75+ llvm::df_iterator_default_set<Block *, 4 > doneBlocks;
76+ doneBlocks.insert (skipBlocks.begin (), skipBlocks.end ());
77+
78+ for (Block *block : llvm::depth_first_ext (headerBlock, doneBlocks)) {
79+ if (skipHeader && block == headerBlock)
80+ continue ;
10981 if (failed (blockHandler (block)))
11082 return failure ();
111- doneBlocks.insert (block);
112-
113- for (auto *successor : block->getSuccessors ()) {
114- if (failed (visitNormalBlock (successor)))
115- return failure ();
116- }
117-
118- return success ();
11983 }
120-
121- BlockHandlerType blockHandler;
122- SmallPtrSet<Block *, 4 > doneBlocks;
123- };
124- } // namespace
84+ return success ();
85+ }
12586
12687namespace {
12788
@@ -757,7 +718,7 @@ LogicalResult Serializer::processFuncOp(FuncOp op) {
757718 return op.emitError (" external function is unhandled" );
758719 }
759720
760- if (failed (PrettyBlockOrderVisitor::visit (
721+ if (failed (visitInPrettyBlockOrder (
761722 &op.front (), [&](Block *block) { return processBlock (block); })))
762723 return failure ();
763724
@@ -1519,9 +1480,9 @@ LogicalResult Serializer::processSelectionOp(spirv::SelectionOp selectionOp) {
15191480 // Process all blocks with a depth-first visitor starting from the header
15201481 // block. The selection header block and merge block are skipped by this
15211482 // visitor.
1522- auto handleBlock = [&](Block *block) { return processBlock (block); };
1523- if ( failed ( PrettyBlockOrderVisitor::visit ( headerBlock, handleBlock ,
1524- {headerBlock, mergeBlock})))
1483+ if ( failed ( visitInPrettyBlockOrder (
1484+ headerBlock, [&](Block *block) { return processBlock (block); } ,
1485+ /* skipHeader= */ true , /* skipBlocks= */ { mergeBlock})))
15251486 return failure ();
15261487
15271488 // There is nothing to do for the merge block in the selection, which just
@@ -1569,9 +1530,9 @@ LogicalResult Serializer::processLoopOp(spirv::LoopOp loopOp) {
15691530 // Process all blocks with a depth-first visitor starting from the header
15701531 // block. The loop header block, loop continue block, and loop merge block are
15711532 // skipped by this visitor and handled later in this function.
1572- auto handleBlock = [&](Block *block) { return processBlock (block); };
1573- if ( failed ( PrettyBlockOrderVisitor::visit (
1574- headerBlock, handleBlock, {headerBlock, continueBlock, mergeBlock})))
1533+ if ( failed ( visitInPrettyBlockOrder (
1534+ headerBlock, [&](Block *block) { return processBlock (block); },
1535+ /* skipHeader= */ true , /* skipBlocks= */ { continueBlock, mergeBlock})))
15751536 return failure ();
15761537
15771538 // We have handled all other blocks. Now get to the loop continue block.
0 commit comments