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

Update the codegen for the the dynamic_cast to a final class optimization when 
pointer authentication is enabled.

>From 0603664d1637ea7fc729486dec97c5f3292cddaa Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oli...@apple.com>
Date: Mon, 4 Aug 2025 20:54:46 -0700
Subject: [PATCH] [clang][PAC] Enable the PAC dynamic_cast to final class
 optimization

Update the codegen for the the dynamic_cast to a final class
optimization when pointer authentication is enabled.
---
 clang/docs/ReleaseNotes.rst                   |   2 +
 clang/lib/CodeGen/CGExprCXX.cpp               |   3 +-
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  39 ++++++
 .../dynamic-cast-exact-disabled.cpp           |   1 -
 .../CodeGenCXX/ptrauth-dynamic-cast-exact.cpp | 128 ++++++++++++++++++
 5 files changed, 170 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0e9fcaa5fac6a..94de70329ee34 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -184,6 +184,8 @@ Bug Fixes to C++ Support
   (``[[assume(expr)]]``) creates temporary objects.
 - Fix the dynamic_cast to final class optimization to correctly handle
   casts that are guaranteed to fail (#GH137518).
+- Support the dynamic_cast to final class optimization with pointer
+  authentication enabled.
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 49d5d8acbe331..afb9ab8a55c56 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -2292,8 +2292,7 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address 
ThisAddr,
   bool IsExact = !IsDynamicCastToVoid &&
                  CGM.getCodeGenOpts().OptimizationLevel > 0 &&
                  DestRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() &&
-                 CGM.getCXXABI().shouldEmitExactDynamicCast(DestRecordTy) &&
-                 !getLangOpts().PointerAuthCalls;
+                 CGM.getCXXABI().shouldEmitExactDynamicCast(DestRecordTy);
 
   std::optional<CGCXXABI::ExactDynamicCastInfo> ExactCastInfo;
   if (IsExact) {
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 5ffc1edb9986f..f4c84c37ce72b 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1745,7 +1745,14 @@ llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
     llvm::BasicBlock *CastFail) {
   const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl();
   const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl();
+  auto AuthenticateVTable = [&](Address ThisAddr, const CXXRecordDecl *Decl) {
+    if (!CGF.getLangOpts().PointerAuthCalls)
+      return;
+    (void)CGF.GetVTablePtr(ThisAddr, CGF.UnqualPtrTy, Decl,
+                           CodeGenFunction::VTableAuthMode::MustTrap);
+  };
 
+  bool PerformPostCastAuthentication = false;
   llvm::Value *VTable = nullptr;
   if (ExactCastInfo.RequiresCastToPrimaryBase) {
     // Base appears in at least two different places. Find the most-derived
@@ -1756,8 +1763,16 @@ llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
         emitDynamicCastToVoid(CGF, ThisAddr, SrcRecordTy);
     ThisAddr = Address(PrimaryBase, CGF.VoidPtrTy, ThisAddr.getAlignment());
     SrcDecl = DestDecl;
+    // This unauthenticated load is unavoidable, so we're relying on the
+    // authenticated load in the dynamic cast to void, and we'll manually
+    // authenticate the resulting v-table at the end of the cast check.
+    PerformPostCastAuthentication = CGF.getLangOpts().PointerAuthCalls;
+    CGPointerAuthInfo StrippingAuthInfo(0, PointerAuthenticationMode::Strip,
+                                        false, false, nullptr);
     Address VTablePtrPtr = ThisAddr.withElementType(CGF.VoidPtrPtrTy);
     VTable = CGF.Builder.CreateLoad(VTablePtrPtr, "vtable");
+    if (PerformPostCastAuthentication)
+      VTable = CGF.EmitPointerAuthAuth(StrippingAuthInfo, VTable);
   } else
     VTable = CGF.GetVTablePtr(ThisAddr, CGF.UnqualPtrTy, SrcDecl);
 
@@ -1774,8 +1789,32 @@ llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
         llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset);
     AdjustedThisPtr = CGF.Builder.CreateInBoundsGEP(CGF.CharTy, 
AdjustedThisPtr,
                                                     OffsetConstant);
+    PerformPostCastAuthentication = CGF.getLangOpts().PointerAuthCalls;
   }
 
