SpirvShader: Add WriteCFGGraphVizDotFile debug function
Spits out a graphviz .dot file describing the control flow of the shader.
This is very similar to the output of the spirv-cfg tool in SPIRV-Tools. This is just much easier to use, and allows us to add more bespoke information to the graph in the future.
Bug: b/140287657
Change-Id: Id90b065a3599b941b192a5e8105dffac9ba8f566
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/32952
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 70d97bb..927bb89 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -730,6 +730,14 @@
it.second.AssignBlockFields();
}
+#ifdef SPIRV_SHADER_CFG_GRAPHVIZ_DOT_FILEPATH
+ {
+ char path[1024];
+ snprintf(path, sizeof(path), SPIRV_SHADER_CFG_GRAPHVIZ_DOT_FILEPATH, codeSerialID);
+ WriteCFGGraphVizDotFile(path);
+ }
+#endif
+
dbgCreateFile();
}
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index b395ca2..dd05d8e 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -1214,6 +1214,10 @@
// Helper for calling rr::Yield with res cast to an rr::Int.
void Yield(YieldResult res) const;
+ // WriteCFGGraphVizDotFile() writes a graphviz dot file of the shader's
+ // control flow to the given file path.
+ void WriteCFGGraphVizDotFile(const char *path) const;
+
// OpcodeName() returns the name of the opcode op.
// If NDEBUG is defined, then OpcodeName() will only return the numerical code.
static std::string OpcodeName(spv::Op op);
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index 8261b14..16f329c 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -23,6 +23,9 @@
#include <queue>
+#include <fstream>
+#include <iostream>
+
namespace sw {
SpirvShader::Block::Block(InsnIterator begin, InsnIterator end)
@@ -729,4 +732,83 @@
dbgUpdateActiveLaneMask(mask, state);
}
+void SpirvShader::WriteCFGGraphVizDotFile(const char *path) const
+{
+ std::ofstream file(path);
+ file << "digraph D {" << std::endl;
+ for(auto &func : functions)
+ {
+ file << " subgraph cluster_function_" << func.first.value() << " {"
+ << std::endl;
+
+ file << " label = \"function<" << func.first.value() << ">"
+ << (func.first == entryPoint ? " (entry point)" : "")
+ << "\"" << std::endl;
+
+ for(auto &block : func.second.blocks)
+ {
+ file << " block_" << block.first.value() << " ["
+ << "shape=circle "
+ << "label=\"" << block.first.value() << "\""
+ << "]" << std::endl;
+ }
+ file << std::endl;
+ for(auto &block : func.second.blocks)
+ {
+ file << " block_" << block.first.value() << " -> {";
+ bool first = true;
+ for(auto outs : block.second.outs)
+ {
+ if(!first) { file << ", "; }
+ file << "block_" << outs.value();
+ first = false;
+ }
+ file << "}" << std::endl;
+ }
+ file << std::endl;
+ for(auto &block : func.second.blocks)
+ {
+ if(block.second.kind == Block::Loop)
+ {
+ if(block.second.mergeBlock != 0)
+ {
+ file << " block_" << block.first.value() << " -> "
+ << "block_" << block.second.mergeBlock.value()
+ << "[label=\"M\" style=dashed color=blue]"
+ << std::endl;
+ }
+ if(block.second.continueTarget != 0)
+ {
+ file << " block_" << block.first.value() << " -> "
+ << "block_" << block.second.continueTarget.value()
+ << "[label=\"C\" style=dashed color=green]"
+ << std::endl;
+ }
+ }
+ }
+
+ file << " }" << std::endl;
+ }
+
+ for(auto &func : functions)
+ {
+ for(auto &block : func.second.blocks)
+ {
+ for(auto insn : block.second)
+ {
+ if(insn.opcode() == spv::OpFunctionCall)
+ {
+ auto target = getFunction(insn.word(3)).entry;
+ file << " block_" << block.first.value() << " -> "
+ << "block_" << target.value()
+ << "[color=\"#00008050\"]"
+ << std::endl;
+ }
+ }
+ }
+ }
+
+ file << "}" << std::endl;
+}
+
} // namespace sw
\ No newline at end of file
diff --git a/src/Pipeline/SpirvShaderDebug.hpp b/src/Pipeline/SpirvShaderDebug.hpp
index 1cf3ce6..959c542 100644
--- a/src/Pipeline/SpirvShaderDebug.hpp
+++ b/src/Pipeline/SpirvShaderDebug.hpp
@@ -20,6 +20,13 @@
// reduced to 1 and execution is deterministic.
#define SPIRV_SHADER_ENABLE_DBG 0
+// Enable this to write a GraphViz dot file containing a graph of the shader's
+// control flow to the given file path. Helpful for diagnosing control-flow
+// related issues.
+#if 0
+# define SPIRV_SHADER_CFG_GRAPHVIZ_DOT_FILEPATH "swiftshader_%d.dot"
+#endif
+
#if SPIRV_SHADER_ENABLE_DBG
# define SPIRV_SHADER_DBG(fmt, ...) rr::Print(fmt "\n", ##__VA_ARGS__)
# include "spirv-tools/libspirv.h"