https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/218546

>From 4682552960d1df367763fdf4b1be6a0575d2c82f Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Thu, 20 Aug 2026 14:05:14 -0700
Subject: [PATCH 1/4] [LLVMABI] Align RecordType::isEmpty() with Clang's
 isEmptyRecord()

While working on empty record handling for AArch64, I stumbled across a
couple of differences between the LLVM ABI library's `RecordType::isEmpty()`
and Clang's `CodeGen::isEmptyRecord()`. Testing verified this led to
observable differences in classification when using the ABI library.

This change updates the ABI library's empty record handling to match Clang.

Assisted-by: Cursor / various models
---
 clang/lib/CodeGen/QualTypeMapper.cpp          |  12 +-
 .../X86/x86_64-empty-cxx-member-abi.cpp       |  35 +++++
 llvm/include/llvm/ABI/Types.h                 |  16 +-
 llvm/lib/ABI/Types.cpp                        |  28 ++--
 llvm/unittests/ABI/CMakeLists.txt             |   1 +
 llvm/unittests/ABI/TypesTest.cpp              | 138 ++++++++++++++++++
 .../gn/secondary/llvm/unittests/ABI/BUILD.gn  |   5 +-
 7 files changed, 217 insertions(+), 18 deletions(-)
 create mode 100644 clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp
 create mode 100644 llvm/unittests/ABI/TypesTest.cpp

diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp 
b/clang/lib/CodeGen/QualTypeMapper.cpp
index c8be9de4769c8..7c08fd9968ace 100644
--- a/clang/lib/CodeGen/QualTypeMapper.cpp
+++ b/clang/lib/CodeGen/QualTypeMapper.cpp
@@ -393,7 +393,10 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl 
*RD) {
   if (RD->isPolymorphic()) {
     const llvm::abi::Type *VtablePointer =
         createPointerTypeForPointee(ASTCtx.VoidPtrTy);
-    Fields.emplace_back(VtablePointer, 0);
+    Fields.emplace_back(VtablePointer, 0, /*IsBitField=*/false,
+                        /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false,
+                        /*HasNoUniqueAddress=*/false,
+                        /*IsVTablePointer=*/true);
   }
 
   for (const auto &Base : RD->bases()) {
@@ -409,6 +412,8 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl 
*RD) {
         8;
     BaseClasses.emplace_back(BaseType, BaseOffset, /*IsBitField=*/false,
                              /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false,
+                             /*HasNoUniqueAddress=*/false,
+                             /*IsVTablePointer=*/false,
                              /*IsVirtualBase=*/Base.isVirtual());
   }
 
@@ -424,6 +429,8 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl 
*RD) {
                                     /*IsBitField=*/false,
                                     /*BitFieldWidth=*/0,
                                     /*IsUnnamedBitField=*/false,
+                                    /*HasNoUniqueAddress=*/false,
+                                    /*IsVTablePointer=*/false,
                                     /*IsVirtualBase=*/true);
   }
 
@@ -575,8 +582,9 @@ void QualTypeMapper::computeFieldInfo(
       IsUnnamedBitField = FD->isUnnamedBitField();
     }
 
+    bool HasNoUniqueAddress = FD->hasAttr<NoUniqueAddressAttr>();
     Fields.emplace_back(FieldType, OffsetInBits, IsBitField, BitFieldWidth,
-                        IsUnnamedBitField);
+                        IsUnnamedBitField, HasNoUniqueAddress);
     ++FieldIndex;
   }
 }
