mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-08 15:38:09 +02:00
swr/rast: Initial work for debugging support.
Adds ability to step into jitted llvm IR in Visual Studio. - Updated llvm type generation script to also generate corresponding debug types. - New module pass inserts debug metadata into the IR for each function Disabled by default. Reviewed-by: Bruce Cherniak <bruce.cherniak@intel.com>
This commit is contained in:
parent
4660e13152
commit
01ab218bbc
6 changed files with 192 additions and 17 deletions
|
|
@ -26,7 +26,7 @@ from argparse import FileType
|
||||||
|
|
||||||
'''
|
'''
|
||||||
'''
|
'''
|
||||||
def gen_llvm_type(type, name, is_pointer, is_pointer_pointer, is_array, is_array_array, array_count, array_count1, is_llvm_struct, is_llvm_enum, is_llvm_pfn, output_file):
|
def gen_llvm_type(type, name, idx, is_pointer, is_pointer_pointer, is_array, is_array_array, array_count, array_count1, is_llvm_struct, is_llvm_enum, is_llvm_pfn, output_file):
|
||||||
|
|
||||||
llvm_type = ''
|
llvm_type = ''
|
||||||
|
|
||||||
|
|
@ -94,6 +94,7 @@ def gen_llvm_type(type, name, is_pointer, is_pointer_pointer, is_array, is_array
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'name' : name,
|
'name' : name,
|
||||||
|
'lineNum' : idx,
|
||||||
'type' : llvm_type,
|
'type' : llvm_type,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +126,7 @@ def gen_llvm_types(input_file, output_file):
|
||||||
|
|
||||||
type_entry = {
|
type_entry = {
|
||||||
'name' : struct_name,
|
'name' : struct_name,
|
||||||
|
'lineNum' : idx+1,
|
||||||
'members' : [],
|
'members' : [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,7 +292,7 @@ def gen_llvm_types(input_file, output_file):
|
||||||
if type is not None:
|
if type is not None:
|
||||||
type_entry['members'].append(
|
type_entry['members'].append(
|
||||||
gen_llvm_type(
|
gen_llvm_type(
|
||||||
type, name, is_pointer, is_pointer_pointer, is_array, is_array_array,
|
type, name, idx+1, is_pointer, is_pointer_pointer, is_array, is_array_array,
|
||||||
array_count, array_count1, is_llvm_struct, is_llvm_enum, is_llvm_pfn, output_file))
|
array_count, array_count1, is_llvm_struct, is_llvm_enum, is_llvm_pfn, output_file))
|
||||||
|
|
||||||
# Detect end of structure
|
# Detect end of structure
|
||||||
|
|
@ -307,7 +309,9 @@ def gen_llvm_types(input_file, output_file):
|
||||||
output_file,
|
output_file,
|
||||||
cmdline=sys.argv,
|
cmdline=sys.argv,
|
||||||
filename=os.path.basename(output_file),
|
filename=os.path.basename(output_file),
|
||||||
types=types)
|
types=types,
|
||||||
|
input_dir=os.path.dirname(input_file.name),
|
||||||
|
input_file=os.path.basename(input_file.name))
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Function which is invoked when this script is started from a command line.
|
Function which is invoked when this script is started from a command line.
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,18 @@ namespace SwrJit
|
||||||
%endfor
|
%endfor
|
||||||
|
|
||||||
pRetType = StructType::create(members, "${type['name']}", false);
|
pRetType = StructType::create(members, "${type['name']}", false);
|
||||||
|
|
||||||
|
// Compute debug metadata
|
||||||
|
llvm::DIBuilder builder(*pJitMgr->mpCurrentModule);
|
||||||
|
llvm::DIFile* pFile = builder.createFile("${input_file}", "${input_dir}");
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, uint32_t>> dbgMembers;
|
||||||
|
%for member in type['members']:
|
||||||
|
dbgMembers.push_back(std::make_pair("${member['name']}", ${ member['lineNum'] }));
|
||||||
|
%endfor
|
||||||
|
|
||||||
|
pJitMgr->CreateDebugStructType(pRetType, "${type['name']}", pFile, ${type['lineNum']}, dbgMembers);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pRetType;
|
return pRetType;
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,105 @@ void JitManager::SetupNewModule()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DIType* JitManager::CreateDebugStructType(StructType* pType, const std::string& name, DIFile* pFile, uint32_t lineNum,
|
||||||
|
const std::vector<std::pair<std::string, uint32_t>>& members)
|
||||||
|
{
|
||||||
|
DIBuilder builder(*mpCurrentModule);
|
||||||
|
SmallVector<Metadata*, 8> ElemTypes;
|
||||||
|
DataLayout DL = DataLayout(mpCurrentModule);
|
||||||
|
uint32_t size = DL.getTypeAllocSizeInBits(pType);
|
||||||
|
uint32_t alignment = DL.getABITypeAlignment(pType);
|
||||||
|
DINode::DIFlags flags = DINode::DIFlags::FlagPublic;
|
||||||
|
|
||||||
|
DICompositeType* pDIStructTy = builder.createStructType(pFile, name, pFile, lineNum, size, alignment,
|
||||||
|
flags, nullptr, builder.getOrCreateArray(ElemTypes));
|
||||||
|
|
||||||
|
// Register mapping now to break loops (in case struct contains itself or pointers to itself)
|
||||||
|
mDebugStructMap[pType] = pDIStructTy;
|
||||||
|
|
||||||
|
uint32_t idx = 0;
|
||||||
|
for (auto& elem : pType->elements())
|
||||||
|
{
|
||||||
|
std::string name = members[idx].first;
|
||||||
|
uint32_t lineNum = members[idx].second;
|
||||||
|
size = DL.getTypeAllocSizeInBits(elem);
|
||||||
|
alignment = DL.getABITypeAlignment(elem);
|
||||||
|
uint32_t offset = DL.getStructLayout(pType)->getElementOffsetInBits(idx);
|
||||||
|
llvm::DIType* pDebugTy = GetDebugType(elem);
|
||||||
|
ElemTypes.push_back(builder.createMemberType(pDIStructTy, name, pFile, lineNum, size, alignment, offset, flags, pDebugTy));
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDIStructTy->replaceElements(builder.getOrCreateArray(ElemTypes));
|
||||||
|
return pDIStructTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIType* JitManager::GetDebugArrayType(Type* pTy)
|
||||||
|
{
|
||||||
|
DIBuilder builder(*mpCurrentModule);
|
||||||
|
DataLayout DL = DataLayout(mpCurrentModule);
|
||||||
|
ArrayType* pArrayTy = cast<ArrayType>(pTy);
|
||||||
|
uint32_t size = DL.getTypeAllocSizeInBits(pArrayTy);
|
||||||
|
uint32_t alignment = DL.getABITypeAlignment(pArrayTy);
|
||||||
|
|
||||||
|
SmallVector<Metadata*, 8> Elems;
|
||||||
|
Elems.push_back(builder.getOrCreateSubrange(0, pArrayTy->getNumElements()));
|
||||||
|
return builder.createArrayType(size, alignment, GetDebugType(pArrayTy->getElementType()), builder.getOrCreateArray(Elems));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a DIType from llvm Type
|
||||||
|
DIType* JitManager::GetDebugType(Type* pTy)
|
||||||
|
{
|
||||||
|
DIBuilder builder(*mpCurrentModule);
|
||||||
|
Type::TypeID id = pTy->getTypeID();
|
||||||
|
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case Type::VoidTyID: return builder.createUnspecifiedType("void"); break;
|
||||||
|
case Type::HalfTyID: return builder.createBasicType("float16", 16, dwarf::DW_ATE_float); break;
|
||||||
|
case Type::FloatTyID: return builder.createBasicType("float", 32, dwarf::DW_ATE_float); break;
|
||||||
|
case Type::DoubleTyID: return builder.createBasicType("double", 64, dwarf::DW_ATE_float); break;
|
||||||
|
case Type::IntegerTyID: return GetDebugIntegerType(pTy); break;
|
||||||
|
case Type::StructTyID: return GetDebugStructType(pTy); break;
|
||||||
|
case Type::ArrayTyID: return GetDebugArrayType(pTy); break;
|
||||||
|
case Type::PointerTyID: return builder.createPointerType(GetDebugType(pTy->getPointerElementType()), 64, 64); break;
|
||||||
|
case Type::VectorTyID: return GetDebugVectorType(pTy); break;
|
||||||
|
default: SWR_ASSERT(false, "Unimplemented llvm type");
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIType* JitManager::GetDebugIntegerType(Type* pTy)
|
||||||
|
{
|
||||||
|
DIBuilder builder(*mpCurrentModule);
|
||||||
|
IntegerType* pIntTy = cast<IntegerType>(pTy);
|
||||||
|
switch (pIntTy->getBitWidth())
|
||||||
|
{
|
||||||
|
case 1: return builder.createBasicType("int1", 1, dwarf::DW_ATE_unsigned); break;
|
||||||
|
case 8: return builder.createBasicType("int8", 8, dwarf::DW_ATE_signed); break;
|
||||||
|
case 16: return builder.createBasicType("int16", 16, dwarf::DW_ATE_signed); break;
|
||||||
|
case 32: return builder.createBasicType("int", 32, dwarf::DW_ATE_signed); break;
|
||||||
|
case 64: return builder.createBasicType("int64", 64, dwarf::DW_ATE_signed); break;
|
||||||
|
default: SWR_ASSERT(false, "Unimplemented integer bit width");
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIType* JitManager::GetDebugVectorType(Type* pTy)
|
||||||
|
{
|
||||||
|
DIBuilder builder(*mpCurrentModule);
|
||||||
|
VectorType* pVecTy = cast<VectorType>(pTy);
|
||||||
|
DataLayout DL = DataLayout(mpCurrentModule);
|
||||||
|
uint32_t size = DL.getTypeAllocSizeInBits(pVecTy);
|
||||||
|
uint32_t alignment = DL.getABITypeAlignment(pVecTy);
|
||||||
|
SmallVector<Metadata*, 1> Elems;
|
||||||
|
Elems.push_back(builder.getOrCreateSubrange(0, pVecTy->getVectorNumElements()));
|
||||||
|
|
||||||
|
return builder.createVectorType(size, alignment, GetDebugType(pVecTy->getVectorElementType()), builder.getOrCreateArray(Elems));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief Dump function x86 assembly to file.
|
/// @brief Dump function x86 assembly to file.
|
||||||
/// @note This should only be called after the module has been jitted to x86 and the
|
/// @note This should only be called after the module has been jitted to x86 and the
|
||||||
|
|
@ -231,27 +330,56 @@ void JitManager::DumpAsm(Function* pFunction, const char* fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string JitManager::GetOutputDir()
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
DWORD pid = GetCurrentProcessId();
|
||||||
|
char procname[MAX_PATH];
|
||||||
|
GetModuleFileNameA(NULL, procname, MAX_PATH);
|
||||||
|
const char* pBaseName = strrchr(procname, '\\');
|
||||||
|
std::stringstream outDir;
|
||||||
|
outDir << JITTER_OUTPUT_DIR << pBaseName << "_" << pid;
|
||||||
|
CreateDirectoryPath(outDir.str().c_str());
|
||||||
|
return outDir.str();
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief Dump function to file.
|
||||||
|
void JitManager::DumpToFile(Module *M, const char *fileName)
|
||||||
|
{
|
||||||
|
if (KNOB_DUMP_SHADER_IR)
|
||||||
|
{
|
||||||
|
std::string outDir = GetOutputDir();
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
const char *funcName = M->getName().data();
|
||||||
|
char fName[256];
|
||||||
|
#if defined(_WIN32)
|
||||||
|
sprintf(fName, "%s\\%s.%s.ll", outDir.c_str(), funcName, fileName);
|
||||||
|
#else
|
||||||
|
sprintf(fName, "%s.%s.ll", funcName, fileName);
|
||||||
|
#endif
|
||||||
|
raw_fd_ostream fd(fName, EC, llvm::sys::fs::F_None);
|
||||||
|
M->print(fd, nullptr);
|
||||||
|
fd.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief Dump function to file.
|
/// @brief Dump function to file.
|
||||||
void JitManager::DumpToFile(Function *f, const char *fileName)
|
void JitManager::DumpToFile(Function *f, const char *fileName)
|
||||||
{
|
{
|
||||||
if (KNOB_DUMP_SHADER_IR)
|
if (KNOB_DUMP_SHADER_IR)
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
std::string outDir = GetOutputDir();
|
||||||
DWORD pid = GetCurrentProcessId();
|
|
||||||
char procname[MAX_PATH];
|
|
||||||
GetModuleFileNameA(NULL, procname, MAX_PATH);
|
|
||||||
const char* pBaseName = strrchr(procname, '\\');
|
|
||||||
std::stringstream outDir;
|
|
||||||
outDir << JITTER_OUTPUT_DIR << pBaseName << "_" << pid << std::ends;
|
|
||||||
CreateDirectoryPath(outDir.str().c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::error_code EC;
|
std::error_code EC;
|
||||||
const char *funcName = f->getName().data();
|
const char *funcName = f->getName().data();
|
||||||
char fName[256];
|
char fName[256];
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
sprintf(fName, "%s\\%s.%s.ll", outDir.str().c_str(), funcName, fileName);
|
sprintf(fName, "%s\\%s.%s.ll", outDir.c_str(), funcName, fileName);
|
||||||
#else
|
#else
|
||||||
sprintf(fName, "%s.%s.ll", funcName, fileName);
|
sprintf(fName, "%s.%s.ll", funcName, fileName);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -260,7 +388,7 @@ void JitManager::DumpToFile(Function *f, const char *fileName)
|
||||||
pModule->print(fd, nullptr);
|
pModule->print(fd, nullptr);
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
sprintf(fName, "%s\\cfg.%s.%s.dot", outDir.str().c_str(), funcName, fileName);
|
sprintf(fName, "%s\\cfg.%s.%s.dot", outDir.c_str(), funcName, fileName);
|
||||||
#else
|
#else
|
||||||
sprintf(fName, "cfg.%s.%s.dot", funcName, fileName);
|
sprintf(fName, "cfg.%s.%s.dot", funcName, fileName);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -424,6 +552,13 @@ void JitCache::notifyObjectCompiled(const llvm::Module *M, llvm::MemoryBufferRef
|
||||||
fileObj.write((const char*)&header, sizeof(header));
|
fileObj.write((const char*)&header, sizeof(header));
|
||||||
fileObj << Obj.getBuffer();
|
fileObj << Obj.getBuffer();
|
||||||
fileObj.flush();
|
fileObj.flush();
|
||||||
|
|
||||||
|
llvm::SmallString<MAX_PATH> filePath2 = filePath;
|
||||||
|
filePath2 += ".obj";
|
||||||
|
|
||||||
|
llvm::raw_fd_ostream fileObj2(filePath2.c_str(), err, llvm::sys::fs::F_None);
|
||||||
|
fileObj2 << Obj.getBuffer();
|
||||||
|
fileObj2.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a pointer to a newly allocated MemoryBuffer that contains the
|
/// Returns a pointer to a newly allocated MemoryBuffer that contains the
|
||||||
|
|
|
||||||
|
|
@ -157,8 +157,32 @@ struct JitManager
|
||||||
JitInstructionSet mArch;
|
JitInstructionSet mArch;
|
||||||
std::string mCore;
|
std::string mCore;
|
||||||
|
|
||||||
|
// Debugging support
|
||||||
|
std::unordered_map<llvm::StructType*, llvm::DIType*> mDebugStructMap;
|
||||||
|
|
||||||
void SetupNewModule();
|
void SetupNewModule();
|
||||||
|
|
||||||
void DumpAsm(llvm::Function* pFunction, const char* fileName);
|
void DumpAsm(llvm::Function* pFunction, const char* fileName);
|
||||||
static void DumpToFile(llvm::Function *f, const char *fileName);
|
static void DumpToFile(llvm::Function *f, const char *fileName);
|
||||||
|
static void DumpToFile(llvm::Module *M, const char *fileName);
|
||||||
|
static std::string GetOutputDir();
|
||||||
|
|
||||||
|
// Debugging support methods
|
||||||
|
llvm::DIType* GetDebugType(llvm::Type* pTy);
|
||||||
|
llvm::DIType* GetDebugIntegerType(llvm::Type* pTy);
|
||||||
|
llvm::DIType* GetDebugArrayType(llvm::Type* pTy);
|
||||||
|
llvm::DIType* GetDebugVectorType(llvm::Type* pTy);
|
||||||
|
|
||||||
|
llvm::DIType* GetDebugStructType(llvm::Type* pType)
|
||||||
|
{
|
||||||
|
llvm::StructType* pStructTy = llvm::cast<llvm::StructType>(pType);
|
||||||
|
if (mDebugStructMap.find(pStructTy) == mDebugStructMap.end())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return mDebugStructMap[pStructTy];
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::DIType* CreateDebugStructType(llvm::StructType* pType, const std::string& name, llvm::DIFile* pFile, uint32_t lineNum,
|
||||||
|
const std::vector<std::pair<std::string, uint32_t>>& members);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1484,7 +1484,7 @@ namespace SwrJit
|
||||||
Function* pfnCttz = Intrinsic::getDeclaration(mpJitMgr->mpCurrentModule, Intrinsic::cttz, { mInt32Ty });
|
Function* pfnCttz = Intrinsic::getDeclaration(mpJitMgr->mpCurrentModule, Intrinsic::cttz, { mInt32Ty });
|
||||||
|
|
||||||
// Setup loop basic block
|
// Setup loop basic block
|
||||||
BasicBlock* pLoop = BasicBlock::Create(mpJitMgr->mContext, "Scatter Loop", pFunc);
|
BasicBlock* pLoop = BasicBlock::Create(mpJitMgr->mContext, "Scatter_Loop", pFunc);
|
||||||
|
|
||||||
// compute first set bit
|
// compute first set bit
|
||||||
Value* pIndex = CALL(pfnCttz, { pMask, C(false) });
|
Value* pIndex = CALL(pfnCttz, { pMask, C(false) });
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ using PassManager = llvm::legacy::PassManager;
|
||||||
#include "llvm/Support/Host.h"
|
#include "llvm/Support/Host.h"
|
||||||
#include "llvm/Support/DynamicLibrary.h"
|
#include "llvm/Support/DynamicLibrary.h"
|
||||||
|
|
||||||
|
#include "llvm/IR/DIBuilder.h"
|
||||||
#include "llvm/IR/Function.h"
|
#include "llvm/IR/Function.h"
|
||||||
#include "llvm/IR/Constants.h"
|
#include "llvm/IR/Constants.h"
|
||||||
#include "llvm/IR/Type.h"
|
#include "llvm/IR/Type.h"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue