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

>From 510e12ce090a18f8cf97528254718f8c551031e8 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Wed, 19 Aug 2026 09:16:52 -0700
Subject: [PATCH 1/2] [LLVMABI][AARCH64] Support transparent union arguments

This adds support for using the first field in a transparent union for the
purposes of argument classification.

The `useFirstFieldIfTransparentUnion` function was already implemented for
the `X86_64TargetInfo` class. I moved it to the `TargetInfo` base class
because there is nothing target-specific about it.

Assisted-by: Cursor / various models
---
 .../CodeGen/AArch64/abi-classify-arg-types.c  | 23 ++++++
 llvm/include/llvm/ABI/TargetInfo.h            |  4 +
 llvm/lib/ABI/TargetInfo.cpp                   | 11 +++
 llvm/lib/ABI/Targets/AArch64.cpp              |  2 +
 llvm/lib/ABI/Targets/X86.cpp                  | 13 ----
 llvm/unittests/ABI/AArch64TargetInfoTest.cpp  | 74 +++++++++++++++++++
 6 files changed, 114 insertions(+), 13 deletions(-)

diff --git a/clang/test/CodeGen/AArch64/abi-classify-arg-types.c 
b/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
index 78be22a7aa3e1..a647586a86547 100644
--- a/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
+++ b/clang/test/CodeGen/AArch64/abi-classify-arg-types.c
@@ -70,3 +70,26 @@ void arg_void_ptr(void* pv) {}
 typedef float fx2x2_t __attribute__((matrix_type(2, 2)));
 void arg_matrix(fx2x2_t m) {}
 // CHECK: define{{.*}} void @arg_matrix(<4 x float> noundef %{{.*}})
+
+// Transparent unions are passed as their first field.
+typedef union {
+  int i;
+  float f;
+} tu_int_t __attribute__((transparent_union));
+void arg_transparent_union_int(tu_int_t tu) {}
+// CHECK: define{{.*}} void @arg_transparent_union_int(i32 %{{.*}})
+
+typedef union {
+  char c;
+  signed char sc;
+} tu_char_t __attribute__((transparent_union));
+void arg_transparent_union_char(tu_char_t tu) {}
+// AAPCS: define{{.*}} void @arg_transparent_union_char(i8 %{{.*}})
+// DARWIN: define{{.*}} void @arg_transparent_union_char(i8 noundef signext 
%{{.*}})
+
+typedef union {
+  void *p;
+  int *ip;
+} tu_ptr_t __attribute__((transparent_union));
+void arg_transparent_union_ptr(tu_ptr_t tu) {}
+// CHECK: define{{.*}} void @arg_transparent_union_ptr(ptr %{{.*}})
diff --git a/llvm/include/llvm/ABI/TargetInfo.h 
b/llvm/include/llvm/ABI/TargetInfo.h
index 7ecf8df0edbf0..8132de140064a 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -83,6 +83,10 @@ class TargetInfo {
                                            bool ByVal = true) const;
   LLVM_ABI bool isAggregateTypeForABI(const Type *Ty) const;
 
+  /// If Ty is a transparent union, return its first field type; otherwise
+  /// return Ty unchanged.
+  LLVM_ABI const Type *useFirstFieldIfTransparentUnion(const Type *Ty) const;
+
   /// Apply rules for classifying return types that are common to all targets.
   LLVM_ABI bool maybeCommonClassifyReturnType(FunctionInfo &FI) const;
 };
diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp
index a0190bd1fcb8a..955e3b2018c7b 100644
--- a/llvm/lib/ABI/TargetInfo.cpp
+++ b/llvm/lib/ABI/TargetInfo.cpp
@@ -52,6 +52,17 @@ RecordArgABI TargetInfo::getRecordArgABI(const Type *Ty) 
const {
   return getRecordArgABI(RT);
 }
 
