From 01ab218bbc5c8058a99077a6bc3dc9884e9d218a Mon Sep 17 00:00:00 2001 From: George Kyriazis Date: Fri, 19 Jan 2018 15:46:59 -0600 Subject: [PATCH] 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 --- .../swr/rasterizer/codegen/gen_llvm_types.py | 10 +- .../rasterizer/codegen/templates/gen_llvm.hpp | 12 ++ .../swr/rasterizer/jitter/JitManager.cpp | 157 ++++++++++++++++-- .../swr/rasterizer/jitter/JitManager.h | 24 +++ .../swr/rasterizer/jitter/builder_misc.cpp | 2 +- .../drivers/swr/rasterizer/jitter/jit_pch.hpp | 2 +- 6 files changed, 191 insertions(+), 16 deletions(-) diff --git a/src/gallium/drivers/swr/rasterizer/codegen/gen_llvm_types.py b/src/gallium/drivers/swr/rasterizer/codegen/gen_llvm_types.py index ccf2bde1edd..398cde3ed92 100644 --- a/src/gallium/drivers/swr/rasterizer/codegen/gen_llvm_types.py +++ b/src/gallium/drivers/swr/rasterizer/codegen/gen_llvm_types.py @@ -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 = '' @@ -94,6 +94,7 @@ def gen_llvm_type(type, name, is_pointer, is_pointer_pointer, is_array, is_array return { 'name' : name, + 'lineNum' : idx, 'type' : llvm_type, } @@ -125,6 +126,7 @@ def gen_llvm_types(input_file, output_file): type_entry = { 'name' : struct_name, + 'lineNum' : idx+1, 'members' : [], } @@ -290,7 +292,7 @@ def gen_llvm_types(input_file, output_file): if type is not None: type_entry['members'].append( 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)) # Detect end of structure @@ -307,7 +309,9 @@ def gen_llvm_types(input_file, output_file): output_file, cmdline=sys.argv, 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. diff --git a/src/gallium/drivers/swr/rasterizer/codegen/templates/gen_llvm.hpp b/src/gallium/drivers/swr/rasterizer/codegen/templates/gen_llvm.hpp index 574ee5aaa79..61c31328a6a 100644 --- a/src/gallium/drivers/swr/rasterizer/codegen/templates/gen_llvm.hpp +++ b/src/gallium/drivers/swr/rasterizer/codegen/templates/gen_llvm.hpp @@ -53,6 +53,18 @@ namespace SwrJit %endfor 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> 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; diff --git a/src/gallium/drivers/swr/rasterizer/jitter/JitManager.cpp b/src/gallium/drivers/swr/rasterizer/jitter/JitManager.cpp index fbca1a74a72..2b993613f53 100644 --- a/src/gallium/drivers/swr/rasterizer/jitter/JitManager.cpp +++ b/src/gallium/drivers/swr/rasterizer/jitter/JitManager.cpp @@ -190,6 +190,105 @@ void JitManager::SetupNewModule() } +DIType* JitManager::CreateDebugStructType(StructType* pType, const std::string& name, DIFile* pFile, uint32_t lineNum, + const std::vector>& members) +{ + DIBuilder builder(*mpCurrentModule); + SmallVector 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(pTy); + uint32_t size = DL.getTypeAllocSizeInBits(pArrayTy); + uint32_t alignment = DL.getABITypeAlignment(pArrayTy); + + SmallVector 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(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(pTy); + DataLayout DL = DataLayout(mpCurrentModule); + uint32_t size = DL.getTypeAllocSizeInBits(pVecTy); + uint32_t alignment = DL.getABITypeAlignment(pVecTy); + SmallVector 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. /// @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(Function *f, const char *fileName) +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) - 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()); + 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. +void JitManager::DumpToFile(Function *f, const char *fileName) +{ + if (KNOB_DUMP_SHADER_IR) + { + std::string outDir = GetOutputDir(); std::error_code EC; const char *funcName = f->getName().data(); char fName[256]; #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 sprintf(fName, "%s.%s.ll", funcName, fileName); #endif @@ -260,7 +388,7 @@ void JitManager::DumpToFile(Function *f, const char *fileName) pModule->print(fd, nullptr); #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 sprintf(fName, "cfg.%s.%s.dot", funcName, fileName); #endif @@ -424,6 +552,13 @@ void JitCache::notifyObjectCompiled(const llvm::Module *M, llvm::MemoryBufferRef fileObj.write((const char*)&header, sizeof(header)); fileObj << Obj.getBuffer(); fileObj.flush(); + + llvm::SmallString 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 @@ -489,4 +624,4 @@ std::unique_ptr JitCache::getObject(const llvm::Module* M) fclose(fpIn); return pBuf; -} \ No newline at end of file +} diff --git a/src/gallium/drivers/swr/rasterizer/jitter/JitManager.h b/src/gallium/drivers/swr/rasterizer/jitter/JitManager.h index 9e5e4cf2b6d..4cf2214fd6f 100644 --- a/src/gallium/drivers/swr/rasterizer/jitter/JitManager.h +++ b/src/gallium/drivers/swr/rasterizer/jitter/JitManager.h @@ -157,8 +157,32 @@ struct JitManager JitInstructionSet mArch; std::string mCore; + // Debugging support + std::unordered_map mDebugStructMap; + void SetupNewModule(); void DumpAsm(llvm::Function* pFunction, 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(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>& members); }; diff --git a/src/gallium/drivers/swr/rasterizer/jitter/builder_misc.cpp b/src/gallium/drivers/swr/rasterizer/jitter/builder_misc.cpp index 7b29440010d..af9c0e58020 100644 --- a/src/gallium/drivers/swr/rasterizer/jitter/builder_misc.cpp +++ b/src/gallium/drivers/swr/rasterizer/jitter/builder_misc.cpp @@ -1484,7 +1484,7 @@ namespace SwrJit Function* pfnCttz = Intrinsic::getDeclaration(mpJitMgr->mpCurrentModule, Intrinsic::cttz, { mInt32Ty }); // 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 Value* pIndex = CALL(pfnCttz, { pMask, C(false) }); diff --git a/src/gallium/drivers/swr/rasterizer/jitter/jit_pch.hpp b/src/gallium/drivers/swr/rasterizer/jitter/jit_pch.hpp index a35fc4c377b..d0bb479128f 100644 --- a/src/gallium/drivers/swr/rasterizer/jitter/jit_pch.hpp +++ b/src/gallium/drivers/swr/rasterizer/jitter/jit_pch.hpp @@ -70,7 +70,7 @@ using PassManager = llvm::legacy::PassManager; #include "llvm/Support/Host.h" #include "llvm/Support/DynamicLibrary.h" - +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/Function.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Type.h" -- 2.30.2