Commit 62bb5edf by Ben Clayton

SpirvShader: Rework CFG traversal.

The previous traversal could create huge pending lists of duplicate-blocks, to just skip over them. This was due a couple of factors, including BFS traversal and always appending the downstream blocks to the queue. There's no particular reason to do BFS traversal, so perform DFS traversal instead, and check the downstream blocks haven't been processed already before enquing. Fixes pending lists reaching counts as high as 1e7 for tests such as dEQP-VK.spirv_assembly.instruction.compute.opphi.nested - making these tests much faster to pass and drastically reducing peak memory usage. Bug: b/135512559 Change-Id: Ibbfa03e582f08d6b41e6c8496d05b8b4027b31b6 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/32951Reviewed-by: 's avatarChris Forbes <chrisforbes@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com>
parent d70129a3
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include <spirv/unified1/spirv.hpp> #include <spirv/unified1/spirv.hpp>
#include <spirv/unified1/GLSL.std.450.h> #include <spirv/unified1/GLSL.std.450.h>
#include <queue>
namespace namespace
{ {
constexpr float PI = 3.141592653589793f; constexpr float PI = 3.141592653589793f;
...@@ -1980,20 +1982,38 @@ namespace sw ...@@ -1980,20 +1982,38 @@ namespace sw
{ {
auto oldPending = state->pending; auto oldPending = state->pending;
std::queue<Block::ID> pending; std::deque<Block::ID> pending;
state->pending = &pending; state->pending = &pending;
pending.push(id); pending.push_front(id);
while (pending.size() > 0) while (pending.size() > 0)
{ {
auto id = pending.front(); auto id = pending.front();
pending.pop();
auto const &block = getBlock(id); auto const &block = getBlock(id);
if (id == ignore) if (id == ignore)
{ {
pending.pop_front();
continue; continue;
} }
// Ensure all dependency blocks have been generated.
auto depsDone = true;
ForeachBlockDependency(id, [&](Block::ID dep)
{
if (state->visited.count(dep) == 0)
{
state->pending->push_front(dep);
depsDone = false;
}
});
if (!depsDone)
{
continue;
}
pending.pop_front();
state->currentBlock = id; state->currentBlock = id;
switch (block.kind) switch (block.kind)
...@@ -2036,28 +2056,23 @@ namespace sw ...@@ -2036,28 +2056,23 @@ namespace sw
} }
} }
void SpirvShader::EmitNonLoop(EmitState *state) const void SpirvShader::ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const
{ {
auto blockId = state->currentBlock;
auto block = getBlock(blockId); auto block = getBlock(blockId);
for (auto dep : block.ins)
// Ensure all incoming blocks have been generated.
auto depsDone = true;
for (auto in : block.ins)
{ {
if (state->visited.count(in) == 0) if (block.kind != Block::Loop || // if not a loop...
!existsPath(blockId, dep, block.mergeBlock)) // or a loop and not a loop back edge
{ {
state->pending->emplace(in); f(dep);
depsDone = false;
} }
} }
}
if (!depsDone) void SpirvShader::EmitNonLoop(EmitState *state) const
{ {
// come back to this once the dependencies have been generated auto blockId = state->currentBlock;
state->pending->emplace(blockId); auto block = getBlock(blockId);
return;
}
if (!state->visited.emplace(blockId).second) if (!state->visited.emplace(blockId).second)
{ {
...@@ -2080,7 +2095,10 @@ namespace sw ...@@ -2080,7 +2095,10 @@ namespace sw
for (auto out : block.outs) for (auto out : block.outs)
{ {
state->pending->emplace(out); if (state->visited.count(out) == 0)
{
state->pending->push_back(out);
}
} }
} }
...@@ -2091,27 +2109,6 @@ namespace sw ...@@ -2091,27 +2109,6 @@ namespace sw
auto mergeBlockId = block.mergeBlock; auto mergeBlockId = block.mergeBlock;
auto &mergeBlock = getBlock(mergeBlockId); auto &mergeBlock = getBlock(mergeBlockId);
// Ensure all incoming non-back edge blocks have been generated.
auto depsDone = true;
for (auto in : block.ins)
{
if (state->visited.count(in) == 0)
{
if (!existsPath(blockId, in, mergeBlockId)) // if not a loop back edge
{
state->pending->emplace(in);
depsDone = false;
}
}
}
if (!depsDone)
{
// come back to this once the dependencies have been generated
state->pending->emplace(blockId);
return;
}
if (!state->visited.emplace(blockId).second) if (!state->visited.emplace(blockId).second)
{ {
return; // Already emitted this loop. return; // Already emitted this loop.
...@@ -2260,7 +2257,7 @@ namespace sw ...@@ -2260,7 +2257,7 @@ namespace sw
// Continue emitting from the merge block. // Continue emitting from the merge block.
Nucleus::setInsertBlock(mergeBasicBlock); Nucleus::setInsertBlock(mergeBasicBlock);
state->pending->emplace(mergeBlockId); state->pending->push_back(mergeBlockId);
for (auto it : mergeActiveLaneMasks) for (auto it : mergeActiveLaneMasks)
{ {
state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second); state->addActiveLaneMaskEdge(it.first, mergeBlockId, it.second);
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <queue> #include <deque>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
...@@ -888,7 +888,7 @@ namespace sw ...@@ -888,7 +888,7 @@ namespace sw
Block::ID currentBlock; // The current block being built. Block::ID currentBlock; // The current block being built.
Block::Set visited; // Blocks already built. Block::Set visited; // Blocks already built.
std::unordered_map<Block::Edge, RValue<SIMD::Int>, Block::Edge::Hash> edgeActiveLaneMasks; std::unordered_map<Block::Edge, RValue<SIMD::Int>, Block::Edge::Hash> edgeActiveLaneMasks;
std::queue<Block::ID> *pending; std::deque<Block::ID> *pending;
const vk::DescriptorSet::Bindings &descriptorSets; const vk::DescriptorSet::Bindings &descriptorSets;
}; };
...@@ -910,7 +910,12 @@ namespace sw ...@@ -910,7 +910,12 @@ namespace sw
// Asserts if from is reachable and the edge does not exist. // Asserts if from is reachable and the edge does not exist.
RValue<SIMD::Int> GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const; RValue<SIMD::Int> GetActiveLaneMaskEdge(EmitState *state, Block::ID from, Block::ID to) const;
// Emit all the unvisited blocks (except for ignore) in BFS order, // ForeachBlockDependency calls f with each dependency of the given
// block. A dependency is an incoming block that is not a loop-back
// edge.
void ForeachBlockDependency(Block::ID blockId, std::function<void(Block::ID)> f) const;
// Emit all the unvisited blocks (except for ignore) in DFS order,
// starting with id. // starting with id.
void EmitBlocks(Block::ID id, EmitState *state, Block::ID ignore = 0) const; void EmitBlocks(Block::ID id, EmitState *state, Block::ID ignore = 0) const;
void EmitNonLoop(EmitState *state) const; void EmitNonLoop(EmitState *state) const;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment