Author: hstk30-hw
Date: 2025-11-08T21:52:13+08:00
New Revision: 21c1b7845b05fb126631b91e7c978a51c3f9eb9a

URL: 
https://github.com/llvm/llvm-project/commit/21c1b7845b05fb126631b91e7c978a51c3f9eb9a
DIFF: 
https://github.com/llvm/llvm-project/commit/21c1b7845b05fb126631b91e7c978a51c3f9eb9a.diff

LOG: fix: C++ empty record with align lead to va_list out of sync (#72197)

Fix AArch64 argument passing for C++ empty classes with large explicitly 
specified  alignment

reproducer: https://godbolt.org/z/qsze8fqra 
rel issue: https://github.com/llvm/llvm-project/issues/69872 
rel commit: 
https://github.com/llvm/llvm-project/commit/1711cc930bda8d27e87a2092bd220c18e4600c98

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/CodeGen/Targets/AArch64.cpp
    clang/test/CodeGen/AArch64/args.cpp
    clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp
    clang/test/CodeGen/arm64-microsoft-arguments.cpp
    clang/test/CodeGenCXX/aarch64-arguments.cpp
    clang/test/CodeGenCXX/arm64-darwinpcs.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3a4e1fce2511e..6b396e7ba63f3 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -119,6 +119,7 @@ C++ Specific Potentially Breaking Changes
 
 ABI Changes in This Version
 ---------------------------
+- Fix AArch64 argument passing for C++ empty classes with large explicitly 
specified alignment.
 
 AST Dumping Potentially Breaking Changes
 ----------------------------------------

diff  --git a/clang/lib/CodeGen/Targets/AArch64.cpp 
b/clang/lib/CodeGen/Targets/AArch64.cpp
index bb41a14f5d2f3..d42fcd8b3237c 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -422,6 +422,12 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType 
Ty, bool IsVariadicFn,
   }
 
   // Empty records:
+  // AAPCS64 does not say that empty records are ignored as arguments,
+  // but other compilers do so in certain situations, and we copy that 
behavior.
+  // Those situations are in fact language-mode-specific, which seems really
+  // unfortunate, but it's something we just have to accept. If this doesn't
+  // apply, just fall through to the standard argument-handling path.
+  // Darwin overrides the psABI here to ignore all empty records in all modes.
   uint64_t Size = getContext().getTypeSize(Ty);
   bool IsEmpty = isEmptyRecord(getContext(), Ty, true);
   if (!Ty->isSVESizelessBuiltinType() && (IsEmpty || Size == 0)) {
@@ -434,9 +440,6 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType 
Ty, bool IsVariadicFn,
     // behaviour here.
     if (Size == 0)
       return ABIArgInfo::getIgnore();
-
-    // Otherwise, they are passed as if they have a size of 1 byte.
-    return ABIArgInfo::getDirect(llvm::Type::getInt8Ty(getVMContext()));
   }
 
   // Homogeneous Floating-point Aggregates (HFAs) need to be expanded.

diff  --git a/clang/test/CodeGen/AArch64/args.cpp 
b/clang/test/CodeGen/AArch64/args.cpp
index 3cb62d3119ecf..c284316a5e1b4 100644
--- a/clang/test/CodeGen/AArch64/args.cpp
+++ b/clang/test/CodeGen/AArch64/args.cpp
@@ -17,11 +17,29 @@ struct Empty {};
 
 // DARWIN: define{{.*}} i32 @empty_arg(i32 noundef %a)
 // C: define{{.*}} i32 @empty_arg(i32 noundef %a)
-// CXX: define{{.*}} i32 @empty_arg(i8 %e.coerce, i32 noundef %a)
+// CXX: define{{.*}} i32 @empty_arg(i64 %e.coerce, i32 noundef %a)
 EXTERNC int empty_arg(struct Empty e, int a) {
   return a;
 }
 
+// CXX: define{{.*}} i32 @empty_align8_arg(i64 %a.coerce, i32 noundef %b)
+struct EmptyAlign8 { int __attribute__((aligned(8))) : 0; };
+EXTERNC int empty_align8_arg(struct EmptyAlign8 a, int b) {
+  return b;
+}
+
+// CXX: define{{.*}} i32 @empty_align16_arg(i128 %a.coerce, i32 noundef %b)
+struct EmptyAlign16 { long long int __attribute__((aligned(16))) : 0; };
+EXTERNC int empty_align16_arg(struct EmptyAlign16 a, int b) {
+  return b;
+}
+
+// CXX: define{{.*}} i32 @empty_align32_arg(ptr dead_on_return noundef %a, i32 
noundef %b)
+struct EmptyAlign32 { long long int __attribute__((aligned(32))) : 0; };
+EXTERNC int empty_align32_arg(struct EmptyAlign32 a, int b) {
+  return b;
+}
+
 // DARWIN: define{{.*}} void @empty_ret()
 // C: define{{.*}} void @empty_ret()
 // CXX: define{{.*}} void @empty_ret()

