1353 строки
48 KiB
C++
1353 строки
48 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// DxilDbgValueToDbgDeclare.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. //
|
|
// //
|
|
// Converts calls to llvm.dbg.value to llvm.dbg.declare + alloca + stores. //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
#include "dxc/DXIL/DxilConstants.h"
|
|
#include "dxc/DXIL/DxilModule.h"
|
|
#include "dxc/DXIL/DxilResourceBase.h"
|
|
#include "dxc/DxilPIXPasses/DxilPIXPasses.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/IR/DIBuilder.h"
|
|
#include "llvm/IR/DebugInfo.h"
|
|
#include "llvm/IR/DebugInfoMetadata.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/Intrinsics.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Pass.h"
|
|
|
|
#include "PixPassHelpers.h"
|
|
using namespace PIXPassHelpers;
|
|
|
|
using namespace llvm;
|
|
|
|
//#define VALUE_TO_DECLARE_LOGGING
|
|
|
|
#ifdef VALUE_TO_DECLARE_LOGGING
|
|
#ifndef PIX_DEBUG_DUMP_HELPER
|
|
#error Turn on PIX_DEBUG_DUMP_HELPER in PixPassHelpers.h
|
|
#endif
|
|
#define VALUE_TO_DECLARE_LOG Log
|
|
#else
|
|
#define VALUE_TO_DECLARE_LOG(...)
|
|
#endif
|
|
|
|
#define DEBUG_TYPE "dxil-dbg-value-to-dbg-declare"
|
|
|
|
namespace {
|
|
using OffsetInBits = unsigned;
|
|
using SizeInBits = unsigned;
|
|
|
|
// OffsetManager is used to map between "packed" and aligned offsets.
|
|
//
|
|
// For example, the aligned offsets for a struct [float, half, int, double]
|
|
// will be {0, 32, 64, 128} (assuming 32 bit alignments for ints, and 64
|
|
// bit for doubles), while the packed offsets will be {0, 32, 48, 80}.
|
|
//
|
|
// This mapping makes it easier to deal with llvm.dbg.values whose value
|
|
// operand does not match exactly the Variable operand's type.
|
|
class OffsetManager {
|
|
unsigned DescendTypeToGetAlignMask(llvm::DIType *Ty) {
|
|
unsigned AlignMask = Ty->getAlignInBits();
|
|
|
|
auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty);
|
|
if (DerivedTy != nullptr) {
|
|
// Working around a bug where byte size is stored instead of bit size
|
|
if (AlignMask == 4 && Ty->getSizeInBits() == 32) {
|
|
AlignMask = 32;
|
|
}
|
|
if (AlignMask == 0) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
switch (DerivedTy->getTag()) {
|
|
case llvm::dwarf::DW_TAG_restrict_type:
|
|
case llvm::dwarf::DW_TAG_reference_type:
|
|
case llvm::dwarf::DW_TAG_const_type:
|
|
case llvm::dwarf::DW_TAG_typedef: {
|
|
llvm::DIType *baseType = DerivedTy->getBaseType().resolve(EmptyMap);
|
|
if (baseType != nullptr) {
|
|
if (baseType->getAlignInBits() == 0) {
|
|
(void)baseType->getAlignInBits();
|
|
}
|
|
return DescendTypeToGetAlignMask(baseType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return AlignMask;
|
|
}
|
|
|
|
public:
|
|
OffsetManager() = default;
|
|
|
|
// AlignTo aligns the current aligned offset to Ty's natural alignment.
|
|
void AlignTo(llvm::DIType *Ty) {
|
|
unsigned AlignMask = DescendTypeToGetAlignMask(Ty);
|
|
if (AlignMask) {
|
|
VALUE_TO_DECLARE_LOG("Aligning to %d", AlignMask);
|
|
// This is some magic arithmetic. Here's an example:
|
|
//
|
|
// Assume the natural alignment for Ty is 16 bits. Then
|
|
//
|
|
// AlignMask = 0x0000000f(15)
|
|
//
|
|
// If the current aligned offset is
|
|
//
|
|
// CurrentAlignedOffset = 0x00000048(72)
|
|
//
|
|
// Then
|
|
//
|
|
// T = CurrentAlignOffset + AlignMask = 0x00000057(87)
|
|
//
|
|
// Which mean
|
|
//
|
|
// T & ~CurrentOffset = 0x00000050(80)
|
|
//
|
|
// is the aligned offset where Ty should be placed.
|
|
AlignMask = AlignMask - 1;
|
|
m_CurrentAlignedOffset =
|
|
(m_CurrentAlignedOffset + AlignMask) & ~AlignMask;
|
|
} else {
|
|
VALUE_TO_DECLARE_LOG("Failed to find alignment");
|
|
}
|
|
}
|
|
|
|
// Add is used to "add" an aggregate element (struct field, array element)
|
|
// at the current aligned/packed offsets, bumping them by Ty's size.
|
|
OffsetInBits Add(llvm::DIBasicType *Ty) {
|
|
VALUE_TO_DECLARE_LOG("Adding known type at aligned %d / packed %d, size %d",
|
|
m_CurrentAlignedOffset, m_CurrentPackedOffset,
|
|
Ty->getSizeInBits());
|
|
|
|
m_PackedOffsetToAlignedOffset[m_CurrentPackedOffset] =
|
|
m_CurrentAlignedOffset;
|
|
m_AlignedOffsetToPackedOffset[m_CurrentAlignedOffset] =
|
|
m_CurrentPackedOffset;
|
|
|
|
const OffsetInBits Ret = m_CurrentAlignedOffset;
|
|
m_CurrentPackedOffset += Ty->getSizeInBits();
|
|
m_CurrentAlignedOffset += Ty->getSizeInBits();
|
|
|
|
return Ret;
|
|
}
|
|
|
|
// AlignToAndAddUnhandledType is used for error handling when Ty
|
|
// could not be handled by the transformation. This is a best-effort
|
|
// way to continue the pass by ignoring the current type and hoping
|
|
// that adding Ty as a blob other fields/elements added will land
|
|
// in the proper offset.
|
|
void AlignToAndAddUnhandledType(llvm::DIType *Ty) {
|
|
VALUE_TO_DECLARE_LOG(
|
|
"Adding unhandled type at aligned %d / packed %d, size %d",
|
|
m_CurrentAlignedOffset, m_CurrentPackedOffset, Ty->getSizeInBits());
|
|
AlignTo(Ty);
|
|
m_CurrentPackedOffset += Ty->getSizeInBits();
|
|
m_CurrentAlignedOffset += Ty->getSizeInBits();
|
|
}
|
|
|
|
void AddResourceType(llvm::DIType *Ty) {
|
|
VALUE_TO_DECLARE_LOG(
|
|
"Adding resource type at aligned %d / packed %d, size %d",
|
|
m_CurrentAlignedOffset, m_CurrentPackedOffset, Ty->getSizeInBits());
|
|
m_PackedOffsetToAlignedOffset[m_CurrentPackedOffset] =
|
|
m_CurrentAlignedOffset;
|
|
m_AlignedOffsetToPackedOffset[m_CurrentAlignedOffset] =
|
|
m_CurrentPackedOffset;
|
|
|
|
m_CurrentPackedOffset += Ty->getSizeInBits();
|
|
m_CurrentAlignedOffset += Ty->getSizeInBits();
|
|
}
|
|
|
|
bool GetAlignedOffsetFromPackedOffset(OffsetInBits PackedOffset,
|
|
OffsetInBits *AlignedOffset) const {
|
|
return GetOffsetWithMap(m_PackedOffsetToAlignedOffset, PackedOffset,
|
|
AlignedOffset);
|
|
}
|
|
|
|
bool GetPackedOffsetFromAlignedOffset(OffsetInBits AlignedOffset,
|
|
OffsetInBits *PackedOffset) const {
|
|
return GetOffsetWithMap(m_AlignedOffsetToPackedOffset, AlignedOffset,
|
|
PackedOffset);
|
|
}
|
|
|
|
OffsetInBits GetCurrentPackedOffset() const { return m_CurrentPackedOffset; }
|
|
|
|
OffsetInBits GetCurrentAlignedOffset() const {
|
|
return m_CurrentAlignedOffset;
|
|
}
|
|
|
|
private:
|
|
OffsetInBits m_CurrentPackedOffset = 0;
|
|
OffsetInBits m_CurrentAlignedOffset = 0;
|
|
|
|
using OffsetMap = std::unordered_map<OffsetInBits, OffsetInBits>;
|
|
|
|
OffsetMap m_PackedOffsetToAlignedOffset;
|
|
OffsetMap m_AlignedOffsetToPackedOffset;
|
|
|
|
static bool GetOffsetWithMap(const OffsetMap &Map, OffsetInBits SrcOffset,
|
|
OffsetInBits *DstOffset) {
|
|
auto it = Map.find(SrcOffset);
|
|
if (it == Map.end()) {
|
|
return false;
|
|
}
|
|
|
|
*DstOffset = it->second;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// VariableRegisters contains the logic for traversing a DIType T and
|
|
// creating AllocaInsts that map back to a specific offset within T.
|
|
class VariableRegisters {
|
|
public:
|
|
VariableRegisters(llvm::DebugLoc const &m_dbgLoc,
|
|
llvm::BasicBlock::iterator allocaInsertionPoint,
|
|
llvm::DIVariable *Variable, llvm::DIType *Ty,
|
|
llvm::Module *M);
|
|
|
|
llvm::AllocaInst *
|
|
GetRegisterForAlignedOffset(OffsetInBits AlignedOffset) const;
|
|
|
|
const OffsetManager &GetOffsetManager() const { return m_Offsets; }
|
|
|
|
static SizeInBits GetVariableSizeInbits(DIVariable *Var);
|
|
|
|
private:
|
|
void PopulateAllocaMap(llvm::DIType *Ty);
|
|
|
|
void PopulateAllocaMap_BasicType(llvm::DIBasicType *Ty);
|
|
|
|
void PopulateAllocaMap_ArrayType(llvm::DICompositeType *Ty);
|
|
|
|
void PopulateAllocaMap_StructType(llvm::DICompositeType *Ty);
|
|
|
|
llvm::DILocation *GetVariableLocation() const;
|
|
llvm::Value *GetMetadataAsValue(llvm::Metadata *M) const;
|
|
llvm::DIExpression *GetDIExpression(llvm::DIType *Ty, OffsetInBits Offset,
|
|
SizeInBits ParentSize) const;
|
|
|
|
llvm::DebugLoc const &m_dbgLoc;
|
|
llvm::DIVariable *m_Variable = nullptr;
|
|
llvm::IRBuilder<> m_B;
|
|
llvm::Function *m_DbgDeclareFn = nullptr;
|
|
|
|
OffsetManager m_Offsets;
|
|
std::unordered_map<OffsetInBits, llvm::AllocaInst *> m_AlignedOffsetToAlloca;
|
|
};
|
|
|
|
struct GlobalEmbeddedArrayElementStorage {
|
|
std::string Name;
|
|
OffsetInBits Offset;
|
|
SizeInBits Size;
|
|
};
|
|
using GlobalVariableToLocalMirrorMap =
|
|
std::map<llvm::Function const *, llvm::DILocalVariable *>;
|
|
struct LocalMirrorsAndStorage {
|
|
std::vector<GlobalEmbeddedArrayElementStorage> ArrayElementStorage;
|
|
GlobalVariableToLocalMirrorMap LocalMirrors;
|
|
};
|
|
using GlobalStorageMap =
|
|
std::map<llvm::DIGlobalVariable *, LocalMirrorsAndStorage>;
|
|
|
|
class DxilDbgValueToDbgDeclare : public llvm::ModulePass {
|
|
public:
|
|
static char ID;
|
|
DxilDbgValueToDbgDeclare() : llvm::ModulePass(ID) {}
|
|
bool runOnModule(llvm::Module &M) override;
|
|
|
|
private:
|
|
void handleDbgValue(llvm::Module &M, llvm::DbgValueInst *DbgValue);
|
|
bool handleStoreIfDestIsGlobal(llvm::Module &M,
|
|
GlobalStorageMap &GlobalStorage,
|
|
llvm::StoreInst *Store);
|
|
|
|
std::unordered_map<llvm::DIVariable *, std::unique_ptr<VariableRegisters>>
|
|
m_Registers;
|
|
};
|
|
} // namespace
|
|
|
|
char DxilDbgValueToDbgDeclare::ID = 0;
|
|
|
|
struct ValueAndOffset {
|
|
llvm::Value *m_V;
|
|
OffsetInBits m_PackedOffset;
|
|
};
|
|
|
|
// SplitValue splits an llvm::Value into possibly multiple
|
|
// scalar Values. Those scalar values will later be "stored"
|
|
// into their corresponding register.
|
|
static OffsetInBits SplitValue(llvm::Value *V, OffsetInBits CurrentOffset,
|
|
std::vector<ValueAndOffset> *Values,
|
|
llvm::IRBuilder<> &B) {
|
|
auto *VTy = V->getType();
|
|
if (auto *ArrTy = llvm::dyn_cast<llvm::ArrayType>(VTy)) {
|
|
for (unsigned i = 0; i < ArrTy->getNumElements(); ++i) {
|
|
CurrentOffset =
|
|
SplitValue(B.CreateExtractValue(V, {i}), CurrentOffset, Values, B);
|
|
}
|
|
} else if (auto *StTy = llvm::dyn_cast<llvm::StructType>(VTy)) {
|
|
for (unsigned i = 0; i < StTy->getNumElements(); ++i) {
|
|
CurrentOffset =
|
|
SplitValue(B.CreateExtractValue(V, {i}), CurrentOffset, Values, B);
|
|
}
|
|
} else if (auto *VecTy = llvm::dyn_cast<llvm::VectorType>(VTy)) {
|
|
for (unsigned i = 0; i < VecTy->getNumElements(); ++i) {
|
|
CurrentOffset =
|
|
SplitValue(B.CreateExtractElement(V, i), CurrentOffset, Values, B);
|
|
}
|
|
} else {
|
|
assert(VTy->isFloatTy() || VTy->isDoubleTy() || VTy->isHalfTy() ||
|
|
VTy->isIntegerTy(32) || VTy->isIntegerTy(64) ||
|
|
VTy->isIntegerTy(16) || VTy->isPointerTy());
|
|
Values->emplace_back(ValueAndOffset{V, CurrentOffset});
|
|
CurrentOffset += VTy->getScalarSizeInBits();
|
|
}
|
|
|
|
return CurrentOffset;
|
|
}
|
|
|
|
// A more convenient version of SplitValue.
|
|
static std::vector<ValueAndOffset>
|
|
SplitValue(llvm::Value *V, OffsetInBits CurrentOffset, llvm::IRBuilder<> &B) {
|
|
std::vector<ValueAndOffset> Ret;
|
|
SplitValue(V, CurrentOffset, &Ret, B);
|
|
return Ret;
|
|
}
|
|
|
|
// Convenient helper for parsing a DIExpression's offset.
|
|
static OffsetInBits GetAlignedOffsetFromDIExpression(llvm::DIExpression *Exp) {
|
|
if (!Exp->isBitPiece()) {
|
|
return 0;
|
|
}
|
|
|
|
return Exp->getBitPieceOffset();
|
|
}
|
|
|
|
llvm::DISubprogram *GetFunctionDebugInfo(llvm::Module &M, llvm::Function *fn) {
|
|
auto FnMap = makeSubprogramMap(M);
|
|
return FnMap[fn];
|
|
}
|
|
|
|
GlobalVariableToLocalMirrorMap
|
|
GenerateGlobalToLocalMirrorMap(llvm::Module &M, llvm::DIGlobalVariable *DIGV) {
|
|
auto &Functions = M.getFunctionList();
|
|
|
|
std::string LocalMirrorOfGlobalName =
|
|
std::string("global.") + std::string(DIGV->getName());
|
|
|
|
GlobalVariableToLocalMirrorMap ret;
|
|
DenseMap<const Function *, DISubprogram *> FnMap;
|
|
|
|
for (llvm::Function const &fn : Functions) {
|
|
auto &blocks = fn.getBasicBlockList();
|
|
if (!blocks.empty()) {
|
|
auto &LocalMirror = ret[&fn];
|
|
for (auto &block : blocks) {
|
|
bool breakOut = false;
|
|
for (auto &instruction : block) {
|
|
if (auto const *DbgValue =
|
|
llvm::dyn_cast<llvm::DbgValueInst>(&instruction)) {
|
|
auto *Variable = DbgValue->getVariable();
|
|
if (Variable->getName().equals(LocalMirrorOfGlobalName)) {
|
|
LocalMirror = Variable;
|
|
breakOut = true;
|
|
break;
|
|
}
|
|
}
|
|
if (auto const *DbgDeclare =
|
|
llvm::dyn_cast<llvm::DbgDeclareInst>(&instruction)) {
|
|
auto *Variable = DbgDeclare->getVariable();
|
|
if (Variable->getName().equals(LocalMirrorOfGlobalName)) {
|
|
LocalMirror = Variable;
|
|
breakOut = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (breakOut)
|
|
break;
|
|
}
|
|
if (LocalMirror == nullptr) {
|
|
// If we didn't find a dbg.value for any member of this
|
|
// DIGlobalVariable, then no local mirror exists. We must manufacture
|
|
// one.
|
|
if (FnMap.empty()) {
|
|
FnMap = makeSubprogramMap(M);
|
|
}
|
|
auto DIFn = FnMap[&fn];
|
|
if (DIFn != nullptr) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
auto DIGVType = DIGV->getType().resolve(EmptyMap);
|
|
DIBuilder DbgInfoBuilder(M);
|
|
LocalMirror = DbgInfoBuilder.createLocalVariable(
|
|
dwarf::DW_TAG_auto_variable, DIFn, LocalMirrorOfGlobalName,
|
|
DIFn->getFile(), DIFn->getLine(), DIGVType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::vector<GlobalEmbeddedArrayElementStorage>
|
|
DescendTypeAndFindEmbeddedArrayElements(llvm::StringRef VariableName,
|
|
uint64_t AccumulatedMemberOffset,
|
|
llvm::DIType *Ty, uint64_t OffsetToSeek,
|
|
uint64_t SizeToSeek) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
|
|
auto BaseTy = DerivedTy->getBaseType().resolve(EmptyMap);
|
|
auto storage = DescendTypeAndFindEmbeddedArrayElements(
|
|
VariableName, AccumulatedMemberOffset, BaseTy, OffsetToSeek,
|
|
SizeToSeek);
|
|
if (!storage.empty()) {
|
|
return storage;
|
|
}
|
|
} else if (auto *CompositeTy = llvm::dyn_cast<llvm::DICompositeType>(Ty)) {
|
|
switch (CompositeTy->getTag()) {
|
|
case llvm::dwarf::DW_TAG_array_type: {
|
|
for (auto Element : CompositeTy->getElements()) {
|
|
// First element for an array is DISubrange
|
|
if (auto Subrange = llvm::dyn_cast<DISubrange>(Element)) {
|
|
auto ElementTy = CompositeTy->getBaseType().resolve(EmptyMap);
|
|
if (auto *BasicTy = llvm::dyn_cast<llvm::DIBasicType>(ElementTy)) {
|
|
bool CorrectLowerOffset = AccumulatedMemberOffset == OffsetToSeek;
|
|
bool CorrectUpperOffset =
|
|
AccumulatedMemberOffset +
|
|
Subrange->getCount() * BasicTy->getSizeInBits() ==
|
|
OffsetToSeek + SizeToSeek;
|
|
if (BasicTy != nullptr && CorrectLowerOffset &&
|
|
CorrectUpperOffset) {
|
|
std::vector<GlobalEmbeddedArrayElementStorage> storage;
|
|
for (int64_t i = 0; i < Subrange->getCount(); ++i) {
|
|
auto ElementOffset =
|
|
AccumulatedMemberOffset + i * BasicTy->getSizeInBits();
|
|
GlobalEmbeddedArrayElementStorage element;
|
|
element.Name = VariableName.str() + "." + std::to_string(i);
|
|
element.Offset = static_cast<OffsetInBits>(ElementOffset);
|
|
element.Size =
|
|
static_cast<SizeInBits>(BasicTy->getSizeInBits());
|
|
storage.push_back(std::move(element));
|
|
}
|
|
return storage;
|
|
}
|
|
}
|
|
|
|
// If we didn't succeed and return above, then we need to process each
|
|
// element in the array
|
|
std::vector<GlobalEmbeddedArrayElementStorage> storage;
|
|
for (int64_t i = 0; i < Subrange->getCount(); ++i) {
|
|
auto elementStorage = DescendTypeAndFindEmbeddedArrayElements(
|
|
VariableName,
|
|
AccumulatedMemberOffset + ElementTy->getSizeInBits() * i,
|
|
ElementTy, OffsetToSeek, SizeToSeek);
|
|
std::move(elementStorage.begin(), elementStorage.end(),
|
|
std::back_inserter(storage));
|
|
}
|
|
if (!storage.empty()) {
|
|
return storage;
|
|
}
|
|
}
|
|
}
|
|
for (auto Element : CompositeTy->getElements()) {
|
|
// First element for an array is DISubrange
|
|
if (auto Subrange = llvm::dyn_cast<DISubrange>(Element)) {
|
|
auto ElementType = CompositeTy->getBaseType().resolve(EmptyMap);
|
|
for (int64_t i = 0; i < Subrange->getCount(); ++i) {
|
|
auto storage = DescendTypeAndFindEmbeddedArrayElements(
|
|
VariableName,
|
|
AccumulatedMemberOffset + ElementType->getSizeInBits() * i,
|
|
ElementType, OffsetToSeek, SizeToSeek);
|
|
if (!storage.empty()) {
|
|
return storage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
case llvm::dwarf::DW_TAG_structure_type:
|
|
case llvm::dwarf::DW_TAG_class_type: {
|
|
for (auto Element : CompositeTy->getElements()) {
|
|
if (auto diMember = llvm::dyn_cast<DIType>(Element)) {
|
|
auto storage = DescendTypeAndFindEmbeddedArrayElements(
|
|
VariableName,
|
|
AccumulatedMemberOffset + diMember->getOffsetInBits(), diMember,
|
|
OffsetToSeek, SizeToSeek);
|
|
if (!storage.empty()) {
|
|
return storage;
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
GlobalStorageMap GatherGlobalEmbeddedArrayStorage(llvm::Module &M) {
|
|
GlobalStorageMap ret;
|
|
auto DebugFinder = llvm::make_unique<llvm::DebugInfoFinder>();
|
|
DebugFinder->processModule(M);
|
|
auto GlobalVariables = DebugFinder->global_variables();
|
|
|
|
// First find the list of global variables that represent HLSL global statics:
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
SmallVector<llvm::DIGlobalVariable *, 8> GlobalStaticVariables;
|
|
for (llvm::DIGlobalVariable *DIGV : GlobalVariables) {
|
|
if (DIGV->isLocalToUnit()) {
|
|
llvm::DIType *DIGVType = DIGV->getType().resolve(EmptyMap);
|
|
// We're only interested in aggregates, since only they might have
|
|
// embedded arrays:
|
|
if (isa<llvm::DICompositeType>(DIGVType)) {
|
|
auto LocalMirrors = GenerateGlobalToLocalMirrorMap(M, DIGV);
|
|
if (!LocalMirrors.empty()) {
|
|
GlobalStaticVariables.push_back(DIGV);
|
|
ret[DIGV].LocalMirrors = std::move(LocalMirrors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now find any globals that represent embedded arrays inside the global
|
|
// statics
|
|
for (auto HLSLStruct : GlobalStaticVariables) {
|
|
for (llvm::DIGlobalVariable *DIGV : GlobalVariables) {
|
|
if (DIGV != HLSLStruct && !DIGV->isLocalToUnit()) {
|
|
llvm::DIType *DIGVType = DIGV->getType().resolve(EmptyMap);
|
|
if (auto *DIGVDerivedType =
|
|
llvm::dyn_cast<llvm::DIDerivedType>(DIGVType)) {
|
|
if (DIGVDerivedType->getTag() == llvm::dwarf::DW_TAG_member) {
|
|
// This type is embedded within the containing DIGSV type
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
auto *Ty = HLSLStruct->getType().resolve(EmptyMap);
|
|
auto Storage = DescendTypeAndFindEmbeddedArrayElements(
|
|
DIGV->getName(), 0, Ty, DIGVDerivedType->getOffsetInBits(),
|
|
DIGVDerivedType->getSizeInBits());
|
|
auto &ArrayStorage = ret[HLSLStruct].ArrayElementStorage;
|
|
std::move(Storage.begin(), Storage.end(),
|
|
std::back_inserter(ArrayStorage));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool DxilDbgValueToDbgDeclare::runOnModule(llvm::Module &M) {
|
|
auto GlobalEmbeddedArrayStorage = GatherGlobalEmbeddedArrayStorage(M);
|
|
|
|
bool Changed = false;
|
|
|
|
auto &Functions = M.getFunctionList();
|
|
for (auto &fn : Functions) {
|
|
// #DSLTodo: We probably need to merge the list of variables for each export
|
|
// into one set so that WinPIX shader debugging can follow a thread through
|
|
// any function within a given module. (Unless PIX chooses to launch a new
|
|
// debugging session whenever control passes from one function to another.)
|
|
// For now, it's sufficient to treat each exported function as having
|
|
// completely separate variables by clearing this member:
|
|
m_Registers.clear();
|
|
// Note: they key problem here is variables in common functions called by
|
|
// multiple exported functions. The DILocalVariables in the common function
|
|
// will be exactly the same objects no matter which export called the common
|
|
// function, so the instrumentation here gets a bit confused that the same
|
|
// variable is present in two functions and ends up pointing one function
|
|
// to allocas in another function. (This is easy to repro: comment out the
|
|
// above clear(), and run PixTest::PixStructAnnotation_Lib_DualRaygen.)
|
|
// Not sure what the right path forward is: might be that we have to tag
|
|
// m_Registers with the exported function, and maybe write out a function
|
|
// identifier during debug instrumentation...
|
|
auto &blocks = fn.getBasicBlockList();
|
|
if (!blocks.empty()) {
|
|
for (auto &block : blocks) {
|
|
std::vector<Instruction *> instructions;
|
|
for (auto &instruction : block) {
|
|
instructions.push_back(&instruction);
|
|
}
|
|
// Handle store instructions before handling dbg.value, since the
|
|
// latter will add store instructions that we don't need to examine.
|
|
// Why do we handle store instructions? It's for the case of
|
|
// non-const global statics that are backed by an llvm global,
|
|
// rather than an alloca. In the llvm global case, there is no
|
|
// debug linkage between the store and the HLSL variable being
|
|
// modified. But we can patch together enough knowledge about those
|
|
// from the lists of such globals (HLSL and llvm) and comparing the
|
|
// lists.
|
|
for (auto &instruction : instructions) {
|
|
if (auto *Store = llvm::dyn_cast<llvm::StoreInst>(instruction)) {
|
|
Changed =
|
|
handleStoreIfDestIsGlobal(M, GlobalEmbeddedArrayStorage, Store);
|
|
}
|
|
}
|
|
for (auto &instruction : instructions) {
|
|
if (auto *DbgValue =
|
|
llvm::dyn_cast<llvm::DbgValueInst>(instruction)) {
|
|
llvm::Value *V = DbgValue->getValue();
|
|
if (PIXPassHelpers::IsAllocateRayQueryInstruction(V)) {
|
|
continue;
|
|
}
|
|
Changed = true;
|
|
handleDbgValue(M, DbgValue);
|
|
DbgValue->eraseFromParent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
static llvm::DIType *FindStructMemberTypeAtOffset(llvm::DICompositeType *Ty,
|
|
uint64_t Offset,
|
|
uint64_t Size);
|
|
|
|
static llvm::DIType *FindMemberTypeAtOffset(llvm::DIType *Ty, uint64_t Offset,
|
|
uint64_t Size) {
|
|
VALUE_TO_DECLARE_LOG("PopulateAllocaMap for type tag %d", Ty->getTag());
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
|
|
switch (DerivedTy->getTag()) {
|
|
default:
|
|
assert(!"Unhandled DIDerivedType");
|
|
return nullptr;
|
|
case llvm::dwarf::DW_TAG_arg_variable: // "this" pointer
|
|
case llvm::dwarf::DW_TAG_pointer_type: // "this" pointer
|
|
// what to do here?
|
|
return nullptr;
|
|
case llvm::dwarf::DW_TAG_restrict_type:
|
|
case llvm::dwarf::DW_TAG_reference_type:
|
|
case llvm::dwarf::DW_TAG_const_type:
|
|
case llvm::dwarf::DW_TAG_typedef:
|
|
return FindMemberTypeAtOffset(DerivedTy->getBaseType().resolve(EmptyMap),
|
|
Offset, Size);
|
|
case llvm::dwarf::DW_TAG_member:
|
|
return FindMemberTypeAtOffset(DerivedTy->getBaseType().resolve(EmptyMap),
|
|
Offset, Size);
|
|
case llvm::dwarf::DW_TAG_subroutine_type:
|
|
// ignore member functions.
|
|
return nullptr;
|
|
}
|
|
} else if (auto *CompositeTy = llvm::dyn_cast<llvm::DICompositeType>(Ty)) {
|
|
switch (CompositeTy->getTag()) {
|
|
default:
|
|
assert(!"Unhandled DICompositeType");
|
|
return nullptr;
|
|
case llvm::dwarf::DW_TAG_array_type:
|
|
return nullptr;
|
|
case llvm::dwarf::DW_TAG_structure_type:
|
|
case llvm::dwarf::DW_TAG_class_type:
|
|
return FindStructMemberTypeAtOffset(CompositeTy, Offset, Size);
|
|
case llvm::dwarf::DW_TAG_enumeration_type:
|
|
return nullptr;
|
|
}
|
|
} else if (auto *BasicTy = llvm::dyn_cast<llvm::DIBasicType>(Ty)) {
|
|
if (Offset == 0 && Ty->getSizeInBits() == Size) {
|
|
return BasicTy;
|
|
}
|
|
}
|
|
|
|
assert(!"Unhandled DIType");
|
|
return nullptr;
|
|
}
|
|
|
|
// SortMembers traverses all of Ty's members and returns them sorted
|
|
// by their offset from Ty's start. Returns true if the function succeeds
|
|
// and false otherwise.
|
|
static bool
|
|
SortMembers(llvm::DICompositeType *Ty,
|
|
std::map<OffsetInBits, llvm::DIDerivedType *> *SortedMembers) {
|
|
auto Elements = Ty->getElements();
|
|
if (Elements.begin() == Elements.end()) {
|
|
return false;
|
|
}
|
|
for (auto *Element : Elements) {
|
|
switch (Element->getTag()) {
|
|
case llvm::dwarf::DW_TAG_member: {
|
|
if (auto *Member = llvm::dyn_cast<llvm::DIDerivedType>(Element)) {
|
|
if (Member->getSizeInBits()) {
|
|
auto it = SortedMembers->emplace(
|
|
std::make_pair(Member->getOffsetInBits(), Member));
|
|
(void)it;
|
|
assert(it.second &&
|
|
"Invalid DIStructType"
|
|
" - members with the same offset -- are unions possible?");
|
|
}
|
|
break;
|
|
}
|
|
assert(!"member is not a Member");
|
|
return false;
|
|
}
|
|
case llvm::dwarf::DW_TAG_subprogram: {
|
|
if (isa<llvm::DISubprogram>(Element)) {
|
|
continue;
|
|
}
|
|
assert(!"DISubprogram not understood");
|
|
return false;
|
|
}
|
|
case llvm::dwarf::DW_TAG_inheritance: {
|
|
if (auto *Member = llvm::dyn_cast<llvm::DIDerivedType>(Element)) {
|
|
auto it = SortedMembers->emplace(
|
|
std::make_pair(Member->getOffsetInBits(), Member));
|
|
(void)it;
|
|
assert(it.second &&
|
|
"Invalid DIStructType"
|
|
" - members with the same offset -- are unions possible?");
|
|
}
|
|
continue;
|
|
}
|
|
default:
|
|
assert(!"Unhandled field type in DIStructType");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool IsResourceObject(llvm::DIDerivedType *DT) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
auto *BT = DT->getBaseType().resolve(EmptyMap);
|
|
if (auto *CompositeTy = llvm::dyn_cast<llvm::DICompositeType>(BT)) {
|
|
// Resource variables (e.g. TextureCube) are composite types but have no
|
|
// elements:
|
|
if (CompositeTy->getElements().begin() ==
|
|
CompositeTy->getElements().end()) {
|
|
auto name = CompositeTy->getName();
|
|
auto openTemplateListMarker = name.find_first_of('<');
|
|
if (openTemplateListMarker != llvm::StringRef::npos) {
|
|
auto hlslType = name.substr(0, openTemplateListMarker);
|
|
for (int i = static_cast<int>(hlsl::DXIL::ResourceKind::Invalid) + 1;
|
|
i < static_cast<int>(hlsl::DXIL::ResourceKind::NumEntries); ++i) {
|
|
if (hlslType == hlsl::GetResourceKindName(
|
|
static_cast<hlsl::DXIL::ResourceKind>(i))) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static llvm::DIType *FindStructMemberTypeAtOffset(llvm::DICompositeType *Ty,
|
|
uint64_t Offset,
|
|
uint64_t Size) {
|
|
std::map<OffsetInBits, llvm::DIDerivedType *> SortedMembers;
|
|
if (!SortMembers(Ty, &SortedMembers)) {
|
|
return Ty;
|
|
}
|
|
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
|
|
for (auto &member : SortedMembers) {
|
|
// "Inheritance" is a member of a composite type, but has size of zero.
|
|
// Therefore, we must descend the hierarchy once to find an actual type.
|
|
llvm::DIType *memberType = member.second;
|
|
if (memberType->getTag() == llvm::dwarf::DW_TAG_inheritance) {
|
|
memberType = member.second->getBaseType().resolve(EmptyMap);
|
|
}
|
|
if (Offset >= member.first &&
|
|
Offset < member.first + memberType->getSizeInBits()) {
|
|
uint64_t OffsetIntoThisType = Offset - member.first;
|
|
return FindMemberTypeAtOffset(memberType, OffsetIntoThisType, Size);
|
|
}
|
|
}
|
|
|
|
// Structure resources are expected to fail this (they have no real meaning in
|
|
// storage)
|
|
if (SortedMembers.size() == 1) {
|
|
switch (SortedMembers.begin()->second->getTag()) {
|
|
case llvm::dwarf::DW_TAG_structure_type:
|
|
case llvm::dwarf::DW_TAG_class_type:
|
|
if (IsResourceObject(SortedMembers.begin()->second)) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
#ifdef VALUE_TO_DECLARE_LOGGING
|
|
VALUE_TO_DECLARE_LOG(
|
|
"Didn't find a member that straddles the sought type. Container:");
|
|
{
|
|
ScopedIndenter indent;
|
|
Ty->dump();
|
|
DumpFullType(Ty);
|
|
}
|
|
VALUE_TO_DECLARE_LOG(
|
|
"Sought type is at offset %d size %d. Members and offsets:", Offset,
|
|
Size);
|
|
{
|
|
ScopedIndenter indent;
|
|
for (auto const &member : SortedMembers) {
|
|
member.second->dump();
|
|
LogPartialLine("Offset %d (size %d): ", member.first,
|
|
member.second->getSizeInBits());
|
|
DumpFullType(member.second);
|
|
}
|
|
}
|
|
#endif
|
|
assert(!"Didn't find a member that straddles the sought type");
|
|
return nullptr;
|
|
}
|
|
|
|
static bool IsDITypePointer(DIType *DTy,
|
|
const llvm::DITypeIdentifierMap &EmptyMap) {
|
|
DIDerivedType *DerivedTy = dyn_cast<DIDerivedType>(DTy);
|
|
if (!DerivedTy)
|
|
return false;
|
|
switch (DerivedTy->getTag()) {
|
|
case llvm::dwarf::DW_TAG_pointer_type:
|
|
return true;
|
|
case llvm::dwarf::DW_TAG_typedef:
|
|
case llvm::dwarf::DW_TAG_const_type:
|
|
case llvm::dwarf::DW_TAG_restrict_type:
|
|
case llvm::dwarf::DW_TAG_reference_type:
|
|
return IsDITypePointer(DerivedTy->getBaseType().resolve(EmptyMap),
|
|
EmptyMap);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DxilDbgValueToDbgDeclare::handleDbgValue(llvm::Module &M,
|
|
llvm::DbgValueInst *DbgValue) {
|
|
VALUE_TO_DECLARE_LOG("DbgValue named %s", DbgValue->getName().str().c_str());
|
|
|
|
llvm::DIVariable *Variable = DbgValue->getVariable();
|
|
if (Variable != nullptr) {
|
|
VALUE_TO_DECLARE_LOG("... DbgValue referred to variable named %s",
|
|
Variable->getName().str().c_str());
|
|
} else {
|
|
VALUE_TO_DECLARE_LOG("... variable was null too");
|
|
}
|
|
|
|
llvm::Value *V = DbgValue->getValue();
|
|
if (V == nullptr) {
|
|
// The metadata contained a null Value, so we ignore it. This
|
|
// seems to be a dxcompiler bug.
|
|
VALUE_TO_DECLARE_LOG("...Null value!");
|
|
return;
|
|
}
|
|
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
llvm::DIType *Ty = Variable->getType().resolve(EmptyMap);
|
|
if (Ty == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (llvm::isa<llvm::PointerType>(V->getType())) {
|
|
// Safeguard: If the type is not a pointer type, then this is
|
|
// dbg.value directly pointing to a memory location instead of
|
|
// a value.
|
|
if (!IsDITypePointer(Ty, EmptyMap)) {
|
|
// We only know how to handle AllocaInsts for now
|
|
if (!isa<AllocaInst>(V)) {
|
|
VALUE_TO_DECLARE_LOG(
|
|
"... variable had pointer type, but is not an alloca.");
|
|
return;
|
|
}
|
|
|
|
IRBuilder<> B(DbgValue->getNextNode());
|
|
V = B.CreateLoad(V);
|
|
}
|
|
}
|
|
|
|
// Members' "base type" is actually the containing aggregate's type.
|
|
// To find the actual type of the variable, we must descend the container's
|
|
// type hierarchy to find the type at the expected offset/size.
|
|
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
switch (DerivedTy->getTag()) {
|
|
case llvm::dwarf::DW_TAG_member: {
|
|
Ty = FindMemberTypeAtOffset(DerivedTy->getBaseType().resolve(EmptyMap),
|
|
DerivedTy->getOffsetInBits(),
|
|
DerivedTy->getSizeInBits());
|
|
if (Ty == nullptr) {
|
|
return;
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
auto &Register = m_Registers[Variable];
|
|
if (Register == nullptr) {
|
|
Register.reset(new VariableRegisters(
|
|
DbgValue->getDebugLoc(),
|
|
DbgValue->getParent()->getParent()->getEntryBlock().begin(), Variable,
|
|
Ty, &M));
|
|
}
|
|
|
|
// Convert the offset from DbgValue's expression to a packed
|
|
// offset, which we'll need in order to determine the (packed)
|
|
// offset of each scalar Value in DbgValue.
|
|
llvm::DIExpression *expression = DbgValue->getExpression();
|
|
const OffsetInBits AlignedOffsetFromVar =
|
|
GetAlignedOffsetFromDIExpression(expression);
|
|
OffsetInBits PackedOffsetFromVar;
|
|
const OffsetManager &Offsets = Register->GetOffsetManager();
|
|
if (!Offsets.GetPackedOffsetFromAlignedOffset(AlignedOffsetFromVar,
|
|
&PackedOffsetFromVar)) {
|
|
// todo: output geometry for GS
|
|
return;
|
|
}
|
|
|
|
const OffsetInBits InitialOffset = PackedOffsetFromVar;
|
|
auto *insertPt = llvm::dyn_cast<llvm::Instruction>(V);
|
|
if (insertPt != nullptr && !llvm::isa<TerminatorInst>(insertPt)) {
|
|
insertPt = insertPt->getNextNode();
|
|
// Drivers may crash if phi nodes aren't always at the top of a block,
|
|
// so we must skip over them before inserting instructions.
|
|
while (llvm::isa<llvm::PHINode>(insertPt)) {
|
|
insertPt = insertPt->getNextNode();
|
|
}
|
|
|
|
if (insertPt != nullptr) {
|
|
llvm::IRBuilder<> B(insertPt);
|
|
B.SetCurrentDebugLocation(llvm::DebugLoc());
|
|
|
|
auto *Zero = B.getInt32(0);
|
|
|
|
// Now traverse a list of pairs {Scalar Value, InitialOffset + Offset}.
|
|
// InitialOffset is the offset from DbgValue's expression (i.e., the
|
|
// offset from the Variable's start), and Offset is the Scalar Value's
|
|
// packed offset from DbgValue's value.
|
|
for (const ValueAndOffset &VO : SplitValue(V, InitialOffset, B)) {
|
|
|
|
OffsetInBits AlignedOffset;
|
|
if (!Offsets.GetAlignedOffsetFromPackedOffset(VO.m_PackedOffset,
|
|
&AlignedOffset)) {
|
|
continue;
|
|
}
|
|
|
|
auto *AllocaInst = Register->GetRegisterForAlignedOffset(AlignedOffset);
|
|
if (AllocaInst == nullptr) {
|
|
assert(!"Failed to find alloca for var[offset]");
|
|
continue;
|
|
}
|
|
|
|
if (AllocaInst->getAllocatedType()->getArrayElementType() ==
|
|
VO.m_V->getType()) {
|
|
auto *GEP = B.CreateGEP(AllocaInst, {Zero, Zero});
|
|
B.CreateStore(VO.m_V, GEP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ScopedInstruction {
|
|
llvm::Instruction *m_Instruction;
|
|
|
|
public:
|
|
ScopedInstruction(llvm::Instruction *I) : m_Instruction(I) {}
|
|
~ScopedInstruction() { delete m_Instruction; }
|
|
llvm::Instruction *Get() const { return m_Instruction; }
|
|
};
|
|
|
|
struct GlobalVariableAndStorage {
|
|
llvm::DIGlobalVariable *DIGV;
|
|
OffsetInBits Offset;
|
|
};
|
|
|
|
GlobalVariableAndStorage
|
|
GetOffsetFromGlobalVariable(llvm::StringRef name,
|
|
GlobalStorageMap &GlobalEmbeddedArrayStorage) {
|
|
GlobalVariableAndStorage ret{};
|
|
for (auto &Variable : GlobalEmbeddedArrayStorage) {
|
|
for (auto &Storage : Variable.second.ArrayElementStorage) {
|
|
if (llvm::StringRef(Storage.Name).equals(name)) {
|
|
ret.DIGV = Variable.first;
|
|
ret.Offset = Storage.Offset;
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool DxilDbgValueToDbgDeclare::handleStoreIfDestIsGlobal(
|
|
llvm::Module &M, GlobalStorageMap &GlobalEmbeddedArrayStorage,
|
|
llvm::StoreInst *Store) {
|
|
if (Store->getDebugLoc()) {
|
|
llvm::Value *V = Store->getPointerOperand();
|
|
std::string MemberName;
|
|
if (auto *Constant = llvm::dyn_cast<llvm::ConstantExpr>(V)) {
|
|
ScopedInstruction asInstr(Constant->getAsInstruction());
|
|
if (auto *asGEP =
|
|
llvm::dyn_cast<llvm::GetElementPtrInst>(asInstr.Get())) {
|
|
// We are only interested in the case of basic types within an array
|
|
// because the PIX debug instrumentation operates at that level.
|
|
// Aggregate members will have been descended through to produce their
|
|
// own entries in the GlobalStorageMap.
|
|
// Consequently, we're only interested in the GEP's index into the
|
|
// array. Any deeper indexing in the GEP will be for embedded
|
|
// aggregates. The three operands in such a GEP mean:
|
|
// 0 = the pointer
|
|
// 1 = dereference the pointer (expected to be constant int zero)
|
|
// 2 = the index into the array
|
|
if (asGEP->getNumOperands() == 3 &&
|
|
llvm::isa<ConstantInt>(asGEP->getOperand(1)) &&
|
|
llvm::dyn_cast<ConstantInt>(asGEP->getOperand(1))
|
|
->getLimitedValue() == 0) {
|
|
// TODO: The case where this index is not a constant int
|
|
// (Needs changes to the allocas generated elsewhere in this pass.)
|
|
if (auto *arrayIndexAsConstInt =
|
|
llvm::dyn_cast<ConstantInt>(asGEP->getOperand(2))) {
|
|
int MemberIndex = arrayIndexAsConstInt->getLimitedValue();
|
|
MemberName = std::string(asGEP->getPointerOperand()->getName()) +
|
|
"." + std::to_string(MemberIndex);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
MemberName = V->getName();
|
|
}
|
|
if (!MemberName.empty()) {
|
|
auto Storage =
|
|
GetOffsetFromGlobalVariable(MemberName, GlobalEmbeddedArrayStorage);
|
|
if (Storage.DIGV != nullptr) {
|
|
llvm::DILocalVariable *Variable =
|
|
GlobalEmbeddedArrayStorage[Storage.DIGV]
|
|
.LocalMirrors[Store->getParent()->getParent()];
|
|
if (Variable != nullptr) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
llvm::DIType *Ty = Variable->getType().resolve(EmptyMap);
|
|
if (Ty != nullptr) {
|
|
auto &Register = m_Registers[Variable];
|
|
if (Register == nullptr) {
|
|
Register.reset(new VariableRegisters(
|
|
Store->getDebugLoc(),
|
|
Store->getParent()->getParent()->getEntryBlock().begin(),
|
|
Variable, Ty, &M));
|
|
}
|
|
auto *AllocaInst =
|
|
Register->GetRegisterForAlignedOffset(Storage.Offset);
|
|
if (AllocaInst != nullptr) {
|
|
IRBuilder<> B(Store->getNextNode());
|
|
auto *Zero = B.getInt32(0);
|
|
auto *GEP = B.CreateGEP(AllocaInst, {Zero, Zero});
|
|
B.CreateStore(Store->getValueOperand(), GEP);
|
|
return true; // yes, we modified the module
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false; // no we did not modify the module
|
|
}
|
|
|
|
SizeInBits VariableRegisters::GetVariableSizeInbits(DIVariable *Var) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
DIType *Ty = Var->getType().resolve(EmptyMap);
|
|
DIDerivedType *DerivedTy = nullptr;
|
|
while (Ty && (Ty->getSizeInBits() == 0 &&
|
|
(DerivedTy = dyn_cast<DIDerivedType>(Ty)))) {
|
|
Ty = DerivedTy->getBaseType().resolve(EmptyMap);
|
|
}
|
|
|
|
if (!Ty) {
|
|
assert(false &&
|
|
"Unexpected inability to resolve base type with a real size.");
|
|
return 0;
|
|
}
|
|
return Ty->getSizeInBits();
|
|
}
|
|
|
|
llvm::AllocaInst *
|
|
VariableRegisters::GetRegisterForAlignedOffset(OffsetInBits Offset) const {
|
|
auto it = m_AlignedOffsetToAlloca.find(Offset);
|
|
if (it == m_AlignedOffsetToAlloca.end()) {
|
|
return nullptr;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
// DITypePeelTypeAlias peels const, typedef, and other alias types off of Ty,
|
|
// returning the unalised type.
|
|
static llvm::DIType *DITypePeelTypeAlias(llvm::DIType *Ty) {
|
|
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
switch (DerivedTy->getTag()) {
|
|
case llvm::dwarf::DW_TAG_restrict_type:
|
|
case llvm::dwarf::DW_TAG_reference_type:
|
|
case llvm::dwarf::DW_TAG_const_type:
|
|
case llvm::dwarf::DW_TAG_typedef:
|
|
case llvm::dwarf::DW_TAG_pointer_type:
|
|
return DITypePeelTypeAlias(DerivedTy->getBaseType().resolve(EmptyMap));
|
|
case llvm::dwarf::DW_TAG_member:
|
|
return DITypePeelTypeAlias(DerivedTy->getBaseType().resolve(EmptyMap));
|
|
}
|
|
}
|
|
|
|
return Ty;
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
VariableRegisters::VariableRegisters(
|
|
llvm::DebugLoc const &dbgLoc,
|
|
llvm::BasicBlock::iterator allocaInsertionPoint, llvm::DIVariable *Variable,
|
|
llvm::DIType *Ty, llvm::Module *M)
|
|
: m_dbgLoc(dbgLoc), m_Variable(Variable), m_B(allocaInsertionPoint),
|
|
m_DbgDeclareFn(
|
|
llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::dbg_declare)) {
|
|
PopulateAllocaMap(Ty);
|
|
m_Offsets.AlignTo(Ty); // For padding.
|
|
|
|
// (min16* types can occupy 16 or 32 bits depending on whether or not they are
|
|
// natively supported. If non-native, the alignment will be 32, but the
|
|
// claimed size will still be 16, hence the "max" here)
|
|
assert(m_Offsets.GetCurrentAlignedOffset() ==
|
|
std::max<uint64_t>(DITypePeelTypeAlias(Ty)->getSizeInBits(),
|
|
DITypePeelTypeAlias(Ty)->getAlignInBits()));
|
|
}
|
|
|
|
void VariableRegisters::PopulateAllocaMap(llvm::DIType *Ty) {
|
|
VALUE_TO_DECLARE_LOG("PopulateAllocaMap for type tag %d", Ty->getTag());
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
if (auto *DerivedTy = llvm::dyn_cast<llvm::DIDerivedType>(Ty)) {
|
|
switch (DerivedTy->getTag()) {
|
|
default:
|
|
assert(!"Unhandled DIDerivedType");
|
|
m_Offsets.AlignToAndAddUnhandledType(DerivedTy);
|
|
return;
|
|
case llvm::dwarf::DW_TAG_arg_variable: // "this" pointer
|
|
case llvm::dwarf::DW_TAG_pointer_type: // "this" pointer
|
|
case llvm::dwarf::DW_TAG_restrict_type:
|
|
case llvm::dwarf::DW_TAG_reference_type:
|
|
case llvm::dwarf::DW_TAG_const_type:
|
|
case llvm::dwarf::DW_TAG_typedef:
|
|
PopulateAllocaMap(DerivedTy->getBaseType().resolve(EmptyMap));
|
|
return;
|
|
case llvm::dwarf::DW_TAG_member:
|
|
PopulateAllocaMap(DerivedTy->getBaseType().resolve(EmptyMap));
|
|
return;
|
|
case llvm::dwarf::DW_TAG_subroutine_type:
|
|
// ignore member functions.
|
|
return;
|
|
}
|
|
} else if (auto *CompositeTy = llvm::dyn_cast<llvm::DICompositeType>(Ty)) {
|
|
switch (CompositeTy->getTag()) {
|
|
default:
|
|
assert(!"Unhandled DICompositeType");
|
|
m_Offsets.AlignToAndAddUnhandledType(CompositeTy);
|
|
return;
|
|
case llvm::dwarf::DW_TAG_array_type:
|
|
PopulateAllocaMap_ArrayType(CompositeTy);
|
|
return;
|
|
case llvm::dwarf::DW_TAG_structure_type:
|
|
case llvm::dwarf::DW_TAG_class_type:
|
|
PopulateAllocaMap_StructType(CompositeTy);
|
|
return;
|
|
case llvm::dwarf::DW_TAG_enumeration_type: {
|
|
auto *baseType = CompositeTy->getBaseType().resolve(EmptyMap);
|
|
if (baseType != nullptr) {
|
|
PopulateAllocaMap(baseType);
|
|
} else {
|
|
m_Offsets.AlignToAndAddUnhandledType(CompositeTy);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
} else if (auto *BasicTy = llvm::dyn_cast<llvm::DIBasicType>(Ty)) {
|
|
PopulateAllocaMap_BasicType(BasicTy);
|
|
return;
|
|
}
|
|
|
|
assert(!"Unhandled DIType");
|
|
m_Offsets.AlignToAndAddUnhandledType(Ty);
|
|
}
|
|
|
|
static llvm::Type *GetLLVMTypeFromDIBasicType(llvm::IRBuilder<> &B,
|
|
llvm::DIBasicType *Ty) {
|
|
const SizeInBits Size = Ty->getSizeInBits();
|
|
|
|
switch (Ty->getEncoding()) {
|
|
default:
|
|
break;
|
|
|
|
case llvm::dwarf::DW_ATE_boolean:
|
|
case llvm::dwarf::DW_ATE_signed:
|
|
case llvm::dwarf::DW_ATE_unsigned:
|
|
switch (Size) {
|
|
case 16:
|
|
return B.getInt16Ty();
|
|
case 32:
|
|
return B.getInt32Ty();
|
|
case 64:
|
|
return B.getInt64Ty();
|
|
}
|
|
break;
|
|
case llvm::dwarf::DW_ATE_float:
|
|
switch (Size) {
|
|
case 16:
|
|
return B.getHalfTy();
|
|
case 32:
|
|
return B.getFloatTy();
|
|
case 64:
|
|
return B.getDoubleTy();
|
|
}
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void VariableRegisters::PopulateAllocaMap_BasicType(llvm::DIBasicType *Ty) {
|
|
llvm::Type *AllocaElementTy = GetLLVMTypeFromDIBasicType(m_B, Ty);
|
|
assert(AllocaElementTy != nullptr);
|
|
if (AllocaElementTy == nullptr) {
|
|
return;
|
|
}
|
|
|
|
const OffsetInBits AlignedOffset = m_Offsets.Add(Ty);
|
|
|
|
llvm::Type *AllocaTy = llvm::ArrayType::get(AllocaElementTy, 1);
|
|
llvm::AllocaInst *&Alloca = m_AlignedOffsetToAlloca[AlignedOffset];
|
|
Alloca = m_B.CreateAlloca(AllocaTy, m_B.getInt32(0));
|
|
Alloca->setDebugLoc(llvm::DebugLoc());
|
|
|
|
auto *Storage = GetMetadataAsValue(llvm::ValueAsMetadata::get(Alloca));
|
|
auto *Variable = GetMetadataAsValue(m_Variable);
|
|
auto *Expression = GetMetadataAsValue(
|
|
GetDIExpression(Ty, AlignedOffset, GetVariableSizeInbits(m_Variable)));
|
|
auto *DbgDeclare =
|
|
m_B.CreateCall(m_DbgDeclareFn, {Storage, Variable, Expression});
|
|
DbgDeclare->setDebugLoc(m_dbgLoc);
|
|
}
|
|
|
|
static unsigned NumArrayElements(llvm::DICompositeType *Array) {
|
|
if (Array->getElements().size() == 0) {
|
|
return 0;
|
|
}
|
|
|
|
unsigned NumElements = 1;
|
|
for (llvm::DINode *N : Array->getElements()) {
|
|
if (auto *Subrange = llvm::dyn_cast<llvm::DISubrange>(N)) {
|
|
NumElements *= Subrange->getCount();
|
|
} else {
|
|
assert(!"Unhandled array element");
|
|
return 0;
|
|
}
|
|
}
|
|
return NumElements;
|
|
}
|
|
|
|
void VariableRegisters::PopulateAllocaMap_ArrayType(llvm::DICompositeType *Ty) {
|
|
unsigned NumElements = NumArrayElements(Ty);
|
|
if (NumElements == 0) {
|
|
m_Offsets.AlignToAndAddUnhandledType(Ty);
|
|
return;
|
|
}
|
|
|
|
const SizeInBits ArraySizeInBits = Ty->getSizeInBits();
|
|
(void)ArraySizeInBits;
|
|
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
llvm::DIType *ElementTy = Ty->getBaseType().resolve(EmptyMap);
|
|
assert(ArraySizeInBits % NumElements == 0 &&
|
|
" invalid DIArrayType"
|
|
" - Size is not a multiple of NumElements");
|
|
|
|
// After aligning the current aligned offset to ElementTy's natural
|
|
// alignment, the current aligned offset must match Ty's offset
|
|
// in bits.
|
|
m_Offsets.AlignTo(ElementTy);
|
|
|
|
for (unsigned i = 0; i < NumElements; ++i) {
|
|
// This is only needed if ElementTy's size is not a multiple of
|
|
// its natural alignment.
|
|
m_Offsets.AlignTo(ElementTy);
|
|
PopulateAllocaMap(ElementTy);
|
|
}
|
|
}
|
|
|
|
void VariableRegisters::PopulateAllocaMap_StructType(
|
|
llvm::DICompositeType *Ty) {
|
|
VALUE_TO_DECLARE_LOG("Struct type : %s, size %d", Ty->getName().str().c_str(),
|
|
Ty->getSizeInBits());
|
|
std::map<OffsetInBits, llvm::DIDerivedType *> SortedMembers;
|
|
if (!SortMembers(Ty, &SortedMembers)) {
|
|
m_Offsets.AlignToAndAddUnhandledType(Ty);
|
|
return;
|
|
}
|
|
|
|
m_Offsets.AlignTo(Ty);
|
|
const OffsetInBits StructStart = m_Offsets.GetCurrentAlignedOffset();
|
|
(void)StructStart;
|
|
const llvm::DITypeIdentifierMap EmptyMap;
|
|
|
|
for (auto OffsetAndMember : SortedMembers) {
|
|
VALUE_TO_DECLARE_LOG("Member: %s at aligned offset %d",
|
|
OffsetAndMember.second->getName().str().c_str(),
|
|
OffsetAndMember.first);
|
|
// Align the offsets to the member's type natural alignment. This
|
|
// should always result in the current aligned offset being the
|
|
// same as the member's offset.
|
|
m_Offsets.AlignTo(OffsetAndMember.second);
|
|
assert(m_Offsets.GetCurrentAlignedOffset() ==
|
|
StructStart + OffsetAndMember.first &&
|
|
"Offset mismatch in DIStructType");
|
|
if (IsResourceObject(OffsetAndMember.second)) {
|
|
m_Offsets.AddResourceType(OffsetAndMember.second);
|
|
} else {
|
|
PopulateAllocaMap(
|
|
OffsetAndMember.second->getBaseType().resolve(EmptyMap));
|
|
}
|
|
}
|
|
}
|
|
|
|
// HLSL Change: remove unused function
|
|
#if 0
|
|
llvm::DILocation *VariableRegisters::GetVariableLocation() const
|
|
{
|
|
const unsigned DefaultColumn = 1;
|
|
return llvm::DILocation::get(
|
|
m_B.getContext(),
|
|
m_Variable->getLine(),
|
|
DefaultColumn,
|
|
m_Variable->getScope());
|
|
}
|
|
#endif
|
|
|
|
llvm::Value *VariableRegisters::GetMetadataAsValue(llvm::Metadata *M) const {
|
|
return llvm::MetadataAsValue::get(m_B.getContext(), M);
|
|
}
|
|
|
|
llvm::DIExpression *
|
|
VariableRegisters::GetDIExpression(llvm::DIType *Ty, OffsetInBits Offset,
|
|
SizeInBits ParentSize) const {
|
|
llvm::SmallVector<uint64_t, 3> ExpElements;
|
|
if (Offset != 0 || Ty->getSizeInBits() != ParentSize) {
|
|
ExpElements.emplace_back(llvm::dwarf::DW_OP_bit_piece);
|
|
ExpElements.emplace_back(Offset);
|
|
ExpElements.emplace_back(Ty->getSizeInBits());
|
|
}
|
|
return llvm::DIExpression::get(m_B.getContext(), ExpElements);
|
|
}
|
|
|
|
using namespace llvm;
|
|
|
|
INITIALIZE_PASS(DxilDbgValueToDbgDeclare, DEBUG_TYPE,
|
|
"Converts calls to dbg.value to dbg.declare + stores to new "
|
|
"virtual registers",
|
|
false, false)
|
|
|
|
ModulePass *llvm::createDxilDbgValueToDbgDeclarePass() {
|
|
return new DxilDbgValueToDbgDeclare();
|
|
}
|