浏览代码

PIX: Synthesize dbg.value for global-backed arrays embedded in non-const global statics (#5223)

In some cases, arrays embedded within an HLSL global static will be backed by an llvm global variable, rather than an alloca or a set of discrete values.
In those cases, there will be no dbg.value or dbg.declare statements that will allow PIX to associate stores to those globals with the HLSL variable.
But it's possible to stitch the storage to the HLSL variable and emit the dbg.value (and "fake" alloca/stores that PIX's debug instrumentation uses for tracking changes to values during execution).
Prior to this work, PIX had no visibility into non-const global statics at all. Now, PIX can see most things, inlcluding the above-described class of embedded arrays. However, more work remains:
-dynamic indexing of the arrays is not considered
-arrays that have been reduced to 1-dimensional equivalents are not covered
The first two will be tackled next. The last will probably be left until and unless requested by a PIX customer.

In brief, this change walks the global variables and compares types, offsets and sizes to determine where arrays might be embedded within other global variables. Maps are constructed to go from a global member's name to the actual member's offset/size, and from the HLSL DIGlobalVariable to the per-function DILocalVariable "alias" or "mirror" of that global (which are denoted by the "global." name prefix) . This allows the "fake" alloca/store and dbg.declares to be emitted for any store to a GEP operand that references those embedded arrays. (If no other members of the HLSL global are accessed, then there will be no DILocalVariable for the new dbg.declares to be associated with, so a new DILocalVariable must be emitted.)
Jeff Noyle 2 年之前
父节点
当前提交
59cef25eb9

+ 26 - 11
lib/DxilDia/DxcPixLiveVariables.cpp

@@ -73,6 +73,7 @@ struct dxil_debug_info::LiveVariables::Impl
   DxcPixDxilDebugInfo *m_pDxilDebugInfo;
   DxcPixDxilDebugInfo *m_pDxilDebugInfo;
   llvm::Module *m_pModule;
   llvm::Module *m_pModule;
   LiveVarsMap m_LiveVarsDbgDeclare;
   LiveVarsMap m_LiveVarsDbgDeclare;
+  VariableInfoMap m_LiveGlobalVarsDbgDeclare;
 
 
   void Init(
   void Init(
       IMalloc *pMalloc,
       IMalloc *pMalloc,
@@ -87,6 +88,9 @@ struct dxil_debug_info::LiveVariables::Impl
       llvm::Value *Address,
       llvm::Value *Address,
       unsigned FragmentIndex,
       unsigned FragmentIndex,
       unsigned FragmentOffsetInBits);
       unsigned FragmentOffsetInBits);
+
+    bool IsVariableLive(const VariableInfoMap::value_type &VarAndInfo,
+                      const llvm::DIScope* S, const llvm::DebugLoc &DL);
 };
 };
 
 
 void dxil_debug_info::LiveVariables::Impl::Init(
 void dxil_debug_info::LiveVariables::Impl::Init(
@@ -148,6 +152,13 @@ void dxil_debug_info::LiveVariables::Impl::Init_DbgDeclare(
     return;
     return;
   }
   }
 
 
+  VariableInfoMap *LiveVarInfoMap;
+  if (Variable->getName().startswith("global.")) {
+    LiveVarInfoMap = &m_LiveGlobalVarsDbgDeclare;
+  } else {
+    LiveVarInfoMap = &m_LiveVarsDbgDeclare[S];
+  }
+ 
 
 
   unsigned FragmentIndex;
   unsigned FragmentIndex;
   while (Iter->Next(&FragmentIndex))
   while (Iter->Next(&FragmentIndex))
@@ -158,7 +169,7 @@ void dxil_debug_info::LiveVariables::Impl::Init_DbgDeclare(
         Iter->OffsetInBits(FragmentIndex);
         Iter->OffsetInBits(FragmentIndex);
 
 
     VariableInfo* VarInfo = AssignValueToOffset(
     VariableInfo* VarInfo = AssignValueToOffset(
-        &m_LiveVarsDbgDeclare[S],
+        LiveVarInfoMap,
         Variable,
         Variable,
         Address,
         Address,
         FragmentIndex,
         FragmentIndex,
@@ -176,7 +187,8 @@ void dxil_debug_info::LiveVariables::Impl::Init_DbgDeclare(
   }
   }
 }
 }
 
 
-dxil_debug_info::VariableInfo* dxil_debug_info::LiveVariables::Impl::AssignValueToOffset(
+dxil_debug_info::VariableInfo *
+dxil_debug_info::LiveVariables::Impl::AssignValueToOffset(
     VariableInfoMap *VarInfoMap,
     VariableInfoMap *VarInfoMap,
     llvm::DIVariable *Variable,
     llvm::DIVariable *Variable,
     llvm::Value *Address,
     llvm::Value *Address,
@@ -251,29 +263,32 @@ HRESULT dxil_debug_info::LiveVariables::GetLiveVariablesAtInstruction(
       {
       {
         auto *Var = VarAndInfo.first;
         auto *Var = VarAndInfo.first;
         llvm::StringRef VarName = Var->getName();
         llvm::StringRef VarName = Var->getName();
-        if (Var->getLine() > DL.getLine())
-        {
+        if (Var->getLine() > DL.getLine()) {
           // Defined later in the HLSL source.
           // Defined later in the HLSL source.
           continue;
           continue;
         }
         }
-        if (VarName.empty())
-        {
+        if (VarName.empty()) {
           // No name?...
           // No name?...
           continue;
           continue;
         }
         }
-        if (!LiveVarsName.insert(VarName).second)
-        {
+        if (!LiveVarsName.insert(VarAndInfo.first->getName()).second) {
           // There's a variable with the same name; use the
           // There's a variable with the same name; use the
           // previous one instead.
           // previous one instead.
-          continue;
+          return false;
         }
         }
-
         LiveVars.emplace_back(VarAndInfo.second.get());
         LiveVars.emplace_back(VarAndInfo.second.get());
       }
       }
     }
     }
     S = S->getScope().resolve(EmptyMap);
     S = S->getScope().resolve(EmptyMap);
   }
   }
