[llvm] 913b561 - [Assignment Tracking][6/*] Add trackAssignments function

via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 8 08:52:27 PST 2022


Author: OCHyams
Date: 2022-11-08T16:52:11Z
New Revision: 913b561c0a1d5817be7e2bc811a42bd4b9b6ff1e

URL: https://github.com/llvm/llvm-project/commit/913b561c0a1d5817be7e2bc811a42bd4b9b6ff1e
DIFF: https://github.com/llvm/llvm-project/commit/913b561c0a1d5817be7e2bc811a42bd4b9b6ff1e.diff

LOG: [Assignment Tracking][6/*] Add trackAssignments function

The Assignment Tracking debug-info feature is outlined in this RFC:

https://discourse.llvm.org/t/
rfc-assignment-tracking-a-better-way-of-specifying-variable-locations-in-ir

Add trackAssignments which adds assignment tracking metadata to a function for
a specified set of variables. The intended callers are the inliner and the
front end - those calls will be added in separate patches.

I've added a pass called declare-to-assign (AssignmentTrackingPass) that
converts dbg.declare intrinsics to dbg.assigns using trackAssignments so that
the function can be easily tested (see
llvm/test/DebugInfo/Generic/track-assignments.ll). The pass could also be used
by front ends to easily test out enabling assignment tracking.

Reviewed By: jmorse

Differential Revision: https://reviews.llvm.org/D132225

Added: 
    llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll

Modified: 
    llvm/include/llvm/IR/DIBuilder.h
    llvm/include/llvm/IR/DebugInfo.h
    llvm/lib/IR/DIBuilder.cpp
    llvm/lib/IR/DebugInfo.cpp
    llvm/lib/Passes/PassBuilder.cpp
    llvm/lib/Passes/PassRegistry.def

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 61fa4d8f3b9fd..ef64fcd8cb924 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -37,6 +37,7 @@ namespace llvm {
   class LLVMContext;
   class Module;
   class Value;
+  class DbgAssignIntrinsic;
 
   class DIBuilder {
     Module &M;
@@ -47,6 +48,7 @@ namespace llvm {
     Function *ValueFn;       ///< llvm.dbg.value
     Function *LabelFn;       ///< llvm.dbg.label
     Function *AddrFn;        ///< llvm.dbg.addr
+    Function *AssignFn;      ///< llvm.dbg.assign
 
     SmallVector<TrackingMDNodeRef, 4> AllEnumTypes;
     /// Track the RetainTypes, since they can be updated later on.
@@ -917,6 +919,26 @@ namespace llvm {
                                DIExpression *Expr, const DILocation *DL,
                                BasicBlock *InsertAtEnd);
 
+    /// Insert a new llvm.dbg.assign intrinsic call.
+    /// \param LinkedInstr   Instruction with a DIAssignID to link with the new
+    ///                      intrinsic. The intrinsic will be inserted after
+    ///                      this instruction.
+    /// \param Val           The value component of this dbg.assign.
+    /// \param SrcVar        Variable's debug info descriptor.
+    /// \param ValExpr       A complex location expression to modify \p Val.
+    /// \param Addr          The address component (store destination).
+    /// \param AddrExpr      A complex location expression to modify \p Addr.
+    ///                      NOTE: \p ValExpr carries the FragInfo for the
+    ///                      variable.
+    /// \param DL            Debug info location, usually: (line: 0,
+    ///                      column: 0, scope: var-decl-scope). See
+    ///                      getDebugValueLoc.
+    DbgAssignIntrinsic *insertDbgAssign(Instruction *LinkedInstr, Value *Val,
+                                        DILocalVariable *SrcVar,
+                                        DIExpression *ValExpr, Value *Addr,
+                                        DIExpression *AddrExpr,
+                                        const DILocation *DL);
+
     /// Insert a new llvm.dbg.declare intrinsic call.
     /// \param Storage      llvm::Value of the variable
     /// \param VarInfo      Variable's debug info descriptor.

diff  --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index ab6a36946af6a..7e0a68b7b9383 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -18,10 +18,13 @@
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/ADT/iterator_range.h"
+#include "llvm/IR/DataLayout.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/PassManager.h"
 
 namespace llvm {
 
@@ -225,8 +228,75 @@ void RAUW(DIAssignID *Old, DIAssignID *New);
 /// Remove all Assignment Tracking related intrinsics and metadata from \p F.
 void deleteAll(Function *F);
 
+/// Helper struct for trackAssignments, below. We don't use the similar
+/// DebugVariable class because trackAssignments doesn't (yet?) understand
+/// partial variables (fragment info) as input and want to make that clear and
+/// explicit using types. In addition, eventually we will want to understand
+/// expressions that modify the base address too, which a DebugVariable doesn't
+/// capture.
+struct VarRecord {
+  DILocalVariable *Var;
+  DILocation *DL;
+
+  VarRecord(DbgVariableIntrinsic *DVI)
+      : Var(DVI->getVariable()), DL(getDebugValueLoc(DVI)) {}
+  VarRecord(DILocalVariable *Var, DILocation *DL) : Var(Var), DL(DL) {}
+  friend bool operator<(const VarRecord &LHS, const VarRecord &RHS) {
+    return std::tie(LHS.Var, LHS.DL) < std::tie(RHS.Var, RHS.DL);
+  }
+  friend bool operator==(const VarRecord &LHS, const VarRecord &RHS) {
+    return std::tie(LHS.Var, LHS.DL) == std::tie(RHS.Var, RHS.DL);
+  }
+};
+
+/// Map of backing storage to a set of variables that are stored to it.
+/// TODO: Backing storage shouldn't be limited to allocas only. Some local
+/// variables have their storage allocated by the calling function (addresses
+/// passed in with sret & byval parameters).
+using StorageToVarsMap = DenseMap<const AllocaInst *, SmallSet<VarRecord, 2>>;
+
+/// Track assignments to \p Vars between \p Start and \p End.
+
+void trackAssignments(Function::iterator Start, Function::iterator End,
+                      const StorageToVarsMap &Vars, const DataLayout &DL,
+                      bool DebugPrints = false);
+
+/// Describes properties of a store that has a static size and offset into a
+/// some base storage. Used by the getAssignmentInfo functions.
+struct AssignmentInfo {
+  AllocaInst const *Base;  ///< Base storage.
+  uint64_t OffsetInBits;   ///< Offset into Base.
+  uint64_t SizeInBits;     ///< Number of bits stored.
+  bool StoreToWholeAlloca; ///< SizeInBits equals the size of the base storage.
+
+  AssignmentInfo(const DataLayout &DL, AllocaInst const *Base,
+                 uint64_t OffsetInBits, uint64_t SizeInBits)
+      : Base(Base), OffsetInBits(OffsetInBits), SizeInBits(SizeInBits),
+        StoreToWholeAlloca(
+            OffsetInBits == 0 &&
+            SizeInBits == DL.getTypeSizeInBits(Base->getAllocatedType())) {}
+};
+
+Optional<AssignmentInfo> getAssignmentInfo(const DataLayout &DL,
+                                           const MemIntrinsic *I);
+Optional<AssignmentInfo> getAssignmentInfo(const DataLayout &DL,
+                                           const StoreInst *SI);
+Optional<AssignmentInfo> getAssignmentInfo(const DataLayout &DL,
+                                           const AllocaInst *AI);
+
 } // end namespace at
 
+/// Convert @llvm.dbg.declare intrinsics into sets of @llvm.dbg.assign
+/// intrinsics by treating stores to the dbg.declare'd address as assignments
+/// to the variable. Not all kinds of variables are supported yet; those will
+/// be left with their dbg.declare intrinsics.
+class AssignmentTrackingPass : public PassInfoMixin<AssignmentTrackingPass> {
+public:
+  void runOnFunction(Function &F);
+  PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
 /// Return true if assignment tracking is enabled.
 bool getEnableAssignmentTracking();
 } // end namespace llvm

diff  --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 76d7ade09a88c..989b55463b4dc 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -30,7 +30,7 @@ static cl::opt<bool>
 
 DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU)
     : M(m), VMContext(M.getContext()), CUNode(CU), DeclareFn(nullptr),
-      ValueFn(nullptr), LabelFn(nullptr), AddrFn(nullptr),
+      ValueFn(nullptr), LabelFn(nullptr), AddrFn(nullptr), AssignFn(nullptr),
       AllowUnresolvedNodes(AllowUnresolvedNodes) {
   if (CUNode) {
     if (const auto &ETs = CUNode->getEnumTypes())
@@ -960,6 +960,36 @@ Instruction *DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo,
   return insertDeclare(Storage, VarInfo, Expr, DL, InsertAtEnd, InsertBefore);
 }
 
+DbgAssignIntrinsic *
+DIBuilder::insertDbgAssign(Instruction *LinkedInstr, Value *Val,
+                           DILocalVariable *SrcVar, DIExpression *ValExpr,
+                           Value *Addr, DIExpression *AddrExpr,
+                           const DILocation *DL) {
+  LLVMContext &Ctx = LinkedInstr->getContext();
+  Module *M = LinkedInstr->getModule();
+  if (!AssignFn)
+    AssignFn = Intrinsic::getDeclaration(M, Intrinsic::dbg_assign);
+
+  auto *Link = LinkedInstr->getMetadata(LLVMContext::MD_DIAssignID);
+  assert(Link && "Linked instruction must have DIAssign metadata attached");
+
+  std::array<Value *, 6> Args = {
+      MetadataAsValue::get(Ctx, ValueAsMetadata::get(Val)),
+      MetadataAsValue::get(Ctx, SrcVar),
+      MetadataAsValue::get(Ctx, ValExpr),
+      MetadataAsValue::get(Ctx, Link),
+      MetadataAsValue::get(Ctx, ValueAsMetadata::get(Addr)),
+      MetadataAsValue::get(Ctx, AddrExpr),
+  };
+
+  IRBuilder<> B(Ctx);
+  B.SetCurrentDebugLocation(DL);
+
+  auto *DVI = cast<DbgAssignIntrinsic>(B.CreateCall(AssignFn, Args));
+  DVI->insertAfter(LinkedInstr);
+  return DVI;
+}
+
 Instruction *DIBuilder::insertLabel(DILabel *LabelInfo, const DILocation *DL,
                                     Instruction *InsertBefore) {
   return insertLabel(LabelInfo, DL,

diff  --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 64d606ec15a60..8c9ef9839eb1d 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -32,6 +32,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
 #include "llvm/Support/Casting.h"
 #include <algorithm>
 #include <cassert>
@@ -1713,3 +1714,237 @@ void at::deleteAll(Function *F) {
   for (auto *DAI : ToDelete)
     DAI->eraseFromParent();
 }
+
+/// Collect constant properies (base, size, offset) of \p StoreDest.
+/// Return None if any properties are not constants.
+static Optional<AssignmentInfo> getAssignmentInfoImpl(const DataLayout &DL,
+                                                      const Value *StoreDest,
+                                                      uint64_t SizeInBits) {
+  APInt GEPOffset(DL.getIndexTypeSizeInBits(StoreDest->getType()), 0);
+  const Value *Base = StoreDest->stripAndAccumulateConstantOffsets(
+      DL, GEPOffset, /*AllowNonInbounds*/ true);
+  uint64_t OffsetInBytes = GEPOffset.getLimitedValue();
+  // Check for overflow.
+  if (OffsetInBytes == UINT64_MAX)
+    return None;
+  if (const auto *Alloca = dyn_cast<AllocaInst>(Base))
+    return AssignmentInfo(DL, Alloca, OffsetInBytes * 8, SizeInBits);
+  return None;
+}
+
+Optional<AssignmentInfo> at::getAssignmentInfo(const DataLayout &DL,
+                                               const MemIntrinsic *I) {
+  const Value *StoreDest = I->getRawDest();
+  // Assume 8 bit bytes.
+  auto *ConstLengthInBytes = dyn_cast<ConstantInt>(I->getLength());
+  if (!ConstLengthInBytes)
+    // We can't use a non-const size, bail.
+    return None;
+  uint64_t SizeInBits = 8 * ConstLengthInBytes->getZExtValue();
+  return getAssignmentInfoImpl(DL, StoreDest, SizeInBits);
+}
+
+Optional<AssignmentInfo> at::getAssignmentInfo(const DataLayout &DL,
+                                               const StoreInst *SI) {
+  const Value *StoreDest = SI->getPointerOperand();
+  uint64_t SizeInBits = DL.getTypeSizeInBits(SI->getValueOperand()->getType());
+  return getAssignmentInfoImpl(DL, StoreDest, SizeInBits);
+}
+
+Optional<AssignmentInfo> at::getAssignmentInfo(const DataLayout &DL,
+                                               const AllocaInst *AI) {
+  uint64_t SizeInBits = DL.getTypeSizeInBits(AI->getAllocatedType());
+  return getAssignmentInfoImpl(DL, AI, SizeInBits);
+}
+
+static CallInst *emitDbgAssign(AssignmentInfo Info, Value *Val, Value *Dest,
+                               Instruction &StoreLikeInst,
+                               const VarRecord &VarRec, DIBuilder &DIB) {
+  auto *ID = StoreLikeInst.getMetadata(LLVMContext::MD_DIAssignID);
+  assert(ID && "Store instruction must have DIAssignID metadata");
+
+  DIExpression *Expr = DIExpression::get(StoreLikeInst.getContext(), None);
+  if (!Info.StoreToWholeAlloca) {
+    auto R = DIExpression::createFragmentExpression(Expr, Info.OffsetInBits,
+                                                    Info.SizeInBits);
+    assert(R.has_value() && "failed to create fragment expression");
+    Expr = R.value();
+  }
+  DIExpression *AddrExpr = DIExpression::get(StoreLikeInst.getContext(), None);
+  return DIB.insertDbgAssign(&StoreLikeInst, Val, VarRec.Var, Expr, Dest,
+                             AddrExpr, VarRec.DL);
+}
+
+#undef DEBUG_TYPE // Silence redefinition warning (from ConstantsContext.h).
+#define DEBUG_TYPE "assignment-tracking"
+
+void at::trackAssignments(Function::iterator Start, Function::iterator End,
+                          const StorageToVarsMap &Vars, const DataLayout &DL,
+                          bool DebugPrints) {
+  // Early-exit if there are no interesting variables.
+  if (Vars.empty())
+    return;
+
+  auto &Ctx = Start->getContext();
+  auto &Module = *Start->getModule();
+
+  // Undef type doesn't matter, so long as it isn't void. Let's just use i1.
+  auto *Undef = UndefValue::get(Type::getInt1Ty(Ctx));
+  DIBuilder DIB(Module, /*AllowUnresolved*/ false);
+
+  // Scan the instructions looking for stores to local variables' storage.
+  LLVM_DEBUG(errs() << "# Scanning instructions\n");
+  for (auto BBI = Start; BBI != End; ++BBI) {
+    for (Instruction &I : *BBI) {
+
+      Optional<AssignmentInfo> Info = None;
+      Value *ValueComponent = nullptr;
+      Value *DestComponent = nullptr;
+      if (auto *AI = dyn_cast<AllocaInst>(&I)) {
+        // We want to track the variable's stack home from its alloca's
+        // position onwards so we treat it as an assignment (where the stored
+        // value is Undef).
+        Info = getAssignmentInfo(DL, AI);
+        ValueComponent = Undef;
+        DestComponent = AI;
+      } else if (auto *SI = dyn_cast<StoreInst>(&I)) {
+        Info = getAssignmentInfo(DL, SI);
+        ValueComponent = SI->getValueOperand();
+        DestComponent = SI->getPointerOperand();
+      } else if (auto *MI = dyn_cast<MemTransferInst>(&I)) {
+        Info = getAssignmentInfo(DL, MI);
+        // May not be able to represent this value easily.
+        ValueComponent = Undef;
+        DestComponent = MI->getOperand(0);
+      } else if (auto *MI = dyn_cast<MemSetInst>(&I)) {
+        Info = getAssignmentInfo(DL, MI);
+        // If we're zero-initing we can state the assigned value is zero,
+        // otherwise use undef.
+        auto *ConstValue = dyn_cast<ConstantInt>(MI->getOperand(1));
+        if (ConstValue && ConstValue->isZero())
+          ValueComponent = ConstValue;
+        else
+          ValueComponent = Undef;
+        DestComponent = MI->getOperand(0);
+      } else {
+        // Not a store-like instruction.
+        continue;
+      }
+
+      assert(ValueComponent && DestComponent);
+      LLVM_DEBUG(errs() << "SCAN: Found store-like: " << I << "\n");
+
+      // Check if getAssignmentInfo failed to understand this store.
+      if (!Info.has_value()) {
+        LLVM_DEBUG(
+            errs()
+            << " | SKIP: Untrackable store (e.g. through non-const gep)\n");
+        continue;
+      }
+      LLVM_DEBUG(errs() << " | BASE: " << *Info->Base << "\n");
+
+      //  Check if the store destination is a local variable with debug info.
+      auto LocalIt = Vars.find(Info->Base);
+      if (LocalIt == Vars.end()) {
+        LLVM_DEBUG(
+            errs()
+            << " | SKIP: Base address not associated with local variable\n");
+        continue;
+      }
+
+      DIAssignID *ID =
+          cast_or_null<DIAssignID>(I.getMetadata(LLVMContext::MD_DIAssignID));
+      if (!ID) {
+        ID = DIAssignID::getDistinct(Ctx);
+        I.setMetadata(LLVMContext::MD_DIAssignID, ID);
+      }
+
+      for (const VarRecord &R : LocalIt->second) {
+        auto *Assign =
+            emitDbgAssign(*Info, ValueComponent, DestComponent, I, R, DIB);
+        (void)Assign;
+        LLVM_DEBUG(errs() << " > INSERT: " << *Assign << "\n");
+      }
+    }
+  }
+}
+
+void AssignmentTrackingPass::runOnFunction(Function &F) {
+  // Collect a map of {backing storage : dbg.declares} (currently "backing
+  // storage" is limited to Allocas). We'll use this to find dbg.declares to
+  // delete after running `trackAssignments`.
+  DenseMap<const AllocaInst *, SmallPtrSet<DbgDeclareInst *, 2>> DbgDeclares;
+  // Create another similar map of {storage : variables} that we'll pass to
+  // trackAssignments.
+  StorageToVarsMap Vars;
+  for (auto &BB : F) {
+    for (auto &I : BB) {
+      DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(&I);
+      if (!DDI)
+        continue;
+      // FIXME: trackAssignments doesn't let you specify any modifiers to the
+      // variable (e.g. fragment) or location (e.g. offset), so we have to
+      // leave dbg.declares with non-empty expressions in place.
+      if (DDI->getExpression()->getNumElements() != 0)
+        continue;
+      if (AllocaInst *Alloca =
+              dyn_cast<AllocaInst>(DDI->getAddress()->stripPointerCasts())) {
+        DbgDeclares[Alloca].insert(DDI);
+        Vars[Alloca].insert(VarRecord(DDI));
+      }
+    }
+  }
+
+  auto DL = std::make_unique<DataLayout>(F.getParent());
+  // FIXME: Locals can be backed by caller allocas (sret, byval).
+  // Note: trackAssignments doesn't respect dbg.declare's IR positions (as it
+  // doesn't "understand" dbg.declares). However, this doesn't appear to break
+  // any rules given this description of dbg.declare from
+  // llvm/docs/SourceLevelDebugging.rst:
+  //
+  //   It is not control-dependent, meaning that if a call to llvm.dbg.declare
+  //   exists and has a valid location argument, that address is considered to
+  //   be the true home of the variable across its entire lifetime.
+  trackAssignments(F.begin(), F.end(), Vars, *DL);
+
+  // Delete dbg.declares for variables now tracked with assignment tracking.
+  for (auto &P : DbgDeclares) {
+    const AllocaInst *Alloca = P.first;
+    auto Markers = at::getAssignmentMarkers(Alloca);
+    for (DbgDeclareInst *DDI : P.second) {
+      // Assert that the alloca that DDI uses is now linked to a dbg.assign
+      // describing the same variable (i.e. check that this dbg.declare
+      // has been replaced by a dbg.assign).
+      assert(std::find_if(Markers.begin(), Markers.end(),
+                          [DDI](DbgAssignIntrinsic *DAI) {
+                            return DebugVariable(DAI) == DebugVariable(DDI);
+                          }) != Markers.end());
+      // Delete DDI because the variable location is now tracked using
+      // assignment tracking.
+      DDI->eraseFromParent();
+    }
+  }
+}
+
+PreservedAnalyses AssignmentTrackingPass::run(Function &F,
+                                              FunctionAnalysisManager &AM) {
+  runOnFunction(F);
+  // Q: Can we return a less conservative set than just CFGAnalyses? Can we
+  // return PreservedAnalyses::all()?
+  PreservedAnalyses PA;
+  PA.preserveSet<CFGAnalyses>();
+  return PA;
+}
+
+PreservedAnalyses AssignmentTrackingPass::run(Module &M,
+                                              ModuleAnalysisManager &AM) {
+  for (auto &F : M)
+    runOnFunction(F);
+  // Q: Can we return a less conservative set than just CFGAnalyses? Can we
+  // return PreservedAnalyses::all()?
+  PreservedAnalyses PA;
+  PA.preserveSet<CFGAnalyses>();
+  return PA;
+}
+
+#undef DEBUG_TYPE

