зеркало из https://github.com/stride3d/xkslang.git
Implement inReadableOrder().
This commit is contained in:
Родитель
454796e008
Коммит
44bfb0d0cd
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 2.8)
|
|||
|
||||
set(SOURCES
|
||||
GlslangToSpv.cpp
|
||||
InReadableOrder.cpp
|
||||
SpvBuilder.cpp
|
||||
SPVRemapper.cpp
|
||||
doc.cpp
|
||||
|
|
|
@ -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 <vector>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
#include <vector>
|
||||
|
||||
namespace spv {
|
||||
|
||||
|
@ -157,7 +158,7 @@ public:
|
|||
virtual ~Block()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Id getId() { return instructions.front()->getResultId(); }
|
||||
|
||||
Function& getParent() const { return parent; }
|
||||
|
@ -168,6 +169,19 @@ public:
|
|||
const std::vector<Block*> getSuccessors() const { return successors; }
|
||||
void setUnreachable() { unreachable = true; }
|
||||
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
|
||||
{
|
||||
|
@ -217,6 +231,11 @@ protected:
|
|||
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.
|
||||
//
|
||||
|
@ -253,8 +272,7 @@ public:
|
|||
parameterInstructions[p]->dump(out);
|
||||
|
||||
// Blocks
|
||||
for (int b = 0; b < (int)blocks.size(); ++b)
|
||||
blocks[b]->dump(out);
|
||||
inReadableOrder(blocks[0], [&out](const Block* b) { b->dump(out); });
|
||||
Instruction end(0, 0, OpFunctionEnd);
|
||||
end.dump(out);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче