https://github.com/ahatanak created 
https://github.com/llvm/llvm-project/pull/226615

This fixes two bugs in GenBinaryFunc::visitVolatileTrivial 
(CGNonTrivialStruct.cpp):

- It was passing the field's type instead of the containing struct's type to 
MakeAddrLValue.

- It was calling EmitLoadOfLValue and EmitStoreThroughLValue even when the 
lvalue was an aggregate or a _Complex type. The functions end up calling 
EmitLoadOfScalar/EmitStoreOfScalar, which are meant to be used only for scalar 
values.

Both caused CodeGen to emit incorrect TBAA metadata, which made the IR verifier 
fail.

rdar://187077389

>From 5bd03eea8498c90a4ff4690af27b8094e5ff7033 Mon Sep 17 00:00:00 2001
From: Akira Hatanaka <[email protected]>
Date: Thu, 24 Sep 2026 18:14:19 -0700
Subject: [PATCH] [CodeGen] Fix TBAA verification failure when copying volatile
 C structs

This fixes two bugs in GenBinaryFunc::visitVolatileTrivial
(CGNonTrivialStruct.cpp):

- It was passing the field's type instead of the containing
  struct's type to MakeAddrLValue.

- It was calling EmitLoadOfLValue and EmitStoreThroughLValue even
  when the lvalue was an aggregate or a _Complex type. The
  functions end up calling EmitLoadOfScalar/EmitStoreOfScalar,
  which are meant to be used only for scalar values.

Both caused CodeGen to emit incorrect TBAA metadata, which made the
IR verifier fail.

rdar://187077389
---
 clang/lib/CodeGen/CGNonTrivialStruct.cpp      | 46 ++++++++----
 .../ptrauth-nontrivial-c-struct-volatile.c    | 75 +++++++++++++++++++
 2 files changed, 108 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/CodeGen/ptrauth-nontrivial-c-struct-volatile.c

diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp 
b/clang/lib/CodeGen/CGNonTrivialStruct.cpp
index 0a383c8f919d95..3af65c8c9eb6cd 100644
--- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp
+++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp
@@ -516,7 +516,7 @@ template <class Derived> struct GenFuncBase {
   CodeGenFunction *CGF = nullptr;
 };
 
-template <class Derived, bool IsMove>
+template <class Derived, bool IsMove, bool IsCtor>
 struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>,
                        GenFuncBase<Derived> {
   GenBinaryFunc(ASTContext &Ctx) : CopyStructVisitor<Derived, IsMove>(Ctx) {}
@@ -563,13 +563,16 @@ struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>,
       CanQualType RT =
           this->CGF->getContext().getCanonicalTagType(FD->getParent());
       llvm::Type *Ty = this->CGF->ConvertType(RT);
+      // FT is volatile-qualified, so propagate that onto the base lvalue's
+      // type.
+      QualType QT = QualType(RT).withVolatile();
       Address DstAddr = this->getAddrWithOffset(Addrs[DstIdx], Offset);
       LValue DstBase =
-          this->CGF->MakeAddrLValue(DstAddr.withElementType(Ty), FT);
+          this->CGF->MakeAddrLValue(DstAddr.withElementType(Ty), QT);
       DstLV = this->CGF->EmitLValueForField(DstBase, FD);
       Address SrcAddr = this->getAddrWithOffset(Addrs[SrcIdx], Offset);
       LValue SrcBase =
-          this->CGF->MakeAddrLValue(SrcAddr.withElementType(Ty), FT);
+          this->CGF->MakeAddrLValue(SrcAddr.withElementType(Ty), QT);
       SrcLV = this->CGF->EmitLValueForField(SrcBase, FD);
     } else {
       llvm::Type *Ty = this->CGF->ConvertTypeForMem(FT);
@@ -578,8 +581,25 @@ struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>,
       DstLV = this->CGF->MakeAddrLValue(DstAddr, FT);
       SrcLV = this->CGF->MakeAddrLValue(SrcAddr, FT);
     }
-    RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation());
-    this->CGF->EmitStoreThroughLValue(SrcVal, DstLV);
+    // Load the value from the source. For the aggregate case, load directly
+    // into DstLV's own storage via an AggValueSlot.
+    AggValueSlot Slot = AggValueSlot::forLValue(
+        DstLV, AggValueSlot::IsDestructed, AggValueSlot::DoesNotNeedGCBarriers,
+        IsCtor ? AggValueSlot::IsNotAliased : AggValueSlot::IsAliased,
+        AggValueSlot::DoesNotOverlap);
+    RValue SrcVal =
+        this->CGF->EmitLoadOfAnyValue(SrcLV, Slot, SourceLocation());
+    switch (CodeGenFunction::getEvaluationKind(FT)) {
+    case TEK_Aggregate:
+      break; // Already copied into DstLV via Slot above.
+    case TEK_Complex:
+      this->CGF->EmitStoreOfComplex(SrcVal.getComplexVal(), DstLV,
+                                    /*isInit=*/IsCtor);
+      break;
+    case TEK_Scalar:
+      this->CGF->EmitStoreThroughLValue(SrcVal, DstLV, /*isInit=*/IsCtor);
+      break;
+    }
   }
   void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset,
                     std::array<Address, 2> Addrs) {
@@ -692,9 +712,9 @@ struct GenDefaultInitialize
   }
 };
 