-
+  for (const auto &VarAndInfo : m_pImpl->m_LiveGlobalVarsDbgDeclare) {
+    if (!LiveVarsName.insert(VarAndInfo.first->getName()).second) {
+      // There shouldn't ever be a global variable with the same name,
+      // but it doesn't hurt to check
+      return false;
+    }
+    LiveVars.emplace_back(VarAndInfo.second.get());
+  }
   return CreateDxilLiveVariables(
   return CreateDxilLiveVariables(
       m_pImpl->m_pDxilDebugInfo,
       m_pImpl->m_pDxilDebugInfo,
       std::move(LiveVars),
       std::move(LiveVars),

+ 4 - 1
lib/DxilDia/DxcPixTypes.cpp

@@ -155,7 +155,10 @@ STDMETHODIMP dxil_debug_info::DxcPixScalarType::UnAlias(
 STDMETHODIMP dxil_debug_info::DxcPixArrayType::GetName(
 STDMETHODIMP dxil_debug_info::DxcPixArrayType::GetName(
     _Outptr_result_z_ BSTR *Name)
     _Outptr_result_z_ BSTR *Name)
 {
 {
-  return E_FAIL;
+  CComBSTR name(CA2W(m_pBaseType->getName().data()));
+  name.Append(L"[]");
+  *Name = name.Detach();
+  return S_OK;
 }
 }
 
 
 STDMETHODIMP dxil_debug_info::DxcPixArrayType::GetSizeInBits(
 STDMETHODIMP dxil_debug_info::DxcPixArrayType::GetSizeInBits(

+ 375 - 23
lib/DxilPIXPasses/DxilDbgValueToDbgDeclare.cpp

@@ -19,8 +19,10 @@
 #include "dxc/DXIL/DxilResourceBase.h"
 #include "dxc/DXIL/DxilResourceBase.h"
 #include "dxc/DXIL/DxilModule.h"
 #include "dxc/DXIL/DxilModule.h"
 #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
 #include "dxc/DxilPIXPasses/DxilPIXPasses.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/Intrinsics.h"
@@ -240,7 +242,8 @@ class VariableRegisters
 {
 {
 public:
 public:
   VariableRegisters(
   VariableRegisters(
-      llvm::DbgValueInst* DbgValue,
+      llvm::DebugLoc const &m_dbgLoc,
+      llvm::BasicBlock::iterator allocaInsertionPoint,
       llvm::DIVariable *Variable,
       llvm::DIVariable *Variable,
       llvm::DIType* Ty,
       llvm::DIType* Ty,
       llvm::Module *M
       llvm::Module *M
@@ -291,6 +294,19 @@ private:
   std::unordered_map<OffsetInBits, llvm::AllocaInst *> m_AlignedOffsetToAlloca;
   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 {
 class DxilDbgValueToDbgDeclare : public llvm::ModulePass {
 public:
 public:
     static char ID;
     static char ID;
@@ -305,8 +321,12 @@ private:
   void handleDbgValue(
   void handleDbgValue(
       llvm::Module &M,
       llvm::Module &M,
       llvm::DbgValueInst *DbgValue);
       llvm::DbgValueInst *DbgValue);
+  bool handleStoreIfDestIsGlobal(llvm::Module &M,
+                                 GlobalStorageMap &GlobalStorage,
+                                 llvm::StoreInst *Store);
 
 
-  std::unordered_map<llvm::DIVariable *, std::unique_ptr<VariableRegisters>> m_Registers;
+  std::unordered_map<llvm::DIVariable *, std::unique_ptr<VariableRegisters>>
+      m_Registers;
 };
 };
 }  // namespace
 }  // namespace
 
 
@@ -399,16 +419,222 @@ static OffsetInBits GetAlignedOffsetFromDIExpression(
   return Exp->getBitPieceOffset();
   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 (auto *DIGVCompositeType =
+              llvm::dyn_cast<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(
 bool DxilDbgValueToDbgDeclare::runOnModule(
     llvm::Module &M
     llvm::Module &M
 )
 )
 {
 {
-  hlsl::DxilModule &DM = M.GetOrCreateDxilModule();
+  auto GlobalEmbeddedArrayStorage = GatherGlobalEmbeddedArrayStorage(M);
 
 
   bool Changed = false;
   bool Changed = false;
 
 
-  auto entryPoints = DM.GetExportedFunctions();
-  for (auto &fn : entryPoints) {
+  auto & Functions = M.getFunctionList();
+  for (auto &fn : Functions) {
     // #DSLTodo: We probably need to merge the list of variables for each export
     // #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
     // 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
     // any function within a given module. (Unless PIX chooses to launch a new
@@ -426,21 +652,39 @@ bool DxilDbgValueToDbgDeclare::runOnModule(
     // Not sure what the right path forward is: might be that we have to tag
     // 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
     // m_Registers with the exported function, and maybe write out a function
     // identifier during debug instrumentation...
     // identifier during debug instrumentation...
-    auto &blocks = fn->getBasicBlockList();
-    for (auto &block : blocks) {
-      std::vector<Instruction *> instructions;
-      for (auto &instruction : block) {
-        instructions.push_back(&instruction);
-      }
-      for (auto & instruction : instructions) {
-        if (auto *DbgValue = llvm::dyn_cast<llvm::DbgValueInst>(instruction)) {
-          llvm::Value *V = DbgValue->getValue();
-          if (PIXPassHelpers::IsAllocateRayQueryInstruction(V)) {
-            continue;
+    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();
           }
           }
-          Changed = true;
-          handleDbgValue(M, DbgValue);
-          DbgValue->eraseFromParent();
         }
         }
       }
       }
     }
     }
@@ -734,7 +978,10 @@ void DxilDbgValueToDbgDeclare::handleDbgValue(
   if (Register == nullptr)
   if (Register == nullptr)
   {
   {
     Register.reset(
     Register.reset(
-        new VariableRegisters(DbgValue, Variable, Ty, &M));
+        new VariableRegisters(
+          DbgValue->getDebugLoc(),
+          DbgValue->getParent()->getParent()->getEntryBlock().begin(),
+          Variable, Ty, &M));
   }
   }
 
 
   // Convert the offset from DbgValue's expression to a packed
   // Convert the offset from DbgValue's expression to a packed
@@ -796,6 +1043,110 @@ void DxilDbgValueToDbgDeclare::handleDbgValue(
   }
   }
 }
 }
 
 
+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) {
 SizeInBits VariableRegisters::GetVariableSizeInbits(DIVariable *Var) {
   const llvm::DITypeIdentifierMap EmptyMap;
   const llvm::DITypeIdentifierMap EmptyMap;
   DIType *Ty = Var->getType().resolve(EmptyMap);
   DIType *Ty = Var->getType().resolve(EmptyMap);
@@ -853,13 +1204,14 @@ static llvm::DIType *DITypePeelTypeAlias(
 #endif // NDEBUG
 #endif // NDEBUG
 
 
 VariableRegisters::VariableRegisters(
 VariableRegisters::VariableRegisters(
-    llvm::DbgValueInst *DbgValue,
+    llvm::DebugLoc const &dbgLoc,
+    llvm::BasicBlock::iterator allocaInsertionPoint,
     llvm::DIVariable *Variable,
     llvm::DIVariable *Variable,
     llvm::DIType* Ty,
     llvm::DIType* Ty,
     llvm::Module *M)
     llvm::Module *M)
-  : m_dbgLoc(DbgValue->getDebugLoc())
+  : m_dbgLoc(dbgLoc)
   , m_Variable(Variable)
   , m_Variable(Variable)
-  , m_B(DbgValue->getParent()->getParent()->getEntryBlock().begin())
+  , m_B(allocaInsertionPoint)
   , m_DbgDeclareFn(llvm::Intrinsic::getDeclaration(
   , m_DbgDeclareFn(llvm::Intrinsic::getDeclaration(
       M, llvm::Intrinsic::dbg_declare))
       M, llvm::Intrinsic::dbg_declare))
 {
 {

+ 48 - 0
tools/clang/test/HLSLFileCheck/pix/GlobalBackedGlobalStaticEmbeddedArrays_NoDbgValue.hlsl

@@ -0,0 +1,48 @@
+// RUN: %dxc -Tlib_6_5 /Od /Zi %s | %FileCheck %s -check-prefixes=BEFORE
+// RUN: %dxc -Tlib_6_5 /Od /Zi %s | %opt -S -dxil-dbg-value-to-dbg-declare | %FileCheck %s -check-prefixes=AFTER
+
+// In this shader, globalStruct.FloatArray will be the only member of globalStruct stored in an llvm global.
+// Consequently, there won't be a DILocalVariable for globalStruct in main, but we expect the above pass to add one to be referenced by a new dbg-dot-declare.
+// Nor do we expect a dbg.declare nor DIExpression for FloatArray before the pass is run
+// 
+// BEFORE-NOT:dbg.declare
+// BEFORE-NOT:!DIExpression(DW_OP_bit_piece, 64, 32)
+// BEFORE-NOT:!DIExpression(DW_OP_bit_piece, 96, 32)
+// BEFORE:ret void
+// BEFORE-NOT:!DILocalVariable{{.*}}name: "global.globalStruct"
+
+// After the pass is run, the should be a dbg.declare for bit-piece for both members of FloatArray,
+// and a new local-variable for globalStruct
+// AFTER:dbg.declare{{.*}}!DIExpression(DW_OP_bit_piece, 64, 32)
+// AFTER:dbg.declare{{.*}}!DIExpression(DW_OP_bit_piece, 96, 32)
+// AFTER:!DILocalVariable(tag: DW_TAG_auto_variable, name: "global.globalStruct"
+
+RWStructuredBuffer<float> floatRWUAV : register(u0);
+
+struct GlobalStruct {
+  int IntArray[2];
+  float FloatArray[2];
+};
+
+static GlobalStruct globalStruct;
+
+[numthreads(1, 1, 1)] void main() {
+  float Accumulator;
+  globalStruct.IntArray[0] = floatRWUAV[0];
+  globalStruct.IntArray[1] = floatRWUAV[1];
+  globalStruct.FloatArray[0] = floatRWUAV[2];
+  globalStruct.FloatArray[1] = floatRWUAV[3];
+  Accumulator = 0;
+
+  uint killSwitch = 0;
+
+  [loop] // do not unroll this
+      while (true) {
+    Accumulator += globalStruct.FloatArray[killSwitch % 2];
+
+    if (killSwitch++ == 4)
+      break;
+  }
+
+  floatRWUAV[0] = Accumulator + globalStruct.IntArray[0] + globalStruct.IntArray[1];
+}

+ 47 - 0
tools/clang/test/HLSLFileCheck/pix/GlobalBackedGlobalStaticEmbeddedArrays_WithDbgValue.hlsl

@@ -0,0 +1,47 @@
+// RUN: %dxc -Tlib_6_5 /Od /Zi %s | %FileCheck %s -check-prefixes=BEFORE
+// RUN: %dxc -Tlib_6_5 /Od /Zi %s | %opt -S -dxil-dbg-value-to-dbg-declare | %FileCheck %s -check-prefixes=AFTER
+
+// In this shader, globalStruct.FloatArray will be the only member of globalStruct stored in an llvm global, but Accumulator will force a 
+// DILocalVariable for globalStruct in main (because there will be dbg.value/declare for Accumulator), 
+// but we expect the above pass to find it and reference it by a new dbg-dot-declare.
+// But there still won't be a bit-piece for FloatArray (we're going to add one)
+// 
+// BEFORE-NOT:dbg.declare
+// BEFORE-NOT:!DIExpression(DW_OP_bit_piece, 64, 32)
+// BEFORE-NOT:!DIExpression(DW_OP_bit_piece, 96, 32)
+// BEFORE:ret void
+// BEFORE:!DILocalVariable(tag: DW_TAG_arg_variable, name: "global.globalStruct"
+
+// After the pass is run, the should be a dbg.declare for bit-piece for both members of FloatArray:
+// AFTER:dbg.declare{{.*}}!DIExpression(DW_OP_bit_piece, 64, 32)
+// AFTER:dbg.declare{{.*}}!DIExpression(DW_OP_bit_piece, 96, 32)
+
+RWStructuredBuffer<float> floatRWUAV : register(u0);
+
+struct GlobalStruct {
+  int IntArray[2];
+  float FloatArray[2];
+  float Accumulator;
+};
+
+static GlobalStruct globalStruct;
+
+[numthreads(1, 1, 1)] void main() {
+  globalStruct.IntArray[0] = floatRWUAV[0];
+  globalStruct.IntArray[1] = floatRWUAV[1];
+  globalStruct.FloatArray[0] = floatRWUAV[2];
+  globalStruct.FloatArray[1] = floatRWUAV[3];
+  globalStruct.Accumulator = 0;
+
+  uint killSwitch = 0;
+
+  [loop] // do not unroll this
+      while (true) {
+    globalStruct.Accumulator += globalStruct.FloatArray[killSwitch % 2];
+
+    if (killSwitch++ == 4)
+      break;
+  }
+
+  floatRWUAV[0] = globalStruct.Accumulator + globalStruct.IntArray[0] + globalStruct.IntArray[1];
+}

+ 449 - 48
tools/clang/unittests/HLSL/PixTest.cpp

@@ -230,6 +230,9 @@ public:
   TEST_METHOD(PixTypeManager_XBoxDiaAssert)
   TEST_METHOD(PixTypeManager_XBoxDiaAssert)
 
 
   TEST_METHOD(DxcPixDxilDebugInfo_InstructionOffsets)
   TEST_METHOD(DxcPixDxilDebugInfo_InstructionOffsets)
+  TEST_METHOD(DxcPixDxilDebugInfo_GlobalBackedGlobalStaticEmbeddedArrays_NoDbgValue)
+  TEST_METHOD(DxcPixDxilDebugInfo_GlobalBackedGlobalStaticEmbeddedArrays_WithDbgValue)
+  TEST_METHOD(DxcPixDxilDebugInfo_GlobalBackedGlobalStaticEmbeddedArrays_ArrayInValues)
 
 
   TEST_METHOD(VirtualRegisters_InstructionCounts)
   TEST_METHOD(VirtualRegisters_InstructionCounts)
   TEST_METHOD(VirtualRegisters_AlignedOffsets)
   TEST_METHOD(VirtualRegisters_AlignedOffsets)
@@ -645,7 +648,7 @@ public:
   CComPtr<IDxcBlob> Compile(
   CComPtr<IDxcBlob> Compile(
     const char* hlsl,
     const char* hlsl,
     const wchar_t* target,
     const wchar_t* target,
-      std::vector<const wchar_t*> extraArgs = {},
+    std::vector<const wchar_t*> extraArgs = {},
     const wchar_t* entry = L"main")
     const wchar_t* entry = L"main")
   {
   {
     CComPtr<IDxcCompiler> pCompiler;
     CComPtr<IDxcCompiler> pCompiler;
@@ -879,7 +882,7 @@ static std::string ToString(std::wstring from)
     return ret;
     return ret;
 }
 }
 
 
-  PassOutput RunAnnotationPasses(IDxcBlob * dxil, int startingLineNumber = 0)
+  PassOutput RunValueToDeclarePass(IDxcBlob * dxil, int startingLineNumber = 0)
   {
   {
     CComPtr<IDxcOptimizer> pOptimizer;
     CComPtr<IDxcOptimizer> pOptimizer;
     VERIFY_SUCCEEDED(
     VERIFY_SUCCEEDED(
@@ -887,6 +890,29 @@ static std::string ToString(std::wstring from)
     std::vector<LPCWSTR> Options;
     std::vector<LPCWSTR> Options;
     Options.push_back(L"-opt-mod-passes");
     Options.push_back(L"-opt-mod-passes");
     Options.push_back(L"-dxil-dbg-value-to-dbg-declare");
     Options.push_back(L"-dxil-dbg-value-to-dbg-declare");
+
+    CComPtr<IDxcBlob> pOptimizedModule;
+    CComPtr<IDxcBlobEncoding> pText;
+    VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(
+        dxil, Options.data(), Options.size(), &pOptimizedModule, &pText));
+
+    std::string outputText;
+    if (pText->GetBufferSize() != 0)
+    {
+      outputText = reinterpret_cast<const char*>(pText->GetBufferPointer());
+    }
+
+    return {
+        std::move(pOptimizedModule), {}, Tokenize(outputText.c_str(), "\n")};
+  }
+
+  PassOutput RunAnnotationPasses(IDxcBlob *dxil, int startingLineNumber = 0) {
+    CComPtr<IDxcOptimizer> pOptimizer;
+    VERIFY_SUCCEEDED(
+        m_dllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
+    std::vector<LPCWSTR> Options;
+    Options.push_back(L"-opt-mod-passes");
+    Options.push_back(L"-dxil-dbg-value-to-dbg-declare");
     std::wstring annotationCommandLine =
     std::wstring annotationCommandLine =
         L"-dxil-annotate-with-virtual-regs,startInstruction=" +
         L"-dxil-annotate-with-virtual-regs,startInstruction=" +
         std::to_wstring(startingLineNumber);
         std::to_wstring(startingLineNumber);
@@ -898,9 +924,8 @@ static std::string ToString(std::wstring from)
         dxil, Options.data(), Options.size(), &pOptimizedModule, &pText));
         dxil, Options.data(), Options.size(), &pOptimizedModule, &pText));
 
 
     std::string outputText;
     std::string outputText;
-    if (pText->GetBufferSize() != 0)
-    {
-      outputText = reinterpret_cast<const char*>(pText->GetBufferPointer());
+    if (pText->GetBufferSize() != 0) {
+      outputText = reinterpret_cast<const char *>(pText->GetBufferPointer());
     }
     }
 
 
     auto disasm = ToString(Disassemble(pOptimizedModule));
     auto disasm = ToString(Disassemble(pOptimizedModule));
@@ -909,12 +934,12 @@ static std::string ToString(std::wstring from)
 
 
     std::vector<ValueLocation> valueLocations;
     std::vector<ValueLocation> valueLocations;
 
 
-    for (auto const& r2n : registerToName)
-    {
+    for (auto const &r2n : registerToName) {
       valueLocations.push_back({r2n.first.first, r2n.first.second});
       valueLocations.push_back({r2n.first.first, r2n.first.second});
     }
     }
 
 
-    return { std::move(pOptimizedModule), std::move(valueLocations), Tokenize(outputText.c_str(), "\n") };
+    return {std::move(pOptimizedModule), std::move(valueLocations),
+            Tokenize(outputText.c_str(), "\n")};
   }
   }
 
 
   std::wstring Disassemble(IDxcBlob * pProgram)
   std::wstring Disassemble(IDxcBlob * pProgram)
@@ -1144,11 +1169,24 @@ static std::string ToString(std::wstring from)
                                                                  blob);
                                                                  blob);
   CComPtr<IDxcBlob> RunDxilPIXMeshShaderOutputPass(IDxcBlob* blob);
   CComPtr<IDxcBlob> RunDxilPIXMeshShaderOutputPass(IDxcBlob* blob);
   void CompileAndRunAnnotationAndGetDebugPart(
   void CompileAndRunAnnotationAndGetDebugPart(
+      dxc::DxcDllSupport &dllSupport, const char *source, const wchar_t *profile,
+      IDxcBlob **ppDebugPart, std::vector<const wchar_t *> extraArgs = {});
+  void CompileAndRunValueToDeclareAndGetDebugPart(
       dxc::DxcDllSupport &dllSupport, const char *source, wchar_t *profile,
       dxc::DxcDllSupport &dllSupport, const char *source, wchar_t *profile,
       IDxcBlob **ppDebugPart);
       IDxcBlob **ppDebugPart);
-  void CompileAndRunAnnotationAndLoadDiaSource(dxc::DxcDllSupport &dllSupport,
-                                   const char *source, wchar_t *profile,
-                                   IDiaDataSource **ppDataSource);
+  void CompileAndRunAnnotationAndLoadDiaSource(
+      dxc::DxcDllSupport &dllSupport, const char *source, const wchar_t *profile,
+      IDiaDataSource **ppDataSource,
+      std::vector<const wchar_t *> extraArgs = {});
+
+  struct VariableComponentInfo {
+    std::wstring Name;
+    std::wstring Type;
+  };
+  void TestGlobalStaticCase(
+      const char *hlsl, const wchar_t * profile, const char *lineAtWhichToExamineVariables,
+      std::vector<VariableComponentInfo> const &ExpectedVariables);
+
 
 
 private:
 private:
   CComPtr<IDxcBlob> WrapInNewContainer(IDxcBlob * part);
   CComPtr<IDxcBlob> WrapInNewContainer(IDxcBlob * part);
@@ -1814,7 +1852,7 @@ TEST_F(PixTest, PixDebugCompileInfo) {
 static LPCWSTR defaultFilename = L"source.hlsl";
 static LPCWSTR defaultFilename = L"source.hlsl";
 
 
 static void CompileAndLogErrors(dxc::DxcDllSupport &dllSupport, LPCSTR pText,
 static void CompileAndLogErrors(dxc::DxcDllSupport &dllSupport, LPCSTR pText,
-                     LPWSTR pTargetProfile, std::vector<LPCWSTR> &args,
+                     LPCWSTR pTargetProfile, std::vector<LPCWSTR> &args,
                      _Outptr_ IDxcBlob **ppResult) {
                      _Outptr_ IDxcBlob **ppResult) {
   CComPtr<IDxcCompiler> pCompiler;
   CComPtr<IDxcCompiler> pCompiler;
   CComPtr<IDxcBlobEncoding> pSource;
   CComPtr<IDxcBlobEncoding> pSource;
@@ -1837,16 +1875,55 @@ static void CompileAndLogErrors(dxc::DxcDllSupport &dllSupport, LPCSTR pText,
   VERIFY_SUCCEEDED(hrCompile);
   VERIFY_SUCCEEDED(hrCompile);
   VERIFY_SUCCEEDED(pResult->GetResult(ppResult));
   VERIFY_SUCCEEDED(pResult->GetResult(ppResult));
 }
 }
-void PixTest::CompileAndRunAnnotationAndGetDebugPart(
-    dxc::DxcDllSupport &dllSupport, const char *source, wchar_t *profile,
-    IDxcBlob **ppDebugPart) {
-  CComPtr<IDxcBlob> pContainer;
+
+static CComPtr<IDxcBlob> GetDebugPart(dxc::DxcDllSupport &dllSupport,
+                                      IDxcBlob *container) {
+
   CComPtr<IDxcLibrary> pLib;
   CComPtr<IDxcLibrary> pLib;
+  VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
   CComPtr<IDxcContainerReflection> pReflection;
   CComPtr<IDxcContainerReflection> pReflection;
+
+  VERIFY_SUCCEEDED(
+      dllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
+  VERIFY_SUCCEEDED(pReflection->Load(container));
+
   UINT32 index;
   UINT32 index;
+  VERIFY_SUCCEEDED(
+      pReflection->FindFirstPartKind(hlsl::DFCC_ShaderDebugInfoDXIL, &index));
+
+  CComPtr<IDxcBlob> debugPart;
+  VERIFY_SUCCEEDED(pReflection->GetPartContent(index, &debugPart));
+
+  return debugPart;
+}
+
+void PixTest::CompileAndRunValueToDeclareAndGetDebugPart(
+    dxc::DxcDllSupport &dllSupport, const char *source, wchar_t *profile,
+    IDxcBlob **ppDebugPart) {
+  CComPtr<IDxcBlob> pContainer;
+  std::vector<LPCWSTR> args;
+  args.push_back(L"/Zi");
+  args.push_back(L"/Od");
+  args.push_back(L"/Qembed_debug");
+
+  CompileAndLogErrors(dllSupport, source, profile, args, &pContainer);
+
+  auto annotated = RunValueToDeclarePass(pContainer);
+
+  CComPtr<IDxcBlob> pNewContainer = WrapInNewContainer(annotated.blob);
+
+  *ppDebugPart = GetDebugPart(dllSupport, pNewContainer).Detach();
+}
+
+void PixTest::CompileAndRunAnnotationAndGetDebugPart(
+    dxc::DxcDllSupport &dllSupport, const char *source, const wchar_t *profile,
+    IDxcBlob **ppDebugPart, std::vector<const wchar_t *> extraArgs) {
+
+  CComPtr<IDxcBlob> pContainer;
   std::vector<LPCWSTR> args;
   std::vector<LPCWSTR> args;
   args.push_back(L"/Zi");
   args.push_back(L"/Zi");
   args.push_back(L"/Qembed_debug");
   args.push_back(L"/Qembed_debug");
+  args.insert(args.end(), extraArgs.begin(), extraArgs.end());
 
 
   CompileAndLogErrors(dllSupport, source, profile, args, &pContainer);
   CompileAndLogErrors(dllSupport, source, profile, args, &pContainer);
 
 
@@ -1854,18 +1931,13 @@ void PixTest::CompileAndRunAnnotationAndGetDebugPart(
 
 
   CComPtr<IDxcBlob> pNewContainer = WrapInNewContainer(annotated.blob);
   CComPtr<IDxcBlob> pNewContainer = WrapInNewContainer(annotated.blob);
 
 
-  VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
-  VERIFY_SUCCEEDED(
-      dllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
-  VERIFY_SUCCEEDED(pReflection->Load(pNewContainer));
-  VERIFY_SUCCEEDED(
-      pReflection->FindFirstPartKind(hlsl::DFCC_ShaderDebugInfoDXIL, &index));
-  VERIFY_SUCCEEDED(pReflection->GetPartContent(index, ppDebugPart));
+  *ppDebugPart = GetDebugPart(dllSupport, pNewContainer).Detach();
 }
 }
 
 
 void PixTest::CompileAndRunAnnotationAndLoadDiaSource(
 void PixTest::CompileAndRunAnnotationAndLoadDiaSource(
-    dxc::DxcDllSupport &dllSupport, const char *source, wchar_t *profile,
-    IDiaDataSource **ppDataSource) {
+    dxc::DxcDllSupport &dllSupport, const char *source, const wchar_t *profile,
+    IDiaDataSource **ppDataSource,
+    std::vector<const wchar_t *> extraArgs) {
   CComPtr<IDxcBlob> pDebugContent;
   CComPtr<IDxcBlob> pDebugContent;
   CComPtr<IStream> pStream;
   CComPtr<IStream> pStream;
   CComPtr<IDiaDataSource> pDiaSource;
   CComPtr<IDiaDataSource> pDiaSource;
@@ -1873,7 +1945,7 @@ void PixTest::CompileAndRunAnnotationAndLoadDiaSource(
   VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
   VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
 
 
   CompileAndRunAnnotationAndGetDebugPart(dllSupport, source, profile,
   CompileAndRunAnnotationAndGetDebugPart(dllSupport, source, profile,
-                                         &pDebugContent);
+                                         &pDebugContent, extraArgs);
   VERIFY_SUCCEEDED(pLib->CreateStreamFromBlobReadOnly(pDebugContent, &pStream));
   VERIFY_SUCCEEDED(pLib->CreateStreamFromBlobReadOnly(pDebugContent, &pStream));
   VERIFY_SUCCEEDED(
   VERIFY_SUCCEEDED(
       dllSupport.CreateInstance(CLSID_DxcDiaDataSource, &pDiaSource));
       dllSupport.CreateInstance(CLSID_DxcDiaDataSource, &pDiaSource));
@@ -2328,6 +2400,357 @@ void MyMissShader(inout RayPayload payload)
   }
   }
 }
 }
 
 
+static HRESULT UnAliasType(IDxcPixType *MaybeAlias,
+                           IDxcPixType **OriginalType) {
+  *OriginalType = nullptr;
+  CComPtr<IDxcPixType> Tmp(MaybeAlias);
+
+  do {
+    HRESULT hr;
+
+    CComPtr<IDxcPixType> Alias;
+    hr = Tmp->UnAlias(&Alias);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    if (hr == S_FALSE) {
+      break;
+    }
+    Tmp = Alias;
+  } while (true);
+
+  *OriginalType = Tmp.Detach();
+  return S_OK;
+}
+
+static bool AddStorageComponents(
+    IDxcPixDxilStorage *pStorage, std::wstring Name,
+    std::vector<PixTest::VariableComponentInfo> &VariableComponents) {
+  CComPtr<IDxcPixType> StorageType;
+  if (FAILED(pStorage->GetType(&StorageType))) {
+    return false;
+  }
+
+  CComPtr<IDxcPixType> UnAliasedType;
+  if (FAILED(UnAliasType(StorageType, &UnAliasedType))) {
+    return false;
+  }
+
+  CComPtr<IDxcPixArrayType> ArrayType;
+  CComPtr<IDxcPixScalarType> ScalarType;
+  CComPtr<IDxcPixStructType> StructType;
+
+  if (!FAILED(UnAliasedType->QueryInterface(&ScalarType))) {
+    CComBSTR TypeName;
+    // StorageType is the type that the storage was defined in HLSL, i.e.,
+    // it could be a typedef, const etc.
+    if (FAILED(StorageType->GetName(&TypeName))) {
+      return false;
+    }
+
+    VariableComponents.emplace_back(PixTest::VariableComponentInfo{
+        std::move(Name), std::wstring(TypeName)});
+    return true;
+  } else if (!FAILED(UnAliasedType->QueryInterface(&ArrayType))) {
+    DWORD NumElements;
+    if (FAILED(ArrayType->GetNumElements(&NumElements))) {
+      return false;
+    }
+
+    std::wstring BaseName = Name + L'[';
+    for (DWORD i = 0; i < NumElements; ++i) {
+      CComPtr<IDxcPixDxilStorage> EntryStorage;
+      if (FAILED(pStorage->Index(i, &EntryStorage))) {
+        return false;
+      }
+
+      if (!AddStorageComponents(EntryStorage,
+                                      BaseName + std::to_wstring(i) + L"]",
+                                      VariableComponents)) {
+        return false;
+      }
+    }
+  } else if (!FAILED(UnAliasedType->QueryInterface(&StructType))) {
+    DWORD NumFields;
+    if (FAILED(StructType->GetNumFields(&NumFields))) {
+      return false;
+    }
+
+    std::wstring BaseName = Name + L'.';
+    for (DWORD i = 0; i < NumFields; ++i) {
+      CComPtr<IDxcPixStructField> Field;
+      if (FAILED(StructType->GetFieldByIndex(i, &Field))) {
+        return false;
+      }
+
+      CComBSTR FieldName;
+      if (FAILED(Field->GetName(&FieldName))) {
+        return false;
+      }
+
+      CComPtr<IDxcPixDxilStorage> FieldStorage;
+      if (FAILED(pStorage->AccessField(FieldName, &FieldStorage))) {
+        return false;
+      }
+
+      if (!AddStorageComponents(FieldStorage,
+                                      BaseName + std::wstring(FieldName),
+                                      VariableComponents)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+static bool ContainedBy(std::vector<PixTest::VariableComponentInfo> const &v1,
+                        std::vector<PixTest::VariableComponentInfo> const &v2) {
+  for (auto const &c1 : v1) {
+    bool FoundThis = false;
+    for (auto const &c2 : v2) {
+      if (c1.Name == c2.Name && c1.Type == c2.Type) {
+        FoundThis = true;
+        break;
+      }
+    }
+    if (!FoundThis) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void PixTest::TestGlobalStaticCase(const char *hlsl,
+  const wchar_t * profile,
+  const char *lineAtWhichToExamineVariables,
+  std::vector<VariableComponentInfo> const &ExpectedVariables) {
+  CComPtr<IDiaDataSource> pDiaDataSource;
+  CompileAndRunAnnotationAndLoadDiaSource(m_dllSupport, hlsl, profile,
+                                          &pDiaDataSource, {L"-Od"});
+
+  CComPtr<IDiaSession> session;
+  VERIFY_SUCCEEDED(pDiaDataSource->openSession(&session));
+
+  CComPtr<IDxcPixDxilDebugInfoFactory> Factory;
+  VERIFY_SUCCEEDED(session->QueryInterface(IID_PPV_ARGS(&Factory)));
+
+  CComPtr<IDxcPixDxilDebugInfo> dxilDebugger;
+  VERIFY_SUCCEEDED(Factory->NewDxcPixDxilDebugInfo(&dxilDebugger));
+
+  auto lines = SplitAndPreserveEmptyLines(std::string(hlsl), '\n');
+  auto FindInterestingLine = std::find_if(
+      lines.begin(), lines.end(),
+      [&lineAtWhichToExamineVariables](std::string const &line) {
+        return line.find(lineAtWhichToExamineVariables) != std::string::npos;
+      });
+  auto InterestingLine =
+      static_cast<DWORD>(FindInterestingLine - lines.begin()) + 1;
+
+  CComPtr<IDxcPixDxilLiveVariables> liveVariables;
+  for (; InterestingLine <= static_cast<DWORD>(lines.size());
+       ++InterestingLine) {
+    CComPtr<IDxcPixDxilInstructionOffsets> instructionOffsets;
+    if (SUCCEEDED(dxilDebugger->InstructionOffsetsFromSourceLocation(
+            defaultFilename, InterestingLine, 0, &instructionOffsets))) {
+      if (instructionOffsets->GetCount() > 0) {
+        auto instructionOffset = instructionOffsets->GetOffsetByIndex(0);
+        if (SUCCEEDED(dxilDebugger->GetLiveVariablesAt(instructionOffset,
+                                                       &liveVariables))) {
+          break;
+        }
+      }
+    }
+  }
+  VERIFY_IS_TRUE(liveVariables != nullptr);
+
+  DWORD count;
+  VERIFY_SUCCEEDED(liveVariables->GetCount(&count));
+  bool FoundGlobal = false;
+  for (DWORD i = 0; i < count; ++i) {
+    CComPtr<IDxcPixVariable> variable;
+    VERIFY_SUCCEEDED(liveVariables->GetVariableByIndex(i, &variable));
+    CComBSTR name;
+    variable->GetName(&name);
+    if (0 == wcscmp(name, L"global.globalStruct")) {
+      FoundGlobal = true;
+      CComPtr<IDxcPixDxilStorage> storage;
+      VERIFY_SUCCEEDED(variable->GetStorage(&storage));
+      std::vector<VariableComponentInfo> ActualVariableComponents;
+      VERIFY_IS_TRUE(AddStorageComponents(storage, L"global.globalStruct",
+                                          ActualVariableComponents));
+      VERIFY_IS_TRUE(ContainedBy(ActualVariableComponents, ExpectedVariables));
+      break;
+    }
+  }
+  VERIFY_IS_TRUE(FoundGlobal);
+}
+
+TEST_F(PixTest,
+       DxcPixDxilDebugInfo_GlobalBackedGlobalStaticEmbeddedArrays_NoDbgValue) {
+  if (m_ver.SkipDxilVersion(1, 5))
+    return;
+
+  const char *hlsl = R"(
+RWStructuredBuffer<float> floatRWUAV: register(u0);
+
+struct GlobalStruct
+{
+    int IntArray[2];
+    float FloatArray[2];
+};
+
+static GlobalStruct globalStruct;
+[noinline]
+void fn()
+{
+    float Accumulator;
+    globalStruct.IntArray[0] = floatRWUAV[0];
+    globalStruct.IntArray[1] = floatRWUAV[1];
+    globalStruct.FloatArray[0] = floatRWUAV[4];
+    globalStruct.FloatArray[1] = floatRWUAV[5];
+    Accumulator = 0;
+
+    uint killSwitch = 0;
+
+    [loop] // do not unroll this
+    while (true)
+    {
+        Accumulator += globalStruct.FloatArray[killSwitch % 2];
+
+        if (killSwitch++ == 4) break;
+    }
+
+    floatRWUAV[0] = Accumulator + globalStruct.IntArray[0] + globalStruct.IntArray[1];
+}
+
+[numthreads(1, 1, 1)]
+void main()
+{
+  fn();
+}
+
+)";
+  // The above HLSL should generate a module that represents the FloatArray
+  // member as a global, and the IntArray as an alloca. Since only embedded
+  // arrays are present in GlobalStruct, no dbg.value will be present for
+  // globalStruct. We expect the value-to-declare pass to generate its own
+  // dbg.value for stores into FloatArray. We will observe those dbg.value here
+  // via the PIX-specific debug data API.
+
+  std::vector<VariableComponentInfo> Expected;
+  Expected.push_back({L"global.globalStruct.IntArray[0]", L"int"});
+  Expected.push_back({L"global.globalStruct.IntArray[1]", L"int"});
+  Expected.push_back({L"global.globalStruct.FloatArray[0]", L"float"});
+  Expected.push_back({L"global.globalStruct.FloatArray[1]", L"float"});
+  TestGlobalStaticCase(hlsl, L"lib_6_6", "float Accumulator", Expected);
+}
+
+TEST_F(PixTest,
+       DxcPixDxilDebugInfo_GlobalBackedGlobalStaticEmbeddedArrays_WithDbgValue) {
+  if (m_ver.SkipDxilVersion(1, 5))
+    return;
+
+  const char *hlsl = R"(
+RWStructuredBuffer<float> floatRWUAV: register(u0);
+
+struct GlobalStruct
+{
+    float Accumulator;
+    int IntArray[2];
+    float FloatArray[2];
+};
+
+static GlobalStruct globalStruct;
+[numthreads(1, 1, 1)]
+void main()
+{
+    globalStruct.IntArray[0] = floatRWUAV[0];
+    globalStruct.IntArray[1] = floatRWUAV[1];
+    globalStruct.FloatArray[0] = floatRWUAV[4];
+    globalStruct.FloatArray[1] = floatRWUAV[5];
+    globalStruct.Accumulator = 0;
+
+    uint killSwitch = 0;
+
+    [loop] // do not unroll this
+    while (true)
+    {
+        globalStruct.Accumulator += globalStruct.FloatArray[killSwitch % 2];
+
+        if (killSwitch++ == 4) break;
+    }
+
+    floatRWUAV[0] = globalStruct.Accumulator + globalStruct.IntArray[0] + globalStruct.IntArray[1];
+}
+
+)";
+  // The above HLSL should generate a module that represents the FloatArray
+  // member as a global, and the IntArray as an alloca. The presence of Accumulator 
+  // in the GlobalStruct will force a dbg.value to be present for
+  // globalStruct. We expect the value-to-declare pass to find that dbg.value.
+
+  std::vector<VariableComponentInfo> Expected;
+  Expected.push_back({L"global.globalStruct.Accumulator", L"float"});
+  Expected.push_back({L"global.globalStruct.IntArray[0]", L"int"});
+  Expected.push_back({L"global.globalStruct.IntArray[1]", L"int"});
+  Expected.push_back({L"global.globalStruct.FloatArray[0]", L"float"});
+  Expected.push_back({L"global.globalStruct.FloatArray[1]", L"float"});
+  TestGlobalStaticCase(hlsl, L"cs_6_6", "float Accumulator", Expected);
+}
+
+
+TEST_F(PixTest,
+    DxcPixDxilDebugInfo_GlobalBackedGlobalStaticEmbeddedArrays_ArrayInValues) {
+  if (m_ver.SkipDxilVersion(1, 5))
+    return;
+
+  const char *hlsl = R"(
+RWStructuredBuffer<float> floatRWUAV: register(u0);
+
+struct GlobalStruct
+{
+    float Accumulator;
+    int IntArray[2];
+    float FloatArray[2];
+};
+
+static GlobalStruct globalStruct;
+[numthreads(1, 1, 1)]
+void main()
+{
+    globalStruct.IntArray[0] =0;
+    globalStruct.IntArray[1] =1;
+    globalStruct.FloatArray[0] = floatRWUAV[4];
+    globalStruct.FloatArray[1] = floatRWUAV[5];
+    globalStruct.Accumulator = 0;
+
+    uint killSwitch = 0;
+
+    [loop] // do not unroll this
+    while (true)
+    {
+        globalStruct.Accumulator += globalStruct.FloatArray[killSwitch % 2];
+
+        if (killSwitch++ == 4) break;
+    }
+
+    floatRWUAV[0] = globalStruct.Accumulator + globalStruct.IntArray[0] + globalStruct.IntArray[1];
+}
+
+)";
+  // The above HLSL should generate a module that represents the FloatArray
+  // member as a global, and the IntArray as individual values. 
+
+  std::vector<VariableComponentInfo> Expected;
+  Expected.push_back({L"global.globalStruct.Accumulator", L"float"});
+  Expected.push_back({L"global.globalStruct.IntArray[0]", L"int"});
+  Expected.push_back({L"global.globalStruct.IntArray[1]", L"int"});
+  Expected.push_back({L"global.globalStruct.FloatArray[0]", L"float"});
+  Expected.push_back({L"global.globalStruct.FloatArray[1]", L"float"});
+  TestGlobalStaticCase(hlsl, L"lib_6_6", "float Accumulator", Expected);
+}
+
 CComPtr<IDxcBlob> PixTest::RunShaderAccessTrackingPass(IDxcBlob *blob) {
 CComPtr<IDxcBlob> PixTest::RunShaderAccessTrackingPass(IDxcBlob *blob) {
   CComPtr<IDxcOptimizer> pOptimizer;
   CComPtr<IDxcOptimizer> pOptimizer;
   VERIFY_SUCCEEDED(
   VERIFY_SUCCEEDED(
@@ -4395,28 +4818,6 @@ float4 main(int i : A, float j : B) : SV_TARGET
   VERIFY_IS_TRUE(foundGlobalRS);
   VERIFY_IS_TRUE(foundGlobalRS);
 }
 }
 
 
-static CComPtr<IDxcBlob> GetDebugPart(dxc::DxcDllSupport &dllSupport,
-                                      IDxcBlob *container) {
-
-  CComPtr<IDxcLibrary> pLib;
-  VERIFY_SUCCEEDED(dllSupport.CreateInstance(CLSID_DxcLibrary, &pLib));
-  CComPtr<IDxcContainerReflection> pReflection;
-
-  VERIFY_SUCCEEDED(
-      dllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
-  VERIFY_SUCCEEDED(pReflection->Load(container));
-
-  UINT32 index;
-  VERIFY_SUCCEEDED(
-      pReflection->FindFirstPartKind(hlsl::DFCC_ShaderDebugInfoDXIL, &index));
-
-  CComPtr<IDxcBlob> debugPart;
-  VERIFY_SUCCEEDED(pReflection->GetPartContent(index, &debugPart));
-
-  return debugPart;
-}
-
-
 TEST_F(PixTest, SymbolManager_Embedded2DArray) {
 TEST_F(PixTest, SymbolManager_Embedded2DArray) {
   const char *code = R"x(
   const char *code = R"x(
 struct EmbeddedStruct
 struct EmbeddedStruct