+const Type *TargetInfo::useFirstFieldIfTransparentUnion(const Type *Ty) const {
+  if (const auto *RT = dyn_cast<RecordType>(Ty)) {
+    if (RT->isUnion() && RT->isTransparentUnion()) {
+      auto Fields = RT->getFields();
+      assert(!Fields.empty() && "transparent union cannot be empty");
+      return Fields.front().FieldType;
+    }
+  }
+  return Ty;
+}
+
 bool TargetInfo::maybeCommonClassifyReturnType(FunctionInfo &FI) const {
   const abi::Type *Ty = FI.getReturnType();
 
diff --git a/llvm/lib/ABI/Targets/AArch64.cpp b/llvm/lib/ABI/Targets/AArch64.cpp
index b1a352c230c8d..90e111ba5147c 100644
--- a/llvm/lib/ABI/Targets/AArch64.cpp
+++ b/llvm/lib/ABI/Targets/AArch64.cpp
@@ -92,6 +92,8 @@ ArgInfo AArch64TargetInfo::classifyReturnType(const Type 
*RetTy,
 ArgInfo AArch64TargetInfo::classifyArgumentType(
     const Type *Ty, bool IsVariadicFn, bool IsNamedArg,
     unsigned CallingConvention, unsigned &NSRN, unsigned &NPRN) const {
+  Ty = useFirstFieldIfTransparentUnion(Ty);
+
   // TODO: Handle variadic functins here when Windows Arm64 EC is supported.
 
   if (Ty->isVector()) {
diff --git a/llvm/lib/ABI/Targets/X86.cpp b/llvm/lib/ABI/Targets/X86.cpp
index 7b5135419e8b5..43e30c5a43a18 100644
--- a/llvm/lib/ABI/Targets/X86.cpp
+++ b/llvm/lib/ABI/Targets/X86.cpp
@@ -111,7 +111,6 @@ class X86_64TargetInfo : public TargetInfo {
   ArgInfo classifyArgumentType(const Type *Ty, unsigned FreeIntRegs,
                                unsigned &NeededInt, unsigned &NeededSse,
                                bool IsNamedArg, bool IsRegCall = false) const;
-  const Type *useFirstFieldIfTransparentUnion(const Type *Ty) const;
 
 public:
   X86_64TargetInfo(TypeBuilder &TypeBuilder, X86AVXABILevel AVXABILevel,
@@ -596,18 +595,6 @@ void X86_64TargetInfo::classify(const Type *T, uint64_t 
OffsetBase, Class &Lo,
   Hi = NoClass;
 }
 
-const Type *
-X86_64TargetInfo::useFirstFieldIfTransparentUnion(const Type *Ty) const {
-  if (const auto *RT = dyn_cast<RecordType>(Ty)) {
-    if (RT->isUnion() && RT->isTransparentUnion()) {
-      auto Fields = RT->getFields();
-      assert(!Fields.empty() && "transparent union cannot be empty");
-      return Fields.front().FieldType;
-    }
-  }
-  return Ty;
-}
-
 ArgInfo
 X86_64TargetInfo::classifyArgumentType(const Type *Ty, unsigned FreeIntRegs,
                                        unsigned &NeededInt, unsigned 
&NeededSSE,
diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp 
b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
index e73366f007a8c..a977d1d1a9eb7 100644
--- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
@@ -227,4 +227,78 @@ TEST_F(AArch64TargetInfoTest, 
ClassifyArgumentScalarsDirectWin64) {
   }
 }
 
+static void expectNaturalAlignIndirect(const ArgInfo &Info,
+                                       llvm::Align ExpectedAlign, bool ByVal) {
+  EXPECT_TRUE(Info.isIndirect());
+  EXPECT_EQ(Info.getIndirectAlign(), ExpectedAlign);
+  EXPECT_EQ(Info.getIndirectByVal(), ByVal);
+}
+
+// Records that cannot be passed in registers (e.g. non-trivial C++ types) are
+// classified as Indirect with ByVal=false under all AArch64 ABI kinds.
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentRecordCannotPassInRegisters) {
+  // A record without CanPassInRegisters is treated like a C++ type with a
+  // non-trivial copy constructor or destructor.
+  const ABIType *CannotPass = TB.getRecordType(
+      {llvm::abi::FieldInfo(I32)}, llvm::TypeSize::getFixed(32), 
llvm::Align(4),
+      llvm::abi::StructPacking::Default, /*BaseClasses=*/{},
+      /*VirtualBaseClasses=*/{}, llvm::abi::RecordFlags::IsCXXRecord);
+
+  for (AArch64ABIKind Kind :
+       {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, 
AArch64ABIKind::Win64,
+        AArch64ABIKind::AAPCSSoft}) {
+    std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind);
+    std::unique_ptr<FunctionInfo> FI =
+        FunctionInfo::create(llvm::CallingConv::C, Void, {CannotPass});
+    TI->computeInfo(*FI);
+    expectNaturalAlignIndirect(FI->getArgInfo(0).Info, llvm::Align(4),
+                               /*ByVal=*/false);
+  }
+}
+
+// Transparent unions are classified as their first field type.
+TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) {
+  using llvm::abi::FieldInfo;
+  using llvm::abi::RecordFlags;
+  using llvm::abi::StructPacking;
+
+  // First field is i32; second field is ignored for classification.
+  const ABIType *TUInt = TB.getUnionType(
+      {FieldInfo(I32), FieldInfo(F32)}, llvm::TypeSize::getFixed(32),
+      llvm::Align(4), StructPacking::Default, RecordFlags::IsTransparent);
+
+  for (AArch64ABIKind Kind :
+       {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, 
AArch64ABIKind::Win64,
+        AArch64ABIKind::AAPCSSoft}) {
+    std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind);
+    std::unique_ptr<FunctionInfo> FI =
+        FunctionInfo::create(llvm::CallingConv::C, Void, {TUInt});
+    TI->computeInfo(*FI);
+    expectUncoercedDirect(FI->getArgInfo(0).Info);
+  }
+
+  // First field is a promotable integer: DarwinPCS extends; others are Direct.
+  const ABIType *TUChar = TB.getUnionType(
+      {FieldInfo(I8), FieldInfo(U8)}, llvm::TypeSize::getFixed(8),
+      llvm::Align(1), StructPacking::Default, RecordFlags::IsTransparent);
+
+  {
+    std::unique_ptr<TargetInfo> TI =
+        createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS);
+    std::unique_ptr<FunctionInfo> FI =
+        FunctionInfo::create(llvm::CallingConv::C, Void, {TUChar});
+    TI->computeInfo(*FI);
+    expectExtendInteger(FI->getArgInfo(0).Info, I8, /*IsSigned=*/true);
+  }
+
+  for (AArch64ABIKind Kind : {AArch64ABIKind::AAPCS, AArch64ABIKind::Win64,
+                              AArch64ABIKind::AAPCSSoft}) {
+    std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind);
+    std::unique_ptr<FunctionInfo> FI =
+        FunctionInfo::create(llvm::CallingConv::C, Void, {TUChar});
+    TI->computeInfo(*FI);
+    expectUncoercedDirect(FI->getArgInfo(0).Info);
+  }
+}
+
 } // namespace

