SpirvShader: Add a flag for printing SPIRV execution
The ENABLE_DBG_MSGS flag controls printing of verbose debug messages to
stdout as each SPIR-V instruction is executed. This is very handy for
performing text diffs when the thread count is reduced to 1 and execution
is deterministic.
Bug: b/140287657
Change-Id: I3b62f0f9f3017087cf7f2786e1c30497cfa05a20
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31488
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/BUILD.gn b/src/Pipeline/BUILD.gn
index b85cf6a..998cb47 100644
--- a/src/Pipeline/BUILD.gn
+++ b/src/Pipeline/BUILD.gn
@@ -24,6 +24,7 @@
"SetupRoutine.hpp",
"ShaderCore.hpp",
"SpirvShader.hpp",
+ "SpirvShaderDebug.hpp",
"VertexProgram.hpp",
"VertexRoutine.hpp",
]
diff --git a/src/Pipeline/ShaderCore.hpp b/src/Pipeline/ShaderCore.hpp
index bbd4c2a..2b705cb 100644
--- a/src/Pipeline/ShaderCore.hpp
+++ b/src/Pipeline/ShaderCore.hpp
@@ -544,7 +544,19 @@
return PrintValue::vals(v.x, v.y, v.z, v.w);
}
};
+template<>
+struct PrintValue::Ty<sw::SIMD::Pointer>
+{
+ static std::string fmt(const sw::SIMD::Pointer &v)
+ {
+ return "{" + PrintValue::fmt(v.base) + " +" + PrintValue::fmt(v.offsets()) + "}";
+ }
+ static std::vector<rr::Value *> val(const sw::SIMD::Pointer &v)
+ {
+ return PrintValue::vals(v.base, v.offsets());
+ }
+};
} // namespace rr
#endif // ENABLE_RR_PRINT
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 9a9ccd3..70d97bb 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "SpirvShader.hpp"
+#include "SpirvShaderDebug.hpp"
#include "System/Debug.hpp"
#include "Vulkan/VkPipelineLayout.hpp"
@@ -1597,6 +1598,19 @@
auto opcode = insn.opcode();
+#if SPIRV_SHADER_ENABLE_DBG
+ {
+ auto text = spvtools::spvInstructionBinaryToText(
+ SPV_ENV_VULKAN_1_1,
+ insn.wordPointer(0),
+ insn.wordCount(),
+ insns.data(),
+ insns.size(),
+ SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
+ SPIRV_SHADER_DBG("{0}", text);
+ }
+#endif // ENABLE_DBG_MSGS
+
switch(opcode)
{
case spv::OpTypeVoid:
@@ -2148,6 +2162,11 @@
dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i))); // TODO: IfThenElse()
}
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), dst);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), cond);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), lhs);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(5), rhs);
+
return EmitResult::Continue;
}
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 7df782e..b395ca2 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -83,13 +83,22 @@
delete[] scalar;
}
- void move(uint32_t i, RValue<SIMD::Float> &&scalar) { emplace(i, scalar.value); }
- void move(uint32_t i, RValue<SIMD::Int> &&scalar) { emplace(i, scalar.value); }
- void move(uint32_t i, RValue<SIMD::UInt> &&scalar) { emplace(i, scalar.value); }
+ // TypeHint is used as a hint for rr::PrintValue::Ty<sw::Intermediate> to
+ // decide the format used to print the intermediate data.
+ enum class TypeHint
+ {
+ Float,
+ Int,
+ UInt
+ };
- void move(uint32_t i, const RValue<SIMD::Float> &scalar) { emplace(i, scalar.value); }
- void move(uint32_t i, const RValue<SIMD::Int> &scalar) { emplace(i, scalar.value); }
- void move(uint32_t i, const RValue<SIMD::UInt> &scalar) { emplace(i, scalar.value); }
+ void move(uint32_t i, RValue<SIMD::Float> &&scalar) { emplace(i, scalar.value, TypeHint::Float); }
+ void move(uint32_t i, RValue<SIMD::Int> &&scalar) { emplace(i, scalar.value, TypeHint::Int); }
+ void move(uint32_t i, RValue<SIMD::UInt> &&scalar) { emplace(i, scalar.value, TypeHint::UInt); }
+
+ void move(uint32_t i, const RValue<SIMD::Float> &scalar) { emplace(i, scalar.value, TypeHint::Float); }
+ void move(uint32_t i, const RValue<SIMD::Int> &scalar) { emplace(i, scalar.value, TypeHint::Int); }
+ void move(uint32_t i, const RValue<SIMD::UInt> &scalar) { emplace(i, scalar.value, TypeHint::UInt); }
// Value retrieval functions.
RValue<SIMD::Float> Float(uint32_t i) const
@@ -122,14 +131,20 @@
const uint32_t componentCount;
private:
- void emplace(uint32_t i, rr::Value *value)
+ void emplace(uint32_t i, rr::Value *value, TypeHint type)
{
ASSERT(i < componentCount);
ASSERT(scalar[i] == nullptr);
scalar[i] = value;
+ RR_PRINT_ONLY(typeHint = type;)
}
rr::Value **const scalar;
+
+#ifdef ENABLE_RR_PRINT
+ friend struct rr::PrintValue::Ty<sw::Intermediate>;
+ TypeHint typeHint = TypeHint::Float;
+#endif // ENABLE_RR_PRINT
};
class SpirvShader
@@ -1028,6 +1043,8 @@
}
private:
+ RR_PRINT_ONLY(friend struct rr::PrintValue::Ty<Operand>;)
+
// Delegate constructor
Operand(const EmitState *state, const Object &object);
@@ -1038,6 +1055,8 @@
const uint32_t componentCount;
};
+ RR_PRINT_ONLY(friend struct rr::PrintValue::Ty<Operand>;)
+
Type const &getType(Type::ID id) const
{
auto it = types.find(id);
diff --git a/src/Pipeline/SpirvShaderArithmetic.cpp b/src/Pipeline/SpirvShaderArithmetic.cpp
index 59c499b..e730505 100644
--- a/src/Pipeline/SpirvShaderArithmetic.cpp
+++ b/src/Pipeline/SpirvShaderArithmetic.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "SpirvShader.hpp"
+#include "SpirvShaderDebug.hpp"
#include "ShaderCore.hpp"
@@ -505,6 +506,10 @@
}
}
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), dst);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs);
+
return EmitResult::Continue;
}
@@ -518,6 +523,11 @@
auto rhs = Operand(this, state, insn.word(4));
dst.move(0, Dot(lhsType.componentCount, lhs, rhs));
+
+ SPIRV_SHADER_DBG("{0}: {1}", insn.resultId(), dst);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs);
+ SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs);
+
return EmitResult::Continue;
}
diff --git a/src/Pipeline/SpirvShaderControlFlow.cpp b/src/Pipeline/SpirvShaderControlFlow.cpp
index 80cf455..8261b14 100644
--- a/src/Pipeline/SpirvShaderControlFlow.cpp
+++ b/src/Pipeline/SpirvShaderControlFlow.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "SpirvShader.hpp"
+#include "SpirvShaderDebug.hpp"
#include "Reactor/Coroutine.hpp" // rr::Yield
@@ -298,8 +299,10 @@
for(auto in : block.ins)
{
auto inMask = GetActiveLaneMaskEdge(state, in, blockId);
+ SPIRV_SHADER_DBG("Block {0} -> {1} mask: {2}", in, blockId, inMask);
activeLaneMask |= inMask;
}
+ SPIRV_SHADER_DBG("Block {0} mask: {1}", blockId, activeLaneMask);
SetActiveLaneMask(activeLaneMask, state);
}
@@ -312,6 +315,8 @@
state->pending->push_back(out);
}
}
+
+ SPIRV_SHADER_DBG("Block {0} done", blockId);
}
void SpirvShader::EmitLoop(EmitState *state) const
@@ -327,6 +332,8 @@
return; // Already emitted this loop.
}
+ SPIRV_SHADER_DBG("*** LOOP HEADER ***");
+
// Gather all the blocks that make up the loop.
std::unordered_set<Block::ID> loopBlocks;
loopBlocks.emplace(block.mergeBlock);
@@ -376,6 +383,8 @@
Nucleus::createBr(headerBasicBlock);
Nucleus::setInsertBlock(headerBasicBlock);
+ SPIRV_SHADER_DBG("*** LOOP START (mask: {0}) ***", loopActiveLaneMask);
+
// Load the active lane mask.
SetActiveLaneMask(loopActiveLaneMask, state);
@@ -432,6 +441,8 @@
}
}
+ SPIRV_SHADER_DBG("*** LOOP END (mask: {0}) ***", loopActiveLaneMask);
+
// Use the [loop -> merge] active lane masks to update the phi values in
// the merge block. We need to do this to handle divergent control flow
// in the loop.
@@ -516,6 +527,7 @@
auto sel = Operand(this, state, selId);
ASSERT_MSG(sel.componentCount == 1, "Selector must be a scalar");
+ SPIRV_SHADER_DBG("switch({0})", sel);
auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
@@ -531,11 +543,13 @@
auto label = block.branchInstruction.word(i * 2 + 3);
auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4));
auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label));
+ SPIRV_SHADER_DBG("case {0}: {1}", label, caseLabelMatch & state->activeLaneMask());
state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch);
defaultLaneMask &= ~caseLabelMatch;
}
auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
+ SPIRV_SHADER_DBG("default: {0}", defaultLaneMask);
state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
return EmitResult::Terminator;
@@ -654,6 +668,7 @@
for(uint32_t i = 0; i < type.componentCount; i++)
{
dst.move(i, storage[i]);
+ SPIRV_SHADER_DBG("LoadPhi({0}.{1}): {2}", objectId, i, storage[i]);
}
}
@@ -683,8 +698,15 @@
for(uint32_t i = 0; i < type.componentCount; i++)
{
storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask));
+ SPIRV_SHADER_DBG("StorePhi({0}.{1}): [{2} <- {3}] {4}: {5}, mask: {6}",
+ objectId, i, currentBlock, blockId, varId, in.UInt(i), mask);
}
}
+
+ for(uint32_t i = 0; i < type.componentCount; i++)
+ {
+ SPIRV_SHADER_DBG("StorePhi({0}.{1}): {2}", objectId, i, As<SIMD::UInt>(storage[i]));
+ }
}
void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
diff --git a/src/Pipeline/SpirvShaderDebug.hpp b/src/Pipeline/SpirvShaderDebug.hpp
new file mode 100644
index 0000000..1cf3ce6
--- /dev/null
+++ b/src/Pipeline/SpirvShaderDebug.hpp
@@ -0,0 +1,138 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef sw_SpirvShaderDebug_hpp
+#define sw_SpirvShaderDebug_hpp
+
+// Enable this to print verbose debug messages as each SPIR-V instructon is
+// executed. Very handy for performing text diffs when the thread count is
+// reduced to 1 and execution is deterministic.
+#define SPIRV_SHADER_ENABLE_DBG 0
+
+#if SPIRV_SHADER_ENABLE_DBG
+# define SPIRV_SHADER_DBG(fmt, ...) rr::Print(fmt "\n", ##__VA_ARGS__)
+# include "spirv-tools/libspirv.h"
+namespace spvtools {
+// Function implemented in third_party/SPIRV-Tools/source/disassemble.cpp
+// but with no public header.
+std::string spvInstructionBinaryToText(const spv_target_env env,
+ const uint32_t *inst_binary,
+ const size_t inst_word_count,
+ const uint32_t *binary,
+ const size_t word_count,
+ const uint32_t options);
+
+} // namespace spvtools
+#else
+# define SPIRV_SHADER_DBG(...)
+#endif // SPIRV_SHADER_ENABLE_DBG
+
+#ifdef ENABLE_RR_PRINT
+namespace rr {
+template<>
+struct PrintValue::Ty<sw::SpirvShader::Object::ID>
+{
+ static inline std::string fmt(sw::SpirvShader::Object::ID v) { return "Object<" + std::to_string(v.value()) + ">"; }
+ static inline std::vector<Value *> val(sw::SpirvShader::Object::ID v) { return {}; }
+};
+template<>
+struct PrintValue::Ty<sw::SpirvShader::Type::ID>
+{
+ static inline std::string fmt(sw::SpirvShader::Type::ID v) { return "Type<" + std::to_string(v.value()) + ">"; }
+ static inline std::vector<Value *> val(sw::SpirvShader::Type::ID v) { return {}; }
+};
+template<>
+struct PrintValue::Ty<sw::SpirvShader::Block::ID>
+{
+ static inline std::string fmt(sw::SpirvShader::Block::ID v) { return "Block<" + std::to_string(v.value()) + ">"; }
+ static inline std::vector<Value *> val(sw::SpirvShader::Block::ID v) { return {}; }
+};
+
+template<>
+struct PrintValue::Ty<sw::Intermediate>
+{
+ static inline std::string fmt(const sw::Intermediate &v, uint32_t i)
+ {
+ switch(v.typeHint)
+ {
+ case sw::Intermediate::TypeHint::Float:
+ return PrintValue::Ty<sw::SIMD::Float>::fmt(v.Float(i));
+ case sw::Intermediate::TypeHint::Int:
+ return PrintValue::Ty<sw::SIMD::Int>::fmt(v.Int(i));
+ case sw::Intermediate::TypeHint::UInt:
+ return PrintValue::Ty<sw::SIMD::UInt>::fmt(v.UInt(i));
+ }
+ return "";
+ }
+
+ static inline std::vector<Value *> val(const sw::Intermediate &v, uint32_t i)
+ {
+ switch(v.typeHint)
+ {
+ case sw::Intermediate::TypeHint::Float:
+ return PrintValue::Ty<sw::SIMD::Float>::val(v.Float(i));
+ case sw::Intermediate::TypeHint::Int:
+ return PrintValue::Ty<sw::SIMD::Int>::val(v.Int(i));
+ case sw::Intermediate::TypeHint::UInt:
+ return PrintValue::Ty<sw::SIMD::UInt>::val(v.UInt(i));
+ }
+ return {};
+ }
+
+ static inline std::string fmt(const sw::Intermediate &v)
+ {
+ if(v.componentCount == 1)
+ {
+ return fmt(v, 0);
+ }
+
+ std::string out = "[";
+ for(uint32_t i = 0; i < v.componentCount; i++)
+ {
+ if(i > 0) { out += ", "; }
+ out += std::to_string(i) + ": ";
+ out += fmt(v, i);
+ }
+ return out + "]";
+ }
+
+ static inline std::vector<Value *> val(const sw::Intermediate &v)
+ {
+ std::vector<Value *> out;
+ for(uint32_t i = 0; i < v.componentCount; i++)
+ {
+ auto vals = val(v, i);
+ out.insert(out.end(), vals.begin(), vals.end());
+ }
+ return out;
+ }
+};
+
+template<>
+struct PrintValue::Ty<sw::SpirvShader::Operand>
+{
+ static inline std::string fmt(const sw::SpirvShader::Operand &v)
+ {
+ return (v.intermediate != nullptr) ? PrintValue::Ty<sw::Intermediate>::fmt(*v.intermediate) : PrintValue::Ty<sw::SIMD::UInt>::fmt(v.UInt(0));
+ }
+
+ static inline std::vector<Value *> val(const sw::SpirvShader::Operand &v)
+ {
+ return (v.intermediate != nullptr) ? PrintValue::Ty<sw::Intermediate>::val(*v.intermediate) : PrintValue::Ty<sw::SIMD::UInt>::val(v.UInt(0));
+ }
+};
+} // namespace rr
+#endif // ENABLE_RR_PRINT
+
+#endif // sw_SpirvShaderDebug_hpp
diff --git a/src/Pipeline/SpirvShaderMemory.cpp b/src/Pipeline/SpirvShaderMemory.cpp
index 8407166..cbed2de 100644
--- a/src/Pipeline/SpirvShaderMemory.cpp
+++ b/src/Pipeline/SpirvShaderMemory.cpp
@@ -13,6 +13,7 @@
// limitations under the License.
#include "SpirvShader.hpp"
+#include "SpirvShaderDebug.hpp"
#include "ShaderCore.hpp"
@@ -63,6 +64,8 @@
dst.move(el.index, p.Load<SIMD::Float>(robustness, state->activeLaneMask(), atomic, memoryOrder));
});
+ SPIRV_SHADER_DBG("Load(atomic: {0}, order: {1}, ptr: {2}, val: {3}, mask: {4})", atomic, int(memoryOrder), ptr, dst, state->activeLaneMask());
+
return EmitResult::Continue;
}
@@ -105,6 +108,8 @@
mask = mask & state->storesAndAtomicsMask();
}
+ SPIRV_SHADER_DBG("Store(atomic: {0}, order: {1}, ptr: {2}, val: {3}, mask: {4}", atomic, int(memoryOrder), ptr, value, mask);
+
VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
auto p = ptr + el.offset;
if(interleavedByLane) { p = InterleaveByLane(p); }
diff --git a/src/Reactor/Print.hpp b/src/Reactor/Print.hpp
index ca70465..163af82 100644
--- a/src/Reactor/Print.hpp
+++ b/src/Reactor/Print.hpp
@@ -35,6 +35,7 @@
// * Static arrays in the form T[N] where T can be any of the above.
class PrintValue
{
+public:
// Ty is a template that can be specialized for printing type T.
// Each specialization must expose:
// * A 'static std::string fmt(const T& v)' method that provides the
@@ -83,7 +84,6 @@
return buf;
}
-public:
const std::string format;
const std::vector<Value *> values;
@@ -456,6 +456,9 @@
} // namespace rr
+# define RR_PRINT_ONLY(x) x
+#else
+# define RR_PRINT_ONLY(x)
#endif // ENABLE_RR_PRINT
#endif // rr_Print_hpp