diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -765,29 +765,35 @@ /// struct C { }; /// /// C *f(B* b) { return dynamic_cast(b); } -bool CXXDynamicCastExpr::isAlwaysNull() const -{ +bool CXXDynamicCastExpr::isAlwaysNull() const { + if (isValueDependent() || getCastKind() != CK_Dynamic) + return false; + QualType SrcType = getSubExpr()->getType(); QualType DestType = getType(); - if (const auto *SrcPTy = SrcType->getAs()) { - SrcType = SrcPTy->getPointeeType(); - DestType = DestType->castAs()->getPointeeType(); + if (DestType->isVoidPointerType()) + return false; + + if (DestType->isPointerType()) { + SrcType = SrcType->getPointeeType(); + DestType = DestType->getPointeeType(); } - if (DestType->isVoidType()) - return false; + const auto *SrcRD = SrcType->getAsCXXRecordDecl(); + const auto *DestRD = DestType->getAsCXXRecordDecl(); + assert(SrcRD && DestRD); - const auto *SrcRD = - cast(SrcType->castAs()->getDecl()); + if (SrcRD->isEffectivelyFinal()) { + assert(!SrcRD->isDerivedFrom(DestRD) && + "upcasts should not use CK_Dynamic"); + return true; + } - if (!SrcRD->hasAttr()) - return false; + if (DestRD->isEffectivelyFinal() && !DestRD->isDerivedFrom(SrcRD)) + return true; - const auto *DestRD = - cast(DestType->castAs()->getDecl()); - - return !DestRD->isDerivedFrom(SrcRD); + return false; } CXXReinterpretCastExpr * diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -287,6 +287,7 @@ virtual bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, QualType SrcRecordTy) = 0; + virtual bool shouldEmitExactDynamicCast() = 0; virtual llvm::Value * EmitDynamicCastCall(CodeGenFunction &CGF, Address Value, @@ -298,6 +299,14 @@ QualType SrcRecordTy, QualType DestTy) = 0; + /// Emit a dynamic_cast from SrcRecordTy to DestRecordTy. The cast fails if + /// the dynamic type of Value is not exactly DestRecordTy. + virtual llvm::Value *EmitExactDynamicCast(CodeGenFunction &CGF, Address Value, + QualType SrcRecordTy, + QualType DestTy, + QualType DestRecordTy, + llvm::BasicBlock *CastFail) = 0; + virtual bool EmitBadCastCall(CodeGenFunction &CGF) = 0; virtual llvm::Value *GetVirtualBaseClassOffset(CodeGenFunction &CGF, diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -2230,7 +2230,6 @@ if (!CGF.CGM.getCXXABI().EmitBadCastCall(CGF)) return nullptr; - CGF.EmitBlock(CGF.createBasicBlock("dynamic_cast.end")); return llvm::UndefValue::get(DestLTy); } @@ -2244,17 +2243,16 @@ // C++ [expr.dynamic.cast]p7: // If T is "pointer to cv void," then the result is a pointer to the most // derived object pointed to by v. - const PointerType *DestPTy = DestTy->getAs(); - - bool isDynamicCastToVoid; + bool IsDynamicCastToVoid = DestTy->isVoidPointerType(); QualType SrcRecordTy; QualType DestRecordTy; - if (DestPTy) { - isDynamicCastToVoid = DestPTy->getPointeeType()->isVoidType(); + if (IsDynamicCastToVoid) { + SrcRecordTy = SrcTy->getPointeeType(); + // No DestRecordTy. + } else if (const PointerType *DestPTy = DestTy->getAs()) { SrcRecordTy = SrcTy->castAs()->getPointeeType(); DestRecordTy = DestPTy->getPointeeType(); } else { - isDynamicCastToVoid = false; SrcRecordTy = SrcTy; DestRecordTy = DestTy->castAs()->getPointeeType(); } @@ -2267,18 +2265,29 @@ EmitTypeCheck(TCK_DynamicOperation, DCE->getExprLoc(), ThisAddr.getPointer(), SrcRecordTy); - if (DCE->isAlwaysNull()) - if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy)) + if (DCE->isAlwaysNull()) { + if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy)) { + // Expression emission is expected to retain a valid insertion point. + if (!Builder.GetInsertBlock()) + EmitBlock(createBasicBlock("dynamic_cast.unreachable")); return T; + } + } assert(SrcRecordTy->isRecordType() && "source type must be a record type!"); + // If the destination is effectively final, the cast succeeds if and only + // if the dynamic type of the pointer is exactly the destination type. + bool IsExact = !IsDynamicCastToVoid && + DestRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() && + CGM.getCXXABI().shouldEmitExactDynamicCast(); + // C++ [expr.dynamic.cast]p4: // If the value of v is a null pointer value in the pointer case, the result // is the null pointer value of type T. bool ShouldNullCheckSrcValue = - CGM.getCXXABI().shouldDynamicCastCallBeNullChecked(SrcTy->isPointerType(), - SrcRecordTy); + IsExact || CGM.getCXXABI().shouldDynamicCastCallBeNullChecked( + SrcTy->isPointerType(), SrcRecordTy); llvm::BasicBlock *CastNull = nullptr; llvm::BasicBlock *CastNotNull = nullptr; @@ -2294,30 +2303,44 @@ } llvm::Value *Value; - if (isDynamicCastToVoid) { + if (IsDynamicCastToVoid) { Value = CGM.getCXXABI().EmitDynamicCastToVoid(*this, ThisAddr, SrcRecordTy, DestTy); + } else if (IsExact) { + // If the destination type is effectively final, this pointer points to the + // right type if and only if its vptr has the right value. + Value = CGM.getCXXABI().EmitExactDynamicCast( + *this, ThisAddr, SrcRecordTy, DestTy, DestRecordTy, CastNull); } else { assert(DestRecordTy->isRecordType() && "destination type must be a record type!"); Value = CGM.getCXXABI().EmitDynamicCastCall(*this, ThisAddr, SrcRecordTy, DestTy, DestRecordTy, CastEnd); - CastNotNull = Builder.GetInsertBlock(); } + CastNotNull = Builder.GetInsertBlock(); + llvm::Value *NullValue = nullptr; if (ShouldNullCheckSrcValue) { EmitBranch(CastEnd); EmitBlock(CastNull); + NullValue = EmitDynamicCastToNull(*this, DestTy); + + auto *Block = Builder.GetInsertBlock(); + if (Block && !Block->getTerminator()) + CastNull = Block; + else + CastNull = nullptr; + EmitBranch(CastEnd); } EmitBlock(CastEnd); - if (ShouldNullCheckSrcValue) { + if (CastNull) { llvm::PHINode *PHI = Builder.CreatePHI(Value->getType(), 2); PHI->addIncoming(Value, CastNotNull); - PHI->addIncoming(llvm::Constant::getNullValue(Value->getType()), CastNull); + PHI->addIncoming(NullValue, CastNull); Value = PHI; } diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -184,12 +184,18 @@ bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, QualType SrcRecordTy) override; + bool shouldEmitExactDynamicCast() override { return true; } llvm::Value *EmitDynamicCastCall(CodeGenFunction &CGF, Address Value, QualType SrcRecordTy, QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastEnd) override; + llvm::Value *EmitExactDynamicCast(CodeGenFunction &CGF, Address ThisAddr, + QualType SrcRecordTy, QualType DestTy, + QualType DestRecordTy, + llvm::BasicBlock *CastFail) override; + llvm::Value *EmitDynamicCastToVoid(CodeGenFunction &CGF, Address Value, QualType SrcRecordTy, QualType DestTy) override; @@ -1486,6 +1492,96 @@ return Value; } +llvm::Value *ItaniumCXXABI::EmitExactDynamicCast( + CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy, + QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastFail) { + ASTContext &Context = getContext(); + + // Find all the inheritance paths. + const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); + const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl(); + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + (void)DestDecl->isDerivedFrom(SrcDecl, Paths); + + // Find all offsets within `DestDecl` where a `SrcDecl` instance and its vptr + // might appear. + llvm::SmallVector Offsets; + for (const CXXBasePath &Path : Paths) { + // dynamic_cast only finds public inheritance paths. + if (Path.Access != AS_public) + continue; + + CharUnits Offset; + for (const CXXBasePathElement &PathElement : Path) { + // Find the offset along this inheritance step. + const CXXRecordDecl *Base = + PathElement.Base->getType()->getAsCXXRecordDecl(); + if (PathElement.Base->isVirtual()) { + // For a virtual base class, we know that the derived class is exactly + // DestDecl, so we can use the vbase offset from its layout. + const ASTRecordLayout &L = Context.getASTRecordLayout(DestDecl); + Offset = L.getVBaseClassOffset(Base); + } else { + const ASTRecordLayout &L = + Context.getASTRecordLayout(PathElement.Class); + Offset += L.getBaseClassOffset(Base); + } + } + Offsets.push_back(Offset); + } + + // We might have found multiple paths to a virtual base. Deduplicate them. + llvm::sort(Offsets); + Offsets.erase(std::unique(Offsets.begin(), Offsets.end()), Offsets.end()); + + if (Offsets.empty()) { + // If there are no inheritance paths, the cast always fails. + CGF.Builder.CreateBr(CastFail); + return llvm::UndefValue::get(CGF.VoidPtrTy); + } + + llvm::BasicBlock *SuccessBlock = CGF.createBasicBlock("dynamic_cast.success"); + llvm::PHINode *OffsetPhi = nullptr; + if (Offsets.size() > 1) + OffsetPhi = llvm::PHINode::Create(CGF.PtrDiffTy, Offsets.size(), + "dynamic_cast.offset", SuccessBlock); + llvm::Value *OffsetValue = OffsetPhi; + + // Compare the vptr against each vptr for DestDecl. + llvm::Value *VPtr = CGF.GetVTablePtr(ThisAddr, CGF.VoidPtrPtrTy, SrcDecl); + for (auto [I, Offset] : llvm::enumerate(Offsets)) { + llvm::BasicBlock *NextBlock; + if (I == Offsets.size() - 1) + NextBlock = CastFail; + else + NextBlock = CGF.createBasicBlock("dynamic_cast.cont"); + + llvm::Value *Success = CGF.Builder.CreateICmpEQ( + VPtr, getVTableAddressPoint(BaseSubobject(SrcDecl, Offset), DestDecl)); + CGF.Builder.CreateCondBr(Success, SuccessBlock, NextBlock); + + llvm::Value *SrcToDestAdjustment = + llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset.getQuantity()); + if (OffsetPhi) + OffsetPhi->addIncoming(SrcToDestAdjustment, CGF.Builder.GetInsertBlock()); + else + OffsetValue = SrcToDestAdjustment; + + if (I != Offsets.size() - 1) + CGF.EmitBlock(NextBlock); + } + + CGF.EmitBlock(SuccessBlock); + + // FIXME: Should IRBuilder fold this for us? + if (Offsets.size() == 1 && Offsets[0].isZero()) + return ThisAddr.getPointer(); + + return CGF.Builder.CreateInBoundsGEP(CGF.CharTy, ThisAddr.getPointer(), + {OffsetValue}); +} + llvm::Value *ItaniumCXXABI::EmitDynamicCastToVoid(CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy, diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -152,6 +152,17 @@ bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, QualType SrcRecordTy) override; + bool shouldEmitExactDynamicCast() override { + // TODO: Add support for exact dynamic_casts. + return false; + } + llvm::Value *EmitExactDynamicCast(CodeGenFunction &CGF, Address Value, + QualType SrcRecordTy, QualType DestTy, + QualType DestRecordTy, + llvm::BasicBlock *CastFail) override { + llvm_unreachable("unsupported"); + } + llvm::Value *EmitDynamicCastCall(CodeGenFunction &CGF, Address Value, QualType SrcRecordTy, QualType DestTy, QualType DestRecordTy, diff --git a/clang/test/CodeGenCXX/dynamic-cast-always-null.cpp b/clang/test/CodeGenCXX/dynamic-cast-always-null.cpp --- a/clang/test/CodeGenCXX/dynamic-cast-always-null.cpp +++ b/clang/test/CodeGenCXX/dynamic-cast-always-null.cpp @@ -3,21 +3,48 @@ struct B final : A { }; struct C { virtual ~C(); int c; }; +// CHECK: @_Z8nonnull1P1C +A *nonnull1(C* c) { + // CHECK: call {{.*}} @__dynamic_cast + return dynamic_cast(c); +} + +// CHECK: @_Z8nonnull2P1A +C *nonnull2(A* a) { + // CHECK: call {{.*}} @__dynamic_cast + return dynamic_cast(a); +} + // CHECK: @_Z1fP1B C *f(B* b) { - // CHECK-NOT: call ptr @__dynamic_cast + // CHECK-NOT: call {{.*}} @__dynamic_cast // CHECK: ret ptr null return dynamic_cast(b); } // CHECK: @_Z1fR1B C &f(B& b) { - // CHECK-NOT: call ptr @__dynamic_cast + // CHECK-NOT: call {{.*}} @__dynamic_cast // CHECK: call void @__cxa_bad_cast() [[NR:#[0-9]+]] // CHECK: ret ptr undef return dynamic_cast(b); } +// CHECK: @_Z1gP1C +B *g(C* c) { + // CHECK-NOT: call {{.*}} @__dynamic_cast + // CHECK: ret ptr null + return dynamic_cast(c); +} + +// CHECK: @_Z1gR1C +B &g(C& c) { + // CHECK-NOT: call {{.*}} @__dynamic_cast + // CHECK: call void @__cxa_bad_cast() [[NR:#[0-9]+]] + // CHECK: ret ptr undef + return dynamic_cast(c); +} + void dont_crash() { (void) dynamic_cast((A*)0); (void) dynamic_cast((B*)0); diff --git a/clang/test/CodeGenCXX/dynamic-cast-exact.cpp b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/dynamic-cast-exact.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -emit-llvm -fcxx-exceptions -fexceptions -std=c++11 -o - | FileCheck %s --implicit-check-not='call {{.*}} @__dynamic_cast' +struct A { virtual ~A(); }; +struct B final : A { }; + +struct C { virtual ~C(); int c; }; +struct D : A { int d; }; +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; }; + +// CHECK-LABEL: @_Z7inexactP1A +C *inexact(A *a) { + // CHECK: call {{.*}} @__dynamic_cast + return dynamic_cast(a); +} + +// CHECK-LABEL: @_Z12exact_singleP1A +B *exact_single(A *a) { + // CHECK: %[[PTR_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null + // CHECK: br i1 %[[PTR_NULL]], label %[[LABEL_NULL:.*]], label %[[LABEL_NOTNULL:.*]] + + // CHECK: [[LABEL_NOTNULL]]: + // CHECK: %[[VPTR:.*]] = load ptr, ptr %[[PTR]] + // CHECK: %[[MATCH:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV1B, i32 0, inrange i32 0, i32 2) + // CHECK: br i1 %[[MATCH]], label %[[LABEL_SUCCESS:.*]], label %[[LABEL_NULL]] + + // CHECK: [[LABEL_SUCCESS]]: + // CHECK: br label %[[LABEL_END:.*]] + + // CHECK: [[LABEL_NULL]]: + // CHECK: br label %[[LABEL_END]] + + // CHECK: [[LABEL_END]]: + // CHECK: phi ptr [ %[[PTR]], %[[LABEL_SUCCESS]] ], [ null, %[[LABEL_NULL]] ] + return dynamic_cast(a); +} + +// CHECK-LABEL: @_Z9exact_refR1A +B &exact_ref(A &a) { + // CHECK: %[[PTR_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null + // CHECK: br i1 %[[PTR_NULL]], label %[[LABEL_NULL:.*]], label %[[LABEL_NOTNULL:.*]] + + // CHECK: [[LABEL_NOTNULL]]: + // CHECK: %[[VPTR:.*]] = load ptr, ptr %[[PTR]] + // CHECK: %[[MATCH:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [4 x ptr] }, ptr @_ZTV1B, i32 0, inrange i32 0, i32 2) + // CHECK: br i1 %[[MATCH]], label %[[LABEL_SUCCESS:.*]], label %[[LABEL_NULL]] + + // CHECK: [[LABEL_SUCCESS]]: + // CHECK: br label %[[LABEL_END:.*]] + + // CHECK: [[LABEL_NULL]]: + // CHECK: call {{.*}} @__cxa_bad_cast + // CHECK: unreachable + + // CHECK: [[LABEL_END]]: + // CHECK: ret ptr %[[PTR]] + return dynamic_cast(a); +} + +// CHECK-LABEL: @_Z11exact_multiP1A +H *exact_multi(A *a) { + // CHECK: %[[PTR_NULL:.*]] = icmp eq ptr %[[PTR:.*]], null + // CHECK: br i1 %[[PTR_NULL]], label %[[LABEL_NULL:.*]], label %[[LABEL_NOTNULL:.*]] + + // CHECK: [[LABEL_NOTNULL]]: + // CHECK: %[[VPTR:.*]] = load ptr, ptr %[[PTR]] + // CHECK: %[[MATCH_1:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [5 x ptr], [4 x ptr], [4 x ptr], [6 x ptr], [6 x ptr] }, ptr @_ZTV1H, i32 0, inrange i32 1, i32 2) + // CHECK: br i1 %[[MATCH_1]], label %[[LABEL_SUCCESS:.*]], label %[[LABEL_CONT_1:.*]] + + // CHECK: [[LABEL_CONT_1]]: + // CHECK: %[[MATCH_2:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [5 x ptr], [4 x ptr], [4 x ptr], [6 x ptr], [6 x ptr] }, ptr @_ZTV1H, i32 0, inrange i32 2, i32 2) + // CHECK: br i1 %[[MATCH_2]], label %[[LABEL_SUCCESS:.*]], label %[[LABEL_CONT_2:.*]] + + // CHECK: [[LABEL_CONT_2]]: + // CHECK: %[[MATCH_3:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [5 x ptr], [4 x ptr], [4 x ptr], [6 x ptr], [6 x ptr] }, ptr @_ZTV1H, i32 0, inrange i32 3, i32 4) + // CHECK: br i1 %[[MATCH_3]], label %[[LABEL_SUCCESS:.*]], label %[[LABEL_NULL]] + + // CHECK: [[LABEL_SUCCESS]]: + // CHECK: %[[SUCCESS_OFFSET:.*]] = phi i64 [ -16, %[[LABEL_NOTNULL]] ], [ -32, %[[LABEL_CONT_1]] ], [ -48, %[[LABEL_CONT_2]] ] + // CHECK: %[[SUCCESS_VALUE:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 %[[SUCCESS_OFFSET]] + // CHECK: br label %[[LABEL_END:.*]] + + // CHECK: [[LABEL_NULL]]: + // CHECK: br label %[[LABEL_END]] + + // CHECK: [[LABEL_END]]: + // CHECK: phi ptr [ %[[SUCCESS_VALUE]], %[[LABEL_SUCCESS]] ], [ null, %[[LABEL_NULL]] ] + return dynamic_cast(a); +}