+  if (PerformPostCastAuthentication) {
+    // If we've changed the object pointer we authenticate the vtable pointer
+    // of the resulting object.
+    llvm::BasicBlock *NonNullBlock = CGF.Builder.GetInsertBlock();
+    llvm::BasicBlock *PostCastAuthSuccess =
+        CGF.createBasicBlock("dynamic_cast.postauth.success");
+    llvm::BasicBlock *PostCastAuthComplete =
+        CGF.createBasicBlock("dynamic_cast.postauth.complete");
+    CGF.Builder.CreateCondBr(Success, PostCastAuthSuccess,
+                             PostCastAuthComplete);
+    CGF.EmitBlock(PostCastAuthSuccess);
+    Address AdjustedThisAddr =
+        Address(AdjustedThisPtr, CGF.IntPtrTy, CGF.getPointerAlign());
+    AuthenticateVTable(AdjustedThisAddr, DestDecl);
+    CGF.EmitBranch(PostCastAuthComplete);
+    CGF.EmitBlock(PostCastAuthComplete);
+    llvm::PHINode *PHI = CGF.Builder.CreatePHI(AdjustedThisPtr->getType(), 2);
+    PHI->addIncoming(AdjustedThisPtr, PostCastAuthSuccess);
+    llvm::Value *NullValue =
+        llvm::Constant::getNullValue(AdjustedThisPtr->getType());
+    PHI->addIncoming(NullValue, NonNullBlock);
+    AdjustedThisPtr = PHI;
+  }
   CGF.Builder.CreateCondBr(Success, CastSuccess, CastFail);
   return AdjustedThisPtr;
 }
diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp 
b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
index 3156e1bb21b1e..bf202d14c3398 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
@@ -3,7 +3,6 @@
 // RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 
-fvisibility=hidden -emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,INEXACT
 // RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 -fapple-kext 
-emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,INEXACT
 // RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -O1 
-fno-assume-unique-vtables -emit-llvm -std=c++11 -o - | FileCheck %s 
--check-prefixes=CHECK,INEXACT
-// RUN: %clang_cc1 -I%S %s -triple arm64e-apple-darwin10 -O1 -fptrauth-calls 
-emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,INEXACT
 
 struct A { virtual ~A(); };
 struct B final : A { };
