[clang] [clang] Fix crash in dynamic_cast final class optimization (PR #152076)
Oliver Hunt via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 4 21:50:11 PDT 2025
https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/152076
>From 7af97ae2f3a155920b15f28c2ad4b24f3550b143 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Mon, 4 Aug 2025 20:54:46 -0700
Subject: [PATCH] [clang] Fix crash in dynamic_cast final class optimization
This corrects the codegen for the final class optimization to
correct handle the case where there is no path to perform the
cast, and also corrects the codegen to handle ptrauth protected
vtable pointers.
As part of this fix we separate out the path computation as
that makes it easier to reason about the failure code paths
and more importantly means we can know what the type of the
this object is during the cast.
The allows us to use the GetVTablePointer interface which
correctly performs the authentication operations required
when pointer authentication is enabled.
There is one place where we still lose a fully authenticated
path, and that is if there multiple paths from the source
type to the destination type. In that case we're forced to
perform a dynamic_cast to void* to find the primary base. As
we do not know the primary base at this point we do not yet
know the dynamic type of the adjusted this object and so cannot
authenticate the vtable load. The approach this PR takes to
mitigate this gap is to authenticate the vtable of the original
object, and then if the stripped vtable pointer matches the
expected type we then know the type of the object and so
perform a fully authenticated load of the vtable from the
resulting object.
Fixes #137518
---
clang/docs/ReleaseNotes.rst | 3 +
clang/lib/CodeGen/CGExprCXX.cpp | 13 +-
clang/lib/CodeGen/ItaniumCXXABI.cpp | 129 +++++++++++++----
.../dynamic-cast-exact-disabled.cpp | 16 +++
clang/test/CodeGenCXX/dynamic-cast-exact.cpp | 37 ++++-
.../CodeGenCXX/ptrauth-dynamic-cast-exact.cpp | 135 ++++++++++++++++++
6 files changed, 298 insertions(+), 35 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 9231f2cae9bfa..d8d13edb86d7c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -160,6 +160,9 @@ Bug Fixes to C++ Support
- Fix a crash when deleting a pointer to an incomplete array (#GH150359).
- Fix an assertion failure when expression in assumption attribute
(``[[assume(expr)]]``) creates temporary objects.
+- Fix the dynamic_cast to final class optimization to correctly handle
+ casts that are guaranteed to fail, and correctly handle ptrauth protected
+ vtable pointers (#GH137518).
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index b8238a4702c4d..209678907138e 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -2344,11 +2344,14 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr,
EmitBlock(CastEnd);
if (CastNull) {
- llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2);
- PHI->addIncoming(Value, CastNotNull);
- PHI->addIncoming(NullValue, CastNull);
-
- Value = PHI;
+ if (CastNotNull) {
+ llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2);
+ PHI->addIncoming(Value, CastNotNull);
+ PHI->addIncoming(NullValue, CastNull);
+
+ Value = PHI;
+ } else
+ Value = NullValue;
}
return Value;
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index f4a99467010af..96305b1b0c1b0 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -226,6 +226,15 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
return hasUniqueVTablePointer(DestRecordTy);
}
+ struct ExactDynamicCastInfo {
+ bool RequiresCastToPrimaryBase;
+ std::optional<CharUnits> Offset;
+ };
+
+ ExactDynamicCastInfo getExactDynamicCastInfo(QualType SrcRecordTy,
+ QualType DestTy,
+ QualType DestRecordTy);
+
llvm::Value *emitDynamicCastCall(CodeGenFunction &CGF, Address Value,
QualType SrcRecordTy, QualType DestTy,
QualType DestRecordTy,
@@ -1681,10 +1690,11 @@ llvm::Value *ItaniumCXXABI::emitDynamicCastCall(
return Value;
}
-llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
- CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy,
- QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastSuccess,
- llvm::BasicBlock *CastFail) {
+ItaniumCXXABI::ExactDynamicCastInfo
+ItaniumCXXABI::getExactDynamicCastInfo(QualType SrcRecordTy, QualType DestTy,
+ QualType DestRecordTy) {
+ assert(shouldEmitExactDynamicCast(DestRecordTy));
+
ASTContext &Context = getContext();
// Find all the inheritance paths.
@@ -1722,41 +1732,102 @@ llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
if (!Offset)
Offset = PathOffset;
else if (Offset != PathOffset) {
- // Base appears in at least two different places. Find the most-derived
- // object and see if it's a DestDecl. Note that the most-derived object
- // must be at least as aligned as this base class subobject, and must
- // have a vptr at offset 0.
- ThisAddr = Address(emitDynamicCastToVoid(CGF, ThisAddr, SrcRecordTy),
- CGF.VoidPtrTy, ThisAddr.getAlignment());
- SrcDecl = DestDecl;
- Offset = CharUnits::Zero();
- break;
+ // Base appears in at least two different places.
+ return ExactDynamicCastInfo{/*RequiresCastToPrimaryBase=*/true,
+ CharUnits::Zero()};
}
}
- if (!Offset) {
+ return ExactDynamicCastInfo{/*RequiresCastToPrimaryBase=*/false, Offset};
+}
+
+llvm::Value *ItaniumCXXABI::emitExactDynamicCast(
+ CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy,
+ QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastSuccess,
+ 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);
+ };
+
+ ExactDynamicCastInfo ExactCastInfo =
+ getExactDynamicCastInfo(SrcRecordTy, DestTy, DestRecordTy);
+ if (!ExactCastInfo.Offset) {
// If there are no public inheritance paths, the cast always fails.
+ AuthenticateVTable(ThisAddr, SrcDecl);
CGF.EmitBranch(CastFail);
return llvm::PoisonValue::get(CGF.VoidPtrTy);
}
+ bool PerformPostCastAuthentication = false;
+ llvm::Value *VTable = nullptr;
+ if (ExactCastInfo.RequiresCastToPrimaryBase) {
+ // Base appears in at least two different places. Find the most-derived
+ // object and see if it's a DestDecl. Note that the most-derived object
+ // must be at least as aligned as this base class subobject, and must
+ // have a vptr at offset 0.
+ llvm::Value *PrimaryBase =
+ 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);
+
// Compare the vptr against the expected vptr for the destination type at
- // this offset. Note that we do not know what type ThisAddr points to in
- // the case where the derived class multiply inherits from the base class
- // so we can't use GetVTablePtr, so we load the vptr directly instead.
- llvm::Instruction *VPtr = CGF.Builder.CreateLoad(
- ThisAddr.withElementType(CGF.VoidPtrPtrTy), "vtable");
- CGM.DecorateInstructionWithTBAA(
- VPtr, CGM.getTBAAVTablePtrAccessInfo(CGF.VoidPtrPtrTy));
- llvm::Value *Success = CGF.Builder.CreateICmpEQ(
- VPtr, getVTableAddressPoint(BaseSubobject(SrcDecl, *Offset), DestDecl));
- llvm::Value *Result = ThisAddr.emitRawPointer(CGF);
- if (!Offset->isZero())
- Result = CGF.Builder.CreateInBoundsGEP(
- CGF.CharTy, Result,
- {llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset->getQuantity())});
+ // this offset.
+ llvm::Constant *ExpectedVTable = getVTableAddressPoint(
+ BaseSubobject(SrcDecl, *ExactCastInfo.Offset), DestDecl);
+ llvm::Value *Success = CGF.Builder.CreateICmpEQ(VTable, ExpectedVTable);
+ llvm::Value *AdjustedThisPtr = ThisAddr.emitRawPointer(CGF);
+
+ if (!ExactCastInfo.Offset->isZero()) {
+ CharUnits::QuantityType Offset = ExactCastInfo.Offset->getQuantity();
+ llvm::Constant *OffsetConstant =
+ 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 Result;
+ return AdjustedThisPtr;
}
llvm::Value *ItaniumCXXABI::emitDynamicCastToVoid(CodeGenFunction &CGF,
diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
index 9a8ce1997a7f9..bf202d14c3398 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp
@@ -13,3 +13,19 @@ B *exact(A *a) {
// EXACT-NOT: call {{.*}} @__dynamic_cast
return dynamic_cast<B*>(a);
}
+
+struct C {
+ virtual ~C();
+};
+
+struct D final : private C {
+
+};
+
+// CHECK-LABEL: @_Z5exactP1C
+D *exact(C *a) {
+ // INEXACT: call {{.*}} @__dynamic_cast
+ // EXACT: entry:
+ // EXACT-NEXT: ret ptr null
+ return dynamic_cast<D*>(a);
+}
diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
index 86e1965b4ba68..adb9d6a6f9cf4 100644
--- a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
+++ b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp
@@ -9,6 +9,7 @@ struct E : A { int e; };
struct F : virtual A { int f; };
struct G : virtual A { int g; };
struct H final : C, D, E, F, G { int h; };
+struct H1 final: C, private D { int h1; };
// CHECK-LABEL: @_Z7inexactP1A
C *inexact(A *a) {
@@ -77,10 +78,44 @@ H *exact_multi(A *a) {
return dynamic_cast<H*>(a);
}
+// CHECK-LABEL: @_Z19exact_invalid_multiP1D
+H1 *exact_invalid_multi(D* d) {
+ // CHECK: dynamic_cast.end:
+ // CHECK-NEXT: ret ptr null
+ return dynamic_cast<H1*>(d);
+}
+
+// CHECK-LABEL: @_Z19exact_invalid_multiR1D
+H1 &exact_invalid_multi(D& d) {
+ // CHECK: dynamic_cast.notnull:
+ // CHECK-NEXT: br label %dynamic_cast.null
+ // CHECK: dynamic_cast.null:
+ // CHECK-NEXT: call void @__cxa_bad_cast()
+ // CHECK-NEXT: unreachable
+ return dynamic_cast<H1&>(d);
+}
+
+namespace GH137518 {
+ class base { virtual void fn() = 0; };
+ class test final : base { virtual void fn() { } };
+ test* new_test() { return new test(); }
+
+ // CHECK-LABEL: @_ZN8GH1375184castEPNS_4baseE(
+ test* cast(base* b) {
+ // CHECK: dynamic_cast.notnull:
+ // CHECK: br label %dynamic_cast.null
+ // CHECK: dynamic_cast.null:
+ // CHECK: br label %dynamic_cast.end
+ // CHECK: dynamic_cast.end:
+ // CHECK: ret ptr null
+ return dynamic_cast<test*>(b);
+ }
+}
+
namespace GH64088 {
// Ensure we mark the B vtable as used here, because we're going to emit a
// reference to it.
- // CHECK: define {{.*}} @_ZN7GH640881BD0
+ // CHECK: define {{.*}} void @_ZN7GH640881BD0Ev(
struct A { virtual ~A(); };
struct B final : A { virtual ~B() = default; };
B *cast(A *p) { return dynamic_cast<B*>(p); }
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..7ce5071aa5c4d
--- /dev/null
+++ b/clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp
@@ -0,0 +1,135 @@
+// 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: dynamic_cast.notnull: ; preds = %entry
+ // CHECK: [[VPTR:%.*]] = load ptr, ptr %d, align 8, !tbaa !2
+ // CHECK: [[VPTR_ADDRI:%.*]] = ptrtoint ptr %d to i64
+ // CHECK: [[VPTR_DISC:%.*]] = tail call i64 @llvm.ptrauth.blend(i64 [[SRC_VPTR_ADDRI]], i64 12810)
+ // CHECK: [[VPTRI:%.*]] = ptrtoint ptr [[VPTR]] to i64
+ // CHECK: tail call i64 @llvm.ptrauth.auth(i64 [[VPTRI]], i32 2, i64 [[VPTR_DISC]])
+ // CHECK: br label %dynamic_cast.end
+ // CHECK: dynamic_cast.end:
+ // CHECK: ret ptr null
+ return dynamic_cast<M*>(d);
+}
More information about the cfe-commits
mailing list