WinPIX: Changes to support bitfields in PIX's shader debugger (#6219)

Two related sets of changes:
1) Modify the value-to-declare pass to tolerate bitfields, which are
detected by their habit of being smaller than the type in which they are
contained. The pass produces dbg.declare statements that map each HLSL
variable to some storage. With bitfields, that mapping becomes
many-to-one, because many bitfields can be stored in the same basic
type. Most of the changes are to preserve the size of the bitfield as
the code descends the type hierarchy into the underlying basic type,
which is what motivates these new "type overrride" parameters to some
functions. Complications to watch out for are min16 types, which may or
may not be their own purpose-made (16-bit) types depending on whether or
not they are enabled.
2) Modify the PIX debug-info API to return enough data to PIX to allow
it to extract (by bit shifting) a bitfield from the underlying type.
This bit is also about preserving knowledge of the field's size as
opposed to its containing type's size, hence the new dxcapi
interface/method.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Jeff Noyle 2024-02-02 09:44:32 -08:00 коммит произвёл GitHub
Родитель 74e3256484
Коммит a23d58663f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 437 добавлений и 82 удалений

Просмотреть файл

@ -47,12 +47,23 @@ struct __declspec(uuid("9ba0d9d3-457b-426f-8019-9f3849982aa2")) IDxcPixArrayType
};
struct __declspec(uuid("6c707d08-7995-4a84-bae5-e6d8291f3b78"))
IDxcPixStructField0 : public IUnknown {
virtual STDMETHODIMP GetName(_Outptr_result_z_ BSTR *Name) = 0;
virtual STDMETHODIMP GetType(_COM_Outptr_ IDxcPixType **ppType) = 0;
virtual STDMETHODIMP GetOffsetInBits(_Out_ DWORD *pOffsetInBits) = 0;
};
struct __declspec(uuid("de45597c-5869-4f97-a77b-d6650b9a16cf"))
IDxcPixStructField : public IUnknown {
virtual STDMETHODIMP GetName(_Outptr_result_z_ BSTR *Name) = 0;
virtual STDMETHODIMP GetType(_COM_Outptr_ IDxcPixType **ppType) = 0;
virtual STDMETHODIMP GetOffsetInBits(_Out_ DWORD *pOffsetInBits) = 0;
virtual STDMETHODIMP GetFieldSizeInBits(_Out_ DWORD *pFieldSizeInBits) = 0;
};
struct __declspec(uuid("24c08c44-684b-4b1c-b41b-f8772383d074"))

Просмотреть файл