-struct GenCopyConstructor : GenBinaryFunc<GenCopyConstructor, false> {
+struct GenCopyConstructor : GenBinaryFunc<GenCopyConstructor, false, true> {
   GenCopyConstructor(ASTContext &Ctx)
-      : GenBinaryFunc<GenCopyConstructor, false>(Ctx) {}
+      : GenBinaryFunc<GenCopyConstructor, false, true>(Ctx) {}
 
   void visitARCStrong(QualType QT, const FieldDecl *FD,
                       CharUnits CurStructOffset, std::array<Address, 2> Addrs) 
{
@@ -722,9 +742,9 @@ struct GenCopyConstructor : 
GenBinaryFunc<GenCopyConstructor, false> {
   }
 };
 
-struct GenMoveConstructor : GenBinaryFunc<GenMoveConstructor, true> {
+struct GenMoveConstructor : GenBinaryFunc<GenMoveConstructor, true, true> {
   GenMoveConstructor(ASTContext &Ctx)
-      : GenBinaryFunc<GenMoveConstructor, true>(Ctx) {}
+      : GenBinaryFunc<GenMoveConstructor, true, true>(Ctx) {}
 
   void visitARCStrong(QualType QT, const FieldDecl *FD,
                       CharUnits CurStructOffset, std::array<Address, 2> Addrs) 
{
@@ -754,9 +774,9 @@ struct GenMoveConstructor : 
GenBinaryFunc<GenMoveConstructor, true> {
   }
 };
 
-struct GenCopyAssignment : GenBinaryFunc<GenCopyAssignment, false> {
+struct GenCopyAssignment : GenBinaryFunc<GenCopyAssignment, false, false> {
   GenCopyAssignment(ASTContext &Ctx)
-      : GenBinaryFunc<GenCopyAssignment, false>(Ctx) {}
+      : GenBinaryFunc<GenCopyAssignment, false, false>(Ctx) {}
 
   void visitARCStrong(QualType QT, const FieldDecl *FD,
                       CharUnits CurStructOffset, std::array<Address, 2> Addrs) 
{
@@ -785,9 +805,9 @@ struct GenCopyAssignment : GenBinaryFunc<GenCopyAssignment, 
false> {
   }
 };
 
-struct GenMoveAssignment : GenBinaryFunc<GenMoveAssignment, true> {
+struct GenMoveAssignment : GenBinaryFunc<GenMoveAssignment, true, false> {
   GenMoveAssignment(ASTContext &Ctx)
-      : GenBinaryFunc<GenMoveAssignment, true>(Ctx) {}
+      : GenBinaryFunc<GenMoveAssignment, true, false>(Ctx) {}
 
   void visitARCStrong(QualType QT, const FieldDecl *FD,
                       CharUnits CurStructOffset, std::array<Address, 2> Addrs) 
{
diff --git a/clang/test/CodeGen/ptrauth-nontrivial-c-struct-volatile.c 
b/clang/test/CodeGen/ptrauth-nontrivial-c-struct-volatile.c
new file mode 100644
index 00000000000000..14d50fe369f68d
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-nontrivial-c-struct-volatile.c
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls 
-fptrauth-returns -fptrauth-intrinsics -O1 -disable-llvm-passes -emit-llvm -o - 
%s | FileCheck %s
+
+#define AQ __ptrauth(1, 1, 50)
+
+struct HasScalarField {
+  void (* AQ fp)(void);
+  int f3;
+};
+
+// CHECK-LABEL: define{{.*}} void @copyScalar(
+// CHECK: call void @[[COPY_ASSIGNMENT_SCALAR:__copy_assignment[a-zA-Z0-9_]*]](
+void copyScalar(volatile struct HasScalarField *dst,
+                 volatile struct HasScalarField *src) {
+  *dst = *src;
+}
+
+// CHECK: define{{.*}} void @[[COPY_ASSIGNMENT_SCALAR]](
+// CHECK: %[[DST:.*]] = getelementptr inbounds nuw %struct.HasScalarField, ptr 
%{{.*}}, i32 0, i32 1
+// CHECK: %[[SRC:.*]] = getelementptr inbounds nuw %struct.HasScalarField, ptr 
%{{.*}}, i32 0, i32 1
+// CHECK: load volatile i32, ptr %[[SRC]], align 8, !tbaa 
![[TBAA_SCALAR:[0-9]+]]{{$}}
+// CHECK: store volatile i32 %{{.*}}, ptr %[[DST]], align 8, !tbaa 
![[TBAA_SCALAR]]{{$}}
+// CHECK: ret void
+
+struct Inner {
+  int a;
+  int b;
+};
+
+struct HasAggregateField {
+  void (* AQ fp)(void);
+  struct Inner f3;
+};
+
+// CHECK-LABEL: define{{.*}} void @copyAggregate(
+// CHECK: call void 
@[[COPY_ASSIGNMENT_AGGREGATE:__copy_assignment[a-zA-Z0-9_]*]](
+void copyAggregate(volatile struct HasAggregateField *dst,
+                    volatile struct HasAggregateField *src) {
+  *dst = *src;
+}
+
+// CHECK: define{{.*}} void @[[COPY_ASSIGNMENT_AGGREGATE]](
+// CHECK: %[[DST:.*]] = getelementptr inbounds nuw %struct.HasAggregateField, 
ptr %{{.*}}, i32 0, i32 1
+// CHECK: %[[SRC:.*]] = getelementptr inbounds nuw %struct.HasAggregateField, 
ptr %{{.*}}, i32 0, i32 1
+// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[DST]], ptr align 8 
%[[SRC]], i64 8, i1 true), !tbaa.struct ![[TBAA_STRUCT:[0-9]+]]{{$}}
+// CHECK: ret void
+
+struct HasComplexField {
+  void (* AQ fp)(void);
+  _Complex double f3;
+};
+
+// CHECK-LABEL: define{{.*}} void @copyComplex(
+// CHECK: call void 
@[[COPY_ASSIGNMENT_COMPLEX:__copy_assignment[a-zA-Z0-9_]*]](
+void copyComplex(volatile struct HasComplexField *dst,
+                  volatile struct HasComplexField *src) {
+  *dst = *src;
+}
+
+// _Complex fields aren't given any TBAA at all.
+// CHECK: define{{.*}} void @[[COPY_ASSIGNMENT_COMPLEX]](
+// CHECK: %[[SRC_REAL:.*]] = load volatile double, ptr %{{.*}}, align 8{{$}}
+// CHECK: %[[SRC_IMAG:.*]] = load volatile double, ptr %{{.*}}, align 8{{$}}
+// CHECK: store volatile double %[[SRC_REAL]], ptr %{{.*}}, align 8{{$}}
+// CHECK: store volatile double %[[SRC_IMAG]], ptr %{{.*}}, align 8{{$}}
+// CHECK: ret void
+
+// CHECK: ![[TBAA_INT:[0-9]+]] = !{!"int", ![[TBAA_CHAR:[0-9]+]], i64 0}
+// CHECK: ![[TBAA_CHAR]] = !{!"omnipotent char", ![[TBAA_DOMAIN:[0-9]+]], i64 
0}
+// CHECK: ![[TBAA_DOMAIN]] = !{!"Simple C/C++ TBAA"}
+// CHECK: ![[TBAA_PTR:[0-9]+]] = !{!"any pointer", ![[TBAA_CHAR]], i64 0}
+// CHECK: ![[TBAA_SCALAR]] = !{![[TBAA_SCALAR_BASE:[0-9]+]], ![[TBAA_INT]], 
i64 8}
+// CHECK: ![[TBAA_SCALAR_BASE]] = !{!"HasScalarField", ![[TBAA_PTR]], i64 0, 
![[TBAA_INT]], i64 8}
+
+// CHECK: ![[TBAA_STRUCT]] = !{i64 0, i64 4, ![[TBAA_STRUCT_TAG:[0-9]+]], i64 
4, i64 4, ![[TBAA_STRUCT_TAG]]}
+// CHECK: ![[TBAA_STRUCT_TAG]] = !{![[TBAA_INT]], ![[TBAA_INT]], i64 0}

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

Reply via email to