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:
Родитель
6c0d4b8d60
Коммит
2c19e8121b
|
@ -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
|
Загрузка…
Ссылка в новой задаче