Commit fc951cd1 by Ben Clayton

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/+/31488Tested-by: 's avatarBen Clayton <bclayton@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent 2c9aa76e
...@@ -24,6 +24,7 @@ swiftshader_source_set("Pipeline_headers") { ...@@ -24,6 +24,7 @@ swiftshader_source_set("Pipeline_headers") {
"SetupRoutine.hpp", "SetupRoutine.hpp",
"ShaderCore.hpp", "ShaderCore.hpp",
"SpirvShader.hpp", "SpirvShader.hpp",
"SpirvShaderDebug.hpp",
"VertexProgram.hpp", "VertexProgram.hpp",
"VertexRoutine.hpp", "VertexRoutine.hpp",
] ]
......
...@@ -544,7 +544,19 @@ struct PrintValue::Ty<sw::Vector4s> ...@@ -544,7 +544,19 @@ struct PrintValue::Ty<sw::Vector4s>
return PrintValue::vals(v.x, v.y, v.z, v.w); 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 } // namespace rr
#endif // ENABLE_RR_PRINT #endif // ENABLE_RR_PRINT
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "SpirvShader.hpp" #include "SpirvShader.hpp"
#include "SpirvShaderDebug.hpp"
#include "System/Debug.hpp" #include "System/Debug.hpp"
#include "Vulkan/VkPipelineLayout.hpp" #include "Vulkan/VkPipelineLayout.hpp"
...@@ -1597,6 +1598,19 @@ SpirvShader::EmitResult SpirvShader::EmitInstruction(InsnIterator insn, EmitStat ...@@ -1597,6 +1598,19 @@ SpirvShader::EmitResult SpirvShader::EmitInstruction(InsnIterator insn, EmitStat
auto opcode = insn.opcode(); 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) switch(opcode)
{ {
case spv::OpTypeVoid: case spv::OpTypeVoid:
...@@ -2148,6 +2162,11 @@ SpirvShader::EmitResult SpirvShader::EmitSelect(InsnIterator insn, EmitState *st ...@@ -2148,6 +2162,11 @@ SpirvShader::EmitResult SpirvShader::EmitSelect(InsnIterator insn, EmitState *st
dst.move(i, (sel & lhs.Int(i)) | (~sel & rhs.Int(i))); // TODO: IfThenElse() 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; return EmitResult::Continue;
} }
......
...@@ -83,13 +83,22 @@ public: ...@@ -83,13 +83,22 @@ public:
delete[] scalar; delete[] scalar;
} }
void move(uint32_t i, RValue<SIMD::Float> &&scalar) { emplace(i, scalar.value); } // TypeHint is used as a hint for rr::PrintValue::Ty<sw::Intermediate> to
void move(uint32_t i, RValue<SIMD::Int> &&scalar) { emplace(i, scalar.value); } // decide the format used to print the intermediate data.
void move(uint32_t i, RValue<SIMD::UInt> &&scalar) { emplace(i, scalar.value); } enum class TypeHint
{
Float,
Int,
UInt
};
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); } 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); } 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); } void move(uint32_t i, const RValue<SIMD::UInt> &scalar) { emplace(i, scalar.value, TypeHint::UInt); }
// Value retrieval functions. // Value retrieval functions.
RValue<SIMD::Float> Float(uint32_t i) const RValue<SIMD::Float> Float(uint32_t i) const
...@@ -122,14 +131,20 @@ public: ...@@ -122,14 +131,20 @@ public:
const uint32_t componentCount; const uint32_t componentCount;
private: private:
void emplace(uint32_t i, rr::Value *value) void emplace(uint32_t i, rr::Value *value, TypeHint type)
{ {
ASSERT(i < componentCount); ASSERT(i < componentCount);
ASSERT(scalar[i] == nullptr); ASSERT(scalar[i] == nullptr);
scalar[i] = value; scalar[i] = value;
RR_PRINT_ONLY(typeHint = type;)
} }
rr::Value **const scalar; 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 class SpirvShader
...@@ -1028,6 +1043,8 @@ private: ...@@ -1028,6 +1043,8 @@ private:
} }
private: private:
RR_PRINT_ONLY(friend struct rr::PrintValue::Ty<Operand>;)
// Delegate constructor // Delegate constructor
Operand(const EmitState *state, const Object &object); Operand(const EmitState *state, const Object &object);
...@@ -1038,6 +1055,8 @@ private: ...@@ -1038,6 +1055,8 @@ private:
const uint32_t componentCount; const uint32_t componentCount;
}; };
RR_PRINT_ONLY(friend struct rr::PrintValue::Ty<Operand>;)
Type const &getType(Type::ID id) const Type const &getType(Type::ID id) const
{ {
auto it = types.find(id); auto it = types.find(id);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "SpirvShader.hpp" #include "SpirvShader.hpp"
#include "SpirvShaderDebug.hpp"
#include "ShaderCore.hpp" #include "ShaderCore.hpp"
...@@ -505,6 +506,10 @@ SpirvShader::EmitResult SpirvShader::EmitBinaryOp(InsnIterator insn, EmitState * ...@@ -505,6 +506,10 @@ SpirvShader::EmitResult SpirvShader::EmitBinaryOp(InsnIterator insn, EmitState *
} }
} }
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; return EmitResult::Continue;
} }
...@@ -518,6 +523,11 @@ SpirvShader::EmitResult SpirvShader::EmitDot(InsnIterator insn, EmitState *state ...@@ -518,6 +523,11 @@ SpirvShader::EmitResult SpirvShader::EmitDot(InsnIterator insn, EmitState *state
auto rhs = Operand(this, state, insn.word(4)); auto rhs = Operand(this, state, insn.word(4));
dst.move(0, Dot(lhsType.componentCount, lhs, rhs)); 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; return EmitResult::Continue;
} }
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "SpirvShader.hpp" #include "SpirvShader.hpp"
#include "SpirvShaderDebug.hpp"
#include "Reactor/Coroutine.hpp" // rr::Yield #include "Reactor/Coroutine.hpp" // rr::Yield
...@@ -298,8 +299,10 @@ void SpirvShader::EmitNonLoop(EmitState *state) const ...@@ -298,8 +299,10 @@ void SpirvShader::EmitNonLoop(EmitState *state) const
for(auto in : block.ins) for(auto in : block.ins)
{ {
auto inMask = GetActiveLaneMaskEdge(state, in, blockId); auto inMask = GetActiveLaneMaskEdge(state, in, blockId);
SPIRV_SHADER_DBG("Block {0} -> {1} mask: {2}", in, blockId, inMask);
activeLaneMask |= inMask; activeLaneMask |= inMask;
} }
SPIRV_SHADER_DBG("Block {0} mask: {1}", blockId, activeLaneMask);
SetActiveLaneMask(activeLaneMask, state); SetActiveLaneMask(activeLaneMask, state);
} }
...@@ -312,6 +315,8 @@ void SpirvShader::EmitNonLoop(EmitState *state) const ...@@ -312,6 +315,8 @@ void SpirvShader::EmitNonLoop(EmitState *state) const
state->pending->push_back(out); state->pending->push_back(out);
} }
} }
SPIRV_SHADER_DBG("Block {0} done", blockId);
} }
void SpirvShader::EmitLoop(EmitState *state) const void SpirvShader::EmitLoop(EmitState *state) const
...@@ -327,6 +332,8 @@ void SpirvShader::EmitLoop(EmitState *state) const ...@@ -327,6 +332,8 @@ void SpirvShader::EmitLoop(EmitState *state) const
return; // Already emitted this loop. return; // Already emitted this loop.
} }
SPIRV_SHADER_DBG("*** LOOP HEADER ***");
// Gather all the blocks that make up the loop. // Gather all the blocks that make up the loop.
std::unordered_set<Block::ID> loopBlocks; std::unordered_set<Block::ID> loopBlocks;
loopBlocks.emplace(block.mergeBlock); loopBlocks.emplace(block.mergeBlock);
...@@ -376,6 +383,8 @@ void SpirvShader::EmitLoop(EmitState *state) const ...@@ -376,6 +383,8 @@ void SpirvShader::EmitLoop(EmitState *state) const
Nucleus::createBr(headerBasicBlock); Nucleus::createBr(headerBasicBlock);
Nucleus::setInsertBlock(headerBasicBlock); Nucleus::setInsertBlock(headerBasicBlock);
SPIRV_SHADER_DBG("*** LOOP START (mask: {0}) ***", loopActiveLaneMask);
// Load the active lane mask. // Load the active lane mask.
SetActiveLaneMask(loopActiveLaneMask, state); SetActiveLaneMask(loopActiveLaneMask, state);
...@@ -432,6 +441,8 @@ void SpirvShader::EmitLoop(EmitState *state) const ...@@ -432,6 +441,8 @@ void SpirvShader::EmitLoop(EmitState *state) const
} }
} }
SPIRV_SHADER_DBG("*** LOOP END (mask: {0}) ***", loopActiveLaneMask);
// Use the [loop -> merge] active lane masks to update the phi values in // 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 // the merge block. We need to do this to handle divergent control flow
// in the loop. // in the loop.
...@@ -516,6 +527,7 @@ SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *st ...@@ -516,6 +527,7 @@ SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *st
auto sel = Operand(this, state, selId); auto sel = Operand(this, state, selId);
ASSERT_MSG(sel.componentCount == 1, "Selector must be a scalar"); ASSERT_MSG(sel.componentCount == 1, "Selector must be a scalar");
SPIRV_SHADER_DBG("switch({0})", sel);
auto numCases = (block.branchInstruction.wordCount() - 3) / 2; auto numCases = (block.branchInstruction.wordCount() - 3) / 2;
...@@ -531,11 +543,13 @@ SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *st ...@@ -531,11 +543,13 @@ SpirvShader::EmitResult SpirvShader::EmitSwitch(InsnIterator insn, EmitState *st
auto label = block.branchInstruction.word(i * 2 + 3); auto label = block.branchInstruction.word(i * 2 + 3);
auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4)); auto caseBlockId = Block::ID(block.branchInstruction.word(i * 2 + 4));
auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label)); auto caseLabelMatch = CmpEQ(sel.Int(0), SIMD::Int(label));
SPIRV_SHADER_DBG("case {0}: {1}", label, caseLabelMatch & state->activeLaneMask());
state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch); state->addOutputActiveLaneMaskEdge(caseBlockId, caseLabelMatch);
defaultLaneMask &= ~caseLabelMatch; defaultLaneMask &= ~caseLabelMatch;
} }
auto defaultBlockId = Block::ID(block.branchInstruction.word(2)); auto defaultBlockId = Block::ID(block.branchInstruction.word(2));
SPIRV_SHADER_DBG("default: {0}", defaultLaneMask);
state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask); state->addOutputActiveLaneMaskEdge(defaultBlockId, defaultLaneMask);
return EmitResult::Terminator; return EmitResult::Terminator;
...@@ -654,6 +668,7 @@ void SpirvShader::LoadPhi(InsnIterator insn, EmitState *state) const ...@@ -654,6 +668,7 @@ void SpirvShader::LoadPhi(InsnIterator insn, EmitState *state) const
for(uint32_t i = 0; i < type.componentCount; i++) for(uint32_t i = 0; i < type.componentCount; i++)
{ {
dst.move(i, storage[i]); dst.move(i, storage[i]);
SPIRV_SHADER_DBG("LoadPhi({0}.{1}): {2}", objectId, i, storage[i]);
} }
} }
...@@ -683,8 +698,15 @@ void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState ...@@ -683,8 +698,15 @@ void SpirvShader::StorePhi(Block::ID currentBlock, InsnIterator insn, EmitState
for(uint32_t i = 0; i < type.componentCount; i++) for(uint32_t i = 0; i < type.componentCount; i++)
{ {
storage[i] = As<SIMD::Float>((As<SIMD::Int>(storage[i]) & ~mask) | (in.Int(i) & mask)); 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 void SpirvShader::Fence(spv::MemorySemanticsMask semantics) const
......
// 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
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "SpirvShader.hpp" #include "SpirvShader.hpp"
#include "SpirvShaderDebug.hpp"
#include "ShaderCore.hpp" #include "ShaderCore.hpp"
...@@ -63,6 +64,8 @@ SpirvShader::EmitResult SpirvShader::EmitLoad(InsnIterator insn, EmitState *stat ...@@ -63,6 +64,8 @@ SpirvShader::EmitResult SpirvShader::EmitLoad(InsnIterator insn, EmitState *stat
dst.move(el.index, p.Load<SIMD::Float>(robustness, state->activeLaneMask(), atomic, memoryOrder)); 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; return EmitResult::Continue;
} }
...@@ -105,6 +108,8 @@ void SpirvShader::Store(Object::ID pointerId, const Operand &value, bool atomic, ...@@ -105,6 +108,8 @@ void SpirvShader::Store(Object::ID pointerId, const Operand &value, bool atomic,
mask = mask & state->storesAndAtomicsMask(); 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) { VisitMemoryObject(pointerId, [&](const MemoryElement &el) {
auto p = ptr + el.offset; auto p = ptr + el.offset;
if(interleavedByLane) { p = InterleaveByLane(p); } if(interleavedByLane) { p = InterleaveByLane(p); }
......
...@@ -35,6 +35,7 @@ namespace rr { ...@@ -35,6 +35,7 @@ namespace rr {
// * Static arrays in the form T[N] where T can be any of the above. // * Static arrays in the form T[N] where T can be any of the above.
class PrintValue class PrintValue
{ {
public:
// Ty is a template that can be specialized for printing type T. // Ty is a template that can be specialized for printing type T.
// Each specialization must expose: // Each specialization must expose:
// * A 'static std::string fmt(const T& v)' method that provides the // * A 'static std::string fmt(const T& v)' method that provides the
...@@ -83,7 +84,6 @@ class PrintValue ...@@ -83,7 +84,6 @@ class PrintValue
return buf; return buf;
} }
public:
const std::string format; const std::string format;
const std::vector<Value *> values; const std::vector<Value *> values;
...@@ -456,6 +456,9 @@ static_assert(3 == RR_COUNT_ARGUMENTS(a, b, c), "RR_COUNT_ARGUMENTS broken"); ...@@ -456,6 +456,9 @@ static_assert(3 == RR_COUNT_ARGUMENTS(a, b, c), "RR_COUNT_ARGUMENTS broken");
} // namespace rr } // namespace rr
# define RR_PRINT_ONLY(x) x
#else
# define RR_PRINT_ONLY(x)
#endif // ENABLE_RR_PRINT #endif // ENABLE_RR_PRINT
#endif // rr_Print_hpp #endif // rr_Print_hpp
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