https://github.com/ojhunt created https://github.com/llvm/llvm-project/pull/152076
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 >From e5cc79d81a1013b8b2e5da1e2f7d4ceca10112b2 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] 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/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 ++++++++++++++++++ 5 files changed, 295 insertions(+), 35 deletions(-) create mode 100644 clang/test/CodeGenCXX/ptrauth-dynamic-cast-exact.cpp 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); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits