Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 24, 2025

Conversation

ojhunt
Copy link
Contributor

@ojhunt ojhunt commented Apr 22, 2025

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

@ojhunt ojhunt requested a review from ahatanak April 22, 2025 23:06
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. labels Apr 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 22, 2025

@llvm/pr-subscribers-clang

Author: Oliver Hunt (ojhunt)

Changes

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


Patch is 26.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/136783.diff

13 Files Affected:

  • (modified) clang/include/clang/AST/NonTrivialTypeVisitor.h (+2)
  • (modified) clang/include/clang/AST/Type.h (+3)
  • (modified) clang/lib/AST/ASTContext.cpp (+3)
  • (modified) clang/lib/AST/Type.cpp (+6)
  • (modified) clang/lib/CodeGen/CGBlocks.cpp (+55-1)
  • (modified) clang/lib/CodeGen/CGBlocks.h (+1)
  • (modified) clang/lib/CodeGen/CGCall.cpp (+1-1)
  • (modified) clang/lib/CodeGen/CGNonTrivialStruct.cpp (+19)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+3)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+6)
  • (added) clang/test/CodeGen/ptrauth-in-c-struct.c (+172)
  • (added) clang/test/CodeGen/ptrauth-qualifier-blocks.c (+107)
  • (modified) clang/test/Sema/ptrauth-qualifier.c (+43)
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]]
+  // CHEC...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Apr 22, 2025

@llvm/pr-subscribers-clang-codegen

Author: Oliver Hunt (ojhunt)

Changes

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


Patch is 26.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/136783.diff

13 Files Affected:

  • (modified) clang/include/clang/AST/NonTrivialTypeVisitor.h (+2)
  • (modified) clang/include/clang/AST/Type.h (+3)
  • (modified) clang/lib/AST/ASTContext.cpp (+3)
  • (modified) clang/lib/AST/Type.cpp (+6)
  • (modified) clang/lib/CodeGen/CGBlocks.cpp (+55-1)
  • (modified) clang/lib/CodeGen/CGBlocks.h (+1)
  • (modified) clang/lib/CodeGen/CGCall.cpp (+1-1)
  • (modified) clang/lib/CodeGen/CGNonTrivialStruct.cpp (+19)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+3)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+6)
  • (added) clang/test/CodeGen/ptrauth-in-c-struct.c (+172)
  • (added) clang/test/CodeGen/ptrauth-qualifier-blocks.c (+107)
  • (modified) clang/test/Sema/ptrauth-qualifier.c (+43)
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]]
+  // CHEC...
[truncated]

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to do anything special for constant expression handling?

@ojhunt
Copy link
Contributor Author

ojhunt commented Apr 23, 2025

Do we need to do anything special for constant expression handling?

I do not believe so as pointer auth does not occur during consteval, but I'll see if we have any additional existing tests that I haven't included here. If not I'll add some new ones:

  • Type checking (e.g. pointer auth is a no op in constexpr, but void*__ptrauth(a)* is not the same as void*__ptrauth(b)*)
  • constexpr struct copied into another struct should have correct signing

Copy link
Collaborator

@zmodem zmodem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable to my non-expert eyes

@@ -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:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style nit: looks like the cases were sorted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed locally

intp __ptrauth(1, 0, 50) f1;
};

// Test for r353556.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in the svn revision? Maybe use the git hash instead :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmmm, I forget how old the pointer auth code is :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@ojhunt
Copy link
Contributor Author

ojhunt commented Apr 24, 2025

(just waiting on build bots)

@ojhunt ojhunt merged commit 5b16941 into llvm:main Apr 24, 2025
11 checks passed
@ojhunt ojhunt deleted the users/ojhunt/ptrauth-struct-copying branch April 24, 2025 23:22
@llvm-ci
Copy link
Collaborator

llvm-ci commented Apr 24, 2025

LLVM Buildbot has detected a new failure on builder lldb-aarch64-ubuntu running on linaro-lldb-aarch64-ubuntu while building clang at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/59/builds/16608

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteAttach.py (1205 of 2127)
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteCompletion.py (1206 of 2127)
UNSUPPORTED: lldb-api :: tools/lldb-server/TestGdbRemoteForkNonStop.py (1207 of 2127)
UNSUPPORTED: lldb-api :: tools/lldb-server/TestGdbRemoteForkResume.py (1208 of 2127)
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteExitCode.py (1209 of 2127)
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteHostInfo.py (1210 of 2127)
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteModuleInfo.py (1211 of 2127)
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteAuxvSupport.py (1212 of 2127)
PASS: lldb-api :: tools/lldb-server/TestGdbRemoteKill.py (1213 of 2127)
UNRESOLVED: lldb-api :: tools/lldb-dap/variables/TestDAP_variables.py (1214 of 2127)
******************** TEST 'lldb-api :: tools/lldb-dap/variables/TestDAP_variables.py' FAILED ********************
Script:
--
/usr/bin/python3.10 /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --arch aarch64 --build-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/lldb --compiler /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/clang --dsymutil /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --lldb-obj-root /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb --lldb-libs-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/tools/lldb-dap/variables -p TestDAP_variables.py
--
Exit Code: 1

Command Output (stdout):
--
lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision 5b16941f5707c3a326b688e0954f8882d8a36951)
  clang revision 5b16941f5707c3a326b688e0954f8882d8a36951
  llvm revision 5b16941f5707c3a326b688e0954f8882d8a36951
Skipping the following test categories: ['libc++', 'dsym', 'gmodules', 'debugserver', 'objc']

