https://github.com/pcc updated https://github.com/llvm/llvm-project/pull/133538

>From e816ed160ed53ff8d9d9039b778c41ecad8a7da2 Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <p...@google.com>
Date: Wed, 6 Aug 2025 17:12:25 -0700
Subject: [PATCH 1/2] Add tests and documentation

Created using spr 1.3.6-beta.1
---
 clang/docs/StructureProtection.rst       |  32 +++-
 clang/docs/index.rst                     |   1 +
 clang/lib/CodeGen/CGCall.cpp             |  21 ++-
 clang/test/CodeGenCXX/pfp-coerce.cpp     | 220 +++++++++++++++++++++++
 clang/test/CodeGenCXX/pfp-null-init.cpp  |  13 +-
 clang/test/CodeGenCXX/pfp-struct-gep.cpp |  29 ++-
 6 files changed, 296 insertions(+), 20 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/pfp-coerce.cpp

diff --git a/clang/docs/StructureProtection.rst 
b/clang/docs/StructureProtection.rst
index 06187f270d49a..6db01cc579b8e 100644
--- a/clang/docs/StructureProtection.rst
+++ b/clang/docs/StructureProtection.rst
@@ -9,11 +9,12 @@ Structure Protection
 Introduction
 ============
 
-Structure protection is an experimental mitigation against use-after-free
-vulnerabilities. For more details, please see the original `RFC
+Structure protection is an *experimental* mitigation
+against use-after-free vulnerabilities. For
+more information, please see the original `RFC
 
