[clang] [llvm] [BPF] Generate BTF info using 'btf:type_tag' annotation (PR #91424)

via cfe-commits cfe-commits at lists.llvm.org
Tue May 7 22:39:28 PDT 2024


https://github.com/eddyz87 updated https://github.com/llvm/llvm-project/pull/91424

>From 4f3fbf2e4d831ea92d3bd5170e5e3c14b36fff5c Mon Sep 17 00:00:00 2001
From: Eduard Zingerman <eddyz87 at gmail.com>
Date: Tue, 14 Feb 2023 00:49:33 +0200
Subject: [PATCH 1/3] [DebugInfo][BPF] Add 'annotations' field for DIBasicType
 & DISubroutineType

Extend DIBasicType and DISubroutineType with additional field
'annotations', e.g. as below:

  !5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !6)
  !6 = !{!7}
  !7 = !{!"btf:type_tag", !"tag1"}

The field would be used by BPF backend to generate DWARF attributes
corresponding to "btf_type_tag" type attributes, e.g.:

  0x00000029:   DW_TAG_base_type
                  DW_AT_name	("int")
                  DW_AT_encoding	(DW_ATE_signed)
                  DW_AT_byte_size	(0x04)

  0x0000002d:     DW_TAG_LLVM_annotation
                    DW_AT_name	("btf:type_tag")
                    DW_AT_const_value	("tag1")

Such DWARF entries would be used to generate BTF definitions by tools
like pahole [1].

Note: similar fields with similar purposes are already present in
DIDerivedType and DICompositeType.

Currently "btf_type_tag" attributes are represented in debug
information as 'annotations' fields in DIDerivedType with
DW_TAG_pointer_type tag. The annotation on a pointer corresponds to
pointee having the attributes in the final BTF.

The discussion at [2] came to conclusion, that such annotations should
apply to the annotated type itself. Hence the necessity to extend
DIBasicType & DISubroutineType types with 'annotations' field to
represent cases like below:

  int __attribute__((btf_type_tag("foo"))) bar;

[1] https://github.com/acmel/dwarves
[2] https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/

This was previously tracked as differential revision:
https://reviews.llvm.org/D143966
---
 llvm/include/llvm/IR/DebugInfoMetadata.h      |  80 ++++++++---
 llvm/lib/AsmParser/LLParser.cpp               |  17 ++-
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    |  16 ++-
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |   2 +
 llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp     |   6 +-
 llvm/lib/IR/AsmWriter.cpp                     |   6 +-
 llvm/lib/IR/DebugInfoMetadata.cpp             |  15 +-
 llvm/lib/IR/LLVMContextImpl.h                 |  30 ++--
 llvm/test/Bitcode/attr-btf_tag-dibasic.ll     |  36 +++++
 .../test/Bitcode/attr-btf_tag-disubroutine.ll |  41 ++++++
 llvm/test/DebugInfo/attr-btf_type_tag.ll      | 136 +++++++++++++-----
 11 files changed, 295 insertions(+), 90 deletions(-)
 create mode 100644 llvm/test/Bitcode/attr-btf_tag-dibasic.ll
 create mode 100644 llvm/test/Bitcode/attr-btf_tag-disubroutine.ll

diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 42291d45da2be..1dfaa4aced791 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -828,40 +828,45 @@ class DIBasicType : public DIType {
   static DIBasicType *getImpl(LLVMContext &Context, unsigned Tag,
                               StringRef Name, uint64_t SizeInBits,
                               uint32_t AlignInBits, unsigned Encoding,
-                              DIFlags Flags, StorageType Storage,
-                              bool ShouldCreate = true) {
+                              DIFlags Flags, DINodeArray Annotations,
+                              StorageType Storage, bool ShouldCreate = true) {
     return getImpl(Context, Tag, getCanonicalMDString(Context, Name),
-                   SizeInBits, AlignInBits, Encoding, Flags, Storage,
-                   ShouldCreate);
+                   SizeInBits, AlignInBits, Encoding, Flags, Annotations.get(),
+                   Storage, ShouldCreate);
   }
   static DIBasicType *getImpl(LLVMContext &Context, unsigned Tag,
                               MDString *Name, uint64_t SizeInBits,
                               uint32_t AlignInBits, unsigned Encoding,
-                              DIFlags Flags, StorageType Storage,
-                              bool ShouldCreate = true);
+                              DIFlags Flags, Metadata *Annotations,
+                              StorageType Storage, bool ShouldCreate = true);
 
   TempDIBasicType cloneImpl() const {
     return getTemporary(getContext(), getTag(), getName(), getSizeInBits(),
-                        getAlignInBits(), getEncoding(), getFlags());
+                        getAlignInBits(), getEncoding(), getFlags(),
+                        getAnnotations());
   }
 
 public:
   DEFINE_MDNODE_GET(DIBasicType, (unsigned Tag, StringRef Name),
-                    (Tag, Name, 0, 0, 0, FlagZero))
+                    (Tag, Name, 0, 0, 0, FlagZero, {}))
   DEFINE_MDNODE_GET(DIBasicType,
                     (unsigned Tag, StringRef Name, uint64_t SizeInBits),
-                    (Tag, Name, SizeInBits, 0, 0, FlagZero))
+                    (Tag, Name, SizeInBits, 0, 0, FlagZero, {}))
   DEFINE_MDNODE_GET(DIBasicType,
                     (unsigned Tag, MDString *Name, uint64_t SizeInBits),
-                    (Tag, Name, SizeInBits, 0, 0, FlagZero))
+                    (Tag, Name, SizeInBits, 0, 0, FlagZero, {}))
   DEFINE_MDNODE_GET(DIBasicType,
                     (unsigned Tag, StringRef Name, uint64_t SizeInBits,
-                     uint32_t AlignInBits, unsigned Encoding, DIFlags Flags),
-                    (Tag, Name, SizeInBits, AlignInBits, Encoding, Flags))
+                     uint32_t AlignInBits, unsigned Encoding, DIFlags Flags,
+                     DINodeArray Annotations = {}),
+                    (Tag, Name, SizeInBits, AlignInBits, Encoding, Flags,
+                     Annotations))
   DEFINE_MDNODE_GET(DIBasicType,
                     (unsigned Tag, MDString *Name, uint64_t SizeInBits,
-                     uint32_t AlignInBits, unsigned Encoding, DIFlags Flags),
-                    (Tag, Name, SizeInBits, AlignInBits, Encoding, Flags))
+                     uint32_t AlignInBits, unsigned Encoding, DIFlags Flags,
+                     Metadata *Annotations = nullptr),
+                    (Tag, Name, SizeInBits, AlignInBits, Encoding, Flags,
+                     Annotations))
 
   TempDIBasicType clone() const { return cloneImpl(); }
 
@@ -873,6 +878,16 @@ class DIBasicType : public DIType {
   /// neither signed nor unsigned.
   std::optional<Signedness> getSignedness() const;
 
+  Metadata *getRawAnnotations() const { return getOperand(3); }
+
+  DINodeArray getAnnotations() const {
+    return cast_or_null<MDTuple>(getRawAnnotations());
+  }
+
+  void replaceAnnotations(DINodeArray Annotations) {
+    replaceOperandWith(3, Annotations.get());
+  }
+
   static bool classof(const Metadata *MD) {
     return MD->getMetadataID() == DIBasicTypeKind;
   }
@@ -1112,6 +1127,10 @@ class DIDerivedType : public DIType {
   }
   Metadata *getRawAnnotations() const { return getOperand(5); }
 
+  void replaceAnnotations(DINodeArray Annotations) {
+    replaceOperandWith(5, Annotations.get());
+  }
+
   /// Get casted version of extra data.
   /// @{
   DIType *getClassType() const;
@@ -1339,6 +1358,10 @@ class DICompositeType : public DIType {
     return cast_or_null<MDTuple>(getRawAnnotations());
   }
 
+  void replaceAnnotations(DINodeArray Annotations) {
+    replaceOperandWith(13, Annotations.get());
+  }
+
   /// Replace operands.
   ///
   /// If this \a isUniqued() and not \a isResolved(), on a uniquing collision
@@ -1385,26 +1408,30 @@ class DISubroutineType : public DIType {
 
   static DISubroutineType *getImpl(LLVMContext &Context, DIFlags Flags,
                                    uint8_t CC, DITypeRefArray TypeArray,
-                                   StorageType Storage,
+                                   DINodeArray Annotations, StorageType Storage,
                                    bool ShouldCreate = true) {
-    return getImpl(Context, Flags, CC, TypeArray.get(), Storage, ShouldCreate);
+    return getImpl(Context, Flags, CC, TypeArray.get(), Annotations.get(),
+                   Storage, ShouldCreate);
   }
   static DISubroutineType *getImpl(LLVMContext &Context, DIFlags Flags,
                                    uint8_t CC, Metadata *TypeArray,
-                                   StorageType Storage,
+                                   Metadata *Annotations, StorageType Storage,
                                    bool ShouldCreate = true);
 
   TempDISubroutineType cloneImpl() const {
-    return getTemporary(getContext(), getFlags(), getCC(), getTypeArray());
+    return getTemporary(getContext(), getFlags(), getCC(), getTypeArray(),
+                        getAnnotations());
   }
 
 public:
   DEFINE_MDNODE_GET(DISubroutineType,
-                    (DIFlags Flags, uint8_t CC, DITypeRefArray TypeArray),
-                    (Flags, CC, TypeArray))
+                    (DIFlags Flags, uint8_t CC, DITypeRefArray TypeArray,
+                     DINodeArray Annotations = nullptr),
+                    (Flags, CC, TypeArray, Annotations))
   DEFINE_MDNODE_GET(DISubroutineType,
-                    (DIFlags Flags, uint8_t CC, Metadata *TypeArray),
-                    (Flags, CC, TypeArray))
+                    (DIFlags Flags, uint8_t CC, Metadata *TypeArray,
+                     Metadata *Annotations = nullptr),
+                    (Flags, CC, TypeArray, Annotations))
 
   TempDISubroutineType clone() const { return cloneImpl(); }
   // Returns a new temporary DISubroutineType with updated CC
@@ -1422,6 +1449,15 @@ class DISubroutineType : public DIType {
 
   Metadata *getRawTypeArray() const { return getOperand(3); }
 
+  Metadata *getRawAnnotations() const { return getOperand(4); }
+  DINodeArray getAnnotations() const {
+    return cast_or_null<MDTuple>(getRawAnnotations());
+  }
+
+  void replaceAnnotations(DINodeArray Annotations) {
+    replaceOperandWith(4, Annotations.get());
+  }
+
   static bool classof(const Metadata *MD) {
     return MD->getMetadataID() == DISubroutineTypeKind;
   }
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 34053a5ca9c8e..813454aefd9e1 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -5210,7 +5210,7 @@ bool LLParser::parseDIEnumerator(MDNode *&Result, bool IsDistinct) {
 
 /// parseDIBasicType:
 ///   ::= !DIBasicType(tag: DW_TAG_base_type, name: "int", size: 32, align: 32,
-///                    encoding: DW_ATE_encoding, flags: 0)
+///                    encoding: DW_ATE_encoding, flags: 0, annotations: !1)
 bool LLParser::parseDIBasicType(MDNode *&Result, bool IsDistinct) {
 #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED)                                    \
   OPTIONAL(tag, DwarfTagField, (dwarf::DW_TAG_base_type));                     \
@@ -5218,12 +5218,14 @@ bool LLParser::parseDIBasicType(MDNode *&Result, bool IsDistinct) {
   OPTIONAL(size, MDUnsignedField, (0, UINT64_MAX));                            \
   OPTIONAL(align, MDUnsignedField, (0, UINT32_MAX));                           \
   OPTIONAL(encoding, DwarfAttEncodingField, );                                 \
-  OPTIONAL(flags, DIFlagField, );
+  OPTIONAL(flags, DIFlagField, );                                              \
+  OPTIONAL(annotations, MDField, );
   PARSE_MD_FIELDS();
 #undef VISIT_MD_FIELDS
 
-  Result = GET_OR_DISTINCT(DIBasicType, (Context, tag.Val, name.Val, size.Val,
-                                         align.Val, encoding.Val, flags.Val));
+  Result = GET_OR_DISTINCT(DIBasicType,
+                           (Context, tag.Val, name.Val, size.Val, align.Val,
+                            encoding.Val, flags.Val, annotations.Val));
   return false;
 }
 
@@ -5360,12 +5362,13 @@ bool LLParser::parseDISubroutineType(MDNode *&Result, bool IsDistinct) {
 #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED)                                    \
   OPTIONAL(flags, DIFlagField, );                                              \
   OPTIONAL(cc, DwarfCCField, );                                                \
-  REQUIRED(types, MDField, );
+  REQUIRED(types, MDField, );                                                  \
+  OPTIONAL(annotations, MDField, );
   PARSE_MD_FIELDS();
 #undef VISIT_MD_FIELDS
 
-  Result = GET_OR_DISTINCT(DISubroutineType,
-                           (Context, flags.Val, cc.Val, types.Val));
+  Result = GET_OR_DISTINCT(DISubroutineType, (Context, flags.Val, cc.Val,
+                                              types.Val, annotations.Val));
   return false;
 }
 
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 9102f3a60cffc..bc06c55f1662c 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -1527,7 +1527,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     break;
   }
   case bitc::METADATA_BASIC_TYPE: {
-    if (Record.size() < 6 || Record.size() > 7)
+    if (Record.size() < 6 || Record.size() > 8)
       return error("Invalid record");
 
     IsDistinct = Record[0];
@@ -1535,10 +1535,14 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
                                 ? static_cast<DINode::DIFlags>(Record[6])
                                 : DINode::FlagZero;
 
+    Metadata *Annotations = nullptr;
+    if (Record.size() > 7 && Record[7])
+      Annotations = getMDOrNull(Record[7]);
+
     MetadataList.assignValue(
         GET_OR_DISTINCT(DIBasicType,
                         (Context, Record[1], getMDString(Record[2]), Record[3],
-                         Record[4], Record[5], Flags)),
+                         Record[4], Record[5], Flags, Annotations)),
         NextMetadataNo);
     NextMetadataNo++;
     break;
@@ -1703,7 +1707,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     break;
   }
   case bitc::METADATA_SUBROUTINE_TYPE: {
-    if (Record.size() < 3 || Record.size() > 4)
+    if (Record.size() < 3 || Record.size() > 5)
       return error("Invalid record");
     bool IsOldTypeRefArray = Record[0] < 2;
     unsigned CC = (Record.size() > 3) ? Record[3] : 0;
@@ -1713,9 +1717,13 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     Metadata *Types = getMDOrNull(Record[2]);
     if (LLVM_UNLIKELY(IsOldTypeRefArray))
       Types = MetadataList.upgradeTypeRefArray(Types);
+    Metadata *Annotations = nullptr;
+    if (Record.size() > 4 && Record[4])
+      Annotations = getMDOrNull(Record[4]);
 
     MetadataList.assignValue(
-        GET_OR_DISTINCT(DISubroutineType, (Context, Flags, CC, Types)),
+        GET_OR_DISTINCT(DISubroutineType,
+                        (Context, Flags, CC, Types, Annotations)),
         NextMetadataNo);
     NextMetadataNo++;
     break;
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 6d01e3b4d8218..d7b5c4f7f3a1f 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1798,6 +1798,7 @@ void ModuleBitcodeWriter::writeDIBasicType(const DIBasicType *N,
   Record.push_back(N->getAlignInBits());
   Record.push_back(N->getEncoding());
   Record.push_back(N->getFlags());
+  Record.push_back(VE.getMetadataOrNullID(N->getRawAnnotations()));
 
   Stream.EmitRecord(bitc::METADATA_BASIC_TYPE, Record, Abbrev);
   Record.clear();
@@ -1893,6 +1894,7 @@ void ModuleBitcodeWriter::writeDISubroutineType(
   Record.push_back(N->getFlags());
   Record.push_back(VE.getMetadataOrNullID(N->getTypeArray().get()));
   Record.push_back(N->getCC());
+  Record.push_back(VE.getMetadataOrNullID(N->getRawAnnotations()));
 
   Stream.EmitRecord(bitc::METADATA_SUBROUTINE_TYPE, Record, Abbrev);
   Record.clear();
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 56c288ee95b43..203f496536002 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -696,7 +696,9 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIBasicType *BTy) {
   if (!Name.empty())
     addString(Buffer, dwarf::DW_AT_name, Name);
 
-  // An unspecified type only has a name attribute.
+  addAnnotation(Buffer, BTy->getAnnotations());
+
+  // An unspecified type only has a name attribute & annotations.
   if (BTy->getTag() == dwarf::DW_TAG_unspecified_type)
     return;
 
@@ -865,6 +867,8 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DISubroutineType *CTy) {
 
   if (CTy->isRValueReference())
     addFlag(Buffer, dwarf::DW_AT_rvalue_reference);
+
+  addAnnotation(Buffer, CTy->getAnnotations());
 }
 
 void DwarfUnit::addAnnotation(DIE &Buffer, DINodeArray Annotations) {
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 941f6a7a7d823..b37c01a10d025 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2096,9 +2096,9 @@ static void writeDIEnumerator(raw_ostream &Out, const DIEnumerator *N,
 }
 
 static void writeDIBasicType(raw_ostream &Out, const DIBasicType *N,
-                             AsmWriterContext &) {
+                             AsmWriterContext &WriterCtx) {
   Out << "!DIBasicType(";
-  MDFieldPrinter Printer(Out);
+  MDFieldPrinter Printer(Out, WriterCtx);
   if (N->getTag() != dwarf::DW_TAG_base_type)
     Printer.printTag(N);
   Printer.printString("name", N->getName());
@@ -2107,6 +2107,7 @@ static void writeDIBasicType(raw_ostream &Out, const DIBasicType *N,
   Printer.printDwarfEnum("encoding", N->getEncoding(),
                          dwarf::AttributeEncodingString);
   Printer.printDIFlags("flags", N->getFlags());
+  Printer.printMetadata("annotations", N->getRawAnnotations());
   Out << ")";
 }
 
@@ -2202,6 +2203,7 @@ static void writeDISubroutineType(raw_ostream &Out, const DISubroutineType *N,
   Printer.printDwarfEnum("cc", N->getCC(), dwarf::ConventionString);
   Printer.printMetadata("types", N->getRawTypeArray(),
                         /* ShouldSkipNull */ false);
+  Printer.printMetadata("annotations", N->getRawAnnotations());
   Out << ")";
 }
 
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 570515505607f..264d8e467fde0 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -663,12 +663,12 @@ DIEnumerator *DIEnumerator::getImpl(LLVMContext &Context, const APInt &Value,
 DIBasicType *DIBasicType::getImpl(LLVMContext &Context, unsigned Tag,
                                   MDString *Name, uint64_t SizeInBits,
                                   uint32_t AlignInBits, unsigned Encoding,
-                                  DIFlags Flags, StorageType Storage,
-                                  bool ShouldCreate) {
+                                  DIFlags Flags, Metadata *Annotations,
+                                  StorageType Storage, bool ShouldCreate) {
   assert(isCanonical(Name) && "Expected canonical MDString");
-  DEFINE_GETIMPL_LOOKUP(DIBasicType,
-                        (Tag, Name, SizeInBits, AlignInBits, Encoding, Flags));
-  Metadata *Ops[] = {nullptr, nullptr, Name};
+  DEFINE_GETIMPL_LOOKUP(DIBasicType, (Tag, Name, SizeInBits, AlignInBits,
+                                      Encoding, Flags, Annotations));
+  Metadata *Ops[] = {nullptr, nullptr, Name, Annotations};
   DEFINE_GETIMPL_STORE(DIBasicType,
                        (Tag, SizeInBits, AlignInBits, Encoding, Flags), Ops);
 }
@@ -872,10 +872,11 @@ DISubroutineType::DISubroutineType(LLVMContext &C, StorageType Storage,
 
 DISubroutineType *DISubroutineType::getImpl(LLVMContext &Context, DIFlags Flags,
                                             uint8_t CC, Metadata *TypeArray,
+                                            Metadata *Annotations,
                                             StorageType Storage,
                                             bool ShouldCreate) {
-  DEFINE_GETIMPL_LOOKUP(DISubroutineType, (Flags, CC, TypeArray));
-  Metadata *Ops[] = {nullptr, nullptr, nullptr, TypeArray};
+  DEFINE_GETIMPL_LOOKUP(DISubroutineType, (Flags, CC, TypeArray, Annotations));
+  Metadata *Ops[] = {nullptr, nullptr, nullptr, TypeArray, Annotations};
   DEFINE_GETIMPL_STORE(DISubroutineType, (Flags, CC), Ops);
 }
 
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 399fe0dad26c7..86bfcce599e77 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -465,25 +465,29 @@ template <> struct MDNodeKeyImpl<DIBasicType> {
   uint32_t AlignInBits;
   unsigned Encoding;
   unsigned Flags;
+  Metadata *Annotations;
 
   MDNodeKeyImpl(unsigned Tag, MDString *Name, uint64_t SizeInBits,
-                uint32_t AlignInBits, unsigned Encoding, unsigned Flags)
+                uint32_t AlignInBits, unsigned Encoding, unsigned Flags,
+                Metadata *Annotations)
       : Tag(Tag), Name(Name), SizeInBits(SizeInBits), AlignInBits(AlignInBits),
-        Encoding(Encoding), Flags(Flags) {}
+        Encoding(Encoding), Flags(Flags), Annotations(Annotations) {}
   MDNodeKeyImpl(const DIBasicType *N)
       : Tag(N->getTag()), Name(N->getRawName()), SizeInBits(N->getSizeInBits()),
         AlignInBits(N->getAlignInBits()), Encoding(N->getEncoding()),
-        Flags(N->getFlags()) {}
+        Flags(N->getFlags()), Annotations(N->getRawAnnotations()) {}
 
   bool isKeyOf(const DIBasicType *RHS) const {
     return Tag == RHS->getTag() && Name == RHS->getRawName() &&
            SizeInBits == RHS->getSizeInBits() &&
            AlignInBits == RHS->getAlignInBits() &&
-           Encoding == RHS->getEncoding() && Flags == RHS->getFlags();
+           Encoding == RHS->getEncoding() && Flags == RHS->getFlags() &&
+           Annotations == RHS->getRawAnnotations();
   }
 
   unsigned getHashValue() const {
-    return hash_combine(Tag, Name, SizeInBits, AlignInBits, Encoding);
+    return hash_combine(Tag, Name, SizeInBits, AlignInBits, Encoding,
+                        Annotations);
   }
 };
 
@@ -712,18 +716,24 @@ template <> struct MDNodeKeyImpl<DISubroutineType> {
   unsigned Flags;
   uint8_t CC;
   Metadata *TypeArray;
+  Metadata *Annotations;
 
-  MDNodeKeyImpl(unsigned Flags, uint8_t CC, Metadata *TypeArray)
-      : Flags(Flags), CC(CC), TypeArray(TypeArray) {}
+  MDNodeKeyImpl(unsigned Flags, uint8_t CC, Metadata *TypeArray,
+                Metadata *Annotations)
+      : Flags(Flags), CC(CC), TypeArray(TypeArray), Annotations(Annotations) {}
   MDNodeKeyImpl(const DISubroutineType *N)
-      : Flags(N->getFlags()), CC(N->getCC()), TypeArray(N->getRawTypeArray()) {}
+      : Flags(N->getFlags()), CC(N->getCC()), TypeArray(N->getRawTypeArray()),
+        Annotations(N->getRawAnnotations()) {}
 
   bool isKeyOf(const DISubroutineType *RHS) const {
     return Flags == RHS->getFlags() && CC == RHS->getCC() &&
-           TypeArray == RHS->getRawTypeArray();
+           TypeArray == RHS->getRawTypeArray() &&
+           Annotations == RHS->getRawAnnotations();
   }
 
-  unsigned getHashValue() const { return hash_combine(Flags, CC, TypeArray); }
+  unsigned getHashValue() const {
+    return hash_combine(Flags, CC, TypeArray, Annotations);
+  }
 };
 
 template <> struct MDNodeKeyImpl<DIFile> {
diff --git a/llvm/test/Bitcode/attr-btf_tag-dibasic.ll b/llvm/test/Bitcode/attr-btf_tag-dibasic.ll
new file mode 100644
index 0000000000000..7a73f921466d9
--- /dev/null
+++ b/llvm/test/Bitcode/attr-btf_tag-dibasic.ll
@@ -0,0 +1,36 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   int __tag1 g;
+; Compilation flag:
+;   clang -S -g -emit-llvm test.c
+
+ at g = dso_local global i32 0, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!8, !9, !10, !11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 2, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git c15ba1bb9498fa04f6c374337313df43486c9713)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "79feb01d60b549b43abc493c324fe2a8")
+!4 = !{!0}
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !6)
+!6 = !{!7}
+!7 = !{!"btf:type_tag", !"tag1"}
+
+; CHECK: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L1:[0-9]+]], isLocal: false, isDefinition: true)
+; CHECK: ![[L1]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed, annotations: ![[L2:[0-9]+]])
+; CHECK: ![[L2]] = !{![[L3:[0-9]+]]}
+; CHECK: ![[L3]] = !{!"btf:type_tag", !"tag1"}
+
+!8 = !{i32 7, !"Dwarf Version", i32 5}
+!9 = !{i32 2, !"Debug Info Version", i32 3}
+!10 = !{i32 1, !"wchar_size", i32 4}
+!11 = !{i32 8, !"PIC Level", i32 2}
+!12 = !{i32 7, !"PIE Level", i32 2}
+!13 = !{i32 7, !"uwtable", i32 2}
+!14 = !{i32 7, !"frame-pointer", i32 2}
+!15 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git c15ba1bb9498fa04f6c374337313df43486c9713)"}
diff --git a/llvm/test/Bitcode/attr-btf_tag-disubroutine.ll b/llvm/test/Bitcode/attr-btf_tag-disubroutine.ll
new file mode 100644
index 0000000000000..277348c3eb461
--- /dev/null
+++ b/llvm/test/Bitcode/attr-btf_tag-disubroutine.ll
@@ -0,0 +1,41 @@
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   int (__tag1 * g)(void);
+
+; Compilation flag:
+;   clang -S -g -emit-llvm test.c
+
+ at g = dso_local global ptr null, align 8, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!11, !12, !13, !14, !15, !16, !17}
+!llvm.ident = !{!18}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 2, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git c15ba1bb9498fa04f6c374337313df43486c9713)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "2ed8742fd12b44b948de1ac5e433bd63")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64)
+!6 = !DISubroutineType(types: !7, annotations: !9)
+!7 = !{!8}
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !{!10}
+!10 = !{!"btf:type_tag", !"tag1"}
+
+; CHECK: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L1:[0-9]+]], isLocal: false, isDefinition: true)
+; CHECK: ![[L1]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L2:[0-9]+]], size: [[#]])
+; CHECK: ![[L2]] = !DISubroutineType(types: ![[#]], annotations: ![[L3:[0-9]+]])
+; CHECK: ![[L3]] = !{![[L4:[0-9]+]]}
+; CHECK: ![[L4]] = !{!"btf:type_tag", !"tag1"}
+
+!11 = !{i32 7, !"Dwarf Version", i32 5}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{i32 8, !"PIC Level", i32 2}
+!15 = !{i32 7, !"PIE Level", i32 2}
+!16 = !{i32 7, !"uwtable", i32 2}
+!17 = !{i32 7, !"frame-pointer", i32 2}
+!18 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git c15ba1bb9498fa04f6c374337313df43486c9713)"}
diff --git a/llvm/test/DebugInfo/attr-btf_type_tag.ll b/llvm/test/DebugInfo/attr-btf_type_tag.ll
index 47a2aa59106d1..329b95cb5d63b 100644
--- a/llvm/test/DebugInfo/attr-btf_type_tag.ll
+++ b/llvm/test/DebugInfo/attr-btf_type_tag.ll
@@ -4,59 +4,121 @@
 ; Source:
 ;   #define __tag1 __attribute__((btf_type_tag("tag1")))
 ;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   #define __tag3 __attribute__((btf_type_tag("tag3")))
+;   #define __tag4 __attribute__((btf_type_tag("tag4")))
+;
+;   int   __tag1  a;
+;   int * __tag2  b;
+;   void  __tag3 *c;
+;   void (__tag4 *d)(void);
+;
 ;
-;   int * __tag1 * __tag2 *g;
 ; Compilation flag:
 ;   clang -target x86_64 -g -S -emit-llvm t.c
+;
+; Note: only "btf:type_tag" annotations are checked for brevity.
 
- at g = dso_local global ptr null, align 8, !dbg !0
+ at a = dso_local global i32 0, align 4, !dbg !0
+ at b = dso_local global ptr null, align 8, !dbg !5
+ at c = dso_local global ptr null, align 8, !dbg !11
+ at d = dso_local global ptr null, align 8, !dbg !19
 
 !llvm.dbg.cu = !{!2}
-!llvm.module.flags = !{!13, !14, !15, !16, !17}
-!llvm.ident = !{!18}
+!llvm.module.flags = !{!31, !32, !33, !34, !35}
+!llvm.ident = !{!36}
 
 !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
-!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true)
-!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 14.0.0 (https://github.com/llvm/llvm-project.git 2c240a5eefae1a945dfd36cdaa0c677eca90dd82)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
-!3 = !DIFile(filename: "t.c", directory: "/home/yhs/work/tests/llvm/btf_tag_type")
-!4 = !{!0}
-!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64, annotations: !11)
-!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64, annotations: !9)
-!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64)
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 6, type: !28, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang version 17.0.0 (https://github.com/llvm/llvm-project.git ffde01565bce81795ba0442108742557a9a4562d)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "test.c", directory: "/home/eddy/work/tmp", checksumkind: CSK_MD5, checksum: "71845c02e58f6b1a8b0162797b4d3f37")
+!4 = !{!0, !5, !11, !19}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+
+; CHECK:                 DW_TAG_variable
+; CHECK-NEXT:               DW_AT_name      ("a")
+; CHECK-NEXT:               DW_AT_type      (0x[[T1:[0-9a-f]+]] "int")
+
+; CHECK:      0x[[T1]]:  DW_TAG_base_type
+; CHECK-NEXT:               DW_AT_name      ("int")
+
+; CHECK:                    DW_TAG_LLVM_annotation
+; CHECK-NEXT:                 DW_AT_name    ("btf:type_tag")
+; CHECK-NEXT:                 DW_AT_const_value     ("tag1")
+
+!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 7, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, annotations: !9)
 !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
 !9 = !{!10}
-!10 = !{!"btf_type_tag", !"tag1"}
-!11 = !{!12}
-!12 = !{!"btf_type_tag", !"tag2"}
+!10 = !{!"btf:type_tag", !"tag2"}
+!11 = !DIGlobalVariableExpression(var: !12, expr: !DIExpression())
+
+; CHECK:                DW_TAG_variable
+; CHECK-NEXT:               DW_AT_name      ("b")
+; CHECK-NEXT:               DW_AT_type      (0x[[T2:[0-9a-f]+]] "int *")
+
+; CHECK:      0x[[T2]]:   DW_TAG_pointer_type
+; CHECK-NEXT:               DW_AT_type      (0x[[T3:[0-9a-f]+]] "int")
+
+; CHECK:                    DW_TAG_LLVM_annotation
+; CHECK-NEXT:                 DW_AT_name    ("btf:type_tag")
+; CHECK-NEXT:                 DW_AT_const_value     ("tag2")
+
+; CHECK:      0x[[T3]]:   DW_TAG_base_type
+; CHECK-NEXT:               DW_AT_name      ("int")
+; CHECK-NEXT:               DW_AT_encoding  (DW_ATE_signed)
+
+!12 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 8, type: !13, isLocal: false, isDefinition: true)
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64, annotations: !17)
+!14 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "void", annotations: !15)
+!15 = !{!16}
+!16 = !{!"btf:type_tag", !"tag3"}
+!17 = !{!18}
+!18 = !{!"btf_type_tag", !"tag3"}
+
+; CHECK:                DW_TAG_variable
+; CHECK-NEXT:             DW_AT_name      ("c")
+; CHECK-NEXT:             DW_AT_type      (0x[[T4:[0-9a-f]+]] "void *")
 
