Implement inReadableOrder().
This commit is contained in:
parent
454796e008
commit
44bfb0d0cd
3 changed files with 88 additions and 5 deletions
|
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
GlslangToSpv.cpp
|
GlslangToSpv.cpp
|
||||||
|
InReadableOrder.cpp
|
||||||
SpvBuilder.cpp
|
SpvBuilder.cpp
|
||||||
SPVRemapper.cpp
|
SPVRemapper.cpp
|
||||||
doc.cpp
|
doc.cpp
|
||||||
|
|
|
||||||
64
SPIRV/InReadableOrder.cpp
Normal file
64
SPIRV/InReadableOrder.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// The SPIR-V spec requires code blocks to appear in an order satisfying the
|
||||||
|
// dominator-tree direction (ie, dominator before the dominated). This is,
|
||||||
|
// actually, easy to achieve: any pre-order CFG traversal algorithm will do it.
|
||||||
|
// Because such algorithms visit a block only after traversing some path to it
|
||||||
|
// from the root, they necessarily visit the block's idom first.
|
||||||
|
//
|
||||||
|
// But not every graph-traversal algorithm outputs blocks in an order that
|
||||||
|
// appears logical to human readers. The problem is that unrelated branches may
|
||||||
|
// be interspersed with each other, and merge blocks may come before some of the
|
||||||
|
// branches being merged.
|
||||||
|
//
|
||||||
|
// A good, human-readable order of blocks may be achieved by performing
|
||||||
|
// depth-first search but delaying merge nodes until after all their branches
|
||||||
|
// have been visited. This is implemented below by the inReadableOrder()
|
||||||
|
// function.
|
||||||
|
|
||||||
|
#include "spvIR.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <deque>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
using spv::Block;
|
||||||
|
using spv::Id;
|
||||||
|
using BlockSet = std::unordered_set<Id>;
|
||||||
|
using IdToBool = std::unordered_map<Id, bool>;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// True if any of prerequisites have not yet been visited.
|
||||||
|
bool delay(const BlockSet& prereqs, const IdToBool& visited) {
|
||||||
|
return std::any_of(prereqs.begin(), prereqs.end(),
|
||||||
|
[&visited](Id b) { return !visited.count(b); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spv::inReadableOrder(Block* root, std::function<void(Block*)> callback) {
|
||||||
|
// Prerequisites for a merge block; must be completed prior to visiting the
|
||||||
|
// merge block.
|
||||||
|
std::unordered_map<Id, BlockSet> prereqs;
|
||||||
|
IdToBool visited; // Whether a block has already been visited.
|
||||||
|
std::deque<Block*> worklist; // DFS worklist
|
||||||
|
worklist.push_back(root);
|
||||||
|
while (!worklist.empty()) {
|
||||||
|
Block* current = worklist.front();
|
||||||
|
worklist.pop_front();
|
||||||
|
// Nodes may be pushed repeadetly (before they're first visited) if they
|
||||||
|
// have multiple predecessors. Skip the already-visited ones.
|
||||||
|
if (visited[current->getId()]) continue;
|
||||||
|
callback(current);
|
||||||
|
visited[current->getId()] = true;
|
||||||
|
if (auto merge = current->getMergeInstruction()) {
|
||||||
|
auto& mergePrereqs = prereqs[merge->getIdOperand(0)];
|
||||||
|
// Delay visiting merge blocks until all branches are visited.
|
||||||
|
for (const auto succ : current->getSuccessors())
|
||||||
|
mergePrereqs.insert(succ->getId());
|
||||||
|
}
|
||||||
|
for (auto succ : current->getSuccessors()) {
|
||||||
|
if (!visited[succ->getId()] && !delay(prereqs[succ->getId()], visited)) {
|
||||||
|
worklist.push_back(succ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,10 +52,11 @@
|
||||||
|
|
||||||
#include "spirv.hpp"
|
#include "spirv.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <cassert>
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <assert.h>
|
#include <vector>
|
||||||
|
|
||||||
namespace spv {
|
namespace spv {
|
||||||
|
|
||||||
|
|
@ -157,7 +158,7 @@ public:
|
||||||
virtual ~Block()
|
virtual ~Block()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Id getId() { return instructions.front()->getResultId(); }
|
Id getId() { return instructions.front()->getResultId(); }
|
||||||
|
|
||||||
Function& getParent() const { return parent; }
|
Function& getParent() const { return parent; }
|
||||||
|
|
@ -168,6 +169,19 @@ public:
|
||||||
const std::vector<Block*> getSuccessors() const { return successors; }
|
const std::vector<Block*> getSuccessors() const { return successors; }
|
||||||
void setUnreachable() { unreachable = true; }
|
void setUnreachable() { unreachable = true; }
|
||||||
bool isUnreachable() const { return unreachable; }
|
bool isUnreachable() const { return unreachable; }
|
||||||
|
// Returns the block's merge instruction, if one exists (otherwise null).
|
||||||
|
const Instruction* getMergeInstruction() const {
|
||||||
|
if (instructions.size() < 2) return nullptr;
|
||||||
|
const Instruction* nextToLast = *(instructions.cend() - 2);
|
||||||
|
switch (nextToLast->getOpCode()) {
|
||||||
|
case OpSelectionMerge:
|
||||||
|
case OpLoopMerge:
|
||||||
|
return nextToLast;
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool isTerminated() const
|
bool isTerminated() const
|
||||||
{
|
{
|
||||||
|
|
@ -217,6 +231,11 @@ protected:
|
||||||
bool unreachable;
|
bool unreachable;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Traverses the control-flow graph rooted at root in an order suited for
|
||||||
|
// readable code generation. Invokes callback at every node in the traversal
|
||||||
|
// order.
|
||||||
|
void inReadableOrder(Block* root, std::function<void(Block*)> callback);
|
||||||
|
|
||||||
//
|
//
|
||||||
// SPIR-V IR Function.
|
// SPIR-V IR Function.
|
||||||
//
|
//
|
||||||
|
|
@ -253,8 +272,7 @@ public:
|
||||||
parameterInstructions[p]->dump(out);
|
parameterInstructions[p]->dump(out);
|
||||||
|
|
||||||
// Blocks
|
// Blocks
|
||||||
for (int b = 0; b < (int)blocks.size(); ++b)
|
inReadableOrder(blocks[0], [&out](const Block* b) { b->dump(out); });
|
||||||
blocks[b]->dump(out);
|
|
||||||
Instruction end(0, 0, OpFunctionEnd);
|
Instruction end(0, 0, OpFunctionEnd);
|
||||||
end.dump(out);
|
end.dump(out);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue