[clang] [clang] Ensure correct copying of records with authenticated fields (PR #136783)

Oliver Hunt via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 22 16:06:24 PDT 2025


https://github.com/ojhunt created https://github.com/llvm/llvm-project/pull/136783

When records contain fields with pointer authentication, even simple copies can require
additional work be performed. This patch contains the core functionality required to
handle user defined structs, as well as the implicitly constructed structs for blocks, etc.

Co-authored-by: Ahmed Bougacha
Co-authored-by: Akira Hatanaka
Co-authored-by: John Mccall

>From 7c8c531dcfd516e8559e88bd9cf69b49e3e5c9c8 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Tue, 22 Apr 2025 16:00:08 -0700
Subject: [PATCH] [clang][ptrauth] Ensure correct copying of records with
 authenticated fields

---
 .../include/clang/AST/NonTrivialTypeVisitor.h |   2 +
 clang/include/clang/AST/Type.h                |   3 +
 clang/lib/AST/ASTContext.cpp                  |   3 +
 clang/lib/AST/Type.cpp                        |   6 +
 clang/lib/CodeGen/CGBlocks.cpp                |  56 +++++-
 clang/lib/CodeGen/CGBlocks.h                  |   1 +
 clang/lib/CodeGen/CGCall.cpp                  |   2 +-
 clang/lib/CodeGen/CGNonTrivialStruct.cpp      |  19 ++
 clang/lib/Sema/SemaChecking.cpp               |   3 +
 clang/lib/Sema/SemaDecl.cpp                   |   6 +
 clang/test/CodeGen/ptrauth-in-c-struct.c      | 172 ++++++++++++++++++
 clang/test/CodeGen/ptrauth-qualifier-blocks.c | 107 +++++++++++
 clang/test/Sema/ptrauth-qualifier.c           |  43 +++++
 13 files changed, 421 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/ptrauth-in-c-struct.c
 create mode 100644 clang/test/CodeGen/ptrauth-qualifier-blocks.c

diff --git a/clang/include/clang/AST/NonTrivialTypeVisitor.h b/clang/include/clang/AST/NonTrivialTypeVisitor.h
index cf320c8a478af..c06dfa06eb922 100644
--- a/clang/include/clang/AST/NonTrivialTypeVisitor.h
+++ b/clang/include/clang/AST/NonTrivialTypeVisitor.h
@@ -95,6 +95,8 @@ struct CopiedTypeVisitor {
       return asDerived().visitARCWeak(FT, std::forward<Ts>(Args)...);
     case QualType::PCK_Struct:
       return asDerived().visitStruct(FT, std::forward<Ts>(Args)...);
+    case QualType::PCK_PtrAuth:
+      return asDerived().visitPtrAuth(FT, std::forward<Ts>(Args)...);
     case QualType::PCK_Trivial:
       return asDerived().visitTrivial(FT, std::forward<Ts>(Args)...);
     case QualType::PCK_VolatileTrivial:
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 20ff529c7e0c6..b85fc45cf9562 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1507,6 +1507,9 @@ class QualType {
     /// with the ARC __weak qualifier.
     PCK_ARCWeak,
 
+    /// The type is an address-discriminated signed pointer type.
+    PCK_PtrAuth,
+
     /// The type is a struct containing a field whose type is neither
     /// PCK_Trivial nor PCK_VolatileTrivial.
     /// Note that a C++ struct type does not necessarily match this; C++ copying
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2836d68b05ff6..b1ecd9d63702b 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -8372,6 +8372,9 @@ bool ASTContext::BlockRequiresCopying(QualType Ty,
     return true;
   }
 
+  if (Ty.hasAddressDiscriminatedPointerAuth())
+    return true;
+
   // The block needs copy/destroy helpers if Ty is non-trivial to destructively
   // move or destroy.
   if (Ty.isNonTrivialToPrimitiveDestructiveMove() || Ty.isDestructedType())
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 67cd690af7499..632b3f3a37111 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -2801,6 +2801,10 @@ static bool isTriviallyCopyableTypeImpl(const QualType &type,
   if (type.hasNonTrivialObjCLifetime())
     return false;
 
+  QualType::PrimitiveCopyKind PCK = type.isNonTrivialToPrimitiveCopy();
+  if (PCK != QualType::PCK_Trivial && PCK != QualType::PCK_VolatileTrivial)
+    return false;
+
   // C++11 [basic.types]p9 - See Core 2094
   //   Scalar types, trivially copyable class types, arrays of such types, and
   //   cv-qualified versions of these types are collectively
@@ -2968,6 +2972,8 @@ QualType::PrimitiveCopyKind QualType::isNonTrivialToPrimitiveCopy() const {
   case Qualifiers::OCL_Weak:
     return PCK_ARCWeak;
   default:
+    if (hasAddressDiscriminatedPointerAuth())
+      return PCK_PtrAuth;
     return Qs.hasVolatile() ? PCK_VolatileTrivial : PCK_Trivial;
   }
 }
diff --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp
index faef6a5fbe1f5..ba0d87fdc5d43 100644
--- a/clang/lib/CodeGen/CGBlocks.cpp
+++ b/clang/lib/CodeGen/CGBlocks.cpp
@@ -1591,6 +1591,10 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
     return std::make_pair(BlockCaptureEntityKind::BlockObject, Flags);
   }
 
+  if (T.hasAddressDiscriminatedPointerAuth())
+    return std::make_pair(
+        BlockCaptureEntityKind::AddressDiscriminatedPointerAuth, Flags);
+
   Flags = BLOCK_FIELD_IS_OBJECT;
   bool isBlockPointer = T->isBlockPointerType();
   if (isBlockPointer)
@@ -1611,6 +1615,10 @@ computeCopyInfoForBlockCapture(const BlockDecl::Capture &CI, QualType T,
     return std::make_pair(!isBlockPointer ? BlockCaptureEntityKind::ARCStrong
                                           : BlockCaptureEntityKind::BlockObject,
                           Flags);
+  case QualType::PCK_PtrAuth:
+    return std::make_pair(
+        BlockCaptureEntityKind::AddressDiscriminatedPointerAuth,
+        BlockFieldFlags());
   case QualType::PCK_Trivial:
   case QualType::PCK_VolatileTrivial: {
     if (!T->isObjCRetainableType())
@@ -1713,6 +1721,13 @@ static std::string getBlockCaptureStr(const CGBlockInfo::Capture &Cap,
   case BlockCaptureEntityKind::ARCStrong:
     Str += "s";
     break;
+  case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: {
+    auto PtrAuth = CaptureTy.getPointerAuth();
+    assert(PtrAuth && PtrAuth.isAddressDiscriminated());
+    Str += "p" + llvm::to_string(PtrAuth.getKey()) + "d" +
+           llvm::to_string(PtrAuth.getExtraDiscriminator());
+    break;
+  }
   case BlockCaptureEntityKind::BlockObject: {
     const VarDecl *Var = CI.getVariable();
     unsigned F = Flags.getBitMask();
@@ -1829,6 +1844,7 @@ static void pushCaptureCleanup(BlockCaptureEntityKind CaptureKind,
     }
     break;
   }
+  case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth:
   case BlockCaptureEntityKind::None:
     break;
   }
@@ -1925,6 +1941,14 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) {
     case BlockCaptureEntityKind::ARCWeak:
       EmitARCCopyWeak(dstField, srcField);
       break;
+    case BlockCaptureEntityKind::AddressDiscriminatedPointerAuth: {
+      QualType Type = CI.getVariable()->getType();
+      PointerAuthQualifier PointerAuth = Type.getPointerAuth();
+      assert(PointerAuth && PointerAuth.isAddressDiscriminated());
+      EmitPointerAuthCopy(PointerAuth, Type, dstField, srcField);
+      // We don't need to push cleanups for ptrauth types.
+      continue;
+    }
     case BlockCaptureEntityKind::NonTrivialCStruct: {
       // If this is a C struct that requires non-trivial copy construction,
       // emit a call to its copy constructor.
@@ -2261,6 +2285,33 @@ class CXXByrefHelpers final : public BlockByrefHelpers {
   }
 };
 
+/// Emits the copy/dispose helpers for a __block variable with
+/// address-discriminated pointer authentication.
+class AddressDiscriminatedByrefHelpers final : public BlockByrefHelpers {
+  QualType VarType;
+
+public:
+  AddressDiscriminatedByrefHelpers(CharUnits Alignment, QualType Type)
+      : BlockByrefHelpers(Alignment), VarType(Type) {
+    assert(Type.hasAddressDiscriminatedPointerAuth());
+  }
+
+  void emitCopy(CodeGenFunction &CGF, Address DestField,
+                Address SrcField) override {
+    CGF.EmitPointerAuthCopy(VarType.getPointerAuth(), VarType, DestField,
+                            SrcField);
+  }
+
+  bool needsDispose() const override { return false; }
+  void emitDispose(CodeGenFunction &CGF, Address Field) override {
+    llvm_unreachable("should never be called");
+  }
+
+  void profileImpl(llvm::FoldingSetNodeID &ID) const override {
+    ID.AddPointer(VarType.getCanonicalType().getAsOpaquePtr());
+  }
+};
+
 /// Emits the copy/dispose helpers for a __block variable that is a non-trivial
 /// C struct.
 class NonTrivialCStructByrefHelpers final : public BlockByrefHelpers {
@@ -2462,7 +2513,10 @@ CodeGenFunction::buildByrefHelpers(llvm::StructType &byrefType,
     return ::buildByrefHelpers(
         CGM, byrefInfo, CXXByrefHelpers(valueAlignment, type, copyExpr));
   }
-
+  if (type.hasAddressDiscriminatedPointerAuth()) {
+    return ::buildByrefHelpers(
+        CGM, byrefInfo, AddressDiscriminatedByrefHelpers(valueAlignment, type));
+  }
   // If type is a non-trivial C struct type that is non-trivial to
   // destructly move or destroy, build the copy and dispose helpers.
   if (type.isNonTrivialToPrimitiveDestructiveMove() == QualType::PCK_Struct ||
diff --git a/clang/lib/CodeGen/CGBlocks.h b/clang/lib/CodeGen/CGBlocks.h
index 8d10c4f69b202..b2e850046ab9a 100644
--- a/clang/lib/CodeGen/CGBlocks.h
+++ b/clang/lib/CodeGen/CGBlocks.h
@@ -146,6 +146,7 @@ class BlockByrefInfo {
 enum class BlockCaptureEntityKind {
   None,
   CXXRecord, // Copy or destroy
+  AddressDiscriminatedPointerAuth,
   ARCWeak,
   ARCStrong,
   NonTrivialCStruct,
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 8cb27420dd911..836f34e5b347d 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -4857,7 +4857,7 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
 
   if (HasAggregateEvalKind && isa<ImplicitCastExpr>(E) &&
       cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue &&
-      !type->isArrayParameterType()) {
+      !type->isArrayParameterType() && !type.isNonTrivialToPrimitiveCopy()) {
     LValue L = EmitLValue(cast<CastExpr>(E)->getSubExpr());
     assert(L.isSimple());
     args.addUncopiedAggregate(L, type);
diff --git a/clang/lib/CodeGen/CGNonTrivialStruct.cpp b/clang/lib/CodeGen/CGNonTrivialStruct.cpp
index c634b5c010e2d..e0983ef256e71 100644
--- a/clang/lib/CodeGen/CGNonTrivialStruct.cpp
+++ b/clang/lib/CodeGen/CGNonTrivialStruct.cpp
@@ -266,6 +266,18 @@ struct GenBinaryFuncName : CopyStructVisitor<GenBinaryFuncName<IsMove>, IsMove>,
     this->appendStr("_tv" + llvm::to_string(OffsetInBits) + "w" +
                     llvm::to_string(getFieldSize(FD, FT, this->Ctx)));
   }
+
+  void visitPtrAuth(QualType FT, const FieldDecl *FD,
+                    CharUnits CurStructOffset) {
+    this->appendStr("_pa");
+    PointerAuthQualifier PtrAuth = FT.getPointerAuth().withoutKeyNone();
+    this->appendStr(llvm::to_string(PtrAuth.getKey()) + "_");
+    this->appendStr(llvm::to_string(PtrAuth.getExtraDiscriminator()) + "_");
+    if (PtrAuth.authenticatesNullValues())
+      this->appendStr("anv_");
+    CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD);
+    this->appendStr(llvm::to_string(FieldOffset.getQuantity()));
+  }
 };
 
 struct GenDefaultInitializeFuncName
@@ -568,6 +580,13 @@ struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>,
     RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation());
     this->CGF->EmitStoreThroughLValue(SrcVal, DstLV);
   }
+  void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset,
+                    std::array<Address, 2> Addrs) {
+    PointerAuthQualifier PtrAuth = FT.getPointerAuth().withoutKeyNone();
+    Addrs[DstIdx] = this->getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD);
+    Addrs[SrcIdx] = this->getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD);
+    this->CGF->EmitPointerAuthCopy(PtrAuth, FT, Addrs[DstIdx], Addrs[SrcIdx]);
+  }
 };
 
 // These classes that emit the special functions for a non-trivial struct.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b2b26a2c39cf2..9c9372d9ee2b0 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -9443,6 +9443,9 @@ struct SearchNonTrivialToCopyField
   void visitARCWeak(QualType FT, SourceLocation SL) {
     S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
   }
