Commit 2cb1db08 by Ben Clayton

SpirvShaderDebugger: Implement OpenCL.Debug.100

This is a first, rough, implementation of the OpenCL.Debug.100 extension instruction set. This is incomplete, and likely buggy. It does however implement enough to perform single line stepping through hand-crafted HLSL, with basic variable inspection. Many tests and fixes to come... Bug: b/145351270 Change-Id: Ia570f5b22a8b3ce39df0438ca3c6700d1a00d014 Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/40268Reviewed-by: 's avatarAntonio Maiorano <amaiorano@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com>
parent 5c6a3c24
...@@ -24,10 +24,31 @@ ...@@ -24,10 +24,31 @@
# include "Vulkan/Debug/Thread.hpp" # include "Vulkan/Debug/Thread.hpp"
# include "Vulkan/Debug/Variable.hpp" # include "Vulkan/Debug/Variable.hpp"
# include "spirv-tools/ext/OpenCLDebugInfo100.h"
# include "spirv-tools/libspirv.h" # include "spirv-tools/libspirv.h"
# include <algorithm> # include <algorithm>
namespace {
// ArgTy<F>::type resolves to the single argument type of the function F.
template<typename F>
struct ArgTy
{
using type = typename ArgTy<decltype(&F::operator())>::type;
};
template<typename R, typename C, typename Arg>
struct ArgTy<R (C::*)(Arg) const>
{
using type = typename std::decay<Arg>::type;
};
template<typename T>
using ArgTyT = typename ArgTy<T>::type;
} // anonymous namespace
namespace spvtools { namespace spvtools {
// Function implemented in third_party/SPIRV-Tools/source/disassemble.cpp // Function implemented in third_party/SPIRV-Tools/source/disassemble.cpp
...@@ -54,6 +75,10 @@ std::string tostring(const T &s) ...@@ -54,6 +75,10 @@ std::string tostring(const T &s)
{ {
return std::to_string(s); return std::to_string(s);
} }
std::string tostring(char *s)
{
return s;
}
std::string tostring(const char *s) std::string tostring(const char *s)
{ {
return s; return s;
...@@ -63,6 +88,258 @@ std::string tostring(sw::SpirvShader::Object::ID id) ...@@ -63,6 +88,258 @@ std::string tostring(sw::SpirvShader::Object::ID id)
return "%" + std::to_string(id.value()); return "%" + std::to_string(id.value());
} }
////////////////////////////////////////////////////////////////////////////////
// OpenCL.Debug.100 data structures
////////////////////////////////////////////////////////////////////////////////
namespace debug {
struct Member;
struct Object
{
enum class Kind
{
Object,
Declare,
Expression,
Function,
InlinedAt,
LocalVariable,
Member,
Operation,
Source,
SourceScope,
Value,
// Scopes
CompilationUnit,
LexicalBlock,
// Types
BasicType,
VectorType,
FunctionType,
CompositeType,
};
using ID = sw::SpirvID<Object>;
static constexpr auto KIND = Kind::Object;
inline Object(Kind kind)
: kind(kind)
{}
const Kind kind;
// kindof() returns true iff kind is of this type, or any type deriving from
// this type.
static constexpr bool kindof(Object::Kind kind) { return true; }
};
template<typename TYPE_, typename BASE, Object::Kind KIND_>
struct ObjectImpl : public BASE
{
using ID = sw::SpirvID<TYPE_>;
static constexpr auto KIND = KIND_;
ObjectImpl()
: BASE(KIND)
{}
static_assert(BASE::kindof(KIND), "BASE::kindof() returned false");
// kindof() returns true iff kind is of this type, or any type deriving from
// this type.
static constexpr bool kindof(Object::Kind kind) { return kind == KIND; }
};
template<typename TO, typename FROM>
TO *cast(FROM *obj)
{
return (TO::kindof(obj->kind)) ? static_cast<TO *>(obj) : nullptr;
}
template<typename TO, typename FROM>
const TO *cast(const FROM *obj)
{
return (TO::kindof(obj->kind)) ? static_cast<const TO *>(obj) : nullptr;
}
struct Scope : public Object
{
// Root represents the root stack frame scope.
static const Scope Root;
using ID = sw::SpirvID<Scope>;
inline Scope(Kind kind)
: Object(kind)
{}
// kindof() returns true iff kind is of this type, or any type deriving from
// this type.
static constexpr bool kindof(Kind kind)
{
return kind == Kind::CompilationUnit ||
kind == Kind::LexicalBlock;
}
struct Source *source = nullptr;
};
struct Type : public Object
{
using ID = sw::SpirvID<Type>;
inline Type(Kind kind)
: Object(kind)
{}
// kindof() returns true iff kind is of this type, or any type deriving from
// this type.
static constexpr bool kindof(Kind kind)
{
return kind == Kind::BasicType ||
kind == Kind::VectorType ||
kind == Kind::FunctionType ||
kind == Kind::CompositeType;
}
};
struct CompilationUnit : ObjectImpl<CompilationUnit, Scope, Object::Kind::CompilationUnit>
{
};
struct Source : ObjectImpl<Source, Object, Object::Kind::Source>
{
spv::SourceLanguage language;
uint32_t version = 0;
std::string file;
std::string source;
std::shared_ptr<vk::dbg::File> dbgFile;
};
struct BasicType : ObjectImpl<BasicType, Type, Object::Kind::BasicType>
{
std::string name;
uint32_t size = 0; // in bits.
OpenCLDebugInfo100DebugBaseTypeAttributeEncoding encoding = OpenCLDebugInfo100Unspecified;
};
struct VectorType : ObjectImpl<VectorType, Type, Object::Kind::VectorType>
{
Type *base = nullptr;
uint32_t components = 0;
};
struct FunctionType : ObjectImpl<FunctionType, Type, Object::Kind::FunctionType>
{
uint32_t flags = 0; // OR'd from OpenCLDebugInfo100DebugInfoFlags
Type *returnTy = nullptr;
std::vector<Type *> paramTys;
};
struct CompositeType : ObjectImpl<CompositeType, Type, Object::Kind::CompositeType>
{
std::string name;
OpenCLDebugInfo100DebugCompositeType tag = OpenCLDebugInfo100Class;
Source *source = nullptr;
uint32_t line = 0;
uint32_t column = 0;
Object *parent = nullptr;
std::string linkage;
uint32_t size = 0; // in bits.
uint32_t flags = 0; // OR'd from OpenCLDebugInfo100DebugInfoFlags
std::vector<Member *> members;
};
struct Member : ObjectImpl<Member, Object, Object::Kind::Member>
{
std::string name;
Type *type = nullptr;
Source *source = nullptr;
uint32_t line = 0;
uint32_t column = 0;
CompositeType *parent = nullptr;
uint32_t offset = 0; // in bits
uint32_t size = 0; // in bits
uint32_t flags = 0; // OR'd from OpenCLDebugInfo100DebugInfoFlags
};
struct Function : ObjectImpl<Function, Object, Object::Kind::Function>
{
std::string name;
FunctionType *type = nullptr;
Source *source = nullptr;
uint32_t line = 0;
uint32_t column = 0;
struct LexicalBlock *parent = nullptr;
std::string linkage;
uint32_t flags = 0; // OR'd from OpenCLDebugInfo100DebugInfoFlags
uint32_t scopeLine = 0;
sw::SpirvShader::Function::ID function;
};
struct LexicalBlock : ObjectImpl<LexicalBlock, Scope, Object::Kind::LexicalBlock>
{
uint32_t line = 0;
uint32_t column = 0;
Scope *parent = nullptr;
std::string name;
Function *function = nullptr;
};
struct InlinedAt : ObjectImpl<InlinedAt, Object, Object::Kind::InlinedAt>
{
uint32_t line = 0;
Scope *scope = nullptr;
InlinedAt *inlined = nullptr;
};
struct SourceScope : ObjectImpl<SourceScope, Object, Object::Kind::SourceScope>
{
LexicalBlock *scope = nullptr;
InlinedAt *inlinedAt = nullptr;
};
struct LocalVariable : ObjectImpl<LocalVariable, Object, Object::Kind::LocalVariable>
{
static constexpr uint32_t NoArg = ~uint32_t(0);
std::string name;
Type *type = nullptr;
Source *source = nullptr;
uint32_t line = 0;
uint32_t column = 0;
Scope *parent = nullptr;
uint32_t arg = NoArg;
};
struct Operation : ObjectImpl<Operation, Object, Object::Kind::Operation>
{
uint32_t opcode = 0;
std::vector<uint32_t> operands;
};
struct Expression : ObjectImpl<Expression, Object, Object::Kind::Expression>
{
std::vector<Operation *> operations;
};
struct Declare : ObjectImpl<Declare, Object, Object::Kind::Declare>
{
LocalVariable *local = nullptr;
sw::SpirvShader::Object::ID variable;
Expression *expression = nullptr;
};
struct Value : ObjectImpl<Value, Object, Object::Kind::Value>
{
LocalVariable *local = nullptr;
sw::SpirvShader::Object::ID variable;
Expression *expression = nullptr;
std::vector<uint32_t> indexes;
};
const Scope Scope::Root = CompilationUnit{};
} // namespace debug
} // anonymous namespace } // anonymous namespace
namespace rr { namespace rr {
...@@ -98,6 +375,14 @@ struct SpirvShader::Impl::Debugger ...@@ -98,6 +375,14 @@ struct SpirvShader::Impl::Debugger
class Group; class Group;
class State; class State;
enum class Pass
{
Define,
Emit
};
void process(const SpirvShader *shader, const InsnIterator &insn, EmitState *state, Pass pass);
void setPosition(EmitState *state, const std::string &path, uint32_t line, uint32_t column); void setPosition(EmitState *state, const std::string &path, uint32_t line, uint32_t column);
// exposeVariable exposes the variable with the given ID to the debugger // exposeVariable exposes the variable with the given ID to the debugger
...@@ -106,6 +391,8 @@ struct SpirvShader::Impl::Debugger ...@@ -106,6 +391,8 @@ struct SpirvShader::Impl::Debugger
void exposeVariable( void exposeVariable(
const SpirvShader *shader, const SpirvShader *shader,
const Key &key, const Key &key,
const debug::Scope *scope,
const debug::Type *type,
Object::ID id, Object::ID id,
EmitState *state) const; EmitState *state) const;
...@@ -117,6 +404,7 @@ struct SpirvShader::Impl::Debugger ...@@ -117,6 +404,7 @@ struct SpirvShader::Impl::Debugger
const Group &group, const Group &group,
int lane, int lane,
const Key &key, const Key &key,
const debug::Type *type,
Object::ID id, Object::ID id,
EmitState *state, EmitState *state,
int wordOffset = 0) const; int wordOffset = 0) const;
...@@ -127,7 +415,32 @@ struct SpirvShader::Impl::Debugger ...@@ -127,7 +415,32 @@ struct SpirvShader::Impl::Debugger
std::unordered_map<const void *, Object::ID> results; // instruction pointer to result ID std::unordered_map<const void *, Object::ID> results; // instruction pointer to result ID
private: private:
// add() registers the debug object with the given id.
template<typename ID, typename T>
void add(ID id, T *);
// get() returns the debug object with the given id.
// The object must exist and be of type (or derive from type) T.
template<typename T>
T *get(SpirvID<T> id) const;
// use get() and add() to access this
std::unordered_map<debug::Object::ID, std::unique_ptr<debug::Object>> objects;
std::unordered_map<std::string, vk::dbg::File::ID> fileIDs; std::unordered_map<std::string, vk::dbg::File::ID> fileIDs;
// defineOrEmit() when called in Pass::Define, creates and stores a
// zero-initialized object into the Debugger::objects map using the
// object identifier held by second instruction operand.
// When called in Pass::Emit, defineOrEmit() calls the function F with the
// previously-built object.
//
// F must be a function with the signature:
// void(OBJECT_TYPE *)
//
// The object type is automatically inferred from the function signature.
template<typename F, typename T = typename std::remove_pointer<ArgTyT<F>>::type>
void defineOrEmit(InsnIterator insn, Pass pass, F &&emit);
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
...@@ -148,10 +461,12 @@ public: ...@@ -148,10 +461,12 @@ public:
void exit(); void exit();
void updateActiveLaneMask(int lane, bool enabled); void updateActiveLaneMask(int lane, bool enabled);
void update(vk::dbg::File::ID file, int line, int column); void update(vk::dbg::File::ID file, int line, int column);
void createScope(const debug::Scope *);
void setScope(debug::SourceScope *newScope);
vk::dbg::VariableContainer *locals(); vk::dbg::VariableContainer *locals(const debug::Scope *);
vk::dbg::VariableContainer *hovers(); vk::dbg::VariableContainer *hovers(const debug::Scope *);
vk::dbg::VariableContainer *localsLane(int lane); vk::dbg::VariableContainer *localsLane(const debug::Scope *, int lane);
template<typename K> template<typename K>
vk::dbg::VariableContainer *group(vk::dbg::VariableContainer *vc, K key); vk::dbg::VariableContainer *group(vk::dbg::VariableContainer *vc, K key);
...@@ -159,6 +474,11 @@ public: ...@@ -159,6 +474,11 @@ public:
template<typename K, typename V> template<typename K, typename V>
void putVal(vk::dbg::VariableContainer *vc, K key, V value); void putVal(vk::dbg::VariableContainer *vc, K key, V value);
template<typename K, typename V>
void putRef(vk::dbg::VariableContainer *vc, K key, V *ptr);
// Scopes holds pointers to the vk::dbg::Scopes for local variables, hover
// variables and the locals indexed by SIMD lane.
struct Scopes struct Scopes
{ {
std::shared_ptr<vk::dbg::Scope> locals; std::shared_ptr<vk::dbg::Scope> locals;
...@@ -166,9 +486,14 @@ public: ...@@ -166,9 +486,14 @@ public:
std::array<std::shared_ptr<vk::dbg::VariableContainer>, sw::SIMD::Width> localsByLane; std::array<std::shared_ptr<vk::dbg::VariableContainer>, sw::SIMD::Width> localsByLane;
}; };
// getScopes() returns the Scopes object for the given debug::Scope.
const Scopes &getScopes(const debug::Scope *scope);
const Debugger *debugger; const Debugger *debugger;
const std::shared_ptr<vk::dbg::Thread> thread; const std::shared_ptr<vk::dbg::Thread> thread;
Scopes threadScopes; std::unordered_map<const debug::Scope *, Scopes> scopes;
Scopes rootScopes; // Scopes for the root stack frame.
debug::SourceScope *srcScope = nullptr; // Current source scope.
}; };
SpirvShader::Impl::Debugger::State *SpirvShader::Impl::Debugger::State::create(const Debugger *debugger, const char *name) SpirvShader::Impl::Debugger::State *SpirvShader::Impl::Debugger::State::create(const Debugger *debugger, const char *name)
...@@ -188,12 +513,12 @@ SpirvShader::Impl::Debugger::State::State(const Debugger *debugger, const char * ...@@ -188,12 +513,12 @@ SpirvShader::Impl::Debugger::State::State(const Debugger *debugger, const char *
{ {
enter(lock, stackBase); enter(lock, stackBase);
thread->update([&](vk::dbg::Frame &frame) { thread->update([&](vk::dbg::Frame &frame) {
threadScopes.locals = frame.locals; rootScopes.locals = frame.locals;
threadScopes.hovers = frame.hovers; rootScopes.hovers = frame.hovers;
for(int i = 0; i < sw::SIMD::Width; i++) for(int i = 0; i < sw::SIMD::Width; i++)
{ {
threadScopes.localsByLane[i] = lock.createVariableContainer(); rootScopes.localsByLane[i] = lock.createVariableContainer();
frame.locals->variables->put(laneNames[i], threadScopes.localsByLane[i]); frame.locals->variables->put(laneNames[i], rootScopes.localsByLane[i]);
} }
}); });
} }
...@@ -215,7 +540,7 @@ void SpirvShader::Impl::Debugger::State::exit() ...@@ -215,7 +540,7 @@ void SpirvShader::Impl::Debugger::State::exit()
void SpirvShader::Impl::Debugger::State::updateActiveLaneMask(int lane, bool enabled) void SpirvShader::Impl::Debugger::State::updateActiveLaneMask(int lane, bool enabled)
{ {
threadScopes.localsByLane[lane]->put("enabled", vk::dbg::make_constant(enabled)); rootScopes.localsByLane[lane]->put("enabled", vk::dbg::make_constant(enabled));
} }
void SpirvShader::Impl::Debugger::State::update(vk::dbg::File::ID fileID, int line, int column) void SpirvShader::Impl::Debugger::State::update(vk::dbg::File::ID fileID, int line, int column)
...@@ -226,19 +551,19 @@ void SpirvShader::Impl::Debugger::State::update(vk::dbg::File::ID fileID, int li ...@@ -226,19 +551,19 @@ void SpirvShader::Impl::Debugger::State::update(vk::dbg::File::ID fileID, int li
}); });
} }
vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::locals() vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::locals(const debug::Scope *scope)
{ {
return threadScopes.locals->variables.get(); return getScopes(scope).locals->variables.get();
} }
vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::hovers() vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::hovers(const debug::Scope *scope)
{ {
return threadScopes.hovers->variables.get(); return getScopes(scope).hovers->variables.get();
} }
vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::localsLane(int i) vk::dbg::VariableContainer *SpirvShader::Impl::Debugger::State::localsLane(const debug::Scope *scope, int i)
{ {
return threadScopes.localsByLane[i].get(); return getScopes(scope).localsByLane[i].get();
} }
template<typename K> template<typename K>
...@@ -255,6 +580,67 @@ void SpirvShader::Impl::Debugger::State::putVal(vk::dbg::VariableContainer *vc, ...@@ -255,6 +580,67 @@ void SpirvShader::Impl::Debugger::State::putVal(vk::dbg::VariableContainer *vc,
vc->put(tostring(key), vk::dbg::make_constant(value)); vc->put(tostring(key), vk::dbg::make_constant(value));
} }
template<typename K, typename V>
void SpirvShader::Impl::Debugger::State::putRef(vk::dbg::VariableContainer *vc, K key, V *ptr)
{
vc->put(tostring(key), vk::dbg::make_reference(*ptr));
}
void SpirvShader::Impl::Debugger::State::createScope(const debug::Scope *spirvScope)
{
ASSERT(spirvScope != nullptr);
// TODO: Deal with scope nesting.
auto lock = debugger->ctx->lock();
Scopes s = {};
s.locals = lock.createScope(spirvScope->source->dbgFile);
s.hovers = lock.createScope(spirvScope->source->dbgFile);
for(int i = 0; i < sw::SIMD::Width; i++)
{
s.localsByLane[i] = lock.createVariableContainer();
s.locals->variables->put(laneNames[i], s.localsByLane[i]);
}
scopes.emplace(spirvScope, std::move(s));
}
void SpirvShader::Impl::Debugger::State::setScope(debug::SourceScope *newSrcScope)
{
auto oldSrcScope = srcScope;
if(oldSrcScope == newSrcScope) { return; }
srcScope = newSrcScope;
auto lock = debugger->ctx->lock();
auto thread = lock.currentThread();
debug::Function *oldFunction = oldSrcScope ? oldSrcScope->scope->function : nullptr;
debug::Function *newFunction = newSrcScope ? newSrcScope->scope->function : nullptr;
if(oldFunction != newFunction)
{
if(oldFunction) { thread->exit(); }
if(newFunction) { thread->enter(lock, newFunction->source->dbgFile, newFunction->name); }
}
auto dbgScope = getScopes(srcScope->scope);
thread->update([&](vk::dbg::Frame &frame) {
frame.locals = dbgScope.locals;
frame.hovers = dbgScope.hovers;
});
}
const SpirvShader::Impl::Debugger::State::Scopes &SpirvShader::Impl::Debugger::State::getScopes(const debug::Scope *scope)
{
if(scope == &debug::Scope::Root)
{
return rootScopes;
}
auto dbgScopeIt = scopes.find(scope);
ASSERT_MSG(dbgScopeIt != scopes.end(), "createScope() not called for debug::Scope %p", scope);
return dbgScopeIt->second;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// sw::SpirvShader::Impl::Debugger::Group // sw::SpirvShader::Impl::Debugger::Group
// //
...@@ -266,9 +652,9 @@ class SpirvShader::Impl::Debugger::Group ...@@ -266,9 +652,9 @@ class SpirvShader::Impl::Debugger::Group
public: public:
using Ptr = rr::Pointer<rr::Byte>; using Ptr = rr::Pointer<rr::Byte>;
static Group hovers(Ptr state); static Group hovers(Ptr state, const debug::Scope *scope);
static Group locals(Ptr state); static Group locals(Ptr state, const debug::Scope *scope);
static Group localsLane(Ptr state, int lane); static Group localsLane(Ptr state, const debug::Scope *scope, int lane);
Group(Ptr state, Ptr group); Group(Ptr state, Ptr group);
...@@ -278,6 +664,9 @@ public: ...@@ -278,6 +664,9 @@ public:
template<typename K, typename V, typename RK, typename RV> template<typename K, typename V, typename RK, typename RV>
void put(RK key, RV value) const; void put(RK key, RV value) const;
template<typename K, typename V, typename RK>
void putRef(RK key, RValue<Pointer<Byte>> ref) const;
template<typename K, typename V, typename RK, typename RV> template<typename K, typename V, typename RK, typename RV>
void put(RK key, RV x, RV y) const; void put(RK key, RV x, RV y) const;
...@@ -296,21 +685,21 @@ private: ...@@ -296,21 +685,21 @@ private:
}; };
SpirvShader::Impl::Debugger::Group SpirvShader::Impl::Debugger::Group
SpirvShader::Impl::Debugger::Group::hovers(Ptr state) SpirvShader::Impl::Debugger::Group::hovers(Ptr state, const debug::Scope *scope)
{ {
return Group(state, rr::Call(&State::hovers, state)); return Group(state, rr::Call(&State::hovers, state, scope));
} }
SpirvShader::Impl::Debugger::Group SpirvShader::Impl::Debugger::Group
SpirvShader::Impl::Debugger::Group::locals(Ptr state) SpirvShader::Impl::Debugger::Group::locals(Ptr state, const debug::Scope *scope)
{ {
return Group(state, rr::Call(&State::locals, state)); return Group(state, rr::Call(&State::locals, state, scope));
} }
SpirvShader::Impl::Debugger::Group SpirvShader::Impl::Debugger::Group
SpirvShader::Impl::Debugger::Group::localsLane(Ptr state, int lane) SpirvShader::Impl::Debugger::Group::localsLane(Ptr state, const debug::Scope *scope, int lane)
{ {
return Group(state, rr::Call(&State::localsLane, state, lane)); return Group(state, rr::Call(&State::localsLane, state, scope, lane));
} }
SpirvShader::Impl::Debugger::Group::Group(Ptr state, Ptr group) SpirvShader::Impl::Debugger::Group::Group(Ptr state, Ptr group)
...@@ -330,6 +719,12 @@ void SpirvShader::Impl::Debugger::Group::put(RK key, RV value) const ...@@ -330,6 +719,12 @@ void SpirvShader::Impl::Debugger::Group::put(RK key, RV value) const
rr::Call(&State::putVal<K, V>, state, ptr, key, value); rr::Call(&State::putVal<K, V>, state, ptr, key, value);
} }
template<typename K, typename V, typename RK>
void SpirvShader::Impl::Debugger::Group::putRef(RK key, RValue<Pointer<Byte>> ref) const
{
rr::Call(&State::putRef<K, V>, state, ptr, key, ref);
}
template<typename K, typename V, typename RK, typename RV> template<typename K, typename V, typename RK, typename RV>
void SpirvShader::Impl::Debugger::Group::put(RK key, RV x, RV y) const void SpirvShader::Impl::Debugger::Group::put(RK key, RV x, RV y) const
{ {
...@@ -369,6 +764,201 @@ void SpirvShader::Impl::Debugger::Group::putVec3(K key, const VEC &v) const ...@@ -369,6 +764,201 @@ void SpirvShader::Impl::Debugger::Group::putVec3(K key, const VEC &v) const
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// sw::SpirvShader::Impl::Debugger methods // sw::SpirvShader::Impl::Debugger methods
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
template<typename F, typename T>
void SpirvShader::Impl::Debugger::defineOrEmit(InsnIterator insn, Pass pass, F &&emit)
{
auto id = SpirvID<T>(insn.word(2));
switch(pass)
{
case Pass::Define:
add(id, new T());
break;
case Pass::Emit:
emit(get<T>(id));
break;
}
}
void SpirvShader::Impl::Debugger::process(const SpirvShader *shader, const InsnIterator &insn, EmitState *state, Pass pass)
{
auto dbg = shader->impl.debugger;
auto extInstIndex = insn.word(4);
switch(extInstIndex)
{
case OpenCLDebugInfo100DebugCompilationUnit:
defineOrEmit(insn, pass, [&](debug::CompilationUnit *cu) {
cu->source = get(debug::Source::ID(insn.word(7)));
});
break;
case OpenCLDebugInfo100DebugTypeBasic:
defineOrEmit(insn, pass, [&](debug::BasicType *type) {
type->name = shader->getString(insn.word(5));
type->size = shader->GetConstScalarInt(insn.word(6));
type->encoding = static_cast<OpenCLDebugInfo100DebugBaseTypeAttributeEncoding>(insn.word(7));
});
break;
case OpenCLDebugInfo100DebugTypeVector:
defineOrEmit(insn, pass, [&](debug::VectorType *type) {
type->base = get(debug::Type::ID(insn.word(5)));
type->components = insn.word(6);
});
break;
case OpenCLDebugInfo100DebugTypeFunction:
defineOrEmit(insn, pass, [&](debug::FunctionType *type) {
type->flags = insn.word(5);
type->returnTy = get(debug::Type::ID(insn.word(6)));
for(uint32_t i = 7; i < insn.wordCount(); i++)
{
type->paramTys.push_back(get(debug::Type::ID(insn.word(i))));
}
});
break;
case OpenCLDebugInfo100DebugTypeComposite:
defineOrEmit(insn, pass, [&](debug::CompositeType *type) {
type->name = shader->getString(insn.word(5));
type->tag = static_cast<OpenCLDebugInfo100DebugCompositeType>(insn.word(6));
type->source = get(debug::Source::ID(insn.word(7)));
type->line = insn.word(8);
type->column = insn.word(9);
type->parent = get(debug::Object::ID(insn.word(10)));
type->linkage = shader->getString(insn.word(11));
type->size = shader->GetConstScalarInt(insn.word(12));
type->flags = insn.word(13);
for(uint32_t i = 14; i < insn.wordCount(); i++)
{
auto obj = get(debug::Object::ID(insn.word(i)));
if(auto member = debug::cast<debug::Member>(obj)) // Can also be Function or TypeInheritance, which we don't care about.
{
type->members.push_back(member);
}
}
});
break;
case OpenCLDebugInfo100DebugTypeMember:
defineOrEmit(insn, pass, [&](debug::Member *member) {
member->name = shader->getString(insn.word(5));
member->type = get(debug::Type::ID(insn.word(6)));
member->source = get(debug::Source::ID(insn.word(7)));
member->line = insn.word(8);
member->column = insn.word(9);
member->parent = get(debug::CompositeType::ID(insn.word(10)));
member->offset = shader->GetConstScalarInt(insn.word(11));
member->size = shader->GetConstScalarInt(insn.word(12));
member->flags = insn.word(13);
});
break;
case OpenCLDebugInfo100DebugFunction:
defineOrEmit(insn, pass, [&](debug::Function *func) {
func->name = shader->getString(insn.word(5));
func->type = get(debug::FunctionType::ID(insn.word(6)));
func->source = get(debug::Source::ID(insn.word(7)));
func->line = insn.word(8);
func->column = insn.word(9);
func->parent = get(debug::LexicalBlock::ID(insn.word(10)));
func->linkage = shader->getString(insn.word(11));
func->flags = insn.word(12);
func->scopeLine = insn.word(13);
func->function = Function::ID(insn.word(14));
// declaration: word(13)
func->parent->function = func;
});
break;
case OpenCLDebugInfo100DebugLexicalBlock:
defineOrEmit(insn, pass, [&](debug::LexicalBlock *scope) {
scope->source = get(debug::Source::ID(insn.word(5)));
scope->line = insn.word(6);
scope->column = insn.word(7);
scope->parent = get(debug::Scope::ID(insn.word(8)));
if(insn.wordCount() > 9)
{
scope->name = shader->getString(insn.word(9));
}
// TODO: We're creating scopes per-shader invocation.
// This is all really static information, and should only be created
// once *per program*.
rr::Call(&State::createScope, state->routine->dbgState, scope);
});
break;
case OpenCLDebugInfo100DebugScope:
defineOrEmit(insn, pass, [&](debug::SourceScope *ss) {
ss->scope = get(debug::LexicalBlock::ID(insn.word(5)));
if(insn.wordCount() > 6)
{
ss->inlinedAt = get(debug::InlinedAt::ID(insn.word(6)));
}
rr::Call(&State::setScope, state->routine->dbgState, ss);
});
break;
case OpenCLDebugInfo100DebugNoScope:
break;
case OpenCLDebugInfo100DebugLocalVariable:
defineOrEmit(insn, pass, [&](debug::LocalVariable *var) {
var->name = shader->getString(insn.word(5));
var->type = get(debug::Type::ID(insn.word(6)));
var->source = get(debug::Source::ID(insn.word(7)));
var->line = insn.word(8);
var->column = insn.word(9);
var->parent = get(debug::Scope::ID(insn.word(10)));
if(insn.wordCount() > 11)
{
var->arg = insn.word(11);
}
});
break;
case OpenCLDebugInfo100DebugDeclare:
defineOrEmit(insn, pass, [&](debug::Declare *decl) {
decl->local = get(debug::LocalVariable::ID(insn.word(5)));
decl->variable = Object::ID(insn.word(6));
decl->expression = get(debug::Expression::ID(insn.word(7)));
exposeVariable(
shader,
decl->local->name.c_str(),
decl->local->parent,
decl->local->type,
decl->variable,
state);
});
break;
case OpenCLDebugInfo100DebugValue:
defineOrEmit(insn, pass, [&](debug::Value *value) {
value->local = get(debug::LocalVariable::ID(insn.word(5)));
value->variable = Object::ID(insn.word(6));
value->expression = get(debug::Expression::ID(insn.word(7)));
for(uint32_t i = 8; i < insn.wordCount(); i++)
{
value->indexes.push_back(insn.word(i));
}
});
break;
case OpenCLDebugInfo100DebugExpression:
defineOrEmit(insn, pass, [&](debug::Expression *expr) {
for(uint32_t i = 5; i < insn.wordCount(); i++)
{
expr->operations.push_back(get(debug::Operation::ID(insn.word(i))));
}
});
break;
case OpenCLDebugInfo100DebugSource:
defineOrEmit(insn, pass, [&](debug::Source *source) {
source->file = shader->getString(insn.word(5));
if(insn.wordCount() > 6)
{
source->source = shader->getString(insn.word(6));
}
auto file = dbg->ctx->lock().createVirtualFile(source->file.c_str(), source->source.c_str());
source->dbgFile = file;
fileIDs.emplace(source->file.c_str(), file->id);
});
break;
default:
UNSUPPORTED("Unsupported OpenCLDebugInfo100 instruction %d", int(extInstIndex));
}
}
void SpirvShader::Impl::Debugger::setPosition(EmitState *state, const std::string &path, uint32_t line, uint32_t column) void SpirvShader::Impl::Debugger::setPosition(EmitState *state, const std::string &path, uint32_t line, uint32_t column)
{ {
auto it = fileIDs.find(path); auto it = fileIDs.find(path);
...@@ -378,19 +968,39 @@ void SpirvShader::Impl::Debugger::setPosition(EmitState *state, const std::strin ...@@ -378,19 +968,39 @@ void SpirvShader::Impl::Debugger::setPosition(EmitState *state, const std::strin
} }
} }
template<typename ID, typename T>
void SpirvShader::Impl::Debugger::add(ID id, T *obj)
{
auto added = objects.emplace(debug::Object::ID(id.value()), obj).second;
ASSERT_MSG(added, "Debug object with %d already exists", id.value());
}
template<typename T>
T *SpirvShader::Impl::Debugger::get(SpirvID<T> id) const
{
auto it = objects.find(debug::Object::ID(id.value()));
ASSERT_MSG(it != objects.end(), "Unknown debug object %d", id.value());
auto ptr = debug::cast<T>(it->second.get());
ASSERT_MSG(ptr, "Debug object %d is not of the correct type. Got: %d, want: %d",
id.value(), int(it->second->kind), int(T::KIND));
return ptr;
}
template<typename Key> template<typename Key>
void SpirvShader::Impl::Debugger::exposeVariable( void SpirvShader::Impl::Debugger::exposeVariable(
const SpirvShader *shader, const SpirvShader *shader,
const Key &key, const Key &key,
const debug::Scope *scope,
const debug::Type *type,
Object::ID id, Object::ID id,
EmitState *state) const EmitState *state) const
{ {
auto dbgState = state->routine->dbgState; auto dbgState = state->routine->dbgState;
auto hover = Group::hovers(dbgState).group<Key>(key); auto hover = Group::hovers(dbgState, scope).group<Key>(key);
for(int lane = 0; lane < SIMD::Width; lane++) for(int lane = 0; lane < SIMD::Width; lane++)
{ {
exposeVariable(shader, Group::localsLane(dbgState, lane), lane, key, id, state); exposeVariable(shader, Group::localsLane(dbgState, scope, lane), lane, key, type, id, state);
exposeVariable(shader, hover, lane, laneNames[lane], id, state); exposeVariable(shader, hover, lane, laneNames[lane], type, id, state);
} }
} }
...@@ -400,10 +1010,146 @@ void SpirvShader::Impl::Debugger::exposeVariable( ...@@ -400,10 +1010,146 @@ void SpirvShader::Impl::Debugger::exposeVariable(
const Group &group, const Group &group,
int l, int l,
const Key &key, const Key &key,
const debug::Type *type,
Object::ID id, Object::ID id,
EmitState *state, EmitState *state,
int wordOffset /* = 0 */) const int wordOffset /* = 0 */) const
{ {
if(type != nullptr)
{
if(auto ty = debug::cast<debug::BasicType>(type))
{
auto &obj = shader->getObject(id);
SIMD::Int val;
switch(obj.kind)
{
case Object::Kind::InterfaceVariable:
case Object::Kind::Pointer:
{
auto ptr = shader->GetPointerToData(id, 0, state) + sizeof(uint32_t) * wordOffset;
auto &ptrTy = shader->getType(obj.type);
if(IsStorageInterleavedByLane(ptrTy.storageClass))
{
ptr = InterleaveByLane(ptr);
}
auto addr = &ptr.base[Extract(ptr.offsets(), l)];
switch(ty->encoding)
{
case OpenCLDebugInfo100Address:
// TODO: This function takes a SIMD vector, and pointers cannot
// be held in them.
break;
case OpenCLDebugInfo100Boolean:
group.putRef<Key, bool>(key, addr);
break;
case OpenCLDebugInfo100Float:
group.putRef<Key, float>(key, addr);
break;
case OpenCLDebugInfo100Signed:
group.putRef<Key, int>(key, addr);
break;
case OpenCLDebugInfo100SignedChar:
group.putRef<Key, int8_t>(key, addr);
break;
case OpenCLDebugInfo100Unsigned:
group.putRef<Key, unsigned int>(key, addr);
break;
case OpenCLDebugInfo100UnsignedChar:
group.putRef<Key, uint8_t>(key, addr);
break;
default:
break;
}
}
break;
case Object::Kind::Constant:
case Object::Kind::Intermediate:
{
auto val = GenericValue(shader, state, id).Int(wordOffset);
switch(ty->encoding)
{
case OpenCLDebugInfo100Address:
// TODO: This function takes a SIMD vector, and pointers cannot
// be held in them.
break;
case OpenCLDebugInfo100Boolean:
group.put<Key, bool>(key, Extract(val, l) != 0);
break;
case OpenCLDebugInfo100Float:
group.put<Key, float>(key, Extract(As<SIMD::Float>(val), l));
break;
case OpenCLDebugInfo100Signed:
group.put<Key, int>(key, Extract(val, l));
break;
case OpenCLDebugInfo100SignedChar:
group.put<Key, int8_t>(key, SByte(Extract(val, l)));
break;
case OpenCLDebugInfo100Unsigned:
group.put<Key, unsigned int>(key, Extract(val, l));
break;
case OpenCLDebugInfo100UnsignedChar:
group.put<Key, uint8_t>(key, Byte(Extract(val, l)));
break;
default:
break;
}
}
break;
default:
break;
}
return;
}
else if(auto ty = debug::cast<debug::VectorType>(type))
{
auto elWords = 1; // Currently vector elements must only be basic types, 32-bit wide
auto elTy = ty->base;
auto vecGroup = group.group<Key>(key);
switch(ty->components)
{
case 1:
exposeVariable(shader, vecGroup, l, "x", elTy, id, state, wordOffset + 0 * elWords);
break;
case 2:
exposeVariable(shader, vecGroup, l, "x", elTy, id, state, wordOffset + 0 * elWords);
exposeVariable(shader, vecGroup, l, "y", elTy, id, state, wordOffset + 1 * elWords);
break;
case 3:
exposeVariable(shader, vecGroup, l, "x", elTy, id, state, wordOffset + 0 * elWords);
exposeVariable(shader, vecGroup, l, "y", elTy, id, state, wordOffset + 1 * elWords);
exposeVariable(shader, vecGroup, l, "z", elTy, id, state, wordOffset + 2 * elWords);
break;
case 4:
exposeVariable(shader, vecGroup, l, "x", elTy, id, state, wordOffset + 0 * elWords);
exposeVariable(shader, vecGroup, l, "y", elTy, id, state, wordOffset + 1 * elWords);
exposeVariable(shader, vecGroup, l, "z", elTy, id, state, wordOffset + 2 * elWords);
exposeVariable(shader, vecGroup, l, "w", elTy, id, state, wordOffset + 3 * elWords);
break;
default:
for(uint32_t i = 0; i < ty->components; i++)
{
exposeVariable(shader, vecGroup, l, std::to_string(i).c_str(), elTy, id, state, wordOffset + i * elWords);
}
break;
}
return;
}
else if(auto ty = debug::cast<debug::CompositeType>(type))
{
auto objectGroup = group.group<Key>(key);
for(auto member : ty->members)
{
auto memberGroup = objectGroup.template group<const char *>(member->name.c_str());
exposeVariable(shader, memberGroup, l, member->name.c_str(), member->type, id, state, member->offset / 32);
}
return;
}
}
// No debug type information. Derive from SPIR-V.
GenericValue val(shader, state, id); GenericValue val(shader, state, id);
switch(shader->getType(val.type).opcode()) switch(shader->getType(val.type).opcode())
{ {
...@@ -539,7 +1285,7 @@ void SpirvShader::dbgBeginEmit(EmitState *state) const ...@@ -539,7 +1285,7 @@ void SpirvShader::dbgBeginEmit(EmitState *state) const
SetActiveLaneMask(state->activeLaneMask(), state); SetActiveLaneMask(state->activeLaneMask(), state);
auto locals = Group::locals(dbgState); auto locals = Group::locals(dbgState, &debug::Scope::Root);
locals.put<const char *, int>("subgroupSize", routine->invocationsPerSubgroup); locals.put<const char *, int>("subgroupSize", routine->invocationsPerSubgroup);
switch(executionModel) switch(executionModel)
...@@ -553,7 +1299,7 @@ void SpirvShader::dbgBeginEmit(EmitState *state) const ...@@ -553,7 +1299,7 @@ void SpirvShader::dbgBeginEmit(EmitState *state) const
for(int i = 0; i < SIMD::Width; i++) for(int i = 0; i < SIMD::Width; i++)
{ {
auto lane = Group::localsLane(dbgState, i); auto lane = Group::localsLane(dbgState, &debug::Scope::Root, i);
lane.put<const char *, int>("globalInvocationId", lane.put<const char *, int>("globalInvocationId",
rr::Extract(routine->globalInvocationID[0], i), rr::Extract(routine->globalInvocationID[0], i),
rr::Extract(routine->globalInvocationID[1], i), rr::Extract(routine->globalInvocationID[1], i),
...@@ -570,7 +1316,7 @@ void SpirvShader::dbgBeginEmit(EmitState *state) const ...@@ -570,7 +1316,7 @@ void SpirvShader::dbgBeginEmit(EmitState *state) const
locals.put<const char *, int>("viewIndex", routine->viewID); locals.put<const char *, int>("viewIndex", routine->viewID);
for(int i = 0; i < SIMD::Width; i++) for(int i = 0; i < SIMD::Width; i++)
{ {
auto lane = Group::localsLane(dbgState, i); auto lane = Group::localsLane(dbgState, &debug::Scope::Root, i);
lane.put<const char *, float>("fragCoord", lane.put<const char *, float>("fragCoord",
rr::Extract(routine->fragCoord[0], i), rr::Extract(routine->fragCoord[0], i),
rr::Extract(routine->fragCoord[1], i), rr::Extract(routine->fragCoord[1], i),
...@@ -612,12 +1358,15 @@ void SpirvShader::dbgBeginEmitInstruction(InsnIterator insn, EmitState *state) c ...@@ -612,12 +1358,15 @@ void SpirvShader::dbgBeginEmitInstruction(InsnIterator insn, EmitState *state) c
printf("%s\n", instruction.c_str()); printf("%s\n", instruction.c_str());
# endif // PRINT_EACH_PROCESSED_INSTRUCTION # endif // PRINT_EACH_PROCESSED_INSTRUCTION
if(extensionsImported.count(Extension::OpenCLDebugInfo100) == 0)
{
auto dbg = impl.debugger; auto dbg = impl.debugger;
if(!dbg) { return; } if(!dbg) { return; }
auto line = dbg->spirvLineMappings.at(insn.wordPointer(0)); auto line = dbg->spirvLineMappings.at(insn.wordPointer(0));
auto column = 0; auto column = 0;
rr::Call(&Impl::Debugger::State::update, state->routine->dbgState, dbg->spirvFile->id, line, column); rr::Call(&Impl::Debugger::State::update, state->routine->dbgState, dbg->spirvFile->id, line, column);
}
} }
void SpirvShader::dbgEndEmitInstruction(InsnIterator insn, EmitState *state) const void SpirvShader::dbgEndEmitInstruction(InsnIterator insn, EmitState *state) const
...@@ -638,7 +1387,7 @@ void SpirvShader::dbgExposeIntermediate(Object::ID id, EmitState *state) const ...@@ -638,7 +1387,7 @@ void SpirvShader::dbgExposeIntermediate(Object::ID id, EmitState *state) const
auto dbg = impl.debugger; auto dbg = impl.debugger;
if(!dbg) { return; } if(!dbg) { return; }
dbg->exposeVariable(this, id, id, state); dbg->exposeVariable(this, id, &debug::Scope::Root, nullptr, id, state);
} }
void SpirvShader::dbgUpdateActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const void SpirvShader::dbgUpdateActiveLaneMask(RValue<SIMD::Int> mask, EmitState *state) const
...@@ -674,10 +1423,18 @@ SpirvShader::EmitResult SpirvShader::EmitLine(InsnIterator insn, EmitState *stat ...@@ -674,10 +1423,18 @@ SpirvShader::EmitResult SpirvShader::EmitLine(InsnIterator insn, EmitState *stat
void SpirvShader::DefineOpenCLDebugInfo100(const InsnIterator &insn) void SpirvShader::DefineOpenCLDebugInfo100(const InsnIterator &insn)
{ {
auto dbg = impl.debugger;
if(!dbg) { return; }
dbg->process(this, insn, nullptr, Impl::Debugger::Pass::Define);
} }
SpirvShader::EmitResult SpirvShader::EmitOpenCLDebugInfo100(InsnIterator insn, EmitState *state) const SpirvShader::EmitResult SpirvShader::EmitOpenCLDebugInfo100(InsnIterator insn, EmitState *state) const
{ {
if(auto dbg = impl.debugger)
{
dbg->process(this, insn, state, Impl::Debugger::Pass::Emit);
}
return EmitResult::Continue; return EmitResult::Continue;
} }
......
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