[Assignment Tracking][3/*] Add DIAssignID metadata boilerplate
authorOCHyams <orlando.hyams@sony.com>
Thu, 3 Nov 2022 09:50:31 +0000 (09:50 +0000)
committerOCHyams <orlando.hyams@sony.com>
Mon, 7 Nov 2022 09:05:56 +0000 (09:05 +0000)
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 the DIAssignID metadata attachment boilerplate. Includes a textual-bitcode
roundtrip test and tests that the verifier and parser catch badly formed IR.

This piece of metadata links together stores (used as an attachment) and the
yet-to-be-added llvm.dbg.assign debug intrinsic (used as an operand).

Reviewed By: jmorse

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

16 files changed:
llvm/include/llvm-c/DebugInfo.h
llvm/include/llvm/Bitcode/LLVMBitCodes.h
llvm/include/llvm/IR/DebugInfoMetadata.h
llvm/include/llvm/IR/FixedMetadataKinds.def
llvm/include/llvm/IR/Metadata.def
llvm/lib/AsmParser/LLParser.cpp
llvm/lib/Bitcode/Reader/MetadataLoader.cpp
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
llvm/lib/IR/AsmWriter.cpp
llvm/lib/IR/DebugInfo.cpp
llvm/lib/IR/DebugInfoMetadata.cpp
llvm/lib/IR/Verifier.cpp
llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/distinct.ll [new file with mode: 0644]
llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/instruction-type.ll [new file with mode: 0644]
llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/operands.ll [new file with mode: 0644]
llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/roundtrip.ll [new file with mode: 0644]

index 8554a01..ef6a147 100644 (file)
@@ -169,7 +169,8 @@ enum {
   LLVMDICommonBlockMetadataKind,
   LLVMDIStringTypeMetadataKind,
   LLVMDIGenericSubrangeMetadataKind,
-  LLVMDIArgListMetadataKind
+  LLVMDIArgListMetadataKind,
+  LLVMDIAssignIDMetadataKind,
 };
 typedef unsigned LLVMMetadataKind;
 
index ee5669c..74a51d5 100644 (file)
@@ -349,7 +349,8 @@ enum MetadataCodes {
   // info.
   METADATA_COMMON_BLOCK = 44,     // [distinct, scope, name, variable,...]
   METADATA_GENERIC_SUBRANGE = 45, // [distinct, count, lo, up, stride]
-  METADATA_ARG_LIST = 46          // [n x [type num, value num]]
+  METADATA_ARG_LIST = 46,         // [n x [type num, value num]]
+  METADATA_ASSIGN_ID = 47,        // [distinct, ...]
 };
 
 // The constants block (CONSTANTS_BLOCK_ID) describes emission for each
index 5b20bf3..f57691f 100644 (file)
@@ -215,6 +215,7 @@ public:
     case DIImportedEntityKind:
     case DIModuleKind:
     case DIGenericSubrangeKind:
+    case DIAssignIDKind:
       return true;
     }
   }
@@ -295,6 +296,41 @@ public:
   }
 };
 
+/// Assignment ID.
+/// Used to link stores (as an attachment) and dbg.assigns (as an operand).
+/// DIAssignID metadata is never uniqued as we compare instances using
+/// referential equality (the instance/address is the ID).
+class DIAssignID : public MDNode {
+  friend class LLVMContextImpl;
+  friend class MDNode;
+
+  DIAssignID(LLVMContext &C, StorageType Storage)
+      : MDNode(C, DIAssignIDKind, Storage, None) {}
+
+  ~DIAssignID() { dropAllReferences(); }
+
+  static DIAssignID *getImpl(LLVMContext &Context, StorageType Storage,
+                             bool ShouldCreate = true);
+
+  TempDIAssignID cloneImpl() const { return getTemporary(getContext()); }
+
+public:
+  // This node has no operands to replace.
+  void replaceOperandWith(unsigned I, Metadata *New) = delete;
+
+  static DIAssignID *getDistinct(LLVMContext &Context) {
+    return getImpl(Context, Distinct);
+  }
+  static TempDIAssignID getTemporary(LLVMContext &Context) {
+    return TempDIAssignID(getImpl(Context, Temporary));
+  }
+  // NOTE: Do not define get(LLVMContext&) - see class comment.
+
+  static bool classof(const Metadata *MD) {
+    return MD->getMetadataID() == DIAssignIDKind;
+  }
+};
+
 /// Array subrange.
 ///
 /// TODO: Merge into node for DW_TAG_array_type, which should have a custom
index 3d98632..8723bf2 100644 (file)
@@ -49,3 +49,4 @@ LLVM_FIXED_MD_KIND(MD_memprof, "memprof", 34)
 LLVM_FIXED_MD_KIND(MD_callsite, "callsite", 35)
 LLVM_FIXED_MD_KIND(MD_kcfi_type, "kcfi_type", 36)
 LLVM_FIXED_MD_KIND(MD_pcsections, "pcsections", 37)
+LLVM_FIXED_MD_KIND(MD_DIAssignID, "DIAssignID", 38)
index bbf349e..36c34c1 100644 (file)
@@ -110,6 +110,7 @@ HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILocalVariable)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DILabel)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIObjCProperty)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIImportedEntity)
+HANDLE_SPECIALIZED_MDNODE_LEAF(DIAssignID)
 HANDLE_SPECIALIZED_MDNODE_BRANCH(DIMacroNode)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacro)
 HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacroFile)
index 8767da4..2520484 100644 (file)
@@ -4681,6 +4681,24 @@ bool LLParser::parseDILocation(MDNode *&Result, bool IsDistinct) {
   return false;
 }
 
+/// parseDIAssignID:
+///   ::= distinct !DIAssignID()
+bool LLParser::parseDIAssignID(MDNode *&Result, bool IsDistinct) {
+  if (!IsDistinct)
+    return Lex.Error("missing 'distinct', required for !DIAssignID()");
+
+  Lex.Lex();
+
+  // Now eat the parens.
+  if (parseToken(lltok::lparen, "expected '(' here"))
+    return true;
+  if (parseToken(lltok::rparen, "expected ')' here"))
+    return true;
+
+  Result = DIAssignID::getDistinct(Context);
+  return false;
+}
+
 /// parseGenericDINode:
 ///   ::= !GenericDINode(tag: 15, header: "...", operands: {...})
 bool LLParser::parseGenericDINode(MDNode *&Result, bool IsDistinct) {
index 02d76f6..1ac1502 100644 (file)
@@ -856,6 +856,7 @@ MetadataLoader::MetadataLoaderImpl::lazyLoadModuleMetadataBlock() {
       case bitc::METADATA_TEMPLATE_VALUE:
       case bitc::METADATA_GLOBAL_VAR:
       case bitc::METADATA_LOCAL_VAR:
+      case bitc::METADATA_ASSIGN_ID:
       case bitc::METADATA_LABEL:
       case bitc::METADATA_EXPRESSION:
       case bitc::METADATA_OBJC_PROPERTY:
@@ -1964,6 +1965,18 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
 
     break;
   }
+  case bitc::METADATA_ASSIGN_ID: {
+    if (Record.size() != 1)
+      return error("Invalid DIAssignID record.");
+
+    IsDistinct = Record[0] & 1;
+    if (!IsDistinct)
+      return error("Invalid DIAssignID record. Must be distinct");
+
+    MetadataList.assignValue(DIAssignID::getDistinct(Context), NextMetadataNo);
+    NextMetadataNo++;
+    break;
+  }
   case bitc::METADATA_LOCAL_VAR: {
     // 10th field is for the obseleted 'inlinedAt:' field.
     if (Record.size() < 8 || Record.size() > 10)
index 85ada2c..4bf881a 100644 (file)
@@ -340,6 +340,8 @@ private:
                       unsigned Abbrev);
   void writeDIModule(const DIModule *N, SmallVectorImpl<uint64_t> &Record,
                      unsigned Abbrev);
+  void writeDIAssignID(const DIAssignID *N, SmallVectorImpl<uint64_t> &Record,
+                       unsigned Abbrev);
   void writeDITemplateTypeParameter(const DITemplateTypeParameter *N,
                                     SmallVectorImpl<uint64_t> &Record,
                                     unsigned Abbrev);
@@ -1949,6 +1951,15 @@ void ModuleBitcodeWriter::writeDIModule(const DIModule *N,
   Record.clear();
 }
 
+void ModuleBitcodeWriter::writeDIAssignID(const DIAssignID *N,
+                                          SmallVectorImpl<uint64_t> &Record,
+                                          unsigned Abbrev) {
+  // There are no arguments for this metadata type.
+  Record.push_back(N->isDistinct());
+  Stream.EmitRecord(bitc::METADATA_ASSIGN_ID, Record, Abbrev);
+  Record.clear();
+}
+
 void ModuleBitcodeWriter::writeDITemplateTypeParameter(
     const DITemplateTypeParameter *N, SmallVectorImpl<uint64_t> &Record,
     unsigned Abbrev) {
index 21e662b..d49b871 100644 (file)
@@ -1865,6 +1865,12 @@ static void writeDILocation(raw_ostream &Out, const DILocation *DL,
   Out << ")";
 }
 
+static void writeDIAssignID(raw_ostream &Out, const DIAssignID *DL,
+                            AsmWriterContext &WriterCtx) {
+  Out << "!DIAssignID()";
+  MDFieldPrinter Printer(Out, WriterCtx);
+}
+
 static void writeDISubrange(raw_ostream &Out, const DISubrange *N,
                             AsmWriterContext &WriterCtx) {
   Out << "!DISubrange(";
index 8f6d58c..d30fca6 100644 (file)
@@ -469,9 +469,13 @@ bool llvm::stripDebugInfo(Function &F) {
         if (NewLoopID != LoopID)
           I.setMetadata(LLVMContext::MD_loop, NewLoopID);
       }
-      // Strip heapallocsite attachments, they point into the DIType system.
-      if (I.hasMetadataOtherThanDebugLoc())
+      // Strip other attachments that are or use debug info.
+      if (I.hasMetadataOtherThanDebugLoc()) {
+        // Heapallocsites point into the DIType system.
         I.setMetadata("heapallocsite", nullptr);
+        // DIAssignID are debug info metadata primitives.
+        I.setMetadata(LLVMContext::MD_DIAssignID, nullptr);
+      }
     }
   }
   return Changed;
index 9b4f92a..5483595 100644 (file)
@@ -1253,6 +1253,13 @@ bool DIExpression::startsWithDeref() const {
   return getNumElements() > 0 && getElement(0) == dwarf::DW_OP_deref;
 }
 
+DIAssignID *DIAssignID::getImpl(LLVMContext &Context, StorageType Storage,
+                                bool ShouldCreate) {
+  // Uniqued DIAssignID are not supported as the instance address *is* the ID.
+  assert(Storage != StorageType::Uniqued && "uniqued DIAssignID unsupported");
+  return storeImpl(new (0u, Storage) DIAssignID(Context, Storage), Storage);
+}
+
 unsigned DIExpression::ExprOperand::getSize() const {
   uint64_t Op = getOp();
 
index 3c68f07..f0097da 100644 (file)
@@ -471,6 +471,7 @@ private:
   void visitCallStackMetadata(MDNode *MD);
   void visitMemProfMetadata(Instruction &I, MDNode *MD);
   void visitCallsiteMetadata(Instruction &I, MDNode *MD);
+  void visitDIAssignIDMetadata(Instruction &I, MDNode *MD);
   void visitAnnotationMetadata(MDNode *Annotation);
   void visitAliasScopeMetadata(const MDNode *MD);
   void visitAliasScopeListMetadata(const MDNode *MD);
@@ -1483,6 +1484,11 @@ void Verifier::visitDILocalVariable(const DILocalVariable &N) {
     CheckDI(!isa<DISubroutineType>(Ty), "invalid type", &N, N.getType());
 }
 
+void Verifier::visitDIAssignID(const DIAssignID &N) {
+  CheckDI(!N.getNumOperands(), "DIAssignID has no arguments", &N);
+  CheckDI(N.isDistinct(), "DIAssignID must be distinct", &N);
+}
+
 void Verifier::visitDILabel(const DILabel &N) {
   if (auto *S = N.getRawScope())
     CheckDI(isa<DIScope>(S), "invalid scope", &N, S);
@@ -4529,6 +4535,14 @@ void Verifier::visitProfMetadata(Instruction &I, MDNode *MD) {
   }
 }
 
+void Verifier::visitDIAssignIDMetadata(Instruction &I, MDNode *MD) {
+  assert(I.hasMetadata(LLVMContext::MD_DIAssignID));
+  bool ExpectedInstTy =
+      isa<AllocaInst>(I) || isa<StoreInst>(I) || isa<MemIntrinsic>(I);
+  CheckDI(ExpectedInstTy, "!DIAssignID attached to unexpected instruction kind",
+          I, MD);
+}
+
 void Verifier::visitCallStackMetadata(MDNode *MD) {
   // Call stack metadata should consist of a list of at least 1 constant int
   // (representing a hash of the location).
@@ -4830,6 +4844,9 @@ void Verifier::visitInstruction(Instruction &I) {
   if (MDNode *MD = I.getMetadata(LLVMContext::MD_callsite))
     visitCallsiteMetadata(I, MD);
 
+  if (MDNode *MD = I.getMetadata(LLVMContext::MD_DIAssignID))
+    visitDIAssignIDMetadata(I, MD);
+
   if (MDNode *Annotation = I.getMetadata(LLVMContext::MD_annotation))
     visitAnnotationMetadata(Annotation);
 
diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/distinct.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/distinct.ll
new file mode 100644 (file)
index 0000000..2cc5452
--- /dev/null
@@ -0,0 +1,9 @@
+; RUN: not opt -S %s -experimental-assignment-tracking 2>&1 \
+; RUN: | FileCheck %s
+
+;; Check that badly formed assignment tracking metadata is caught either
+;; while parsing or by the verifier.
+
+; CHECK: error: missing 'distinct', required for !DIAssignID()
+
+!1 = !DIAssignID()
diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/instruction-type.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/instruction-type.ll
new file mode 100644 (file)
index 0000000..d0f447e
--- /dev/null
@@ -0,0 +1,36 @@
+; RUN: opt -S %s -verify -experimental-assignment-tracking 2>&1 \
+; RUN: | FileCheck %s
+
+;; NOTE: Expect opt to return zero because the badly formed debug info
+;; is going to be stripped.
+
+;; Check that badly formed assignment tracking metadata is caught either
+;; while parsing or by the verifier.
+
+;; Check verifier output.
+; CHECK: !DIAssignID attached to unexpected instruction kind
+
+;; Check DIAssignID is stripped from IR.
+; CHECK: define dso_local void @fun() {
+; CHECK-NOT: DIAssignID
+
+define dso_local void @fun() !dbg !7 {
+entry:
+  ret void, !DIAssignID !14
+}
+
+!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: "fun", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!14 = distinct !DIAssignID()
diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/operands.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/operands.ll
new file mode 100644 (file)
index 0000000..79adcb9
--- /dev/null
@@ -0,0 +1,9 @@
+; RUN: not opt -S %s -experimental-assignment-tracking 2>&1 \
+; RUN: | FileCheck %s
+
+;; Check that badly formed assignment tracking metadata is caught either
+;; while parsing or by the verifier.
+
+; CHECK: error: expected ')' here
+
+!1 = distinct !DIAssignID(0)
diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/roundtrip.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/roundtrip.ll
new file mode 100644 (file)
index 0000000..1ddb95b
--- /dev/null
@@ -0,0 +1,33 @@
+; RUN: opt %s -verify -experimental-assignment-tracking   \
+; RUN: | opt -verify -S -experimental-assignment-tracking \
+; RUN: | FileCheck %s
+
+;; Roundtrip test (text -> bitcode -> text) for DIAssignID attachments.
+
+; CHECK: %local = alloca i32, align 4, !DIAssignID ![[ID:[0-9]+]]
+; CHECK-DAG: ![[ID]] = distinct !DIAssignID()
+
+define dso_local void @fun() !dbg !7 {
+entry:
+  %local = alloca i32, align 4, !DIAssignID !14
+  ret void, !dbg !13
+}
+
+!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: "fun", 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: "local", scope: !7, file: !1, line: 2, type: !11)
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!13 = !DILocation(line: 3, column: 1, scope: !7)
+!14 = distinct !DIAssignID()