+  void visitPtrAuth(QualType FT, SourceLocation SL) {
+    S.DiagRuntimeBehavior(SL, E, S.PDiag(diag::note_nontrivial_field) << 0);
+  }
   void visitStruct(QualType FT, SourceLocation SL) {
     for (const FieldDecl *FD : FT->castAs<RecordType>()->getDecl()->fields())
       visit(FD->getType(), FD->getLocation());
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 46933c5c43168..3b42cdc4def46 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13395,6 +13395,12 @@ struct DiagNonTrivalCUnionCopyVisitor
         asDerived().visit(FD->getType(), FD, InNonTrivialUnion);
   }
 
+  void visitPtrAuth(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {
+    if (InNonTrivialUnion)
+      S.Diag(FD->getLocation(), diag::note_non_trivial_c_union)
+          << 1 << 2 << QT << FD->getName();
+  }
+
   void preVisit(QualType::PrimitiveCopyKind PCK, QualType QT,
                 const FieldDecl *FD, bool InNonTrivialUnion) {}
   void visitTrivial(QualType QT, const FieldDecl *FD, bool InNonTrivialUnion) {}
diff --git a/clang/test/CodeGen/ptrauth-in-c-struct.c b/clang/test/CodeGen/ptrauth-in-c-struct.c
new file mode 100644
index 0000000000000..4bd605a842b05
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-in-c-struct.c
@@ -0,0 +1,172 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fblocks -fptrauth-calls -fptrauth-returns -fptrauth-intrinsics -emit-llvm -o - %s | FileCheck %s
+
+#define AQ1_50 __ptrauth(1,1,50)
+#define AQ2_30 __ptrauth(2,1,30)
+#define IQ __ptrauth(1,0,50)
+
+typedef void (^BlockTy)(void);
+
+// CHECK: %[[STRUCT_SA:.*]] = type { i32, ptr }
+// CHECK: %[[STRUCT_SA2:.*]] = type { i32, ptr }
+// CHECK: %[[STRUCT_SI:.*]] = type { ptr }
+
+typedef struct {
+  int f0;
+  int * AQ1_50 f1; // Signed using address discrimination.
+} SA;
+
+typedef struct {
+  int f0;
+  int * AQ2_30 f1; // Signed using address discrimination.
+} SA2;
+
+typedef struct {
+  int * IQ f; // No address discrimination.
+} SI;
+
+SA getSA(void);
+void calleeSA(SA);
+
+int g0;
+
+// CHECK: define void @test_copy_constructor_SA(ptr noundef %{{.*}})
+// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa1_50_8(ptr noundef %[[DST:.*]], ptr noundef %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[DST]], ptr %[[DST_ADDR]], align 8
+// CHECK: store ptr %[[SRC]], ptr %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load ptr, ptr %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load ptr, ptr %[[SRC_ADDR]], align 8
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 8
+// CHECK: %[[V9:.*]] = getelementptr inbounds i8, ptr %[[V1]], i64 8
+// CHECK: %[[V11:.*]] = load ptr, ptr %[[V9]], align 8
+// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V9]] to i64
+// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V12]], i64 50)
+// CHECK: %[[V14:.*]] = ptrtoint ptr %[[V6]] to i64
+// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V14]], i64 50)
+// CHECK: %[[V17:.*]] = ptrtoint ptr %[[V11]] to i64
+// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V17]], i32 1, i64 %[[V13]], i32 1, i64 %[[V15]])
+
+void test_copy_constructor_SA(SA *s) {
+  SA t = *s;
+}
+
+// CHECK: define void @test_copy_constructor_SA2(ptr noundef %{{.*}})
+// CHECK: call void @__copy_constructor_8_8_t0w4_pa2_30_8(
+
+// CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_pa2_30_8(ptr noundef %[[DST:.*]], ptr noundef %[[SRC:.*]])
+// CHECK: %[[DST_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[SRC_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[DST]], ptr %[[DST_ADDR]], align 8
+// CHECK: store ptr %[[SRC]], ptr %[[SRC_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load ptr, ptr %[[DST_ADDR]], align 8
+// CHECK: %[[V1:.*]] = load ptr, ptr %[[SRC_ADDR]], align 8
+// CHECK: %[[V6:.*]] = getelementptr inbounds i8, ptr %[[V0]], i64 8
+// CHECK: %[[V9:.*]] = getelementptr inbounds i8, ptr %[[V1]], i64 8
+// CHECK: %[[V11:.*]] = load ptr, ptr %[[V9]], align 8
+// CHECK: %[[V12:.*]] = ptrtoint ptr %[[V9]] to i64
+// CHECK: %[[V13:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V12]], i64 30)
+// CHECK: %[[V14:.*]] = ptrtoint ptr %[[V6]] to i64
+// CHECK: %[[V15:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V14]], i64 30)
+// CHECK: %[[V17:.*]] = ptrtoint ptr %[[V11]] to i64
+// CHECK: %[[V18:.*]] = call i64 @llvm.ptrauth.resign(i64 %[[V17]], i32 2, i64 %[[V13]], i32 2, i64 %[[V15]])
+
+void test_copy_constructor_SA2(SA2 *s) {
+  SA2 t = *s;
+}
+
+// CHECK: define void @test_copy_assignment_SA(
+// CHECK: call void @__copy_assignment_8_8_t0w4_pa1_50_8(
+
+// CHECK: define linkonce_odr hidden void @__copy_assignment_8_8_t0w4_pa1_50_8(
+
+void test_copy_assignment_SA(SA *d, SA *s) {
+  *d = *s;
+}
+
+// CHECK: define void @test_move_constructor_SA(
+// CHECK: define internal void @__Block_byref_object_copy_(
+// CHECK: define linkonce_odr hidden void @__move_constructor_8_8_t0w4_pa1_50_8(
+
+void test_move_constructor_SA(void) {
+  __block SA t;
+  BlockTy b = ^{ (void)t; };
+}
+
+// CHECK: define void @test_move_assignment_SA(
+// CHECK: call void @__move_assignment_8_8_t0w4_pa1_50_8(
+// CHECK: define linkonce_odr hidden void @__move_assignment_8_8_t0w4_pa1_50_8(
+
+void test_move_assignment_SA(SA *p) {
+  *p = getSA();
+}
+
+// CHECK: define void @test_parameter_SA(ptr noundef %{{.*}})
+// CHECK-NOT: call
+// CHECK: ret void
+
+void test_parameter_SA(SA a) {
+}
+
+// CHECK: define void @test_argument_SA(ptr noundef %[[A:.*]])
+// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8
+// CHECK: %[[AGG_TMP:.*]] = alloca %[[STRUCT_SA]], align 8
+// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8
+// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(ptr %[[AGG_TMP]], ptr %[[V0]])
+// CHECK: call void @calleeSA(ptr noundef %[[AGG_TMP]])
+// CHECK-NOT: call
+// CHECK: ret void
+
+void test_argument_SA(SA *a) {
+  calleeSA(*a);
+}
+
+// CHECK: define void @test_return_SA(ptr dead_on_unwind noalias writable sret(%struct.SA) align 8 %[[AGG_RESULT:.*]], ptr noundef %[[A:.*]])
+// CHECK: %[[A_ADDR:.*]] = alloca ptr, align 8
+// CHECK: store ptr %[[A]], ptr %[[A_ADDR]], align 8
+// CHECK: %[[V0:.*]] = load ptr, ptr %[[A_ADDR]], align 8
+// CHECK: call void @__copy_constructor_8_8_t0w4_pa1_50_8(ptr %[[AGG_RESULT]], ptr %[[V0]])
+// CHECK-NOT: call
+// CHECK: ret void
+
+SA test_return_SA(SA *a) {
+  return *a;
+}
+
+// CHECK: define void @test_copy_constructor_SI(
+// CHECK-NOT: call
+// CHECK: call void @llvm.memcpy.p0.p0.i64(
+// CHECK-NOT: call
+// CHECK: ret void
+
+void test_copy_constructor_SI(SI *s) {
+  SI t = *s;
+}
+
+// CHECK: define void @test_parameter_SI(i64 %{{.*}})
+// CHECK-NOT: call
+// CHECK: ret void
+
+void test_parameter_SI(SI a) {
+}
+
+// CHECK-LABEL: define void @test_array(
+// CHECK: %[[F1:.*]] = getelementptr inbounds nuw %[[STRUCT_SA]], ptr %{{.*}}, i32 0, i32 1
+// CHECK: %[[V0:.*]] = ptrtoint ptr %[[F1]] to i64
+// CHECK: %[[V1:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V0]], i64 50)
+// CHECK: %[[V2:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 %[[V1]])
+// CHECK: %[[V3:.*]] = inttoptr i64 %[[V2]] to ptr
+// CHECK: store ptr %[[V3]], ptr %[[F1]], align 8
+// CHECK: %[[F12:.*]] = getelementptr inbounds nuw %[[STRUCT_SA]], ptr %{{.*}}, i32 0, i32 1
+// CHECK: %[[V4:.*]] = ptrtoint ptr %[[F12]] to i64
+// CHECK: %[[V5:.*]] = call i64 @llvm.ptrauth.blend(i64 %[[V4]], i64 50)
+// CHECK: %[[V6:.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr @g0 to i64), i32 1, i64 %[[V5]])
+// CHECK: %[[V7:.*]] = inttoptr i64 %[[V6]] to ptr
+// CHECK: store ptr %[[V7]], ptr %[[F12]], align 8
+
+void test_array(void) {
+  const SA a[] = {{0, &g0}, {1, &g0}};
+}
diff --git a/clang/test/CodeGen/ptrauth-qualifier-blocks.c b/clang/test/CodeGen/ptrauth-qualifier-blocks.c
new file mode 100644
index 0000000000000..62da59cf327f6
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-qualifier-blocks.c
@@ -0,0 +1,107 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -fblocks -emit-llvm %s  -o - | FileCheck %s
+
+struct A {
+  int value;
+};
+struct A *createA(void);
+
+void use_block(int (^)(void));
+
+// CHECK-LABEL: define void @test_block_nonaddress_capture(
+void test_block_nonaddress_capture() {
+  // CHECK: [[VAR:%.*]] = alloca ptr,
+  // CHECK: [[BLOCK:%.*]] = alloca
+  //   flags - no copy/dispose required
+  // CHECK: store i32 1073741824, ptr
+  // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds {{.*}} [[BLOCK]], i32 0, i32 5
+  // CHECK: [[LOAD:%.*]] = load ptr, ptr [[VAR]],
+  // CHECK: store ptr [[LOAD]], ptr [[CAPTURE]]
+  struct A * __ptrauth(1, 0, 15) ptr = createA();
+  use_block(^{ return ptr->value; });
+}
+// CHECK-LABEL: define internal i32 @__test_block_nonaddress_capture_block_invoke
+// CHECK: call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 1, i64 15)
+
+// CHECK-LABEL: define void @test_block_address_capture(
+void test_block_address_capture() {
+  // CHECK: [[VAR:%.*]] = alloca ptr,
+  // CHECK: [[BLOCK:%.*]] = alloca
+  //   flags - copy/dispose required
+  // CHECK: store i32 1107296256, ptr
+  // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds {{.*}} [[BLOCK]], i32 0, i32 5
+  // CHECK: [[LOAD:%.*]] = load ptr, ptr [[VAR]],
+  // CHECK: [[T0:%.*]] = ptrtoint ptr [[VAR]] to i64
+  // CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30)
+  // CHECK: [[T0:%.*]] = ptrtoint ptr [[CAPTURE]] to i64
+  // CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30)
+  // CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+  // CHECK: br i1 [[T0]]
+  // CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+  // CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+  // CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr
+  // CHECK: [[T0:%.*]] = phi
+  // CHECK: store ptr [[T0]], ptr [[CAPTURE]]
+  struct A * __ptrauth(1, 1, 30) ptr = createA();
+  use_block(^{ return ptr->value; });
+}
+// CHECK-LABEL: define internal i32 @__test_block_address_capture_block_invoke
+// CHECK: call i64 @llvm.ptrauth.auth(i64 {{%.*}}, i32 1, i64 {{%.*}})
+
+// CHECK: linkonce_odr hidden void @__copy_helper_block_8_32p1d30(
+// CHECK: [[OLDSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 5
+// CHECK: [[NEWSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 5
+// CHECK: [[LOAD:%.*]] = load ptr, ptr [[OLDSLOT]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[OLDSLOT]] to i64
+// CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30)
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[NEWSLOT]] to i64
+// CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 30)
+// CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK: br i1 [[T0]]
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK: [[T0:%.*]] = phi
+// CHECK: store ptr [[T0]], ptr [[NEWSLOT]]
+
+// CHECK-LABEL: define void @test_block_nonaddress_byref_capture(
+void test_block_nonaddress_byref_capture() {
+  //   flags - no copy/dispose required for byref
+  // CHECK: store i32 0,
+  // CHECK: call ptr @createA()
+  //   flags - copy/dispose required for block (because it captures byref)
+  // CHECK: store i32 1107296256,
+  __block struct A * __ptrauth(1, 0, 45) ptr = createA();
+  use_block(^{ return ptr->value; });
+}
+
+// CHECK-LABEL: define void @test_block_address_byref_capture(
+void test_block_address_byref_capture() {
+  // CHECK: [[BYREF:%.*]] = alloca [[BYREF_T:.*]], align
+  // CHECK: [[BLOCK:%.*]] = alloca
+  //   flags - byref requires copy/dispose
+  // CHECK: store i32 33554432,
+  // CHECK: store i32 48,
+  // CHECK: [[COPY_HELPER_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 4
+  // CHECK: store ptr @__Block_byref_object_copy_, ptr [[COPY_HELPER_FIELD]], align
+  // CHECK: [[DISPOSE_HELPER_FIELD:%.*]] = getelementptr inbounds nuw [[BYREF_T]], ptr [[BYREF]], i32 0, i32 5
+  // CHECK: store ptr @__Block_byref_object_dispose_, ptr [[DISPOSE_HELPER_FIELD]], align
+  //   flags - copy/dispose required
+  // CHECK: store i32 1107296256, ptr
+  __block struct A * __ptrauth(1, 1, 60) ptr = createA();
+  use_block(^{ return ptr->value; });
+}
+// CHECK-LABEL: define internal void @__Block_byref_object_copy_
+// CHECK: [[NEWSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 6
+// CHECK: [[OLDSLOT:%.*]] = getelementptr inbounds {{.*}} {{.*}}, i32 0, i32 6
+// CHECK: [[LOAD:%.*]] = load ptr, ptr [[OLDSLOT]],
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[OLDSLOT]] to i64
+// CHECK: [[OLDDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 60)
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[NEWSLOT]] to i64
+// CHECK: [[NEWDISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 60)
+// CHECK: [[T0:%.*]] = icmp ne ptr [[LOAD]], null
+// CHECK: br i1 [[T0]]
+// CHECK: [[T0:%.*]] = ptrtoint ptr [[LOAD]] to i64
+// CHECK: [[T1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[T0]], i32 1, i64 [[OLDDISC]], i32 1, i64 [[NEWDISC]])
+// CHECK: [[T2:%.*]] = inttoptr i64 [[T1]] to ptr
+// CHECK: [[T0:%.*]] = phi
+// CHECK: store ptr [[T0]], ptr [[NEWSLOT]]
diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c
index ab12acd8975f4..c6d0ff1c6caf7 100644
--- a/clang/test/Sema/ptrauth-qualifier.c
+++ b/clang/test/Sema/ptrauth-qualifier.c
@@ -103,3 +103,46 @@ static_assert(_Generic(typeof(overload_func(&ptr0)), int : 1, default : 0));
 static_assert(_Generic(typeof(overload_func(&valid0)), float : 1, default : 0));
 
 void func(int array[__ptrauth(VALID_DATA_KEY) 10]); // expected-error {{'__ptrauth' qualifier only applies to pointer types; 'int[10]' is invalid}}
