[llvm] [CodeView] Support LF_CLASS2 and friends (PR #228761)

via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 3 12:52:21 PDT 2026


https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/228761

None

>From ee094eb006d3dc3f115bd18b59736b45f377b3be Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Sat, 3 Oct 2026 21:48:04 +0200
Subject: [PATCH] [CodeView] Support LF_CLASS2 and friends

---
 .../DebugInfo/CodeView/CodeViewRecordIO.h     |  12 ++
 .../llvm/DebugInfo/CodeView/CodeViewTypes.def |  12 +-
 .../llvm/DebugInfo/CodeView/TypeRecord.h      |  24 ++-
 llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp |  18 +-
 .../DebugInfo/CodeView/TypeIndexDiscovery.cpp |   4 +
 .../DebugInfo/CodeView/TypeRecordHelpers.cpp  |   8 +
 .../DebugInfo/CodeView/TypeRecordMapping.cpp  |  87 ++++++--
 .../LogicalView/Readers/LVCodeViewVisitor.cpp |   8 +-
 .../DebugInfo/PDB/Native/NativeExeSymbol.cpp  |  13 +-
 .../DebugInfo/PDB/Native/NativeTypeUDT.cpp    |   4 +
 llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp |   4 +
 llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp  |   8 +
 llvm/test/DebugInfo/COFF/tag-types2.ll        | 198 ++++++++++++++++++
 llvm/test/DebugInfo/PDB/DIA/tag-types2.test   |  71 +++++++
 .../test/DebugInfo/PDB/Inputs/tag-types2.yaml | 146 +++++++++++++
 .../test/DebugInfo/PDB/Native/tag-types2.test |  73 +++++++
 llvm/test/tools/llvm-pdbutil/tag-types2.yaml  | 148 +++++++++++++
 .../llvm-pdbutil/TypeReferenceTracker.cpp     |   4 +
 .../CodeView/TypeIndexDiscoveryTest.cpp       |  16 ++
 19 files changed, 817 insertions(+), 41 deletions(-)
 create mode 100644 llvm/test/DebugInfo/COFF/tag-types2.ll
 create mode 100644 llvm/test/DebugInfo/PDB/DIA/tag-types2.test
 create mode 100644 llvm/test/DebugInfo/PDB/Inputs/tag-types2.yaml
 create mode 100644 llvm/test/DebugInfo/PDB/Native/tag-types2.test
 create mode 100644 llvm/test/tools/llvm-pdbutil/tag-types2.yaml

diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h b/llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h
index 668428e487e11..90033371e11e6 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h
@@ -112,6 +112,18 @@ class CodeViewRecordIO {
     return Reader->readInteger(Value);
   }
 
+  template <typename Trunc, typename T>
+  Error mapTruncInteger(T &Value, const Twine &Comment = "") {
+    static_assert(sizeof(Trunc) <= sizeof(T));
+
+    Trunc Truncated = static_cast<Trunc>(Value);
+    auto EC = mapInteger(Truncated, Comment);
+    if (!EC && isReading())
+      Value = Truncated;
+
+    return EC;
+  }
+
   template <typename T> Error mapEnum(T &Value, const Twine &Comment = "") {
     if (!isStreaming() && sizeof(Value) > maxFieldLength())
       return make_error<CodeViewError>(cv_error_code::insufficient_buffer);
diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeViewTypes.def b/llvm/include/llvm/DebugInfo/CodeView/CodeViewTypes.def
index 86a74292dbb11..9830d8c797230 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CodeViewTypes.def
+++ b/llvm/include/llvm/DebugInfo/CodeView/CodeViewTypes.def
@@ -50,6 +50,13 @@ TYPE_RECORD_ALIAS(LF_STRUCTURE, 0x1505, Struct, Class)
 TYPE_RECORD_ALIAS(LF_INTERFACE, 0x1519, Interface, Class)
 TYPE_RECORD(LF_UNION, 0x1506, Union)
 TYPE_RECORD(LF_ENUM, 0x1507, Enum)
+
+TYPE_RECORD_ALIAS(LF_CLASS2, 0x1608, Class2, Class)
+TYPE_RECORD_ALIAS(LF_STRUCTURE2, 0x1609, Struct2, Class)
+TYPE_RECORD_ALIAS(LF_INTERFACE2, 0x160b, Interface2, Class)
+
+TYPE_RECORD_ALIAS(LF_UNION2, 0x160a, Union2, Union)
+
 TYPE_RECORD(LF_TYPESERVER2, 0x1515, TypeServer2)
 TYPE_RECORD(LF_VFTABLE, 0x151d, VFTable)
 TYPE_RECORD(LF_VTSHAPE, 0x000a, VFTableShape)
@@ -83,11 +90,6 @@ TYPE_RECORD(LF_STRING_ID, 0x1605, StringId)
 TYPE_RECORD(LF_UDT_SRC_LINE, 0x1606, UdtSourceLine)
 TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine)
 
-CV_TYPE(LF_CLASS2, 0x1608)
-CV_TYPE(LF_STRUCTURE2, 0x1609)
-CV_TYPE(LF_UNION2, 0x160a)
-CV_TYPE(LF_INTERFACE2, 0x160b)
-
 TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList)
 
 TYPE_RECORD(LF_PRECOMP, 0x1509, Precomp)
