Skip to content
This repository was archived by the owner on Apr 23, 2021. It is now read-only.

Commit e6d8471

Browse files
antiagainsttensorflower-gardener
authored andcommitted
[spirv] Use LLVM graph traversal utility for PrettyBlockOrderVisitor
This removes a bunch of special tailored DFS code in favor of the common LLVM utility. Besides, we avoid recursion with system stack given that llvm::depth_first_ext is iterator based and maintains its own stack. PiperOrigin-RevId: 277272961
1 parent e2fabe9 commit e6d8471

File tree

1 file changed

+33
-72
lines changed

1 file changed

+33
-72
lines changed

lib/Dialect/SPIRV/Serialization/Serializer.cpp

Lines changed: 33 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
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

12687
namespace {
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

Comments
 (0)