+
+struct S0 { // expected-note 4 {{struct S0' has subobjects that are non-trivial to copy}}
+  intp __ptrauth(1, 1, 50) f0; // expected-note 4 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}}
+};
+
+union U0 { // expected-note 4 {{union U0' has subobjects that are non-trivial to copy}}
+  struct S0 s0;
+};
+
+struct S1 {
+  intp __ptrauth(1, 0, 50) f0;
+};
+
+union U1 {
+  struct S1 s1;
+};
+
+union U2 { // expected-note 2 {{union U2' has subobjects that are non-trivial to copy}}
+  intp __ptrauth(1, 1, 50) f0; // expected-note 2 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}}
+  intp __ptrauth(1, 0, 50) f1;
+};
+
+// Test for r353556.
+struct S2 { // expected-note 2 {{struct S2' has subobjects that are non-trivial to copy}}
+  intp __ptrauth(1, 1, 50) f0[4]; // expected-note 2 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}}
+};
+
+union U3 { // expected-note 2 {{union U3' has subobjects that are non-trivial to copy}}
+  struct S2 s2;
+};
+
+struct S4 {
+  union U0 u0;
+};
+
+union U0 foo0(union U0); // expected-error {{cannot use type 'union U0' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U0' for a function/method parameter since it is a union that is non-trivial to copy}}
+union U1 foo1(union U1);
+
+union U2 foo2(union U2); // expected-error {{cannot use type 'union U2' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U2' for a function/method parameter since it is a union that is non-trivial to copy}}
+
+union U3 foo3(union U3); // expected-error {{cannot use type 'union U3' for function/method return since it is a union that is non-trivial to copy}} expected-error {{cannot use type 'union U3' for a function/method parameter since it is a union that is non-trivial to copy}}
+
+struct S4 foo4(struct S4);  // expected-error {{cannot use type 'struct S4' for function/method return since it contains a union that is non-trivial to copy}} expected-error {{cannot use type 'struct S4' for a function/method parameter since it contains a union that is non-trivial to copy}}



More information about the cfe-commits mailing list