diff --git a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
index 5a84fac5f5903..4f12fc5938d12 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/TypeRecord.h
@@ -417,7 +417,7 @@ class TagRecord : public TypeRecord {
 protected:
   TagRecord() = default;
   explicit TagRecord(TypeRecordKind Kind) : TypeRecord(Kind) {}
-  TagRecord(TypeRecordKind Kind, uint16_t MemberCount, ClassOptions Options,
+  TagRecord(TypeRecordKind Kind, uint64_t MemberCount, ClassOptions Options,
             TypeIndex FieldList, StringRef Name, StringRef UniqueName)
       : TypeRecord(Kind), MemberCount(MemberCount), Options(Options),
         FieldList(FieldList), Name(Name), UniqueName(UniqueName) {}
@@ -448,25 +448,29 @@ class TagRecord : public TypeRecord {
     return (Options & ClassOptions::Scoped) != ClassOptions::None;
   }
 
-  uint16_t getMemberCount() const { return MemberCount; }
+  uint64_t getMemberCount() const { return MemberCount; }
   ClassOptions getOptions() const { return Options; }
   TypeIndex getFieldList() const { return FieldList; }
   StringRef getName() const { return Name; }
   StringRef getUniqueName() const { return UniqueName; }
 
-  uint16_t MemberCount = 0;
+  /// Number of members in the associated field list.
+  ///
+  /// Note that the full 64 bit range can only be used by the *2 records.
+  /// The others use a 16 bit value for this.
+  uint64_t MemberCount = 0;
   ClassOptions Options = ClassOptions::None;
   TypeIndex FieldList;
   StringRef Name;
   StringRef UniqueName;
 };
 
-// LF_CLASS, LF_STRUCTURE, LF_INTERFACE
+// LF_CLASS, LF_STRUCTURE, LF_INTERFACE, LF_CLASS2, LF_STRUCTURE2, LF_INTERFACE2
 class ClassRecord : public TagRecord {
 public:
   ClassRecord() = default;
   explicit ClassRecord(TypeRecordKind Kind) : TagRecord(Kind) {}
-  ClassRecord(TypeRecordKind Kind, uint16_t MemberCount, ClassOptions Options,
+  ClassRecord(TypeRecordKind Kind, uint64_t MemberCount, ClassOptions Options,
               TypeIndex FieldList, TypeIndex DerivationList,
               TypeIndex VTableShape, uint64_t Size, StringRef Name,
               StringRef UniqueName)
@@ -494,14 +498,18 @@ class ClassRecord : public TagRecord {
   uint64_t Size = 0;
 };
 
-// LF_UNION
+// LF_UNION, LF_UNION2
 struct UnionRecord : public TagRecord {
   UnionRecord() = default;
   explicit UnionRecord(TypeRecordKind Kind) : TagRecord(Kind) {}
   UnionRecord(uint16_t MemberCount, ClassOptions Options, TypeIndex FieldList,
               uint64_t Size, StringRef Name, StringRef UniqueName)
-      : TagRecord(TypeRecordKind::Union, MemberCount, Options, FieldList, Name,
-                  UniqueName),
+      : UnionRecord(TypeRecordKind::Union, MemberCount, Options, FieldList,
+                    Size, Name, UniqueName) {}
+  UnionRecord(TypeRecordKind Kind, uint16_t MemberCount, ClassOptions Options,
+              TypeIndex FieldList, uint64_t Size, StringRef Name,
+              StringRef UniqueName)
+      : TagRecord(Kind, MemberCount, Options, FieldList, Name, UniqueName),
         Size(Size) {}
 
   HfaKind getHfa() const {
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index 84ea5e349f01d..a417151610000 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -74,6 +74,12 @@
 using namespace llvm;
 using namespace llvm::codeview;
 
+static cl::opt<bool> UseTagRecord2(
+    "use-codeview-tagrecord2", cl::Hidden,
+    cl::desc(
+        "Use the *2 versions for tag records in CodeView (LF_CLASS2, etc.)"),
+    cl::init(false));
+
 namespace {
 class CVMCAdapter : public CodeViewRecordStreamer {
 public:
@@ -2205,9 +2211,9 @@ static MethodKind translateMethodKindFlags(const DISubprogram *SP,
 static TypeRecordKind getRecordKind(const DICompositeType *Ty) {
   switch (Ty->getTag()) {
   case dwarf::DW_TAG_class_type:
-    return TypeRecordKind::Class;
+    return UseTagRecord2 ? TypeRecordKind::Class2 : TypeRecordKind::Class;
   case dwarf::DW_TAG_structure_type:
-    return TypeRecordKind::Struct;
+    return UseTagRecord2 ? TypeRecordKind::Struct2 : TypeRecordKind::Struct;
   default:
     llvm_unreachable("unexpected tag");
   }
@@ -2511,7 +2517,9 @@ TypeIndex CodeViewDebug::lowerTypeUnion(const DICompositeType *Ty) {
   ClassOptions CO =
       ClassOptions::ForwardReference | getCommonClassOptions(Ty);
   std::string FullName = getFullyQualifiedName(Ty);
-  UnionRecord UR(0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier());
+  TypeRecordKind Kind =
+      UseTagRecord2 ? TypeRecordKind::Union2 : TypeRecordKind::Union;
+  UnionRecord UR(Kind, 0, CO, TypeIndex(), 0, FullName, Ty->getIdentifier());
   TypeIndex FwdDeclTI = TypeTable.writeLeafType(UR);
   if (!Ty->isForwardDecl())
     DeferredCompleteTypes.push_back(Ty);
@@ -2532,7 +2540,9 @@ TypeIndex CodeViewDebug::lowerCompleteTypeUnion(const DICompositeType *Ty) {
   uint64_t SizeInBytes = Ty->getSizeInBits() / 8;
   std::string FullName = getFullyQualifiedName(Ty);
 
-  UnionRecord UR(FieldCount, CO, FieldTI, SizeInBytes, FullName,
+  TypeRecordKind Kind =
+      UseTagRecord2 ? TypeRecordKind::Union2 : TypeRecordKind::Union;
+  UnionRecord UR(Kind, FieldCount, CO, FieldTI, SizeInBytes, FullName,
                  Ty->getIdentifier());
   TypeIndex UnionTI = TypeTable.writeLeafType(UR);
 
diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
index 745c53a8de32a..e1f8085ff21c3 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp
@@ -327,9 +327,13 @@ static void discoverTypeIndices(ArrayRef<uint8_t> Content, TypeLeafKind Kind,
   case TypeLeafKind::LF_CLASS:
   case TypeLeafKind::LF_STRUCTURE:
   case TypeLeafKind::LF_INTERFACE:
+  case TypeLeafKind::LF_CLASS2:
+  case TypeLeafKind::LF_STRUCTURE2:
+  case TypeLeafKind::LF_INTERFACE2:
     Refs.push_back({TiRefKind::TypeRef, 4, 3});
     break;
   case TypeLeafKind::LF_UNION:
+  case TypeLeafKind::LF_UNION2:
     Refs.push_back({TiRefKind::TypeRef, 4, 1});
     break;
   case TypeLeafKind::LF_ENUM:
diff --git a/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp
index 35acfcf18d175..dee90e18a5e58 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeRecordHelpers.cpp
@@ -30,12 +30,16 @@ bool llvm::codeview::isUdtForwardRef(CVType CVT) {
   case LF_STRUCTURE:
   case LF_CLASS:
   case LF_INTERFACE:
+  case LF_STRUCTURE2:
+  case LF_CLASS2:
+  case LF_INTERFACE2:
     UdtOptions = getUdtOptions<ClassRecord>(std::move(CVT));
     break;
   case LF_ENUM:
     UdtOptions = getUdtOptions<EnumRecord>(std::move(CVT));
     break;
   case LF_UNION:
+  case LF_UNION2:
     UdtOptions = getUdtOptions<UnionRecord>(std::move(CVT));
     break;
   default:
@@ -173,8 +177,12 @@ uint64_t llvm::codeview::getSizeInBytesForTypeRecord(CVType CVT) {
   case LF_STRUCTURE:
   case LF_CLASS:
   case LF_INTERFACE:
+  case LF_STRUCTURE2:
+  case LF_CLASS2:
+  case LF_INTERFACE2:
     return getUdtSize<ClassRecord>(std::move(CVT));
   case LF_UNION:
+  case LF_UNION2:
     return getUdtSize<UnionRecord>(std::move(CVT));
   default:
     assert(false && "not an aggregate");
diff --git a/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp b/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp
index e8c9744935bf7..a58af389d8f74 100644
--- a/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp
+++ b/llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp
@@ -19,6 +19,7 @@
 #include "llvm/DebugInfo/CodeView/RecordSerialization.h"
 #include "llvm/DebugInfo/CodeView/TypeIndex.h"
 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/Support/ErrorExtras.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MD5.h"
 #include "llvm/Support/ScopedPrinter.h"
@@ -410,20 +411,47 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ArrayRecord &Record) {
 }
 
 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) {
-  assert((CVR.kind() == TypeLeafKind::LF_STRUCTURE) ||
-         (CVR.kind() == TypeLeafKind::LF_CLASS) ||
-         (CVR.kind() == TypeLeafKind::LF_INTERFACE));
-
   std::string PropertiesNames = getFlagNames(
       IO, static_cast<uint16_t>(Record.Options), getClassOptionNames());
-  error(IO.mapInteger(Record.MemberCount, "MemberCount"));
-  error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames));
-  error(IO.mapInteger(Record.FieldList, "FieldList"));
-  error(IO.mapInteger(Record.DerivationList, "DerivedFrom"));
-  error(IO.mapInteger(Record.VTableShape, "VShape"));
-  error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
-  error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
-                             Record.hasUniqueName()));
+
+  // The original and *2 versions are handled in the same high level record as
+  // they're almost identical except for the widths of members. The *2 versions
+  // store the properties as 32 bits and the member count as an encoded integer.
+  TypeLeafKind Kind = CVR.kind();
+  if (Kind == TypeLeafKind::LF_STRUCTURE2 || Kind == TypeLeafKind::LF_CLASS2 ||
+      Kind == TypeLeafKind::LF_INTERFACE2) {
+    uint32_t Options = llvm::to_underlying(Record.Options);
+    error(IO.mapInteger(Options, "Properties" + PropertiesNames));
+    if (IO.isReading())
+      Record.Options = static_cast<ClassOptions>(Options);
+
+    error(IO.mapInteger(Record.FieldList, "FieldList"));
+    error(IO.mapInteger(Record.DerivationList, "DerivedFrom"));
+    error(IO.mapInteger(Record.VTableShape, "VShape"));
+    error(IO.mapEncodedInteger(Record.MemberCount, "MemberCount"));
+    error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
+    error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
+                               Record.hasUniqueName()));
+  } else {
+    assert(Kind == TypeLeafKind::LF_STRUCTURE ||
+           Kind == TypeLeafKind::LF_CLASS ||
+           Kind == TypeLeafKind::LF_INTERFACE);
+
+    // This is not an error to avoid failing to write all type records.
+    assert(
+        (IO.isReading() ||
+         Record.MemberCount <= std::numeric_limits<uint16_t>::max()) &&
+        "use the *2 versions of this record if there are 2^16 or more members");
+
+    error(IO.mapTruncInteger<uint16_t>(Record.MemberCount, "MemberCount"));
+    error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames));
+    error(IO.mapInteger(Record.FieldList, "FieldList"));
+    error(IO.mapInteger(Record.DerivationList, "DerivedFrom"));
+    error(IO.mapInteger(Record.VTableShape, "VShape"));
+    error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
+    error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
+                               Record.hasUniqueName()));
+  }
 
   return Error::success();
 }
@@ -431,12 +459,33 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ClassRecord &Record) {
 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) {
   std::string PropertiesNames = getFlagNames(
       IO, static_cast<uint16_t>(Record.Options), getClassOptionNames());
-  error(IO.mapInteger(Record.MemberCount, "MemberCount"));
-  error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames));
-  error(IO.mapInteger(Record.FieldList, "FieldList"));
-  error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
-  error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
-                             Record.hasUniqueName()));
+
+  if (CVR.kind() == LF_UNION2) {
+    uint32_t Options = llvm::to_underlying(Record.Options);
+    error(IO.mapInteger(Options, "Properties" + PropertiesNames));
+    if (IO.isReading())
+      Record.Options = static_cast<ClassOptions>(Options);
+
+    error(IO.mapInteger(Record.FieldList, "FieldList"));
+    error(IO.mapEncodedInteger(Record.MemberCount, "MemberCount"));
+    error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
+    error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
+                               Record.hasUniqueName()));
+  } else {
+    // This is not an error to avoid failing to write all type records.
+    assert(
+        (IO.isReading() ||
+         Record.MemberCount <= std::numeric_limits<uint16_t>::max()) &&
+        "use the *2 versions of this record if there are 2^16 or more members");
+
+    error(IO.mapTruncInteger<uint16_t>(Record.MemberCount, "MemberCount"));
+
+    error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames));
+    error(IO.mapInteger(Record.FieldList, "FieldList"));
+    error(IO.mapEncodedInteger(Record.Size, "SizeOf"));
+    error(mapNameAndUniqueName(IO, Record.Name, Record.UniqueName,
+                               Record.hasUniqueName()));
+  }
 
   return Error::success();
 }