-; CHECK:      DW_TAG_variable
-; CHECK-NEXT:   DW_AT_name      ("g")
-; CHECK-NEXT:   DW_AT_type      (0x[[T1:[0-9a-f]+]] "int ***")
+; CHECK:      0x[[T4]]: DW_TAG_pointer_type
+; CHECK-NEXT:             DW_AT_type      (0x[[T5:[0-9a-f]+]] "void")
 
-; CHECK:      0x[[T1]]: DW_TAG_pointer_type
-; CHECK-NEXT:   DW_AT_type      (0x[[T2:[0-9a-f]+]] "int **")
+; CHECK:      0x[[T5]]: DW_TAG_unspecified_type
+; CHECK-NEXT:             DW_AT_name      ("void")
 
-; CHECK:        DW_TAG_LLVM_annotation
-; CHECK-NEXT:     DW_AT_name    ("btf_type_tag")
-; CHECK-NEXT:     DW_AT_const_value     ("tag2")
+; CHECK:                  DW_TAG_LLVM_annotation
+; CHECK-NEXT:               DW_AT_name    ("btf:type_tag")
+; CHECK-NEXT:               DW_AT_const_value     ("tag3")
 
-; CHECK:        NULL
+!19 = !DIGlobalVariableExpression(var: !20, expr: !DIExpression())
+!20 = distinct !DIGlobalVariable(name: "d", scope: !2, file: !3, line: 9, type: !21, isLocal: false, isDefinition: true)
+!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64, annotations: !26)
+!22 = !DISubroutineType(types: !23, annotations: !24)
+!23 = !{null}
+!24 = !{!25}
+!25 = !{!"btf:type_tag", !"tag4"}
+!26 = !{!27}
+!27 = !{!"btf_type_tag", !"tag4"}
+!28 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !29)
+!29 = !{!30}
+!30 = !{!"btf:type_tag", !"tag1"}
 
-; CHECK:      0x[[T2]]: DW_TAG_pointer_type
-; CHECK-NEXT:   DW_AT_type      (0x[[T3:[0-9a-f]+]] "int *")
+; CHECK:                DW_TAG_variable
+; CHECK-NEXT:             DW_AT_name      ("d")
+; CHECK-NEXT:             DW_AT_type      (0x[[T6:[0-9a-f]+]] "void (*)(")
 
-; CHECK:        DW_TAG_LLVM_annotation
-; CHECK-NEXT:     DW_AT_name    ("btf_type_tag")
-; CHECK-NEXT:     DW_AT_const_value     ("tag1")
+; CHECK:      0x[[T6]]: DW_TAG_pointer_type
+; CHECK-NEXT:             DW_AT_type      (0x[[T7:[0-9a-f]+]] "void (")
 
-; CHECK:        NULL
+; CHECK:      0x[[T7]]: DW_TAG_subroutine_type
+; CHECK-NEXT:             DW_AT_prototyped        (true)
 
-; CHECK:      0x[[T3]]: DW_TAG_pointer_type
-; CHECK-NEXT:   DW_AT_type      (0x{{[0-9a-f]+}} "int")
+; CHECK:                  DW_TAG_LLVM_annotation
+; CHECK-NEXT:               DW_AT_name    ("btf:type_tag")
+; CHECK-NEXT:               DW_AT_const_value     ("tag4")
 
-!13 = !{i32 7, !"Dwarf Version", i32 4}
-!14 = !{i32 2, !"Debug Info Version", i32 3}
-!15 = !{i32 1, !"wchar_size", i32 4}
-!16 = !{i32 7, !"uwtable", i32 1}
-!17 = !{i32 7, !"frame-pointer", i32 2}
-!18 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 2c240a5eefae1a945dfd36cdaa0c677eca90dd82)"}
+!31 = !{i32 7, !"Dwarf Version", i32 5}
+!32 = !{i32 2, !"Debug Info Version", i32 3}
+!33 = !{i32 1, !"wchar_size", i32 4}
+!34 = !{i32 7, !"uwtable", i32 2}
+!35 = !{i32 7, !"frame-pointer", i32 2}
+!36 = !{!"clang version 17.0.0 (https://github.com/llvm/llvm-project.git ffde01565bce81795ba0442108742557a9a4562d)"}

>From ba6a989deeb98950822d0b50e113d1f887a509b2 Mon Sep 17 00:00:00 2001
From: Eduard Zingerman <eddyz87 at gmail.com>
Date: Tue, 14 Feb 2023 01:19:50 +0200
Subject: [PATCH 2/3] [DebugInfo][BPF] Add 'btf:type_tag' annotation in DWARF

This commit is a follow-up for BPF mailing list discussion at [1].
It changes the way `__attribute__((btf_type_tag("...")))`s are
represented in DWARF.

Prior to this commit type tags could only be attached to pointers.
Such attachments associated the tags with a pointee type.
E.g. for the following C code:

    int __attribute__((btf_type_tag("tag1"))) *g;

Generated DWARF looked as follows:

    0x0000001e:   DW_TAG_variable
                    DW_AT_name      ("g")
                    DW_AT_type      (0x00000029 "int *")

    0x00000029:   DW_TAG_pointer_type
                    DW_AT_type      (0x00000032 "int")

    0x0000002e:     DW_TAG_LLVM_annotation
                      DW_AT_name    ("btf_type_tag")
                      DW_AT_const_value     ("tag1")

    0x00000032:   DW_TAG_base_type
                    DW_AT_name      ("int")

The goal of this commit is to allow attachment of type tags to the
tagged types instead. E.g. for the same example DWARF should look as
follows:

    0x0000001e:   DW_TAG_variable
                    DW_AT_name      ("g")
                    DW_AT_type      (0x00000029 "int *")

    0x00000029:   DW_TAG_pointer_type
                    DW_AT_type      (0x00000032 "int")

    0x00000032:   DW_TAG_base_type
                    DW_AT_name      ("int")

    0x00000036:     DW_TAG_LLVM_annotation
                      DW_AT_name    ("btf:type_tag")
                      DW_AT_const_value     ("tag1")

A new tag name, `btf:type_tag`, is used so that DWARF consumers
could distinguish between old and new attachment semantics.

This feature is mostly used by Linux Kernel in combination with tool
named pahole [2]. Reasonably recent versions of pahole generate
errors (1.23, 1.24) or warnings (1.25) when `DW_TAG_LLVM_annotation`
is attached to `DW_TAG_base_type` or `DW_TAG_unspecified_type`.
Hence the `btf:type_tag` generation is controlled by a hidden option
`-mllvm -btf-type-tag-v2`. The goal is to provide a way for tooling to
work on adding support `btf:type_tag` and eventually replace
`btf_type_tag` by `btf:type_tag`, removing the above option.

The commit includes the following changes:
- Changes in debug info generation:
  - New method `DIBuilder::createAnnotationsPlaceholder()` is added,
    it creates a temporary `DIDerivedType` that plays as annotations
    placeholder while debug info metadata is being constructed;
  - New overload for `CGDebugInfo::CreateType` method is added:

      llvm::DIType *CGDebugInfo::CreateType(const BTFTagAttributedType *Ty,
                                            llvm::DIFile *Unit);

    This overload collects BTF type tags in `Ty`, creates annotations
    placeholder pointing to the base type of `Ty`, registers the
    placeholder in the `CGDebugInfo::AnnotationsPlaceholder` vector.
  - `CGDebugInfo::finalize()` is updated to do the following for each
    annotation placeholder:
    - clone underlying base type;
    - attach annotations the clone using `replaceAnnotations()` call;
    - replace all placeholder usages by a clone.
  Such scheme allows to deal with type cycles.

- Changes in AST construction:
  - `ASTContext::getBTFTagAttributedType()` is updated to ensure that
    `BTFTagAttributedType` always wraps `QualType` w/o local
    constant/volatile/restricted qualifiers. This simplifies debug info
    generation.

[1] https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/
[2] https://git.kernel.org/pub/scm/devel/pahole/pahole.git/

This was previously tracked as differential revision:
https://reviews.llvm.org/D143967
---
 clang/lib/CodeGen/CGDebugInfo.cpp             | 204 +++++++++++++++---
 clang/lib/CodeGen/CGDebugInfo.h               |   3 +
 .../test/CodeGen/attr-btf_type_tag-circular.c |  18 ++
 clang/test/CodeGen/attr-btf_type_tag-const.c  |  41 ++++
 .../test/CodeGen/attr-btf_type_tag-func-ptr.c |  19 +-
 clang/test/CodeGen/attr-btf_type_tag-func.c   |  50 +++--
 .../test/CodeGen/attr-btf_type_tag-restrict.c |  21 ++
 .../CodeGen/attr-btf_type_tag-similar-type.c  |  47 ++--
 .../CodeGen/attr-btf_type_tag-typedef-field.c |  66 ++++--
 clang/test/CodeGen/attr-btf_type_tag-var.c    |  77 +++++--
 clang/test/CodeGen/attr-btf_type_tag-void.c   |  12 ++
 .../test/CodeGen/attr-btf_type_tag-volatile.c |  18 ++
 llvm/include/llvm/IR/DIBuilder.h              |   3 +
 llvm/lib/IR/DIBuilder.cpp                     |  11 +
 14 files changed, 493 insertions(+), 97 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-btf_type_tag-circular.c
 create mode 100644 clang/test/CodeGen/attr-btf_type_tag-const.c
 create mode 100644 clang/test/CodeGen/attr-btf_type_tag-restrict.c
 create mode 100644 clang/test/CodeGen/attr-btf_type_tag-void.c
 create mode 100644 clang/test/CodeGen/attr-btf_type_tag-volatile.c

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index fac278f0e20a4..9518a75a3b1f1 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -56,6 +56,16 @@
 using namespace clang;
 using namespace clang::CodeGen;
 