>From 5e9f1b19cc9c23f853e1f7c560e7a665087875a3 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Thu, 20 Aug 2026 15:54:33 -0700
Subject: [PATCH 2/2] Remove test case from bad rebase

---
 llvm/unittests/ABI/AArch64TargetInfoTest.cpp | 29 --------------------
 1 file changed, 29 deletions(-)

diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp 
b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
index a977d1d1a9eb7..237053f78b7b6 100644
--- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
@@ -227,35 +227,6 @@ TEST_F(AArch64TargetInfoTest, 
ClassifyArgumentScalarsDirectWin64) {
   }
 }
 
-static void expectNaturalAlignIndirect(const ArgInfo &Info,
-                                       llvm::Align ExpectedAlign, bool ByVal) {
-  EXPECT_TRUE(Info.isIndirect());
-  EXPECT_EQ(Info.getIndirectAlign(), ExpectedAlign);
-  EXPECT_EQ(Info.getIndirectByVal(), ByVal);
-}
-
-// Records that cannot be passed in registers (e.g. non-trivial C++ types) are
-// classified as Indirect with ByVal=false under all AArch64 ABI kinds.
-TEST_F(AArch64TargetInfoTest, ClassifyArgumentRecordCannotPassInRegisters) {
-  // A record without CanPassInRegisters is treated like a C++ type with a
-  // non-trivial copy constructor or destructor.
-  const ABIType *CannotPass = TB.getRecordType(
-      {llvm::abi::FieldInfo(I32)}, llvm::TypeSize::getFixed(32), 
llvm::Align(4),
-      llvm::abi::StructPacking::Default, /*BaseClasses=*/{},
-      /*VirtualBaseClasses=*/{}, llvm::abi::RecordFlags::IsCXXRecord);
-
-  for (AArch64ABIKind Kind :
-       {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, 
AArch64ABIKind::Win64,
-        AArch64ABIKind::AAPCSSoft}) {
-    std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind);
-    std::unique_ptr<FunctionInfo> FI =
-        FunctionInfo::create(llvm::CallingConv::C, Void, {CannotPass});
-    TI->computeInfo(*FI);
-    expectNaturalAlignIndirect(FI->getArgInfo(0).Info, llvm::Align(4),
-                               /*ByVal=*/false);
-  }
-}
-
 // Transparent unions are classified as their first field type.
 TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) {
   using llvm::abi::FieldInfo;

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

Reply via email to