@@ -444,7 +493,7 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, UnionRecord &Record) {
 Error TypeRecordMapping::visitKnownRecord(CVType &CVR, EnumRecord &Record) {
   std::string PropertiesNames = getFlagNames(
       IO, static_cast<uint16_t>(Record.Options), getClassOptionNames());
-  error(IO.mapInteger(Record.MemberCount, "NumEnumerators"));
+  error(IO.mapTruncInteger<uint16_t>(Record.MemberCount, "NumEnumerators"));
   error(IO.mapEnum(Record.Options, "Properties" + PropertiesNames));
   error(IO.mapInteger(Record.UnderlyingType, "UnderlyingType"));
   error(IO.mapInteger(Record.FieldList, "FieldListType"));
diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
index 77e90db429478..d1500ed1e248d 100644
--- a/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVCodeViewVisitor.cpp
@@ -86,9 +86,10 @@ static StringRef getRecordName(LazyRandomTypeCollection &Types, TypeIndex TI) {
   };
 
   TypeRecordKind RK = static_cast<TypeRecordKind>(CVReference.kind());
-  if (RK == TypeRecordKind::Class || RK == TypeRecordKind::Struct)
+  if (RK == TypeRecordKind::Class || RK == TypeRecordKind::Class2 ||
+      RK == TypeRecordKind::Struct || RK == TypeRecordKind::Struct2)
     GetName(ClassRecord(RK));
-  else if (RK == TypeRecordKind::Union)
+  else if (RK == TypeRecordKind::Union || RK == TypeRecordKind::Union2)
     GetName(UnionRecord(RK));
   else if (RK == TypeRecordKind::Enum)
     GetName(EnumRecord(RK));
@@ -3073,6 +3074,7 @@ LVElement *LVLogicalVisitor::createElement(TypeLeafKind Kind) {
     CurrentScope->setTag(dwarf::DW_TAG_array_type);
     return CurrentScope;
   case TypeLeafKind::LF_CLASS:
+  case TypeLeafKind::LF_CLASS2:
     CurrentScope = Reader->createScopeAggregate();
     CurrentScope->setTag(dwarf::DW_TAG_class_type);
     CurrentScope->setIsClass();
@@ -3089,11 +3091,13 @@ LVElement *LVLogicalVisitor::createElement(TypeLeafKind Kind) {
     CurrentScope->setTag(dwarf::DW_TAG_subprogram);
     return CurrentScope;
   case TypeLeafKind::LF_STRUCTURE:
+  case TypeLeafKind::LF_STRUCTURE2:
     CurrentScope = Reader->createScopeAggregate();
     CurrentScope->setIsStructure();
     CurrentScope->setTag(dwarf::DW_TAG_structure_type);
     return CurrentScope;
   case TypeLeafKind::LF_UNION:
+  case TypeLeafKind::LF_UNION2:
     CurrentScope = Reader->createScopeAggregate();
     CurrentScope->setIsUnion();
     CurrentScope->setTag(dwarf::DW_TAG_union_type);
diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp
index 8571ca632f52b..01eed4ad5f405 100644
--- a/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/NativeExeSymbol.cpp
@@ -46,9 +46,16 @@ NativeExeSymbol::findChildren(PDB_SymType Type) const {
   case PDB_SymType::PointerType:
     return Session.getSymbolCache().createTypeEnumerator(codeview::LF_POINTER);
   case PDB_SymType::UDT:
-    return Session.getSymbolCache().createTypeEnumerator(
-        {codeview::LF_STRUCTURE, codeview::LF_CLASS, codeview::LF_UNION,
-         codeview::LF_INTERFACE});
+    return Session.getSymbolCache().createTypeEnumerator({
+        codeview::LF_STRUCTURE,
+        codeview::LF_STRUCTURE2,
+        codeview::LF_CLASS,
+        codeview::LF_CLASS2,
+        codeview::LF_UNION,
+        codeview::LF_UNION2,
+        codeview::LF_INTERFACE,
+        codeview::LF_INTERFACE2,
+    });
   case PDB_SymType::VTableShape:
     return Session.getSymbolCache().createTypeEnumerator(codeview::LF_VTSHAPE);
   case PDB_SymType::FunctionSig:
diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp
index c69cca166f041..2b02ea703915a 100644
--- a/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/NativeTypeUDT.cpp
@@ -112,12 +112,16 @@ PDB_UdtType NativeTypeUDT::getUdtKind() const {
 
   switch (Tag->Kind) {
   case TypeRecordKind::Class:
+  case TypeRecordKind::Class2:
     return PDB_UdtType::Class;
   case TypeRecordKind::Union:
+  case TypeRecordKind::Union2:
     return PDB_UdtType::Union;
   case TypeRecordKind::Struct:
+  case TypeRecordKind::Struct2:
     return PDB_UdtType::Struct;
   case TypeRecordKind::Interface:
+  case TypeRecordKind::Interface2:
     return PDB_UdtType::Interface;
   default:
     llvm_unreachable("Unexpected udt kind");
diff --git a/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
index 4cb4472354267..7946a9a75b4b5 100644
--- a/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/SymbolCache.cpp
@@ -206,11 +206,15 @@ SymIndexId SymbolCache::findSymbolByTypeIndex(codeview::TypeIndex Index) const {
                                                            std::move(CVT));
     break;
   case codeview::LF_CLASS:
+  case codeview::LF_CLASS2:
   case codeview::LF_STRUCTURE:
+  case codeview::LF_STRUCTURE2:
   case codeview::LF_INTERFACE:
+  case codeview::LF_INTERFACE2:
     Id = createSymbolForType<NativeTypeUDT, ClassRecord>(Index, std::move(CVT));
     break;
   case codeview::LF_UNION:
+  case codeview::LF_UNION2:
     Id = createSymbolForType<NativeTypeUDT, UnionRecord>(Index, std::move(CVT));
     break;
   case codeview::LF_POINTER:
diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp
index 941ce78027a21..dbbda0c16ca77 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiHashing.cpp
@@ -88,10 +88,14 @@ static Expected<uint32_t> getSourceLineHash(const CVType &Rec) {
 Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) {
   switch (Type.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_INTERFACE:
+  case LF_INTERFACE2:
     return getTagRecordHashForUdt<ClassRecord>(Type);
   case LF_UNION:
+  case LF_UNION2:
     return getTagRecordHashForUdt<UnionRecord>(Type);
   case LF_ENUM:
     return getTagRecordHashForUdt<EnumRecord>(Type);
@@ -105,10 +109,14 @@ Expected<TagRecordHash> llvm::pdb::hashTagRecord(const codeview::CVType &Type) {
 Expected<uint32_t> llvm::pdb::hashTypeRecord(const CVType &Rec) {
   switch (Rec.kind()) {
   case LF_CLASS:
+  case LF_CLASS2:
   case LF_STRUCTURE:
+  case LF_STRUCTURE2:
   case LF_INTERFACE:
+  case LF_INTERFACE2:
     return getHashForUdt<ClassRecord>(Rec);
   case LF_UNION:
+  case LF_UNION2:
     return getHashForUdt<UnionRecord>(Rec);
   case LF_ENUM:
     return getHashForUdt<EnumRecord>(Rec);
diff --git a/llvm/test/DebugInfo/COFF/tag-types2.ll b/llvm/test/DebugInfo/COFF/tag-types2.ll
new file mode 100644
index 0000000000000..f7ebe62c48b26
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/tag-types2.ll
@@ -0,0 +1,198 @@
+; RUN: llc < %s -filetype=obj | llvm-readobj - --codeview | FileCheck --check-prefixes=ONE,BOTH %s
+; RUN: llc < %s -filetype=obj -use-codeview-tagrecord2 | llvm-readobj - --codeview | FileCheck --check-prefixes=TWO,BOTH %s
+
+; BOTH: CodeViewTypes [
+; BOTH:   Section: .debug$T (5)
+; ONE:   Union (0x1000) {
+; TWO:   Union2 (0x1000) {
+; ONE-NEXT:      TypeLeafKind: LF_UNION (0x1506)
+; TWO-NEXT:      TypeLeafKind: LF_UNION2 (0x160A)
+; BOTH-NEXT:     MemberCount: 0
+; BOTH-NEXT:     Properties [ (0x280)
+; BOTH-NEXT:       ForwardReference (0x80)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     FieldList: 0x0
+; BOTH-NEXT:     SizeOf: 0
+; BOTH-NEXT:     Name: AUnion
+; BOTH-NEXT:     LinkageName: .?ATAUnion@@
+; BOTH-NEXT:   }
+; BOTH:   FieldList (0x1001) {
+; BOTH:     TypeLeafKind: LF_FIELDLIST (0x1203)
+; BOTH:   }
+; BOTH:   Enum (0x1002) {
+; BOTH-NEXT:     TypeLeafKind: LF_ENUM (0x1507)
+; BOTH-NEXT:     NumEnumerators: 3
+; BOTH-NEXT:     Properties [ (0x200)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     UnderlyingType: int (0x74)
+; BOTH-NEXT:     FieldListType: <field list> (0x1001)
+; BOTH-NEXT:     Name: AEnum
+; BOTH-NEXT:     LinkageName: .?AW4AEnum@@
+; BOTH-NEXT:   }
+; BOTH:   StringId (0x1003) {
+; BOTH:   }
+; BOTH:   UdtSourceLine (0x1004) {
+; BOTH:   }
+; BOTH:   Pointer (0x1005) {
+; BOTH:     PointeeType: AUnion (0x1000)
+; BOTH:   }
+; ONE:   Class (0x1006) {
+; TWO:   Class2 (0x1006) {
+; ONE-NEXT:      TypeLeafKind: LF_CLASS (0x1504)
+; TWO-NEXT:      TypeLeafKind: LF_CLASS2 (0x1608)
+; BOTH-NEXT:     MemberCount: 0
+; BOTH-NEXT:     Properties [ (0x280)
+; BOTH-NEXT:       ForwardReference (0x80)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     FieldList: 0x0
+; BOTH-NEXT:     DerivedFrom: 0x0
+; BOTH-NEXT:     VShape: 0x0
+; BOTH-NEXT:     SizeOf: 0
+; BOTH-NEXT:     Name: AClass
+; BOTH-NEXT:     LinkageName: .?AVAClass@@
+; BOTH-NEXT:   }
+; ONE:   Struct (0x1007) {
+; TWO:   Struct2 (0x1007) {
+; ONE-NEXT:      TypeLeafKind: LF_STRUCTURE (0x1505)
+; TWO-NEXT:      TypeLeafKind: LF_STRUCTURE2 (0x1609)
+; BOTH-NEXT:     MemberCount: 0
+; BOTH-NEXT:     Properties [ (0x280)
+; BOTH-NEXT:       ForwardReference (0x80)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     FieldList: 0x0
+; BOTH-NEXT:     DerivedFrom: 0x0
+; BOTH-NEXT:     VShape: 0x0
+; BOTH-NEXT:     SizeOf: 0
+; BOTH-NEXT:     Name: AStruct
+; BOTH-NEXT:     LinkageName: .?AUAStruct@@
+; BOTH-NEXT:   }
+; BOTH:   FieldList (0x1008) {
+; BOTH:     TypeLeafKind: LF_FIELDLIST (0x1203)
+; BOTH:   }
+; ONE:   Union (0x1009) {
+; TWO:   Union2 (0x1009) {
+; ONE-NEXT:      TypeLeafKind: LF_UNION (0x1506)
+; TWO-NEXT:      TypeLeafKind: LF_UNION2 (0x160A)
+; BOTH-NEXT:     MemberCount: 4
+; BOTH-NEXT:     Properties [ (0x600)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:       Sealed (0x400)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     FieldList: <field list> (0x1008)
+; BOTH-NEXT:     SizeOf: 8
+; BOTH-NEXT:     Name: AUnion
+; BOTH-NEXT:     LinkageName: .?ATAUnion@@
+; BOTH-NEXT:   }
+; BOTH:   UdtSourceLine (0x100A) {
+; BOTH:   }
+; BOTH:   FieldList (0x100B) {
+; BOTH:     TypeLeafKind: LF_FIELDLIST (0x1203)
+; BOTH:   }
+; ONE:   Class (0x100C) {
+; TWO:   Class2 (0x100C) {
+; ONE-NEXT:      TypeLeafKind: LF_CLASS (0x1504)
+; TWO-NEXT:      TypeLeafKind: LF_CLASS2 (0x1608)
+; BOTH-NEXT:     MemberCount: 1
+; BOTH-NEXT:     Properties [ (0x200)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     FieldList: <field list> (0x100B)
+; BOTH-NEXT:     DerivedFrom: 0x0
+; BOTH-NEXT:     VShape: 0x0
+; BOTH-NEXT:     SizeOf: 8
+; BOTH-NEXT:     Name: AClass
+; BOTH-NEXT:     LinkageName: .?AVAClass@@
+; BOTH-NEXT:   }
+; BOTH:   UdtSourceLine (0x100D) {
+; BOTH:   }
+; BOTH:   Pointer (0x100E) {
+; BOTH:     PointeeType: AClass (0x1006)
+; BOTH:   }
+; BOTH:   FieldList (0x100F) {
+; BOTH:     TypeLeafKind: LF_FIELDLIST (0x1203)
+; BOTH:   }
+; ONE:   Struct (0x1010) {
+; TWO:   Struct2 (0x1010) {
+; ONE-NEXT:      TypeLeafKind: LF_STRUCTURE (0x1505)
+; TWO-NEXT:      TypeLeafKind: LF_STRUCTURE2 (0x1609)
+; BOTH-NEXT:     MemberCount: 1
+; BOTH-NEXT:     Properties [ (0x200)
+; BOTH-NEXT:       HasUniqueName (0x200)
+; BOTH-NEXT:     ]
+; BOTH-NEXT:     FieldList: <field list> (0x100F)
+; BOTH-NEXT:     DerivedFrom: 0x0
+; BOTH-NEXT:     VShape: 0x0
+; BOTH-NEXT:     SizeOf: 8
+; BOTH-NEXT:     Name: AStruct
+; BOTH-NEXT:     LinkageName: .?AUAStruct@@
+; BOTH-NEXT:   }
+; BOTH: ]
+
+; BOTH: Subsection [
+; BOTH:   SubSectionType: Symbols (0xF1)
+; BOTH:   SubSectionSize: 0x30
+; BOTH:   UDTSym {
+; BOTH:     Kind: S_UDT (0x1108)
+; BOTH:     Type: AUnion (0x1009)
+; BOTH:     UDTName: AUnion
+; BOTH:   }
+; BOTH:   UDTSym {
+; BOTH:     Kind: S_UDT (0x1108)
+; BOTH:     Type: AClass (0x100C)
+; BOTH:     UDTName: AClass
+; BOTH:   }
+; BOTH:   UDTSym {
+; BOTH:     Kind: S_UDT (0x1108)
+; BOTH:     Type: AStruct (0x1010)
+; BOTH:     UDTName: AStruct
+; BOTH:   }
+; BOTH: ]
+
+source_filename = "tag-types.cpp"
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.51.36260"
+
+%union.AUnion = type { ptr }
+
+@"?u@@3TAUnion@@A" = dso_local global %union.AUnion zeroinitializer, align 8, !dbg !0
+
+!llvm.dbg.cu = !{!2}
+!llvm.module.flags = !{!26, !27, !28, !29, !30}
+!llvm.ident = !{!31}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "u", linkageName: "?u@@3TAUnion@@A", scope: !2, file: !3, line: 17, type: !12, isLocal: false, isDefinition: true)
+!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !11, splitDebugInlining: false, nameTableKind: None)
+!3 = !DIFile(filename: "tag-types.cpp", directory: "/tmp")
+!4 = !{!5}
+!5 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "AEnum", file: !3, line: 9, baseType: !6, size: 32, elements: !7, identifier: ".?AW4AEnum@@")
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !{!8, !9, !10}
+!8 = !DIEnumerator(name: "Foo", value: 0)
+!9 = !DIEnumerator(name: "Bar", value: 1)
+!10 = !DIEnumerator(name: "Baz", value: 2)
+!11 = !{!0}
+!12 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "AUnion", file: !3, line: 10, size: 64, flags: DIFlagTypePassByValue, elements: !13, identifier: ".?ATAUnion@@")
+!13 = !{!14, !15, !17, !21}
+!14 = !DIDerivedType(tag: DW_TAG_member, name: "e", scope: !12, file: !3, line: 11, baseType: !5, size: 32)
+!15 = !DIDerivedType(tag: DW_TAG_member, name: "u", scope: !12, file: !3, line: 12, baseType: !16, size: 64)
+!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64)
+!17 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !12, file: !3, line: 13, baseType: !18, size: 64)
+!18 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "AClass", file: !3, line: 6, size: 64, flags: DIFlagTypePassByValue, elements: !19, identifier: ".?AVAClass@@")
+!19 = !{!20}
+!20 = !DIDerivedType(tag: DW_TAG_member, name: "u", scope: !18, file: !3, line: 7, baseType: !16, size: 64)
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: !12, file: !3, line: 14, baseType: !22, size: 64)
+!22 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "AStruct", file: !3, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !23, identifier: ".?AUAStruct@@")
+!23 = !{!24}
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "clazz", scope: !22, file: !3, line: 3, baseType: !25, size: 64)
+!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64)
+!26 = !{i32 2, !"CodeView", i32 1}
+!27 = !{i32 2, !"Debug Info Version", i32 3}
+!28 = !{i32 8, !"PIC Level", i32 2}
+!29 = !{i32 7, !"uwtable", i32 2}
+!30 = !{i32 1, !"MaxTLSAlign", i32 65536}
+!31 = !{!"clang"}
diff --git a/llvm/test/DebugInfo/PDB/DIA/tag-types2.test b/llvm/test/DebugInfo/PDB/DIA/tag-types2.test
new file mode 100644
index 0000000000000..284304c07b16e
--- /dev/null
+++ b/llvm/test/DebugInfo/PDB/DIA/tag-types2.test
@@ -0,0 +1,71 @@
+; RUN: llvm-pdbutil yaml2pdb %p/../Inputs/tag-types2.yaml -pdb=%t.pdb
+; RUN: llvm-pdbutil diadump -udts %t.pdb | FileCheck %s
+
+; CHECK: {
+; CHECK:   symIndexId: 1
+; CHECK:   symTag: UDT
+; CHECK:   name: AUnion
+; CHECK:   length: 8
+; CHECK:   udtKind: union
+; CHECK:   constructor: 0
+; CHECK:   constType: 0
+; CHECK:   hasAssignmentOperator: 0
+; CHECK:   hasCastOperator: 0
+; CHECK:   hasNestedTypes: 0
+; CHECK:   overloadedOperator: 0
+; CHECK:   isInterfaceUdt: 0
+; CHECK:   intrinsic: 0
+; CHECK:   nested: 0
+; CHECK:   packed: 0
+; CHECK:   isRefUdt: 0
+; CHECK:   scoped: 0
+; CHECK:   unalignedType: 0
+; CHECK:   isValueUdt: 0
+; CHECK:   volatileType: 0
+; CHECK: }
+; CHECK: {
+; CHECK:   symIndexId: 3
+; CHECK:   symTag: UDT
+; CHECK:   name: AClass
+; CHECK:   virtualTableShapeId: 4
+; CHECK:   length: 8
+; CHECK:   udtKind: class
+; CHECK:   constructor: 0
+; CHECK:   constType: 0
+; CHECK:   hasAssignmentOperator: 0
+; CHECK:   hasCastOperator: 0
+; CHECK:   hasNestedTypes: 0
+; CHECK:   overloadedOperator: 0
+; CHECK:   isInterfaceUdt: 0
+; CHECK:   intrinsic: 0
+; CHECK:   nested: 0
+; CHECK:   packed: 0
+; CHECK:   isRefUdt: 0
+; CHECK:   scoped: 0
+; CHECK:   unalignedType: 0
+; CHECK:   isValueUdt: 0
+; CHECK:   volatileType: 0
+; CHECK: }
+; CHECK: {
+; CHECK:   symIndexId: 5
+; CHECK:   symTag: UDT
+; CHECK:   name: AStruct
+; CHECK:   virtualTableShapeId: 4
+; CHECK:   length: 8
+; CHECK:   udtKind: struct
+; CHECK:   constructor: 0
+; CHECK:   constType: 0
+; CHECK:   hasAssignmentOperator: 0
+; CHECK:   hasCastOperator: 0
+; CHECK:   hasNestedTypes: 0
+; CHECK:   overloadedOperator: 0
+; CHECK:   isInterfaceUdt: 0
+; CHECK:   intrinsic: 0
+; CHECK:   nested: 0
+; CHECK:   packed: 0
+; CHECK:   isRefUdt: 0
+; CHECK:   scoped: 0
+; CHECK:   unalignedType: 0
+; CHECK:   isValueUdt: 0
+; CHECK:   volatileType: 0
+; CHECK: }
diff --git a/llvm/test/DebugInfo/PDB/Inputs/tag-types2.yaml b/llvm/test/DebugInfo/PDB/Inputs/tag-types2.yaml
new file mode 100644
index 0000000000000..e0ec0a504448c
--- /dev/null
+++ b/llvm/test/DebugInfo/PDB/Inputs/tag-types2.yaml
@@ -0,0 +1,146 @@
+---
+TpiStream:
+  Version:         VC80
+  Records:
+    - Kind:            LF_ARGLIST
+      ArgList:
+        ArgIndices:      [  ]
+    - Kind:            LF_PROCEDURE
+      Procedure:
+        ReturnType:      116
+        CallConv:        NearC
+        Options:         [ None ]
+        ParameterCount:  0
+        ArgumentList:    4096
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_ENUMERATE
+          Enumerator:
+            Attrs:           3
+            Value:           0
+            Name:            Foo
+        - Kind:            LF_ENUMERATE
+          Enumerator:
+            Attrs:           3
+            Value:           1
+            Name:            Bar
+        - Kind:            LF_ENUMERATE
+          Enumerator:
+            Attrs:           3
+            Value:           2
+            Name:            Baz
+    - Kind:            LF_ENUM
+      Enum:
+        NumEnumerators:  3
+        Options:         [ None, HasUniqueName ]
+        FieldList:       4098
+        Name:            AEnum
+        UniqueName:      '.?AW4AEnum@@'
+        UnderlyingType:  116
+    - Kind:            LF_UNION2
+      Union:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            AUnion
+        UniqueName:      '.?ATAUnion@@'
+        Size:            0
+    - Kind:            LF_POINTER
+      Pointer:
+        ReferentType:    4100
+        Attrs:           65548
+    - Kind:            LF_CLASS2
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            AClass
+        UniqueName:      '.?AVAClass@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_STRUCTURE2
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            AStruct
+        UniqueName:      '.?AUAStruct@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            4099
+            FieldOffset:     0
+            Name:            e
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            4101
+            FieldOffset:     0
+            Name:            u
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            4102
+            FieldOffset:     0
+            Name:            c
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            4103
+            FieldOffset:     0
+            Name:            s
+    - Kind:            LF_UNION2
+      Union:
+        MemberCount:     4
+        Options:         [ None, HasUniqueName, Sealed ]
+        FieldList:       4104
+        Name:            AUnion
+        UniqueName:      '.?ATAUnion@@'
+        Size:            8
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           1
+            Type:            4101
+            FieldOffset:     0
+            Name:            u
+    - Kind:            LF_CLASS2
+      Class:
+        MemberCount:     1
+        Options:         [ None, HasUniqueName ]
+        FieldList:       4106
+        Name:            AClass
+        UniqueName:      '.?AVAClass@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            8
+    - Kind:            LF_POINTER
+      Pointer:
+        ReferentType:    4102
+        Attrs:           65548
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            4108
+            FieldOffset:     0
+            Name:            clazz
+    - Kind:            LF_STRUCTURE2
+      Class:
+        MemberCount:     1
+        Options:         [ None, HasUniqueName ]
+        FieldList:       4109
+        Name:            AStruct
+        UniqueName:      '.?AUAStruct@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            8
+...
diff --git a/llvm/test/DebugInfo/PDB/Native/tag-types2.test b/llvm/test/DebugInfo/PDB/Native/tag-types2.test
new file mode 100644
index 0000000000000..c254739c68450
--- /dev/null
+++ b/llvm/test/DebugInfo/PDB/Native/tag-types2.test
@@ -0,0 +1,73 @@
+; Test that we can report LF_CLASS2 and friends as UDTs.
+; The only difference to the DIA/ test is in the symbol indexes.
+; RUN: llvm-pdbutil yaml2pdb %p/../Inputs/tag-types2.yaml -pdb=%t.pdb
+; RUN: llvm-pdbutil diadump -native -udts %t.pdb | FileCheck %s
+
+; CHECK: {
+; CHECK:   symIndexId: 2
+; CHECK:   symTag: UDT
+; CHECK:   name: AUnion
+; CHECK:   length: 8
+; CHECK:   udtKind: union
+; CHECK:   constructor: 0
+; CHECK:   constType: 0
+; CHECK:   hasAssignmentOperator: 0
+; CHECK:   hasCastOperator: 0
+; CHECK:   hasNestedTypes: 0
+; CHECK:   overloadedOperator: 0
+; CHECK:   isInterfaceUdt: 0
+; CHECK:   intrinsic: 0
+; CHECK:   nested: 0
+; CHECK:   packed: 0
+; CHECK:   isRefUdt: 0
+; CHECK:   scoped: 0
+; CHECK:   unalignedType: 0
+; CHECK:   isValueUdt: 0
+; CHECK:   volatileType: 0
+; CHECK: }
+; CHECK: {
+; CHECK:   symIndexId: 3
+; CHECK:   symTag: UDT
+; CHECK:   name: AClass
+; CHECK:   virtualTableShapeId: 4
+; CHECK:   length: 8
+; CHECK:   udtKind: class
+; CHECK:   constructor: 0
+; CHECK:   constType: 0
+; CHECK:   hasAssignmentOperator: 0
+; CHECK:   hasCastOperator: 0
+; CHECK:   hasNestedTypes: 0
+; CHECK:   overloadedOperator: 0
+; CHECK:   isInterfaceUdt: 0
+; CHECK:   intrinsic: 0
+; CHECK:   nested: 0
+; CHECK:   packed: 0
+; CHECK:   isRefUdt: 0
+; CHECK:   scoped: 0
+; CHECK:   unalignedType: 0
+; CHECK:   isValueUdt: 0
+; CHECK:   volatileType: 0
+; CHECK: }
+; CHECK: {
+; CHECK:   symIndexId: 5
+; CHECK:   symTag: UDT
+; CHECK:   name: AStruct
+; CHECK:   virtualTableShapeId: 4
+; CHECK:   length: 8
+; CHECK:   udtKind: struct
+; CHECK:   constructor: 0
+; CHECK:   constType: 0
+; CHECK:   hasAssignmentOperator: 0
+; CHECK:   hasCastOperator: 0
+; CHECK:   hasNestedTypes: 0
+; CHECK:   overloadedOperator: 0
+; CHECK:   isInterfaceUdt: 0
+; CHECK:   intrinsic: 0
+; CHECK:   nested: 0
+; CHECK:   packed: 0
+; CHECK:   isRefUdt: 0
+; CHECK:   scoped: 0
+; CHECK:   unalignedType: 0
+; CHECK:   isValueUdt: 0
+; CHECK:   volatileType: 0
+; CHECK: }
diff --git a/llvm/test/tools/llvm-pdbutil/tag-types2.yaml b/llvm/test/tools/llvm-pdbutil/tag-types2.yaml
new file mode 100644
index 0000000000000..b12699c5970bf
--- /dev/null
+++ b/llvm/test/tools/llvm-pdbutil/tag-types2.yaml
@@ -0,0 +1,148 @@
+# RUN: llvm-pdbutil yaml2pdb %s --pdb=%t.pdb
+# RUN: llvm-pdbutil dump --types --type-extras %t.pdb | FileCheck --check-prefix=YAML2PDB %s
+
+# RUN: llvm-pdbutil pdb2yaml --tpi-stream %t.pdb > %t.yaml
+# RUN: FileCheck --input-file=%t.yaml --check-prefix=PDB2YAML %s
+
+# YAML2PDB:        0x1000 | LF_UNION2 [size = 36, hash = 0x211D0] `AUnion`
+# YAML2PDB-NEXT:            unique name: `.?ATAUnion@@`
+# YAML2PDB-NEXT:            field list: <no type>
+# YAML2PDB-NEXT:            options: forward ref (-> 0x1003) | has unique name, sizeof 0
+# YAML2PDB-NEXT:   0x1001 | LF_CLASS2 [size = 44, hash = 0x252B1] `AClass`
+# YAML2PDB-NEXT:            unique name: `.?AVAClass@@`
+# YAML2PDB-NEXT:            vtable: <no type>, base list: <no type>, field list: <no type>
+# YAML2PDB-NEXT:            options: forward ref (-> 0x1004) | has unique name, sizeof 0
+# YAML2PDB-NEXT:   0x1002 | LF_STRUCTURE2 [size = 48, hash = 0x301BC] `AStruct`
+# YAML2PDB-NEXT:            unique name: `.?AUAStruct@@`
+# YAML2PDB-NEXT:            vtable: <no type>, base list: <no type>, field list: <no type>
+# YAML2PDB-NEXT:            options: forward ref (-> 0x1005) | has unique name, sizeof 0
+# YAML2PDB-NEXT:   0x1003 | LF_UNION2 [size = 36, hash = 0x399E2] `AUnion`
+# YAML2PDB-NEXT:            unique name: `.?ATAUnion@@`
+# YAML2PDB-NEXT:            field list: 0x1008
+# YAML2PDB-NEXT:            options: has unique name | sealed, sizeof 8
+# YAML2PDB-NEXT:   0x1004 | LF_CLASS2 [size = 44, hash = 0x952C] `AClass`
+# YAML2PDB-NEXT:            unique name: `.?AVAClass@@`
+# YAML2PDB-NEXT:            vtable: <no type>, base list: <no type>, field list: 0x100A
+# YAML2PDB-NEXT:            options: has unique name, sizeof 8
+# YAML2PDB-NEXT:   0x1005 | LF_STRUCTURE2 [size = 48, hash = 0x2293A] `AStruct`
+# YAML2PDB-NEXT:            unique name: `.?AUAStruct@@`
+# YAML2PDB-NEXT:            vtable: <no type>, base list: <no type>, field list: 0x100D
+# YAML2PDB-NEXT:            options: has unique name, sizeof 8
+
+# PDB2YAML:       Records:
+# PDB2YAML-NEXT:     - Kind:            LF_UNION2
+# PDB2YAML-NEXT:       Union:
+# PDB2YAML-NEXT:         MemberCount:     0
+# PDB2YAML-NEXT:         Options:         [ None, ForwardReference, HasUniqueName ]
+# PDB2YAML-NEXT:         FieldList:       0
+# PDB2YAML-NEXT:         Name:            AUnion
+# PDB2YAML-NEXT:         UniqueName:      '.?ATAUnion@@'
+# PDB2YAML-NEXT:         Size:            0
+# PDB2YAML-NEXT:     - Kind:            LF_CLASS2
+# PDB2YAML-NEXT:       Class:
+# PDB2YAML-NEXT:         MemberCount:     0
+# PDB2YAML-NEXT:         Options:         [ None, ForwardReference, HasUniqueName ]
+# PDB2YAML-NEXT:         FieldList:       0
+# PDB2YAML-NEXT:         Name:            AClass
+# PDB2YAML-NEXT:         UniqueName:      '.?AVAClass@@'
+# PDB2YAML-NEXT:         DerivationList:  0
+# PDB2YAML-NEXT:         VTableShape:     0
+# PDB2YAML-NEXT:         Size:            0
+# PDB2YAML-NEXT:     - Kind:            LF_STRUCTURE2
+# PDB2YAML-NEXT:       Class:
+# PDB2YAML-NEXT:         MemberCount:     0
+# PDB2YAML-NEXT:         Options:         [ None, ForwardReference, HasUniqueName ]
+# PDB2YAML-NEXT:         FieldList:       0
+# PDB2YAML-NEXT:         Name:            AStruct
+# PDB2YAML-NEXT:         UniqueName:      '.?AUAStruct@@'
+# PDB2YAML-NEXT:         DerivationList:  0
+# PDB2YAML-NEXT:         VTableShape:     0
+# PDB2YAML-NEXT:         Size:            0
+# PDB2YAML-NEXT:     - Kind:            LF_UNION2
+# PDB2YAML-NEXT:       Union:
+# PDB2YAML-NEXT:         MemberCount:     4
+# PDB2YAML-NEXT:         Options:         [ None, HasUniqueName, Sealed ]
+# PDB2YAML-NEXT:         FieldList:       4104
+# PDB2YAML-NEXT:         Name:            AUnion
+# PDB2YAML-NEXT:         UniqueName:      '.?ATAUnion@@'
+# PDB2YAML-NEXT:         Size:            8
+# PDB2YAML-NEXT:     - Kind:            LF_CLASS2
+# PDB2YAML-NEXT:       Class:
+# PDB2YAML-NEXT:         MemberCount:     1
+# PDB2YAML-NEXT:         Options:         [ None, HasUniqueName ]
+# PDB2YAML-NEXT:         FieldList:       4106
+# PDB2YAML-NEXT:         Name:            AClass
+# PDB2YAML-NEXT:         UniqueName:      '.?AVAClass@@'
+# PDB2YAML-NEXT:         DerivationList:  0
+# PDB2YAML-NEXT:         VTableShape:     0
+# PDB2YAML-NEXT:         Size:            8
+# PDB2YAML-NEXT:     - Kind:            LF_STRUCTURE2
+# PDB2YAML-NEXT:       Class:
+# PDB2YAML-NEXT:         MemberCount:     1
+# PDB2YAML-NEXT:         Options:         [ None, HasUniqueName ]
+# PDB2YAML-NEXT:         FieldList:       4109
+# PDB2YAML-NEXT:         Name:            AStruct
+# PDB2YAML-NEXT:         UniqueName:      '.?AUAStruct@@'
+# PDB2YAML-NEXT:         DerivationList:  0
+# PDB2YAML-NEXT:         VTableShape:     0
+# PDB2YAML-NEXT:         Size:            8
+
+TpiStream:
+  Version:         VC80
+  Records:
+    - Kind:            LF_UNION2
+      Union:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            AUnion
+        UniqueName:      '.?ATAUnion@@'
+        Size:            0
+    - Kind:            LF_CLASS2
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            AClass
+        UniqueName:      '.?AVAClass@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_STRUCTURE2
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            AStruct
+        UniqueName:      '.?AUAStruct@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_UNION2
+      Union:
+        MemberCount:     4
+        Options:         [ None, HasUniqueName, Sealed ]
+        FieldList:       4104
+        Name:            AUnion
+        UniqueName:      '.?ATAUnion@@'
+        Size:            8
+    - Kind:            LF_CLASS2
+      Class:
+        MemberCount:     1
+        Options:         [ None, HasUniqueName ]
+        FieldList:       4106
+        Name:            AClass
+        UniqueName:      '.?AVAClass@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            8
+    - Kind:            LF_STRUCTURE2
+      Class:
+        MemberCount:     1
+        Options:         [ None, HasUniqueName ]
+        FieldList:       4109
+        Name:            AStruct
+        UniqueName:      '.?AUAStruct@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            8
diff --git a/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
index c7653b7c06d48..d302717b1723b 100644
--- a/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
+++ b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp
@@ -150,9 +150,13 @@ void TypeReferenceTracker::markReferencedTypes() {
       default:
         break;
       case LF_CLASS:
+      case LF_CLASS2:
       case LF_INTERFACE:
+      case LF_INTERFACE2:
       case LF_STRUCTURE:
+      case LF_STRUCTURE2:
       case LF_UNION:
+      case LF_UNION2:
       case LF_ENUM:
         addOneTypeRef(TiRefKind::TypeRef,
                       cantFail(Tpi->findFullDeclForForwardRef(RefTI)));
diff --git a/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp b/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp
index 810aeada33da7..3082a115d2cbf 100644
--- a/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp
+++ b/llvm/unittests/DebugInfo/CodeView/TypeIndexDiscoveryTest.cpp
@@ -613,3 +613,19 @@ TEST_F(TypeIndexIteratorTest, RegRelativeIndir) {
   writeSymbolRecords(RR);
   checkTypeReferences(0, RR.Type);
 }
+
+TEST_F(TypeIndexIteratorTest, Class2) {
+  ClassRecord CR(TypeRecordKind::Class2);
+  CR.FieldList = TypeIndex::Int32();
+  CR.DerivationList = TypeIndex::Float32();
+  CR.VTableShape = TypeIndex::UInt32();
+  writeTypeRecords(CR);
+  checkTypeReferences(0, CR.FieldList, CR.DerivationList, CR.VTableShape);
+}
+
+TEST_F(TypeIndexIteratorTest, Union2) {
+  UnionRecord UR(TypeRecordKind::Union2);
+  UR.FieldList = TypeIndex::Int32();
+  writeTypeRecords(UR);
+  checkTypeReferences(0, UR.FieldList);
+}



More information about the llvm-commits mailing list