blob: db743d7645b2944c8d94b002373a61f3abe8a4e1 [file] [log] [blame]
// Copyright 2019 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 rr_LLVMReactorDebugInfo_hpp
#define rr_LLVMReactorDebugInfo_hpp
#include "Reactor.hpp"
#ifdef ENABLE_RR_DEBUG_INFO
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <memory>
// Forward declarations
namespace llvm
{
class BasicBlock;
class ConstantFolder;
class DIBuilder;
class DICompileUnit;
class DIFile;
class DILocation;
class DIScope;
class DISubprogram;
class DIType;
class Function;
class Instruction;
class IRBuilderDefaultInserter;
class JITEventListener;
class LLVMContext;
class LoadedObjectInfo;
class Module;
class Type;
class Value;
namespace object
{
class ObjectFile;
}
template <typename T, typename Inserter> class IRBuilder;
} // namespace llvm
namespace rr
{
class Type;
class Value;
// DebugInfo generates LLVM DebugInfo IR from the C++ source that calls
// into Reactor functions. See docs/ReactorDebugInfo.mk for more information.
class DebugInfo
{
public:
using IRBuilder = llvm::IRBuilder<llvm::ConstantFolder, llvm::IRBuilderDefaultInserter>;
DebugInfo(IRBuilder *builder,
llvm::LLVMContext *context,
llvm::Module *module,
llvm::Function *function);
~DebugInfo();
// Finalize debug info generation. Must be called before the LLVM module
// is built.
void Finalize();
// Updates the current source location.
void EmitLocation();
// Binds the value to its symbol in the source file.
// See docs/ReactorDebugInfo.mk for more information.
void EmitVariable(Value *value);
// Forcefully flush the binding of the last variable name.
// Used for binding the initializer of `For` loops.
void Flush();
// NotifyObjectEmitted informs any attached debuggers of the JIT'd
// object.
static void NotifyObjectEmitted(const llvm::object::ObjectFile &Obj, const llvm::LoadedObjectInfo &L);
// NotifyFreeingObject informs any attached debuggers that the JIT'd
// object is now invalid.
static void NotifyFreeingObject(const llvm::object::ObjectFile &Obj);
private:
struct Token
{
enum Kind
{
Identifier,
Return
};
Kind kind;
std::string identifier;
};
using LineTokens = std::unordered_map<unsigned int, Token>;
struct FunctionLocation
{
std::string name;
std::string file;
bool operator == (const FunctionLocation &rhs) const { return name == rhs.name && file == rhs.file; }
bool operator != (const FunctionLocation &rhs) const { return !(*this == rhs); }
struct Hash
{
std::size_t operator()(const FunctionLocation &l) const noexcept
{
return std::hash<std::string>()(l.file) * 31 +
std::hash<std::string>()(l.name);
}
};
};
struct Location
{
FunctionLocation function;
unsigned int line = 0;
bool operator == (const Location &rhs) const { return function == rhs.function && line == rhs.line; }
bool operator != (const Location &rhs) const { return !(*this == rhs); }
struct Hash
{
std::size_t operator()(const Location &l) const noexcept
{
return FunctionLocation::Hash()(l.function) * 31 +
std::hash<unsigned int>()(l.line);
}
};
};
using Backtrace = std::vector<Location>;
struct Pending
{
std::string name;
Location location;
llvm::DILocation *diLocation = nullptr;
llvm::Value *value = nullptr;
llvm::Instruction *insertAfter = nullptr;
llvm::BasicBlock *block = nullptr;
llvm::DIScope *scope = nullptr;
bool addNopOnNextLine = false;
};
struct Scope
{
Location location;
llvm::DIScope *di;
std::unordered_set<std::string> symbols;
Pending pending;
};
void registerBasicTypes();
void emitPending(Scope &scope, IRBuilder *builder);
// Returns the source location of the non-Reactor calling function.
Location getCallerLocation() const;
// Returns the backtrace for the callstack, starting at the first
// non-Reactor file. If limit is non-zero, then a maximum of limit
// frames will be returned.
Backtrace getCallerBacktrace(size_t limit = 0) const;
llvm::DILocation* getLocation(const Backtrace &backtrace, size_t i);
llvm::DIType *getOrCreateType(llvm::Type* type);
llvm::DIFile *getOrCreateFile(const char* path);
LineTokens const *getOrParseFileTokens(const char* path);
// Synchronizes diScope with the current backtrace.
void syncScope(Backtrace const& backtrace);
IRBuilder *builder;
llvm::LLVMContext *context;
llvm::Module *module;
llvm::Function *function;
std::unique_ptr<llvm::DIBuilder> diBuilder;
llvm::DICompileUnit *diCU;
llvm::DISubprogram *diSubprogram;
llvm::DILocation *diRootLocation;
std::vector<Scope> diScope;
std::unordered_map<std::string, llvm::DIFile*> diFiles;
std::unordered_map<llvm::Type*, llvm::DIType*> diTypes;
std::unordered_map<std::string, std::unique_ptr<LineTokens>> fileTokens;
std::vector<void const*> pushed;
};
} // namespace rr
#endif // ENABLE_RR_DEBUG_INFO
#endif // rr_LLVMReactorDebugInfo_hpp