diff --git a/clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp 
b/clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp
new file mode 100644
index 0000000000000..7ce36fc620707
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_64-empty-cxx-member-abi.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s 
-o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm 
-fexperimental-abi-lowering %s -o - | FileCheck %s
+
+typedef float V4F __attribute__((vector_size(16)));
+
+struct Empty {};
+
+extern "C" {
+
+union VecAndEmpty {
+  V4F v;
+  Empty e;
+};
+
+void take_vec_and_empty(union VecAndEmpty u);
+void call_vec_and_empty(union VecAndEmpty u) { take_vec_and_empty(u); }
+// CHECK-DAG: declare void @take_vec_and_empty(<2 x double>)
+
+union VecAndEmpty ret_vec_and_empty(void);
+void call_ret_vec_and_empty(void) { ret_vec_and_empty(); }
+// CHECK-DAG: declare <2 x double> @ret_vec_and_empty()
+
+struct VecNoUniqueAddress {
+  V4F v;
+  [[no_unique_address]] Empty e;
+};
+
+void take_vec_nua(VecNoUniqueAddress s);
+void call_vec_nua(VecNoUniqueAddress s) { take_vec_nua(s); }
+// CHECK-DAG: declare void @take_vec_nua(<4 x float>)
+
+VecNoUniqueAddress ret_vec_nua(void);
+void call_ret_vec_nua(void) { ret_vec_nua(); }
+// CHECK-DAG: declare <4 x float> @ret_vec_nua()
+}
diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h
index 07c0480835794..2247cc59b3978 100644
--- a/llvm/include/llvm/ABI/Types.h
+++ b/llvm/include/llvm/ABI/Types.h
@@ -237,14 +237,19 @@ struct FieldInfo {
   uint64_t BitFieldWidth;
   bool IsBitField;
   bool IsUnnamedBitfield;
+  bool HasNoUniqueAddress;
+  bool IsVTablePointer;
   bool IsVirtualBase;
 
   FieldInfo(const Type *FieldType, uint64_t OffsetInBits = 0,
             bool IsBitField = false, uint64_t BitFieldWidth = 0,
-            bool IsUnnamedBitField = false, bool IsVirtualBase = false)
+            bool IsUnnamedBitField = false, bool HasNoUniqueAddress = false,
+            bool IsVTablePointer = false, bool IsVirtualBase = false)
       : FieldType(FieldType), OffsetInBits(OffsetInBits),
         BitFieldWidth(BitFieldWidth), IsBitField(IsBitField),
-        IsUnnamedBitfield(IsUnnamedBitField), IsVirtualBase(IsVirtualBase) {}
+        IsUnnamedBitfield(IsUnnamedBitField),
+        HasNoUniqueAddress(HasNoUniqueAddress),
+        IsVTablePointer(IsVTablePointer), IsVirtualBase(IsVirtualBase) {}
 
   LLVM_ABI bool isEmpty() const;
 };
