WinPix: variables in subroutines called from multiple functions (#5954)

From the comment in one of the changed files:

// If a function is inlined, then the scope of variables within that
function
// will be that scope. Since many such callers may inline the function,
we
// actually need a "scope" that's unique to each "instance" of that
function.
// The caller's scope would be unique, but would have the undesirable
// side-effect of smushing all of the function's variables together with
the
// caller's variables, at least from the point of view of PIX. The pair
of
// scopes (the function's and the caller's) is both unique to that
particular
// inlining, and distinct from the caller's scope by itself.
This commit is contained in:
Jeff Noyle 2023-11-14 16:34:15 -08:00 коммит произвёл GitHub
Родитель 6c0d4b8d60
Коммит 2c19e8121b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 228 добавлений и 9 удалений

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

@ -29,6 +29,58 @@
#include <unordered_map>
// If a function is inlined, then the scope of variables within that function
// will be that scope. Since many such callers may inline the function, we
// actually need a "scope" that's unique to each "instance" of that function.
// The caller's scope would be unique, but would have the undesirable
// side-effect of smushing all of the function's variables together with the
// caller's variables, at least from the point of view of PIX. The pair of
// scopes (the function's and the caller's) is both unique to that particular
// inlining, and distinct from the caller's scope by itself.
class UniqueScopeForInlinedFunctions {
llvm::DIScope *FunctionScope;
llvm::DIScope *InlinedAtScope;
public:
bool Valid() const { return FunctionScope != nullptr; }
void AscendScopeHierarchy() {
// DINamespace has a getScope member (that hides DIScope's)
// that returns a DIScope directly, but if that namespace
// is at file-level scope, it will return nullptr.
if (auto Namespace = llvm::dyn_cast<llvm::DINamespace>(FunctionScope)) {
if (auto *ContainingScope = Namespace->getScope()) {
FunctionScope = ContainingScope;
return;
}
}
const llvm::DITypeIdentifierMap EmptyMap;
FunctionScope = FunctionScope->getScope().resolve(EmptyMap);
}
static UniqueScopeForInlinedFunctions Create(llvm::DebugLoc const &DbgLoc,
llvm::DIScope *FunctionScope) {
UniqueScopeForInlinedFunctions ret;
ret.FunctionScope = FunctionScope;
ret.InlinedAtScope =
llvm::dyn_cast_or_null<llvm::DIScope>(DbgLoc.getInlinedAtScope());
return ret;
}
bool operator==(const UniqueScopeForInlinedFunctions &o) const {
return FunctionScope == o.FunctionScope &&
InlinedAtScope == o.InlinedAtScope;
}
std::size_t operator()(const UniqueScopeForInlinedFunctions &k) const {
using std::hash;
using std::size_t;
const uint64_t f = reinterpret_cast<uint64_t>(k.FunctionScope);
const uint64_t s = reinterpret_cast<uint64_t>(k.InlinedAtScope);
std::hash<uint64_t> h;
return (h(f) ^ h(s));
}
};
// ValidateDbgDeclare ensures that all of the bits in
// [FragmentSizeInBits, FragmentOffsetInBits) are currently
// not assigned to a dxil alloca register -- i.e., it
@ -61,7 +113,9 @@ struct dxil_debug_info::LiveVariables::Impl {
using VariableInfoMap =
std::unordered_map<llvm::DIVariable *, std::unique_ptr<VariableInfo>>;
using LiveVarsMap = std::unordered_map<llvm::DIScope *, VariableInfoMap>;
using LiveVarsMap =
std::unordered_map<UniqueScopeForInlinedFunctions, VariableInfoMap,
UniqueScopeForInlinedFunctions>;
IMalloc *m_pMalloc;
DxcPixDxilDebugInfo *m_pDxilDebugInfo;
@ -115,8 +169,9 @@ void dxil_debug_info::LiveVariables::Impl::Init_DbgDeclare(
return;
}
auto *S = Variable->getScope();
if (S == nullptr) {
auto S = UniqueScopeForInlinedFunctions::Create(DbgDeclare->getDebugLoc(),
Variable->getScope());
if (!S.Valid()) {
return;
}
@ -204,13 +259,12 @@ HRESULT dxil_debug_info::LiveVariables::GetLiveVariablesAtInstruction(
return E_FAIL;
}
llvm::DIScope *S = DL->getScope();
if (S == nullptr) {
auto S = UniqueScopeForInlinedFunctions::Create(DL, DL->getScope());
if (!S.Valid()) {
return E_FAIL;
}
const llvm::DITypeIdentifierMap EmptyMap;
while (S != nullptr) {
while (S.Valid()) {
auto it = m_pImpl->m_LiveVarsDbgDeclare.find(S);
if (it != m_pImpl->m_LiveVarsDbgDeclare.end()) {
for (const auto &VarAndInfo : it->second) {
@ -232,7 +286,7 @@ HRESULT dxil_debug_info::LiveVariables::GetLiveVariablesAtInstruction(
LiveVars.emplace_back(VarAndInfo.second.get());
}
}
S = S->getScope().resolve(EmptyMap);
S.AscendScopeHierarchy();
}
for (const auto &VarAndInfo : m_pImpl->m_LiveGlobalVarsDbgDeclare) {
if (!LiveVarsName.insert(VarAndInfo.first->getName()).second) {

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

@ -20,6 +20,7 @@
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "..\DxilPIXPasses\PixPassHelpers.h"
#include "DxilDia.h"
#include "DxilDiaEnumTables.h"
#include "DxilDiaTable.h"
@ -63,7 +64,9 @@ void dxil_dia::Session::Init(std::shared_ptr<llvm::LLVMContext> context,
// Build up a linear list of instructions. The index will be used as the
// RVA.
for (llvm::Function &fn : m_module->functions()) {
std::vector<llvm::Function *> allInstrumentableFunctions =
PIXPassHelpers::GetAllInstrumentableFunctions(*m_dxilModule.get());
for (auto fn : allInstrumentableFunctions) {
for (llvm::inst_iterator it = inst_begin(fn), end = inst_end(fn); it != end;
++it) {
llvm::Instruction &i = *it;

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

@ -177,6 +177,7 @@ public:
TEST_METHOD(DxcPixDxilDebugInfo_UnnamedField)
TEST_METHOD(DxcPixDxilDebugInfo_SubProgramsInNamespaces)
TEST_METHOD(DxcPixDxilDebugInfo_SubPrograms)
TEST_METHOD(DxcPixDxilDebugInfo_TwiceInlinedFunctions)
dxc::DxcDllSupport m_dllSupport;
VersionSupportInfo m_ver;
@ -2758,4 +2759,165 @@ void main()
RunSubProgramsCase(hlsl);
}
static DWORD AdvanceUntilFunctionEntered(IDxcPixDxilDebugInfo *dxilDebugger,
DWORD instructionOffset,
wchar_t const *fnName) {
for (;;) {
CComBSTR FunctioName;
if (FAILED(
dxilDebugger->GetFunctionName(instructionOffset, &FunctioName))) {
VERIFY_FAIL(L"Didn't find function");
return -1;
}
if (FunctioName == fnName)
break;
instructionOffset++;
}
return instructionOffset;
}
static DWORD GetRegisterNumberForVariable(IDxcPixDxilDebugInfo *dxilDebugger,
DWORD instructionOffset,
wchar_t const *variableName,
wchar_t const *memberName) {
CComPtr<IDxcPixDxilLiveVariables> DxcPixDxilLiveVariables;
if (SUCCEEDED(dxilDebugger->GetLiveVariablesAt(instructionOffset,
&DxcPixDxilLiveVariables))) {
DWORD count = 42;
VERIFY_SUCCEEDED(DxcPixDxilLiveVariables->GetCount(&count));
for (DWORD i = 0; i < count; ++i) {
CComPtr<IDxcPixVariable> DxcPixVariable;
VERIFY_SUCCEEDED(
DxcPixDxilLiveVariables->GetVariableByIndex(i, &DxcPixVariable));
CComBSTR Name;
VERIFY_SUCCEEDED(DxcPixVariable->GetName(&Name));
if (Name == variableName) {
CComPtr<IDxcPixDxilStorage> DxcPixDxilStorage;
VERIFY_SUCCEEDED(DxcPixVariable->GetStorage(&DxcPixDxilStorage));
CComPtr<IDxcPixDxilStorage> DxcPixDxilMemberStorage;
VERIFY_SUCCEEDED(DxcPixDxilStorage->AccessField(
memberName, &DxcPixDxilMemberStorage));
DWORD RegisterNumber = 42;
VERIFY_SUCCEEDED(
DxcPixDxilMemberStorage->GetRegisterNumber(&RegisterNumber));
return RegisterNumber;
}
}
}
VERIFY_FAIL(L"Couldn't find register number");
return -1;
}
TEST_F(PixDiaTest, DxcPixDxilDebugInfo_TwiceInlinedFunctions) {
if (m_ver.SkipDxilVersion(1, 2))
return;
const char *hlsl = R"(
struct RayPayload
{
float4 color;
};
RWStructuredBuffer<float4> floatRWUAV: register(u0);
namespace StressScopesABit
{
#include "included.h"
}
namespace StressScopesMore
{
float4 InlinedFunction(in BuiltInTriangleIntersectionAttributes attr, int offset)
{
float4 ret = floatRWUAV.Load(offset + attr.barycentrics.x + 42);
float4 color2 = StressScopesABit::StressScopesEvenMore::DeeperInlinedFunction(attr, offset) + ret;
float4 color3 = StressScopesABit::StressScopesEvenMore::DeeperInlinedFunction(attr, offset+1);
return color2 + color3;
}
}
[shader("closesthit")]
void ClosestHitShader0(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
payload.color = StressScopesMore::InlinedFunction(attr, 0);
}
[shader("closesthit")]
void ClosestHitShader1(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
payload.color = StressScopesMore::InlinedFunction(attr, 1);
}
[shader("closesthit")]
void ClosestHitShader2(inout RayPayload payload, in BuiltInTriangleIntersectionAttributes attr)
{
float4 generateSomeLocalInstrucitons = floatRWUAV.Load(0);
float4 c0 = StressScopesMore::InlinedFunction(attr, 0);
float4 c1 = StressScopesABit::StressScopesEvenMore::DeeperInlinedFunction(attr, 42);
payload.color = c0 + c1 + generateSomeLocalInstrucitons;
}
)";
CComPtr<DxcIncludeHandlerForInjectedSourcesForPix> pIncludeHandler =
new DxcIncludeHandlerForInjectedSourcesForPix(this, {{L"included.h",
R"(
namespace StressScopesEvenMore
{
float4 DeeperInlinedFunction(in BuiltInTriangleIntersectionAttributes attr, int offset)
{
float4 ret = float4(0,0,0,0);
for(int i =0; i < offset; ++i)
{
float4 color0 = floatRWUAV.Load(offset + attr.barycentrics.x);
float4 color1 = floatRWUAV.Load(offset + attr.barycentrics.y);
ret += color0 + color1;
}
return ret;
}
}
)"}});
auto dxilDebugger =
CompileAndCreateDxcDebug(hlsl, L"lib_6_6", pIncludeHandler);
DWORD instructionOffset =
AdvanceUntilFunctionEntered(dxilDebugger, 0, L"ClosestHitShader0");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"DeeperInlinedFunction");
DWORD RegisterNumber0 = GetRegisterNumberForVariable(
dxilDebugger, instructionOffset, L"ret", L"x");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"InlinedFunction");
DWORD RegisterNumber2 = GetRegisterNumberForVariable(
dxilDebugger, instructionOffset, L"color2", L"x");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"ClosestHitShader1");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"DeeperInlinedFunction");
DWORD RegisterNumber1 = GetRegisterNumberForVariable(
dxilDebugger, instructionOffset, L"ret", L"x");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"InlinedFunction");
DWORD RegisterNumber3 = GetRegisterNumberForVariable(
dxilDebugger, instructionOffset, L"color2", L"x");
VERIFY_ARE_NOT_EQUAL(RegisterNumber0, RegisterNumber1);
VERIFY_ARE_NOT_EQUAL(RegisterNumber2, RegisterNumber3);
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"ClosestHitShader2");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"InlinedFunction");
DWORD ColorRegisterNumberWhenCalledFromOuterForInlined =
GetRegisterNumberForVariable(dxilDebugger, instructionOffset, L"ret",
L"x");
instructionOffset = AdvanceUntilFunctionEntered(
dxilDebugger, instructionOffset, L"DeeperInlinedFunction");
DWORD ColorRegisterNumberWhenCalledFromOuterForDeeper =
GetRegisterNumberForVariable(dxilDebugger, instructionOffset, L"ret",
L"x");
VERIFY_ARE_NOT_EQUAL(ColorRegisterNumberWhenCalledFromOuterForInlined,
ColorRegisterNumberWhenCalledFromOuterForDeeper);
}
#endif