Skip to content

Commit

Permalink
Merge pull request #246 from frasercrmck/spirv-ll-opencl-dbg-full
Browse files Browse the repository at this point in the history
[spirv-ll] Fully support OpenCL.DebugInfo.100 instructions
  • Loading branch information
frasercrmck authored Dec 11, 2023
2 parents 2ee7cd0 + 33edf73 commit 050d91a
Show file tree
Hide file tree
Showing 20 changed files with 3,295 additions and 60 deletions.
19 changes: 16 additions & 3 deletions modules/compiler/spirv-ll/include/spirv-ll/builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Metadata.h>
#include <multi_llvm/multi_llvm.h>
#include <multi_llvm/vector_type_helper.h>
#include <spirv-ll/context.h>
Expand Down Expand Up @@ -55,6 +56,17 @@ static inline llvm::Error makeStringError(const llvm::Twine &message) {
llvm::inconvertibleErrorCode());
}

static inline std::string getIDAsStr(spv::Id id, Module *module = nullptr) {
std::string id_str = "%" + std::to_string(id);
if (module) {
std::string name = module->getName(id);
if (!name.empty()) {
id_str += "[%" + name + "]";
}
}
return id_str;
}

/// @brief An interface for builders of extended instruction sets.
class ExtInstSetHandler {
public:
Expand Down Expand Up @@ -759,11 +771,12 @@ class Builder {
/// set ID.
///
/// Each handler is created only once per set.
template <typename Handler>
void registerExtInstHandler(ExtendedInstrSet Set) {
template <typename Handler, typename... Args>
void registerExtInstHandler(ExtendedInstrSet Set, Args... args) {
auto &builder = ext_inst_handlers[Set];
if (!builder) {
builder = std::make_unique<Handler>(*this, module);
builder =
std::make_unique<Handler>(*this, module, std::forward<Args>(args)...);
}
}
/// @brief Add debug metadata to the appropriate instructions
Expand Down
126 changes: 124 additions & 2 deletions modules/compiler/spirv-ll/include/spirv-ll/builder_debug_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,144 @@
#define SPIRV_LL_SPV_BUILDER_DEBUG_INFO_H_INCLUDED

#include <spirv-ll/builder.h>
#include <spirv-ll/module.h>
#include <spirv-ll/opcodes.h>

namespace spirv_ll {

/// @brief Combined builder for the DebugInfo and OpenCLDebugInfo100 extended
/// instruction sets.
class DebugInfoBuilder : public ExtInstSetHandler {
public:
enum Workarounds {
// Some versions of llvm-spirv mistakenly swap
// DebugTypeTemplateTemplateParameter and DebugTypeTemplateParameterPack
// opcodes, leading to incorrect binaries. When this workaround is enabled,
// we assume binaries *may* have been created with this bug, and try to
// infer which opcode is intended based on the operands.
// See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/pull/2248
TemplateTemplateSwappedWithParameterPack = 1 << 0,
};

/// @brief Constructor.
///
/// @param[in] builder spirv_ll::Builder object that will own this object.
/// @param[in] module The module being translated.
DebugInfoBuilder(Builder &builder, Module &module)
: ExtInstSetHandler(builder, module) {}
DebugInfoBuilder(Builder &builder, Module &module, uint64_t workarounds = 0)
: ExtInstSetHandler(builder, module), workarounds(workarounds) {}

/// @see ExtInstSetHandler::create
virtual llvm::Error create(OpExtInst const &opc) override;

virtual llvm::Error finishModuleProcessing() override;

private:
uint64_t workarounds = 0;
/// @brief Map from DebugInfo instructions to the llvm::DIBuilder that builds
/// them.
std::unordered_map<spv::Id, std::unique_ptr<llvm::DIBuilder>>
debug_builder_map;

/// @brief Cache of translated DebugInfo instructions.
std::unordered_map<spv::Id, llvm::MDNode *> debug_info_cache;

/// @brief Create an DebugInfo extended instruction transformation to LLVM IR.
///
/// @tparam T The OpenCL extended instruction class template to create.
/// @param opc The OpCode object to translate.
///
/// @return Returns an `llvm::Error` object representing either success, or
/// an error value.
template <typename T>
llvm::Error create(OpExtInst const &opc);

/// @brief Returns the LLVM DIBuilder for the given instruction.
///
/// We may only have one DICompileUnit per DIBuilder, so must support
/// multiple builders. This function finds the DIBuilder for the instruction
/// based on its chain of scopes, if applicable.
llvm::DIBuilder &getDIBuilder(const OpExtInst *op) const;

/// @brief Returns the first registered DIBuilder, for when it doesn't matter
/// which is used.
///
/// @see getDIBuilder
llvm::DIBuilder &getDefaultDIBuilder() const;

/// @brief Returns true if the given ID is DebugInfoNone.
bool isDebugInfoNone(spv::Id id) const;

/// @brief Returns true if the extended instruction set represented by the
/// given ID is one covered by this builder.
bool isDebugInfoSet(uint32_t set_id) const;

/// @brief Returns the constant integer value of an ID, or std::nullopt for
/// DebugInfoNone, or an error.
llvm::Expected<std::optional<uint64_t>> getConstantIntValue(spv::Id id) const;

/// @brief Translates a DebugInfo extension instruction to LLVM IR.
///
/// This does it "on the fly", as opposed to 'create' which visits them in
/// program order.
template <typename T = llvm::MDNode>
llvm::Expected<T *> translateDebugInst(spv::Id id) {
auto it = debug_info_cache.find(id);
if (it != debug_info_cache.end()) {
return static_cast<T *>(it->second);
}
auto *const op = module.get_or_null(id);
// If this isn't a recognized ID, it's probably a forward reference. We
// count this as an error in this case, as forward references are generally
// not allowed in DebugInfo instruction sets.
if (!op) {
return makeStringError("Unknown id " + getIDAsStr(id, &module) +
" - unexpected forward reference?");
}
auto *const op_ext_inst = dyn_cast<OpExtInst>(op);

// If this isn't an OpExtInst, we're trying to translate the wrong thing.
if (!op_ext_inst || !isDebugInfoSet(op_ext_inst->Set())) {
return makeStringError("id " + getIDAsStr(id, &module) +
" is not a DebugInfo OpExtInst");
}

llvm::Expected<llvm::MDNode *> res_or_error =
translateDebugInstImpl(op_ext_inst);
if (auto err = res_or_error.takeError()) {
return std::move(err);
}
// Cache this result
debug_info_cache[id] = res_or_error.get();
return static_cast<T *>(res_or_error.get());
}

llvm::Expected<llvm::MDNode *> translateDebugInstImpl(const OpExtInst *op);

/// @brief Given an operation that is either a
/// DebugTypeTemplateTemplateParameter or DebugTypeTemplateParameterPack, try
/// and infer which is which (in the presence of several known bugs in
/// ecosystem tooling) and translate it as such.
llvm::Expected<llvm::MDNode *>
translateTemplateTemplateParameterOrTemplateParameterPack(
const OpExtInst *op);

template <typename T>
llvm::Expected<llvm::MDNode *> translate(const T *op);

/// @brief Process the DebugTypeComposite instructions once all other nodes
/// have been visited.
llvm::Error finalizeCompositeTypes();

/// @brief A collection of DebugTypeTemplate instructions
///
/// These instructions are processed at the end of the module, (seemingly)
/// due to bugs in producers allowing forward references to these nodes.
llvm::SmallVector<spv::Id, 4> template_types;
/// @brief A collection of DebugTypeComposite instructions
///
/// These instructions are processed at the end of the module because they
/// make contain forward references to other nodes, as per the specification.
llvm::SmallVector<spv::Id, 4> composite_types;
};

} // namespace spirv_ll
Expand Down
64 changes: 57 additions & 7 deletions modules/compiler/spirv-ll/include/spirv-ll/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <memory>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>

namespace spirv_ll {
/// @brief Enum class used to represent an Extended Instruction Set.
Expand Down Expand Up @@ -362,6 +363,26 @@ class Module : public ModuleHeader {
///
const std::string &getSourceMetadataString() const;

/// @brief Sets the string used to hold the process/processor
void setModuleProcess(const std::string &str);

/// @brief Gets the string used to hold the process/processor
const std::string &getModuleProcess() const;

/// @brief Check if this ID is an OpExtInst with the given opcode.
///
/// @return Returns true if this opcode is an OpExtInst with the given
/// opcode, false otherwise.
bool isOpExtInst(spv::Id id, uint32_t opcode,
const std::unordered_set<ExtendedInstrSet> &sets) const;

/// @brief Check if this ID is an OpExtInst with any of the given opcodes.
///
/// @return Returns true if this opcode is an OpExtInst with any of the given
/// opcodes, false otherwise.
bool isOpExtInst(spv::Id id, const std::unordered_set<uint32_t> &opcodes,
const std::unordered_set<ExtendedInstrSet> &sets) const;

/// @brief Set the DICompileUnit for this module.
void setCompileUnit(llvm::DICompileUnit *compile_unit);

Expand Down Expand Up @@ -793,13 +814,14 @@ class Module : public ModuleHeader {
///
/// @return A pointer to the Op or nullptr if not found.
template <class Op = OpCode>
const Op *get(spv::Id id) const {
auto ty = Types.find(id);
if (ty != Types.end()) {
const Op *get_or_null(spv::Id id) const {
if (!id) {
return nullptr;
}
if (auto ty = Types.find(id); ty != Types.end()) {
return cast<Op>(ty->second.Op);
}
auto val = Values.find(id);
if (val != Values.end()) {
if (auto val = Values.find(id); val != Values.end()) {
return cast<Op>(val->second.Op);
}
auto found = std::find_if(
Expand All @@ -813,8 +835,22 @@ class Module : public ModuleHeader {
}
return false;
});
SPIRV_LL_ASSERT(found != OpCodes.end(), "OpCode for ID not found");
return cast<Op>(found->get());
return found == OpCodes.end() ? nullptr : cast<Op>(found->get());
}

/// @brief Get the SPIR-V Op for the given ID.
///
/// The function will search both the Types and the Values to try and find
/// the given ID. Asserts that the op was found.
///
/// @param[in] id The ID for the Op to get.
///
/// @return A pointer to the Op.
template <class Op = OpCode>
const Op *get(spv::Id id) const {
auto *const op = get_or_null<Op>(id);
SPIRV_LL_ASSERT(op, "OpCode for ID not found");
return op;
}

/// @brief Get the SpirV Op for the given LLVM Value.
Expand Down Expand Up @@ -962,6 +998,13 @@ class Module : public ModuleHeader {
std::unordered_map<const OpType *, llvm::Function *>>>
reductionWrapperMap;

/// @brief Turn off the use of implicit debug scopes across the module.
void disableImplicitDebugScopes();

/// @brief Returns true if implicit debug scopes should be created to handle
/// debug information.
bool useImplicitDebugScopes() const;

private:
/// @brief The set of enabled capabilities.
llvm::SmallSet<spv::Capability, 16> capabilities;
Expand Down Expand Up @@ -1097,6 +1140,13 @@ class Module : public ModuleHeader {
/// deferred.
llvm::SmallVector<spirv_ll::OpSpecConstantOp const *, 2>
deferredSpecConstantOps;
std::string ModuleProcess;
/// @brief True if debug scopes should be inferred and generated when
/// processing debug information.
///
/// False if a DebugInfo-like extension is enabled, and only explicit scope
/// instructions are to be obeyed.
bool ImplicitDebugScopes = true;
};

/// @brief Less than operator that compares the descriptor binding in each `ID`
Expand Down
8 changes: 8 additions & 0 deletions modules/compiler/spirv-ll/include/spirv-ll/opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -3024,6 +3024,14 @@ class OpNoLine : public OpCode {
static const spv::Op ClassCode = spv::OpNoLine;
};

class OpModuleProcessed : public OpCode {
public:
OpModuleProcessed(OpCode const &other)
: OpCode(other, spv::OpModuleProcessed) {}
llvm::StringRef Process() const;
static const spv::Op ClassCode = spv::OpModuleProcessed;
};

class OpAtomicFlagTestAndSet : public OpResult {
public:
OpAtomicFlagTestAndSet(OpCode const &other)
Expand Down
2 changes: 2 additions & 0 deletions modules/compiler/spirv-ll/source/builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <compiler/utils/target_extension_types.h>
#include <llvm/BinaryFormat/Dwarf.h>
#include <llvm/IR/Attributes.h>
#include <llvm/IR/DIBuilder.h>
#include <llvm/IR/Metadata.h>
#include <llvm/Support/Debug.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/type_traits.h>
Expand Down
Loading

0 comments on commit 050d91a

Please sign in to comment.