@@ -419,10 +424,9 @@ class TypeBuilder {
     FieldInfo *FieldArray = Allocator.Allocate<FieldInfo>(Fields.size());
 
     for (size_t I = 0, E = Fields.size(); I != E; ++I) {
-      const FieldInfo &Field = Fields[I];
-      new (&FieldArray[I])
-          FieldInfo(Field.FieldType, 0, Field.IsBitField, Field.BitFieldWidth,
-                    Field.IsUnnamedBitfield);
+      FieldInfo Field = Fields[I];
+      Field.OffsetInBits = 0;
+      new (&FieldArray[I]) FieldInfo(Field);
     }
 
     ArrayRef<FieldInfo> FieldsRef(FieldArray, Fields.size());
diff --git a/llvm/lib/ABI/Types.cpp b/llvm/lib/ABI/Types.cpp
index 6c44e1f4d1e48..2e3ec907cf251 100644
--- a/llvm/lib/ABI/Types.cpp
+++ b/llvm/lib/ABI/Types.cpp
@@ -13,10 +13,10 @@ using namespace llvm;
 using namespace llvm::abi;
 
 bool RecordType::isEmpty() const {
-  if (hasFlexibleArrayMember() || isPolymorphic() ||
-      getNumVirtualBaseClasses() != 0)
+  if (hasFlexibleArrayMember())
     return false;
 
+  // Direct virtual bases are not included in the base class list.
   for (const FieldInfo &Base : getBaseClasses()) {
     const auto *BaseRT = dyn_cast<RecordType>(Base.FieldType);
     if (!BaseRT || !BaseRT->isEmpty())
@@ -24,6 +24,9 @@ bool RecordType::isEmpty() const {
   }
 
   for (const FieldInfo &FI : getFields()) {
+    // Don't treat vtable pointers as a source field for emptiness.
+    if (FI.IsVTablePointer)
+      continue;
     if (!FI.isEmpty())
       return false;
   }
@@ -67,18 +70,25 @@ RecordType::getElementContainingOffset(unsigned 
OffsetInBits) const {
 bool FieldInfo::isEmpty() const {
   if (IsUnnamedBitfield)
     return true;
-  if (IsBitField && BitFieldWidth == 0)
-    return true;
 
   const Type *Ty = FieldType;
+  bool WasArray = false;
   while (const auto *AT = dyn_cast<ArrayType>(Ty)) {
-    if (AT->getNumElements() != 1)
-      break;
+    // Constant arrays of zero length always count as empty.
+    if (AT->getNumElements() == 0)
+      return true;
     Ty = AT->getElementType();
+    WasArray = true;
   }
 
-  if (const auto *RT = dyn_cast<RecordType>(Ty))
-    return RT->isEmpty();
+  const auto *RT = dyn_cast<RecordType>(Ty);
+  if (!RT)
+    return false;
+
+  // C++ record fields are never empty unless [[no_unique_address]] applies.
+  // That exception does not apply to arrays of C++ empty records.
+  if (RT->isCXXRecord() && (WasArray || !HasNoUniqueAddress))
+    return false;
 
-  return Ty->isZeroSize();
+  return RT->isEmpty();
 }
diff --git a/llvm/unittests/ABI/CMakeLists.txt 
b/llvm/unittests/ABI/CMakeLists.txt
index a26da474bd83a..fdc665e5a2c98 100644
--- a/llvm/unittests/ABI/CMakeLists.txt
+++ b/llvm/unittests/ABI/CMakeLists.txt
@@ -7,4 +7,5 @@ set(LLVM_LINK_COMPONENTS
 add_llvm_unittest(ABITests
   AArch64TargetInfoTest.cpp
   X86TargetInfoTest.cpp
+  TypesTest.cpp
   )
diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp
new file mode 100644
index 0000000000000..ad448882b82b1
--- /dev/null
+++ b/llvm/unittests/ABI/TypesTest.cpp
@@ -0,0 +1,138 @@
+//===- TypesTest.cpp - ABI type emptiness unit tests 
----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ABI/Types.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/TypeSize.h"
+#include "gtest/gtest.h"
+
+using llvm::Align;
+using llvm::TypeSize;
+using llvm::abi::FieldInfo;
+using llvm::abi::RecordFlags;
+using llvm::abi::RecordType;
+using llvm::abi::StructPacking;
+using llvm::abi::TypeBuilder;
+
+namespace {
+
+class ABITypesTest : public ::testing::Test {
+protected:
+  llvm::BumpPtrAllocator Alloc;
+  TypeBuilder TB;
+
+  ABITypesTest() : TB(Alloc) {}
+
+  const RecordType *makeRecord(llvm::ArrayRef<FieldInfo> Fields,
+                               uint64_t SizeBits, RecordFlags Flags,
+                               llvm::ArrayRef<FieldInfo> Bases = {},
+                               llvm::ArrayRef<FieldInfo> VBases = {}) {
+    return TB.getRecordType(Fields, TypeSize::getFixed(SizeBits), Align(1),
+                            StructPacking::Default, Bases, VBases, Flags);
+  }
+};
+
+TEST_F(ABITypesTest, EmptyCRecord) {
+  const RecordType *Empty = makeRecord({}, 0, RecordFlags::CanPassInRegisters);
+  EXPECT_TRUE(Empty->isEmpty());
+}
+
+TEST_F(ABITypesTest, NestedEmptyCRecordField) {
+  const RecordType *Empty = makeRecord({}, 8, RecordFlags::CanPassInRegisters);
+  const RecordType *Nested =
+      makeRecord({FieldInfo(Empty, 0)}, 8, RecordFlags::CanPassInRegisters);
+  EXPECT_TRUE(Nested->isEmpty());
+}
+
+TEST_F(ABITypesTest, CXXNestedEmptyFieldRequiresNoUniqueAddress) {
+  RecordFlags CXXFlags = static_cast<RecordFlags>(
+      RecordFlags::CanPassInRegisters | RecordFlags::IsCXXRecord);
+  const RecordType *Empty = makeRecord({}, 8, CXXFlags);
+
+  const RecordType *WithoutNUA = makeRecord({FieldInfo(Empty, 0)}, 8, 
CXXFlags);
+  EXPECT_FALSE(WithoutNUA->isEmpty());
+
+  FieldInfo NUAField(Empty, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0,
+                     /*IsUnnamedBitField=*/false,
+                     /*HasNoUniqueAddress=*/true);
+  const RecordType *WithNUA = makeRecord({NUAField}, 8, CXXFlags);
+  EXPECT_TRUE(WithNUA->isEmpty());
+}
+
+TEST_F(ABITypesTest, ArrayOfEmptyRecords) {
+  RecordFlags CFlags = RecordFlags::CanPassInRegisters;
+  RecordFlags CXXFlags = static_cast<RecordFlags>(
+      RecordFlags::CanPassInRegisters | RecordFlags::IsCXXRecord);
+  const RecordType *EmptyC = makeRecord({}, 8, CFlags);
+  const RecordType *EmptyCXX = makeRecord({}, 8, CXXFlags);
+  const llvm::abi::Type *ArrC = TB.getArrayType(EmptyC, 2, 16);
+  const llvm::abi::Type *ArrCXX = TB.getArrayType(EmptyCXX, 2, 16);
+  const llvm::abi::Type *ZeroArrCXX = TB.getArrayType(EmptyCXX, 0, 0);
+
+  EXPECT_TRUE(makeRecord({FieldInfo(ArrC, 0)}, 16, CFlags)->isEmpty());
+  EXPECT_FALSE(makeRecord({FieldInfo(ArrCXX, 0)}, 16, CXXFlags)->isEmpty());
+  EXPECT_TRUE(makeRecord({FieldInfo(ZeroArrCXX, 0)}, 0, CXXFlags)->isEmpty());
+}
+
+TEST_F(ABITypesTest, BitfieldsAndFlexibleArrays) {
+  const llvm::abi::Type *I32 = TB.getIntegerType(32, Align(4), 
/*Signed=*/true);
+  FieldInfo Unnamed(I32, 0, /*IsBitField=*/true, /*BitFieldWidth=*/3,
+                    /*IsUnnamedBitField=*/true);
+  FieldInfo NamedZero(I32, 0, /*IsBitField=*/true, /*BitFieldWidth=*/0);
+
+  EXPECT_TRUE(
+      makeRecord({Unnamed}, 8, RecordFlags::CanPassInRegisters)->isEmpty());
+  EXPECT_FALSE(
+      makeRecord({NamedZero}, 8, RecordFlags::CanPassInRegisters)->isEmpty());
+  EXPECT_FALSE(
+      makeRecord({}, 0, RecordFlags::HasFlexibleArrayMember)->isEmpty());
+}
+
+TEST_F(ABITypesTest, DirectVirtualBasesAndVTablePointer) {
+  RecordFlags CXXFlags = static_cast<RecordFlags>(
+      RecordFlags::CanPassInRegisters | RecordFlags::IsCXXRecord);
+  const RecordType *Empty = makeRecord({}, 8, CXXFlags);
+  const RecordType *IntField = makeRecord(
+      {FieldInfo(TB.getIntegerType(32, Align(4), /*Signed=*/true), 0)}, 32,
+      CXXFlags);
+  const llvm::abi::Type *VPtr = TB.getPointerType(64, Align(8));
+  FieldInfo VTable(VPtr, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0,
+                   /*IsUnnamedBitField=*/false,
+                   /*HasNoUniqueAddress=*/false,
+                   /*IsVTablePointer=*/true);
+  // Direct virtual bases appear in both the base-class list (with
+  // IsVirtualBase) and the virtual-base list, matching CXXRecordDecl::bases()
+  // and vbases().
+  FieldInfo EmptyVBase(Empty, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0,
+                       /*IsUnnamedBitField=*/false,
+                       /*HasNoUniqueAddress=*/false,
+                       /*IsVTablePointer=*/false,
+                       /*IsVirtualBase=*/true);
+  FieldInfo NonEmptyVBase(IntField, 0, /*IsBitField=*/false,
+                          /*BitFieldWidth=*/0,
+                          /*IsUnnamedBitField=*/false,
+                          /*HasNoUniqueAddress=*/false,
+                          /*IsVTablePointer=*/false,
+                          /*IsVirtualBase=*/true);
+
+  EXPECT_TRUE(makeRecord({}, 8, CXXFlags, /*Bases=*/{EmptyVBase},
+                         /*VBases=*/{EmptyVBase})
+                  ->isEmpty());
+  EXPECT_FALSE(makeRecord({}, 32, CXXFlags, /*Bases=*/{NonEmptyVBase},
+                          /*VBases=*/{NonEmptyVBase})
+                   ->isEmpty());
+  EXPECT_TRUE(makeRecord({VTable}, 64,
+                         static_cast<RecordFlags>(CXXFlags |
+                                                  RecordFlags::IsPolymorphic),
+                         {FieldInfo(Empty, 0)})
+                  ->isEmpty());
+}
+
+} // namespace
diff --git a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn 
b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn
index a1c51980f0cbe..b355517a9251d 100644
--- a/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/unittests/ABI/BUILD.gn
@@ -6,5 +6,8 @@ unittest("ABITests") {
     "//llvm/lib/IR",
     "//llvm/lib/Support",
   ]
-  sources = [ "AArch64TargetInfoTest.cpp" ]
+  sources = [
+    "AArch64TargetInfoTest.cpp",
+    "TypesTest.cpp",
+  ]
 }

>From d0afb5ec0d9ab6b7b983c71fe5f10025d2595e8f Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Mon, 24 Aug 2026 17:09:10 -0700
Subject: [PATCH 2/4] Remove stale comment

---
 llvm/lib/ABI/Types.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/ABI/Types.cpp b/llvm/lib/ABI/Types.cpp
index 2e3ec907cf251..6c5684cf26eeb 100644
--- a/llvm/lib/ABI/Types.cpp
+++ b/llvm/lib/ABI/Types.cpp
@@ -16,7 +16,6 @@ bool RecordType::isEmpty() const {
   if (hasFlexibleArrayMember())
     return false;
 
-  // Direct virtual bases are not included in the base class list.
   for (const FieldInfo &Base : getBaseClasses()) {
     const auto *BaseRT = dyn_cast<RecordType>(Base.FieldType);
     if (!BaseRT || !BaseRT->isEmpty())

>From 87a3747b75cff31d063520d4529ec96f2366332a Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Tue, 25 Aug 2026 09:57:30 -0700
Subject: [PATCH 3/4] Remove special tracking of vtable pointer

---
 clang/lib/CodeGen/QualTypeMapper.cpp |  7 +------
 llvm/include/llvm/ABI/Types.h        |  5 ++---
 llvm/lib/ABI/Types.cpp               |  7 ++-----
 llvm/unittests/ABI/TypesTest.cpp     | 25 ++++++++++---------------
 4 files changed, 15 insertions(+), 29 deletions(-)

diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp 
b/clang/lib/CodeGen/QualTypeMapper.cpp
index 7c08fd9968ace..d26c6be88d1d4 100644
--- a/clang/lib/CodeGen/QualTypeMapper.cpp
+++ b/clang/lib/CodeGen/QualTypeMapper.cpp
@@ -393,10 +393,7 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl 
*RD) {
   if (RD->isPolymorphic()) {
     const llvm::abi::Type *VtablePointer =
         createPointerTypeForPointee(ASTCtx.VoidPtrTy);
-    Fields.emplace_back(VtablePointer, 0, /*IsBitField=*/false,
-                        /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false,
-                        /*HasNoUniqueAddress=*/false,
-                        /*IsVTablePointer=*/true);
+    Fields.emplace_back(VtablePointer, 0);
   }
 
   for (const auto &Base : RD->bases()) {
@@ -413,7 +410,6 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl 
*RD) {
     BaseClasses.emplace_back(BaseType, BaseOffset, /*IsBitField=*/false,
                              /*BitFieldWidth=*/0, /*IsUnnamedBitField=*/false,
                              /*HasNoUniqueAddress=*/false,
-                             /*IsVTablePointer=*/false,
                              /*IsVirtualBase=*/Base.isVirtual());
   }
 
@@ -430,7 +426,6 @@ QualTypeMapper::convertCXXRecordType(const CXXRecordDecl 
*RD) {
                                     /*BitFieldWidth=*/0,
                                     /*IsUnnamedBitField=*/false,
                                     /*HasNoUniqueAddress=*/false,
-                                    /*IsVTablePointer=*/false,
                                     /*IsVirtualBase=*/true);
   }
 
diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h
index 2247cc59b3978..21a614e97f8bd 100644
--- a/llvm/include/llvm/ABI/Types.h
+++ b/llvm/include/llvm/ABI/Types.h
@@ -238,18 +238,17 @@ struct FieldInfo {
   bool IsBitField;
   bool IsUnnamedBitfield;
   bool HasNoUniqueAddress;
-  bool IsVTablePointer;
   bool IsVirtualBase;
 
   FieldInfo(const Type *FieldType, uint64_t OffsetInBits = 0,
             bool IsBitField = false, uint64_t BitFieldWidth = 0,
             bool IsUnnamedBitField = false, bool HasNoUniqueAddress = false,
-            bool IsVTablePointer = false, bool IsVirtualBase = false)
+            bool IsVirtualBase = false)
       : FieldType(FieldType), OffsetInBits(OffsetInBits),
         BitFieldWidth(BitFieldWidth), IsBitField(IsBitField),
         IsUnnamedBitfield(IsUnnamedBitField),
         HasNoUniqueAddress(HasNoUniqueAddress),
-        IsVTablePointer(IsVTablePointer), IsVirtualBase(IsVirtualBase) {}
+        IsVirtualBase(IsVirtualBase) {}
 
   LLVM_ABI bool isEmpty() const;
 };
diff --git a/llvm/lib/ABI/Types.cpp b/llvm/lib/ABI/Types.cpp
index 6c5684cf26eeb..09636189cdb50 100644
--- a/llvm/lib/ABI/Types.cpp
+++ b/llvm/lib/ABI/Types.cpp
@@ -22,13 +22,10 @@ bool RecordType::isEmpty() const {
       return false;
   }
 
-  for (const FieldInfo &FI : getFields()) {
-    // Don't treat vtable pointers as a source field for emptiness.
-    if (FI.IsVTablePointer)
-      continue;
+  for (const FieldInfo &FI : getFields())
     if (!FI.isEmpty())
       return false;
-  }
+
   return true;
 }
 
diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp
index ad448882b82b1..31ba4aa8ef423 100644
--- a/llvm/unittests/ABI/TypesTest.cpp
+++ b/llvm/unittests/ABI/TypesTest.cpp
@@ -103,36 +103,31 @@ TEST_F(ABITypesTest, DirectVirtualBasesAndVTablePointer) {
       {FieldInfo(TB.getIntegerType(32, Align(4), /*Signed=*/true), 0)}, 32,
       CXXFlags);
   const llvm::abi::Type *VPtr = TB.getPointerType(64, Align(8));
-  FieldInfo VTable(VPtr, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0,
-                   /*IsUnnamedBitField=*/false,
-                   /*HasNoUniqueAddress=*/false,
-                   /*IsVTablePointer=*/true);
+  FieldInfo VTable(VPtr, 0);
   // Direct virtual bases appear in both the base-class list (with
   // IsVirtualBase) and the virtual-base list, matching CXXRecordDecl::bases()
   // and vbases().
   FieldInfo EmptyVBase(Empty, 0, /*IsBitField=*/false, /*BitFieldWidth=*/0,
                        /*IsUnnamedBitField=*/false,
                        /*HasNoUniqueAddress=*/false,
-                       /*IsVTablePointer=*/false,
                        /*IsVirtualBase=*/true);
   FieldInfo NonEmptyVBase(IntField, 0, /*IsBitField=*/false,
                           /*BitFieldWidth=*/0,
                           /*IsUnnamedBitField=*/false,
                           /*HasNoUniqueAddress=*/false,
-                          /*IsVTablePointer=*/false,
                           /*IsVirtualBase=*/true);
 
-  EXPECT_TRUE(makeRecord({}, 8, CXXFlags, /*Bases=*/{EmptyVBase},
-                         /*VBases=*/{EmptyVBase})
-                  ->isEmpty());
-  EXPECT_FALSE(makeRecord({}, 32, CXXFlags, /*Bases=*/{NonEmptyVBase},
+  EXPECT_FALSE(makeRecord({VTable}, 8, CXXFlags, /*Bases=*/{EmptyVBase},
+                          /*VBases=*/{EmptyVBase})
+                   ->isEmpty());
+  EXPECT_FALSE(makeRecord({VTable}, 32, CXXFlags, /*Bases=*/{NonEmptyVBase},
                           /*VBases=*/{NonEmptyVBase})
                    ->isEmpty());
-  EXPECT_TRUE(makeRecord({VTable}, 64,
-                         static_cast<RecordFlags>(CXXFlags |
-                                                  RecordFlags::IsPolymorphic),
-                         {FieldInfo(Empty, 0)})
-                  ->isEmpty());
+  EXPECT_FALSE(makeRecord({VTable}, 64,
+                          static_cast<RecordFlags>(CXXFlags |
+                                                   RecordFlags::IsPolymorphic),
+                          {FieldInfo(Empty, 0)})
+                   ->isEmpty());
 }
 
 } // namespace

>From 5eafef12f03d1d6eb44a12ee61642f1d0c63d1f9 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Tue, 25 Aug 2026 12:50:43 -0700
Subject: [PATCH 4/4] Fix formatting

---
 llvm/include/llvm/ABI/Types.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h
index 21a614e97f8bd..6cf70daa5bcbe 100644
--- a/llvm/include/llvm/ABI/Types.h
+++ b/llvm/include/llvm/ABI/Types.h
@@ -247,8 +247,7 @@ struct FieldInfo {
       : FieldType(FieldType), OffsetInBits(OffsetInBits),
         BitFieldWidth(BitFieldWidth), IsBitField(IsBitField),
         IsUnnamedBitfield(IsUnnamedBitField),
-        HasNoUniqueAddress(HasNoUniqueAddress),
-        IsVirtualBase(IsVirtualBase) {}
+        HasNoUniqueAddress(HasNoUniqueAddress), IsVirtualBase(IsVirtualBase) {}
 
   LLVM_ABI bool isEmpty() const;
 };

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to