diff  --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp 
b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp
index f0c9ef28201a5..97fdd0ce56c66 100644
--- a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp
+++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp
@@ -575,19 +575,21 @@ void TSpp_align16(SSpp_align16 s) { *s.a.x = 1; }
 struct Sempty {
 };
 // CHECK-A64-LABEL: define dso_local void @_Z6Tempty6Sempty(
-// CHECK-A64-SAME: i8 [[S_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] {
 // CHECK-A64-NEXT:  [[ENTRY:.*:]]
 // CHECK-A64-NEXT:    [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1
 // CHECK-A64-NEXT:    [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw 
[[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0
-// CHECK-A64-NEXT:    store i8 [[S_COERCE]], ptr [[COERCE_DIVE]], align 1
+// CHECK-A64-NEXT:    [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i8
+// CHECK-A64-NEXT:    store i8 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 1
 // CHECK-A64-NEXT:    ret void
 //
 // CHECK-A64_32-LABEL: define void @_Z6Tempty6Sempty(
-// CHECK-A64_32-SAME: i8 [[S_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] {
 // CHECK-A64_32-NEXT:  [[ENTRY:.*:]]
 // CHECK-A64_32-NEXT:    [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1
 // CHECK-A64_32-NEXT:    [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw 
[[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0
-// CHECK-A64_32-NEXT:    store i8 [[S_COERCE]], ptr [[COERCE_DIVE]], align 1
+// CHECK-A64_32-NEXT:    [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i8
+// CHECK-A64_32-NEXT:    store i8 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], 
align 1
 // CHECK-A64_32-NEXT:    ret void
 //
 void Tempty(Sempty s) { }

diff  --git a/clang/test/CodeGen/arm64-microsoft-arguments.cpp 
b/clang/test/CodeGen/arm64-microsoft-arguments.cpp
index a0a81be54325f..f7eb0cc765354 100644
--- a/clang/test/CodeGen/arm64-microsoft-arguments.cpp
+++ b/clang/test/CodeGen/arm64-microsoft-arguments.cpp
@@ -57,7 +57,7 @@ S4 f4() {
 
 // Pass and return from instance method called from instance method.
 // CHECK: define {{.*}} void @{{.*}}bar@Q1{{.*}}(ptr {{[^,]*}} %this, ptr 
dead_on_unwind inreg noalias writable sret(%class.P1) align 1 %agg.result)
-// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr 
dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i8 %0)
+// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr 
dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i64 
%coerce.val.ii)
 
 class P1 {
 public:
@@ -76,7 +76,7 @@ P1 Q1::bar() {
 
 // Pass and return from instance method called from free function.
 // CHECK: define {{.*}} void {{.*}}bar{{.*}}()
-// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr 
dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i8 %0)
+// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr 
dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i64 
%coerce.val.ii)
 class P2 {
 public:
   P2 foo(P2 x);

diff  --git a/clang/test/CodeGenCXX/aarch64-arguments.cpp 
b/clang/test/CodeGenCXX/aarch64-arguments.cpp
index ffb0cafa8882d..3206e38ad0090 100644
--- a/clang/test/CodeGenCXX/aarch64-arguments.cpp
+++ b/clang/test/CodeGenCXX/aarch64-arguments.cpp
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -triple arm64-none-linux -emit-llvm -w -o - %s | FileCheck 
-check-prefix=PCS %s
 
-// PCS: define{{.*}} void @{{.*}}(i8 %a
+// PCS: define{{.*}} void @{{.*}}(i64 %a.coerce)
 struct s0 {};
 void f0(s0 a) {}

diff  --git a/clang/test/CodeGenCXX/arm64-darwinpcs.cpp 
b/clang/test/CodeGenCXX/arm64-darwinpcs.cpp
index a0b0d9efdd4c4..ef0e2da3effac 100644
--- a/clang/test/CodeGenCXX/arm64-darwinpcs.cpp
+++ b/clang/test/CodeGenCXX/arm64-darwinpcs.cpp
@@ -7,7 +7,7 @@ void test_extensions(bool a, char b, short c) {}
 
 struct Empty {};
 void test_empty(Empty e) {}
-// CHECK: define{{.*}} void @_Z10test_empty5Empty(i8
+// CHECK: define{{.*}} void @_Z10test_empty5Empty(i64 %e.coerce)
 // CHECK-DARWIN: define{{.*}} void @_Z10test_empty5Empty()
 
 struct HFA {


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

Reply via email to