@ -232,7 +232,7 @@ HRESULT CreateEntrypointWrapper(IMalloc *pMalloc, IUnknown *pReal, REFIID iid,
// Entrypoint is the base class for all entrypoints, providing
// the default QueryInterface implementation, as well as a
// more convenient way of calling SetupAndRun.
template <typename I> class Entrypoint : public I {
template <typename I, typename IParent = I> class Entrypoint : public I {
protected:
using IInterface = I;
@ -251,15 +251,16 @@ public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
void **ppvObject) override final {
return SetupAndRun(m_pMalloc,
std::mem_fn(&Entrypoint<IInterface>::QueryInterfaceImpl),
ThisPtr(this), iid, CheckNotNull(OutParam(ppvObject)));
return SetupAndRun(
m_pMalloc,
std::mem_fn(&Entrypoint<IInterface, IParent>::QueryInterfaceImpl),
ThisPtr(this), iid, CheckNotNull(OutParam(ppvObject)));
}
HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID iid, void **ppvObject) {
// Special-casing so we don't need to create a new wrapper.
if (iid == __uuidof(IInterface) || iid == __uuidof(IUnknown) ||
iid == __uuidof(INoMarshal)) {
if (iid == __uuidof(IInterface) || iid == __uuidof(IParent) ||
iid == __uuidof(IUnknown) || iid == __uuidof(INoMarshal)) {
this->AddRef();
*ppvObject = this;
return S_OK;
@ -275,6 +276,11 @@ public:
Name(IMalloc *M, IInterface *pI) : Entrypoint<IInterface>(M, pI) {} \
DXC_MICROCOM_TM_ALLOC(Name)
#define DEFINE_ENTRYPOINT_BOILERPLATE2(Name, ParentIFace) \
Name(IMalloc *M, IInterface *pI) \
: Entrypoint<IInterface, ParentIFace>(M, pI) {} \
DXC_MICROCOM_TM_ALLOC(Name)
struct IUnknownEntrypoint : public Entrypoint<IUnknown> {
DEFINE_ENTRYPOINT_BOILERPLATE(IUnknownEntrypoint);
};
@ -390,8 +396,10 @@ struct IDxcPixArrayTypeEntrypoint : public Entrypoint<IDxcPixArrayType> {
};
DEFINE_ENTRYPOINT_WRAPPER_TRAIT(IDxcPixArrayType);
struct IDxcPixStructFieldEntrypoint : public Entrypoint<IDxcPixStructField> {
DEFINE_ENTRYPOINT_BOILERPLATE(IDxcPixStructFieldEntrypoint);
struct IDxcPixStructFieldEntrypoint
: public Entrypoint<IDxcPixStructField, IDxcPixStructField0> {
DEFINE_ENTRYPOINT_BOILERPLATE2(IDxcPixStructFieldEntrypoint,
IDxcPixStructField0);
STDMETHODIMP GetName(BSTR *Name) override {
return InvokeOnReal(&IInterface::GetName, CheckNotNull(OutParam(Name)));
@ -401,6 +409,11 @@ struct IDxcPixStructFieldEntrypoint : public Entrypoint<IDxcPixStructField> {
return InvokeOnReal(&IInterface::GetType, CheckNotNull(OutParam(ppType)));
}
STDMETHODIMP GetFieldSizeInBits(DWORD *pFieldSizeInBits) override {
return InvokeOnReal(&IInterface::GetFieldSizeInBits,
CheckNotNull(OutParam(pFieldSizeInBits)));
}
STDMETHODIMP GetOffsetInBits(DWORD *pOffsetInBits) override {
return InvokeOnReal(&IInterface::GetOffsetInBits,
CheckNotNull(OutParam(pOffsetInBits)));

Просмотреть файл

@ -99,7 +99,7 @@ InitialOffsetInBitsFromDIExpression(const llvm::DataLayout &DataLayout,
}
FragmentOffsetInBits = Expression->getBitPieceOffset();
assert(Expression->getBitPieceSize() ==
assert(Expression->getBitPieceSize() <=
DataLayout.getTypeAllocSizeInBits(Alloca->getAllocatedType()));
}

Просмотреть файл

@ -291,3 +291,10 @@ dxil_debug_info::DxcPixStructField::GetOffsetInBits(DWORD *pOffsetInBits) {
*pOffsetInBits = m_pField->getOffsetInBits();
return S_OK;
}
STDMETHODIMP
dxil_debug_info::DxcPixStructField::GetFieldSizeInBits(
DWORD *pFieldSizeInBits) {
*pFieldSizeInBits = m_pField->getSizeInBits();
return S_OK;
}

Просмотреть файл

@ -208,6 +208,9 @@ public:
STDMETHODIMP GetBaseType(IDxcPixType **ppType) override;
};
struct __declspec(uuid("6c707d08-7995-4a84-bae5-e6d8291f3b78"))
PreviousDxcPixStructField {};
class DxcPixStructField : public IDxcPixStructField {
private:
DXC_MICROCOM_TM_REF_FIELDS()
@ -228,6 +231,12 @@ public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
void **ppvObject) override {
if (IsEqualIID(iid, __uuidof(PreviousDxcPixStructField))) {
*(IDxcPixStructField **)ppvObject = this;
this->AddRef();
return S_OK;
}
return DoBasicQueryInterface<IDxcPixStructField>(this, iid, ppvObject);
}
@ -236,5 +245,7 @@ public:
STDMETHODIMP GetType(IDxcPixType **ppType) override;
STDMETHODIMP GetOffsetInBits(DWORD *pOffsetInBits) override;
STDMETHODIMP GetFieldSizeInBits(DWORD *pFieldSizeInBits) override;
};
} // namespace dxil_debug_info

Просмотреть файл

@ -51,6 +51,44 @@ using namespace llvm;
namespace {
using OffsetInBits = unsigned;
using SizeInBits = unsigned;
struct Offsets {
OffsetInBits Aligned;
OffsetInBits Packed;
};
// 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;
}
llvm::DIBasicType *BaseTypeIfItIsBasicAndLarger(llvm::DIType *Ty) {
// Working around problems with bitfield size/alignment:
// For bitfield types, size may be < 32, but the underlying type
// will have the size of that basic type, e.g. 32 for ints.
// By contrast, for min16float, size will be 16, but align will be 16 or 32
// depending on whether or not 16-bit is enabled.
// So if we find a disparity in size, we can assume it's not e.g. min16float.
auto *baseType = DITypePeelTypeAlias(Ty);
if (Ty->getSizeInBits() != 0 &&
Ty->getSizeInBits() < baseType->getSizeInBits())
return llvm::dyn_cast<llvm::DIBasicType>(baseType);
return nullptr;
}
// OffsetManager is used to map between "packed" and aligned offsets.
//
@ -63,28 +101,28 @@ using SizeInBits = unsigned;
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);
}
if (BaseTypeIfItIsBasicAndLarger(Ty))
AlignMask = 0;
else {
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) {
return DescendTypeToGetAlignMask(baseType);
}
}
}
}
}
}
@ -128,7 +166,7 @@ public:
// 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) {
Offsets Add(llvm::DIBasicType *Ty, unsigned sizeOverride) {
VALUE_TO_DECLARE_LOG("Adding known type at aligned %d / packed %d, size %d",
m_CurrentAlignedOffset, m_CurrentPackedOffset,
Ty->getSizeInBits());
@ -138,9 +176,10 @@ public:
m_AlignedOffsetToPackedOffset[m_CurrentAlignedOffset] =
m_CurrentPackedOffset;
const OffsetInBits Ret = m_CurrentAlignedOffset;
m_CurrentPackedOffset += Ty->getSizeInBits();
m_CurrentAlignedOffset += Ty->getSizeInBits();
const Offsets Ret = {m_CurrentAlignedOffset, m_CurrentPackedOffset};
unsigned size = sizeOverride != 0 ? sizeOverride : Ty->getSizeInBits();
m_CurrentPackedOffset += size;
m_CurrentAlignedOffset += size;
return Ret;
}
@ -230,7 +269,8 @@ public:
private:
void PopulateAllocaMap(llvm::DIType *Ty);
void PopulateAllocaMap_BasicType(llvm::DIBasicType *Ty);
void PopulateAllocaMap_BasicType(llvm::DIBasicType *Ty,
unsigned sizeOverride);
void PopulateAllocaMap_ArrayType(llvm::DICompositeType *Ty);
@ -239,7 +279,8 @@ private:
llvm::DILocation *GetVariableLocation() const;
llvm::Value *GetMetadataAsValue(llvm::Metadata *M) const;
llvm::DIExpression *GetDIExpression(llvm::DIType *Ty, OffsetInBits Offset,
SizeInBits ParentSize) const;
SizeInBits ParentSize,
unsigned sizeOverride) const;
llvm::DebugLoc const &m_dbgLoc;
llvm::DIVariable *m_Variable = nullptr;
@ -1054,6 +1095,8 @@ SizeInBits VariableRegisters::GetVariableSizeInbits(DIVariable *Var) {
const llvm::DITypeIdentifierMap EmptyMap;
DIType *Ty = Var->getType().resolve(EmptyMap);
DIDerivedType *DerivedTy = nullptr;
if (BaseTypeIfItIsBasicAndLarger(Ty))
return Ty->getSizeInBits();
while (Ty && (Ty->getSizeInBits() == 0 &&
(DerivedTy = dyn_cast<DIDerivedType>(Ty)))) {
Ty = DerivedTy->getBaseType().resolve(EmptyMap);
@ -1076,28 +1119,6 @@ VariableRegisters::GetRegisterForAlignedOffset(OffsetInBits Offset) const {
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,
@ -1134,7 +1155,10 @@ void VariableRegisters::PopulateAllocaMap(llvm::DIType *Ty) {
PopulateAllocaMap(DerivedTy->getBaseType().resolve(EmptyMap));
return;
case llvm::dwarf::DW_TAG_member:
PopulateAllocaMap(DerivedTy->getBaseType().resolve(EmptyMap));
if (auto *baseType = BaseTypeIfItIsBasicAndLarger(DerivedTy))
PopulateAllocaMap_BasicType(baseType, DerivedTy->getSizeInBits());
else
PopulateAllocaMap(DerivedTy->getBaseType().resolve(EmptyMap));
return;
case llvm::dwarf::DW_TAG_subroutine_type:
// ignore member functions.
@ -1164,7 +1188,7 @@ void VariableRegisters::PopulateAllocaMap(llvm::DIType *Ty) {
return;
}
} else if (auto *BasicTy = llvm::dyn_cast<llvm::DIBasicType>(Ty)) {
PopulateAllocaMap_BasicType(BasicTy);
PopulateAllocaMap_BasicType(BasicTy, 0 /*no size override*/);
return;
}
@ -1207,24 +1231,28 @@ static llvm::Type *GetLLVMTypeFromDIBasicType(llvm::IRBuilder<> &B,
return nullptr;
}
void VariableRegisters::PopulateAllocaMap_BasicType(llvm::DIBasicType *Ty) {
void VariableRegisters::PopulateAllocaMap_BasicType(llvm::DIBasicType *Ty,
unsigned sizeOverride) {
llvm::Type *AllocaElementTy = GetLLVMTypeFromDIBasicType(m_B, Ty);
assert(AllocaElementTy != nullptr);
if (AllocaElementTy == nullptr) {
return;
}
const OffsetInBits AlignedOffset = m_Offsets.Add(Ty);
const auto offsets = m_Offsets.Add(Ty, sizeOverride);
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());
llvm::AllocaInst *&Alloca = m_AlignedOffsetToAlloca[offsets.Aligned];
if (Alloca == nullptr) {
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)));
GetDIExpression(Ty, sizeOverride == 0 ? offsets.Aligned : offsets.Packed,
GetVariableSizeInbits(m_Variable), sizeOverride));
auto *DbgDeclare =
m_B.CreateCall(m_DbgDeclareFn, {Storage, Variable, Expression});
DbgDeclare->setDebugLoc(m_dbgLoc);
@ -1299,14 +1327,22 @@ void VariableRegisters::PopulateAllocaMap_StructType(
// 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);
if (BaseTypeIfItIsBasicAndLarger(OffsetAndMember.second)) {
// This is the bitfields case (i.e. a field that is smaller
// than the type in which it resides). If we were to take
// the base type, then the information about the member's
// size would be lost
PopulateAllocaMap(OffsetAndMember.second);
} else {
PopulateAllocaMap(
OffsetAndMember.second->getBaseType().resolve(EmptyMap));
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));
}
}
}
}
@ -1330,12 +1366,14 @@ llvm::Value *VariableRegisters::GetMetadataAsValue(llvm::Metadata *M) const {
llvm::DIExpression *
VariableRegisters::GetDIExpression(llvm::DIType *Ty, OffsetInBits Offset,
SizeInBits ParentSize) const {
SizeInBits ParentSize,
unsigned sizeOverride) 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());
ExpElements.emplace_back(sizeOverride != 0 ? sizeOverride
: Ty->getSizeInBits());
}
return llvm::DIExpression::get(m_B.getContext(), ExpElements);
}