--
Command Output (stderr):
--
UNSUPPORTED: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_darwin_dwarf_missing_obj (TestDAP_variables.TestDAP_variables) (requires one of macosx, darwin, ios, tvos, watchos, bridgeos, iphonesimulator, watchsimulator, appletvsimulator) 
UNSUPPORTED: LLDB (/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/bin/clang-aarch64) :: test_darwin_dwarf_missing_obj_with_symbol_ondemand_enabled (TestDAP_variables.TestDAP_variables) (requires one of macosx, darwin, ios, tvos, watchos, bridgeos, iphonesimulator, watchsimulator, appletvsimulator) 
========= DEBUG ADAPTER PROTOCOL LOGS =========
1745539075.668209076 --> (stdin/stdout) {"command":"initialize","type":"request","arguments":{"adapterID":"lldb-native","clientID":"vscode","columnsStartAt1":true,"linesStartAt1":true,"locale":"en-us","pathFormat":"path","supportsRunInTerminalRequest":true,"supportsVariablePaging":true,"supportsVariableType":true,"supportsStartDebuggingRequest":true,"supportsProgressReporting":true,"$__lldb_sourceInitFile":false},"seq":1}
1745539075.670319319 <-- (stdin/stdout) {"body":{"$__lldb_version":"lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision 5b16941f5707c3a326b688e0954f8882d8a36951)\n  clang revision 5b16941f5707c3a326b688e0954f8882d8a36951\n  llvm revision 5b16941f5707c3a326b688e0954f8882d8a36951","completionTriggerCharacters":["."," ","\t"],"exceptionBreakpointFilters":[{"default":false,"filter":"cpp_catch","label":"C++ Catch"},{"default":false,"filter":"cpp_throw","label":"C++ Throw"},{"default":false,"filter":"objc_catch","label":"Objective-C Catch"},{"default":false,"filter":"objc_throw","label":"Objective-C Throw"}],"supportTerminateDebuggee":true,"supportsBreakpointLocationsRequest":true,"supportsCancelRequest":true,"supportsCompletionsRequest":true,"supportsConditionalBreakpoints":true,"supportsConfigurationDoneRequest":true,"supportsDataBreakpoints":true,"supportsDelayedStackTraceLoading":true,"supportsDisassembleRequest":true,"supportsEvaluateForHovers":true,"supportsExceptionInfoRequest":true,"supportsExceptionOptions":true,"supportsFunctionBreakpoints":true,"supportsHitConditionalBreakpoints":true,"supportsInstructionBreakpoints":true,"supportsLogPoints":true,"supportsModulesRequest":true,"supportsReadMemoryRequest":true,"supportsRestartRequest":true,"supportsSetVariable":true,"supportsStepInTargetsRequest":true,"supportsSteppingGranularity":true,"supportsValueFormattingOptions":true},"command":"initialize","request_seq":1,"seq":0,"success":true,"type":"response"}
1745539075.670538902 --> (stdin/stdout) {"command":"launch","type":"request","arguments":{"program":"/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/tools/lldb-dap/variables/TestDAP_variables.test_indexedVariables/a.out","initCommands":["settings clear -all","settings set symbols.enable-external-lookup false","settings set target.inherit-tcc true","settings set target.disable-aslr false","settings set target.detach-on-error false","settings set target.auto-apply-fixits false","settings set plugin.process.gdb-remote.packet-timeout 60","settings set symbols.clang-modules-cache-path \"/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api\"","settings set use-color false","settings set show-statusline false"],"disableASLR":false,"enableAutoVariableSummaries":false,"enableSyntheticChildDebugging":false,"displayExtendedBacktrace":false,"commandEscapePrefix":null},"seq":2}
1745539075.670727253 <-- (stdin/stdout) {"body":{"category":"console","output":"Running initCommands:\n"},"event":"output","seq":0,"type":"event"}
1745539075.670748234 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings clear -all\n"},"event":"output","seq":0,"type":"event"}
1745539075.670757771 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set symbols.enable-external-lookup false\n"},"event":"output","seq":0,"type":"event"}
1745539075.670766354 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.inherit-tcc true\n"},"event":"output","seq":0,"type":"event"}
1745539075.670774221 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.disable-aslr false\n"},"event":"output","seq":0,"type":"event"}
1745539075.670782089 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.detach-on-error false\n"},"event":"output","seq":0,"type":"event"}
1745539075.670790434 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set target.auto-apply-fixits false\n"},"event":"output","seq":0,"type":"event"}
1745539075.670798063 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set plugin.process.gdb-remote.packet-timeout 60\n"},"event":"output","seq":0,"type":"event"}
1745539075.670817375 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set symbols.clang-modules-cache-path \"/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api\"\n"},"event":"output","seq":0,"type":"event"}
1745539075.670825243 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set use-color false\n"},"event":"output","seq":0,"type":"event"}
1745539075.670833111 <-- (stdin/stdout) {"body":{"category":"console","output":"(lldb) settings set show-statusline false\n"},"event":"output","seq":0,"type":"event"}
1745539075.746955633 <-- (stdin/stdout) {"command":"launch","request_seq":2,"seq":0,"success":true,"type":"response"}
1745539075.747012854 <-- (stdin/stdout) {"body":{"isLocalProcess":true,"name":"/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/tools/lldb-dap/variables/TestDAP_variables.test_indexedVariables/a.out","startMethod":"launch","systemProcessId":275162},"event":"process","seq":0,"type":"event"}
1745539075.747023106 <-- (stdin/stdout) {"event":"initialized","seq":0,"type":"event"}
1745539075.747336626 --> (stdin/stdout) {"command":"setBreakpoints","type":"request","arguments":{"source":{"name":"main.cpp","path":"main.cpp"},"sourceModified":false,"lines":[40],"breakpoints":[{"line":40}]},"seq":3}
1745539075.748753309 <-- (stdin/stdout) {"body":{"breakpoints":[{"column":1,"id":1,"instructionReference":"0xAAAACC7D0C54","line":41,"source":{"name":"main.cpp","path":"main.cpp"},"verified":true}]},"command":"setBreakpoints","request_seq":3,"seq":0,"success":true,"type":"response"}

IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…lvm#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
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…lvm#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
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…lvm#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
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
…lvm#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants