[llvm] 171f702 - [Assignment Tracking][5/*] Add core infrastructure for instruction reference

via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 7 04:03:32 PST 2022


Author: OCHyams
Date: 2022-11-07T12:03:02Z
New Revision: 171f7024cc82e8702abebdedb699d37b50574be7

URL: https://github.com/llvm/llvm-project/commit/171f7024cc82e8702abebdedb699d37b50574be7
DIFF: https://github.com/llvm/llvm-project/commit/171f7024cc82e8702abebdedb699d37b50574be7.diff

LOG: [Assignment Tracking][5/*] Add core infrastructure for instruction reference

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

Overview
It's possible to find intrinsics linked to an instruction by looking at the
MetadataAsValue uses of the attached DIAssignID. That covers instruction ->
intrinsic(s) lookup. Add a global DIAssignID -> instruction(s) map which gives
us the ability to perform intrinsic -> instruction(s) lookup. Add plumbing to
keep the map up to date through optimisations and add utility functions
including two that perform those lookups. Finally, add a unittest.

Details
In llvm/lib/IR/LLVMContextImpl.h add AssignmentIDToInstrs which maps DIAssignID
* attachments to Instruction *s. Because the DIAssignID * is the key we can't
use a TrackingMDNodeRef for it, and therefore cannot easily update the mapping
when a temporary DIAssignID is replaced.

Temporary DIAssignID's are only used in IR parsing to deal with metadata
forward references. Update llvm/lib/AsmParser/LLParser.cpp to avoid using
temporary DIAssignID's for attachments.

In llvm/lib/IR/Metadata.cpp add Instruction::updateDIAssignIDMapping which is
called to remove or add an entry (or both) to AssignmentIDToInstrs. Call this
from Instruction::setMetadata and add a call to setMetadata in Intruction's
dtor that explicitly unsets the DIAssignID so that the mappging gets updated.

In llvm/lib/IR/DebugInfo.cpp and DebugInfo.h add utility functions:

    getAssignmentInsts(const DbgAssignIntrinsic *DAI)
    getAssignmentMarkers(const Instruction *Inst)
    RAUW(DIAssignID *Old, DIAssignID *New)
    deleteAll(Function *F)

These core utils are tested in llvm/unittests/IR/DebugInfoTest.cpp.

Reviewed By: jmorse

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

Added: 
    

Modified: 
    llvm/include/llvm/AsmParser/LLParser.h
    llvm/include/llvm/IR/DebugInfo.h
    llvm/include/llvm/IR/Instruction.h
    llvm/lib/AsmParser/LLParser.cpp
    llvm/lib/IR/DebugInfo.cpp
    llvm/lib/IR/Instruction.cpp
    llvm/lib/IR/LLVMContextImpl.h
    llvm/lib/IR/Metadata.cpp
    llvm/lib/IR/Verifier.cpp
    llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll
    llvm/unittests/IR/DebugInfoTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 8757543071559..e9813c34ce373 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -108,6 +108,12 @@ namespace llvm {
 
     SmallVector<Instruction*, 64> InstsWithTBAATag;
 
+    /// DIAssignID metadata does not support temporary RAUW so we cannot use
+    /// the normal metadata forward reference resolution method. Instead,
+    /// non-temporary DIAssignID are attached to instructions (recorded here)
+    /// then replaced later.
+    DenseMap<MDNode *, SmallVector<Instruction *, 2>> TempDIAssignIDAttachments;
+
     // Type resolution handling data structures.  The location is set when we
     // have processed a use of the type but not a definition yet.
     StringMap<std::pair<Type*, LocTy> > NamedTypes;

diff  --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index b35d447a7c891..8f49d39f373a5 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -21,7 +21,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/ADT/iterator_range.h"
-#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/IntrinsicInst.h"
 
 namespace llvm {
 
@@ -159,6 +159,67 @@ class DebugInfoFinder {
   SmallPtrSet<const MDNode *, 32> NodesSeen;
 };
 
+/// Assignment Tracking (at).
+namespace at {
+//
+// Utilities for enumerating storing instructions from an assignment ID.
+//
+/// A range of instructions.
+using AssignmentInstRange =
+    iterator_range<SmallVectorImpl<Instruction *>::iterator>;
+/// Return a range of instructions (typically just one) that have \p ID
+/// as an attachment.
+/// Iterators invalidated by adding or removing DIAssignID metadata to/from any
+/// instruction (including by deleting or cloning instructions).
+AssignmentInstRange getAssignmentInsts(DIAssignID *ID);
+/// Return a range of instructions (typically just one) that perform the
+/// assignment that \p DAI encodes.
+/// Iterators invalidated by adding or removing DIAssignID metadata to/from any
+/// instruction (including by deleting or cloning instructions).
+inline AssignmentInstRange getAssignmentInsts(const DbgAssignIntrinsic *DAI) {
+  return getAssignmentInsts(cast<DIAssignID>(DAI->getAssignID()));
+}
+
+//
+// Utilities for enumerating llvm.dbg.assign intrinsic from an assignment ID.
+//
+/// High level: this is an iterator for llvm.dbg.assign intrinsics.
+/// Implementation details: this is a wrapper around Value's User iterator that
+/// dereferences to a DbgAssignIntrinsic ptr rather than a User ptr.
+class DbgAssignIt
+    : public iterator_adaptor_base<DbgAssignIt, Value::user_iterator,
+                                   typename std::iterator_traits<
+                                       Value::user_iterator>::iterator_category,
+                                   DbgAssignIntrinsic *, std::ptr
diff _t,
+                                   DbgAssignIntrinsic **,
+                                   DbgAssignIntrinsic *&> {
+public:
+  DbgAssignIt(Value::user_iterator It) : iterator_adaptor_base(It) {}
+  DbgAssignIntrinsic *operator*() const { return cast<DbgAssignIntrinsic>(*I); }
+};
+/// A range of llvm.dbg.assign intrinsics.
+using AssignmentMarkerRange = iterator_range<DbgAssignIt>;
+/// Return a range of dbg.assign intrinsics which use \ID as an operand.
+/// Iterators invalidated by deleting an intrinsic contained in this range.
+AssignmentMarkerRange getAssignmentMarkers(DIAssignID *ID);
+/// Return a range of dbg.assign intrinsics for which \p Inst performs the
+/// assignment they encode.
+/// Iterators invalidated by deleting an intrinsic contained in this range.
+inline AssignmentMarkerRange getAssignmentMarkers(const Instruction *Inst) {
+  if (auto *ID = Inst->getMetadata(LLVMContext::MD_DIAssignID))
+    return getAssignmentMarkers(cast<DIAssignID>(ID));
+  else
+    return make_range(Value::user_iterator(), Value::user_iterator());
+}
+
+/// Replace all uses (and attachments) of \p Old with \p New.
+void RAUW(DIAssignID *Old, DIAssignID *New);
+
+/// Remove all Assignment Tracking related intrinsics and metadata from \p F.
+void deleteAll(Function *F);
+
+} // end namespace at
+
 /// Return true if assignment tracking is enabled.
 bool getEnableAssignmentTracking();
 } // end namespace llvm

diff  --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h
index f85fcb93068fa..131a7414a1a7d 100644
--- a/llvm/include/llvm/IR/Instruction.h
+++ b/llvm/include/llvm/IR/Instruction.h
@@ -515,6 +515,10 @@ class Instruction : public User,
   void
   getAllMetadataImpl(SmallVectorImpl<std::pair<unsigned, MDNode *>> &) const;
 
+  /// Update the LLVMContext ID-to-Instruction(s) mapping. If \p ID is nullptr
+  /// then clear the mapping for this instruction.
+  void updateDIAssignIDMapping(DIAssignID *ID);
+
 public:
   //===--------------------------------------------------------------------===//
   // Predicates and helper methods.

diff  --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 25204847ca9ce..c1835b3e3023c 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -853,7 +853,18 @@ bool LLParser::parseStandaloneMetadata() {
   // See if this was forward referenced, if so, handle it.
   auto FI = ForwardRefMDNodes.find(MetadataID);
   if (FI != ForwardRefMDNodes.end()) {
-    FI->second.first->replaceAllUsesWith(Init);
+    auto *ToReplace = FI->second.first.get();
+    // DIAssignID has its own special forward-reference "replacement" for
+    // attachments (the temporary attachments are never actually attached).
+    if (isa<DIAssignID>(Init)) {
+      for (auto *Inst : TempDIAssignIDAttachments[ToReplace]) {
+        assert(!Inst->getMetadata(LLVMContext::MD_DIAssignID) &&
+               "Inst unexpectedly already has DIAssignID attachment");
+        Inst->setMetadata(LLVMContext::MD_DIAssignID, Init);
+      }
+    }
+
+    ToReplace->replaceAllUsesWith(Init);
     ForwardRefMDNodes.erase(FI);
 
     assert(NumberedMetadata[MetadataID] == Init && "Tracking VH didn't work");
@@ -2082,7 +2093,11 @@ bool LLParser::parseInstructionMetadata(Instruction &Inst) {
     if (parseMetadataAttachment(MDK, N))
       return true;
 
-    Inst.setMetadata(MDK, N);
+    if (MDK == LLVMContext::MD_DIAssignID)
+      TempDIAssignIDAttachments[N].push_back(&Inst);
+    else
+      Inst.setMetadata(MDK, N);
+
     if (MDK == LLVMContext::MD_tbaa)
       InstsWithTBAATag.push_back(&Inst);
 

diff  --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index d30fca63067c0..89b5ff218de15 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm-c/DebugInfo.h"
+#include "LLVMContextImpl.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
@@ -37,6 +38,7 @@
 #include <utility>
 
 using namespace llvm;
+using namespace llvm::at;
 using namespace llvm::dwarf;
 
 static cl::opt<bool>
@@ -1632,3 +1634,61 @@ LLVMMetadataKind LLVMGetMetadataKind(LLVMMetadataRef Metadata) {
     return (LLVMMetadataKind)LLVMGenericDINodeMetadataKind;
   }
 }
+
+AssignmentInstRange at::getAssignmentInsts(DIAssignID *ID) {
+  assert(ID && "Expected non-null ID");
+  LLVMContext &Ctx = ID->getContext();
+  auto &Map = Ctx.pImpl->AssignmentIDToInstrs;
+
+  auto MapIt = Map.find(ID);
+  if (MapIt == Map.end())
+    return make_range(nullptr, nullptr);
+
+  return make_range(MapIt->second.begin(), MapIt->second.end());
+}
+
+AssignmentMarkerRange at::getAssignmentMarkers(DIAssignID *ID) {
+  assert(ID && "Expected non-null ID");
+  LLVMContext &Ctx = ID->getContext();
+
+  auto *IDAsValue = MetadataAsValue::getIfExists(Ctx, ID);
+
+  // The ID is only used wrapped in MetadataAsValue(ID), so lets check that
+  // one of those already exists first.
+  if (!IDAsValue)
+    return make_range(Value::user_iterator(), Value::user_iterator());
+
+  return make_range(IDAsValue->user_begin(), IDAsValue->user_end());
+}
+
+void at::RAUW(DIAssignID *Old, DIAssignID *New) {
+  // Replace MetadataAsValue uses.
+  if (auto *OldIDAsValue =
+          MetadataAsValue::getIfExists(Old->getContext(), Old)) {
+    auto *NewIDAsValue = MetadataAsValue::get(Old->getContext(), New);
+    OldIDAsValue->replaceAllUsesWith(NewIDAsValue);
+  }
+
+  // Replace attachments.
+  AssignmentInstRange InstRange = getAssignmentInsts(Old);
+  // Use intermediate storage for the instruction ptrs because the
+  // getAssignmentInsts range iterators will be invalidated by adding and
+  // removing DIAssignID attachments.
+  SmallVector<Instruction *> InstVec(InstRange.begin(), InstRange.end());
+  for (auto *I : InstVec)
+    I->setMetadata(LLVMContext::MD_DIAssignID, New);
+}
+
+void at::deleteAll(Function *F) {
+  SmallVector<DbgAssignIntrinsic *, 12> ToDelete;
+  for (BasicBlock &BB : *F) {
+    for (Instruction &I : BB) {
+      if (auto *DAI = dyn_cast<DbgAssignIntrinsic>(&I))
+        ToDelete.push_back(DAI);
+      else
+        I.setMetadata(LLVMContext::MD_DIAssignID, nullptr);
+    }
+  }
+  for (auto *DAI : ToDelete)
+    DAI->eraseFromParent();
+}

diff  --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index 007e518a1a817..74fc3416b564f 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -55,6 +55,10 @@ Instruction::~Instruction() {
   //   instructions in a BasicBlock are deleted).
   if (isUsedByMetadata())
     ValueAsMetadata::handleRAUW(this, UndefValue::get(getType()));
+
+  // Explicitly remove DIAssignID metadata to clear up ID -> Instruction(s)
+  // mapping in LLVMContext.
+  setMetadata(LLVMContext::MD_DIAssignID, nullptr);
 }
 
 

diff  --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 0b1e5194222fc..3f4f222a0720e 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -1499,6 +1499,11 @@ class LLVMContextImpl {
   /// Collection of metadata used in this context.
   DenseMap<const Value *, MDAttachments> ValueMetadata;
 
+  /// Map DIAssignID -> Instructions with that attachment.
+  /// Managed by Instruction via Instruction::updateDIAssignIDMapping.
+  /// Query using the at:: functions defined in DebugInfo.h.
+  DenseMap<DIAssignID *, SmallVector<Instruction *, 1>> AssignmentIDToInstrs;
+
   /// Collection of per-GlobalObject sections used in this context.
   DenseMap<const GlobalObject *, StringRef> GlobalObjectSections;
 

diff  --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index 052f3b1b37ded..5336902031898 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -1425,6 +1425,37 @@ void Instruction::dropUnknownNonDebugMetadata(ArrayRef<unsigned> KnownIDs) {
   }
 }
 
+void Instruction::updateDIAssignIDMapping(DIAssignID *ID) {
+  auto &IDToInstrs = getContext().pImpl->AssignmentIDToInstrs;
+  if (const DIAssignID *CurrentID =
+          cast_or_null<DIAssignID>(getMetadata(LLVMContext::MD_DIAssignID))) {
+    // Nothing to do if the ID isn't changing.
+    if (ID == CurrentID)
+      return;
+
+    // Unmap this instruction from its current ID.
+    auto InstrsIt = IDToInstrs.find(CurrentID);
+    assert(InstrsIt != IDToInstrs.end() &&
+           "Expect existing attachment to be mapped");
+
+    auto &InstVec = InstrsIt->second;
+    auto *InstIt = std::find(InstVec.begin(), InstVec.end(), this);
+    assert(InstIt != InstVec.end() &&
+           "Expect instruction to be mapped to attachment");
+    // The vector contains a ptr to this. If this is the only element in the
+    // vector, remove the ID:vector entry, otherwise just remove the
+    // instruction from the vector.
+    if (InstVec.size() == 1)
+      IDToInstrs.erase(InstrsIt);
+    else
+      InstVec.erase(InstIt);
+  }
+
+  // Map this instruction to the new ID.
+  if (ID)
+    IDToInstrs[ID].push_back(this);
+}
+
 void Instruction::setMetadata(unsigned KindID, MDNode *Node) {
   if (!Node && !hasMetadata())
     return;
@@ -1435,6 +1466,16 @@ void Instruction::setMetadata(unsigned KindID, MDNode *Node) {
     return;
   }
 
+  // Update DIAssignID to Instruction(s) mapping.
+  if (KindID == LLVMContext::MD_DIAssignID) {
+    // The DIAssignID tracking infrastructure doesn't support RAUWing temporary
+    // nodes with DIAssignIDs. The cast_or_null below would also catch this, but
+    // having a dedicated assert helps make this obvious.
+    assert((!Node || !Node->isTemporary()) &&
+           "Temporary DIAssignIDs are invalid");
+    updateDIAssignIDMapping(cast_or_null<DIAssignID>(Node));
+  }
+
   Value::setMetadata(KindID, Node);
 }
 

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 324fa66fb2ea9..5e41fb1261575 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -68,6 +68,7 @@
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DataLayout.h"
+#include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/DebugLoc.h"
 #include "llvm/IR/DerivedTypes.h"
@@ -4548,6 +4549,10 @@ void Verifier::visitDIAssignIDMetadata(Instruction &I, MDNode *MD) {
       CheckDI(isa<DbgAssignIntrinsic>(User),
               "!DIAssignID should only be used by llvm.dbg.assign intrinsics",
               MD, User);
+      // All of the dbg.assign intrinsics should be in the same function as I.
+      if (auto *DAI = dyn_cast<DbgAssignIntrinsic>(User))
+        CheckDI(DAI->getFunction() == I.getFunction(),
+                "dbg.assign not in same function as inst", DAI, &I);
     }
   }
 }
@@ -6008,6 +6013,10 @@ void Verifier::visitDbgIntrinsic(StringRef Kind, DbgVariableIntrinsic &DII) {
     CheckDI(isa<DIExpression>(DAI->getRawAddressExpression()),
             "invalid llvm.dbg.assign intrinsic address expression", &DII,
             DAI->getRawAddressExpression());
+    // All of the linked instructions should be in the same function as DII.
+    for (Instruction *I : at::getAssignmentInsts(DAI))
+      CheckDI(DAI->getFunction() == I->getFunction(),
+              "inst not in same function as dbg.assign", I, DAI);
   }
 
   // Ignore broken !dbg attachments; they're checked elsewhere.

diff  --git a/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll
index 577289604d536..9fa17e7f06ee1 100644
--- a/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll
+++ b/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll
@@ -6,6 +6,13 @@
 ;;
 ;; Checks for this one are inline.
 
+define dso_local void @fun2() !dbg !15 {
+  ;; DIAssignID copied here from @fun() where it is used by intrinsics.
+  ; CHECK: dbg.assign not in same function as inst
+  %x = alloca i32, align 4, !DIAssignID !14
+  ret void
+}
+
 define dso_local void @fun() !dbg !7 {
 entry:
   %a = alloca i32, align 4, !DIAssignID !14
@@ -50,3 +57,4 @@ declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata,
 !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
 !13 = !DILocation(line: 1, column: 1, scope: !7)
 !14 = distinct !DIAssignID()
+!15 = distinct !DISubprogram(name: "fun2", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)

diff  --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp
index 524752168b091..9888bb6dd8e50 100644
--- a/llvm/unittests/IR/DebugInfoTest.cpp
+++ b/llvm/unittests/IR/DebugInfoTest.cpp
@@ -368,4 +368,130 @@ TEST(DIBuilder, createDbgAddr) {
   EXPECT_EQ(MDExp->getNumElements(), 0u);
 }
 
+TEST(AssignmentTrackingTest, Utils) {
+  // Test the assignment tracking utils defined in DebugInfo.h namespace at {}.
+  // This includes:
+  //     getAssignmentInsts
+  //     getAssignmentMarkers
+  //     RAUW
+  //     deleteAll
+  //
+  // The input IR includes two functions, fun1 and fun2. Both contain an alloca
+  // with a DIAssignID tag. fun1's alloca is linked to two llvm.dbg.assign
+  // intrinsics, one of which is for an inlined variable and appears before the
+  // alloca.
+
+  LLVMContext C;
+  std::unique_ptr<Module> M = parseIR(C, R"(
+    define dso_local void @fun1() !dbg !7 {
+    entry:
+      call void @llvm.dbg.assign(metadata i32 undef, metadata !10, metadata !DIExpression(), metadata !12, metadata i32 undef, metadata !DIExpression()), !dbg !13
+      %local = alloca i32, align 4, !DIAssignID !12
+      call void @llvm.dbg.assign(metadata i32 undef, metadata !16, metadata !DIExpression(), metadata !12, metadata i32 undef, metadata !DIExpression()), !dbg !15
+      ret void, !dbg !15
+    }
+
+    define dso_local void @fun2() !dbg !17 {
+    entry:
+      %local = alloca i32, align 4, !DIAssignID !20
+      call void @llvm.dbg.assign(metadata i32 undef, metadata !18, metadata !DIExpression(), metadata !20, metadata i32 undef, metadata !DIExpression()), !dbg !19
+      ret void, !dbg !19
+    }
+
+    declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+    !llvm.dbg.cu = !{!0}
+    !llvm.module.flags = !{!3, !4, !5}
+    !llvm.ident = !{!6}
+
+    !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+    !1 = !DIFile(filename: "test.c", directory: "/")
+    !2 = !{}
+    !3 = !{i32 7, !"Dwarf Version", i32 4}
+    !4 = !{i32 2, !"Debug Info Version", i32 3}
+    !5 = !{i32 1, !"wchar_size", i32 4}
+    !6 = !{!"clang version 14.0.0"}
+    !7 = distinct !DISubprogram(name: "fun1", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+    !8 = !DISubroutineType(types: !9)
+    !9 = !{null}
+    !10 = !DILocalVariable(name: "local3", scope: !14, file: !1, line: 2, type: !11)
+    !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+    !12 = distinct !DIAssignID()
+    !13 = !DILocation(line: 5, column: 1, scope: !14, inlinedAt: !15)
+    !14 = distinct !DISubprogram(name: "inline", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+    !15 = !DILocation(line: 3, column: 1, scope: !7)
+    !16 = !DILocalVariable(name: "local1", scope: !7, file: !1, line: 2, type: !11)
+    !17 = distinct !DISubprogram(name: "fun2", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+    !18 = !DILocalVariable(name: "local2", scope: !17, file: !1, line: 2, type: !11)
+    !19 = !DILocation(line: 4, column: 1, scope: !17)
+    !20 = distinct !DIAssignID()
+    )");
+
+  // Check the test IR isn't malformed.
+  ASSERT_TRUE(M);
+
+  Function &Fun1 = *M->getFunction("fun1");
+  Instruction &Alloca = *Fun1.getEntryBlock().getFirstNonPHIOrDbg();
+
+  // 1. Check the Instruction <-> Intrinsic mappings work in fun1.
+  //
+  // Check there are two llvm.dbg.assign intrinsics linked to Alloca.
+  auto CheckFun1Mapping = [&Alloca]() {
+    auto Markers = at::getAssignmentMarkers(&Alloca);
+    EXPECT_TRUE(std::distance(Markers.begin(), Markers.end()) == 2);
+    // Check those two entries are distinct.
+    DbgAssignIntrinsic *First = *Markers.begin();
+    DbgAssignIntrinsic *Second = *std::next(Markers.begin());
+    EXPECT_NE(First, Second);
+
+    // Check that we can get back to Alloca from each llvm.dbg.assign.
+    for (auto *DAI : Markers) {
+      auto Insts = at::getAssignmentInsts(DAI);
+      // Check there is exactly one instruction linked to each intrinsic. Use
+      // ASSERT_TRUE because we're going to dereference the begin iterator.
+      ASSERT_TRUE(std::distance(Insts.begin(), Insts.end()) == 1);
+      EXPECT_FALSE(Insts.empty());
+      // Check the linked instruction is Alloca.
+      Instruction *LinkedInst = *Insts.begin();
+      EXPECT_EQ(LinkedInst, &Alloca);
+    }
+  };
+  CheckFun1Mapping();
+
+  // 2. Check DIAssignID RAUW replaces attachments and uses.
+  //
+  DIAssignID *Old =
+      cast_or_null<DIAssignID>(Alloca.getMetadata(LLVMContext::MD_DIAssignID));
+  DIAssignID *New = DIAssignID::getDistinct(C);
+  ASSERT_TRUE(Old && New && New != Old);
+  at::RAUW(Old, New);
+  // Check fun1's alloca and intrinsics have been updated and the mapping still
+  // works.
+  EXPECT_EQ(New, cast_or_null<DIAssignID>(
+                     Alloca.getMetadata(LLVMContext::MD_DIAssignID)));
+  CheckFun1Mapping();
+
+  // Check that fun2's alloca and intrinsic have not not been updated.
+  Instruction &Fun2Alloca =
+      *M->getFunction("fun2")->getEntryBlock().getFirstNonPHIOrDbg();
+  DIAssignID *Fun2ID = cast_or_null<DIAssignID>(
+      Fun2Alloca.getMetadata(LLVMContext::MD_DIAssignID));
+  EXPECT_NE(New, Fun2ID);
+  auto Fun2Markers = at::getAssignmentMarkers(&Fun2Alloca);
+  ASSERT_TRUE(std::distance(Fun2Markers.begin(), Fun2Markers.end()) == 1);
+  auto Fun2Insts = at::getAssignmentInsts(*Fun2Markers.begin());
+  ASSERT_TRUE(std::distance(Fun2Insts.begin(), Fun2Insts.end()) == 1);
+  EXPECT_EQ(*Fun2Insts.begin(), &Fun2Alloca);
+
+  // 3. Check that deleting works and applies only to the target function.
+  at::deleteAll(&Fun1);
+  // There should now only be the alloca and ret in fun1.
+  EXPECT_EQ(Fun1.begin()->size(), 2);
+  // fun2's alloca should have the same DIAssignID and remain linked to its
+  // llvm.dbg.assign.
+  EXPECT_EQ(Fun2ID, cast_or_null<DIAssignID>(
+                        Fun2Alloca.getMetadata(LLVMContext::MD_DIAssignID)));
+  EXPECT_FALSE(at::getAssignmentMarkers(&Fun2Alloca).empty());
+}
+
 } // end namespace


        


More information about the llvm-commits mailing list