Просмотреть файл

@ -12,6 +12,8 @@
// This whole file is win32-only
#ifdef _WIN32
#include <array>
#include "dxc/DxilContainer/DxilContainer.h"
#include "dxc/Support/WinIncludes.h"
@ -176,6 +178,14 @@ public:
TEST_METHOD(DxcPixDxilDebugInfo_UnnamedField)
TEST_METHOD(DxcPixDxilDebugInfo_SubProgramsInNamespaces)
TEST_METHOD(DxcPixDxilDebugInfo_SubPrograms)
TEST_METHOD(DxcPixDxilDebugInfo_Alignment_ConstInt)
TEST_METHOD(DxcPixDxilDebugInfo_QIOldFieldInterface)
TEST_METHOD(DxcPixDxilDebugInfo_BitFields_Simple)
TEST_METHOD(DxcPixDxilDebugInfo_BitFields_Derived)
TEST_METHOD(DxcPixDxilDebugInfo_BitFields_Bool)
TEST_METHOD(DxcPixDxilDebugInfo_BitFields_Overlap)
TEST_METHOD(DxcPixDxilDebugInfo_Min16SizesAndOffsets_Enabled)
TEST_METHOD(DxcPixDxilDebugInfo_Min16SizesAndOffsets_Disabled)
TEST_METHOD(
DxcPixDxilDebugInfo_VariableScopes_InlinedFunctions_TwiceInlinedFunctions)
TEST_METHOD(
@ -627,12 +637,13 @@ public:
void CompileAndRunAnnotationAndGetDebugPart(
dxc::DxcDllSupport &dllSupport, const char *source,
const wchar_t *profile, IDxcIncludeHandler *includer,
IDxcBlob **ppDebugPart, std::vector<const wchar_t *> extraArgs = {});
IDxcBlob **ppDebugPart,
std::vector<const wchar_t *> extraArgs = {L"-Od"});
void CompileAndRunAnnotationAndLoadDiaSource(
dxc::DxcDllSupport &dllSupport, const char *source,
const wchar_t *profile, IDxcIncludeHandler *includer,
IDiaDataSource **ppDataSource,
std::vector<const wchar_t *> extraArgs = {});
std::vector<const wchar_t *> extraArgs = {L"-Od"});
struct VariableComponentInfo {
std::wstring Name;
@ -643,10 +654,15 @@ public:
const char *hlsl, const wchar_t *profile,
const char *lineAtWhichToExamineVariables,
std::vector<VariableComponentInfo> const &ExpectedVariables);
void RunSizeAndOffsetTestCase(const char *hlsl,
std::array<DWORD, 4> const &memberOffsets,
std::array<DWORD, 4> const &memberSizes,
std::vector<const wchar_t *> extraArgs = {
L"-Od"});
DebuggerInterfaces
CompileAndCreateDxcDebug(const char *hlsl, const wchar_t *profile,
IDxcIncludeHandler *includer = nullptr);
IDxcIncludeHandler *includer = nullptr,
std::vector<const wchar_t *> extraArgs = {L"-Od"});
CComPtr<IDxcPixDxilLiveVariables>
GetLiveVariablesAt(const char *hlsl,
@ -777,11 +793,12 @@ void PixDiaTest::CompileAndRunAnnotationAndGetDebugPart(
DebuggerInterfaces
PixDiaTest::CompileAndCreateDxcDebug(const char *hlsl, const wchar_t *profile,
IDxcIncludeHandler *includer) {
IDxcIncludeHandler *includer,
std::vector<const wchar_t *> extraArgs) {
CComPtr<IDiaDataSource> pDiaDataSource;
CompileAndRunAnnotationAndLoadDiaSource(m_dllSupport, hlsl, profile, includer,
&pDiaDataSource, {L"-Od"});
&pDiaDataSource, extraArgs);
CComPtr<IDiaSession> session;
VERIFY_SUCCEEDED(pDiaDataSource->openSession(&session));
@ -2740,6 +2757,263 @@ void main()
RunSubProgramsCase(hlsl);
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_QIOldFieldInterface) {
const char *hlsl = R"(
struct Struct
{
unsigned int first;
unsigned int second;
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Struct s;
s.second = UAV[0];
UAV[16] = s.second; //STOP_HERE
}
)";
auto debugInfo = CompileAndCreateDxcDebug(hlsl, L"cs_6_5", nullptr).debugInfo;
auto live = GetLiveVariablesAt(hlsl, "STOP_HERE", debugInfo);
CComPtr<IDxcPixVariable> bf;
VERIFY_SUCCEEDED(live->GetVariableByName(L"s", &bf));
CComPtr<IDxcPixType> bfType;
VERIFY_SUCCEEDED(bf->GetType(&bfType));
CComPtr<IDxcPixStructType> bfStructType;
VERIFY_SUCCEEDED(bfType->QueryInterface(IID_PPV_ARGS(&bfStructType)));
CComPtr<IDxcPixStructField> field;
VERIFY_SUCCEEDED(bfStructType->GetFieldByIndex(1, &field));
CComPtr<IDxcPixStructField0> mike;
VERIFY_SUCCEEDED(field->QueryInterface(IID_PPV_ARGS(&mike)));
DWORD secondFieldOffset = 0;
VERIFY_SUCCEEDED(mike->GetOffsetInBits(&secondFieldOffset));
VERIFY_ARE_EQUAL(32, secondFieldOffset);
}
void PixDiaTest::RunSizeAndOffsetTestCase(
const char *hlsl, std::array<DWORD, 4> const &memberOffsets,
std::array<DWORD, 4> const &memberSizes,
std::vector<const wchar_t *> extraArgs) {
if (m_ver.SkipDxilVersion(1, 5))
return;
auto debugInfo =
CompileAndCreateDxcDebug(hlsl, L"cs_6_5", nullptr, extraArgs).debugInfo;
auto live = GetLiveVariablesAt(hlsl, "STOP_HERE", debugInfo);
CComPtr<IDxcPixVariable> bf;
VERIFY_SUCCEEDED(live->GetVariableByName(L"bf", &bf));
CComPtr<IDxcPixType> bfType;
VERIFY_SUCCEEDED(bf->GetType(&bfType));
CComPtr<IDxcPixStructType> bfStructType;
VERIFY_SUCCEEDED(bfType->QueryInterface(IID_PPV_ARGS(&bfStructType)));
const wchar_t *memberNames[] = {L"first", L"second", L"third", L"fourth"};
for (size_t i = 0; i < memberOffsets.size(); ++i) {
CComPtr<IDxcPixStructField> field;
VERIFY_SUCCEEDED(
bfStructType->GetFieldByIndex(static_cast<DWORD>(i), &field));
DWORD offsetInBits = 0;
VERIFY_SUCCEEDED(field->GetOffsetInBits(&offsetInBits));
VERIFY_ARE_EQUAL(memberOffsets[i], offsetInBits);
DWORD sizeInBits = 0;
VERIFY_SUCCEEDED(field->GetFieldSizeInBits(&sizeInBits));
VERIFY_ARE_EQUAL(memberSizes[i], sizeInBits);
}
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_BitFields_Simple) {
const char *hlsl = R"(
struct Bitfields
{
unsigned int first : 17;
unsigned int second : 15; // consume all 32 bits of first dword
unsigned int third : 3; // should be at bit offset 32
unsigned int fourth; // should be at bit offset 64
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Bitfields bf;
bf.first = UAV[0];
bf.second = UAV[1];
bf.third = UAV[2];
bf.fourth = UAV[3];
UAV[16] = bf.first + bf.second + bf.third + bf.fourth; //STOP_HERE
}
)";
RunSizeAndOffsetTestCase(hlsl, {0, 17, 32, 64}, {17, 15, 3, 32});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_BitFields_Derived) {
const char *hlsl = R"(
struct Bitfields
{
uint first : 17;
uint second : 15; // consume all 32 bits of first dword
uint third : 3; // should be at bit offset 32
uint fourth; // should be at bit offset 64
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Bitfields bf;
bf.first = UAV[0];
bf.second = UAV[1];
bf.third = UAV[2];
bf.fourth = UAV[3];
UAV[16] = bf.first + bf.second + bf.third + bf.fourth; //STOP_HERE
}
)";
RunSizeAndOffsetTestCase(hlsl, {0, 17, 32, 64}, {17, 15, 3, 32});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_BitFields_Bool) {
const char *hlsl = R"(
struct Bitfields
{
bool first : 1;
bool second : 1;
bool third : 3; // just to be weird
uint fourth; // should be at bit offset 64
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Bitfields bf;
bf.first = UAV[0];
bf.second = UAV[1];
bf.third = UAV[2];
bf.fourth = UAV[3];
UAV[16] = bf.first + bf.second + bf.third + bf.fourth; //STOP_HERE
}
)";
RunSizeAndOffsetTestCase(hlsl, {0, 1, 2, 32}, {1, 1, 3, 32});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_BitFields_Overlap) {
const char *hlsl = R"(
struct Bitfields
{
unsigned int first : 20;
unsigned int second : 20; // should end up in second DWORD
unsigned int third : 3; // should shader second DWORD
unsigned int fourth; // should be in third DWORD
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Bitfields bf;
bf.first = UAV[0];
bf.second = UAV[1];
bf.third = UAV[2];
bf.fourth = UAV[3];
UAV[16] = bf.first + bf.second + bf.third + bf.fourth; //STOP_HERE
}
)";
RunSizeAndOffsetTestCase(hlsl, {0, 32, 52, 64}, {20, 20, 3, 32});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_Alignment_ConstInt) {
if (m_ver.SkipDxilVersion(1, 5))
return;
const char *hlsl = R"(
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
const uint c = UAV[0];
UAV[16] = c;
}
)";
CComPtr<IDiaDataSource> pDiaDataSource;
CompileAndRunAnnotationAndLoadDiaSource(m_dllSupport, hlsl, L"cs_6_5",
nullptr, &pDiaDataSource, {L"-Od"});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_Min16SizesAndOffsets_Enabled) {
if (m_ver.SkipDxilVersion(1, 5))
return;
const char *hlsl = R"(
struct Mins
{
min16uint first;
min16int second;
min12int third;
min16float fourth;
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Mins bf;
bf.first = UAV[0];
bf.second = UAV[1];
bf.third = UAV[2];
bf.fourth = UAV[3];
UAV[16] = bf.first + bf.second + bf.third + bf.fourth; //STOP_HERE
}
)";
RunSizeAndOffsetTestCase(hlsl, {0, 16, 32, 48}, {16, 16, 16, 16},
{L"-Od", L"-enable-16bit-types"});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_Min16SizesAndOffsets_Disabled) {
if (m_ver.SkipDxilVersion(1, 5))
return;
const char *hlsl = R"(
struct Mins
{
min16uint first;
min16int second;
min12int third;
min16float fourth;
};
RWStructuredBuffer<int> UAV: register(u0);
[numthreads(1, 1, 1)]
void main()
{
Mins bf;
bf.first = UAV[0];
bf.second = UAV[1];
bf.third = UAV[2];
bf.fourth = UAV[3];
UAV[16] = bf.first + bf.second + bf.third + bf.fourth; //STOP_HERE
}
)";
RunSizeAndOffsetTestCase(hlsl, {0, 32, 64, 96}, {16, 16, 16, 16}, {L"-Od"});
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_SubProgramsInNamespaces) {
if (m_ver.SkipDxilVersion(1, 2))
return;
@ -3006,7 +3280,8 @@ void ClosestHitShader3(inout RayPayload payload, in BuiltInTriangleIntersectionA
dxilDebugger, instructionOffset, L"InlinedFunction");
DWORD callsite0 = GetRegisterNumberForVariable(
dxilDebugger, instructionOffset, L"ret", L"x");
// advance until we're out of InlinedFunction before we call it a second time
// advance until we're out of InlinedFunction before we call it a second
// time
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"ClosestHitShader3");
instructionOffset = AdvanceUntilFunctionEntered(