Răsfoiți Sursa

Added new PDB interface (#3288)

Adam Yang 4 ani în urmă
părinte
comite
747ee519eb

+ 6 - 0
include/dxc/DXIL/DxilMetadataHelper.h

@@ -112,6 +112,12 @@ public:
   static const char kDxilSourceMainFileNameMDName[];
   static const char kDxilSourceArgsMDName[];
 
+  // Old source info.
+  static const char kDxilSourceContentsOldMDName[];
+  static const char kDxilSourceDefinesOldMDName[];
+  static const char kDxilSourceMainFileNameOldMDName[];
+  static const char kDxilSourceArgsOldMDName[];
+
   static const unsigned kDxilEntryPointNumFields  = 5;
   static const unsigned kDxilEntryPointFunction   = 0;  // Entry point function symbol.
   static const unsigned kDxilEntryPointName       = 1;  // Entry point unmangled name.

+ 36 - 0
include/dxc/dxcapi.h

@@ -557,6 +557,34 @@ struct IDxcOptimizer : public IUnknown {
     _COM_Outptr_opt_ IDxcBlobEncoding **ppOutputText) = 0;
 };
 
+CROSS_PLATFORM_UUIDOF(IDxcPdbUtils, "E6C9647E-9D6A-4C3B-B94C-524B5A6C343D")
+struct IDxcPdbUtils : public IUnknown {
+  virtual HRESULT STDMETHODCALLTYPE Load(_In_ IDxcBlob *pPdbOrDxil) = 0;
+
+  virtual HRESULT STDMETHODCALLTYPE GetSourceCount(_Out_ UINT32 *pCount) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetSource(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobEncoding **ppResult) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetSourceName(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0;
+
+  virtual HRESULT STDMETHODCALLTYPE GetFlagCount(_Out_ UINT32 *pCount) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetFlag(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0;
+
+  virtual HRESULT STDMETHODCALLTYPE GetArgCount(_Out_ UINT32 *pCount) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetArg(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0;
+
+  virtual HRESULT STDMETHODCALLTYPE GetDefineCount(_Out_ UINT32 *pCount) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetDefine(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) = 0;
+
+  virtual HRESULT STDMETHODCALLTYPE GetTargetProfile(_Outptr_result_z_ BSTR *pResult) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetEntryPoint(_Outptr_result_z_ BSTR *pResult) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetMainFileName(_Outptr_result_z_ BSTR *pResult) = 0;
+
+  virtual HRESULT STDMETHODCALLTYPE GetHash(_COM_Outptr_ IDxcBlob **ppResult) = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetName(_Outptr_result_z_ BSTR *pResult) = 0;
+
+  virtual BOOL STDMETHODCALLTYPE IsFullPDB() = 0;
+  virtual HRESULT STDMETHODCALLTYPE GetFullPDB(_COM_Outptr_ IDxcBlob **ppFullPDB) = 0;
+};
+
 static const UINT32 DxcVersionInfoFlags_None = 0;
 static const UINT32 DxcVersionInfoFlags_Debug = 1; // Matches VS_FF_DEBUG
 static const UINT32 DxcVersionInfoFlags_Internal = 2; // Internal Validator (non-signing)
@@ -650,4 +678,12 @@ CLSID_SCOPE const GUID CLSID_DxcContainerBuilder = {
     0x411f,
     0x4574,
     {0xb4, 0xd0, 0x87, 0x41, 0xe2, 0x52, 0x40, 0xd2}};
+
+// {54621dfb-f2ce-457e-ae8c-ec355faeec7c}
+CLSID_SCOPE const GUID CLSID_DxcPdbUtils = {
+    0x54621dfb,
+    0xf2ce,
+    0x457e,
+    {0xae, 0x8c, 0xec, 0x35, 0x5f, 0xae, 0xec, 0x7c}};
+
 #endif

+ 5 - 0
lib/DXIL/DxilMetadataHelper.cpp

@@ -68,6 +68,11 @@ const char DxilMDHelper::kDxilSourceDefinesMDName[]                   = "dx.sour
 const char DxilMDHelper::kDxilSourceMainFileNameMDName[]              = "dx.source.mainFileName";
 const char DxilMDHelper::kDxilSourceArgsMDName[]                      = "dx.source.args";
 
+const char DxilMDHelper::kDxilSourceContentsOldMDName[]               = "llvm.dbg.contents";
+const char DxilMDHelper::kDxilSourceDefinesOldMDName[]                = "llvm.dbg.defines";
+const char DxilMDHelper::kDxilSourceMainFileNameOldMDName[]           = "llvm.dbg.mainFileName";
+const char DxilMDHelper::kDxilSourceArgsOldMDName[]                   = "llvm.dbg.args";
+
 // This is reflection-only metadata
 const char DxilMDHelper::kDxilCountersMDName[]                        = "dx.counters";
 

+ 7 - 1
tools/clang/tools/dxcompiler/CMakeLists.txt

@@ -54,6 +54,7 @@ set(SOURCES
   dxillib.cpp
   dxcutil.cpp
   dxcdisassembler.cpp
+  dxcpdbutils.cpp
   dxclinker.cpp
 )
 else ()
@@ -66,6 +67,7 @@ set(SOURCES
   dxcfilesystem.cpp
   dxcutil.cpp
   dxcdisassembler.cpp
+  dxcpdbutils.cpp
   dxillib.cpp
   dxcvalidator.cpp
 )
@@ -111,6 +113,10 @@ set(GENERATED_HEADERS
   ClangStmtNodes
   )
 
+if (WIN32)
+  find_package(DiaSDK REQUIRED) # Used for constants and declarations.
+endif (WIN32)
+
 add_clang_library(dxcompiler SHARED ${SOURCES})
 add_dependencies(dxcompiler TablegenHLSLOptions) 
 if (WIN32)
@@ -121,7 +127,7 @@ target_link_libraries(dxcompiler PRIVATE ${LIBRARIES})
 if (ENABLE_SPIRV_CODEGEN)
   target_link_libraries(dxcompiler PRIVATE clangSPIRV)
 endif (ENABLE_SPIRV_CODEGEN)
-include_directories(AFTER ${LLVM_INCLUDE_DIR}/dxc/Tracing)
+include_directories(AFTER ${LLVM_INCLUDE_DIR}/dxc/Tracing ${DIASDK_INCLUDE_DIRS})
 
 set_target_properties(dxcompiler
   PROPERTIES

+ 4 - 0
tools/clang/tools/dxcompiler/dxcapi.cpp

@@ -38,6 +38,7 @@ HRESULT CreateDxcAssembler(_In_ REFIID riid, _Out_ LPVOID *ppv);
 HRESULT CreateDxcOptimizer(_In_ REFIID riid, _Out_ LPVOID *ppv);
 HRESULT CreateDxcContainerBuilder(_In_ REFIID riid, _Out_ LPVOID *ppv);
 HRESULT CreateDxcLinker(_In_ REFIID riid, _Out_ LPVOID *ppv);
+HRESULT CreateDxcPdbUtils(_In_ REFIID riid, _Out_ LPVOID *ppv);
 
 namespace hlsl {
 void CreateDxcContainerReflection(IDxcContainerReflection **ppResult);
@@ -121,6 +122,9 @@ static HRESULT ThreadMallocDxcCreateInstance(
   else if (IsEqualCLSID(rclsid, CLSID_DxcContainerBuilder)) {
     hr = CreateDxcContainerBuilder(riid, ppv);
   }
+  else if (IsEqualCLSID(rclsid, CLSID_DxcPdbUtils)) {
+    hr = CreateDxcPdbUtils(riid, ppv);
+  }
 #endif
   else {
     hr = REGDB_E_CLASSNOTREG;

+ 422 - 0
tools/clang/tools/dxcompiler/dxcpdbutils.cpp

@@ -0,0 +1,422 @@
+///////////////////////////////////////////////////////////////////////////////
+//                                                                           //
+// dxilpdbutils.cpp                                                          //
+// Copyright (C) Microsoft Corporation. All rights reserved.                 //
+// This file is distributed under the University of Illinois Open Source     //
+// License. See LICENSE.TXT for details.                                     //
+//                                                                           //
+// Implements IDxcPdbUtils interface, which allows getting basic stuff from  //
+// shader PDBS.                                                              //
+//                                                                           //
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef _WIN32
+
+#include "dxc/Support/Global.h"
+#include "dxc/Support/WinIncludes.h"
+#include "dxc/Support/dxcapi.use.h"
+#include "dxc/Support/FileIOHelper.h"
+#include "llvm/Support/MSFileSystem.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+
+#include "dxc/dxcapi.h"
+#include "dxc/dxcpix.h"
+#include "dxc/Support/microcom.h"
+#include "dxc/DxilContainer/DxilContainer.h"
+#include "dxc/DXIL/DxilUtil.h"
+#include "dxc/DXIL/DxilPDB.h"
+#include "dxc/DXIL/DxilMetadataHelper.h"
+#include "dxc/DXIL/DxilModule.h"
+
+#include <vector>
+#include <locale>
+#include <codecvt>
+#include <string>
+#include <dia2.h>
+
+using namespace dxc;
+using namespace llvm;
+
+static std::wstring ToWstring(const char *ptr, size_t size) {
+  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
+  return converter.from_bytes(ptr, ptr+size);
+}
+static std::wstring ToWstring(const char *ptr) {
+  return ToWstring(ptr, strlen(ptr));
+}
+static std::wstring ToWstring(const std::string &str) {
+  return ToWstring(str.data(), str.size());
+}
+static std::wstring ToWstring(StringRef str) {
+  return ToWstring(str.data(), str.size());
+}
+static HRESULT CopyWstringToBSTR(const std::wstring &str, BSTR *pResult) {
+  if (!pResult) return E_POINTER;
+  *pResult = nullptr;
+
+  if (str.empty())
+    return S_OK;
+
+  CComBSTR buf(str.data());
+  *pResult = buf.Detach();
+  return S_OK;
+}
+
+static bool ShouldIncludeInFlags(StringRef strRef, bool *skip_next_arg) {
+  *skip_next_arg = false;
+  const char *specialCases[] = { "/T", "-T", "-D", "/D", "-E", "/E", };
+  for (unsigned i = 0; i < _countof(specialCases); i++) {
+    if (strRef == specialCases[i]) {
+      *skip_next_arg = true;
+      return false;
+    }
+    else if (strRef.startswith(specialCases[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+struct DxcPdbUtils : public IDxcPdbUtils, public IDxcPixDxilDebugInfoFactory
+{
+private:
+  DXC_MICROCOM_TM_REF_FIELDS()
+
+  struct Source_File {
+    std::wstring Name;
+    CComPtr<IDxcBlobEncoding> Content;
+  };
+
+  CComPtr<IDxcBlob> m_InputBlob;
+  CComPtr<IDxcBlobEncoding> m_pDxilPartBlob;
+  CComPtr<IDxcBlob> m_ContainerBlob;
+  std::vector<Source_File> m_SourceFiles;
+  std::vector<std::wstring> m_Defines;
+  std::vector<std::wstring> m_Args;
+  std::vector<std::wstring> m_Flags;
+  std::wstring m_EntryPoint;
+  std::wstring m_TargetProfile;
+  std::wstring m_Name;
+  std::wstring m_MainFileName;
+  CComPtr<IDxcBlob> m_HashBlob;
+
+  void Reset() {
+    m_pDxilPartBlob = nullptr;
+    m_InputBlob = nullptr;
+    m_ContainerBlob = nullptr;
+    m_SourceFiles.clear();
+    m_Defines.clear();
+    m_Args.clear();
+    m_Flags.clear();
+    m_EntryPoint.clear();
+    m_TargetProfile.clear();
+    m_Name.clear();
+    m_MainFileName.clear();
+    m_HashBlob = nullptr;
+  }
+
+public:
+  DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
+  DXC_MICROCOM_TM_ALLOC(DxcPdbUtils)
+
+  DxcPdbUtils(IMalloc *pMalloc) : m_dwRef(0), m_pMalloc(pMalloc) {}
+
+  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
+    return DoBasicQueryInterface<IDxcPdbUtils, IDxcPixDxilDebugInfoFactory>(this, iid, ppvObject);
+  }
+
+  HRESULT STDMETHODCALLTYPE Load(_In_ IDxcBlob *pPdbOrDxil) override {
+    DxcThreadMalloc TM(m_pMalloc);
+
+    ::llvm::sys::fs::MSFileSystem *msfPtr = nullptr;
+    IFT(CreateMSFileSystemForDisk(&msfPtr));
+    std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr);
+  
+    ::llvm::sys::fs::AutoPerThreadSystem pts(msf.get());
+    IFTLLVM(pts.error_code());
+
+    if (!pPdbOrDxil)
+      return E_POINTER;
+
+    // Remove all the data
+    Reset();
+
+    m_InputBlob = pPdbOrDxil;
+
+    // Right now, what we do here is just to support the current PDB formats.
+    // Note: this API only supports PDB blob, and DXIL container, not DXIL module or program header,
+    // because we're moving away from that.
+    //
+    {
+      CComPtr<IStream> pStream;
+      IFR(hlsl::CreateReadOnlyBlobStream(pPdbOrDxil, &pStream));
+      if (SUCCEEDED(hlsl::pdb::LoadDataFromStream(m_pMalloc, pStream, &m_ContainerBlob))) {}
+      else if (hlsl::IsValidDxilContainer((const hlsl::DxilContainerHeader *)pPdbOrDxil->GetBufferPointer(), pPdbOrDxil->GetBufferSize())) {
+        m_ContainerBlob = pPdbOrDxil;
+      }
+      else {
+        return E_INVALIDARG;
+      }
+    }
+
+    const hlsl::DxilContainerHeader *header = (hlsl::DxilContainerHeader *)m_ContainerBlob->GetBufferPointer();
+    for (auto it = hlsl::begin(header); it != hlsl::end(header); it++) {
+      const hlsl::DxilPartHeader *part = *it;
+      hlsl::DxilFourCC four_cc = (hlsl::DxilFourCC)part->PartFourCC;
+
+      switch (four_cc) {
+
+      case hlsl::DFCC_ShaderHash:
+      {
+        const hlsl::DxilShaderHash *hash_header = (hlsl::DxilShaderHash *)(part+1);
+        IFR(hlsl::DxcCreateBlobOnHeapCopy(hash_header->Digest, sizeof(hash_header->Digest), &m_HashBlob));
+      } break;
+
+      case hlsl::DFCC_ShaderDebugName:
+      {
+        const hlsl::DxilShaderDebugName *name_header = (hlsl::DxilShaderDebugName *)(part+1);
+        const char *ptr = (char *)(name_header+1);
+        m_Name = ToWstring(ptr, name_header->NameLength);
+      } break;
+
+      case hlsl::DFCC_ShaderDebugInfoDXIL:
+      {
+        hlsl::DxilProgramHeader *program_header = (hlsl::DxilProgramHeader *)(part+1);
+
+        IFR(hlsl::DxcCreateBlobWithEncodingFromPinned(program_header, program_header->SizeInUint32*sizeof(UINT32), CP_ACP, &m_pDxilPartBlob));
+
+        UINT32 bitcode_size = 0;
+        const char *bitcode = nullptr;
+        hlsl::GetDxilProgramBitcode(program_header, &bitcode, &bitcode_size);
+
+        llvm::LLVMContext context;
+        std::unique_ptr<llvm::Module> pModule;
+
+        // NOTE: this doesn't copy the memory, just references it.
+        std::unique_ptr<llvm::MemoryBuffer> mb = llvm::MemoryBuffer::getMemBuffer(StringRef(bitcode, bitcode_size), "-", /*RequiresNullTerminator*/ false);
+
+        // Lazily parse the module
+        std::string DiagStr;
+        pModule = hlsl::dxilutil::LoadModuleFromBitcodeLazy(std::move(mb), context, DiagStr);
+        if (!pModule)
+          return E_FAIL;
+
+        // Materialize only the stuff we need, so it's fast
+        {
+          llvm::StringRef DebugMetadataList[] = {
+            hlsl::DxilMDHelper::kDxilSourceContentsMDName,
+            hlsl::DxilMDHelper::kDxilSourceDefinesMDName,
+            hlsl::DxilMDHelper::kDxilSourceArgsMDName,
+            hlsl::DxilMDHelper::kDxilVersionMDName,
+            hlsl::DxilMDHelper::kDxilShaderModelMDName,
+            hlsl::DxilMDHelper::kDxilEntryPointsMDName,
+            hlsl::DxilMDHelper::kDxilSourceMainFileNameMDName,
+          };
+          pModule->materializeSelectNamedMetadata(DebugMetadataList);
+        }
+
+        hlsl::DxilModule &DM = pModule->GetOrCreateDxilModule();
+        m_EntryPoint = ToWstring(DM.GetEntryFunctionName());
+        m_TargetProfile = ToWstring(DM.GetShaderModel()->GetName());
+
+        // For each all the named metadata node in the module
+        for (llvm::NamedMDNode &node : pModule->named_metadata()) {
+          llvm::StringRef node_name = node.getName();
+
+          // dx.source.content
+          if (node_name == hlsl::DxilMDHelper::kDxilSourceContentsMDName ||
+              node_name == hlsl::DxilMDHelper::kDxilSourceContentsOldMDName)
+          {
+            for (unsigned i = 0; i < node.getNumOperands(); i++) {
+              llvm::MDTuple *tup = cast<llvm::MDTuple>(node.getOperand(i));
+              MDString *md_name = cast<MDString>(tup->getOperand(0));
+              MDString *md_content = cast<MDString>(tup->getOperand(1));
+
+              // File name
+              Source_File file;
+              file.Name = ToWstring(md_name->getString());
+
+              // File content
+              IFR(hlsl::DxcCreateBlobWithEncodingOnHeapCopy(
+                md_content->getString().data(),
+                md_content->getString().size(),
+                CP_ACP, // NOTE: ACP instead of UTF8 because it's possible for compiler implementations to
+                        // inject non-UTF8 data here.
+                &file.Content));
+
+              m_SourceFiles.push_back(std::move(file));
+            }
+          }
+          // dx.source.defines
+          else if (node_name == hlsl::DxilMDHelper::kDxilSourceDefinesMDName ||
+                   node_name == hlsl::DxilMDHelper::kDxilSourceDefinesOldMDName)
+          {
+            MDTuple *tup = cast<MDTuple>(node.getOperand(0));
+            for (unsigned i = 0; i < tup->getNumOperands(); i++) {
+              StringRef define = cast<MDString>(tup->getOperand(i))->getString();
+              m_Defines.push_back(ToWstring(define));
+            }
+          }
+          // dx.source.mainFileName
+          else if (node_name == hlsl::DxilMDHelper::kDxilSourceMainFileNameMDName ||
+                   node_name == hlsl::DxilMDHelper::kDxilSourceMainFileNameOldMDName)
+          {
+            MDTuple *tup = cast<MDTuple>(node.getOperand(0));
+            MDString *str = cast<MDString>(tup->getOperand(0));
+            m_MainFileName = ToWstring(str->getString());
+          }
+          // dx.source.args
+          else if (node_name == hlsl::DxilMDHelper::kDxilSourceArgsMDName ||
+                   node_name == hlsl::DxilMDHelper::kDxilSourceArgsOldMDName)
+          {
+            MDTuple *tup = cast<MDTuple>(node.getOperand(0));
+            // Args
+            for (unsigned i = 0; i < tup->getNumOperands(); i++) {
+              StringRef arg = cast<MDString>(tup->getOperand(i))->getString();
+              m_Args.push_back(ToWstring(arg));
+            }
+
+            // Flags - which exclude entry point, target profile, and defines
+            for (unsigned i = 0; i < tup->getNumOperands(); i++) {
+              StringRef arg = cast<MDString>(tup->getOperand(i))->getString();
+              bool skip_another_arg = false;
+              if (ShouldIncludeInFlags(arg, &skip_another_arg)) {
+                m_Flags.push_back(ToWstring(arg));
+              }
+              if (skip_another_arg)
+                i++;
+            }
+          }
+        }
+
+      } break; // hlsl::DFCC_ShaderDebugInfoDXIL
+      } // switch (four_cc)
+    } // For each part
+
+    return S_OK;
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetSourceCount(_Out_ UINT32 *pCount) override {
+    if (!pCount) return E_POINTER;
+    *pCount = (UINT32)m_SourceFiles.size();
+    return S_OK;
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetSource(_In_ UINT32 uIndex, _COM_Outptr_ IDxcBlobEncoding **ppResult) override {
+    if (uIndex >= m_SourceFiles.size()) return E_INVALIDARG;
+    if (!ppResult) return E_POINTER;
+    *ppResult = nullptr;
+    return m_SourceFiles[uIndex].Content.QueryInterface(ppResult);
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetSourceName(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) override {
+    if (uIndex >= m_SourceFiles.size()) return E_INVALIDARG;
+    return CopyWstringToBSTR(m_SourceFiles[uIndex].Name, pResult);
+  }
+
+  static inline HRESULT GetStringCount(const std::vector<std::wstring> &list, _Out_ UINT32 *pCount) {
+    if (!pCount) return E_POINTER;
+    *pCount = (UINT32)list.size();
+    return S_OK;
+  }
+
+  static inline HRESULT GetStringOption(const std::vector<std::wstring> &list, _In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) {
+    if (uIndex >= list.size()) return E_INVALIDARG;
+    return CopyWstringToBSTR(list[uIndex], pResult);
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetFlagCount(_Out_ UINT32 *pCount) override {  return GetStringCount(m_Flags, pCount); }
+  virtual HRESULT STDMETHODCALLTYPE GetFlag(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) override { return GetStringOption(m_Flags, uIndex, pResult); }
+  virtual HRESULT STDMETHODCALLTYPE GetArgCount(_Out_ UINT32 *pCount) override { return GetStringCount(m_Args, pCount); }
+  virtual HRESULT STDMETHODCALLTYPE GetArg(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) override { return GetStringOption(m_Args, uIndex, pResult); }
+  virtual HRESULT STDMETHODCALLTYPE GetDefineCount(_Out_ UINT32 *pCount) override { return GetStringCount(m_Defines, pCount); }
+  virtual HRESULT STDMETHODCALLTYPE GetDefine(_In_ UINT32 uIndex, _Outptr_result_z_ BSTR *pResult) override { return GetStringOption(m_Defines, uIndex, pResult); }
+
+  virtual HRESULT STDMETHODCALLTYPE GetTargetProfile(_Outptr_result_z_ BSTR *pResult) override {
+    return CopyWstringToBSTR(m_TargetProfile, pResult);
+  }
+  virtual HRESULT STDMETHODCALLTYPE GetEntryPoint(_Outptr_result_z_ BSTR *pResult) override {
+    return CopyWstringToBSTR(m_EntryPoint, pResult);
+  }
+  virtual HRESULT STDMETHODCALLTYPE GetMainFileName(_Outptr_result_z_ BSTR *pResult) {
+    return CopyWstringToBSTR(m_MainFileName, pResult);
+  }
+
+  virtual BOOL STDMETHODCALLTYPE IsFullPDB() override {
+    return m_pDxilPartBlob != nullptr;
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetFullPDB(_COM_Outptr_ IDxcBlob **ppFullPDB) override {
+    if (!m_InputBlob)
+      return E_FAIL;
+    if (!ppFullPDB) return E_POINTER;
+    *ppFullPDB = nullptr;
+    return m_InputBlob.QueryInterface(ppFullPDB);
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetHash(_COM_Outptr_ IDxcBlob **ppResult) override {
+    if (!ppResult) return E_POINTER;
+    *ppResult = nullptr;
+    return m_HashBlob.QueryInterface(ppResult);
+  }
+
+  virtual HRESULT STDMETHODCALLTYPE GetName(_Outptr_result_z_ BSTR *pResult) override {
+    return CopyWstringToBSTR(m_Name, pResult);
+  }
+
+  virtual STDMETHODIMP NewDxcPixDxilDebugInfo(
+      _COM_Outptr_ IDxcPixDxilDebugInfo **ppDxilDebugInfo) override
+  {
+    if (!m_pDxilPartBlob)
+      return E_FAIL;
+
+    DxcThreadMalloc TM(m_pMalloc);
+
+    CComPtr<IDiaDataSource> pDataSource;
+    IFR(DxcCreateInstance2(m_pMalloc, CLSID_DxcDiaDataSource, __uuidof(IDiaDataSource),
+      (void **)&pDataSource));
+
+    CComPtr<IStream> pStream;
+    IFR(hlsl::CreateReadOnlyBlobStream(m_pDxilPartBlob, &pStream));
+
+    IFR(pDataSource->loadDataFromIStream(pStream));
+
+    CComPtr<IDiaSession> pSession;
+    IFR(pDataSource->openSession(&pSession));
+
+    CComPtr<IDxcPixDxilDebugInfoFactory> pFactory;
+    IFR(pSession.QueryInterface(&pFactory));
+
+    return pFactory->NewDxcPixDxilDebugInfo(ppDxilDebugInfo);
+  }
+
+  virtual STDMETHODIMP NewDxcPixCompilationInfo(
+      _COM_Outptr_ IDxcPixCompilationInfo **ppCompilationInfo) override
+  {
+    return E_NOTIMPL;
+  }
+};
+
+HRESULT CreateDxcPdbUtils(_In_ REFIID riid, _Out_ LPVOID *ppv) {
+  CComPtr<DxcPdbUtils> result = CreateOnMalloc<DxcPdbUtils>(DxcGetThreadMallocNoRef());
+  if (result == nullptr) {
+    *ppv = nullptr;
+    return E_OUTOFMEMORY;
+  }
+  return result.p->QueryInterface(riid, ppv);
+}
+
+#else
+
+#include "dxc/Support/WinIncludes.h"
+
+HRESULT CreateDxcPdbUtils(_In_ REFIID riid, _Out_ LPVOID *ppv) {
+  return E_NOTIMPL;
+}
+
+#endif
+
+

+ 168 - 0
tools/clang/unittests/HLSL/CompilerTest.cpp

@@ -17,6 +17,7 @@
 #include <vector>
 #include <string>
 #include <map>
+#include <set>
 #include <cassert>
 #include <sstream>
 #include <algorithm>
@@ -125,6 +126,7 @@ public:
   TEST_METHOD(CompileWhenDebugWorksThenStripDebug)
   TEST_METHOD(CompileWhenWorksThenAddRemovePrivate)
   TEST_METHOD(CompileThenAddCustomDebugName)
+  TEST_METHOD(CompileThenTestPdbUtils)
   TEST_METHOD(CompileWithRootSignatureThenStripRootSignature)
 
   TEST_METHOD(CompileWhenIncludeThenLoadInvoked)
@@ -972,6 +974,172 @@ TEST_F(CompilerTest, CompileThenAddCustomDebugName) {
   VERIFY_IS_NULL(pPartHeader);
 }
 
+#ifdef _WIN32
+TEST_F(CompilerTest, CompileThenTestPdbUtils) {
+  CComPtr<TestIncludeHandler> pInclude;
+  CComPtr<IDxcCompiler> pCompiler;
+  CComPtr<IDxcBlobEncoding> pSource;
+  CComPtr<IDxcOperationResult> pResult;
+
+  std::string main_source = "#include \"helper.h\"\r\n"
+    "float4 PSMain() : SV_Target { return ZERO; }";
+  std::string included_File = "#define ZERO 0";
+
+  VERIFY_SUCCEEDED(CreateCompiler(&pCompiler));
+  CreateBlobFromText(main_source.c_str(), &pSource);
+
+  pInclude = new TestIncludeHandler(m_dllSupport);
+  pInclude->CallResults.emplace_back(included_File.c_str());
+
+  const WCHAR *pArgs[] = { L"/Zi", L"/Od", L"-flegacy-macro-expansion", L"-Qembed_debug", L"/DTHIS_IS_A_DEFINE=HELLO" };
+  const DxcDefine pDefines[] = { L"THIS_IS_ANOTHER_DEFINE", L"1" };
+
+  VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"PSMain",
+    L"ps_6_0", pArgs, _countof(pArgs), pDefines, _countof(pDefines), pInclude, &pResult));
+
+  HRESULT CompileStatus = S_OK;
+  VERIFY_SUCCEEDED(pResult->GetStatus(&CompileStatus));
+  VERIFY_SUCCEEDED(CompileStatus);
+
+  CComPtr<IDxcBlob> pCompiledBlob;
+  VERIFY_SUCCEEDED(pResult->GetResult(&pCompiledBlob));
+
+  CComPtr<IDxcPdbUtils> pPdbUtils;
+  VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcPdbUtils, &pPdbUtils));
+
+  {
+    VERIFY_SUCCEEDED(pPdbUtils->Load(pCompiledBlob));
+
+    // Target profile
+    {
+      CComBSTR str;
+      VERIFY_SUCCEEDED(pPdbUtils->GetTargetProfile(&str));
+      VERIFY_ARE_EQUAL(str, L"ps_6_0");
+    }
+
+    // Entry point
+    {
+      CComBSTR str;
+      VERIFY_SUCCEEDED(pPdbUtils->GetEntryPoint(&str));
+      VERIFY_ARE_EQUAL(str, L"PSMain");
+    }
+
+    // PDB file path
+    {
+      CComBSTR pName;
+      VERIFY_SUCCEEDED(pPdbUtils->GetName(&pName));
+      std::wstring suffix = L".pdb";
+      VERIFY_IS_TRUE(pName.Length() >= suffix.size());
+      VERIFY_IS_TRUE(
+        0 == std::memcmp(suffix.c_str(), &pName[pName.Length() - suffix.size()], suffix.size()));
+    }
+
+    // Main file name
+    {
+      CComBSTR pMainFileName;
+      VERIFY_SUCCEEDED(pPdbUtils->GetMainFileName(&pMainFileName));
+      VERIFY_ARE_EQUAL(pMainFileName, L"source.hlsl");
+    }
+
+    // This is a full PDB
+    {
+      VERIFY_IS_TRUE(pPdbUtils->IsFullPDB());
+      CComPtr<IDxcBlob> pPDBBlob;
+      VERIFY_SUCCEEDED(pPdbUtils->GetFullPDB(&pPDBBlob));
+    }
+
+    // There is hash and hash is not empty
+    {
+      CComPtr<IDxcBlob> pHash;
+      VERIFY_SUCCEEDED(pPdbUtils->GetHash(&pHash));
+      BYTE EmptyHash[16] = {};
+      VERIFY_ARE_EQUAL(pHash->GetBufferSize(), _countof(EmptyHash));
+      VERIFY_IS_FALSE(0 == std::memcmp(pHash->GetBufferPointer(), EmptyHash, _countof(EmptyHash)));
+    }
+
+    // Source files
+    {
+      UINT32 uSourceCount = 0;
+      VERIFY_SUCCEEDED(pPdbUtils->GetSourceCount(&uSourceCount));
+      for (UINT32 i = 0; i < uSourceCount; i++) {
+        CComBSTR pFileName;
+        CComPtr<IDxcBlobEncoding> pFileContent;
+        VERIFY_SUCCEEDED(pPdbUtils->GetSourceName(i, &pFileName));
+        VERIFY_SUCCEEDED(pPdbUtils->GetSource(i, &pFileContent));
+        if (0 == wcscmp(pFileName, L"source.hlsl")) {
+          VERIFY_IS_TRUE(pFileContent->GetBufferSize() == main_source.size());
+          VERIFY_IS_TRUE(0 == std::memcmp(pFileContent->GetBufferPointer(), main_source.data(), main_source.size()));
+        }
+        else {
+          VERIFY_IS_TRUE(0 == std::memcmp(pFileContent->GetBufferPointer(), included_File.data(), included_File.size()));
+        }
+      }
+    }
+
+    // Defines
+    {
+      UINT32 uDefineCount = 0;
+      std::map<std::wstring, int> pdb_defines;
+      VERIFY_SUCCEEDED(pPdbUtils->GetDefineCount(&uDefineCount));
+      VERIFY_IS_TRUE(uDefineCount == 2);
+      for (UINT32 i = 0; i < uDefineCount; i++) {
+        CComBSTR def;
+        VERIFY_SUCCEEDED(pPdbUtils->GetDefine(i, &def));
+        pdb_defines[std::wstring(def)]++;
+      }
+      VERIFY_IS_TRUE(1 == pdb_defines[L"THIS_IS_A_DEFINE=HELLO"]);
+      VERIFY_IS_TRUE(1 == pdb_defines[L"THIS_IS_ANOTHER_DEFINE=1"]);
+    }
+
+    // Flags
+    {
+      const WCHAR *pExpectedFlags[] = { L"/Zi", L"/Od", L"-flegacy-macro-expansion", L"-Qembed_debug", };
+
+      UINT32 uCount = 0;
+      std::map<std::wstring, int> tally;
+      VERIFY_SUCCEEDED(pPdbUtils->GetFlagCount(&uCount));
+      VERIFY_IS_TRUE(uCount == _countof(pExpectedFlags));
+      for (UINT32 i = 0; i < uCount; i++) {
+        CComBSTR def;
+        VERIFY_SUCCEEDED(pPdbUtils->GetFlag(i, &def));
+        tally[std::wstring(def)]++;
+      }
+      for (unsigned i = 0; i < _countof(pExpectedFlags); i++) {
+        VERIFY_IS_TRUE(1 == tally[pExpectedFlags[i]]);
+      }
+    }
+
+    // Args
+    {
+      UINT32 uCount = 0;
+      std::map<std::wstring, int> tally;
+      VERIFY_SUCCEEDED(pPdbUtils->GetArgCount(&uCount));
+      for (UINT32 i = 0; i < uCount; i++) {
+        CComBSTR def;
+        VERIFY_SUCCEEDED(pPdbUtils->GetArg(i, &def));
+        tally[std::wstring(def)]++;
+      }
+      VERIFY_IS_TRUE(1 == tally[L"/Zi"]);
+      VERIFY_IS_TRUE(1 == tally[L"/Od"]);
+      VERIFY_IS_TRUE(1 == tally[L"-flegacy-macro-expansion"]);
+      VERIFY_IS_TRUE(1 == tally[L"-Qembed_debug"]);
+      VERIFY_IS_TRUE(1 == tally[L"/DTHIS_IS_A_DEFINE=HELLO"]);
+    }
+
+    // Make the pix debug info
+    {
+      CComPtr<IDxcPixDxilDebugInfoFactory> pFactory;
+      VERIFY_SUCCEEDED(pPdbUtils.QueryInterface(&pFactory));
+      CComPtr<IDxcPixCompilationInfo> pCompInfo;
+      VERIFY_ARE_EQUAL(E_NOTIMPL, pFactory->NewDxcPixCompilationInfo(&pCompInfo));
+      CComPtr<IDxcPixDxilDebugInfo> pDebugInfo;
+      VERIFY_SUCCEEDED(pFactory->NewDxcPixDxilDebugInfo(&pDebugInfo));
+      VERIFY_ARE_NOT_EQUAL(pDebugInfo, nullptr);
+    }
+  }
+}
+#endif
+
 TEST_F(CompilerTest, CompileWithRootSignatureThenStripRootSignature) {
   CComPtr<IDxcCompiler> pCompiler;
   CComPtr<IDxcOperationResult> pResult;