[clang] [llvm] [LLVMABI][AARCH64] Handle records that cannot be passed in registers (PR #217689)
Andy Kaylor via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 24 12:14:41 PDT 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/217689
>From 268068f9a7ffc0a5ddb6292bda0c81c7070fc0a0 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 18 Aug 2026 17:46:31 -0700
Subject: [PATCH 1/2] [LLVMABI][AARCH64] Handle records that can't be passed in
registers
This adds AArch64 handling in the LLVM ABI library for classifying record
arguments as Indirect when the record type cannot be passed in registers.
Assisted-by: Cursor / Grok 4.5 (test generation)
---
llvm/lib/ABI/Targets/AArch64.cpp | 7 +++++
llvm/unittests/ABI/AArch64TargetInfoTest.cpp | 29 ++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/llvm/lib/ABI/Targets/AArch64.cpp b/llvm/lib/ABI/Targets/AArch64.cpp
index 345d87d3b2652..bf48f6a47dabf 100644
--- a/llvm/lib/ABI/Targets/AArch64.cpp
+++ b/llvm/lib/ABI/Targets/AArch64.cpp
@@ -119,6 +119,13 @@ ArgInfo AArch64TargetInfo::classifyArgumentType(
return ArgInfo::getDirect();
}
+ // Structures with either a non-trivial destructor or a non-trivial
+ // copy constructor are always indirect.
+ if (auto RecordRAA = getRecordArgABI(Ty)) {
+ return getNaturalAlignIndirect(Ty, RecordRAA ==
+ RecordArgABI::RAA_DirectInMemory);
+ }
+
reportNYI("Aggregate argument type handling");
return ArgInfo::getDirect();
}
diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
index f942090acd87b..0da1c00d4c9f4 100644
--- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
+++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp
@@ -406,4 +406,33 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) {
}
}
+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);
+ }
+}
+
} // namespace
>From df0e53419420e62f6a88dfa7c8119a45aa8fb9d7 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 20 Aug 2026 09:35:40 -0700
Subject: [PATCH 2/2] Add clang test
---
.../AArch64/abi-classify-arg-types.cpp | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp
diff --git a/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp b/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp
new file mode 100644
index 0000000000000..139f222ecc1fb
--- /dev/null
+++ b/clang/test/CodeGen/AArch64/abi-classify-arg-types.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple arm64_32-apple-ios7.0 -target-abi darwinpcs -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64_be-linux-gnu -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-pc-windows-msvc -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented"
+
+// This test is verifying that the LLVM ABI library classifies C++ record
+// arguments that cannot be passed in registers the same way Clang does without
+// the library.
+
+// Structures with a non-trivial copy constructor or destructor are passed
+// indirectly (as a pointer) rather than in registers.
+
+extern "C" {
+
+struct NonTrivialCopy {
+ NonTrivialCopy(const NonTrivialCopy &);
+ int x;
+};
+
+struct NonTrivialDtorAndCopy {
+ NonTrivialDtorAndCopy(const NonTrivialDtorAndCopy &);
+ ~NonTrivialDtorAndCopy();
+ int x;
+};
+
+struct ExplicitCopy {
+ ExplicitCopy();
+ ExplicitCopy(const ExplicitCopy &);
+ short s;
+};
+
+void arg_nontrivial_copy(NonTrivialCopy a) {}
+// CHECK: define{{.*}} void @arg_nontrivial_copy(ptr nofree noundef align 4 dead_on_return dereferenceable(4) %{{.*}})
+
+void arg_nontrivial_dtor_and_copy(NonTrivialDtorAndCopy a) {}
+// CHECK: define{{.*}} void @arg_nontrivial_dtor_and_copy(ptr nofree noundef align 4 {{(dead_on_return )?}}dereferenceable(4) %{{.*}})
+
+void arg_explicit_copy(ExplicitCopy a) {}
+// CHECK: define{{.*}} void @arg_explicit_copy(ptr nofree noundef align 2 dead_on_return dereferenceable(2) %{{.*}})
+
+}
More information about the llvm-commits
mailing list