diff --git a/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp 
b/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp
new file mode 100644
index 0000000000000..1710ca5563380
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp
@@ -0,0 +1,128 @@
+// RUN: %clang_cc1 -I%S %s -triple arm64e-apple-darwin10 -O1 -fptrauth-calls 
-fptrauth-vtable-pointer-address-discrimination  
-fptrauth-vtable-pointer-type-discrimination -emit-llvm -std=c++11 -o - | 
FileCheck %s --check-prefixes=CHECK
+
+struct A {
+  virtual ~A();
+};
+struct B {
+  int foo;
+  virtual ~B();
+};
+struct C final : A, B {
+  virtual void f(){};
+};
+struct D final : B, A {
+  virtual void f(){};
+};
+
+struct Offset {
+  virtual ~Offset();
+};
+struct E {
+  virtual ~E();
+};
+struct F final : Offset, E {
+};
+struct G {
+  virtual ~G();
+  int g;
+};
+struct H : E {
+  int h;
+};
+struct I : E {
+  int i;
+};
+struct J : virtual E {
+  int j;
+};
+struct K : virtual E {
+  int k;
+};
+struct L final : G, H, I, J, K {
+  int l;
+};
+struct M final: G, private H { int m; };
+
+// CHECK-LABEL: @_Z10exact_to_CP1A
+C *exact_to_C(A *a) {
+  // CHECK: [[UNAUTHED_VPTR:%.*]] = load ptr, ptr %a, align 8
+  // CHECK: [[VPTR_ADDRI:%.*]] = ptrtoint ptr %a to i64
+  // CHECK: [[VPTR_ADDR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 
[[VPTR_ADDRI]], i64 62866)
+  // CHECK: [[UNAUTHED_VPTRI:%.*]] = ptrtoint ptr [[UNAUTHED_VPTR]] to i64
+  // CHECK: [[AUTHED_VPTRI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 
[[UNAUTHED_VPTRI]], i32 2, i64 [[VPTR_ADDR_DISC]])
+  // CHECK: [[IS_EXPECTED:%.*]] = icmp eq i64 [[AUTHED_VPTRI]], ptrtoint (ptr 
getelementptr inbounds nuw inrange(-16, 24) (i8, ptr @_ZTV1C, i64 16) to i64)
+  // CHECK: br i1 [[IS_EXPECTED]], label %dynamic_cast.end, label 
%dynamic_cast.null
+  // CHECK: [[NULL_CHECKED_RESULT:%.*]] = phi ptr [ %a, %dynamic_cast.notnull 
], [ null, %dynamic_cast.null ]
+  // CHECK: ret ptr [[NULL_CHECKED_RESULT]]
+  return dynamic_cast<C*>(a);
+}
+
+// CHECK-LABEL: @_Z9exact_t_DP1A
+D *exact_t_D(A *a) {
+  // CHECK: dynamic_cast.notnull:
+  // CHECK:   [[SRC_UNAUTHED_VPTR:%.*]] = load ptr, ptr %a
+  // CHECK:   [[SRC_VPTR_ADDRI:%.*]] = ptrtoint ptr %a to i64
+  // CHECK:   [[SRC_VPTR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 
[[SRC_VPTR_ADDRI]], i64 62866)
+  // CHECK:   [[SRC_UNAUTHED_VPTRI:%.*]] = ptrtoint ptr [[SRC_UNAUTHED_VPTR]] 
to i64
+  // CHECK:   [[SRC_AUTHED_VPTRI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 
[[SRC_UNAUTHED_VPTRI]], i32 2, i64 [[SRC_VPTR_DISC]])
+  // CHECK:   [[SUCCESS:%.*]] = icmp eq i64 [[SRC_AUTHED_VPTRI]], ptrtoint 
(ptr getelementptr inbounds nuw inrange(-16, 16) (i8, ptr @_ZTV1D, i64 56) to 
i64)
+  // CHECK:   br i1 [[SUCCESS]], label %dynamic_cast.postauth.success, label 
%dynamic_cast.postauth.complete
+  // CHECK: dynamic_cast.postauth.success:
+  // CHECK:   [[ADJUSTED_THIS:%.*]] = getelementptr inbounds i8, ptr %a, i64 
-16
+  // CHECK:   [[ADJUSTED_UNAUTHED_VPTR:%.*]] = load ptr, ptr [[ADJUSTED_THIS]]
+  // CHECK:   [[ADJUSTED_VPTR_ADDRI:%.*]] = ptrtoint ptr [[ADJUSTED_THIS]] to 
i64
+  // CHECK:   [[ADJUSTED_VPTR_DISC:%.*]] = tail call i64 
@llvm.ptrauth.blend(i64 [[ADJUSTED_VPTR_ADDRI]], i64 28965)
+  // CHECK:   [[ADJUSTED_UNAUTHED_VPTRI:%.*]] = ptrtoint ptr 
[[ADJUSTED_UNAUTHED_VPTR]] to i64
+  // CHECK:   [[ADJUSTED_AUTHED_VPTRI:%.*]] = tail call i64 
@llvm.ptrauth.auth(i64 [[ADJUSTED_UNAUTHED_VPTRI]], i32 2, i64 
[[ADJUSTED_VPTR_DISC]])
+  // CHECK:   [[ADJUSTED_AUTHED_VPTR:%.*]] = inttoptr i64 
[[ADJUSTED_AUTHED_VPTRI]] to ptr
+  // CHECK:   br label %dynamic_cast.postauth.complete
+  // CHECK: dynamic_cast.postauth.complete:
+  // CHECK:   [[AUTHED_ADJUSTED_THIS:%.*]] = phi ptr [ [[ADJUSTED_THIS]], 
%dynamic_cast.postauth.success ], [ null, %dynamic_cast.notnull ]
+  // CHECK:   br i1 [[SUCCESS]], label %dynamic_cast.end, label 
%dynamic_cast.null
+  // CHECK: dynamic_cast.null:
+  // CHECK:   br label %dynamic_cast.end
+  // CHECK: dynamic_cast.end:
+  // CHECK:   [[RESULT:%.*]] = phi ptr [ [[AUTHED_ADJUSTED_THIS]], 
%dynamic_cast.postauth.complete ], [ null, %dynamic_cast.null ]
+  // CHECK:   ret ptr [[RESULT]]
+  return dynamic_cast<D*>(a);
+}
+
+// CHECK-LABEL: @_Z11exact_multiP1E
+L *exact_multi(E *e) {
+  // CHECK: dynamic_cast.notnull:
+  // CHECK:   [[VTABLE_ADDR:%.*]] = load ptr, ptr %e, align 8
+  // CHECK:   [[THIS_ADDRI:%.*]] = ptrtoint ptr %e to i64
+  // CHECK:   [[VTABLE_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 
[[THIS_ADDRI]], i64 12810)
+  // CHECK:   [[VTABLE_ADDRI:%.*]] = ptrtoint ptr [[VTABLE_ADDR]] to i64
+  // CHECK:   [[AUTHED_VTABLEI:%.*]] = tail call i64 @llvm.ptrauth.auth(i64 
[[VTABLE_ADDRI]], i32 2, i64 [[VTABLE_DISC]])
+  // CHECK:   [[AUTHED_VTABLE:%.*]] = inttoptr i64 [[AUTHED_VTABLEI]] to ptr
+  // CHECK:   [[PRIMARY_BASE_OFFSET:%.*]] = getelementptr inbounds i8, ptr 
[[AUTHED_VTABLE]], i64 -16
+  // CHECK:   %offset.to.top = load i64, ptr [[PRIMARY_BASE_OFFSET]]
+  // CHECK:   [[ADJUSTED_THIS:%.*]] = getelementptr inbounds i8, ptr %e, i64 
%offset.to.top
+  // CHECK:   [[ADJUSTED_THIS_VTABLE:%.*]] = load ptr, ptr [[ADJUSTED_THIS]]
+  // CHECK:   [[ADJUSTED_THIS_VTABLEI:%.*]] = ptrtoint ptr 
[[ADJUSTED_THIS_VTABLE]] to i64
+  // CHECK:   [[ADJUSTED_THIS_STRIPPED_VTABLEI:%.*]] = tail call i64 
@llvm.ptrauth.strip(i64 [[ADJUSTED_THIS_VTABLEI]], i32 0)
+  // CHECK:   [[SUCCESS:%.*]] = icmp eq i64 
[[ADJUSTED_THIS_STRIPPED_VTABLEI]], ptrtoint (ptr getelementptr inbounds nuw 
inrange(-24, 16) (i8, ptr @_ZTV1L, i64 24) to i64)
+  // CHECK:   br i1 [[SUCCESS]], label %dynamic_cast.postauth.success, label 
%dynamic_cast.postauth.complete
+  // CHECK: dynamic_cast.postauth.success:
+  // CHECK:   [[ADJUSTED_THISI:%.*]] = ptrtoint ptr [[ADJUSTED_THIS]] to i64
+  // CHECK:   [[DEST_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 
[[ADJUSTED_THISI]], i64 41434)
+  // CHECK:   tail call i64 @llvm.ptrauth.auth(i64 [[ADJUSTED_THIS_VTABLEI]], 
i32 2, i64 [[DEST_DISC]])
+  // CHECK:   br label %dynamic_cast.postauth.complete
+  // CHECK: dynamic_cast.postauth.complete:
+  // CHECK:   [[AUTHED_ADJUSTED_THIS:%.*]] = phi ptr [ [[ADJUSTED_THIS]], 
%dynamic_cast.postauth.success ], [ null, %dynamic_cast.notnull ]
+  // CHECK:   br i1 [[SUCCESS]], label %dynamic_cast.end, label 
%dynamic_cast.null
+  // CHECK: dynamic_cast.null:
+  // CHECK:   br label %dynamic_cast.end
+  // CHECK: dynamic_cast.end:
+  // CHECK:   [[RESULT:%.*]] = phi ptr [ [[AUTHED_ADJUSTED_THIS]], 
%dynamic_cast.postauth.complete ], [ null, %dynamic_cast.null ]
+  // CHECK:   ret ptr [[RESULT]]
+  return dynamic_cast<L*>(e);
+}
+
+// CHECK-LABEL: @_Z19exact_invalid_multiP1H
+M *exact_invalid_multi(H* d) {
+  // CHECK: entry:
+  // CHECK-NEXT:   ret ptr null
+  return dynamic_cast<M*>(d);
+}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to