DirectXShaderCompiler/lib/DxilDia/DxilDiaSession.cpp

396 строки
12 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// //
// DxilDiaSession.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. //
// //
// DIA API implementation for DXIL modules. //
// //
///////////////////////////////////////////////////////////////////////////////
#include "DxilDiaSession.h"
#include "dxc/DxilPIXPasses/DxilPIXPasses.h"
#include "dxc/DxilPIXPasses/DxilPIXVirtualRegisters.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/PassRegistry.h"
#include "DxilDia.h"
#include "DxilDiaEnumTables.h"
#include "DxilDiaTable.h"
#include "DxilDiaTableInjectedSources.h"
#include "DxilDiaTableLineNumbers.h"
#include "DxilDiaTableSourceFiles.h"
#include "DxilDiaTableSymbols.h"
void dxil_dia::Session::Init(
std::shared_ptr<llvm::LLVMContext> context,
std::shared_ptr<llvm::Module> mod,
std::shared_ptr<llvm::DebugInfoFinder> finder) {
m_pEnumTables = nullptr;
m_module = mod;
m_context = context;
m_finder = finder;
m_dxilModule = llvm::make_unique<hlsl::DxilModule>(mod.get());
llvm::legacy::PassManager PM;
llvm::initializeDxilDbgValueToDbgDeclarePass(*llvm::PassRegistry::getPassRegistry());
llvm::initializeDxilAnnotateWithVirtualRegisterPass(*llvm::PassRegistry::getPassRegistry());
PM.add(llvm::createDxilDbgValueToDbgDeclarePass());
PM.add(llvm::createDxilAnnotateWithVirtualRegisterPass());
PM.run(*m_module);
// Extract HLSL metadata.
m_dxilModule->LoadDxilMetadata();
// Get file contents.
m_contents =
m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceContentsMDName);
if (!m_contents)
m_contents = m_module->getNamedMetadata("llvm.dbg.contents");
m_defines =
m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceDefinesMDName);
if (!m_defines)
m_defines = m_module->getNamedMetadata("llvm.dbg.defines");
m_mainFileName =
m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceMainFileNameMDName);
if (!m_mainFileName)
m_mainFileName = m_module->getNamedMetadata("llvm.dbg.mainFileName");
m_arguments =
m_module->getNamedMetadata(hlsl::DxilMDHelper::kDxilSourceArgsMDName);
if (!m_arguments)
m_arguments = m_module->getNamedMetadata("llvm.dbg.args");
// Build up a linear list of instructions. The index will be used as the
// RVA.
for (llvm::Function &fn : m_module->functions()) {
for (llvm::inst_iterator it = inst_begin(fn), end = inst_end(fn); it != end; ++it) {
llvm::Instruction &i = *it;
RVA rva;
if (!pix_dxil::PixDxilInstNum::FromInst(&i, &rva)) {
continue;
}
m_rvaMap.insert({ &i, rva });
m_instructions.insert({ rva, &i});
if (llvm::DebugLoc DL = i.getDebugLoc()) {
auto result = m_lineToInfoMap.emplace(DL.getLine(), LineInfo(DL.getCol(), rva, rva + 1));
if (!result.second) {
result.first->second.StartCol = std::min(result.first->second.StartCol, DL.getCol());
result.first->second.Last = rva + 1;
}
m_instructionLines.push_back(&i);
}
}
}
// Sanity check to make sure rva map is same as instruction index.
for (auto It = m_instructions.begin(); It != m_instructions.end(); ++It) {
DXASSERT(m_rvaMap.find(It->second) != m_rvaMap.end(), "instruction not mapped to rva");
DXASSERT(m_rvaMap[It->second] == It->first, "instruction mapped to wrong rva");
}
// Initialize symbols
try {
m_symsMgr.Init(this);
} catch (const hlsl::Exception &) {
m_symsMgr = std::move(dxil_dia::SymbolManager());
}
}
HRESULT dxil_dia::Session::getSourceFileIdByName(
llvm::StringRef fileName,
DWORD *pRetVal) {
if (Contents() != nullptr) {
for (unsigned i = 0; i < Contents()->getNumOperands(); ++i) {
llvm::StringRef fn =
llvm::dyn_cast<llvm::MDString>(Contents()->getOperand(i)->getOperand(0))
->getString();
if (fn.equals(fileName)) {
*pRetVal = i;
return S_OK;
}
}
}
*pRetVal = 0;
return S_FALSE;
}
STDMETHODIMP dxil_dia::Session::get_loadAddress(
/* [retval][out] */ ULONGLONG *pRetVal) {
*pRetVal = 0;
return S_OK;
}
STDMETHODIMP dxil_dia::Session::get_globalScope(
/* [retval][out] */ IDiaSymbol **pRetVal) {
DxcThreadMalloc TM(m_pMalloc);
if (pRetVal == nullptr) {
return E_INVALIDARG;
}
*pRetVal = nullptr;
Symbol *ret;
IFR(m_symsMgr.GetGlobalScope(&ret));
*pRetVal = ret;
return S_OK;
}
STDMETHODIMP dxil_dia::Session::getEnumTables(
/* [out] */ _COM_Outptr_ IDiaEnumTables **ppEnumTables) {
if (!m_pEnumTables) {
DxcThreadMalloc TM(m_pMalloc);
IFR(EnumTables::Create(this, &m_pEnumTables));
}
m_pEnumTables.p->AddRef();
*ppEnumTables = m_pEnumTables;
return S_OK;
}
STDMETHODIMP dxil_dia::Session::findFileById(
/* [in] */ DWORD uniqueId,
/* [out] */ IDiaSourceFile **ppResult) {
if (!m_pEnumTables) {
return E_INVALIDARG;
}
CComPtr<IDiaTable> pTable;
VARIANT vtIndex;
vtIndex.vt = VT_UI4;
vtIndex.uintVal = (int)Table::Kind::SourceFiles;
IFR(m_pEnumTables->Item(vtIndex, &pTable));
CComPtr<IUnknown> pElt;
IFR(pTable->Item(uniqueId, &pElt));
return pElt->QueryInterface(ppResult);
}
STDMETHODIMP dxil_dia::Session::findFile(
/* [in] */ IDiaSymbol *pCompiland,
/* [in] */ LPCOLESTR name,
/* [in] */ DWORD compareFlags,
/* [out] */ IDiaEnumSourceFiles **ppResult) {
if (!m_pEnumTables) {
return E_INVALIDARG;
}
// TODO: properly support compareFlags.
auto namecmp = &_wcsicmp;
if (compareFlags & nsCaseSensitive) {
namecmp = &wcscmp;
}
DxcThreadMalloc TM(m_pMalloc);
CComPtr<IDiaTable> pTable;
VARIANT vtIndex;
vtIndex.vt = VT_UI4;
vtIndex.uintVal = (int)Table::Kind::SourceFiles;
IFR(m_pEnumTables->Item(vtIndex, &pTable));
CComPtr<IDiaEnumSourceFiles> pSourceTable;
IFR(pTable->QueryInterface(&pSourceTable));
HRESULT hr;
CComPtr<IDiaSourceFile> src;
ULONG cnt;
std::vector<CComPtr<IDiaSourceFile>> sources;
pSourceTable->Reset();
while (SUCCEEDED(hr = pSourceTable->Next(1, &src, &cnt)) && hr == S_OK && cnt == 1) {
CComBSTR currName;
IFR(src->get_fileName(&currName));
if (namecmp(name, currName) == 0) {
sources.emplace_back(src);
}
src.Release();
}
*ppResult = CreateOnMalloc<SourceFilesTable>(
GetMallocNoRef(),
this,
std::move(sources));
if (*ppResult == nullptr) {
return E_OUTOFMEMORY;
}
(*ppResult)->AddRef();
return S_OK;
}
namespace dxil_dia {
static HRESULT DxcDiaFindLineNumbersByRVA(
Session *pSession,
DWORD rva,
DWORD length,
IDiaEnumLineNumbers **ppResult)
{
if (!ppResult)
return E_POINTER;
std::vector<const llvm::Instruction*> instructions;
auto &allInstructions = pSession->InstructionsRef();
// Gather the list of insructions that map to the given rva range.
for (DWORD i = rva; i < rva + length; ++i) {
auto It = allInstructions.find(i);
if (It == allInstructions.end())
return E_INVALIDARG;
// Only include the instruction if it has debug info for line mappings.
const llvm::Instruction *inst = It->second;
if (inst->getDebugLoc())
instructions.push_back(inst);
}
// Create line number table from explicit instruction list.
IMalloc *pMalloc = pSession->GetMallocNoRef();
*ppResult = CreateOnMalloc<LineNumbersTable>(pMalloc, pSession, std::move(instructions));
if (*ppResult == nullptr)
return E_OUTOFMEMORY;
(*ppResult)->AddRef();
return S_OK;
}
} // namespace dxil_dia
STDMETHODIMP dxil_dia::Session::findLinesByAddr(
/* [in] */ DWORD seg,
/* [in] */ DWORD offset,
/* [in] */ DWORD length,
/* [out] */ IDiaEnumLineNumbers **ppResult) {
DxcThreadMalloc TM(m_pMalloc);
return DxcDiaFindLineNumbersByRVA(this, offset, length, ppResult);
}
STDMETHODIMP dxil_dia::Session::findLinesByRVA(
/* [in] */ DWORD rva,
/* [in] */ DWORD length,
/* [out] */ IDiaEnumLineNumbers **ppResult) {
DxcThreadMalloc TM(m_pMalloc);
return DxcDiaFindLineNumbersByRVA(this, rva, length, ppResult);
}
STDMETHODIMP dxil_dia::Session::findInlineeLinesByAddr(
/* [in] */ IDiaSymbol *parent,
/* [in] */ DWORD isect,
/* [in] */ DWORD offset,
/* [in] */ DWORD length,
/* [out] */ IDiaEnumLineNumbers **ppResult) {
DxcThreadMalloc TM(m_pMalloc);
return DxcDiaFindLineNumbersByRVA(this, offset, length, ppResult);
}
STDMETHODIMP dxil_dia::Session::findLinesByLinenum(
/* [in] */ IDiaSymbol *compiland,
/* [in] */ IDiaSourceFile *file,
/* [in] */ DWORD linenum,
/* [in] */ DWORD column,
/* [out] */ IDiaEnumLineNumbers **ppResult) {
if (!m_pEnumTables) {
return E_INVALIDARG;
}
*ppResult = nullptr;
DxcThreadMalloc TM(m_pMalloc);
CComPtr<IDiaTable> pTable;
VARIANT vtIndex;
vtIndex.vt = VT_UI4;
vtIndex.uintVal = (int)Table::Kind::LineNumbers;
IFR(m_pEnumTables->Item(vtIndex, &pTable));
CComPtr<IDiaEnumLineNumbers> pLineTable;
IFR(pTable->QueryInterface(&pLineTable));
HRESULT hr;
CComPtr<IDiaLineNumber> line;
ULONG cnt;
std::vector<const llvm::Instruction *> lines;
std::function<bool(DWORD, DWORD)>column_matches = [column](DWORD colStart, DWORD colEnd) -> bool {
return true;
};
if (column != 0) {
column_matches = [column](DWORD colStart, DWORD colEnd) -> bool {
return colStart < column && column < colEnd;
};
}
pLineTable->Reset();
while (SUCCEEDED(hr = pLineTable->Next(1, &line, &cnt)) && hr == S_OK && cnt == 1) {
CComPtr<IDiaSourceFile> f;
DWORD ln, lnEnd, cn, cnEnd;
IFR(line->get_lineNumber(&ln));
IFR(line->get_lineNumberEnd(&lnEnd));
IFR(line->get_columnNumber(&cn));
IFR(line->get_columnNumberEnd(&cnEnd));
IFR(line->get_sourceFile(&f));
if (file == f && (ln <= linenum && linenum <= lnEnd) && column_matches(cn, cnEnd)) {
lines.emplace_back(reinterpret_cast<LineNumber*>(line.p)->Inst());
}
line.Release();
}
HRESULT result = lines.empty() ? S_FALSE : S_OK;
*ppResult = CreateOnMalloc<LineNumbersTable>(
GetMallocNoRef(),
this,
std::move(lines));
if (*ppResult == nullptr) {
return E_OUTOFMEMORY;
}
(*ppResult)->AddRef();
return result;
}
STDMETHODIMP dxil_dia::Session::findInjectedSource(
/* [in] */ LPCOLESTR srcFile,
/* [out] */ IDiaEnumInjectedSources **ppResult) {
if (Contents() != nullptr) {
CW2A pUtf8FileName(srcFile, CP_UTF8);
DxcThreadMalloc TM(m_pMalloc);
IDiaTable *pTable;
IFT(Table::Create(this, Table::Kind::InjectedSource, &pTable));
auto *pInjectedSource =
reinterpret_cast<InjectedSourcesTable *>(pTable);
pInjectedSource->Init(pUtf8FileName.m_psz);
*ppResult = pInjectedSource;
return S_OK;
}
return S_FALSE;
}
static constexpr DWORD kD3DCodeSection = 1;
STDMETHODIMP dxil_dia::Session::findInlineFramesByAddr(
/* [in] */ IDiaSymbol *parent,
/* [in] */ DWORD isect,
/* [in] */ DWORD offset,
/* [out] */ IDiaEnumSymbols **ppResult) {
if (parent != nullptr || isect != kD3DCodeSection || ppResult == nullptr) {
return E_INVALIDARG;
}
*ppResult = nullptr;
DxcThreadMalloc TM(m_pMalloc);
auto &allInstructions = InstructionsRef();
auto It = allInstructions.find(offset);
if (It == allInstructions.end()) {
return E_INVALIDARG;
}
HRESULT hr;
SymbolChildrenEnumerator *ChildrenEnum;
IFR(hr = m_symsMgr.DbgScopeOf(It->second, &ChildrenEnum));
*ppResult = ChildrenEnum;
return hr;
}