<https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555>`_.
-An independent set of documentation will be added here when the feature
-is promoted to non-experimental.
+An independent set of documentation will be contributed when the feature
+is promoted to stable.
 
 Usage
 =====
@@ -24,9 +25,30 @@ To use structure protection, build your program using one of 
the flags:
   field protection with untagged pointers.
 
 - ``-fexperimental-pointer-field-protection=tagged``: Enable pointer
-  field protection with heap pointers assumed to be tagged by the allocator:
+  field protection with heap pointers assumed to be tagged by the allocator.
 
 The entire C++ part of the program must be built with a consistent
 ``-fexperimental-pointer-field-protection`` flag, and the C++ standard
 library must also be built with the same flag and statically linked into
 the program.
+
+To build libc++ with pointer field protection support, pass the following
+CMake flags:
+
+.. code-block:: console
+ 
+    "-DRUNTIMES_${triple}_LIBCXXABI_ENABLE_SHARED=OFF" \
+    "-DRUNTIMES_${triple}_LIBCXX_USE_COMPILER_RT=ON" \
+    "-DRUNTIMES_${triple}_LIBCXX_PFP=untagged" \
+    "-DRUNTIMES_${triple}_LIBCXX_ENABLE_SHARED=OFF" \
+    "-DRUNTIMES_${triple}_LIBCXX_TEST_CONFIG=llvm-libc++-static.cfg.in" \
+    "-DRUNTIMES_${triple}_LIBUNWIND_ENABLE_SHARED=OFF" \
+
+where ``${triple}`` is your target triple, such as
+``aarch64-unknown-linux``.
+
+The resulting toolchain may then be used to build programs
+with pointer field protection by passing ``-stdlib=libc++
+-fexperimental-pointer-field-protection=untagged`` at compile time
+and ``-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic -lm -fuse-ld=lld
+-static-libstdc++`` at link time.
diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index 4871d05e932ae..e267c66af1e1d 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -47,6 +47,7 @@ Using Clang as a Compiler
    LTOVisibility
    SafeStack
    ShadowCallStack
+   StructureProtection
    SourceBasedCodeCoverage
    StandardCPlusPlusModules
    Modules
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index f7fca42cd7a07..c26d51c28f305 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -3538,6 +3538,13 @@ void CodeGenFunction::EmitFunctionProlog(const 
CGFunctionInfo &FI,
           if (SrcSize > DstSize) {
             Builder.CreateMemCpy(Ptr, AddrToStoreInto, DstSize);
           }
+
+          // Structures with PFP fields require a coerced store to add any
+          // pointer signatures.
+          if (getContext().hasPFPFields(Ty)) {
+            llvm::Value *Struct = Builder.CreateLoad(Ptr);
+            CreatePFPCoercedStore(Struct, Ty, Ptr, *this);
+          }
         }
       } else {
         // Simple case, just do a coerced store of the argument into the 
alloca.
@@ -5717,15 +5724,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo 
&CallInfo,
         } else {
           uint64_t SrcSize = SrcTypeSize.getFixedValue();
           uint64_t DstSize = DstTypeSize.getFixedValue();
+          bool HasPFPFields = getContext().hasPFPFields(I->Ty);
 
           // If the source type is smaller than the destination type of the
           // coerce-to logic, copy the source value into a temp alloca the size
           // of the destination type to allow loading all of it. The bits past
           // the source value are left undef.
-          if (SrcSize < DstSize) {
+          if (HasPFPFields || SrcSize < DstSize) {
             Address TempAlloca = CreateTempAlloca(STy, Src.getAlignment(),
                                                   Src.getName() + ".coerce");
-            Builder.CreateMemCpy(TempAlloca, Src, SrcSize);
+            if (HasPFPFields) {
+              // Structures with PFP fields require a coerced load to remove 
any
+              // pointer signatures.
+              Builder.CreateStore(
+                  CreatePFPCoercedLoad(Src, I->Ty, ArgInfo.getCoerceToType(),
+                                       *this),
+                  TempAlloca);
+            } else {
+              Builder.CreateMemCpy(TempAlloca, Src, SrcSize);
+            }
             Src = TempAlloca;
           } else {
             Src = Src.withElementType(STy);
diff --git a/clang/test/CodeGenCXX/pfp-coerce.cpp 
b/clang/test/CodeGenCXX/pfp-coerce.cpp
new file mode 100644
index 0000000000000..db53f2bf45c70
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-coerce.cpp
@@ -0,0 +1,220 @@
+// RUN: %clang_cc1 -triple aarch64-linux 
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck 
--check-prefixes=CHECK,AARCH64 %s
+// RUN: %clang_cc1 -triple x86_64-linux  
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck 
--check-prefixes=CHECK,X86_64 %s
+
+// Non-standard layout. Pointer fields are signed and discriminated by type.
+struct Pointer {
+  int* ptr;
+private:
+  int private_data;
+};
+
+void pass_pointer_callee(Pointer p);
+
+// CHECK: define dso_local void @_Z12pass_pointerP7Pointer(
+void pass_pointer(Pointer *pp) {
+  // CHECK: %0 = load ptr, ptr %pp.addr, align 8
+  // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 
%0, i64 16, i1 false)
+  // CHECK: %1 = getelementptr inbounds i8, ptr %agg.tmp, i64 0
+
+  // AARCH64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 36403, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // X86_64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 51, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+
+  // CHECK: %3 = load ptr, ptr %2, align 8
+
+  // AARCH64: %4 = ptrtoint ptr %3 to i64
+  // AARCH64: %5 = insertvalue [2 x i64] poison, i64 %4, 0
+  // AARCH64: %6 = getelementptr inbounds i8, ptr %agg.tmp, i64 8
+  // AARCH64: %7 = load i64, ptr %6, align 8
+  // AARCH64: %8 = insertvalue [2 x i64] %5, i64 %7, 1
+  // AARCH64: call void @_Z19pass_pointer_callee7Pointer([2 x i64] %8)
+
+  // X86_64: %4 = insertvalue { ptr, i32 } poison, ptr %3, 0
+  // X86_64: %5 = getelementptr inbounds i8, ptr %agg.tmp, i64 8
+  // X86_64: %6 = load i32, ptr %5, align 8
+  // X86_64: %7 = insertvalue { ptr, i32 } %4, i32 %6, 1
+  // X86_64: store { ptr, i32 } %7, ptr %agg.tmp.coerce, align 8
+  // X86_64: %8 = getelementptr inbounds nuw { ptr, i32 }, ptr 
%agg.tmp.coerce, i32 0, i32 0
+  // X86_64: %9 = load ptr, ptr %8, align 8
+  // X86_64: %10 = getelementptr inbounds nuw { ptr, i32 }, ptr 
%agg.tmp.coerce, i32 0, i32 1
+  // X86_64: %11 = load i32, ptr %10, align 8
+  // X86_64: call void @_Z19pass_pointer_callee7Pointer(ptr %9, i32 %11)
+  pass_pointer_callee(*pp);
+}
+
+// AARCH64: define dso_local void @_Z14passed_pointer7PointerPS_([2 x i64] 
%p.coerce, ptr noundef %pp)
+// X86_64: define dso_local void @_Z14passed_pointer7PointerPS_(ptr 
%p.coerce0, i32 %p.coerce1, ptr noundef %pp)
+void passed_pointer(Pointer p, Pointer *pp) {
+  // AARCH64: %p = alloca %struct.Pointer, align 8
+  // AARCH64: %pp.addr = alloca ptr, align 8
+  // AARCH64: %0 = extractvalue [2 x i64] %p.coerce, 0
+  // AARCH64: %1 = getelementptr inbounds i8, ptr %p, i64 0
+  // AARCH64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 36403, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // AARCH64: %3 = inttoptr i64 %0 to ptr
+  // AARCH64: store ptr %3, ptr %2, align 8
+  // AARCH64: %4 = extractvalue [2 x i64] %p.coerce, 1
+  // AARCH64: %5 = getelementptr inbounds i8, ptr %p, i64 8
+  // AARCH64: store i64 %4, ptr %5, align 8
+  // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+  // AARCH64: %6 = load ptr, ptr %pp.addr, align 8
+  // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %p, 
i64 12, i1 false)
+
+  // X86_64: %p = alloca %struct.Pointer, align 8
+  // X86_64: %pp.addr = alloca ptr, align 8
+  // X86_64: %0 = getelementptr inbounds nuw { ptr, i32 }, ptr %p, i32 0, i32 0
+  // X86_64: store ptr %p.coerce0, ptr %0, align 8
+  // X86_64: %1 = getelementptr inbounds nuw { ptr, i32 }, ptr %p, i32 0, i32 1
+  // X86_64: store i32 %p.coerce1, ptr %1, align 8
+  // X86_64: %2 = load %struct.Pointer, ptr %p, align 8
+  // X86_64: %3 = extractvalue %struct.Pointer %2, 0
+  // X86_64: %4 = getelementptr inbounds i8, ptr %p, i64 0
+  // X86_64: %5 = call ptr @llvm.protected.field.ptr(ptr %4, i64 51, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // X86_64: store ptr %3, ptr %5, align 8
+  // X86_64: %6 = extractvalue %struct.Pointer %2, 1
+  // X86_64: %7 = getelementptr inbounds i8, ptr %p, i64 8
+  // X86_64: store i32 %6, ptr %7, align 8
+  // X86_64: %8 = extractvalue %struct.Pointer %2, 2
+  // X86_64: %9 = getelementptr inbounds i8, ptr %p, i64 12
+  // X86_64: store [4 x i8] %8, ptr %9, align 4
+  // X86_64: store ptr %pp, ptr %pp.addr, align 8
+  // X86_64: %10 = load ptr, ptr %pp.addr, align 8
+  // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %10, ptr align 8 %p, 
i64 12, i1 false)
+  *pp = p;
+}
+
+// AARCH64: define dso_local [2 x i64] @_Z14return_pointerP7Pointer(ptr 
noundef %pp)
+// X86_64: define dso_local { ptr, i32 } @_Z14return_pointerP7Pointer(ptr 
noundef %pp)
+Pointer return_pointer(Pointer *pp) {
+  // AARCH64: %retval = alloca %struct.Pointer, align 8
+  // AARCH64: %pp.addr = alloca ptr, align 8
+  // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+  // AARCH64: %0 = load ptr, ptr %pp.addr, align 8
+  // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 
8 %0, i64 16, i1 false)
+  // AARCH64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+  // AARCH64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 36403, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // AARCH64: %3 = load ptr, ptr %2, align 8
+  // AARCH64: %4 = ptrtoint ptr %3 to i64
+  // AARCH64: %5 = insertvalue [2 x i64] poison, i64 %4, 0
+  // AARCH64: %6 = getelementptr inbounds i8, ptr %retval, i64 8
+  // AARCH64: %7 = load i64, ptr %6, align 8
+  // AARCH64: %8 = insertvalue [2 x i64] %5, i64 %7, 1
+  // AARCH64: ret [2 x i64] %8
+
+  // X86_64: %retval = alloca %struct.Pointer, align 8
+  // X86_64: %pp.addr = alloca ptr, align 8
+  // X86_64: store ptr %pp, ptr %pp.addr, align 8
+  // X86_64: %0 = load ptr, ptr %pp.addr, align 8
+  // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 
%0, i64 16, i1 false)
+  // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+  // X86_64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 51, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // X86_64: %3 = load ptr, ptr %2, align 8
+  // X86_64: %4 = insertvalue { ptr, i32 } poison, ptr %3, 0
+  // X86_64: %5 = getelementptr inbounds i8, ptr %retval, i64 8
+  // X86_64: %6 = load i32, ptr %5, align 8
+  // X86_64: %7 = insertvalue { ptr, i32 } %4, i32 %6, 1
+  // X86_64: ret { ptr, i32 } %7
+  return *pp;
+}
+
+Pointer returned_pointer_callee();
+
+// CHECK: define dso_local void @_Z16returned_pointerP7Pointer(ptr noundef %pp)
+void returned_pointer(Pointer *pp) {
+  // AARCH64: %pp.addr = alloca ptr, align 8
+  // AARCH64: %ref.tmp = alloca %struct.Pointer, align 8
+  // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+  // AARCH64: %call = call [2 x i64] @_Z23returned_pointer_calleev()
+  // AARCH64: %0 = extractvalue [2 x i64] %call, 0
+  // AARCH64: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0
+  // AARCH64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 36403, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // AARCH64: %3 = inttoptr i64 %0 to ptr
+  // AARCH64: store ptr %3, ptr %2, align 8
+  // AARCH64: %4 = extractvalue [2 x i64] %call, 1
+  // AARCH64: %5 = getelementptr inbounds i8, ptr %ref.tmp, i64 8
+  // AARCH64: store i64 %4, ptr %5, align 8
+  // AARCH64: %6 = load ptr, ptr %pp.addr, align 8
+  // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 
%ref.tmp, i64 12, i1 false)
+
+  // X86_64: %pp.addr = alloca ptr, align 8
+  // X86_64: %ref.tmp = alloca %struct.Pointer, align 8
+  // X86_64: store ptr %pp, ptr %pp.addr, align 8
+  // X86_64: %call = call { ptr, i32 } @_Z23returned_pointer_calleev()
+  // X86_64: %0 = extractvalue { ptr, i32 } %call, 0
+  // X86_64: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0
+  // X86_64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 51, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+  // X86_64: store ptr %0, ptr %2, align 8
+  // X86_64: %3 = extractvalue { ptr, i32 } %call, 1
+  // X86_64: %4 = getelementptr inbounds i8, ptr %ref.tmp, i64 8
+  // X86_64: store i32 %3, ptr %4, align 8
+  // X86_64: %5 = load ptr, ptr %pp.addr, align 8
+  // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %5, ptr align 8 
%ref.tmp, i64 12, i1 false)
+  *pp = returned_pointer_callee();
+}
+
+class __force_nonstandard_layout_base1 {};
+class __force_nonstandard_layout_base2 : __force_nonstandard_layout_base1 {};
+class __force_nonstandard_layout : __force_nonstandard_layout_base1, 
__force_nonstandard_layout_base2 {};
+
+// Non-standard layout, non-trivially destructible.
+// Pointer fields are signed and discriminated by address.
+// Trivial ABI: passed and returned by value despite being non-trivial.
+struct [[clang::trivial_abi]] TrivialAbiPointer : __force_nonstandard_layout {
+  int *ptr;
+  ~TrivialAbiPointer();
+};
+
+// AARCH64: define dso_local void 
@_Z24pass_trivial_abi_pointer17TrivialAbiPointerPS_(i64 %p.coerce, ptr noundef 
%pp)
+// X86_64: define dso_local void 
@_Z24pass_trivial_abi_pointer17TrivialAbiPointerPS_(ptr %p.coerce, ptr noundef 
%pp)
+void pass_trivial_abi_pointer(TrivialAbiPointer p, TrivialAbiPointer *pp) {
+  // AARCH64: %p = alloca %struct.TrivialAbiPointer, align 8
+  // AARCH64: %pp.addr = alloca ptr, align 8
+  // AARCH64: %coerce.dive = getelementptr inbounds nuw 
%struct.TrivialAbiPointer, ptr %p, i32 0, i32 0
+  // AARCH64: %0 = inttoptr i64 %p.coerce to ptr
+  // AARCH64: %1 = getelementptr inbounds i8, ptr %coerce.dive, i64 0
+  // AARCH64: %2 = ptrtoint ptr %coerce.dive to i64
+  // AARCH64: %3 = call ptr @llvm.protected.field.ptr(ptr %1, i64 %2, i1 true) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+  // AARCH64: store ptr %0, ptr %3, align 8
+  // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+  // AARCH64: %4 = load ptr, ptr %pp.addr, align 8
+  // AARCH64: %call = call noundef nonnull align 8 dereferenceable(8) ptr 
@_ZN17TrivialAbiPointeraSERKS_(ptr noundef nonnull align 8 dereferenceable(8) 
%4, ptr noundef nonnull align 8 dereferenceable(8) %p)
+  // AARCH64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 
8 dereferenceable(8) %p)
+
+  // X86_64: %p = alloca %struct.TrivialAbiPointer, align 8
+  // X86_64: %pp.addr = alloca ptr, align 8
+  // X86_64: %coerce.dive = getelementptr inbounds nuw 
%struct.TrivialAbiPointer, ptr %p, i32 0, i32 0
+  // X86_64: %0 = getelementptr inbounds i8, ptr %coerce.dive, i64 0
+  // X86_64: %1 = call ptr @llvm.protected.field.ptr(ptr %0, i64 33, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+  // X86_64: store ptr %p.coerce, ptr %1, align 8
+  // X86_64: store ptr %pp, ptr %pp.addr, align 8
+  // X86_64: %2 = load ptr, ptr %pp.addr, align 8
+  // X86_64: %call = call noundef nonnull align 8 dereferenceable(8) ptr 
@_ZN17TrivialAbiPointeraSERKS_(ptr noundef nonnull align 8 dereferenceable(8) 
%2, ptr noundef nonnull align 8 dereferenceable(8) %p)
+  // X86_64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 
dereferenceable(8) %p)
+  *pp = p;
+}
+
+// AARCH64: define dso_local i64 
@_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp)
+// X86_64: define dso_local ptr 
@_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp)
+TrivialAbiPointer return_trivial_abi_pointer(TrivialAbiPointer *pp) {
+  // AARCH64: %retval = alloca %struct.TrivialAbiPointer, align 8
+  // AARCH64: %pp.addr = alloca ptr, align 8
+  // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+  // AARCH64: %0 = load ptr, ptr %pp.addr, align 8
+  // AARCH64: call void @_ZN17TrivialAbiPointerC1ERKS_(ptr noundef nonnull 
align 8 dereferenceable(8) %retval, ptr noundef nonnull align 8 
dereferenceable(8) %0)
+  // AARCH64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+  // AARCH64: %2 = ptrtoint ptr %retval to i64
+  // AARCH64: %3 = call ptr @llvm.protected.field.ptr(ptr %1, i64 %2, i1 true) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+  // AARCH64: %4 = load ptr, ptr %3, align 8
+  // AARCH64: %5 = ptrtoint ptr %4 to i64
+  // AARCH64: ret i64 %5
+
+  // X86_64: %retval = alloca %struct.TrivialAbiPointer, align 8
+  // X86_64: %pp.addr = alloca ptr, align 8
+  // X86_64: store ptr %pp, ptr %pp.addr, align 8
+  // X86_64: %0 = load ptr, ptr %pp.addr, align 8
+  // X86_64: call void @_ZN17TrivialAbiPointerC1ERKS_(ptr noundef nonnull 
align 8 dereferenceable(8) %retval, ptr noundef nonnull align 8 
dereferenceable(8) %0)
+  // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+  // X86_64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 33, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+  // X86_64: %3 = load ptr, ptr %2, align 8
+  // X86_64: ret ptr %3
+  return *pp;
+}
+
diff --git a/clang/test/CodeGenCXX/pfp-null-init.cpp 
b/clang/test/CodeGenCXX/pfp-null-init.cpp
index 94c1224f1be13..f0a9152c63ae7 100644
--- a/clang/test/CodeGenCXX/pfp-null-init.cpp
+++ b/clang/test/CodeGenCXX/pfp-null-init.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1  -fexperimental-pointer-field-protection=tagged -emit-llvm 
-o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux 
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck 
--check-prefixes=CHECK,AARCH64 %s
+// RUN: %clang_cc1 -triple x86_64-linux  
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck 
--check-prefixes=CHECK,X86_64 %s
 
 struct S {
   void *p;
@@ -8,9 +9,13 @@ struct S {
 
 // CHECK-LABEL: null_init
 void null_init() {
+  // Check that null initialization was correctly applied to the pointer field.
+  // CHECK: %s = alloca %struct.S, align 8
+  // CHECK: call void @llvm.memset.p0.i64(ptr align 8 %s, i8 0, i64 16, i1 
false)
+  // CHECK: %0 = getelementptr inbounds i8, ptr %s, i64 0
+  // AARCH64: %1 = call ptr @llvm.protected.field.ptr(ptr %0, i64 29832, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.p) ]
+  // X86_64:  %1 = call ptr @llvm.protected.field.ptr(ptr %0, i64 136, i1 
false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.p) ]
+  // CHECK: store ptr null, ptr %1, align 8
   S s{};
 }
 
-// Check that the constructor was applied
-// CHECK: call void @llvm.memset.{{.*}}
-// CHECK: call {{.*}} @llvm.protected.field.ptr({{.*}}, i64 0, metadata 
!"_ZTS1S.p", i1 false)
\ No newline at end of file
diff --git a/clang/test/CodeGenCXX/pfp-struct-gep.cpp 
b/clang/test/CodeGenCXX/pfp-struct-gep.cpp
index 964545efa9f4b..700934baf8420 100644
--- a/clang/test/CodeGenCXX/pfp-struct-gep.cpp
+++ b/clang/test/CodeGenCXX/pfp-struct-gep.cpp
@@ -1,6 +1,5 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NOPFP
-// RUN: %clang_cc1  -fexperimental-pointer-field-protection=tagged -emit-llvm 
-o - %s | FileCheck %s -check-prefix=CHECK-PFP
-
+// RUN: %clang_cc1 -triple aarch64-linux 
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck 
%s -check-prefixes=CHECK,AARCH64
+// RUN: %clang_cc1 -triple x86_64-linux  
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck 
%s -check-prefixes=CHECK,X86_64
 
 struct S {
   int* ptr;
@@ -10,16 +9,28 @@ struct S {
 
 // CHECK-LABEL: load_pointers
 int* load_pointers(S *t) {
+  // CHECK: %t.addr = alloca ptr, align 8
+  // CHECK: store ptr %t, ptr %t.addr, align 8
+  // CHECK: %0 = load ptr, ptr %t.addr, align 8
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.S, ptr %0, i32 0, i32 0
+  // AARCH64: %1 = call ptr @llvm.protected.field.ptr(ptr %ptr, i64 63261, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+  // X86_64: %1 = call ptr @llvm.protected.field.ptr(ptr %ptr, i64 29, i1 
false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+  // CHECK: %2 = load ptr, ptr %1, align 8
+  // CHECK: ret ptr %2
    return t->ptr;
 }
-// CHECK-PFP: call {{.*}} @llvm.protected.field.ptr({{.*}}, i64 0, metadata 
!"_ZTS1S.ptr", i1 false)
-// CHECK-NOPFP: getelementptr
 
 // CHECK-LABEL: store_pointers
 void store_pointers(S* t, int* p) {
+  // CHECK: %t.addr = alloca ptr, align 8
+  // CHECK: %p.addr = alloca ptr, align 8
+  // CHECK: store ptr %t, ptr %t.addr, align 8
+  // CHECK: store ptr %p, ptr %p.addr, align 8
+  // CHECK: %0 = load ptr, ptr %p.addr, align 8
+  // CHECK: %1 = load ptr, ptr %t.addr, align 8
+  // CHECK: %ptr = getelementptr inbounds nuw %struct.S, ptr %1, i32 0, i32 0
+  // AARCH64: %2 = call ptr @llvm.protected.field.ptr(ptr %ptr, i64 63261, i1 
true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+  // X86_64: %2 = call ptr @llvm.protected.field.ptr(ptr %ptr, i64 29, i1 
false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+  // CHECK: store ptr %0, ptr %2, align 8
   t->ptr = p;
 }
-// CHECK-PFP: call {{.*}} @llvm.protected.field.ptr({{.*}}, i64 0, metadata 
!"_ZTS1S.ptr", i1 false)
-// CHECK-NOPFP: getelementptr
-
-

>From 0866b8f821c126e562f7599a0b4c5f05908549a8 Mon Sep 17 00:00:00 2001
From: Peter Collingbourne <p...@google.com>
Date: Fri, 8 Aug 2025 11:30:58 -0700
Subject: [PATCH 2/2] Improve coerce logic and add coerce test

Created using spr 1.3.6-beta.1
---
 clang/include/clang/AST/ASTContext.h          |   4 +-
 clang/lib/AST/ASTContext.cpp                  |  15 ++-
 clang/lib/CodeGen/CGBuiltin.cpp               |  52 ++++++---
 clang/lib/CodeGen/CGExpr.cpp                  |   1 +
 clang/lib/Sema/SemaTypeTraits.cpp             |  20 ++--
 clang/test/CodeGenCXX/pfp-coerce.cpp          |   4 +-
 clang/test/CodeGenCXX/pfp-memcpy.cpp          |   4 +-
 .../CodeGenCXX/pfp-trivially-relocatable.cpp  | 101 ++++++++++++++++++
 8 files changed, 167 insertions(+), 34 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 7c04a3923f066..b1864ca4eccce 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -186,6 +186,7 @@ struct TypeInfoChars {
 struct PFPField {
   CharUnits offset;
   FieldDecl *field;
+  bool isWithinUnion;
 };
 
 /// Holds long-lived AST nodes (such as types and decls) that can be
@@ -3727,7 +3728,8 @@ OPT_LIST(V)
 
   bool isPFPStruct(const RecordDecl *rec) const;
   void findPFPFields(QualType Ty, CharUnits Offset,
-                     std::vector<PFPField> &Fields, bool IncludeVBases) const;
+                     std::vector<PFPField> &Fields, bool IncludeVBases,
+                     bool IsWithinUnion = false) const;
   bool hasPFPFields(QualType ty) const;
   bool isPFPField(const FieldDecl *field) const;
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 1dc5b6bdcafdc..5c9d481406bb6 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15183,6 +15183,10 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl 
VirtualMethodDecl,
 }
 
 bool ASTContext::arePFPFieldsTriviallyRelocatable(const RecordDecl *RD) const {
+  bool IsPAuthSupported =
+      getTargetInfo().getTriple().getArch() == llvm::Triple::aarch64;
+  if (!IsPAuthSupported)
+    return true;
   if (getLangOpts().getPointerFieldProtection() ==
       LangOptions::PointerFieldProtectionKind::Tagged)
     return !isa<CXXRecordDecl>(RD) ||
@@ -15200,7 +15204,7 @@ bool ASTContext::isPFPStruct(const RecordDecl *rec) 
const {
 
 void ASTContext::findPFPFields(QualType Ty, CharUnits Offset,
                                std::vector<PFPField> &Fields,
-                               bool IncludeVBases) const {
+                               bool IncludeVBases, bool IsWithinUnion) const {
   if (auto *AT = getAsConstantArrayType(Ty)) {
     if (auto *ElemDecl = AT->getElementType()->getAsCXXRecordDecl()) {
       const ASTRecordLayout &ElemRL = getASTRecordLayout(ElemDecl);
@@ -15213,26 +15217,27 @@ void ASTContext::findPFPFields(QualType Ty, CharUnits 
Offset,
   auto *Decl = Ty->getAsCXXRecordDecl();
   if (!Decl)
     return;
+  IsWithinUnion |= Decl->isUnion();
   const ASTRecordLayout &RL = getASTRecordLayout(Decl);
   for (FieldDecl *field : Decl->fields()) {
     CharUnits fieldOffset =
         Offset + 
toCharUnitsFromBits(RL.getFieldOffset(field->getFieldIndex()));
     if (isPFPField(field))
-      Fields.push_back({fieldOffset, field});
-    findPFPFields(field->getType(), fieldOffset, Fields, true);
+      Fields.push_back({fieldOffset, field, IsWithinUnion});
+    findPFPFields(field->getType(), fieldOffset, Fields, true, IsWithinUnion);
   }
   for (auto &Base : Decl->bases()) {
     if (Base.isVirtual())
       continue;
     CharUnits BaseOffset =
         Offset + RL.getBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
-    findPFPFields(Base.getType(), BaseOffset, Fields, false);
+    findPFPFields(Base.getType(), BaseOffset, Fields, false, IsWithinUnion);
   }
   if (IncludeVBases) {
     for (auto &Base : Decl->vbases()) {
       CharUnits BaseOffset =
           Offset + 
RL.getVBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
-      findPFPFields(Base.getType(), BaseOffset, Fields, false);
+      findPFPFields(Base.getType(), BaseOffset, Fields, false, IsWithinUnion);
     }
   }
 }
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index d11726f99afd9..7be70ee46eaf5 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4499,14 +4499,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const 
GlobalDecl GD, unsigned BuiltinID,
     Address Dest = EmitPointerWithAlignment(E->getArg(0));
     Address Src = EmitPointerWithAlignment(E->getArg(1));
     Value *SizeVal = EmitScalarExpr(E->getArg(2));
+    Value *TypeSize = ConstantInt::get(
+        SizeVal->getType(),
+        getContext()
+            .getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType())
+            .getQuantity());
     if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate)
-      SizeVal = Builder.CreateMul(
-          SizeVal,
-          ConstantInt::get(
-              SizeVal->getType(),
-              getContext()
-                  
.getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType())
-                  .getQuantity()));
+      SizeVal = Builder.CreateMul(SizeVal, TypeSize);
     EmitArgCheck(TCK_Store, Dest, E->getArg(0), 0);
     EmitArgCheck(TCK_Load, Src, E->getArg(1), 1);
     auto *I = Builder.CreateMemMove(Dest, Src, SizeVal, false);
@@ -4515,13 +4514,38 @@ RValue CodeGenFunction::EmitBuiltinExpr(const 
GlobalDecl GD, unsigned BuiltinID,
       std::vector<PFPField> PFPFields;
       getContext().findPFPFields(E->getArg(0)->getType()->getPointeeType(),
                                  CharUnits::Zero(), PFPFields, true);
-      for (auto &Field : PFPFields) {
-        if (getContext().arePFPFieldsTriviallyRelocatable(
-                Field.field->getParent()))
-          continue;
-        auto DestFieldPtr = EmitAddressOfPFPField(Dest, Field);
-        auto SrcFieldPtr = EmitAddressOfPFPField(Src, Field);
-        Builder.CreateStore(Builder.CreateLoad(SrcFieldPtr), DestFieldPtr);
+      if (!PFPFields.empty()) {
+        BasicBlock *Entry = Builder.GetInsertBlock();
+        BasicBlock *Loop = createBasicBlock("loop");
+        BasicBlock *LoopEnd = createBasicBlock("loop.end");
+        Builder.CreateCondBr(
+            Builder.CreateICmpEQ(SizeVal,
+                                 ConstantInt::get(SizeVal->getType(), 0)),
+            LoopEnd, Loop);
+
+        EmitBlock(Loop);
+        PHINode *Offset = Builder.CreatePHI(SizeVal->getType(), 2);
+        Offset->addIncoming(ConstantInt::get(SizeVal->getType(), 0), Entry);
+        Address DestRec = Dest.withPointer(
+            Builder.CreateInBoundsGEP(Int8Ty, Dest.getBasePointer(), {Offset}),
+            KnownNonNull);
+        Address SrcRec = Src.withPointer(
+            Builder.CreateInBoundsGEP(Int8Ty, Src.getBasePointer(), {Offset}),
+            KnownNonNull);
+        for (auto &Field : PFPFields) {
+          if (getContext().arePFPFieldsTriviallyRelocatable(
+                  Field.field->getParent()))
+            continue;
+          auto DestFieldPtr = EmitAddressOfPFPField(DestRec, Field);
+          auto SrcFieldPtr = EmitAddressOfPFPField(SrcRec, Field);
+          Builder.CreateStore(Builder.CreateLoad(SrcFieldPtr), DestFieldPtr);
+        }
+
+        Value *NextOffset = Builder.CreateAdd(Offset, TypeSize);
+        Offset->addIncoming(NextOffset, Loop);
+        Builder.CreateCondBr(Builder.CreateICmpEQ(NextOffset, SizeVal), 
LoopEnd, Loop);
+
+        EmitBlock(LoopEnd);
       }
     }
     return RValue::get(Dest, *this);
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 3457ec0ee8b5d..cd86818a13674 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5112,6 +5112,7 @@ static Address emitRawAddrOfFieldStorage(CodeGenFunction 
&CGF, Address base,
     return emitAddrOfZeroSizeField(CGF, base, field, IsInBounds);
 
   const RecordDecl *rec = field->getParent();
+
   unsigned idx =
     CGF.CGM.getTypes().getCGRecordLayout(rec).getLLVMFieldNo(field);
 
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp 
b/clang/lib/Sema/SemaTypeTraits.cpp
index c06f53018b338..c171b708b818c 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -235,16 +235,16 @@ static bool IsEligibleForReplacement(Sema &SemaRef, const 
CXXRecordDecl *D) {
 
 static bool IsImplementationDefinedNonRelocatable(Sema &SemaRef,
                                                   const CXXRecordDecl *D) {
-  // FIXME: Should also check for polymorphic union members here if PAuth ABI 
is
-  // enabled.
-
-  // FIXME: PFP should not affect trivial relocatability except in cases where 
a
-  // PFP field is a member of a union, instead it should affect the
-  // implementation of std::trivially_relocate. See:
-  // 
https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555/16?u=pcc
-  if (!SemaRef.Context.arePFPFieldsTriviallyRelocatable(D) &&
-      SemaRef.Context.hasPFPFields(QualType(D->getTypeForDecl(), 0)))
-    return true;
+  // The implementation-defined carveout only exists for polymorphic types.
+  if (!D->isPolymorphic())
+    return false;
+
+  std::vector<PFPField> pfpFields;
+  SemaRef.Context.findPFPFields(QualType(D->getTypeForDecl(), 0),
+                                CharUnits::Zero(), pfpFields, true);
+  for (PFPField f : pfpFields)
+    if (f.isWithinUnion)
+      return true;
 
   return false;
 }
diff --git a/clang/test/CodeGenCXX/pfp-coerce.cpp 
b/clang/test/CodeGenCXX/pfp-coerce.cpp
index db53f2bf45c70..636d61ccf4858 100644
--- a/clang/test/CodeGenCXX/pfp-coerce.cpp
+++ b/clang/test/CodeGenCXX/pfp-coerce.cpp
@@ -186,7 +186,7 @@ void pass_trivial_abi_pointer(TrivialAbiPointer p, 
TrivialAbiPointer *pp) {
   // X86_64: store ptr %p.coerce, ptr %1, align 8
   // X86_64: store ptr %pp, ptr %pp.addr, align 8
   // X86_64: %2 = load ptr, ptr %pp.addr, align 8
-  // X86_64: %call = call noundef nonnull align 8 dereferenceable(8) ptr 
@_ZN17TrivialAbiPointeraSERKS_(ptr noundef nonnull align 8 dereferenceable(8) 
%2, ptr noundef nonnull align 8 dereferenceable(8) %p)
+  // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %2, ptr align 8 %p, 
i64 8, i1 false)
   // X86_64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 
dereferenceable(8) %p)
   *pp = p;
 }
@@ -210,7 +210,7 @@ TrivialAbiPointer 
return_trivial_abi_pointer(TrivialAbiPointer *pp) {
   // X86_64: %pp.addr = alloca ptr, align 8
   // X86_64: store ptr %pp, ptr %pp.addr, align 8
   // X86_64: %0 = load ptr, ptr %pp.addr, align 8
-  // X86_64: call void @_ZN17TrivialAbiPointerC1ERKS_(ptr noundef nonnull 
align 8 dereferenceable(8) %retval, ptr noundef nonnull align 8 
dereferenceable(8) %0)
+  // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 
%0, i64 8, i1 false)
   // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
   // X86_64: %2 = call ptr @llvm.protected.field.ptr(ptr %1, i64 33, i1 false) 
[ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
   // X86_64: %3 = load ptr, ptr %2, align 8
diff --git a/clang/test/CodeGenCXX/pfp-memcpy.cpp 
b/clang/test/CodeGenCXX/pfp-memcpy.cpp
index 3e92db6110ae3..9df63556d71ff 100644
--- a/clang/test/CodeGenCXX/pfp-memcpy.cpp
+++ b/clang/test/CodeGenCXX/pfp-memcpy.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1  -fexperimental-pointer-field-protection=tagged -emit-llvm 
-o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64-linux 
-fexperimental-pointer-field-protection=tagged -emit-llvm -o - %s | FileCheck %s
 
 struct ClassWithTrivialCopy {
   ClassWithTrivialCopy();
@@ -16,4 +16,4 @@ void make_trivial_copy(ClassWithTrivialCopy *s1, 
ClassWithTrivialCopy *s2) {
 
 // CHECK-LABEL: define{{.*}} void 
@_Z17make_trivial_copyP20ClassWithTrivialCopyS0_
 // CHECK-NOT: memcpy
-// CHECK: ret void
\ No newline at end of file
+// CHECK: ret void
diff --git a/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp 
b/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp
new file mode 100644
index 0000000000000..e540d2672c84d
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm 
-fexperimental-pointer-field-protection=untagged -o - %s | FileCheck 
--check-prefix=RELOC %s
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm 
-fexperimental-pointer-field-protection=tagged -o - %s | FileCheck 
--check-prefix=RELOC %s
+// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -emit-llvm 
-fexperimental-pointer-field-protection=untagged -o - %s | FileCheck 
--check-prefix=RELOC %s
+// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -emit-llvm 
-fexperimental-pointer-field-protection=tagged -o - %s | FileCheck 
--check-prefix=NONRELOC %s
+
+typedef __SIZE_TYPE__ size_t;
+
+struct S trivially_relocatable_if_eligible {
+    S(const S&);
+    ~S();
+    int* a;
+private:
+    int* b;
+};
+
+// CHECK: define dso_local void @_Z5test1P1SS0_(
+void test1(S* source, S* dest) {
+  // RELOC:       %0 = load ptr, ptr %dest.addr, align 8
+  // RELOC-NEXT:  %1 = load ptr, ptr %source.addr, align 8
+  // RELOC-NEXT:  call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 
8 %1, i64 16, i1 false)
+  // RELOC-NOT: @llvm.protected.field.ptr
+
+  // NONRELOC:        %0 = load ptr, ptr %dest.addr, align 8
+  // NONRELOC-NEXT:   %1 = load ptr, ptr %source.addr, align 8
+  // NONRELOC-NEXT:   call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr 
align 8 %1, i64 16, i1 false)
+  // NONRELOC-NEXT:   br i1 false, label %loop.end, label %loop
+
+  // NONRELOC:      loop:
+  // NONRELOC-NEXT:   %2 = phi i64 [ 0, %entry ], [ %19, %loop ]
+  // NONRELOC-NEXT:   %3 = getelementptr inbounds i8, ptr %0, i64 %2
+  // NONRELOC-NEXT:   %4 = getelementptr inbounds i8, ptr %1, i64 %2
+  // NONRELOC-NEXT:   %5 = getelementptr inbounds i8, ptr %3, i64 0
+  // NONRELOC-NEXT:   %6 = ptrtoint ptr %3 to i64
+  // NONRELOC-NEXT:   %7 = call ptr @llvm.protected.field.ptr(ptr %5, i64 %6, 
i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+  // NONRELOC-NEXT:   %8 = getelementptr inbounds i8, ptr %4, i64 0
+  // NONRELOC-NEXT:   %9 = ptrtoint ptr %4 to i64
+  // NONRELOC-NEXT:   %10 = call ptr @llvm.protected.field.ptr(ptr %8, i64 %9, 
i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+  // NONRELOC-NEXT:   %11 = load ptr, ptr %10, align 8
+  // NONRELOC-NEXT:   store ptr %11, ptr %7, align 8
+  // NONRELOC-NEXT:   %12 = getelementptr inbounds i8, ptr %3, i64 8
+  // NONRELOC-NEXT:   %13 = ptrtoint ptr %3 to i64
+  // NONRELOC-NEXT:   %14 = call ptr @llvm.protected.field.ptr(ptr %12, i64 
%13, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+  // NONRELOC-NEXT:   %15 = getelementptr inbounds i8, ptr %4, i64 8
+  // NONRELOC-NEXT:   %16 = ptrtoint ptr %4 to i64
+  // NONRELOC-NEXT:   %17 = call ptr @llvm.protected.field.ptr(ptr %15, i64 
%16, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+  // NONRELOC-NEXT:   %18 = load ptr, ptr %17, align 8
+  // NONRELOC-NEXT:   store ptr %18, ptr %14, align 8
+  // NONRELOC-NEXT:   %19 = add i64 %2, 16
+  // NONRELOC-NEXT:   %20 = icmp eq i64 %19, 16
+  // NONRELOC-NEXT:   br i1 %20, label %loop.end, label %loop
+
+  // NONRELOC:      loop.end:
+  // NONRELOC-NEXT:   ret void
+  __builtin_trivially_relocate(dest, source, 1);
+}
+
+// CHECK: define dso_local void @_Z5testNP1SS0_m(
+void testN(S* source, S* dest, size_t count) {
+  // RELOC:       %0 = load ptr, ptr %dest.addr, align 8
+  // RELOC-NEXT:  %1 = load ptr, ptr %source.addr, align 8
+  // RELOC-NEXT:  %2 = load i64, ptr %count.addr, align 8
+  // RELOC-NEXT:  %3 = mul i64 %2, 16
+  // RELOC-NEXT:  call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 
8 %1, i64 %3, i1 false)
+  // RELOC-NOT: @llvm.protected.field.ptr
+ 
+  // NONRELOC:        %0 = load ptr, ptr %dest.addr, align 8
+  // NONRELOC-NEXT:   %1 = load ptr, ptr %source.addr, align 8
+  // NONRELOC-NEXT:   %2 = load i64, ptr %count.addr, align 8
+  // NONRELOC-NEXT:   %3 = mul i64 %2, 16
+  // NONRELOC-NEXT:   call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr 
align 8 %1, i64 %3, i1 false)
+  // NONRELOC-NEXT:   %4 = icmp eq i64 %3, 0
+  // NONRELOC-NEXT:   br i1 %4, label %loop.end, label %loop
+
+  // NONRELOC:      loop:
+  // NONRELOC-NEXT:   %5 = phi i64 [ 0, %entry ], [ %22, %loop ]
+  // NONRELOC-NEXT:   %6 = getelementptr inbounds i8, ptr %0, i64 %5
+  // NONRELOC-NEXT:   %7 = getelementptr inbounds i8, ptr %1, i64 %5
+  // NONRELOC-NEXT:   %8 = getelementptr inbounds i8, ptr %6, i64 0
+  // NONRELOC-NEXT:   %9 = ptrtoint ptr %6 to i64
+  // NONRELOC-NEXT:   %10 = call ptr @llvm.protected.field.ptr(ptr %8, i64 %9, 
i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+  // NONRELOC-NEXT:   %11 = getelementptr inbounds i8, ptr %7, i64 0
+  // NONRELOC-NEXT:   %12 = ptrtoint ptr %7 to i64
+  // NONRELOC-NEXT:   %13 = call ptr @llvm.protected.field.ptr(ptr %11, i64 
%12, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+  // NONRELOC-NEXT:   %14 = load ptr, ptr %13, align 8
+  // NONRELOC-NEXT:   store ptr %14, ptr %10, align 8
+  // NONRELOC-NEXT:   %15 = getelementptr inbounds i8, ptr %6, i64 8
+  // NONRELOC-NEXT:   %16 = ptrtoint ptr %6 to i64
+  // NONRELOC-NEXT:   %17 = call ptr @llvm.protected.field.ptr(ptr %15, i64 
%16, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+  // NONRELOC-NEXT:   %18 = getelementptr inbounds i8, ptr %7, i64 8
+  // NONRELOC-NEXT:   %19 = ptrtoint ptr %7 to i64
+  // NONRELOC-NEXT:   %20 = call ptr @llvm.protected.field.ptr(ptr %18, i64 
%19, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+  // NONRELOC-NEXT:   %21 = load ptr, ptr %20, align 8
+  // NONRELOC-NEXT:   store ptr %21, ptr %17, align 8
+  // NONRELOC-NEXT:   %22 = add i64 %5, 16
+  // NONRELOC-NEXT:   %23 = icmp eq i64 %22, %3
+  // NONRELOC-NEXT:   br i1 %23, label %loop.end, label %loop
+
+  // NONRELOC:      loop.end:
+  // NONRELOC-NEXT:   ret void
+  __builtin_trivially_relocate(dest, source, count);
+};

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
  • [llvm-branch-commits] [clang] ... Peter Collingbourne via llvm-branch-commits

Reply via email to