diff  --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 46e8b52ac7e86..b84f0db188da6 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -73,6 +73,7 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/Analysis/TypeBasedAliasAnalysis.h"
+#include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/IRPrintingPasses.h"
 #include "llvm/IR/PassManager.h"

diff  --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index e7a65ae7b7000..7d8656ee41d7f 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -394,6 +394,7 @@ FUNCTION_PASS("tlshoist", TLSVariableHoistPass())
 FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
 FUNCTION_PASS("tsan", ThreadSanitizerPass())
 FUNCTION_PASS("memprof", MemProfilerPass())
+FUNCTION_PASS("declare-to-assign", llvm::AssignmentTrackingPass())
 #undef FUNCTION_PASS
 
 #ifndef FUNCTION_PASS_WITH_PARAMS

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll
new file mode 100644
index 0000000000000..2dafda04a8247
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll
@@ -0,0 +1,505 @@
+; RUN: opt -passes='declare-to-assign,verify' %s -S -o - -experimental-assignment-tracking \
+; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg"
+
+;; This test checks that `trackAssignments` is working correctly by using the
+;; pass-wrapper `declare-to-assign`. Each function checks some specific
+;; functionality and has its checks inline.
+
+;; $ cat test.cpp
+;; struct Inner { int A, B; };
+;; struct Outer { Inner A, B; };
+;; struct Large { int A[10]; };
+;; struct LCopyCtor { int A[4]; LCopyCtor(); LCopyCtor(LCopyCtor const &); };
+;; int Value, Index, Cond;
+;; Inner InnerA, InnerB;
+;; Large L;
+;;
+;; void zeroInit() { int Z[3] = {0, 0, 0}; }
+;;
+;; void memcpyInit() { int A[4] = {0, 1, 2, 3}; }
+;;
+;; void setField() {
+;;   Outer O;
+;;   O.A.B = Value;
+;; }
+;;
+;; void unknownOffset() {
+;;   int A[2];
+;;   A[Index] = Value;
+;; }
+;;
+;; Inner sharedAlloca() {
+;;   if (Cond) {
+;;     Inner A = InnerA;
+;;     return A;
+;;   } else {
+;;     Inner B = InnerB;
+;;     return B;
+;;   }
+;; }
+;;
+;; Large sret() {
+;;   Large X = L;
+;;   return X;
+;; }
+;;
+;; void byval(Large X) {}
+;;
+;; LCopyCtor indirectReturn() {
+;;  LCopyCtor R;
+;;  return R;
+;; }
+;;
+;; $ clang++ -g test.cpp -o - -emit-llvm -S -Xclang -disable-llvm-passes -O2
+;;
+;; Then these functions are added manually to the IR using
+;;     $ clang++ -g test.cpp -o - -emit-llvm -S -O0
+;; and removing the optnone attributes:
+;;
+;; __attribute__((always_inline))
+;; int sqr(int Y) { return Y * Y;  }
+;; int fun(int X) { return sqr(X); }
+
+
+source_filename = "test.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.Inner = type { i32, i32 }
+%struct.Large = type { [10 x i32] }
+%struct.Outer = type { %struct.Inner, %struct.Inner }
+%struct.LCopyCtor = type { [4 x i32] }
+
+ at Value = dso_local global i32 0, align 4, !dbg !0
+ at Index = dso_local global i32 0, align 4, !dbg !5
+ at Cond = dso_local global i32 0, align 4, !dbg !8
+ at InnerA = dso_local global %struct.Inner zeroinitializer, align 4, !dbg !10
+ at InnerB = dso_local global %struct.Inner zeroinitializer, align 4, !dbg !16
+ at L = dso_local global %struct.Large zeroinitializer, align 4, !dbg !18
+ at __const._Z10memcpyInitv.A = private unnamed_addr constant [4 x i32] [i32 0, i32 1, i32 2, i32 3], align 16
+
+;; Zero init with a memset.
+;;
+;;    void zeroInit() { int Z[3] = {0, 0, 0}; }
+;;
+;; Check that we get two dbg.assign intrinsics. The first linked to the alloca
+;; and the second linked to the zero-init-memset, which should have a constant 0
+;; for the value component.
+define dso_local void @_Z8zeroInitv() #0 !dbg !31 {
+; CHECK-LABEL: define dso_local void @_Z8zeroInitv
+entry:
+  %Z = alloca [3 x i32], align 4
+; CHECK:      %Z = alloca [3 x i32], align 4, !DIAssignID ![[ID_0:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_0:[0-9]+]], metadata !DIExpression(), metadata ![[ID_0]], metadata [3 x i32]* %Z, metadata !DIExpression())
+  %0 = bitcast [3 x i32]* %Z to i8*, !dbg !39
+  call void @llvm.lifetime.start.p0i8(i64 12, i8* %0) #5, !dbg !39
+  call void @llvm.dbg.declare(metadata [3 x i32]* %Z, metadata !35, metadata !DIExpression()), !dbg !40
+  %1 = bitcast [3 x i32]* %Z to i8*, !dbg !40
+  call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 0, i64 12, i1 false), !dbg !40
+; CHECK:       @llvm.memset.p0i8.i64{{.*}}, !DIAssignID ![[ID_1:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR_0]], metadata !DIExpression(), metadata ![[ID_1]], metadata i8* %1, metadata !DIExpression())
+  %2 = bitcast [3 x i32]* %Z to i8*, !dbg !41
+  call void @llvm.lifetime.end.p0i8(i64 12, i8* %2) #5, !dbg !41
+  ret void, !dbg !41
+}
+
+;; Init with a memcpy (from a global).
+;;
+;;    void memcpyInit() { int A[4] = {0, 1, 2, 3}; }
+;;
+;; Check that we get two dbg.assign intrinsics. The first linked to the alloca
+;; and the second linked to the initialising memcpy, which should have an Undef
+;; value component.
+define dso_local void @_Z10memcpyInitv() #0 !dbg !42 {
+; CHECK-LABEL: define dso_local void @_Z10memcpyInitv
+entry:
+  %A = alloca [4 x i32], align 16
+; CHECK:      %A = alloca [4 x i32], align 16, !DIAssignID ![[ID_2:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_1:[0-9]+]], metadata !DIExpression(), metadata ![[ID_2]], metadata [4 x i32]* %A, metadata !DIExpression())
+  %0 = bitcast [4 x i32]* %A to i8*, !dbg !48
+  call void @llvm.lifetime.start.p0i8(i64 16, i8* %0) #5, !dbg !48
+  call void @llvm.dbg.declare(metadata [4 x i32]* %A, metadata !44, metadata !DIExpression()), !dbg !49
+  %1 = bitcast [4 x i32]* %A to i8*, !dbg !49
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %1, i8* align 16 bitcast ([4 x i32]* @__const._Z10memcpyInitv.A to i8*), i64 16, i1 false), !dbg !49
+; CHECK:       @llvm.memcpy.p0i8.p0i8.i64{{.*}}, !DIAssignID ![[ID_3:[0-9]+]]
+; CHECK-NEXT:  call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_1]], metadata !DIExpression(), metadata ![[ID_3]], metadata i8* %1, metadata !DIExpression())
+  %2 = bitcast [4 x i32]* %A to i8*, !dbg !50
+  call void @llvm.lifetime.end.p0i8(i64 16, i8* %2) #5, !dbg !50
+  ret void, !dbg !50
+}
+
+;; Assign to field of local variable.
+;;
+;;    void setField() {
+;;      Outer O;
+;;      O.A.B = Value;
+;;    }
+;;
+;; Check that we get two dbg.assign intrinsics. The first linked to the alloca
+;; and the second linked to the store to O.A.B, which should include the
+;; appropriate fragment info (SizeInBits: 32, OffsetInBits: 32) with respect to
+;; the base variable.
+define dso_local void @_Z8setFieldv() #0 !dbg !51 {
+; CHECK-LABEL: define dso_local void @_Z8setFieldv
+entry:
+  %O = alloca %struct.Outer, align 4
+; CHECK:      %O = alloca %struct.Outer, align 4, !DIAssignID ![[ID_4:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_2:[0-9]+]], metadata !DIExpression(), metadata ![[ID_4]], metadata %struct.Outer* %O, metadata !DIExpression())
+  %0 = bitcast %struct.Outer* %O to i8*, !dbg !58
+  call void @llvm.lifetime.start.p0i8(i64 16, i8* %0) #5, !dbg !58
+  call void @llvm.dbg.declare(metadata %struct.Outer* %O, metadata !53, metadata !DIExpression()), !dbg !59
+  %1 = load i32, i32* @Value, align 4, !dbg !60, !tbaa !61
+  %A = getelementptr inbounds %struct.Outer, %struct.Outer* %O, i32 0, i32 0, !dbg !65
+  %B = getelementptr inbounds %struct.Inner, %struct.Inner* %A, i32 0, i32 1, !dbg !66
+  store i32 %1, i32* %B, align 4, !dbg !67, !tbaa !68
+; CHECK:      store i32 %1, i32* %B, align 4,{{.*}}!DIAssignID ![[ID_5:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %1, metadata ![[VAR_2]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata ![[ID_5]], metadata i32* %B, metadata !DIExpression())
+  %2 = bitcast %struct.Outer* %O to i8*, !dbg !71
+  call void @llvm.lifetime.end.p0i8(i64 16, i8* %2) #5, !dbg !71
+  ret void, !dbg !71
+}
+
+;; Assign to (statically) unknown offset into a local variable.
+;;
+;;   void unknownOffset() {
+;;     int A[2];
+;;     A[Index] = Value;
+;;   }
+;;
+;; Check that we get only one dbg.assign intrinsic and that it is linked to the
+;; alloca. The assignment doesn't get a dbg.assign intrinsic because we cannot
+;; encode the fragment info for the assignment.
+;; Note: This doesn't mean we lose the assignment entirely: the backend will
+;; interpret the store (as it sees it) as an assignment.
+define dso_local void @_Z13unknownOffsetv() #0 !dbg !72 {
+; CHECK-LABEL: define dso_local void @_Z13unknownOffsetv
+entry:
+  %A = alloca [2 x i32], align 4
+; CHECK:      %A = alloca [2 x i32], align 4, !DIAssignID ![[ID_6:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_3:[0-9]+]], metadata !DIExpression(), metadata ![[ID_6]], metadata [2 x i32]* %A, metadata !DIExpression())
+  %0 = bitcast [2 x i32]* %A to i8*, !dbg !78
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* %0) #5, !dbg !78
+  call void @llvm.dbg.declare(metadata [2 x i32]* %A, metadata !74, metadata !DIExpression()), !dbg !79
+  %1 = load i32, i32* @Value, align 4, !dbg !80, !tbaa !61
+  %2 = load i32, i32* @Index, align 4, !dbg !81, !tbaa !61
+  %idxprom = sext i32 %2 to i64, !dbg !82
+  %arrayidx = getelementptr inbounds [2 x i32], [2 x i32]* %A, i64 0, i64 %idxprom, !dbg !82
+  store i32 %1, i32* %arrayidx, align 4, !dbg !83, !tbaa !61
+  %3 = bitcast [2 x i32]* %A to i8*, !dbg !84
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* %3) #5, !dbg !84
+  ret void, !dbg !84
+}
+
+;; Assignments to variables which share the same backing storage.
+;;
+;; Inner sharedAlloca() {
+;;   if (Cond) {
+;;     Inner A = InnerA;
+;;     return A;
+;;   } else {
+;;     Inner B = InnerB;
+;;     return B;
+;;   }
+;; }
+;;
+define dso_local i64 @_Z12sharedAllocav() #0 !dbg !85 {
+; CHECK-LABEL: define dso_local i64 @_Z12sharedAllocav
+entry:
+  %retval = alloca %struct.Inner, align 4
+; CHECK:      %retval = alloca %struct.Inner, align 4, !DIAssignID ![[ID_7:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4:[0-9]+]], metadata !DIExpression(), metadata ![[ID_7]], metadata %struct.Inner* %retval, metadata !DIExpression())
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5:[0-9]+]], metadata !DIExpression(), metadata ![[ID_7]], metadata %struct.Inner* %retval, metadata !DIExpression())
+  %0 = load i32, i32* @Cond, align 4, !dbg !94, !tbaa !61
+  %tobool = icmp ne i32 %0, 0, !dbg !94
+  br i1 %tobool, label %if.then, label %if.else, !dbg !95
+
+if.then:                                          ; preds = %entry
+; CHECK:      if.then:
+  call void @llvm.dbg.declare(metadata %struct.Inner* %retval, metadata !89, metadata !DIExpression()), !dbg !96
+  %1 = bitcast %struct.Inner* %retval to i8*, !dbg !97
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%struct.Inner* @InnerA to i8*), i64 8, i1 false), !dbg !97, !tbaa.struct !98
+; CHECK:      call void @llvm.memcpy{{.*}}, !DIAssignID ![[ID_8:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4]], metadata !DIExpression(), metadata ![[ID_8]], metadata i8* %1, metadata !DIExpression())
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5]], metadata !DIExpression(), metadata ![[ID_8]], metadata i8* %1, metadata !DIExpression())
+  br label %return, !dbg !99
+
+if.else:                                          ; preds = %entry
+; CHECK:      if.else:
+  call void @llvm.dbg.declare(metadata %struct.Inner* %retval, metadata !92, metadata !DIExpression()), !dbg !100
+  %2 = bitcast %struct.Inner* %retval to i8*, !dbg !101
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 bitcast (%struct.Inner* @InnerB to i8*), i64 8, i1 false), !dbg !101, !tbaa.struct !98
+; CHECK:      call void @llvm.memcpy{{.*}}, !DIAssignID ![[ID_9:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4]], metadata !DIExpression(), metadata ![[ID_9]], metadata i8* %2, metadata !DIExpression())
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5]], metadata !DIExpression(), metadata ![[ID_9]], metadata i8* %2, metadata !DIExpression())
+  br label %return, !dbg !102
+
+return:                                           ; preds = %if.else, %if.then
+  %3 = bitcast %struct.Inner* %retval to i64*, !dbg !103
+  %4 = load i64, i64* %3, align 4, !dbg !103
+  ret i64 %4, !dbg !103
+}
+
+;; Caller-allocated memory for a local (sret parameter).
+;;
+;;    Large sret() {
+;;      Large X = L;
+;;      return X;
+;;    }
+;;
+;; TODO: Currently not supported by `trackAssignments` (or the rest of the
+;; assignment tracking pipeline). In lieu of being able to xfail a part of a
+;; test, check that the dbg.declare is preserved so the test fails (and can be
+;; updated) when this is fixed.
+define dso_local void @_Z4sretv(%struct.Large* noalias sret(%struct.Large) align 4 %agg.result) #0 !dbg !104 {
+; CHECK-LABEL: define dso_local void @_Z4sretv
+entry:
+; CHECK: call void @llvm.dbg.declare
+  call void @llvm.dbg.declare(metadata %struct.Large* %agg.result, metadata !108, metadata !DIExpression()), !dbg !109
+  %0 = bitcast %struct.Large* %agg.result to i8*, !dbg !110
+  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.Large* @L to i8*), i64 40, i1 false), !dbg !110, !tbaa.struct !111
+  ret void, !dbg !113
+}
+
+;; Caller-allocated memory for a local (byval parameter).
+;;
+;;    void byval(Large X) {}
+;;
+;; TODO: See comment for sret parameters above.
+define dso_local void @_Z5byval5Large(%struct.Large* noundef byval(%struct.Large) align 8 %X) #0 !dbg !114 {
+; CHECK-LABEL: define dso_local void @_Z5byval5Large
+entry:
+; CHECK: llvm.dbg.declare
+  call void @llvm.dbg.declare(metadata %struct.Large* %X, metadata !118, metadata !DIExpression()), !dbg !119
+  ret void, !dbg !120
+}
+
+;; Caller-allocated memory for a local (sret parameter) with address stored to
+;; local alloca.
+;;
+;;    LCopyCtor indirectReturn() {
+;;      LCopyCtor R;
+;;      return R;
+;;    }
+;;
+;; A sret parameter is used here also, but in this case clang emits an alloca
+;; to store the passed-in address for the storage for the variable. The
+;; dbg.declare for the local R therefore requires a DW_OP_deref expression.
+;; TODO: This isn't supported yet, so check the dbg.declare remains.
+define dso_local void @_Z14indirectReturnv(%struct.LCopyCtor* noalias sret(%struct.LCopyCtor) align 4 %agg.result) #0 !dbg !121 {
+; CHECK-LABEL: define dso_local void @_Z14indirectReturnv
+entry:
+  %result.ptr = alloca i8*, align 8
+  %0 = bitcast %struct.LCopyCtor* %agg.result to i8*
+  store i8* %0, i8** %result.ptr, align 8
+  call void @llvm.dbg.declare(metadata i8** %result.ptr, metadata !126, metadata !DIExpression(DW_OP_deref)), !dbg !127
+; CHECK: call void @llvm.dbg.declare
+  call void @_ZN9LCopyCtorC1Ev(%struct.LCopyCtor* noundef nonnull align 4 dereferenceable(16) %agg.result), !dbg !127
+  ret void, !dbg !128
+}
+
+;; Inlined variable.
+;;
+;;    __attribute__((always_inline))
+;;    int sqr(int Y) { return Y * Y;  }
+;;    int fun(int X) { return sqr(X); }
+;;
+;; Check that dbg.assign intrinsics correctly inherit the !dbg attachment from
+;; the dbg.declre.
+define dso_local noundef i32 @_Z3funi(i32 noundef %X) !dbg !139 {
+; CHECK-LABEL: define dso_local noundef i32 @_Z3funi
+entry:
+  %Y.addr.i = alloca i32, align 4
+; CHECK:      %Y.addr.i = alloca i32, align 4, !DIAssignID ![[ID_10:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_6:[0-9]+]], metadata !DIExpression(), metadata ![[ID_10]], metadata i32* %Y.addr.i, metadata !DIExpression()), !dbg ![[DBG_0:[0-9]+]]
+  %X.addr = alloca i32, align 4
+; CHECK-NEXT: %X.addr = alloca i32, align 4, !DIAssignID ![[ID_11:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_7:[0-9]+]], metadata !DIExpression(), metadata ![[ID_11]], metadata i32* %X.addr, metadata !DIExpression()), !dbg ![[DBG_1:[0-9]+]]
+  store i32 %X, i32* %X.addr, align 4
+; CHECK-NEXT: store i32 %X, i32* %X.addr, align 4, !DIAssignID ![[ID_12:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %X, metadata ![[VAR_7]], metadata !DIExpression(), metadata ![[ID_12]], metadata i32* %X.addr, metadata !DIExpression()), !dbg ![[DBG_1]]
+  call void @llvm.dbg.declare(metadata i32* %X.addr, metadata !140, metadata !DIExpression()), !dbg !141
+  %0 = load i32, i32* %X.addr, align 4, !dbg !142
+  store i32 %0, i32* %Y.addr.i, align 4
+; CHECK:      store i32 %0, i32* %Y.addr.i, align 4, !DIAssignID ![[ID_13:[0-9]+]]
+; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %0, metadata ![[VAR_6]], metadata !DIExpression(), metadata ![[ID_13]], metadata i32* %Y.addr.i, metadata !DIExpression()), !dbg ![[DBG_0]]
+  call void @llvm.dbg.declare(metadata i32* %Y.addr.i, metadata !133, metadata !DIExpression()), !dbg !143
+  %1 = load i32, i32* %Y.addr.i, align 4, !dbg !145
+  %2 = load i32, i32* %Y.addr.i, align 4, !dbg !146
+  %mul.i = mul nsw i32 %1, %2, !dbg !147
+  ret i32 %mul.i, !dbg !148
+}
+
+declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
+declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #3
+declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #4
+declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1
+declare dso_local void @_ZN9LCopyCtorC1Ev(%struct.LCopyCtor* noundef nonnull align 4 dereferenceable(16)) unnamed_addr
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!26, !27, !28, !29}
+!llvm.ident = !{!30}
+
+; CHECK-DAG: ![[VAR_0]] = !DILocalVariable(name: "Z",
+; CHECK-DAG: ![[VAR_1]] = !DILocalVariable(name: "A",
+; CHECK-DAG: ![[VAR_2]] = !DILocalVariable(name: "O",
+; CHECK-DAG: ![[VAR_3]] = !DILocalVariable(name: "A",
+; CHECK-DAG: ![[VAR_4]] = !DILocalVariable(name: "B",
+; CHECK-DAG: ![[VAR_5]] = !DILocalVariable(name: "A",
+; CHECK-DAG: ![[VAR_6]] = !DILocalVariable(name: "Y", arg: 1, scope: ![[SQR:[0-9]+]],
+; CHECK-DAG: ![[VAR_7]] = !DILocalVariable(name: "X", arg: 1, scope: ![[FUN:[0-9]+]],
+; CHECK-DAG: ![[SQR]] = distinct !DISubprogram(name: "sqr",
+; CHECK-DAG: ![[FUN]] = distinct !DISubprogram(name: "fun",
+; CHECK-DAG: ![[DBG_0]] = !DILocation(line: 0, scope: ![[SQR]], inlinedAt: ![[SQR_INLINE_SITE:[0-9]+]])
+; CHECK-DAG: [[SQR_INLINE_SITE]] = distinct !DILocation(line: 3, column: 25, scope: ![[FUN]])
+; CHECK-DAG: ![[DBG_1]] = !DILocation(line: 0, scope: ![[FUN]])
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "Value", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.cpp", directory: "/")
+!4 = !{!0, !5, !8, !10, !16, !18}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "Index", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression())
+!9 = distinct !DIGlobalVariable(name: "Cond", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
+!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
+!11 = distinct !DIGlobalVariable(name: "InnerA", scope: !2, file: !3, line: 6, type: !12, isLocal: false, isDefinition: true)
+!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Inner", file: !3, line: 1, size: 64, flags: DIFlagTypePassByValue, elements: !13, identifier: "_ZTS5Inner")
+!13 = !{!14, !15}
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !12, file: !3, line: 1, baseType: !7, size: 32)
+!15 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !12, file: !3, line: 1, baseType: !7, size: 32, offset: 32)
+!16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression())
+!17 = distinct !DIGlobalVariable(name: "InnerB", scope: !2, file: !3, line: 6, type: !12, isLocal: false, isDefinition: true)
+!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression())
+!19 = distinct !DIGlobalVariable(name: "L", scope: !2, file: !3, line: 7, type: !20, isLocal: false, isDefinition: true)
+!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Large", file: !3, line: 3, size: 320, flags: DIFlagTypePassByValue, elements: !21, identifier: "_ZTS5Large")
+!21 = !{!22}
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !20, file: !3, line: 3, baseType: !23, size: 320)
+!23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 320, elements: !24)
+!24 = !{!25}
+!25 = !DISubrange(count: 10)
+!26 = !{i32 7, !"Dwarf Version", i32 5}
+!27 = !{i32 2, !"Debug Info Version", i32 3}
+!28 = !{i32 1, !"wchar_size", i32 4}
+!29 = !{i32 7, !"uwtable", i32 1}
+!30 = !{!"clang version 14.0.0"}
+!31 = distinct !DISubprogram(name: "zeroInit", linkageName: "_Z8zeroInitv", scope: !3, file: !3, line: 9, type: !32, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !34)
+!32 = !DISubroutineType(types: !33)
+!33 = !{null}
+!34 = !{!35}
+!35 = !DILocalVariable(name: "Z", scope: !31, file: !3, line: 9, type: !36)
+!36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 96, elements: !37)
+!37 = !{!38}
+!38 = !DISubrange(count: 3)
+!39 = !DILocation(line: 9, column: 19, scope: !31)
+!40 = !DILocation(line: 9, column: 23, scope: !31)
+!41 = !DILocation(line: 9, column: 41, scope: !31)
+!42 = distinct !DISubprogram(name: "memcpyInit", linkageName: "_Z10memcpyInitv", scope: !3, file: !3, line: 11, type: !32, scopeLine: 11, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !43)
+!43 = !{!44}
+!44 = !DILocalVariable(name: "A", scope: !42, file: !3, line: 11, type: !45)
+!45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 128, elements: !46)
+!46 = !{!47}
+!47 = !DISubrange(count: 4)
+!48 = !DILocation(line: 11, column: 21, scope: !42)
+!49 = !DILocation(line: 11, column: 25, scope: !42)
+!50 = !DILocation(line: 11, column: 46, scope: !42)
+!51 = distinct !DISubprogram(name: "setField", linkageName: "_Z8setFieldv", scope: !3, file: !3, line: 13, type: !32, scopeLine: 13, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !52)
+!52 = !{!53}
+!53 = !DILocalVariable(name: "O", scope: !51, file: !3, line: 14, type: !54)
+!54 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Outer", file: !3, line: 2, size: 128, flags: DIFlagTypePassByValue, elements: !55, identifier: "_ZTS5Outer")
+!55 = !{!56, !57}
+!56 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !54, file: !3, line: 2, baseType: !12, size: 64)
+!57 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !54, file: !3, line: 2, baseType: !12, size: 64, offset: 64)
+!58 = !DILocation(line: 14, column: 3, scope: !51)
+!59 = !DILocation(line: 14, column: 9, scope: !51)
+!60 = !DILocation(line: 15, column: 11, scope: !51)
+!61 = !{!62, !62, i64 0}
+!62 = !{!"int", !63, i64 0}
+!63 = !{!"omnipotent char", !64, i64 0}
+!64 = !{!"Simple C++ TBAA"}
+!65 = !DILocation(line: 15, column: 5, scope: !51)
+!66 = !DILocation(line: 15, column: 7, scope: !51)
+!67 = !DILocation(line: 15, column: 9, scope: !51)
+!68 = !{!69, !62, i64 4}
+!69 = !{!"_ZTS5Outer", !70, i64 0, !70, i64 8}
+!70 = !{!"_ZTS5Inner", !62, i64 0, !62, i64 4}
+!71 = !DILocation(line: 16, column: 1, scope: !51)
+!72 = distinct !DISubprogram(name: "unknownOffset", linkageName: "_Z13unknownOffsetv", scope: !3, file: !3, line: 18, type: !32, scopeLine: 18, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !73)
+!73 = !{!74}
+!74 = !DILocalVariable(name: "A", scope: !72, file: !3, line: 19, type: !75)
+!75 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !76)
+!76 = !{!77}
+!77 = !DISubrange(count: 2)
+!78 = !DILocation(line: 19, column: 3, scope: !72)
+!79 = !DILocation(line: 19, column: 7, scope: !72)
+!80 = !DILocation(line: 20, column: 14, scope: !72)
+!81 = !DILocation(line: 20, column: 5, scope: !72)
+!82 = !DILocation(line: 20, column: 3, scope: !72)
+!83 = !DILocation(line: 20, column: 12, scope: !72)
+!84 = !DILocation(line: 21, column: 1, scope: !72)
+!85 = distinct !DISubprogram(name: "sharedAlloca", linkageName: "_Z12sharedAllocav", scope: !3, file: !3, line: 23, type: !86, scopeLine: 23, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !88)
+!86 = !DISubroutineType(types: !87)
+!87 = !{!12}
+!88 = !{!89, !92}
+!89 = !DILocalVariable(name: "A", scope: !90, file: !3, line: 25, type: !12)
+!90 = distinct !DILexicalBlock(scope: !91, file: !3, line: 24, column: 13)
+!91 = distinct !DILexicalBlock(scope: !85, file: !3, line: 24, column: 7)
+!92 = !DILocalVariable(name: "B", scope: !93, file: !3, line: 28, type: !12)
+!93 = distinct !DILexicalBlock(scope: !91, file: !3, line: 27, column: 10)
+!94 = !DILocation(line: 24, column: 7, scope: !91)
+!95 = !DILocation(line: 24, column: 7, scope: !85)
+!96 = !DILocation(line: 25, column: 11, scope: !90)
+!97 = !DILocation(line: 25, column: 15, scope: !90)
+!98 = !{i64 0, i64 4, !61, i64 4, i64 4, !61}
+!99 = !DILocation(line: 26, column: 5, scope: !90)
+!100 = !DILocation(line: 28, column: 11, scope: !93)
+!101 = !DILocation(line: 28, column: 15, scope: !93)
+!102 = !DILocation(line: 29, column: 5, scope: !93)
+!103 = !DILocation(line: 31, column: 1, scope: !85)
+!104 = distinct !DISubprogram(name: "sret", linkageName: "_Z4sretv", scope: !3, file: !3, line: 33, type: !105, scopeLine: 33, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !107)
+!105 = !DISubroutineType(types: !106)
+!106 = !{!20}
+!107 = !{!108}
+!108 = !DILocalVariable(name: "X", scope: !104, file: !3, line: 34, type: !20)
+!109 = !DILocation(line: 34, column: 9, scope: !104)
+!110 = !DILocation(line: 34, column: 13, scope: !104)
+!111 = !{i64 0, i64 40, !112}
+!112 = !{!63, !63, i64 0}
+!113 = !DILocation(line: 35, column: 3, scope: !104)
+!114 = distinct !DISubprogram(name: "byval", linkageName: "_Z5byval5Large", scope: !3, file: !3, line: 38, type: !115, scopeLine: 38, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !117)
+!115 = !DISubroutineType(types: !116)
+!116 = !{null, !20}
+!117 = !{!118}
+!118 = !DILocalVariable(name: "X", arg: 1, scope: !114, file: !3, line: 38, type: !20)
+!119 = !DILocation(line: 38, column: 18, scope: !114)
+!120 = !DILocation(line: 38, column: 22, scope: !114)
+!121 = distinct !DISubprogram(name: "indirectReturn", linkageName: "_Z14indirectReturnv", scope: !2, file: !3, line: 41, type: !122, scopeLine: 41, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !125)
+!122 = !DISubroutineType(types: !123)
+!123 = !{!124}
+!124 = !DICompositeType(tag: DW_TAG_structure_type, name: "LCopyCtor", file: !3, line: 4, size: 128, flags: DIFlagFwdDecl | DIFlagNonTrivial, identifier: "_ZTS9LCopyCtor")
+!125 = !{}
+!126 = !DILocalVariable(name: "R", scope: !121, file: !3, line: 42, type: !124)
+!127 = !DILocation(line: 42, column: 13, scope: !121)
+!128 = !DILocation(line: 43, column: 3, scope: !121)
+!129 = distinct !DISubprogram(name: "sqr", linkageName: "_Z3sqri", scope: !2, file: !3, line: 2, type: !130, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !132)
+!130 = !DISubroutineType(types: !131)
+!131 = !{!7, !7}
+!132 = !{}
+!133 = !DILocalVariable(name: "Y", arg: 1, scope: !129, file: !3, line: 2, type: !7)
+!134 = !DILocation(line: 2, column: 13, scope: !129)
+!135 = !DILocation(line: 2, column: 25, scope: !129)
+!136 = !DILocation(line: 2, column: 29, scope: !129)
+!137 = !DILocation(line: 2, column: 27, scope: !129)
+!138 = !DILocation(line: 2, column: 18, scope: !129)
+!139 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funi", scope: !2, file: !3, line: 3, type: !130, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !132)
+!140 = !DILocalVariable(name: "X", arg: 1, scope: !139, file: !3, line: 3, type: !7)
+!141 = !DILocation(line: 3, column: 13, scope: !139)
+!142 = !DILocation(line: 3, column: 29, scope: !139)
+!143 = !DILocation(line: 2, column: 13, scope: !129, inlinedAt: !144)
+!144 = distinct !DILocation(line: 3, column: 25, scope: !139)
+!145 = !DILocation(line: 2, column: 25, scope: !129, inlinedAt: !144)
+!146 = !DILocation(line: 2, column: 29, scope: !129, inlinedAt: !144)
+!147 = !DILocation(line: 2, column: 27, scope: !129, inlinedAt: !144)
+!148 = !DILocation(line: 3, column: 18, scope: !139)


        


More information about the llvm-commits mailing list