+// Temporarily hide new format for btf_type_tags / DW_TAG_LLVM_annotation
+// behind an option to allow transitory period for tooling dependent on
+// this annotation. The goal is to remove this flag after transitory period.
+static llvm::cl::opt<bool> BTFTypeTagV2(
+    "btf-type-tag-v2", llvm::cl::Hidden,
+    llvm::cl::desc("For __attribute__((btf_type_tag(...))) generate "
+                   "DW_TAG_LLVM_annotation tags with DW_AT_name 'btf:type_tag' "
+                   "attached to annotated type itself"),
+    llvm::cl::init(false));
+
 static uint32_t getTypeAlignIfRequired(const Type *Ty, const ASTContext &Ctx) {
   auto TI = Ctx.getTypeInfo(Ty);
   return TI.isAlignRequired() ? TI.Align : 0;
@@ -1185,6 +1195,129 @@ CGDebugInfo::getOrCreateRecordFwdDecl(const RecordType *Ty,
   return RetTy;
 }
 
+static QualType collectBTFTypeTagAnnotations(
+    llvm::LLVMContext &Context, llvm::DIBuilder &DBuilder,
+    llvm::SmallVectorImpl<llvm::Metadata *> &Annots,
+    const BTFTagAttributedType *BTFAttrTy, const char *TagName) {
+  QualType WrappedTy;
+
+  do {
+    StringRef TagValue = BTFAttrTy->getAttr()->getBTFTypeTag();
+    if (!TagValue.empty()) {
+      llvm::Metadata *Ops[] = {
+          llvm::MDString::get(Context, TagName),
+          llvm::MDString::get(Context, TagValue),
+      };
+      Annots.insert(Annots.begin(), llvm::MDNode::get(Context, Ops));
+    }
+    WrappedTy = BTFAttrTy->getWrappedType();
+    BTFAttrTy = dyn_cast<BTFTagAttributedType>(WrappedTy);
+  } while (BTFAttrTy);
+
+  return WrappedTy;
+}
+
+static bool retreiveCVR(llvm::DIDerivedType *DTy, QualifierCollector &Qc) {
+  switch (DTy->getTag()) {
+  case llvm::dwarf::DW_TAG_const_type:
+    Qc.addConst();
+    return true;
+  case llvm::dwarf::DW_TAG_volatile_type:
+    Qc.addVolatile();
+    return true;
+  case llvm::dwarf::DW_TAG_restrict_type:
+    Qc.addRestrict();
+    return true;
+  default:
+    return false;
+  }
+}
+
+// Tags returned by QualifierCollector::getNextQualifier() should be
+// applied in the reverse order, thus use recursive function.
+static llvm::DIType *applyQualifiers(llvm::DIBuilder &DBuilder,
+                                     llvm::DIType *Ty, QualifierCollector &Qc) {
+  llvm::dwarf::Tag Tag = getNextQualifier(Qc);
+  if (!Tag)
+    return Ty;
+  Ty = applyQualifiers(DBuilder, Ty, Qc);
+  return DBuilder.createQualifiedType(Tag, Ty);
+}
+
+static bool isAnnotationsPlaceholder(llvm::DIDerivedType *DTy) {
+  return DTy->isTemporary() &&
+         DTy->getTag() == llvm::dwarf::DW_TAG_LLVM_annotation;
+}
+
+llvm::DIType *CGDebugInfo::CreateType(const BTFTagAttributedType *Ty,
+                                      llvm::DIFile *Unit) {
+  SmallVector<llvm::Metadata *, 4> Annotations;
+  auto WrappedTy = collectBTFTypeTagAnnotations(
+      CGM.getLLVMContext(), DBuilder, Annotations, Ty, "btf:type_tag");
+
+  if (!BTFTypeTagV2 || Annotations.empty())
+    return getOrCreateType(WrappedTy, Unit);
+
+  // After discussion with GCC BPF team in [1] it was decided to avoid
+  // attaching BTF type tags to const/volatile/restrict DWARF DIEs.
+  // So, strip qualifiers from WrappedTy and apply those to a final
+  // annotations placeholder instance at the end of this function.
+  //
+  // [1] https://reviews.llvm.org/D143967
+  QualifierCollector Qc;
+  Qc.addCVRQualifiers(WrappedTy.getLocalCVRQualifiers());
+  WrappedTy.removeLocalFastQualifiers(Qualifiers::CVRMask);
+
+  llvm::DIType *WrappedDI = getOrCreateType(WrappedTy, Unit);
+  if (!WrappedDI)
+    WrappedDI = DBuilder.createUnspecifiedType("void");
+
+  // Stripping local CVR qualifiers might not be enough in cases like this:
+  //
+  //   #define __tag __attribute__((btf_type_tag("tag")))
+  //   const int *foo;
+  //   const int *bar(void) {
+  //     return (typeof(*foo) __tag *)(0);
+  //   }
+  //
+  // Here the AST looks like:
+  //
+  //   BTFTagAttributedType
+  //   |  'typeof (*foo) __attribute__((btf_type_tag("tag")))' sugar
+  //   `-TypeOfExprType 'typeof (*foo)' sugar
+  //     |-ParenExpr 'const int' lvalue
+  //     | `- ...
+  //     `-QualType 'const int' const
+  //       `-BuiltinType 'int'
+  //
+  // The BTFTagAttributedType is applied to TypeOfExpr.
+  // For TypeOfExpr the getOrCreateType(), would return instance of
+  // DIDerivedType with tag DW_TAG_const_type.
+  //
+  // To avoid repeating UnwrapTypeForDebugInfo() logic here just
+  // rebuild CVR metadata nodes if necessary.
+  // The above local CVR qualifiers processing is redundant,
+  // but avoids rebuilding metadata nodes in the most common case.
+  while (auto *DTy = dyn_cast<llvm::DIDerivedType>(WrappedDI)) {
+    if (!retreiveCVR(DTy, Qc))
+      break;
+    WrappedDI = DTy->getBaseType();
+  }
+
+  if (auto *DTy = dyn_cast<llvm::DIDerivedType>(WrappedDI))
+    if (isAnnotationsPlaceholder(DTy)) {
+      WrappedDI = DTy->getBaseType();
+      for (llvm::Metadata *O : DTy->getAnnotations()->operands())
+        Annotations.push_back(O);
+    }
+
+  auto *Placeholder = DBuilder.createAnnotationsPlaceholder(
+      WrappedDI, DBuilder.getOrCreateArray(Annotations));
+  AnnotationPlaceholders.push_back(Placeholder);
+
+  return applyQualifiers(DBuilder, Placeholder, Qc);
+}
+
 llvm::DIType *CGDebugInfo::CreatePointerLikeType(llvm::dwarf::Tag Tag,
                                                  const Type *Ty,
                                                  QualType PointeeTy,
@@ -1197,32 +1330,23 @@ llvm::DIType *CGDebugInfo::CreatePointerLikeType(llvm::dwarf::Tag Tag,
       CGM.getTarget().getDWARFAddressSpace(
           CGM.getTypes().getTargetAddressSpace(PointeeTy));
 
-  SmallVector<llvm::Metadata *, 4> Annots;
-  auto *BTFAttrTy = dyn_cast<BTFTagAttributedType>(PointeeTy);
-  while (BTFAttrTy) {
-    StringRef Tag = BTFAttrTy->getAttr()->getBTFTypeTag();
-    if (!Tag.empty()) {
-      llvm::Metadata *Ops[2] = {
-          llvm::MDString::get(CGM.getLLVMContext(), StringRef("btf_type_tag")),
-          llvm::MDString::get(CGM.getLLVMContext(), Tag)};
-      Annots.insert(Annots.begin(),
-                    llvm::MDNode::get(CGM.getLLVMContext(), Ops));
-    }
-    BTFAttrTy = dyn_cast<BTFTagAttributedType>(BTFAttrTy->getWrappedType());
-  }
-
   llvm::DINodeArray Annotations = nullptr;
-  if (Annots.size() > 0)
-    Annotations = DBuilder.getOrCreateArray(Annots);
+  auto *BTFAttrTy = dyn_cast<BTFTagAttributedType>(PointeeTy.getTypePtr());
+  if (!BTFTypeTagV2 && BTFAttrTy) {
+    SmallVector<llvm::Metadata *, 4> AnnotationsVec;
+    collectBTFTypeTagAnnotations(CGM.getLLVMContext(), DBuilder, AnnotationsVec,
+                                 BTFAttrTy, "btf_type_tag");
+    Annotations = DBuilder.getOrCreateArray(AnnotationsVec);
+  }
 
   if (Tag == llvm::dwarf::DW_TAG_reference_type ||
       Tag == llvm::dwarf::DW_TAG_rvalue_reference_type)
     return DBuilder.createReferenceType(Tag, getOrCreateType(PointeeTy, Unit),
                                         Size, Align, DWARFAddressSpace);
-  else
-    return DBuilder.createPointerType(getOrCreateType(PointeeTy, Unit), Size,
-                                      Align, DWARFAddressSpace, StringRef(),
-                                      Annotations);
+
+  return DBuilder.createPointerType(getOrCreateType(PointeeTy, Unit), Size,
+                                    Align, DWARFAddressSpace, StringRef(),
+                                    Annotations);
 }
 
 llvm::DIType *CGDebugInfo::getOrCreateStructPtrType(StringRef Name,
@@ -3543,9 +3667,6 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
     case Type::Attributed:
       T = cast<AttributedType>(T)->getEquivalentType();
       break;
-    case Type::BTFTagAttributed:
-      T = cast<BTFTagAttributedType>(T)->getWrappedType();
-      break;
     case Type::CountAttributed:
       T = cast<CountAttributedType>(T)->desugar();
       break;
@@ -3745,10 +3866,12 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
   case Type::TemplateSpecialization:
     return CreateType(cast<TemplateSpecializationType>(Ty), Unit);
 
+  case Type::BTFTagAttributed:
+    return CreateType(cast<BTFTagAttributedType>(Ty), Unit);
+
   case Type::CountAttributed:
   case Type::Auto:
   case Type::Attributed:
-  case Type::BTFTagAttributed:
   case Type::Adjusted:
   case Type::Decayed:
   case Type::DeducedTemplateSpecialization:
@@ -5921,6 +6044,35 @@ void CGDebugInfo::setDwoId(uint64_t Signature) {
   TheCU->setDWOId(Signature);
 }
 
+static llvm::DIType *copyAnnotations(llvm::DIBuilder &DBuilder,
+                                     llvm::DIDerivedType *Placeholder) {
+  auto *WrappedDI = Placeholder->getBaseType();
+  SmallVector<llvm::Metadata *, 4> Annotations;
+
+  for (const llvm::Metadata *O : Placeholder->getAnnotations()->operands())
+    Annotations.push_back(const_cast<llvm::Metadata *>(O));
+
+  auto AddAnnotations = [&](auto *Type) {
+    if (llvm::DINodeArray OldAnnotations = Type->getAnnotations())
+      for (const llvm::Metadata *O : OldAnnotations->operands())
+        Annotations.push_back(const_cast<llvm::Metadata *>(O));
+    auto Clone = Type->clone();
+    Clone->replaceAnnotations(DBuilder.getOrCreateArray(Annotations));
+    return llvm::MDNode::replaceWithPermanent(std::move(Clone));
+  };
+
+  if (auto *Ty = dyn_cast<llvm::DIBasicType>(WrappedDI))
+    return AddAnnotations(Ty);
+  if (auto *Ty = dyn_cast<llvm::DICompositeType>(WrappedDI))
+    return AddAnnotations(Ty);
+  if (auto *Ty = dyn_cast<llvm::DIDerivedType>(WrappedDI))
+    return AddAnnotations(Ty);
+  if (auto *Ty = dyn_cast<llvm::DISubroutineType>(WrappedDI))
+    return AddAnnotations(Ty);
+
+  return WrappedDI;
+}
+
 void CGDebugInfo::finalize() {
   // Creating types might create further types - invalidating the current
   // element and the size(), so don't cache/reference them.
@@ -5994,6 +6146,10 @@ void CGDebugInfo::finalize() {
     if (auto MD = TypeCache[RT])
       DBuilder.retainType(cast<llvm::DIType>(MD));
 
+  for (auto &Placeholder : AnnotationPlaceholders)
+    DBuilder.replaceTemporary(llvm::TempDIType(Placeholder),
+                              copyAnnotations(DBuilder, Placeholder));
+
   DBuilder.finalize();
 }
 
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index d6db4d711366a..43a093c725f6a 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -170,6 +170,8 @@ class CGDebugInfo {
   /// The key is coroutine real parameters, value is DIVariable in LLVM IR.
   Param2DILocTy ParamDbgMappings;
 
+  std::vector<llvm::DIDerivedType *> AnnotationPlaceholders;
+
   /// Helper functions for getOrCreateType.
   /// @{
   /// Currently the checksum of an interface includes the number of
@@ -217,6 +219,7 @@ class CGDebugInfo {
   llvm::DIType *CreateType(const MemberPointerType *Ty, llvm::DIFile *F);
   llvm::DIType *CreateType(const AtomicType *Ty, llvm::DIFile *F);
   llvm::DIType *CreateType(const PipeType *Ty, llvm::DIFile *F);
+  llvm::DIType *CreateType(const BTFTagAttributedType *Ty, llvm::DIFile *F);
   /// Get enumeration type.
   llvm::DIType *CreateEnumType(const EnumType *Ty);
   llvm::DIType *CreateTypeDefinition(const EnumType *Ty);
diff --git a/clang/test/CodeGen/attr-btf_type_tag-circular.c b/clang/test/CodeGen/attr-btf_type_tag-circular.c
new file mode 100644
index 0000000000000..4bcb20e63bead
--- /dev/null
+++ b/clang/test/CodeGen/attr-btf_type_tag-circular.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -S -emit-llvm -o - %s | FileCheck %s
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+
+struct st {
+  struct st __tag1 *self;
+} g;
+
+// CHECK: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L1:[0-9]+]], isLocal: false, isDefinition: true)
+// CHECK: ![[L1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "st", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L2:[0-9]+]])
+// CHECK: ![[L2]] = !{![[L3:[0-9]+]]}
+// CHECK: ![[L3]] = !DIDerivedType(tag: DW_TAG_member, name: "self", scope: ![[L1]], file: ![[#]], line: [[#]], baseType: ![[L4:[0-9]+]], size: [[#]])
+// CHECK: ![[L4]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L5:[0-9]+]], size: [[#]])
+// CHECK: ![[L5]] = !DICompositeType(tag: DW_TAG_structure_type, name: "st", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L2]], annotations: ![[L7:[0-9]+]])
+// CHECK: ![[L7]] = !{![[L8:[0-9]+]]}
+// CHECK: ![[L8]] = !{!"btf:type_tag", !"tag1"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-const.c b/clang/test/CodeGen/attr-btf_type_tag-const.c
new file mode 100644
index 0000000000000..94d9c05f5345a
--- /dev/null
+++ b/clang/test/CodeGen/attr-btf_type_tag-const.c
@@ -0,0 +1,41 @@
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -S -emit-llvm -o - %s | FileCheck %s
+
+// Check that BTF type tags are not attached to DW_TAG_const_type DIEs
+// in presence of "sugar" expressions that are transparent for
+// CGDebugInfo.cpp:UnwrapTypeForDebugInfo(), but are not transparent
+// for local qualifiers.
+//
+// For details see:
+//   CGDebugInfo::CreateType(const BTFTagAttributedType, llvm::DIFile)
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+#define __tag2 __attribute__((btf_type_tag("tag2")))
+#define __tag3 __attribute__((btf_type_tag("tag3")))
+
+const int *foo;
+typeof(*foo) __tag1 bar;
+
+// CHECK: distinct !DIGlobalVariable(name: "bar", {{.*}}, type: ![[L01:[0-9]+]], {{.*}})
+// CHECK: ![[L01]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L02:[0-9]+]])
+// CHECK: ![[L02]] = !DIBasicType(name: "int", {{.*}}, annotations: ![[L03:[0-9]+]])
+// CHECK: ![[L03]] = !{![[L04:[0-9]+]]}
+// CHECK: ![[L04]] = !{!"btf:type_tag", !"tag1"}
+
+const int __tag2 *buz;
+
+// CHECK: distinct !DIGlobalVariable(name: "buz", {{.*}}, type: ![[L05:[0-9]+]], {{.*}})
+// CHECK: ![[L05]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L06:[0-9]+]], {{.*}})
+// CHECK: ![[L06]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L08:[0-9]+]])
+// CHECK: ![[L08]] = !DIBasicType(name: "int", size: [[#]], {{.*}}, annotations: ![[L09:[0-9]+]])
+// CHECK: ![[L09]] = !{![[L10:[0-9]+]]}
+// CHECK: ![[L10]] = !{!"btf:type_tag", !"tag2"}
+
+typeof(*buz) __tag3 quux;
+
+// CHECK: distinct !DIGlobalVariable(name: "quux", {{.*}}, type: ![[L12:[0-9]+]], {{.*}})
+// CHECK: ![[L12]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L13:[0-9]+]])
+// CHECK: ![[L13]] = !DIBasicType(name: "int", {{.*}}, annotations: ![[L14:[0-9]+]])
+// CHECK: ![[L14]] = !{![[L15:[0-9]+]], ![[L10]]}
+// CHECK: ![[L15]] = !{!"btf:type_tag", !"tag3"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-func-ptr.c b/clang/test/CodeGen/attr-btf_type_tag-func-ptr.c
index 26935c882a017..8567864692202 100644
--- a/clang/test/CodeGen/attr-btf_type_tag-func-ptr.c
+++ b/clang/test/CodeGen/attr-btf_type_tag-func-ptr.c
@@ -1,4 +1,8 @@
 // RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -emit-llvm -o - %s \
+// RUN: | FileCheck --check-prefix CHECK-V2 %s
 
 struct t {
  int (__attribute__((btf_type_tag("rcu"))) *f)();
@@ -8,8 +12,13 @@ int foo(struct t *arg) {
   return arg->a;
 }
 
-// CHECK:      !DIDerivedType(tag: DW_TAG_member, name: "f"
-// CHECK-SAME: baseType: ![[L18:[0-9]+]]
-// CHECK:      ![[L18]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[#]], size: [[#]], annotations: ![[L21:[0-9]+]])
-// CHECK:      ![[L21]] = !{![[L22:[0-9]+]]}
-// CHECK:      ![[L22]] = !{!"btf_type_tag", !"rcu"}
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "f", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L1:[0-9]+]], size: [[#]])
+// CHECK: ![[L1]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[#]], size: [[#]], annotations: ![[L2:[0-9]+]])
+// CHECK: ![[L2]] = !{![[L3:[0-9]+]]}
+// CHECK: ![[L3]] = !{!"btf_type_tag", !"rcu"}
+
+// CHECK-V2: !DIDerivedType(tag: DW_TAG_member, name: "f", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L1:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L1]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L2:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L2]] = !DISubroutineType(types: ![[#]], annotations: ![[L4:[0-9]+]])
+// CHECK-V2: ![[L4]] = !{![[L5:[0-9]+]]}
+// CHECK-V2: ![[L5]] = !{!"btf:type_tag", !"rcu"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-func.c b/clang/test/CodeGen/attr-btf_type_tag-func.c
index dbb8864759148..890d3ab35428b 100644
--- a/clang/test/CodeGen/attr-btf_type_tag-func.c
+++ b/clang/test/CodeGen/attr-btf_type_tag-func.c
@@ -1,5 +1,17 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 -debug-info-kind=limited \
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -emit-llvm -o - %s \
+// RUN: | FileCheck --check-prefixes CHECK-V2 %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 \
+// RUN:   -debug-info-kind=limited -mllvm -btf-type-tag-v2 -emit-llvm -o - %s \
+// RUN: | FileCheck --check-prefixes CHECK-V2 %s
 
 #if DOUBLE_BRACKET_ATTRS
 #define __tag1 [[clang::btf_type_tag("tag1")]]
@@ -15,14 +27,26 @@
 
 int __tag1 * __tag2 *foo(int __tag1 * __tag2 *arg) { return arg; }
 
-// CHECK: distinct !DISubprogram(name: "foo", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L9:[0-9]+]]
-// CHECK: ![[L9]] = !DISubroutineType(types: ![[L10:[0-9]+]]
-// CHECK: ![[L10]] = !{![[L11:[0-9]+]], ![[L11]]}
-// CHECK: ![[L11]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L12:[0-9]+]], size: [[#]], annotations: ![[L16:[0-9]+]]
-// CHECK: ![[L12]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L13:[0-9]+]], size: [[#]], annotations: ![[L14:[0-9]+]]
-// CHECK: ![[L13]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed
-// CHECK: ![[L14]] = !{![[L15:[0-9]+]]}
-// CHECK: ![[L15]] = !{!"btf_type_tag", !"tag1"}
-// CHECK: ![[L16]] = !{![[L17:[0-9]+]]}
-// CHECK: ![[L17]] = !{!"btf_type_tag", !"tag2"}
-// CHECK: !DILocalVariable(name: "arg", arg: 1, scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L11]])
+// CHECK: distinct !DISubprogram(name: "foo", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L01:[0-9]+]], {{.*}})
+// CHECK: ![[L01]] = !DISubroutineType(types: ![[L02:[0-9]+]])
+// CHECK: ![[L02]] = !{![[L03:[0-9]+]], ![[L03]]}
+// CHECK: ![[L03]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L04:[0-9]+]], size: [[#]], annotations: ![[L05:[0-9]+]])
+// CHECK: ![[L04]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L06:[0-9]+]], size: [[#]], annotations: ![[L07:[0-9]+]])
+// CHECK: ![[L06]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed)
+// CHECK: ![[L07]] = !{![[L11:[0-9]+]]}
+// CHECK: ![[L11]] = !{!"btf_type_tag", !"tag1"}
+// CHECK: ![[L05]] = !{![[L12:[0-9]+]]}
+// CHECK: ![[L12]] = !{!"btf_type_tag", !"tag2"}
+// CHECK: !DILocalVariable(name: "arg", arg: 1, scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L03]])
+
+// CHECK-V2: distinct !DISubprogram(name: "foo", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L01:[0-9]+]], {{.*}})
+// CHECK-V2: ![[L01]] = !DISubroutineType(types: ![[L02:[0-9]+]])
+// CHECK-V2: ![[L02]] = !{![[L03:[0-9]+]], ![[L03]]}
+// CHECK-V2: ![[L03]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L04:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L04]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L06:[0-9]+]], size: [[#]], annotations: ![[L07:[0-9]+]])
+// CHECK-V2: ![[L06]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed, annotations: ![[L08:[0-9]+]])
+// CHECK-V2: ![[L08]] = !{![[L09:[0-9]+]]}
+// CHECK-V2: ![[L09]] = !{!"btf:type_tag", !"tag1"}
+// CHECK-V2: ![[L07]] = !{![[L10:[0-9]+]]}
+// CHECK-V2: ![[L10]] = !{!"btf:type_tag", !"tag2"}
+// CHECK-V2: !DILocalVariable(name: "arg", arg: 1, scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L03]])
diff --git a/clang/test/CodeGen/attr-btf_type_tag-restrict.c b/clang/test/CodeGen/attr-btf_type_tag-restrict.c
new file mode 100644
index 0000000000000..9c96221fc9706
--- /dev/null
+++ b/clang/test/CodeGen/attr-btf_type_tag-restrict.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -S -emit-llvm -o - %s | FileCheck %s
+
+// See attr-btf_type_tag-const.c for reasoning behind this test.
+// Alternatively, see the following method:
+//   CGDebugInfo::CreateType(const BTFTagAttributedType, llvm::DIFile)
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+
+void foo(int * restrict bar, typeof(bar) __tag1 buz) {}
+
+// CHECK: ![[#]] = !DISubroutineType(types: ![[L1:[0-9]+]])
+// CHECK: ![[L1]] = !{null, ![[L2:[0-9]+]], ![[L3:[0-9]+]]}
+// CHECK: ![[L2]] = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: ![[L4:[0-9]+]])
+// CHECK: ![[L4]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L5:[0-9]+]], {{.*}})
+// CHECK: ![[L5]] = !DIBasicType(name: "int", {{.*}}, encoding: DW_ATE_signed)
+// CHECK: ![[L3]] = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: ![[L6:[0-9]+]])
+// CHECK: ![[L6]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L5]], {{.*}}, annotations: ![[L7:[0-9]+]])
+// CHECK: ![[L7]] = !{![[L8:[0-9]+]]}
+// CHECK: ![[L8]] = !{!"btf:type_tag", !"tag1"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-similar-type.c b/clang/test/CodeGen/attr-btf_type_tag-similar-type.c
index 3960d6f5c93fb..25a99c076619b 100644
--- a/clang/test/CodeGen/attr-btf_type_tag-similar-type.c
+++ b/clang/test/CodeGen/attr-btf_type_tag-similar-type.c
@@ -1,4 +1,9 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK-V2 %s
 
 struct map_value {
         int __attribute__((btf_type_tag("tag1"))) __attribute__((btf_type_tag("tag3"))) *a;
@@ -12,15 +17,31 @@ int test(struct map_value *arg)
         return *arg->a;
 }
 
-// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L14:[0-9]+]]
-// CHECK: ![[L14]] = !{![[L15:[0-9]+]], ![[L20:[0-9]+]]}
-// CHECK: ![[L15]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L16:[0-9]+]]
-// CHECK: ![[L16]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[#]], size: [[#]], annotations: ![[L17:[0-9]+]]
-// CHECK: ![[L17]] = !{![[L18:[0-9]+]], ![[L19:[0-9]+]]}
-// CHECK: ![[L18]] = !{!"btf_type_tag", !"tag1"}
-// CHECK: ![[L19]] = !{!"btf_type_tag", !"tag3"}
-// CHECK: ![[L20]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L21:[0-9]+]]
-// CHECK: ![[L21:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[#]], size: [[#]], annotations: ![[L22:[0-9]+]]
-// CHECK: ![[L22]] = !{![[L23:[0-9]+]], ![[L24:[0-9]+]]}
-// CHECK: ![[L23]] = !{!"btf_type_tag", !"tag2"}
-// CHECK: ![[L24]] = !{!"btf_type_tag", !"tag4"}
+// CHECK: ![[L05:[0-9]+]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed)
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L01:[0-9]+]])
+// CHECK: ![[L01]] = !{![[L02:[0-9]+]], ![[L03:[0-9]+]]}
+// CHECK: ![[L02]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L04:[0-9]+]], size: [[#]])
+// CHECK: ![[L04]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L05]], size: [[#]], annotations: ![[L06:[0-9]+]])
+// CHECK: ![[L06]] = !{![[L10:[0-9]+]], ![[L11:[0-9]+]]}
+// CHECK: ![[L10]] = !{!"btf_type_tag", !"tag1"}
+// CHECK: ![[L11]] = !{!"btf_type_tag", !"tag3"}
+// CHECK: ![[L03]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L12:[0-9]+]], size: [[#]], offset: [[#]])
+// CHECK: ![[L12]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L05]], size: [[#]], annotations: ![[L14:[0-9]+]])
+// CHECK: ![[L14]] = !{![[L18:[0-9]+]], ![[L19:[0-9]+]]}
+// CHECK: ![[L18]] = !{!"btf_type_tag", !"tag2"}
+// CHECK: ![[L19]] = !{!"btf_type_tag", !"tag4"}
+
+// CHECK-V2: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L01:[0-9]+]])
+// CHECK-V2: ![[L01]] = !{![[L02:[0-9]+]], ![[L03:[0-9]+]]}
+// CHECK-V2: ![[L02]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L04:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L04]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L05:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L05]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed, annotations: ![[L07:[0-9]+]])
+// CHECK-V2: ![[L07]] = !{![[L08:[0-9]+]], ![[L09:[0-9]+]]}
+// CHECK-V2: ![[L08]] = !{!"btf:type_tag", !"tag1"}
+// CHECK-V2: ![[L09]] = !{!"btf:type_tag", !"tag3"}
+// CHECK-V2: ![[L03]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L12:[0-9]+]], size: [[#]], offset: [[#]])
+// CHECK-V2: ![[L12]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L13:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L13]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed, annotations: ![[L15:[0-9]+]])
+// CHECK-V2: ![[L15]] = !{![[L16:[0-9]+]], ![[L17:[0-9]+]]}
+// CHECK-V2: ![[L16]] = !{!"btf:type_tag", !"tag2"}
+// CHECK-V2: ![[L17]] = !{!"btf:type_tag", !"tag4"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-typedef-field.c b/clang/test/CodeGen/attr-btf_type_tag-typedef-field.c
index 5c8955fbf89a8..ceb4e83aa428b 100644
--- a/clang/test/CodeGen/attr-btf_type_tag-typedef-field.c
+++ b/clang/test/CodeGen/attr-btf_type_tag-typedef-field.c
@@ -1,4 +1,9 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -emit-llvm -o - %s | FileCheck %s --check-prefixes CHECK-V2
 
 #define __tag1 __attribute__((btf_type_tag("tag1")))
 #define __tag2 __attribute__((btf_type_tag("tag2")))
@@ -14,22 +19,43 @@ int *foo1(struct t *a1) {
   return (int *)a1->c;
 }
 
-// CHECK: ![[L4:[0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
-// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L16:[0-9]+]])
-// CHECK: ![[L16]] = !{![[L17:[0-9]+]], ![[L24:[0-9]+]], ![[L31:[0-9]+]]}
-// CHECK: ![[L17]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L18:[0-9]+]], size: [[#]])
-// CHECK: ![[L18]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L19:[0-9]+]], size: [[#]], annotations: ![[L22:[0-9]+]])
-// CHECK: ![[L19]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L4]], size: [[#]], annotations: ![[L20:[0-9]+]])
-// CHECK: ![[L20]] = !{![[L21:[0-9]+]]}
-// CHECK: ![[L21]] = !{!"btf_type_tag", !"tag1"}
-// CHECK: ![[L22]] = !{![[L23:[0-9]+]]}
-// CHECK: ![[L23]] = !{!"btf_type_tag", !"tag2"}
-// CHECK: ![[L24]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L25:[0-9]+]]
-// CHECK: ![[L25]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__fn2_t", file: ![[#]], line: [[#]], baseType: ![[L26:[0-9]+]])
-// CHECK: ![[L26]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L27:[0-9]+]], size: [[#]], annotations: ![[L30:[0-9]+]])
-// CHECK: ![[L27]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__fn_t", file: ![[#]], line: [[#]], baseType: ![[L28:[0-9]+]])
-// CHECK: ![[L28]] = !DISubroutineType(types: ![[L29:[0-9]+]])
-// CHECK: ![[L29]] = !{null, ![[L4]]}
-// CHECK: ![[L30]] = !{![[L21]], ![[L23]]}
-// CHECK: ![[L31]] = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[#]], file: ![[#]], line: [[#]]1, baseType: ![[L32:[0-9]+]]
-// CHECK: ![[L32]] = !DIBasicType(name: "long", size: [[#]], encoding: DW_ATE_signed)
+// CHECK: ![[L01:[0-9]+]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed)
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L02:[0-9]+]])
+// CHECK: ![[L02]] = !{![[L03:[0-9]+]], ![[L04:[0-9]+]], ![[L05:[0-9]+]]}
+// CHECK: ![[L03]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L06:[0-9]+]], size: [[#]])
+// CHECK: ![[L06]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L07:[0-9]+]], size: [[#]], annotations: ![[L08:[0-9]+]])
+// CHECK: ![[L07]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L01]], size: [[#]], annotations: ![[L10:[0-9]+]])
+// CHECK: ![[L10]] = !{![[L14:[0-9]+]]}
+// CHECK: ![[L14]] = !{!"btf_type_tag", !"tag1"}
+// CHECK: ![[L08]] = !{![[L15:[0-9]+]]}
+// CHECK: ![[L15]] = !{!"btf_type_tag", !"tag2"}
+// CHECK: ![[L04]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L16:[0-9]+]], size: [[#]], offset: [[#]])
+// CHECK: ![[L16]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__fn2_t", file: ![[#]], line: [[#]], baseType: ![[L17:[0-9]+]])
+// CHECK: ![[L17]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L18:[0-9]+]], size: [[#]], annotations: ![[L19:[0-9]+]])
+// CHECK: ![[L18]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__fn_t", file: ![[#]], line: [[#]], baseType: ![[L20:[0-9]+]])
+// CHECK: ![[L20]] = !DISubroutineType(types: ![[L22:[0-9]+]])
+// CHECK: ![[L22]] = !{null, ![[L01]]}
+// CHECK: ![[L19]] = !{![[L14]], ![[L15]]}
+// CHECK: ![[L05]] = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L23:[0-9]+]], size: [[#]], offset: [[#]])
+// CHECK: ![[L23]] = !DIBasicType(name: "long", size: [[#]], encoding: DW_ATE_signed)
+
+// CHECK-V2: ![[L01:[0-9]+]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed)
+// CHECK-V2: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: ![[#]], line: [[#]], size: [[#]], elements: ![[L02:[0-9]+]])
+// CHECK-V2: ![[L02]] = !{![[L03:[0-9]+]], ![[L04:[0-9]+]], ![[L05:[0-9]+]]}
+// CHECK-V2: ![[L03]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L06:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L06]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L07:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L07]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L09:[0-9]+]], size: [[#]], annotations: ![[L08:[0-9]+]])
+// CHECK-V2: ![[L09]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed, annotations: ![[L10:[0-9]+]])
+// CHECK-V2: ![[L10]] = !{![[L14:[0-9]+]]}
+// CHECK-V2: ![[L14]] = !{!"btf:type_tag", !"tag1"}
+// CHECK-V2: ![[L08]] = !{![[L15:[0-9]+]]}
+// CHECK-V2: ![[L15]] = !{!"btf:type_tag", !"tag2"}
+// CHECK-V2: ![[L04]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L16:[0-9]+]], size: [[#]], offset: [[#]])
+// CHECK-V2: ![[L16]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__fn2_t", file: ![[#]], line: [[#]], baseType: ![[L17:[0-9]+]])
+// CHECK-V2: ![[L17]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L18:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L18]] = !DIDerivedType(tag: DW_TAG_typedef, name: "__fn_t", file: ![[#]], line: [[#]], baseType: ![[L20:[0-9]+]], annotations: ![[L19:[0-9]+]])
+// CHECK-V2: ![[L20]] = !DISubroutineType(types: ![[L22:[0-9]+]])
+// CHECK-V2: ![[L22]] = !{null, ![[L01]]}
+// CHECK-V2: ![[L19]] = !{![[L14]], ![[L15]]}
+// CHECK-V2: ![[L05]] = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[#]], file: ![[#]], line: [[#]], baseType: ![[L23:[0-9]+]], size: [[#]], offset: [[#]])
+// CHECK-V2: ![[L23]] = !DIBasicType(name: "long", size: [[#]], encoding: DW_ATE_signed)
diff --git a/clang/test/CodeGen/attr-btf_type_tag-var.c b/clang/test/CodeGen/attr-btf_type_tag-var.c
index ed729e245fbcb..eca61b8742fb1 100644
--- a/clang/test/CodeGen/attr-btf_type_tag-var.c
+++ b/clang/test/CodeGen/attr-btf_type_tag-var.c
@@ -1,5 +1,17 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
-// RUN: %clang_cc1 -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 \
+// RUN:   -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -emit-llvm -o - %s \
+// RUN: | FileCheck --check-prefixes CHECK-V2 %s
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -DDOUBLE_BRACKET_ATTRS=1 -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -emit-llvm -o - %s \
+// RUN: | FileCheck --check-prefixes CHECK-V2 %s
 
 #if DOUBLE_BRACKET_ATTRS
 #define __tag1 [[clang::btf_type_tag("tag1")]]
@@ -21,23 +33,44 @@ const volatile int __tag1 __tag2 * __tag3 __tag4 const volatile  * __tag5 __tag6
 const int __tag1 __tag2 volatile * const __tag3  __tag4  volatile * __tag5  __tag6 const volatile * g;
 #endif
 
-// CHECK:  distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L6:[0-9]+]]
-// CHECK:  ![[L6]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L7:[0-9]+]], size: [[#]], annotations: ![[L22:[0-9]+]]
-// CHECK:  ![[L7]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L8:[0-9]+]]
-// CHECK:  ![[L8]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L9:[0-9]+]]
-// CHECK:  ![[L9]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L10:[0-9]+]], size: [[#]], annotations: ![[L19:[0-9]+]]
-// CHECK:  ![[L10]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L11:[0-9]+]]
-// CHECK:  ![[L11]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L12:[0-9]+]]
-// CHECK:  ![[L12]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L13:[0-9]+]], size: [[#]], annotations: ![[L16:[0-9]+]]
-// CHECK:  ![[L13]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L14:[0-9]+]]
-// CHECK:  ![[L14]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L15:[0-9]+]]
-// CHECK:  ![[L15]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed
-// CHECK:  ![[L16]] = !{![[L17:[0-9]+]], ![[L18:[0-9]+]]}
-// CHECK:  ![[L17]] = !{!"btf_type_tag", !"tag1"}
-// CHECK:  ![[L18]] = !{!"btf_type_tag", !"tag2"}
-// CHECK:  ![[L19]] = !{![[L20:[0-9]+]], ![[L21:[0-9]+]]}
-// CHECK:  ![[L20]] = !{!"btf_type_tag", !"tag3"}
-// CHECK:  ![[L21]] = !{!"btf_type_tag", !"tag4"}
-// CHECK:  ![[L22]] = !{![[L23:[0-9]+]], ![[L24:[0-9]+]]}
-// CHECK:  ![[L23]] = !{!"btf_type_tag", !"tag5"}
-// CHECK:  ![[L24]] = !{!"btf_type_tag", !"tag6"}
+// CHECK: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L01:[0-9]+]], isLocal: false, isDefinition: true)
+// CHECK: ![[L01]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L02:[0-9]+]], size: [[#]], annotations: ![[L03:[0-9]+]])
+// CHECK: ![[L02]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L04:[0-9]+]])
+// CHECK: ![[L04]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L05:[0-9]+]])
+// CHECK: ![[L05]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L06:[0-9]+]], size: [[#]], annotations: ![[L07:[0-9]+]])
+// CHECK: ![[L06]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L08:[0-9]+]])
+// CHECK: ![[L08]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L09:[0-9]+]])
+// CHECK: ![[L09]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L10:[0-9]+]], size: [[#]], annotations: ![[L11:[0-9]+]])
+// CHECK: ![[L10]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L12:[0-9]+]])
+// CHECK: ![[L12]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L13:[0-9]+]])
+// CHECK: ![[L13]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed)
+// CHECK: ![[L11]] = !{![[L19:[0-9]+]], ![[L20:[0-9]+]]}
+// CHECK: ![[L19]] = !{!"btf_type_tag", !"tag1"}
+// CHECK: ![[L20]] = !{!"btf_type_tag", !"tag2"}
+// CHECK: ![[L07]] = !{![[L23:[0-9]+]], ![[L24:[0-9]+]]}
+// CHECK: ![[L23]] = !{!"btf_type_tag", !"tag3"}
+// CHECK: ![[L24]] = !{!"btf_type_tag", !"tag4"}
+// CHECK: ![[L03]] = !{![[L25:[0-9]+]], ![[L26:[0-9]+]]}
+// CHECK: ![[L25]] = !{!"btf_type_tag", !"tag5"}
+// CHECK: ![[L26]] = !{!"btf_type_tag", !"tag6"}
+
+// CHECK-V2: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L01:[0-9]+]], isLocal: false, isDefinition: true)
+// CHECK-V2: ![[L01]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L02:[0-9]+]], size: [[#]])
+// CHECK-V2: ![[L02]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L04:[0-9]+]])
+// CHECK-V2: ![[L04]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L05:[0-9]+]])
+// CHECK-V2: ![[L05]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L06:[0-9]+]], size: [[#]], annotations: ![[L07:[0-9]+]])
+// CHECK-V2: ![[L06]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L08:[0-9]+]])
+// CHECK-V2: ![[L08]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L09:[0-9]+]])
+// CHECK-V2: ![[L09]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L10:[0-9]+]], size: [[#]], annotations: ![[L11:[0-9]+]])
+// CHECK-V2: ![[L10]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[L12:[0-9]+]])
+// CHECK-V2: ![[L12]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L13:[0-9]+]])
+// CHECK-V2: ![[L13]] = !DIBasicType(name: "int", size: [[#]], encoding: DW_ATE_signed, annotations: ![[L14:[0-9]+]])
+// CHECK-V2: ![[L14]] = !{![[L15:[0-9]+]], ![[L16:[0-9]+]]}
+// CHECK-V2: ![[L15]] = !{!"btf:type_tag", !"tag1"}
+// CHECK-V2: ![[L16]] = !{!"btf:type_tag", !"tag2"}
+// CHECK-V2: ![[L11]] = !{![[L17:[0-9]+]], ![[L18:[0-9]+]]}
+// CHECK-V2: ![[L17]] = !{!"btf:type_tag", !"tag3"}
+// CHECK-V2: ![[L18]] = !{!"btf:type_tag", !"tag4"}
+// CHECK-V2: ![[L07]] = !{![[L21:[0-9]+]], ![[L22:[0-9]+]]}
+// CHECK-V2: ![[L21]] = !{!"btf:type_tag", !"tag5"}
+// CHECK-V2: ![[L22]] = !{!"btf:type_tag", !"tag6"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-void.c b/clang/test/CodeGen/attr-btf_type_tag-void.c
new file mode 100644
index 0000000000000..9fe49370d2f2c
--- /dev/null
+++ b/clang/test/CodeGen/attr-btf_type_tag-void.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -S -emit-llvm -o - %s | FileCheck %s
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+void __tag1 *g;
+
+// CHECK: distinct !DIGlobalVariable(name: "g", scope: ![[#]], file: ![[#]], line: [[#]], type: ![[L1:[0-9]+]], isLocal: false, isDefinition: true)
+// CHECK: ![[L1]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[L2:[0-9]+]], size: [[#]])
+// CHECK: ![[L2]] = !DIBasicType(tag: DW_TAG_unspecified_type, name: "void", annotations: ![[L4:[0-9]+]])
+// CHECK: ![[L4]] = !{![[L5:[0-9]+]]}
+// CHECK: ![[L5]] = !{!"btf:type_tag", !"tag1"}
diff --git a/clang/test/CodeGen/attr-btf_type_tag-volatile.c b/clang/test/CodeGen/attr-btf_type_tag-volatile.c
new file mode 100644
index 0000000000000..e0039fb23fba2
--- /dev/null
+++ b/clang/test/CodeGen/attr-btf_type_tag-volatile.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 \
+// RUN:   -triple %itanium_abi_triple -debug-info-kind=limited \
+// RUN:   -mllvm -btf-type-tag-v2 -S -emit-llvm -o - %s | FileCheck %s
+
+// See attr-btf_type_tag-const.c for reasoning behind this test.
+// Alternatively, see the following method:
+//   CGDebugInfo::CreateType(const BTFTagAttributedType, llvm::DIFile)
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+
+volatile int foo;
+typeof(foo) __tag1 bar;
+
+// CHECK: ![[#]] = distinct !DIGlobalVariable(name: "bar", {{.*}}, type: ![[L1:[0-9]+]], {{.*}})
+// CHECK: ![[L1]] = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: ![[L2:[0-9]+]])
+// CHECK: ![[L2]] = !DIBasicType(name: "int", size: [[#]], {{.*}}, annotations: ![[L3:[0-9]+]])
+// CHECK: ![[L3]] = !{![[L4:[0-9]+]]}
+// CHECK: ![[L4]] = !{!"btf:type_tag", !"tag1"}
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 97ea38f041baa..03d16a1a2ad07 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -328,6 +328,9 @@ namespace llvm {
                                        DINode::DIFlags Flags = DINode::FlagZero,
                                        DINodeArray Annotations = nullptr);
 
+    DIDerivedType *createAnnotationsPlaceholder(DIType *Ty,
+                                                DINodeArray Annotations);
+
     /// Create debugging information entry for a 'friend'.
     DIDerivedType *createFriend(DIType *Ty, DIType *FriendTy);
 
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index f39149ae0dad4..457de32a0507f 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -367,6 +367,17 @@ DIBuilder::createTemplateAlias(DIType *Ty, StringRef Name, DIFile *File,
                             TParams.get(), Annotations);
 }
 
+DIDerivedType *
+DIBuilder::createAnnotationsPlaceholder(DIType *Ty, DINodeArray Annotations) {
+  auto *RetTy = DIDerivedType::getTemporary(
+                    VMContext, dwarf::DW_TAG_LLVM_annotation, "", nullptr, 0,
+                    nullptr, Ty, 0, 0, 0, std::nullopt, std::nullopt,
+                    DINode::FlagZero, nullptr, Annotations)
+                    .release();
+  trackIfUnresolved(RetTy);
+  return RetTy;
+}
+
 DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) {
   assert(Ty && "Invalid type!");
   assert(FriendTy && "Invalid friend type!");

>From 93ad7f6485eaeffc55921d47d66ba049bacced18 Mon Sep 17 00:00:00 2001
From: Eduard Zingerman <eddyz87 at gmail.com>
Date: Mon, 13 Mar 2023 01:57:57 +0200
Subject: [PATCH 3/3] [BPF] Generate BTF info using 'btf:type_tag' annotation

This is a follow-up for BPF mailing list discussion at [1].

Previous commit in a series updated DWARF generation for the following
example:

    int __attribute__((btf_type_tag("tag1"))) *g;

To generate DWARF that looks as follows:

    0x0000001e:   DW_TAG_variable
                    DW_AT_name      ("g")
                    DW_AT_type      (0x00000029 "int *")

    0x00000029:   DW_TAG_pointer_type
                    DW_AT_type      (0x00000032 "int")

    0x00000032:   DW_TAG_base_type
                    DW_AT_name      ("int")

    0x00000036:     DW_TAG_LLVM_annotation
                      DW_AT_name    ("btf:type_tag")
                      DW_AT_const_value     ("tag1")

The fresh part is attachment of `btf:type_tag` annotations to types
other than pointers. This commit changes BTF generation to rely on
`btf:type_tag` annotations to generate TYPE_TAG entries.
This necessitates the following changes:
- The logic for `BTFTypeTypeTag` chains creation is moved to
  `BTFDebug::addType()`;
- Special logic is added to avoid duplicate BTF entries for tagged
  and un-tagged type variants, e.g. in the following case:

  #define __tag1 __attribute__((btf_type_tag("tag1")))
  #define __tag2 __attribute__((btf_type_tag("tag2")))

  struct foo {};

  struct bar {
    struct foo __tag1 aa;
    struct foo __tag2 bb;
    struct foo cc;
  };

  Debug information generated for this example contains three
  instances of `DICompositeType(name: "foo")` with different
  `annotations` fields, however single BTF definition for structure
  "foo" should be generated. Field `BTFDebug::DIDedupMap` and method
  `BTFDebug::lookupType()` are responsible for this logic;
- Care is taken to avoid references to type tags in relocation
  entries.

[1] https://lore.kernel.org/bpf/87r0w9jjoq.fsf@oracle.com/

This was previously tracked as differential revision:
https://reviews.llvm.org/D145891
---
 llvm/lib/Target/BPF/BTFDebug.cpp              | 629 +++++++++++++++---
 llvm/lib/Target/BPF/BTFDebug.h                |  46 +-
 llvm/test/CodeGen/BPF/BTF/print_btf.py        | 300 +++++++++
 .../BTF/type-tag-cvr-multiple-tags-reuse.ll   |  64 ++
 .../BPF/BTF/type-tag-cvr-multiple-tags.ll     |  51 ++
 .../CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll   |  52 ++
 .../CodeGen/BPF/BTF/type-tag-cvr-reuse.ll     |  55 ++
 .../CodeGen/BPF/BTF/type-tag-cvr-simple.ll    |  44 ++
 .../BPF/BTF/type-tag-decl-tag-typedef.ll      |  66 ++
 .../CodeGen/BPF/BTF/type-tag-dedup-enum.ll    |  87 +++
 .../CodeGen/BPF/BTF/type-tag-dedup-struct.ll  |  82 +++
 .../BPF/BTF/type-tag-dedup-subroutine.ll      | 106 +++
 .../CodeGen/BPF/BTF/type-tag-dedup-typedef.ll |  84 +++
 .../CodeGen/BPF/BTF/type-tag-dedup-union.ll   |  82 +++
 .../CodeGen/BPF/BTF/type-tag-field-relo.ll    | 103 +++
 .../CodeGen/BPF/BTF/type-tag-fixup-fwd-v2.ll  | 102 +++
 .../BPF/BTF/type-tag-fixup-resolved-v2.ll     | 112 ++++
 .../test/CodeGen/BPF/BTF/type-tag-skip-var.ll |  49 ++
 llvm/test/CodeGen/BPF/BTF/type-tag-var-v2.ll  |  46 ++
 llvm/test/CodeGen/BPF/BTF/type-tag-void.ll    |  43 ++
 20 files changed, 2125 insertions(+), 78 deletions(-)
 create mode 100755 llvm/test/CodeGen/BPF/BTF/print_btf.py
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd-v2.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved-v2.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-var-v2.ll
 create mode 100644 llvm/test/CodeGen/BPF/BTF/type-tag-void.ll

diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index b6d3b460005c9..f92de243a840c 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -14,6 +14,8 @@
 #include "BPF.h"
 #include "BPFCORE.h"
 #include "MCTargetDesc/BPFMCTargetDesc.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/CodeGen/AsmPrinter.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
@@ -69,11 +71,10 @@ BTFTypeDerived::BTFTypeDerived(const DIDerivedType *DTy, unsigned Tag,
   BTFType.Info = Kind << 24;
 }
 
-/// Used by DW_TAG_pointer_type only.
-BTFTypeDerived::BTFTypeDerived(unsigned NextTypeId, unsigned Tag,
+BTFTypeDerived::BTFTypeDerived(unsigned NextTypeId, enum BTF::TypeKinds _Kind,
                                StringRef Name)
     : DTy(nullptr), NeedsFixup(false), Name(Name) {
-  Kind = BTF::BTF_KIND_PTR;
+  Kind = _Kind;
   BTFType.Info = Kind << 24;
   BTFType.Type = NextTypeId;
 }
@@ -106,6 +107,8 @@ void BTFTypeDerived::setPointeeType(uint32_t PointeeType) {
   BTFType.Type = PointeeType;
 }
 
+uint32_t BTFTypeDerived::getPointeeType() { return BTFType.Type; }
+
 /// Represent a struct/union forward declaration.
 BTFTypeFwd::BTFTypeFwd(StringRef Name, bool IsUnion) : Name(Name) {
   Kind = BTF::BTF_KIND_FWD;
@@ -490,6 +493,10 @@ void BTFTypeTypeTag::completeType(BTFDebug &BDebug) {
   }
 }
 
+uint32_t BTFTypeTypeTag::getNextTypeId() { return BTFType.Type; }
+
+StringRef BTFTypeTypeTag::getTag() { return Tag; }
+
 uint32_t BTFStringTable::addString(StringRef S) {
   // Check whether the string already exists.
   for (auto &OffsetM : OffsetToIdMap) {
@@ -511,13 +518,278 @@ BTFDebug::BTFDebug(AsmPrinter *AP)
   addString("\0");
 }
 
+static DINodeArray lookupAnnotations(const DIType *Ty) {
+  DINodeArray Annots = {};
+  if (auto *SubTy = dyn_cast<DIBasicType>(Ty))
+    Annots = SubTy->getAnnotations();
+  else if (auto *SubTy = dyn_cast<DICompositeType>(Ty))
+    Annots = SubTy->getAnnotations();
+  else if (auto *SubTy = dyn_cast<DIDerivedType>(Ty))
+    Annots = SubTy->getAnnotations();
+  else if (auto *SubTy = dyn_cast<DISubroutineType>(Ty))
+    Annots = SubTy->getAnnotations();
+  return Annots;
+}
+
+static void collectBTFTypeTags(const DIType *Ty,
+                               SmallVectorImpl<MDString *> &Tags,
+                               StringRef AnnotName) {
+  DINodeArray Annots = lookupAnnotations(Ty);
+  if (!Annots)
+    return;
+
+  for (const Metadata *Annotations : Annots->operands()) {
+    const MDNode *MD = cast<MDNode>(Annotations);
+    if (MD->getNumOperands() != 2)
+      continue;
+    const MDString *Name = dyn_cast<MDString>(MD->getOperand(0));
+    if (!Name)
+      continue;
+    if (!Name->getString().equals(AnnotName))
+      continue;
+    // For type with "int __tag1 __tag2 *p", the Tags will have
+    // content: [__tag1, __tag2].
+    Tags.push_back(cast<MDString>(MD->getOperand(1)));
+  }
+}
+
+/// Generate btf_type_tag chains.
+uint32_t BTFDebug::genBTFTypeTags(const DIType *Ty, int BaseId,
+                                  const DIDerivedType *DTy,
+                                  StringRef AnnotName) {
+  SmallVector<MDString *, 4> MDStrs;
+  collectBTFTypeTags(Ty, MDStrs, AnnotName);
+  // With MDStrs [__tag1, __tag2], the output type chain looks like
+  //   PTR -> __tag2 -> __tag1 -> BaseType
+  // In the below, we construct BTF types with the order of __tag1, __tag2
+  // and PTR.
+  for (unsigned I = 0; I < MDStrs.size(); I++) {
+    const MDString *Value = MDStrs[I];
+    auto TagEntry =
+        BaseId == -1
+            ? std::make_unique<BTFTypeTypeTag>(DTy, Value->getString())
+            : std::make_unique<BTFTypeTypeTag>(BaseId, Value->getString());
+    BaseId = addType(std::move(TagEntry));
+  }
+  return BaseId;
+}
+
+uint32_t BTFDebug::genBTFTypeTagsV1(const DIDerivedType *DTy) {
+  return genBTFTypeTags(DTy, -1, DTy, "btf_type_tag");
+}
+
+uint32_t BTFDebug::genBTFTypeTagsV2(const DIType *Ty, uint32_t BaseId) {
+  return genBTFTypeTags(Ty, BaseId, nullptr, "btf:type_tag");
+}
+
+static hash_code hashElements(const DICompositeType *Ty) {
+  hash_code Hash = hash_value(Ty->getElements().size());
+  for (auto *Node : Ty->getElements()) {
+    if (Node->getTag() == dwarf::DW_TAG_member) {
+      auto *Member = cast<DIDerivedType>(Node);
+      Hash = hash_combine(Hash, Member->getName());
+    }
+    if (Node->getTag() == dwarf::DW_TAG_enumerator) {
+      auto *Member = cast<DIEnumerator>(Node);
+      Hash = hash_combine(Hash, Member->getName());
+    }
+  }
+  return Hash;
+}
+
+static bool compareElements(const DICompositeType *A,
+                            const DICompositeType *B) {
+  if (A->getElements().size() != B->getElements().size())
+    return false;
+
+  auto AI = A->getElements().begin();
+  auto AE = A->getElements().end();
+  auto BI = B->getElements().begin();
+  for (; AI != AE; ++AI, ++BI) {
+    if ((*AI)->getTag() != (*BI)->getTag())
+      return false;
+
+    if ((*AI)->getTag() == dwarf::DW_TAG_member) {
+      auto *MA = cast<DIDerivedType>(*AI);
+      auto *MB = cast<DIDerivedType>(*BI);
+      if (!MA->getName().equals(MB->getName()) ||
+          MA->getBaseType() != MB->getBaseType() ||
+          MA->getOffsetInBits() != MB->getOffsetInBits())
+        return false;
+    }
+
+    if ((*AI)->getTag() == dwarf::DW_TAG_enumerator) {
+      auto *MA = cast<DIEnumerator>(*AI);
+      auto *MB = cast<DIEnumerator>(*BI);
+      if (!MA->getName().equals(MB->getName()) ||
+          MA->isUnsigned() != MB->isUnsigned() ||
+          MA->getValue().getZExtValue() != MB->getValue().getZExtValue())
+        return false;
+    }
+  }
+
+  return true;
+}
+
+bool BTFTypeDedupKey::operator==(const BTFTypeDedupKey &Other) const {
+  auto *OtherTy = Other.CanonTy;
+
+  if (CanonTy->getTag() != OtherTy->getTag())
+    return false;
+
+  if (!CanonTy->getName().equals(OtherTy->getName()))
+    return false;
+
+  switch (CanonTy->getTag()) {
+  case dwarf::DW_TAG_base_type: {
+    auto *A = cast<DIBasicType>(CanonTy);
+    auto *B = cast<DIBasicType>(OtherTy);
+    return A->getEncoding() == B->getEncoding() &&
+           A->getOffsetInBits() == B->getOffsetInBits() &&
+           A->getSizeInBits() == B->getSizeInBits();
+  }
+
+  case dwarf::DW_TAG_typedef: {
+    auto *A = cast<DIDerivedType>(CanonTy);
+    auto *B = cast<DIDerivedType>(OtherTy);
+    return A->getBaseType() == B->getBaseType();
+  }
+
+  case dwarf::DW_TAG_enumeration_type:
+  case dwarf::DW_TAG_structure_type:
+  case dwarf::DW_TAG_union_type: {
+    auto *A = cast<DICompositeType>(CanonTy);
+    auto *B = cast<DICompositeType>(OtherTy);
+    return A->isForwardDecl() == B->isForwardDecl() && compareElements(A, B);
+  }
+
+  case dwarf::DW_TAG_subroutine_type: {
+    auto *A = cast<DISubroutineType>(CanonTy);
+    auto *B = cast<DISubroutineType>(OtherTy);
+    return std::equal(A->getTypeArray().begin(), A->getTypeArray().end(),
+                      B->getTypeArray().begin());
+  }
+
+  default:
+    llvm_unreachable("Comparing unexpected dedup key");
+  }
+}
+
+size_t BTFTypeDedupKey::Hash::operator()(BTFTypeDedupKey const &Key) const {
+  auto *Ty = Key.CanonTy;
+  hash_code Hash =
+      hash_combine(Ty->getTag(), Ty->getName(), Ty->getSizeInBits());
+
+  switch (Ty->getTag()) {
+  case dwarf::DW_TAG_base_type: {
+    auto *BTy = cast<DIBasicType>(Ty);
+    Hash = hash_combine(BTy->getEncoding(), BTy->getOffsetInBits(),
+                        BTy->getSizeInBits());
+    break;
+  }
+
+  case dwarf::DW_TAG_typedef:
+    // Nothing to be done, name & tag suffice
+    break;
+
+  case dwarf::DW_TAG_structure_type:
+  case dwarf::DW_TAG_union_type:
+  case dwarf::DW_TAG_enumeration_type:
+    Hash = hash_combine(Hash, hashElements(cast<DICompositeType>(Key.CanonTy)));
+    break;
+
+  case dwarf::DW_TAG_subroutine_type: {
+    auto *STy = cast<DISubroutineType>(Key.CanonTy);
+    Hash = hash_combine(Hash, STy->getTypeArray().size());
+    for (DIType *Param : STy->getTypeArray())
+      Hash = hash_combine(Hash,
+                          Param ? hash_value(Param->getName()) : hash_value(0));
+    break;
+  }
+
+  default:
+    llvm_unreachable("Hashing unexpected dedup key");
+  };
+
+  return Hash;
+}
+
+static std::optional<BTFTypeDedupKey> makeDedupKey(const DIType *Ty) {
+  if (Ty == nullptr)
+    return std::nullopt;
+
+  switch (Ty->getTag()) {
+  case dwarf::DW_TAG_base_type:
+  case dwarf::DW_TAG_typedef:
+  case dwarf::DW_TAG_structure_type:
+  case dwarf::DW_TAG_union_type:
+  case dwarf::DW_TAG_enumeration_type:
+  case dwarf::DW_TAG_subroutine_type:
+    return std::optional(BTFTypeDedupKey(Ty));
+  default:
+    return std::nullopt;
+  }
+}
+
+// `btf_type_tag`s are encoded in DI classes as `annotations` fields,
+// this might lead to some DI info duplication.
+// For example, the following C code:
+//
+//   #define __tag1 __attribute__((btf_type_tag("tag1")))
+//
+//   struct foo {};
+//   struct bar {
+//     struct foo __tag1 a;
+//     struct foo b;
+//   } g;
+//
+// Generates the following DI representation:
+//
+//   !5  = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar",
+//                                   ..., elements: !6)
+//   !6  = !{!7, !8}
+//   !7  = !DIDerivedType(tag: DW_TAG_member, name: "a", ..., baseType: !10)
+//   !8  = !DIDerivedType(tag: DW_TAG_member, name: "b", ..., baseType: !9)
+//   !9  = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", ...)
+//   !10 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", ...,
+//                          annotations: !11)
+//   !11 = !{!12}
+//   !12 = !{!"btf:type_tag", !"tag1"}
+//
+// Note two instances of structure "foo", one with annotations, one without.
+//
+// In order to avoid such duplication in generated BTF two things are used:
+// - a map `BTFDebug::DIDedupMap`
+// - special logic in `BTFDebug::addType`
+//
+// The key of `BTFDebug::DIDedupMap` is `DIType*` wrapped in an auxiliary
+// type that provides custom hashing and equality operations.
+//
+// The value of `BTFDebug::DIDedupMap` is BTF id of the type w/o type tags.
+//
+// Hashing and equality functions of `BTFDebug::DIDedupMap` ignore
+// `annotations` field on a first level and compare all other fields
+// significant for BTF generation.
+//
+// The `BTFDebug::addType(..., DIType *Ty, ...)` operates as follows:
+// - establishes a base type:
+//   - if a type similar to `Ty` could be found in `BTFDebug::DIDedupMap`
+//     it is used as base;
+//   - otherwise a fresh BTF type is used as base;
+// - adds btf type tag wrappers:
+//   - a series of new BTF_TYPE_TAG types is generated wrapping one
+//     another, with first type wrapping the base type.
 uint32_t BTFDebug::addType(std::unique_ptr<BTFTypeBase> TypeEntry,
-                           const DIType *Ty) {
-  TypeEntry->setId(TypeEntries.size() + 1);
-  uint32_t Id = TypeEntry->getId();
-  DIToIdMap[Ty] = Id;
-  TypeEntries.push_back(std::move(TypeEntry));
-  return Id;
+                           const DIType *Ty, uint32_t *RealId) {
+  uint32_t Id = TypeEntry ? addType(std::move(TypeEntry)) : 0;
+  auto Key = makeDedupKey(Ty);
+  if (Key.has_value())
+    DIDedupMap[*Key] = Id;
+  uint32_t TagId = genBTFTypeTagsV2(Ty, Id);
+  DIToIdMap[Ty] = TagId;
+  if (RealId)
+    *RealId = Id;
+  return TagId;
 }
 
 uint32_t BTFDebug::addType(std::unique_ptr<BTFTypeBase> TypeEntry) {
@@ -527,7 +799,35 @@ uint32_t BTFDebug::addType(std::unique_ptr<BTFTypeBase> TypeEntry) {
   return Id;
 }
 
+uint32_t BTFDebug::replaceType(uint32_t Id,
+                               std::unique_ptr<BTFTypeBase> TypeEntry) {
+  TypeEntry->setId(Id);
+  TypeEntries[Id - 1] = std::move(TypeEntry);
+  return Id;
+}
+
+std::optional<uint32_t> BTFDebug::lookupType(const DIType *Ty) {
+  if (DIToIdMap.find(Ty) != DIToIdMap.end())
+    return std::optional(DIToIdMap[Ty]);
+
+  auto Key = makeDedupKey(Ty);
+  if (!Key.has_value() || (DIDedupMap.find(*Key) == DIDedupMap.end()))
+    return std::nullopt;
+
+  auto TagId = genBTFTypeTagsV2(Ty, DIDedupMap[*Key]);
+  DIToIdMap[Ty] = TagId;
+  return TagId;
+}
+
 void BTFDebug::visitBasicType(const DIBasicType *BTy, uint32_t &TypeId) {
+  // Such "void" entries might arise from use btf_type_tag, e.g.:
+  //   void __attribute__((btf_type_tag("foo"))) *p;
+  if (BTy->getTag() == dwarf::DW_TAG_unspecified_type &&
+      BTy->getName() == "void") {
+    TypeId = addType(nullptr, BTy);
+    return;
+  }
+
   // Only int and binary floating point types are supported in BTF.
   uint32_t Encoding = BTy->getEncoding();
   std::unique_ptr<BTFTypeBase> TypeEntry;
@@ -617,46 +917,6 @@ uint32_t BTFDebug::processDISubprogram(const DISubprogram *SP,
   return FuncId;
 }
 
-/// Generate btf_type_tag chains.
-int BTFDebug::genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId) {
-  SmallVector<const MDString *, 4> MDStrs;
-  DINodeArray Annots = DTy->getAnnotations();
-  if (Annots) {
-    // For type with "int __tag1 __tag2 *p", the MDStrs will have
-    // content: [__tag1, __tag2].
-    for (const Metadata *Annotations : Annots->operands()) {
-      const MDNode *MD = cast<MDNode>(Annotations);
-      const MDString *Name = cast<MDString>(MD->getOperand(0));
-      if (Name->getString() != "btf_type_tag")
-        continue;
-      MDStrs.push_back(cast<MDString>(MD->getOperand(1)));
-    }
-  }
-
-  if (MDStrs.size() == 0)
-    return -1;
-
-  // With MDStrs [__tag1, __tag2], the output type chain looks like
-  //   PTR -> __tag2 -> __tag1 -> BaseType
-  // In the below, we construct BTF types with the order of __tag1, __tag2
-  // and PTR.
-  unsigned TmpTypeId;
-  std::unique_ptr<BTFTypeTypeTag> TypeEntry;
-  if (BaseTypeId >= 0)
-    TypeEntry =
-        std::make_unique<BTFTypeTypeTag>(BaseTypeId, MDStrs[0]->getString());
-  else
-    TypeEntry = std::make_unique<BTFTypeTypeTag>(DTy, MDStrs[0]->getString());
-  TmpTypeId = addType(std::move(TypeEntry));
-
-  for (unsigned I = 1; I < MDStrs.size(); I++) {
-    const MDString *Value = MDStrs[I];
-    TypeEntry = std::make_unique<BTFTypeTypeTag>(TmpTypeId, Value->getString());
-    TmpTypeId = addType(std::move(TypeEntry));
-  }
-  return TmpTypeId;
-}
-
 /// Handle structure/union types.
 void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct,
                                uint32_t &TypeId) {
@@ -678,17 +938,18 @@ void BTFDebug::visitStructType(const DICompositeType *CTy, bool IsStruct,
   auto TypeEntry =
       std::make_unique<BTFTypeStruct>(CTy, IsStruct, HasBitField, VLen);
   StructTypes.push_back(TypeEntry.get());
-  TypeId = addType(std::move(TypeEntry), CTy);
+  uint32_t StructId;
+  TypeId = addType(std::move(TypeEntry), CTy, &StructId);
 
   // Check struct/union annotations
-  processDeclAnnotations(CTy->getAnnotations(), TypeId, -1);
+  processDeclAnnotations(CTy->getAnnotations(), StructId, -1);
 
   // Visit all struct members.
   int FieldNo = 0;
   for (const auto *Element : Elements) {
     const auto Elem = cast<DIDerivedType>(Element);
     visitTypeEntry(Elem);
-    processDeclAnnotations(Elem->getAnnotations(), TypeId, FieldNo);
+    processDeclAnnotations(Elem->getAnnotations(), StructId, FieldNo);
     FieldNo++;
   }
 }
@@ -822,22 +1083,20 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
   }
 
   if (Tag == dwarf::DW_TAG_pointer_type) {
-    int TmpTypeId = genBTFTypeTags(DTy, -1);
-    if (TmpTypeId >= 0) {
-      auto TypeDEntry =
-          std::make_unique<BTFTypeDerived>(TmpTypeId, Tag, DTy->getName());
-      TypeId = addType(std::move(TypeDEntry), DTy);
-    } else {
-      auto TypeEntry = std::make_unique<BTFTypeDerived>(DTy, Tag, false);
-      TypeId = addType(std::move(TypeEntry), DTy);
-    }
+    int TmpTypeId = genBTFTypeTagsV1(DTy);
+    auto TypeEntry = TmpTypeId == -1
+                         ? std::make_unique<BTFTypeDerived>(DTy, Tag, false)
+                         : std::make_unique<BTFTypeDerived>(
+                               TmpTypeId, BTF::BTF_KIND_PTR, DTy->getName());
+    TypeId = addType(std::move(TypeEntry), DTy);
   } else if (Tag == dwarf::DW_TAG_typedef || Tag == dwarf::DW_TAG_const_type ||
              Tag == dwarf::DW_TAG_volatile_type ||
              Tag == dwarf::DW_TAG_restrict_type) {
     auto TypeEntry = std::make_unique<BTFTypeDerived>(DTy, Tag, false);
-    TypeId = addType(std::move(TypeEntry), DTy);
+    uint32_t RealId = 0;
+    TypeId = addType(std::move(TypeEntry), DTy, &RealId);
     if (Tag == dwarf::DW_TAG_typedef)
-      processDeclAnnotations(DTy->getAnnotations(), TypeId, -1);
+      processDeclAnnotations(DTy->getAnnotations(), RealId, -1);
   } else if (Tag != dwarf::DW_TAG_member) {
     return;
   }
@@ -860,8 +1119,13 @@ void BTFDebug::visitDerivedType(const DIDerivedType *DTy, uint32_t &TypeId,
 /// will be generated.
 void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
                               bool CheckPointer, bool SeenPointer) {
-  if (!Ty || DIToIdMap.find(Ty) != DIToIdMap.end()) {
+  if (!Ty) {
     TypeId = DIToIdMap[Ty];
+    return;
+  }
+
+  if (auto OptTypeId = lookupType(Ty)) {
+    TypeId = *OptTypeId;
 
     // To handle the case like the following:
     //    struct t;
@@ -892,14 +1156,14 @@ void BTFDebug::visitTypeEntry(const DIType *Ty, uint32_t &TypeId,
     // We will traverse const/ptr/volatile which already have corresponding
     // BTF types and generate type for 'struct' which might be in Fixup
     // state.
-    if (Ty && (!CheckPointer || !SeenPointer)) {
+    if (!CheckPointer || !SeenPointer) {
       if (const auto *DTy = dyn_cast<DIDerivedType>(Ty)) {
         while (DTy) {
           const DIType *BaseTy = DTy->getBaseType();
           if (!BaseTy)
             break;
 
-          if (DIToIdMap.find(BaseTy) != DIToIdMap.end()) {
+          if (lookupType(BaseTy)) {
             DTy = dyn_cast<DIDerivedType>(BaseTy);
           } else {
             if (CheckPointer && DTy->getTag() == dwarf::DW_TAG_pointer_type) {
@@ -1242,6 +1506,26 @@ void BTFDebug::endFunctionImpl(const MachineFunction *MF) {
   SecNameOff = 0;
 }
 
+BTFTypeBase *BTFDebug::getType(uint32_t Id) {
+  return TypeEntries[Id - 1].get();
+}
+
+uint32_t BTFDebug::skipBTFTypeTags(uint32_t Id) {
+  for (;;) {
+    if (Id == 0)
+      break;
+
+    BTFTypeBase *Ty = getType(Id);
+    if (Ty->getKind() != BTF::BTF_KIND_TYPE_TAG)
+      break;
+
+    auto *TagTy = static_cast<BTFTypeTypeTag *>(Ty);
+    Id = TagTy->getNextTypeId();
+  }
+
+  return Id;
+}
+
 /// On-demand populate types as requested from abstract member
 /// accessing or preserve debuginfo type.
 unsigned BTFDebug::populateType(const DIType *Ty) {
@@ -1249,7 +1533,10 @@ unsigned BTFDebug::populateType(const DIType *Ty) {
   visitTypeEntry(Ty, Id, false, false);
   for (const auto &TypeEntry : TypeEntries)
     TypeEntry->completeType(*this);
-  return Id;
+
+  // Skip type tags, libbpf expects each relocation entry to point to
+  // struct/union/enum.
+  return skipBTFTypeTags(Id);
 }
 
 /// Generate a struct member field relocation.
@@ -1448,6 +1735,9 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
       break;
     }
 
+    // Kernel does not handle VARs with type 'TYPE_TAG -> something'
+    GVTypeId = skipBTFTypeTags(GVTypeId);
+
     // Only support the following globals:
     //  . static variables
     //  . non-static weak or non-weak global variables
@@ -1581,6 +1871,196 @@ void BTFDebug::processFuncPrototypes(const Function *F) {
   }
 }
 
+// Cache IDs for BTF types with shape:
+// - (TYPE_TAG <value> <id>)
+// - (CONST <id>)
+// - (VOLATILE <id>)
+// - (RESTRICT <id>)
+class BTFDebug::QualifiedTypesCache {
+public:
+  struct Entry {
+    const StringRef Tag;
+    const uint32_t Id;
+    const BTF::TypeKinds Kind;
+
+    Entry(uint32_t Id, StringRef Tag)
+        : Tag(Tag), Id(Id), Kind(BTF::BTF_KIND_TYPE_TAG) {}
+    Entry(uint32_t Id, BTF::TypeKinds Kind)
+        : Tag(StringRef()), Id(Id), Kind(Kind) {}
+  };
+
+  struct EntryHash {
+    std::size_t operator()(Entry const &E) const {
+      return hash_combine(E.Tag, E.Id, E.Kind);
+    }
+  };
+
+  struct EntryEq {
+    bool operator()(const Entry &LHS, const Entry &RHS) const {
+      return std::tie(LHS.Tag, LHS.Id, LHS.Kind) ==
+             std::tie(RHS.Tag, RHS.Id, RHS.Kind);
+    }
+  };
+
+private:
+  std::unordered_map<Entry, uint32_t, EntryHash, EntryEq> Cache;
+
+  uint32_t lookup(const Entry &Entry) {
+    auto Cached = Cache.find(Entry);
+    if (Cached != Cache.end())
+      return Cached->second;
+    return 0;
+  }
+
+public:
+  void add(uint32_t Id, BTFTypeDerived *Type) {
+    Cache[Entry(Type->getPointeeType(), (BTF::TypeKinds)Type->getKind())] = Id;
+  }
+
+  void add(uint32_t Id, BTFTypeTypeTag *Type) {
+    Cache[Entry(Type->getNextTypeId(), Type->getTag())] = Id;
+  }
+
+  uint32_t lookupQualified(uint32_t Id, BTF::TypeKinds Kind) {
+    return lookup(Entry(Id, Kind));
+  }
+
+  uint32_t lookupTypeTag(uint32_t Id, StringRef Tag) {
+    return lookup(Entry(Id, Tag));
+  }
+};
+
+// Convert BTF type chain of shape:
+//   CONST -> VOLATILE -> RESTRICT -> TYPE_TAG -> ...
+//    ^
+//    '- TopId
+//
+// To:
+//   TYPE_TAG -> CONST -> VOLATILE -> RESTRICT -> ...
+//    ^
+//    '- TopId
+void BTFDebug::rebuildTypeTagsChain(uint32_t TopId,
+                                    QualifiedTypesCache &Cache) {
+  SmallSet<BTF::TypeKinds, 3> Qualifiers;
+  SmallVector<StringRef, 3> Tags;
+  uint32_t Id = TopId;
+
+  // First, get to the bottom of the chain, accumulating CVR
+  // qualifiers and type tags
+  for (;;) {
+    if (Id == 0)
+      break;
+    auto *Type = getType(Id);
+    if (!Type)
+      break;
+    switch (Type->getKind()) {
+    case BTF::BTF_KIND_CONST:
+    case BTF::BTF_KIND_VOLATILE:
+    case BTF::BTF_KIND_RESTRICT:
+      Qualifiers.insert((BTF::TypeKinds)Type->getKind());
+      Id = static_cast<BTFTypeDerived *>(Type)->getPointeeType();
+      continue;
+    case BTF::BTF_KIND_TYPE_TAG:
+      Tags.push_back(static_cast<BTFTypeTypeTag *>(Type)->getTag());
+      Id = static_cast<BTFTypeTypeTag *>(Type)->getNextTypeId();
+      continue;
+    }
+    break;
+  }
+
+  if (Qualifiers.empty() || Tags.empty())
+    return;
+
+  // Next, rebuild the chain using shape (Tags (Qualifiers Id)).
+  // Track intermediate result in 'Id'.
+  for (auto Kind :
+       {BTF::BTF_KIND_RESTRICT, BTF::BTF_KIND_VOLATILE, BTF::BTF_KIND_CONST}) {
+    if (!Qualifiers.contains(Kind))
+      continue;
+    if (uint32_t CachedId = Cache.lookupQualified(Id, Kind)) {
+      Id = CachedId;
+      continue;
+    }
+    auto Type = std::make_unique<BTFTypeDerived>(Id, Kind);
+    auto *TypePtr = Type.get();
+    Type->completeType(*this);
+    Id = addType(std::move(Type));
+    Cache.add(Id, TypePtr);
+  }
+
+  // All but last type tags could use cache
+  for (int I = Tags.size() - 1; I > 0; --I) {
+    if (uint32_t CachedId = Cache.lookupTypeTag(Id, Tags[I])) {
+      Id = CachedId;
+      continue;
+    }
+    auto Type = std::make_unique<BTFTypeTypeTag>(Id, Tags[I]);
+    auto *TypePtr = Type.get();
+    Type->completeType(*this);
+    Id = addType(std::move(Type));
+    Cache.add(Id, TypePtr);
+  }
+
+  // Last type tag has to be built anew because it needs to replace
+  // entry at TopId
+  auto Type = std::make_unique<BTFTypeTypeTag>(Id, Tags[0]);
+  Type->completeType(*this);
+  Cache.add(TopId, Type.get());
+  replaceType(TopId, std::move(Type));
+}
+
+// Linux Kernel expects type tags to precede CVR qualifiers, but this
+// is not guaranteed by the way BTF is generated from DWARF.
+// Use a post-processing step to ensure this property.
+//
+// Convert each chain of shape:
+//
+//   CONST -> VOLATILE -> TYPE_TAG -> ...
+//    ^        ^           ^
+//    '- Id_A  '- Id_B     '- Id_C
+//
+// To a set of chains:
+//
+//   TYPE_TAG -> CONST -> VOLATILE -> ...
+//    ^            \       /
+//    '- Id_A       new ids
+//                   |
+//   TYPE_TAG -> VOLATILE -> ...
+//    ^
+//    '- Id_B
+//
+//   TYPE_TAG -> ...
+//    ^
+//    '- Id_C
+//
+// Here TYPE_TAG entries for Id_A and Id_B are newly created and might
+// be redundant, however removing such redundancy would require deletion
+// of Id_B and Id_C entries and update of all BTF ids and references.
+//
+// For now, assume that this should not lead to significant BTF
+// increase in practice and keep redundant entries.
+void BTFDebug::moveTypeTagsBeforeCVR() {
+  const size_t N = TypeEntries.size() + 1;
+  QualifiedTypesCache Cache;
+
+  for (uint32_t Id = 1; Id < N; ++Id) {
+    BTFTypeBase *Type = getType(Id);
+    switch (Type->getKind()) {
+    case BTF::BTF_KIND_CONST:
+    case BTF::BTF_KIND_VOLATILE:
+    case BTF::BTF_KIND_RESTRICT:
+      Cache.add(Id, static_cast<BTFTypeDerived *>(Type));
+      break;
+    case BTF::BTF_KIND_TYPE_TAG:
+      Cache.add(Id, static_cast<BTFTypeTypeTag *>(Type));
+      break;
+    }
+  }
+
+  for (uint32_t Id = 1; Id < N; ++Id)
+    rebuildTypeTagsChain(Id, Cache);
+}
+
 void BTFDebug::endModule() {
   // Collect MapDef globals if not collected yet.
   if (MapDefNotCollected) {
@@ -1602,23 +2082,19 @@ void BTFDebug::endModule() {
 
     // Search through struct types
     uint32_t StructTypeId = 0;
-    for (const auto &StructType : StructTypes) {
-      if (StructType->getName() == TypeName) {
-        StructTypeId = StructType->getId();
-        break;
-      }
-    }
+    if (auto OptId = lookupType(CTy))
+      StructTypeId = *OptId;
 
     if (StructTypeId == 0) {
       auto FwdTypeEntry = std::make_unique<BTFTypeFwd>(TypeName, IsUnion);
-      StructTypeId = addType(std::move(FwdTypeEntry));
+      StructTypeId = addType(std::move(FwdTypeEntry), CTy);
     }
 
     for (auto &TypeInfo : Fixup.second) {
       const DIDerivedType *DTy = TypeInfo.first;
       BTFTypeDerived *BDType = TypeInfo.second;
 
-      int TmpTypeId = genBTFTypeTags(DTy, StructTypeId);
+      int TmpTypeId = genBTFTypeTagsV1(DTy);
       if (TmpTypeId >= 0)
         BDType->setPointeeType(TmpTypeId);
       else
@@ -1630,6 +2106,9 @@ void BTFDebug::endModule() {
   for (const auto &TypeEntry : TypeEntries)
     TypeEntry->completeType(*this);
 
+  // BTF to BTF transformations
+  moveTypeTagsBeforeCVR();
+
   // Emit BTF sections.
   emitBTFSection();
   emitBTFExtSection();
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 11a0c59ba6c90..6ded6958491c2 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -48,6 +48,7 @@ class BTFTypeBase {
   virtual ~BTFTypeBase() = default;
   void setId(uint32_t Id) { this->Id = Id; }
   uint32_t getId() { return Id; }
+  uint32_t getKind() { return Kind; }
   uint32_t roundupToBytes(uint32_t NumBits) { return (NumBits + 7) >> 3; }
   /// Get the size of this BTF type entry.
   virtual uint32_t getSize() { return BTF::CommonTypeSize; }
@@ -68,10 +69,12 @@ class BTFTypeDerived : public BTFTypeBase {
 
 public:
   BTFTypeDerived(const DIDerivedType *Ty, unsigned Tag, bool NeedsFixup);
-  BTFTypeDerived(unsigned NextTypeId, unsigned Tag, StringRef Name);
+  BTFTypeDerived(unsigned NextTypeId, enum BTF::TypeKinds Kind,
+                 StringRef Name = StringRef());
   void completeType(BTFDebug &BDebug) override;
   void emitType(MCStreamer &OS) override;
   void setPointeeType(uint32_t PointeeType);
+  uint32_t getPointeeType();
 };
 
 /// Handle struct or union forward declaration.
@@ -240,6 +243,8 @@ class BTFTypeTypeTag : public BTFTypeBase {
   BTFTypeTypeTag(uint32_t NextTypeId, StringRef Tag);
   BTFTypeTypeTag(const DIDerivedType *DTy, StringRef Tag);
   void completeType(BTFDebug &BDebug) override;
+  uint32_t getNextTypeId();
+  StringRef getTag();
 };
 
 /// String table.
@@ -285,6 +290,20 @@ struct BTFFieldReloc {
   uint32_t RelocKind;     ///< What to patch the instruction
 };
 
+/// Used for de-duplication for types annotated with btf_type_tag annotation,
+/// See comment at BTFDebug.cpp:addType() for details.
+struct BTFTypeDedupKey {
+  const DIType *CanonTy;
+
+  BTFTypeDedupKey(const DIType *CanonTy) : CanonTy(CanonTy) {}
+
+  bool operator==(const BTFTypeDedupKey &Other) const;
+
+  struct Hash {
+    size_t operator()(BTFTypeDedupKey const &Key) const;
+  };
+};
+
 /// Collect and emit BTF information.
 class BTFDebug : public DebugHandlerBase {
   MCStreamer &OS;
@@ -296,6 +315,8 @@ class BTFDebug : public DebugHandlerBase {
   BTFStringTable StringTable;
   std::vector<std::unique_ptr<BTFTypeBase>> TypeEntries;
   std::unordered_map<const DIType *, uint32_t> DIToIdMap;
+  std::unordered_map<BTFTypeDedupKey, uint32_t, BTFTypeDedupKey::Hash>
+      DIDedupMap;
   std::map<uint32_t, std::vector<BTFFuncInfo>> FuncInfoTable;
   std::map<uint32_t, std::vector<BTFLineInfo>> LineInfoTable;
   std::map<uint32_t, std::vector<BTFFieldReloc>> FieldRelocTable;
@@ -311,11 +332,17 @@ class BTFDebug : public DebugHandlerBase {
   /// Add types to TypeEntries.
   /// @{
   /// Add types to TypeEntries and DIToIdMap.
-  uint32_t addType(std::unique_ptr<BTFTypeBase> TypeEntry, const DIType *Ty);
+  uint32_t addType(std::unique_ptr<BTFTypeBase> TypeEntry, const DIType *Ty,
+                   uint32_t *RealId = nullptr);
   /// Add types to TypeEntries only and return type id.
   uint32_t addType(std::unique_ptr<BTFTypeBase> TypeEntry);
+  uint32_t replaceType(uint32_t Id, std::unique_ptr<BTFTypeBase> TypeEntry);
   /// @}
 
+  BTFTypeBase *getType(uint32_t Id);
+
+  std::optional<uint32_t> lookupType(const DIType *Ty);
+
   /// IR type visiting functions.
   /// @{
   void visitTypeEntry(const DIType *Ty);
@@ -368,7 +395,10 @@ class BTFDebug : public DebugHandlerBase {
   /// the base type of DTy. Return the type id of the first BTF type_tag
   /// in the chain. If no type_tag's are generated, a negative value
   /// is returned.
-  int genBTFTypeTags(const DIDerivedType *DTy, int BaseTypeId);
+  uint32_t genBTFTypeTags(const DIType *Ty, int BaseId,
+                          const DIDerivedType *DTy, StringRef AnnotName);
+  uint32_t genBTFTypeTagsV1(const DIDerivedType *DTy);
+  uint32_t genBTFTypeTagsV2(const DIType *Ty, uint32_t BaseId);
 
   /// Generate one field relocation record.
   void generatePatchImmReloc(const MCSymbol *ORSym, uint32_t RootId,
@@ -390,6 +420,16 @@ class BTFDebug : public DebugHandlerBase {
   /// Emit the .BTF.ext section.
   void emitBTFExtSection();
 
+  uint32_t skipBTFTypeTags(uint32_t Id);
+
+  /// BTF post processing phase rewriting type chains like below:
+  ///   CONST -> TYPE_TAG '...' -> ...
+  /// To:
+  ///   TYPE_TAG '...' -> CONST -> ...
+  void moveTypeTagsBeforeCVR();
+  class QualifiedTypesCache;
+  void rebuildTypeTagsChain(uint32_t Id, QualifiedTypesCache &Cache);
+
 protected:
   /// Gather pre-function debug information.
   void beginFunctionImpl(const MachineFunction *MF) override;
diff --git a/llvm/test/CodeGen/BPF/BTF/print_btf.py b/llvm/test/CodeGen/BPF/BTF/print_btf.py
new file mode 100755
index 0000000000000..d6ebbe23edcdb
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/print_btf.py
@@ -0,0 +1,300 @@
+#!/usr/bin/env python3
+
+# Ad-hoc script to print BTF file in a readable format.
+# Follows the same printing conventions as bpftool with format 'raw'.
+# Usage:
+#
+#   ./print_btf.py <btf_file>
+#
+# Parameters:
+#
+#   <btf_file> :: a file name or '-' to read from stdin.
+#
+# Intended usage:
+#
+#   llvm-objcopy --dump-section .BTF=- <input> | ./print_btf.py -
+#
+# Kernel documentation contains detailed format description:
+#   https://www.kernel.org/doc/html/latest/bpf/btf.html
+
+import struct
+import ctypes
+import sys
+
+
+class SafeDict(dict):
+    def __getitem__(self, key):
+        try:
+            return dict.__getitem__(self, key)
+        except KeyError:
+            return f"<BAD_KEY: {key}>"
+
+
+KINDS = SafeDict(
+    {
+        0: "UNKN",
+        1: "INT",
+        2: "PTR",
+        3: "ARRAY",
+        4: "STRUCT",
+        5: "UNION",
+        6: "ENUM",
+        7: "FWD",
+        8: "TYPEDEF",
+        9: "VOLATILE",
+        10: "CONST",
+        11: "RESTRICT",
+        12: "FUNC",
+        13: "FUNC_PROTO",
+        14: "VAR",
+        15: "DATASEC",
+        16: "FLOAT",
+        17: "DECL_TAG",
+        18: "TYPE_TAG",
+        19: "ENUM64",
+    }
+)
+
+INT_ENCODING = SafeDict(
+    {0 << 0: "(none)", 1 << 0: "SIGNED", 1 << 1: "CHAR", 1 << 2: "BOOL"}
+)
+
+ENUM_ENCODING = SafeDict({0: "UNSIGNED", 1: "SIGNED"})
+
+FUNC_LINKAGE = SafeDict({0: "static", 1: "global", 2: "extern"})
+
+VAR_LINKAGE = SafeDict(
+    {
+        0: "static",
+        1: "global",
+    }
+)
+
+FWD_KIND = SafeDict(
+    {
+        0: "struct",
+        1: "union",
+    }
+)
+
+for val, name in KINDS.items():
+    globals()["BTF_KIND_" + name] = val
+
+
+def warn(message):
+    print(message, file=sys.stderr)
+
+
+def print_btf(filename):
+    if filename == "-":
+        buf = sys.stdin.buffer.read()
+    else:
+        with open(filename, "rb") as file:
+            buf = file.read()
+
+    fmt_cache = {}
+    endian_pfx = ""
+    off = 0
+
+    def unpack(fmt):
+        nonlocal off, endian_pfx
+        fmt = endian_pfx + fmt
+        if fmt not in fmt_cache:
+            fmt_cache[fmt] = struct.Struct(fmt)
+        st = fmt_cache[fmt]
+        r = st.unpack_from(buf, off)
+        off += st.size
+        return r
+
+    # Use magic number at the header start to determine endianness
+    (magic,) = unpack("H")
+    if magic == 0xEB9F:
+        endian_pfx = "<"
+    elif magic == 0x9FEB:
+        endian_pfx = ">"
+    else:
+        warn(f"Unexpected BTF magic: {magic:02x}")
+        return
+
+    # Rest of the header
+    version, flags, hdr_len = unpack("BBI")
+    type_off, type_len, str_off, str_len = unpack("IIII")
+
+    # Offsets in the header are relative to the end of a header
+    type_off += hdr_len
+    str_off += hdr_len
+    off = hdr_len
+    type_end = type_off + type_len
+
+    def string(rel_off):
+        try:
+            start = str_off + rel_off
+            end = buf.index(b"\0", start)
+            if start == end:
+                return "(anon)"
+            return buf[start:end].decode("utf8")
+        except ValueError as e:
+            warn(f"Can't get string at offset {str_off} + {rel_off}: {e}")
+            return f"<BAD_STRING {rel_off}>"
+
+    idx = 1
+    while off < type_end:
+        name_off, info, size = unpack("III")
+        kind = (info >> 24) & 0x1F
+        vlen = info & 0xFFFF
+        kflag = info >> 31
+        kind_name = KINDS[kind]
+        name = string(name_off)
+
+        def warn_nonzero(val, name):
+            nonlocal idx
+            if val != 0:
+                warn(f"<{idx}> {name} should be 0 but is {val}")
+
+        if kind == BTF_KIND_INT:
+            (info,) = unpack("I")
+            encoding = (info & 0x0F000000) >> 24
+            offset = (info & 0x00FF0000) >> 16
+            bits = info & 0x000000FF
+            enc_name = INT_ENCODING[encoding]
+            print(
+                f"[{idx}] {kind_name} '{name}' size={size} "
+                f"bits_offset={offset} "
+                f"nr_bits={bits} encoding={enc_name}"
+            )
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(vlen, "vlen")
+
+        elif kind in [
+            BTF_KIND_PTR,
+            BTF_KIND_CONST,
+            BTF_KIND_VOLATILE,
+            BTF_KIND_RESTRICT,
+        ]:
+            print(f"[{idx}] {kind_name} '{name}' type_id={size}")
+            warn_nonzero(name_off, "name_off")
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(vlen, "vlen")
+
+        elif kind == BTF_KIND_ARRAY:
+            warn_nonzero(name_off, "name_off")
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(vlen, "vlen")
+            warn_nonzero(size, "size")
+            type, index_type, nelems = unpack("III")
+            print(
+                f"[{idx}] {kind_name} '{name}' type_id={type} "
+                f"index_type_id={index_type} nr_elems={nelems}"
+            )
+
+        elif kind in [BTF_KIND_STRUCT, BTF_KIND_UNION]:
+            print(f"[{idx}] {kind_name} '{name}' size={size} vlen={vlen}")
+            if kflag not in [0, 1]:
+                warn(f"<{idx}> kflag should 0 or 1: {kflag}")
+            for _ in range(0, vlen):
+                name_off, type, offset = unpack("III")
+                if kflag == 0:
+                    print(
+                        f"\t'{string(name_off)}' type_id={type} "
+                        f"bits_offset={offset}"
+                    )
+                else:
+                    bits_offset = offset & 0xFFFFFF
+                    bitfield_size = offset >> 24
+                    print(
+                        f"\t'{string(name_off)}' type_id={type} "
+                        f"bits_offset={bits_offset} "
+                        f"bitfield_size={bitfield_size}"
+                    )
+
+        elif kind == BTF_KIND_ENUM:
+            encoding = ENUM_ENCODING[kflag]
+            print(
+                f"[{idx}] {kind_name} '{name}' encoding={encoding} "
+                f"size={size} vlen={vlen}"
+            )
+            for _ in range(0, vlen):
+                (name_off,) = unpack("I")
+                (val,) = unpack("i" if kflag == 1 else "I")
+                print(f"\t'{string(name_off)}' val={val}")
+
+        elif kind == BTF_KIND_ENUM64:
+            encoding = ENUM_ENCODING[kflag]
+            print(
+                f"[{idx}] {kind_name} '{name}' encoding={encoding} "
+                f"size={size} vlen={vlen}"
+            )
+            for _ in range(0, vlen):
+                name_off, lo, hi = unpack("III")
+                val = hi << 32 | lo
+                if kflag == 1:
+                    val = ctypes.c_long(val).value
+                print(f"\t'{string(name_off)}' val={val}LL")
+
+        elif kind == BTF_KIND_FWD:
+            print(f"[{idx}] {kind_name} '{name}' fwd_kind={FWD_KIND[kflag]}")
+            warn_nonzero(vlen, "vlen")
+            warn_nonzero(size, "size")
+
+        elif kind in [BTF_KIND_TYPEDEF, BTF_KIND_TYPE_TAG]:
+            print(f"[{idx}] {kind_name} '{name}' type_id={size}")
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(kflag, "vlen")
+
+        elif kind == BTF_KIND_FUNC:
+            linkage = FUNC_LINKAGE[vlen]
+            print(f"[{idx}] {kind_name} '{name}' type_id={size} " f"linkage={linkage}")
+            warn_nonzero(kflag, "kflag")
+
+        elif kind == BTF_KIND_FUNC_PROTO:
+            print(f"[{idx}] {kind_name} '{name}' ret_type_id={size} " f"vlen={vlen}")
+            warn_nonzero(name_off, "name_off")
+            warn_nonzero(kflag, "kflag")
+            for _ in range(0, vlen):
+                name_off, type = unpack("II")
+                print(f"\t'{string(name_off)}' type_id={type}")
+
+        elif kind == BTF_KIND_VAR:
+            (linkage,) = unpack("I")
+            linkage = VAR_LINKAGE[linkage]
+            print(f"[{idx}] {kind_name} '{name}' type_id={size}, " f"linkage={linkage}")
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(vlen, "vlen")
+
+        elif kind == BTF_KIND_DATASEC:
+            print(f"[{idx}] {kind_name} '{name}' size={size} vlen={vlen}")
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(size, "size")
+            for _ in range(0, vlen):
+                type, offset, size = unpack("III")
+                print(f"\ttype_id={type} offset={offset} size={size}")
+
+        elif kind == BTF_KIND_FLOAT:
+            print(f"[{idx}] {kind_name} '{name}' size={size}")
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(vlen, "vlen")
+
+        elif kind == BTF_KIND_DECL_TAG:
+            (component_idx,) = unpack("i")
+            print(
+                f"[{idx}] {kind_name} '{name}' type_id={size} "
+                + f"component_idx={component_idx}"
+            )
+            warn_nonzero(kflag, "kflag")
+            warn_nonzero(vlen, "vlen")
+
+        else:
+            warn(
+                f"<{idx}> Unexpected entry: kind={kind_name} "
+                f"name_off={name_off} "
+                f"vlen={vlen} kflag={kflag} size={size}"
+            )
+
+        idx += 1
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        warn(f"Usage: {sys.argv[0]} <btf_file>")
+        sys.exit(1)
+    print_btf(sys.argv[1])
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll
new file mode 100644
index 0000000000000..6bf4322dfc845
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags-reuse.ll
@@ -0,0 +1,64 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   
+;   const volatile __tag1 __tag2 int a;
+;   const volatile __tag1 int b;
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that const->volatile->int chain is reused for variables 'a' and 'b'.
+
+; CHECK: [1] TYPE_TAG 'tag2' type_id=14
+; CHECK: [2] TYPE_TAG 'tag2' type_id=15
+; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [4] TYPE_TAG 'tag1' type_id=3
+; CHECK: [5] TYPE_TAG 'tag2' type_id=4
+; CHECK: [6] VAR 'a' type_id=1, linkage=global
+; CHECK: [7] TYPE_TAG 'tag1' type_id=13
+; CHECK: [8] TYPE_TAG 'tag1' type_id=12
+; CHECK: [9] TYPE_TAG 'tag1' type_id=3
+; CHECK: [10] VAR 'b' type_id=7, linkage=global
+; CHECK: [11] DATASEC '.rodata' size=0 vlen=2
+; CHECK:    type_id=6 offset=0 size=4
+; CHECK:    type_id=10 offset=0 size=4
+; CHECK: [12] VOLATILE '(anon)' type_id=3
+; CHECK: [13] CONST '(anon)' type_id=12
+; CHECK: [14] TYPE_TAG 'tag1' type_id=13
+; CHECK: [15] TYPE_TAG 'tag1' type_id=12
+
+ at a = dso_local constant i32 0, align 4, !dbg !0
+ at b = dso_local constant i32 0, align 4, !dbg !5
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!17, !18, !19, !20}
+!llvm.ident = !{!21}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 4, type: !12, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8)
+!8 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !9)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !10)
+!10 = !{!11}
+!11 = !{!"btf:type_tag", !"tag1"}
+!12 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13)
+!13 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !14)
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !15)
+!15 = !{!11, !16}
+!16 = !{!"btf:type_tag", !"tag2"}
+!17 = !{i32 7, !"Dwarf Version", i32 5}
+!18 = !{i32 2, !"Debug Info Version", i32 3}
+!19 = !{i32 1, !"wchar_size", i32 4}
+!20 = !{i32 7, !"frame-pointer", i32 2}
+!21 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll
new file mode 100644
index 0000000000000..5ed0bd2aa27ec
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-multiple-tags.ll
@@ -0,0 +1,51 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   
+;   const volatile __tag1 __tag2 int a;
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check generation of type tags in chain of CVR modifiers.
+
+; CHECK: [1] TYPE_TAG 'tag2' type_id=10
+; CHECK: [2] TYPE_TAG 'tag2' type_id=11
+; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [4] TYPE_TAG 'tag1' type_id=3
+; CHECK: [5] TYPE_TAG 'tag2' type_id=4
+; CHECK: [6] VAR 'a' type_id=1, linkage=global
+; CHECK: [7] DATASEC '.rodata' size=0 vlen=1
+; CHECK:    type_id=6 offset=0 size=4
+; CHECK: [8] VOLATILE '(anon)' type_id=3
+; CHECK: [9] CONST '(anon)' type_id=8
+; CHECK: [10] TYPE_TAG 'tag1' type_id=9
+; CHECK: [11] TYPE_TAG 'tag1' type_id=8
+
+ at a = dso_local constant i32 0, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!11, !12, !13, !14}
+!llvm.ident = !{!15}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 4, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !6)
+!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8)
+!8 = !{!9, !10}
+!9 = !{!"btf:type_tag", !"tag1"}
+!10 = !{!"btf:type_tag", !"tag2"}
+!11 = !{i32 7, !"Dwarf Version", i32 5}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{i32 7, !"frame-pointer", i32 2}
+!15 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll
new file mode 100644
index 0000000000000..6b3455112021f
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse-2.ll
@@ -0,0 +1,52 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   
+;   const volatile __tag1 int a;
+;   const volatile __tag1 int b;
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that const->volatile->type_tag chain is reused for variables 'a' and 'b'.
+
+; CHECK: [1] TYPE_TAG 'tag1' type_id=9
+; CHECK: [2] TYPE_TAG 'tag1' type_id=8
+; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [4] TYPE_TAG 'tag1' type_id=3
+; CHECK: [5] VAR 'a' type_id=1, linkage=global
+; CHECK: [6] VAR 'b' type_id=1, linkage=global
+; CHECK: [7] DATASEC '.rodata' size=0 vlen=2
+; CHECK:    type_id=5 offset=0 size=4
+; CHECK:    type_id=6 offset=0 size=4
+; CHECK: [8] VOLATILE '(anon)' type_id=3
+; CHECK: [9] CONST '(anon)' type_id=8
+
+ at a = dso_local constant i32 0, align 4, !dbg !0
+ at b = dso_local constant i32 0, align 4, !dbg !5
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13, !14, !15}
+!llvm.ident = !{!16}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 3, type: !7, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 4, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8)
+!8 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !9)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !10)
+!10 = !{!11}
+!11 = !{!"btf:type_tag", !"tag1"}
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!14 = !{i32 1, !"wchar_size", i32 4}
+!15 = !{i32 7, !"frame-pointer", i32 2}
+!16 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll
new file mode 100644
index 0000000000000..787a10053bb45
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-reuse.ll
@@ -0,0 +1,55 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   
+;   const volatile __tag1 int a;
+;   volatile int b;
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that volatile->int chain is reused for variables 'a' and 'b'.
+
+; CHECK: [1] TYPE_TAG 'tag1' type_id=10
+; CHECK: [2] TYPE_TAG 'tag1' type_id=6
+; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [4] TYPE_TAG 'tag1' type_id=3
+; CHECK: [5] VAR 'a' type_id=1, linkage=global
+; CHECK: [6] VOLATILE '(anon)' type_id=3
+; CHECK: [7] VAR 'b' type_id=6, linkage=global
+; CHECK: [8] DATASEC '.bss' size=0 vlen=1
+; CHECK:    type_id=7 offset=0 size=4
+; CHECK: [9] DATASEC '.rodata' size=0 vlen=1
+; CHECK:    type_id=5 offset=0 size=4
+; CHECK: [10] CONST '(anon)' type_id=6
+
+ at a = dso_local constant i32 0, align 4, !dbg !0
+ at b = dso_local global i32 0, align 4, !dbg !5
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!14, !15, !16, !17}
+!llvm.ident = !{!18}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 3, type: !9, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 4, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !8)
+!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!9 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !10)
+!10 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !11)
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !12)
+!12 = !{!13}
+!13 = !{!"btf:type_tag", !"tag1"}
+!14 = !{i32 7, !"Dwarf Version", i32 5}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{i32 7, !"frame-pointer", i32 2}
+!18 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll
new file mode 100644
index 0000000000000..55f63ebdce709
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-cvr-simple.ll
@@ -0,0 +1,44 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   
+;   const volatile __tag1 int a;
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; CHECK: [1] TYPE_TAG 'tag1' type_id=8
+; CHECK: [2] TYPE_TAG 'tag1' type_id=7
+; CHECK: [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [4] TYPE_TAG 'tag1' type_id=3
+; CHECK: [5] VAR 'a' type_id=1, linkage=global
+; CHECK: [6] DATASEC '.rodata' size=0 vlen=1
+; CHECK:    type_id=5 offset=0 size=4
+; CHECK: [7] VOLATILE '(anon)' type_id=3
+; CHECK: [8] CONST '(anon)' type_id=7
+
+ at a = dso_local constant i32 0, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!10, !11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 3, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !6)
+!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8)
+!8 = !{!9}
+!9 = !{!"btf:type_tag", !"tag1"}
+!10 = !{i32 7, !"Dwarf Version", i32 5}
+!11 = !{i32 2, !"Debug Info Version", i32 3}
+!12 = !{i32 1, !"wchar_size", i32 4}
+!13 = !{i32 7, !"frame-pointer", i32 2}
+!14 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll
new file mode 100644
index 0000000000000..1ed034570c852
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-decl-tag-typedef.ll
@@ -0,0 +1,66 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   #define __dtag1 __attribute__((btf_decl_tag("dtag1")))
+;   #define __dtag2 __attribute__((btf_decl_tag("dtag2")))
+;   
+;   typedef int __tag1 __dtag1 foo;
+;   typedef foo __tag2 __dtag2 bar;
+;   struct buz {
+;     bar a;
+;   } g;
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Verify that for both 'foo' and 'bar' btf_decl_tag applies to 'typedef' ID.
+
+; CHECK: [1] STRUCT 'buz' size=4 vlen=1
+; CHECK:    'a' type_id=2 bits_offset=0
+; CHECK: [2] TYPEDEF 'bar' type_id=5
+; CHECK: [3] DECL_TAG 'dtag2' type_id=2 component_idx=-1
+; CHECK: [4] TYPEDEF 'foo' type_id=8
+; CHECK: [5] TYPE_TAG 'tag2' type_id=4
+; CHECK: [6] DECL_TAG 'dtag1' type_id=4 component_idx=-1
+; CHECK: [7] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [8] TYPE_TAG 'tag1' type_id=7
+; CHECK: [9] VAR 'g' type_id=1, linkage=global
+; CHECK: [10] DATASEC '.bss' size=0 vlen=1
+; CHECK:    type_id=9 offset=0 size=4
+
+%struct.buz = type { i32 }
+
+ at g = dso_local global %struct.buz zeroinitializer, align 4, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!18, !19, !20, !21}
+!llvm.ident = !{!22}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 10, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!4 = !{!0}
+!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "buz", file: !3, line: 8, size: 32, elements: !6)
+!6 = !{!7}
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !5, file: !3, line: 9, baseType: !8, size: 32)
+!8 = !DIDerivedType(tag: DW_TAG_typedef, name: "bar", file: !3, line: 7, baseType: !9, annotations: !16)
+!9 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !3, line: 6, baseType: !10, annotations: !13)
+!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !11)
+!11 = !{!12}
+!12 = !{!"btf:type_tag", !"tag1"}
+!13 = !{!14, !15}
+!14 = !{!"btf:type_tag", !"tag2"}
+!15 = !{!"btf_decl_tag", !"dtag1"}
+!16 = !{!17}
+!17 = !{!"btf_decl_tag", !"dtag2"}
+!18 = !{i32 7, !"Dwarf Version", i32 5}
+!19 = !{i32 2, !"Debug Info Version", i32 3}
+!20 = !{i32 1, !"wchar_size", i32 4}
+!21 = !{i32 7, !"frame-pointer", i32 2}
+!22 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll
new file mode 100644
index 0000000000000..7c1b97ccfc38c
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-enum.ll
@@ -0,0 +1,87 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   
+;   enum foo { FOO };
+;   
+;   struct bar {
+;     enum foo __tag1 aa;
+;     enum foo __tag2 bb;
+;     enum foo cc;
+;   };
+;   
+;   void root(struct bar *bar) {}
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that enum entry is not duplicated in BTF despite duplication in DWARF
+; (entries in DWARF are duplicated because of the presence of type tags).
+
+; CHECK:      [[[#]]] STRUCT 'bar' size=12 vlen=3
+; CHECK-NEXT: 	'aa' type_id=[[#tag1:]] bits_offset=0
+; CHECK-NEXT: 	'bb' type_id=[[#tag2:]] bits_offset=32
+; CHECK-NEXT: 	'cc' type_id=[[#foo:]] bits_offset=64
+; CHECK-NEXT: [[[#foo]]] ENUM 'foo' encoding=UNSIGNED size=4 vlen=1
+; CHECK-NEXT: 	'FOO' val=0
+; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=4
+; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=4
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local void @root(ptr noundef %bar) #0 !dbg !15 {
+entry:
+  %bar.addr = alloca ptr, align 8
+  store ptr %bar, ptr %bar.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !31, metadata !DIExpression()), !dbg !32
+  ret void, !dbg !33
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9, !10, !11, !12, !13}
+!llvm.ident = !{!14}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "foo", file: !1, line: 4, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FOO", value: 0)
+!7 = !{i32 7, !"Dwarf Version", i32 5}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{i32 8, !"PIC Level", i32 2}
+!11 = !{i32 7, !"PIE Level", i32 2}
+!12 = !{i32 7, !"uwtable", i32 2}
+!13 = !{i32 7, !"frame-pointer", i32 2}
+!14 = !{!"clang, some version"}
+!15 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !16, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !30)
+!16 = !DISubroutineType(types: !17)
+!17 = !{null, !18}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, size: 96, elements: !20)
+!20 = !{!21, !25, !29}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !19, file: !1, line: 7, baseType: !22, size: 32)
+!22 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "foo", file: !1, line: 4, baseType: !4, size: 32, elements: !5, annotations: !23)
+!23 = !{!24}
+!24 = !{!"btf:type_tag", !"tag1"}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !19, file: !1, line: 8, baseType: !26, size: 32, offset: 32)
+!26 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "foo", file: !1, line: 4, baseType: !4, size: 32, elements: !5, annotations: !27)
+!27 = !{!28}
+!28 = !{!"btf:type_tag", !"tag2"}
+!29 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !19, file: !1, line: 9, baseType: !3, size: 32, offset: 64)
+!30 = !{}
+!31 = !DILocalVariable(name: "bar", arg: 1, scope: !15, file: !1, line: 12, type: !18)
+!32 = !DILocation(line: 12, column: 23, scope: !15)
+!33 = !DILocation(line: 12, column: 29, scope: !15)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll
new file mode 100644
index 0000000000000..2dd7baaa6e89f
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-struct.ll
@@ -0,0 +1,82 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   
+;   struct foo {};
+;   
+;   struct bar {
+;     struct foo __tag1 aa;
+;     struct foo __tag2 bb;
+;     struct foo cc;
+;   };
+;   
+;   void root(struct bar *bar) {}
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that struct entry is not duplicated in BTF despite duplication in DWARF
+; (entries in DWARF are duplicated because of the presence of type tags).
+
+; CHECK:      [[[#]]] STRUCT 'bar' size=0 vlen=3
+; CHECK-NEXT:   'aa' type_id=[[#tag1:]] bits_offset=0
+; CHECK-NEXT:   'bb' type_id=[[#tag2:]] bits_offset=0
+; CHECK-NEXT:   'cc' type_id=[[#foo:]] bits_offset=0
+; CHECK-NEXT: [[[#foo]]]  STRUCT 'foo' size=0 vlen=0
+; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#foo]]
+; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#foo]]
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local void @root(ptr noundef %bar) #0 !dbg !10 {
+entry:
+  %bar.addr = alloca ptr, align 8
+  store ptr %bar, ptr %bar.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !27, metadata !DIExpression()), !dbg !28
+  ret void, !dbg !29
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"frame-pointer", i32 2}
+!9 = !{!"clang, some version"}
+!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !18)
+!11 = !DISubroutineType(types: !12)
+!12 = !{null, !13}
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, elements: !15)
+!15 = !{!16, !21, !25}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 7, baseType: !17)
+!17 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !19)
+!18 = !{}
+!19 = !{!20}
+!20 = !{!"btf:type_tag", !"tag1"}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 8, baseType: !22)
+!22 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !23)
+!23 = !{!24}
+!24 = !{!"btf:type_tag", !"tag2"}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 9, baseType: !26)
+!26 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, elements: !18)
+!27 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13)
+!28 = !DILocation(line: 12, column: 23, scope: !10)
+!29 = !DILocation(line: 12, column: 29, scope: !10)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll
new file mode 100644
index 0000000000000..c78c64fef419e
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-subroutine.ll
@@ -0,0 +1,106 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   #define __tag3 __attribute__((btf_type_tag("tag3")))
+;
+;   struct bar {
+;     void (__tag1 *aa)(int, int);
+;     void (__tag2 *bb)(int, int);
+;     void (*cc)(int, int);
+;     int (__tag3 *dd)(int, int);
+;   };
+;
+;   void root(struct bar *bar) {}
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that func_proto entry is not duplicated in BTF despite duplication in DWARF
+; (entries in DWARF are duplicated because of the presence of type tags).
+
+; CHECK:      [[[#]]] STRUCT 'bar' size=32 vlen=4
+; CHECK-NEXT:   'aa' type_id=[[#ptag1:]] bits_offset=0
+; CHECK-NEXT:   'bb' type_id=[[#ptag2:]] bits_offset=64
+; CHECK-NEXT:   'cc' type_id=[[#pfunc:]] bits_offset=128
+; CHECK-NEXT:   'dd' type_id=[[#ptag3:]] bits_offset=192
+; CHECK-NEXT: [[[#ptag1]]] PTR '(anon)' type_id=[[#tag1:]]
+; CHECK-NEXT: [[[#func:]]] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2
+; CHECK-NEXT:   '(anon)' type_id=[[#int:]]
+; CHECK-NEXT:   '(anon)' type_id=[[#int]]
+; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#func]]
+; CHECK-NEXT: [[[#int]]] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK-NEXT: [[[#ptag2]]] PTR '(anon)' type_id=[[#tag2:]]
+; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#func]]
+; CHECK-NEXT: [[[#pfunc]]] PTR '(anon)' type_id=[[#func]]
+; CHECK-NEXT: [[[#ptag3]]] PTR '(anon)' type_id=[[#tag3:]]
+; CHECK-NEXT: [[[#func2:]]] FUNC_PROTO '(anon)' ret_type_id=[[#int]] vlen=2
+; CHECK-NEXT:   '(anon)' type_id=[[#int]]
+; CHECK-NEXT:   '(anon)' type_id=[[#int]]
+; CHECK-NEXT: [[[#tag3]]] TYPE_TAG 'tag3' type_id=[[#func2]]
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local void @root(ptr noundef %bar) #0 !dbg !10 {
+entry:
+  %bar.addr = alloca ptr, align 8
+  store ptr %bar, ptr %bar.addr, align 8
+  tail call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !38, metadata !DIExpression()), !dbg !39
+  ret void, !dbg !40
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"frame-pointer", i32 2}
+!9 = !{!"clang some version"}
+!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !37)
+!11 = !DISubroutineType(types: !12)
+!12 = !{null, !13}
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 5, size: 256, elements: !15)
+!15 = !{!16, !23, !28, !31}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 6, baseType: !17, size: 64)
+!17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64)
+!18 = !DISubroutineType(types: !19, annotations: !21)
+!19 = !{null, !20, !20}
+!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!21 = !{!22}
+!22 = !{!"btf:type_tag", !"tag1"}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 7, baseType: !24, size: 64, offset: 64)
+!24 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64)
+!25 = !DISubroutineType(types: !19, annotations: !26)
+!26 = !{!27}
+!27 = !{!"btf:type_tag", !"tag2"}
+!28 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 8, baseType: !29, size: 64, offset: 128)
+!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64)
+!30 = !DISubroutineType(types: !19)
+!31 = !DIDerivedType(tag: DW_TAG_member, name: "dd", scope: !14, file: !1, line: 9, baseType: !32, size: 64, offset: 192)
+!32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64)
+!33 = !DISubroutineType(types: !34, annotations: !35)
+!34 = !{!20, !20, !20}
+!35 = !{!36}
+!36 = !{!"btf:type_tag", !"tag3"}
+!37 = !{}
+!38 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13)
+!39 = !DILocation(line: 12, column: 23, scope: !10)
+!40 = !DILocation(line: 12, column: 29, scope: !10)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll
new file mode 100644
index 0000000000000..d1c34d6e4cccd
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-typedef.ll
@@ -0,0 +1,84 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   
+;   typedef int foo;
+;   
+;   struct bar {
+;     foo __tag1 aa;
+;     foo __tag2 bb;
+;     foo cc;
+;   };
+;   
+;   void root(struct bar *bar) {}
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that typedef entry is not duplicated in BTF despite duplication in DWARF
+; (entries in DWARF are duplicated because of the presence of type tags).
+
+; CHECK:      [[[#]]] STRUCT 'bar' size=12 vlen=3
+; CHECK-NEXT:   'aa' type_id=[[#tag1:]] bits_offset=0
+; CHECK-NEXT:   'bb' type_id=[[#tag2:]] bits_offset=32
+; CHECK-NEXT:   'cc' type_id=[[#foo:]] bits_offset=64
+; CHECK-NEXT: [[[#foo]]] TYPEDEF 'foo' type_id=6
+; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#foo]]
+; CHECK-NEXT: [[[#]]]     INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#foo]]
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local void @root(ptr noundef %bar) #0 !dbg !10 {
+entry:
+  %bar.addr = alloca ptr, align 8
+  store ptr %bar, ptr %bar.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !28, metadata !DIExpression()), !dbg !29
+  ret void, !dbg !30
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"frame-pointer", i32 2}
+!9 = !{!"clang, some version"}
+!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !27)
+!11 = !DISubroutineType(types: !12)
+!12 = !{null, !13}
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, size: 96, elements: !15)
+!15 = !{!16, !21, !25}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 7, baseType: !17, size: 32)
+!17 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !1, line: 4, baseType: !18, annotations: !19)
+!18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!19 = !{!20}
+!20 = !{!"btf:type_tag", !"tag1"}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 8, baseType: !22, size: 32, offset: 32)
+!22 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !1, line: 4, baseType: !18, annotations: !23)
+!23 = !{!24}
+!24 = !{!"btf:type_tag", !"tag2"}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 9, baseType: !26, size: 32, offset: 64)
+!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "foo", file: !1, line: 4, baseType: !18)
+!27 = !{}
+!28 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13)
+!29 = !DILocation(line: 12, column: 23, scope: !10)
+!30 = !DILocation(line: 12, column: 29, scope: !10)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll
new file mode 100644
index 0000000000000..e02dfd8273688
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-dedup-union.ll
@@ -0,0 +1,82 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   
+;   union foo {};
+;   
+;   struct bar {
+;     union foo __tag1 aa;
+;     union foo __tag2 bb;
+;     union foo cc;
+;   };
+;   
+;   void root(struct bar *bar) {}
+;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that union entry is not duplicated in BTF despite duplication in DWARF
+; (entries in DWARF are duplicated because of the presence of type tags).
+
+; CHECK:      [[[#]]] STRUCT 'bar' size=0 vlen=3
+; CHECK-NEXT:   'aa' type_id=[[#tag1:]] bits_offset=0
+; CHECK-NEXT:   'bb' type_id=[[#tag2:]] bits_offset=0
+; CHECK-NEXT:   'cc' type_id=[[#foo:]] bits_offset=0
+; CHECK-NEXT: [[[#foo]]]  UNION 'foo' size=0 vlen=0
+; CHECK-NEXT: [[[#tag1]]] TYPE_TAG 'tag1' type_id=[[#foo]]
+; CHECK-NEXT: [[[#tag2]]] TYPE_TAG 'tag2' type_id=[[#foo]]
+
+; Function Attrs: noinline nounwind optnone uwtable
+define dso_local void @root(ptr noundef %bar) #0 !dbg !10 {
+entry:
+  %bar.addr = alloca ptr, align 8
+  store ptr %bar, ptr %bar.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %bar.addr, metadata !27, metadata !DIExpression()), !dbg !28
+  ret void, !dbg !29
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"frame-pointer", i32 2}
+!9 = !{!"clang, some version"}
+!10 = distinct !DISubprogram(name: "root", scope: !1, file: !1, line: 12, type: !11, scopeLine: 12, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !18)
+!11 = !DISubroutineType(types: !12)
+!12 = !{null, !13}
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", file: !1, line: 6, elements: !15)
+!15 = !{!16, !21, !25}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "aa", scope: !14, file: !1, line: 7, baseType: !17)
+!17 = !DICompositeType(tag: DW_TAG_union_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !19)
+!18 = !{}
+!19 = !{!20}
+!20 = !{!"btf:type_tag", !"tag1"}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "bb", scope: !14, file: !1, line: 8, baseType: !22)
+!22 = !DICompositeType(tag: DW_TAG_union_type, name: "foo", file: !1, line: 4, elements: !18, annotations: !23)
+!23 = !{!24}
+!24 = !{!"btf:type_tag", !"tag2"}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "cc", scope: !14, file: !1, line: 9, baseType: !26)
+!26 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "foo", file: !1, line: 4, elements: !18)
+!27 = !DILocalVariable(name: "bar", arg: 1, scope: !10, file: !1, line: 12, type: !13)
+!28 = !DILocation(line: 12, column: 23, scope: !10)
+!29 = !DILocation(line: 12, column: 29, scope: !10)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll
new file mode 100644
index 0000000000000..af697fa272290
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-field-relo.ll
@@ -0,0 +1,103 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s | llvm-objdump -dr --no-show-raw-insn - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s | llvm-objdump -dr --no-show-raw-insn - | FileCheck %s
+;
+; Source:
+;
+;   #define __pai  __attribute__((preserve_access_index));
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;
+;   struct alpha {
+;     int zulu;
+;   } __pai;
+;
+;   struct bravo {
+;     struct alpha __tag1 *yankee;
+;   } __pai;
+;
+;   int func(struct bravo *xray) {
+;     return xray->yankee->zulu;
+;   }
+;
+; Compilation command:
+; 
+;   cat test.c | clang -mllvm -btf-type-tag-v2 -x c -target bpf -O2 -g -emit-llvm -S - -o -
+;
+; The relocation entry for zulu should point to STRUCT 'alpha',
+; not TYPE_TAG 'tag1' -> STRUCT 'alpha'.
+
+@"llvm.alpha:0:0$0:0" = external global i64, !llvm.preserve.access.index !0 #0
+@"llvm.bravo:0:0$0:0" = external global i64, !llvm.preserve.access.index !8 #0
+
+; Function Attrs: nofree nosync nounwind memory(read, inaccessiblemem: none)
+define dso_local i32 @func(ptr noundef readonly %xray) local_unnamed_addr #1 !dbg !20 {
+entry:
+  tail call void @llvm.dbg.value(metadata ptr %xray, metadata !25, metadata !DIExpression()), !dbg !26
+  %0 = load i64, ptr @"llvm.bravo:0:0$0:0", align 8
+  %1 = getelementptr i8, ptr %xray, i64 %0
+  %2 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 1, ptr %1)
+  %3 = load ptr, ptr %2, align 8, !dbg !27, !tbaa !28
+  %4 = load i64, ptr @"llvm.alpha:0:0$0:0", align 8
+  %5 = getelementptr i8, ptr %3, i64 %4
+  %6 = tail call ptr @llvm.bpf.passthrough.p0.p0(i32 0, ptr %5)
+  %7 = load i32, ptr %6, align 4, !dbg !33, !tbaa !34
+  ret i32 %7, !dbg !37
+}
+
+; CHECK:      r[[#]] = *(u64 *)(r[[#]] + 0x0)
+; CHECK-NEXT:   CO-RE <byte_off> [[[#]]] struct bravo::yankee (0:0)
+; CHECK-NEXT: r[[#]] = *(u32 *)(r[[#]] + 0x0)
+; CHECK-NEXT:   CO-RE <byte_off> [[[#]]] struct alpha::zulu (0:0)
+
+; Function Attrs: nofree nosync nounwind memory(none)
+declare ptr @llvm.bpf.passthrough.p0.p0(i32, ptr) #2
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.value(metadata, metadata, metadata) #3
+
+attributes #0 = { "btf_ama" }
+attributes #1 = { nofree nosync nounwind memory(read, inaccessiblemem: none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nofree nosync nounwind memory(none) }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+
+!llvm.dbg.cu = !{!12}
+!llvm.module.flags = !{!14, !15, !16, !17, !18}
+!llvm.ident = !{!19}
+
+!0 = !DICompositeType(tag: DW_TAG_structure_type, name: "alpha", file: !1, line: 4, size: 32, elements: !2, annotations: !6)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir")
+!2 = !{!3}
+!3 = !DIDerivedType(tag: DW_TAG_member, name: "zulu", scope: !4, file: !1, line: 5, baseType: !5, size: 32)
+!4 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "alpha", file: !1, line: 4, size: 32, elements: !2)
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!6 = !{!7}
+!7 = !{!"btf:type_tag", !"tag1"}
+!8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bravo", file: !1, line: 8, size: 64, elements: !9)
+!9 = !{!10}
+!10 = !DIDerivedType(tag: DW_TAG_member, name: "yankee", scope: !8, file: !1, line: 9, baseType: !11, size: 64)
+!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !0, size: 64)
+!12 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang some version", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !13, splitDebugInlining: false, nameTableKind: None)
+!13 = !{!0}
+!14 = !{i32 7, !"Dwarf Version", i32 5}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{i32 7, !"frame-pointer", i32 2}
+!18 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!19 = !{!"clang, some version"}
+!20 = distinct !DISubprogram(name: "func", scope: !1, file: !1, line: 12, type: !21, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !12, retainedNodes: !24)
+!21 = !DISubroutineType(types: !22)
+!22 = !{!5, !23}
+!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64)
+!24 = !{!25}
+!25 = !DILocalVariable(name: "xray", arg: 1, scope: !20, file: !1, line: 12, type: !23)
+!26 = !DILocation(line: 0, scope: !20)
+!27 = !DILocation(line: 13, column: 16, scope: !20)
+!28 = !{!29, !30, i64 0}
+!29 = !{!"bravo", !30, i64 0}
+!30 = !{!"any pointer", !31, i64 0}
+!31 = !{!"omnipotent char", !32, i64 0}
+!32 = !{!"Simple C/C++ TBAA"}
+!33 = !DILocation(line: 13, column: 24, scope: !20)
+!34 = !{!35, !36, i64 0}
+!35 = !{!"alpha", !36, i64 0}
+!36 = !{!"int", !31, i64 0}
+!37 = !DILocation(line: 13, column: 3, scope: !20)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd-v2.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd-v2.ll
new file mode 100644
index 0000000000000..bfca93f9c4407
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-fwd-v2.ll
@@ -0,0 +1,102 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;
+;   struct foo;
+;   struct map_value {
+;           struct foo __tag2 __tag1 *ptr;
+;   };
+;   void func(struct map_value *);
+;   void test(void)
+;   {
+;           struct map_value v = {};
+;           func(&v);
+;   }
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -target bpf -O2 -g -S -emit-llvm test.c
+
+; Check generation of type tags attached to forward declaration.
+
+; CHECK: [1] FUNC_PROTO '(anon)' ret_type_id=0 vlen=0
+; CHECK: [2] FUNC 'test' type_id=1 linkage=global
+; CHECK: [3] FUNC_PROTO '(anon)' ret_type_id=0 vlen=1
+; CHECK: 	'(anon)' type_id=4
+; CHECK: [4] PTR '(anon)' type_id=5
+; CHECK: [5] STRUCT 'map_value' size=8 vlen=1
+; CHECK: 	'ptr' type_id=6 bits_offset=0
+; CHECK: [6] PTR '(anon)' type_id=9
+; CHECK: [7] FWD 'foo' fwd_kind=struct
+; CHECK: [8] TYPE_TAG 'tag2' type_id=7
+; CHECK: [9] TYPE_TAG 'tag1' type_id=8
+; CHECK: [10] FUNC 'func' type_id=3 linkage=extern
+
+%struct.map_value = type { ptr }
+
+; Function Attrs: nounwind
+define dso_local void @test() local_unnamed_addr #0 !dbg !7 {
+entry:
+  %v = alloca %struct.map_value, align 8
+  call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %v) #4, !dbg !23
+  call void @llvm.dbg.declare(metadata ptr %v, metadata !11, metadata !DIExpression()), !dbg !24
+  store i64 0, ptr %v, align 8, !dbg !24
+  call void @func(ptr noundef nonnull %v) #4, !dbg !25
+  call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %v) #4, !dbg !26
+  ret void, !dbg !26
+}
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
+
+declare !dbg !27 dso_local void @func(ptr noundef) local_unnamed_addr #3
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1
+
+attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang, some", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+!6 = !{!"clang, some version"}
+!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 9, type: !8, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !{!11}
+!11 = !DILocalVariable(name: "v", scope: !7, file: !1, line: 11, type: !12)
+!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: !1, line: 5, size: 64, elements: !13)
+!13 = !{!14}
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !12, file: !1, line: 6, baseType: !15, size: 64)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, flags: DIFlagFwdDecl, annotations: !17)
+!17 = !{!18, !19}
+!18 = !{!"btf:type_tag", !"tag2"}
+!19 = !{!"btf:type_tag", !"tag1"}
+!23 = !DILocation(line: 11, column: 9, scope: !7)
+!24 = !DILocation(line: 11, column: 26, scope: !7)
+!25 = !DILocation(line: 12, column: 9, scope: !7)
+!26 = !DILocation(line: 13, column: 1, scope: !7)
+!27 = !DISubprogram(name: "func", scope: !1, file: !1, line: 8, type: !28, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !31)
+!28 = !DISubroutineType(types: !29)
+!29 = !{null, !30}
+!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64)
+!31 = !{!32}
+!32 = !DILocalVariable(arg: 1, scope: !27, file: !1, line: 8, type: !30)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved-v2.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved-v2.ll
new file mode 100644
index 0000000000000..e70c43e28315d
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-fixup-resolved-v2.ll
@@ -0,0 +1,112 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;
+;   struct foo {
+;           int i;
+;   };
+;   struct map_value {
+;           struct foo __tag2 __tag1 *ptr;
+;   };
+;   void func(struct map_value *, struct foo *);
+;   void test(void)
+;   {
+;           struct map_value v = {};
+;           func(&v, v.ptr);
+;   }
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -target bpf -O2 -g -S -emit-llvm test.c
+
+; CHECK: [1] FUNC_PROTO '(anon)' ret_type_id=0 vlen=0
+; CHECK: [2] FUNC 'test' type_id=1 linkage=global
+; CHECK: [3] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2
+; CHECK: 	'(anon)' type_id=4
+; CHECK: 	'(anon)' type_id=7
+; CHECK: [4] PTR '(anon)' type_id=5
+; CHECK: [5] STRUCT 'map_value' size=8 vlen=1
+; CHECK: 	'ptr' type_id=6 bits_offset=0
+; CHECK: [6] PTR '(anon)' type_id=12
+; CHECK: [7] PTR '(anon)' type_id=8
+; CHECK: [8] STRUCT 'foo' size=4 vlen=1
+; CHECK: 	'i' type_id=9 bits_offset=0
+; CHECK: [9] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [10] FUNC 'func' type_id=3 linkage=extern
+; CHECK: [11] TYPE_TAG 'tag2' type_id=8
+; CHECK: [12] TYPE_TAG 'tag1' type_id=11
+
+%struct.map_value = type { ptr }
+
+; Function Attrs: nounwind
+define dso_local void @test() local_unnamed_addr #0 !dbg !7 {
+entry:
+  %v = alloca %struct.map_value, align 8
+  call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %v) #4, !dbg !27
+  call void @llvm.dbg.declare(metadata ptr %v, metadata !11, metadata !DIExpression()), !dbg !28
+  store i64 0, ptr %v, align 8, !dbg !28
+  call void @func(ptr noundef nonnull %v, ptr noundef null) #4, !dbg !29
+  call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %v) #4, !dbg !30
+  ret void, !dbg !30
+}
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #2
+
+declare !dbg !31 dso_local void @func(ptr noundef, ptr noundef) local_unnamed_addr #3
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
+declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1
+
+attributes #0 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
+attributes #2 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #3 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang, some version", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "some-file.c", directory: "/some/dir/")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"frame-pointer", i32 2}
+!6 = !{!"clang, some version"}
+!7 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 11, type: !8, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !{!11}
+!11 = !DILocalVariable(name: "v", scope: !7, file: !1, line: 13, type: !12)
+!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "map_value", file: !1, line: 7, size: 64, elements: !13)
+!13 = !{!14}
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !12, file: !1, line: 8, baseType: !15, size: 64)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, size: 32, elements: !17, annotations: !21)
+!17 = !{!18}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !1, line: 5, baseType: !20, size: 32)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", file: !1, line: 4, size: 32, elements: !17)
+!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!21 = !{!22, !23}
+!22 = !{!"btf:type_tag", !"tag2"}
+!23 = !{!"btf:type_tag", !"tag1"}
+!27 = !DILocation(line: 13, column: 9, scope: !7)
+!28 = !DILocation(line: 13, column: 26, scope: !7)
+!29 = !DILocation(line: 14, column: 9, scope: !7)
+!30 = !DILocation(line: 15, column: 1, scope: !7)
+!31 = !DISubprogram(name: "func", scope: !1, file: !1, line: 10, type: !32, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !36)
+!32 = !DISubroutineType(types: !33)
+!33 = !{null, !34, !35}
+!34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64)
+!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !19, size: 64)
+!36 = !{!37, !38}
+!37 = !DILocalVariable(arg: 1, scope: !31, file: !1, line: 10, type: !34)
+!38 = !DILocalVariable(arg: 2, scope: !31, file: !1, line: 10, type: !35)
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll
new file mode 100644
index 0000000000000..d38fcfac02f96
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-skip-var.ll
@@ -0,0 +1,49 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   int __tag1 *aa;
+;   int __tag1  bb;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+;
+; BTF VAR entry for `b` should be stripped of type tag.
+
+; CHECK: [1] PTR '(anon)' type_id=3
+; CHECK: [2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [3] TYPE_TAG 'tag1' type_id=2
+; CHECK: [4] VAR 'aa' type_id=1, linkage=global
+; CHECK: [5] VAR 'bb' type_id=2, linkage=global
+; CHECK: [6] DATASEC '.bss' size=0 vlen=2
+; CHECK: 	type_id=4 offset=0 size=8
+; CHECK: 	type_id=5 offset=0 size=4
+
+ at aa = dso_local global ptr null, align 8, !dbg !0
+ at bb = dso_local global i32 0, align 4, !dbg !5
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!11, !12, !13, !14, !15, !16, !17}
+!llvm.ident = !{!18}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "aa", scope: !2, file: !3, line: 2, type: !10, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir")
+!4 = !{!0, !5}
+!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression())
+!6 = distinct !DIGlobalVariable(name: "bb", scope: !2, file: !3, line: 3, type: !7, isLocal: false, isDefinition: true)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8)
+!8 = !{!9}
+!9 = !{!"btf:type_tag", !"tag1"}
+!10 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!11 = !{i32 7, !"Dwarf Version", i32 5}
+!12 = !{i32 2, !"Debug Info Version", i32 3}
+!13 = !{i32 1, !"wchar_size", i32 4}
+!14 = !{i32 8, !"PIC Level", i32 2}
+!15 = !{i32 7, !"PIE Level", i32 2}
+!16 = !{i32 7, !"uwtable", i32 2}
+!17 = !{i32 7, !"frame-pointer", i32 2}
+!18 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-var-v2.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-var-v2.ll
new file mode 100644
index 0000000000000..482fc5c0acbeb
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-var-v2.ll
@@ -0,0 +1,46 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   #define __tag2 __attribute__((btf_type_tag("tag2")))
+;   int __tag1 * __tag1 __tag2 *g;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -target bpf -O2 -g -S -emit-llvm test.c
+
+; CHECK: [1] PTR '(anon)' type_id=4
+; CHECK: [2] PTR '(anon)' type_id=6
+; CHECK: [3] TYPE_TAG 'tag1' type_id=2
+; CHECK: [4] TYPE_TAG 'tag2' type_id=3
+; CHECK: [5] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
+; CHECK: [6] TYPE_TAG 'tag1' type_id=5
+; CHECK: [7] VAR 'g' type_id=1, linkage=global
+; CHECK: [8] DATASEC '.bss' size=0 vlen=1
+; CHECK: 	type_id=7 offset=0 size=8
+
+ at g = dso_local local_unnamed_addr global ptr null, align 8, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!12, !13, !14, !15}
+!llvm.ident = !{!16}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !3, line: 3, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64)
+!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64, annotations: !10)
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed, annotations: !8)
+!8 = !{!9}
+!9 = !{!"btf:type_tag", !"tag1"}
+!10 = !{!9, !11}
+!11 = !{!"btf:type_tag", !"tag2"}
+
+!12 = !{i32 7, !"Dwarf Version", i32 5}
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!14 = !{i32 1, !"wchar_size", i32 4}
+!15 = !{i32 7, !"frame-pointer", i32 2}
+!16 = !{!"clang, some version"}
diff --git a/llvm/test/CodeGen/BPF/BTF/type-tag-void.ll b/llvm/test/CodeGen/BPF/BTF/type-tag-void.ll
new file mode 100644
index 0000000000000..2081fa217d7c5
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/type-tag-void.ll
@@ -0,0 +1,43 @@
+; RUN: llc -march=bpfel -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+; RUN: llc -march=bpfeb -filetype=obj -o - %s \
+; RUN: | llvm-objcopy --dump-section .BTF=- - | %python %S/print_btf.py - | FileCheck %s
+;
+; Source:
+;   #define __tag1 __attribute__((btf_type_tag("tag1")))
+;   void __tag1 *foo;
+; Compilation flag:
+;   clang -mllvm -btf-type-tag-v2 -S -g -emit-llvm test.c -o test.ll
+
+; Check that btf:type_tag attached to DW_TAG_unspecified_type is
+; converted to type tag referencing 0.
+
+; CHECK: [1] PTR '(anon)' type_id=2
+; CHECK: [2] TYPE_TAG 'tag1' type_id=0
+; CHECK: [3] VAR 'foo' type_id=1, linkage=global
+; CHECK: [4] DATASEC '.bss' size=0 vlen=1
+; CHECK: 	type_id=3 offset=0 size=8
+
+ at foo = dso_local global ptr null, align 8, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!9, !10, !11, !12, !13, !14, !15}
+!llvm.ident = !{!16}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "foo", scope: !2, file: !3, line: 2, type: !5, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C11, file: !3, producer: "clang, some version", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "some-file.c", directory: "/some/dir")
+!4 = !{!0}
+!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64)
+!6 = !DIBasicType(tag: DW_TAG_unspecified_type, name: "void", annotations: !7)
+!7 = !{!8}
+!8 = !{!"btf:type_tag", !"tag1"}
+!9 = !{i32 7, !"Dwarf Version", i32 5}
+!10 = !{i32 2, !"Debug Info Version", i32 3}
+!11 = !{i32 1, !"wchar_size", i32 4}
+!12 = !{i32 8, !"PIC Level", i32 2}
+!13 = !{i32 7, !"PIE Level", i32 2}
+!14 = !{i32 7, !"uwtable", i32 2}
+!15 = !{i32 7, !"frame-pointer", i32 2}
+!16 = !{!"clang, some version"}



More information about the cfe-commits mailing list