Skip to content

Commit 7e92bbc

Browse files
authored
PIX value-to-declare pass: subprogram type members (#3140)
1 parent c3fa785 commit 7e92bbc

5 files changed

Lines changed: 203 additions & 60 deletions

File tree

lib/DxilDia/DxcPixLiveVariables_FragmentIterator.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ unsigned SizeIfBaseType(llvm::DIType const* diType)
232232

233233
void CompositeTypeFragmentIterator::DetermineStructMemberSizesAndOffsets(llvm::DIType const*diType, uint64_t BaseOffset)
234234
{
235+
assert(diType->getTag() != llvm::dwarf::DW_TAG_subroutine_type);
235236
if (diType->getTag() == llvm::dwarf::DW_TAG_member)
236237
{
237238
BaseOffset += diType->getOffsetInBits();
@@ -340,8 +341,10 @@ dxil_debug_info::CreateMemberIterator(llvm::DbgDeclareInst *DbgDeclare,
340341
} else {
341342
llvm::DICompositeType *CT = llvm::dyn_cast<llvm::DICompositeType>(
342343
DbgDeclare->getVariable()->getType());
343-
if (CT != nullptr && Expression->getNumElements() == 0) {
344-
Iter.reset(new CompositeTypeFragmentIterator(CT));
344+
if (CT != nullptr && Expression->getNumElements() == 0 ) {
345+
if (CT->getTag() != llvm::dwarf::DW_TAG_subroutine_type) {
346+
Iter.reset(new CompositeTypeFragmentIterator(CT));
347+
}
345348
} else {
346349
Iter.reset(
347350
new DILayoutFragmentIterator(DataLayout, Alloca, Expression));

lib/DxilDia/DxcPixTypes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ class DxcPixStructType : public IDxcPixStructType
218218
#ifndef NDEBUG
219219
for (auto *Node : m_pStruct->getElements())
220220
{
221-
assert(llvm::isa<llvm::DIDerivedType>(Node));
221+
assert(llvm::isa<llvm::DIDerivedType>(Node) || llvm::isa<llvm::DISubprogram>(Node));
222222
}
223223
#endif // !NDEBUG
224224
}

lib/DxilDia/DxilDiaDataSource.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ STDMETHODIMP dxil_dia::DataSource::openSession(_COM_Outptr_ IDiaSession **ppSess
156156
*ppSession = nullptr;
157157
if (m_module.get() == nullptr)
158158
return E_FAIL;
159+
160+
::llvm::sys::fs::MSFileSystem *msfPtr;
161+
IFT(CreateMSFileSystemForDisk(&msfPtr));
162+
std::unique_ptr<::llvm::sys::fs::MSFileSystem> msf(msfPtr);
163+
164+
::llvm::sys::fs::AutoPerThreadSystem pts(msf.get());
165+
IFTLLVM(pts.error_code());
166+
159167
CComPtr<Session> pSession = Session::Alloc(DxcGetThreadMallocNoRef());
160168
IFROOM(pSession.p);
161169
pSession->Init(m_context, m_module, m_finder);

lib/DxilPIXPasses/DxilDbgValueToDbgDeclare.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,9 @@ void VariableRegisters::PopulateAllocaMap(
483483
PopulateAllocaMap(
484484
DerivedTy->getBaseType().resolve(EmptyMap));
485485
return;
486+
case llvm::dwarf::DW_TAG_subroutine_type:
487+
//ignore member functions.
488+
return;
486489
}
487490
}
488491
else if (auto *CompositeTy = llvm::dyn_cast<llvm::DICompositeType>(Ty))
@@ -647,7 +650,6 @@ static bool SortMembers(
647650
std::map<OffsetInBits, llvm::DIDerivedType*> *SortedMembers
648651
)
649652
{
650-
const llvm::DITypeIdentifierMap EmptyMap;
651653
for (auto *Element : Ty->getElements())
652654
{
653655
switch (Element->getTag())
@@ -664,7 +666,15 @@ static bool SortMembers(
664666
}
665667
break;
666668
}
667-
// FALLTHROUGH
669+
assert(!"member is not a Member");
670+
return false;
671+
}
672+
case llvm::dwarf::DW_TAG_subprogram: {
673+
if (auto *SubProgram = llvm::dyn_cast<llvm::DISubprogram>(Element)) {
674+
break;
675+
}
676+
assert(!"DISubprogram not understood");
677+
return false;
668678
}
669679
default:
670680
assert(!"Unhandled field type in DIStructType");

tools/clang/unittests/HLSL/PixTest.cpp

Lines changed: 177 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ class PixTest {
204204
TEST_METHOD(PixStructAnnotation_SequentialFloatN)
205205
TEST_METHOD(PixStructAnnotation_EmbeddedFloatN)
206206
TEST_METHOD(PixStructAnnotation_Matrix)
207+
TEST_METHOD(PixStructAnnotation_MemberFunction)
207208
TEST_METHOD(PixStructAnnotation_BigMess)
208209

209210
dxc::DxcDllSupport m_dllSupport;
@@ -1767,61 +1768,65 @@ PixTest::TestableResults PixTest::TestStructAnnotationCase(const char* hlsl)
17671768
{
17681769
llvm::Value* Address = dbgDeclare->getAddress();
17691770
auto* AddressAsAlloca = llvm::dyn_cast<llvm::AllocaInst>(Address);
1770-
auto* Expression = dbgDeclare->getExpression();
1771-
1772-
std::unique_ptr<dxil_debug_info::MemberIterator> iterator = dxil_debug_info::CreateMemberIterator(
1773-
dbgDeclare,
1774-
moduleEtc.GetDxilModule().GetModule()->getDataLayout(),
1775-
AddressAsAlloca,
1776-
Expression);
1777-
1778-
unsigned int startingBit = 0;
1779-
unsigned int coveredBits = 0;
1780-
unsigned int memberIndex = 0;
1781-
while (iterator->Next(&memberIndex))
1771+
if (AddressAsAlloca != nullptr)
17821772
{
1783-
if (memberIndex == 0)
1784-
{
1785-
startingBit = iterator->OffsetInBits(memberIndex);
1786-
coveredBits = iterator->SizeInBits(memberIndex);
1787-
}
1788-
else
1789-
{
1790-
coveredBits = std::max<unsigned int>( coveredBits, iterator->OffsetInBits(memberIndex) + iterator->SizeInBits(memberIndex));
1791-
}
1792-
}
1793-
1794-
// memberIndex is now the count of members in this aggregate type
1795-
ret.OffsetAndSizes.push_back({ memberIndex, startingBit, coveredBits });
1796-
1797-
// Use this independent count of number of struct members to test the
1798-
// function that operates on the alloca type:
1799-
llvm::Type *pAllocaTy = AddressAsAlloca->getType()->getElementType();
1800-
if (auto *AT = llvm::dyn_cast<llvm::ArrayType>(pAllocaTy))
1801-
{
1802-
// This is the case where a struct is passed to a function, and in
1803-
// these tests there should be only one struct behind the pointer.
1804-
VERIFY_ARE_EQUAL(AT->getNumElements(), 1);
1805-
pAllocaTy = AT->getArrayElementType();
1806-
}
1807-
1808-
if (auto* ST = llvm::dyn_cast<llvm::StructType>(pAllocaTy))
1809-
{
1810-
uint32_t countOfMembers = CountStructMembers(ST);
1811-
// memberIndex might be greater, because the fragment iterator also includes contained derived types as
1812-
// fragments, in addition to the members of that contained derived types. CountStructMembers only counts
1813-
// the leaf-node types.
1814-
VERIFY_ARE_EQUAL(countOfMembers, memberIndex);
1815-
}
1816-
else if (pAllocaTy->isFloatingPointTy() || pAllocaTy->isIntegerTy())
1817-
{
1818-
// If there's only one member in the struct in the pass-to-function (by pointer)
1819-
// case, then the underlying type will have been reduced to the contained type.
1820-
VERIFY_ARE_EQUAL(1, memberIndex);
1821-
}
1822-
else
1823-
{
1824-
VERIFY_IS_TRUE(false);
1773+
auto* Expression = dbgDeclare->getExpression();
1774+
1775+
std::unique_ptr<dxil_debug_info::MemberIterator> iterator = dxil_debug_info::CreateMemberIterator(
1776+
dbgDeclare,
1777+
moduleEtc.GetDxilModule().GetModule()->getDataLayout(),
1778+
AddressAsAlloca,
1779+
Expression);
1780+
1781+
unsigned int startingBit = 0;
1782+
unsigned int coveredBits = 0;
1783+
unsigned int memberIndex = 0;
1784+
unsigned int memberCount = 0;
1785+
while (iterator->Next(&memberIndex))
1786+
{
1787+
memberCount++;
1788+
if (memberIndex == 0)
1789+
{
1790+
startingBit = iterator->OffsetInBits(memberIndex);
1791+
coveredBits = iterator->SizeInBits(memberIndex);
1792+
}
1793+
else
1794+
{
1795+
coveredBits = std::max<unsigned int>(coveredBits, iterator->OffsetInBits(memberIndex) + iterator->SizeInBits(memberIndex));
1796+
}
1797+
}
1798+
1799+
ret.OffsetAndSizes.push_back({ memberCount, startingBit, coveredBits });
1800+
1801+
// Use this independent count of number of struct members to test the
1802+
// function that operates on the alloca type:
1803+
llvm::Type* pAllocaTy = AddressAsAlloca->getType()->getElementType();
1804+
if (auto* AT = llvm::dyn_cast<llvm::ArrayType>(pAllocaTy))
1805+
{
1806+
// This is the case where a struct is passed to a function, and in
1807+
// these tests there should be only one struct behind the pointer.
1808+
VERIFY_ARE_EQUAL(AT->getNumElements(), 1);
1809+
pAllocaTy = AT->getArrayElementType();
1810+
}
1811+
1812+
if (auto* ST = llvm::dyn_cast<llvm::StructType>(pAllocaTy))
1813+
{
1814+
uint32_t countOfMembers = CountStructMembers(ST);
1815+
// memberIndex might be greater, because the fragment iterator also includes contained derived types as
1816+
// fragments, in addition to the members of that contained derived types. CountStructMembers only counts
1817+
// the leaf-node types.
1818+
VERIFY_ARE_EQUAL(countOfMembers, memberCount);
1819+
}
1820+
else if (pAllocaTy->isFloatingPointTy() || pAllocaTy->isIntegerTy())
1821+
{
1822+
// If there's only one member in the struct in the pass-to-function (by pointer)
1823+
// case, then the underlying type will have been reduced to the contained type.
1824+
VERIFY_ARE_EQUAL(1, memberCount);
1825+
}
1826+
else
1827+
{
1828+
VERIFY_IS_TRUE(false);
1829+
}
18251830
}
18261831
}
18271832
}
@@ -1892,7 +1897,7 @@ PixTest::TestableResults PixTest::TestStructAnnotationCase(const char* hlsl)
18921897

18931898
void PixTest::ValidateAllocaWrite(std::vector<AllocaWrite> const &allocaWrites,
18941899
size_t index, const char *name) {
1895-
VERIFY_ARE_EQUAL(index, allocaWrites[index].index);
1900+
VERIFY_ARE_EQUAL(index, allocaWrites[index].regBase + allocaWrites[index].index);
18961901
#if DBG
18971902
// Compilation may add a prefix to the struct member name:
18981903
VERIFY_IS_TRUE(0 == strncmp(name, allocaWrites[index].memberName.c_str(), strlen(name)));
@@ -2295,6 +2300,123 @@ void main()
22952300

22962301
}
22972302

2303+
TEST_F(PixTest, PixStructAnnotation_MemberFunction) {
2304+
const char *hlsl = R"(
2305+
2306+
RWStructuredBuffer<float> floatRWUAV: register(u0);
2307+
2308+
struct smallPayload
2309+
{
2310+
int i;
2311+
};
2312+
2313+
float2 signNotZero(float2 v)
2314+
{
2315+
return (v > 0.0f ? float(1).xx : float(-1).xx);
2316+
}
2317+
2318+
float2 unpackUnorm2(uint packed)
2319+
{
2320+
return (1.0 / 65535.0) * float2((packed >> 16) & 0xffff, packed & 0xffff);
2321+
}
2322+
2323+
float3 unpackOctahedralSnorm(float2 e)
2324+
{
2325+
float3 v = float3(e.xy, 1.0f - abs(e.x) - abs(e.y));
2326+
if (v.z < 0.0f) v.xy = (1.0f - abs(v.yx)) * signNotZero(v.xy);
2327+
return normalize(v);
2328+
}
2329+
2330+
float3 unpackOctahedralUnorm(float2 e)
2331+
{
2332+
return unpackOctahedralSnorm(e * 2.0f - 1.0f);
2333+
}
2334+
2335+
float2 unpackHalf2(uint packed)
2336+
{
2337+
return float2(f16tof32(packed >> 16), f16tof32(packed & 0xffff));
2338+
}
2339+
2340+
struct Gbuffer
2341+
{
2342+
float3 worldNormal;
2343+
float3 objectNormal; //offset:12
2344+
2345+
float linearZ; //24
2346+
float prevLinearZ; //28
2347+
2348+
2349+
float fwidthLinearZ; //32
2350+
float fwidthObjectNormal; //36
2351+
2352+
2353+
uint materialType; //40
2354+
uint2 materialParams0; //44
2355+
uint4 materialParams1; //52 <--------- this is the variable that's being covered twice (52*8 = 416 416)
2356+
2357+
uint instanceId; //68 <------- and there's one dword left over, as expected
2358+
2359+
2360+
void load(int2 pixelPos, Texture2DArray<uint4> gbTex)
2361+
{
2362+
uint4 data0 = gbTex.Load(int4(pixelPos, 0, 0));
2363+
uint4 data1 = gbTex.Load(int4(pixelPos, 1, 0));
2364+
uint4 data2 = gbTex.Load(int4(pixelPos, 2, 0));
2365+
2366+
2367+
worldNormal = unpackOctahedralUnorm(unpackUnorm2(data0.x));
2368+
linearZ = f16tof32((data0.y >> 8) & 0xffff);
2369+
materialType = (data0.y & 0xff);
2370+
materialParams0 = data0.zw;
2371+
2372+
2373+
materialParams1 = data1.xyzw;
2374+
2375+
2376+
instanceId = data2.x;
2377+
prevLinearZ = asfloat(data2.y);
2378+
objectNormal = unpackOctahedralUnorm(unpackUnorm2(data2.z));
2379+
float2 fwidth = unpackHalf2(data2.w);
2380+
fwidthLinearZ = fwidth.x;
2381+
fwidthObjectNormal = fwidth.y;
2382+
}
2383+
};
2384+
2385+
Gbuffer loadGbuffer(int2 pixelPos, Texture2DArray<uint4> gbTex)
2386+
{
2387+
Gbuffer output;
2388+
output.load(pixelPos, gbTex);
2389+
return output;
2390+
}
2391+
2392+
Texture2DArray<uint4> g_gbuffer : register(t0, space0);
2393+
2394+
[numthreads(1, 1, 1)]
2395+
void main()
2396+
{
2397+
const Gbuffer gbuffer = loadGbuffer(int2(0,0), g_gbuffer);
2398+
smallPayload p;
2399+
p.i = gbuffer.materialParams1.x + gbuffer.materialParams1.y + gbuffer.materialParams1.z + gbuffer.materialParams1.w;
2400+
DispatchMesh(1, 1, 1, p);
2401+
}
2402+
2403+
2404+
)";
2405+
2406+
auto Testables = TestStructAnnotationCase(hlsl);
2407+
// Can't test member iterator until dbg.declare instructions are emitted when structs
2408+
// contain pointers-to-pointers
2409+
2410+
// Can't validate # of writes: rel and dbg are different
2411+
//VERIFY_ARE_EQUAL(43, Testables.AllocaWrites.size());
2412+
2413+
// Can't test individual writes until struct member names are returned:
2414+
//for (int i = 0; i < 51; ++i)
2415+
//{
2416+
// ValidateAllocaWrite(Testables.AllocaWrites, i, "");
2417+
//}
2418+
}
2419+
22982420
TEST_F(PixTest, PixStructAnnotation_BigMess) {
22992421
if (m_ver.SkipDxilVersion(1, 5)) return;
23002422

0 commit comments

Comments
 (0)