swr/rast: Initial work for debugging support.
authorGeorge Kyriazis <george.kyriazis@intel.com>
Fri, 19 Jan 2018 21:46:59 +0000 (15:46 -0600)
committerGeorge Kyriazis <george.kyriazis@intel.com>
Fri, 19 Jan 2018 22:52:22 +0000 (16:52 -0600)
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>
src/gallium/drivers/swr/rasterizer/codegen/gen_llvm_types.py
src/gallium/drivers/swr/rasterizer/codegen/templates/gen_llvm.hpp
src/gallium/drivers/swr/rasterizer/jitter/JitManager.cpp
src/gallium/drivers/swr/rasterizer/jitter/JitManager.h
src/gallium/drivers/swr/rasterizer/jitter/builder_misc.cpp
src/gallium/drivers/swr/rasterizer/jitter/jit_pch.hpp

index ccf2bde1edd7aa356a9663435746cbd36b1f06de..398cde3ed928a8171a4344be21c212d81738a907 100644 (file)
@@ -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.
index 574ee5aaa7906521a8de2603149fee21fe0adcb1..61c31328a6ae74bf1f8830f73faaa218204da24a 100644 (file)
@@ -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<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;
index fbca1a74a72f50b07111ec24ae93c53b4bc63e0d..2b993613f535f6ac28659273c3094ca31ca2f9ae 100644 (file)
@@ -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.
 /// @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<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
@@ -489,4 +624,4 @@ std::unique_ptr<llvm::MemoryBuffer> JitCache::getObject(const llvm::Module* M)
     fclose(fpIn);
 
     return pBuf;
-}
\ No newline at end of file
+}
index 9e5e4cf2b6d319504bd4bd1901de17d7a2c8825d..4cf2214fd6fa509780a0c37de8437b0a54f30cd9 100644 (file)
@@ -157,8 +157,32 @@ struct JitManager
     JitInstructionSet mArch;
     std::string mCore;
 
+    // Debugging support
+    std::unordered_map<llvm::StructType*, llvm::DIType*> 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<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);
 };
index 7b29440010d791fc1f0a45c66188ec240747fbef..af9c0e5802028ef1db7873e6d9e5a673c7df3748 100644 (file)
@@ -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) });
index a35fc4c377bae8afc3715fa26a1bd5edeac3edfd..d0bb479128f16171b5750d11b115f942a8d87ddc 100644 (file)
@@ -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"