diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -36,6 +36,7 @@ CODEGENOPT(Dwarf32 , 1, 1) ///< -gdwarf32. CODEGENOPT(PreserveAsmComments, 1, 1) ///< -dA, -fno-preserve-as-comments. CODEGENOPT(AssumeSaneOperatorNew , 1, 1) ///< implicit __attribute__((malloc)) operator new +CODEGENOPT(AssumeUniqueVTables , 1, 1) ///< Assume a class has only one vtable. CODEGENOPT(Autolink , 1, 1) ///< -fno-autolink CODEGENOPT(ObjCAutoRefCountExceptions , 1, 0) ///< Whether ARC should be EH-safe. CODEGENOPT(Backchain , 1, 0) ///< -mbackchain diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1253,6 +1253,13 @@ def : Flag<["-"], "shared-libasan">, Alias; def fasm : Flag<["-"], "fasm">, Group; +defm assume_unique_vtables : BoolFOption<"assume-unique-vtables", + CodeGenOpts<"AssumeUniqueVTables">, DefaultTrue, + PosFlag, + NegFlag, + BothFlags<[CoreOption]>>; + def fassume_sane_operator_new : Flag<["-"], "fassume-sane-operator-new">, Group; def fastcp : Flag<["-"], "fastcp">, Group; def fastf : Flag<["-"], "fastf">, Group; 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 @@ -767,29 +767,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(QualType DestRecordTy) = 0; virtual llvm::Value *emitDynamicCastCall(CodeGenFunction &CGF, Address Value, QualType SrcRecordTy, @@ -298,6 +299,15 @@ Address Value, QualType SrcRecordTy) = 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 *CastSuccess, + 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 @@ -2226,8 +2226,8 @@ if (!CGF.CGM.getCXXABI().EmitBadCastCall(CGF)) return nullptr; - CGF.EmitBlock(CGF.createBasicBlock("dynamic_cast.end")); - return llvm::UndefValue::get(DestLTy); + CGF.Builder.ClearInsertionPoint(); + return llvm::PoisonValue::get(DestLTy); } llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr, @@ -2240,17 +2240,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(); } @@ -2263,18 +2262,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(DestRecordTy); + // 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; @@ -2290,29 +2300,38 @@ } llvm::Value *Value; - if (isDynamicCastToVoid) { + if (IsDynamicCastToVoid) { Value = CGM.getCXXABI().emitDynamicCastToVoid(*this, ThisAddr, SrcRecordTy); + } 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, CastEnd, 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); + CastNull = Builder.GetInsertBlock(); + 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/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -926,6 +926,13 @@ // Return the function body address of the given function. llvm::Constant *GetFunctionStart(const ValueDecl *Decl); + // Return whether RTTI information should be emitted for this target. + bool shouldEmitRTTI(bool ForEH = false) { + return (ForEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice && + !(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && + getTriple().isNVPTX()); + } + /// Get the address of the RTTI descriptor for the given type. llvm::Constant *GetAddrOfRTTIDescriptor(QualType Ty, bool ForEH = false); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7148,9 +7148,7 @@ // Return a bogus pointer if RTTI is disabled, unless it's for EH. // FIXME: should we even be calling this method if RTTI is disabled // and it's not for EH? - if ((!ForEH && !getLangOpts().RTTI) || getLangOpts().CUDAIsDevice || - (getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice && - getTriple().isNVPTX())) + if (!shouldEmitRTTI(ForEH)) return llvm::Constant::getNullValue(Int8PtrTy); if (ForEH && Ty->isObjCObjectPointerType() && 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 @@ -36,6 +36,8 @@ #include "llvm/IR/Value.h" #include "llvm/Support/ScopedPrinter.h" +#include + using namespace clang; using namespace CodeGen; @@ -185,11 +187,56 @@ bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, QualType SrcRecordTy) override; + /// Determine whether we know that all instances of type RecordTy will have + /// the same vtable pointer values, that is distinct from all other vtable + /// pointers. While this is required by the Itanium ABI, it doesn't happen in + /// practice in some cases due to language extensions. + bool hasUniqueVTablePointer(QualType RecordTy) { + const CXXRecordDecl *RD = RecordTy->getAsCXXRecordDecl(); + + // Under -fapple-kext, multiple definitions of the same vtable may be + // emitted. + if (!CGM.getCodeGenOpts().AssumeUniqueVTables || + getContext().getLangOpts().AppleKext) + return false; + + // If the type_info* would be null, the vtable might be merged with that of + // another type. + if (!CGM.shouldEmitRTTI()) + return false; + + // If there's only one definition of the vtable in the program, it has a + // unique address. + if (!llvm::GlobalValue::isWeakForLinker(CGM.getVTableLinkage(RD))) + return true; + + // Even if there are multiple definitions of the vtable, they are required + // by the ABI to use the same symbol name, so should be merged at load + // time. However, if the class has hidden visibility, there can be + // different versions of the class in different modules, and the ABI + // library might treat them as being the same. + if (CGM.GetLLVMVisibility(RD->getVisibility()) != + llvm::GlobalValue::DefaultVisibility) + return false; + + return true; + } + + bool shouldEmitExactDynamicCast(QualType DestRecordTy) override { + return hasUniqueVTablePointer(DestRecordTy); + } + 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 *CastSuccess, + llvm::BasicBlock *CastFail) override; + llvm::Value *emitDynamicCastToVoid(CodeGenFunction &CGF, Address Value, QualType SrcRecordTy) override; @@ -1202,7 +1249,8 @@ // Track back to entry -2 and pull out the offset there. llvm::Value *OffsetPtr = CGF.Builder.CreateConstInBoundsGEP1_64( CGF.IntPtrTy, VTable, -2, "complete-offset.ptr"); - llvm::Value *Offset = CGF.Builder.CreateAlignedLoad(CGF.IntPtrTy, OffsetPtr, CGF.getPointerAlign()); + llvm::Value *Offset = CGF.Builder.CreateAlignedLoad(CGF.IntPtrTy, OffsetPtr, + CGF.getPointerAlign()); // Apply the offset. llvm::Value *CompletePtr = @@ -1463,6 +1511,84 @@ return Value; } +llvm::Value *ItaniumCXXABI::emitExactDynamicCast( + CodeGenFunction &CGF, Address ThisAddr, QualType SrcRecordTy, + QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastSuccess, + 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 an offset within `DestDecl` where a `SrcDecl` instance and its vptr + // might appear. + std::optional Offset; + for (const CXXBasePath &Path : Paths) { + // dynamic_cast only finds public inheritance paths. + if (Path.Access != AS_public) + continue; + + CharUnits PathOffset; + 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); + PathOffset = L.getVBaseClassOffset(Base); + } else { + const ASTRecordLayout &L = + Context.getASTRecordLayout(PathElement.Class); + PathOffset += L.getBaseClassOffset(Base); + } + } + + 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; + } + } + + if (!Offset) { + // If there are no public inheritance paths, the cast always fails. + CGF.EmitBranch(CastFail); + return llvm::PoisonValue::get(CGF.VoidPtrTy); + } + + // 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.getPointer(); + if (!Offset->isZero()) + Result = CGF.Builder.CreateInBoundsGEP( + CGF.CharTy, Result, + {llvm::ConstantInt::get(CGF.PtrDiffTy, -Offset->getQuantity())}); + CGF.Builder.CreateCondBr(Success, CastSuccess, CastFail); + return Result; +} + 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 @@ -153,6 +153,18 @@ bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, QualType SrcRecordTy) override; + bool shouldEmitExactDynamicCast(QualType DestRecordTy) 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 *CastSuccess, + llvm::BasicBlock *CastFail) override { + llvm_unreachable("unsupported"); + } + llvm::Value *emitDynamicCastCall(CodeGenFunction &CGF, Address Value, QualType SrcRecordTy, QualType DestTy, QualType DestRecordTy, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6866,6 +6866,10 @@ Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new); + // -fassume-unique-vtables is on by default. + Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables, + options::OPT_fno_assume_unique_vtables); + // -frelaxed-template-template-args is off by default, as it is a severe // breaking change until a corresponding change to template partial ordering // is provided. 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,52 @@ 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 + // CHECK: unreachable + // + // CHECK: ret ptr poison 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: unreachable + // + // CHECK: ret ptr poison + 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-disabled.cpp b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/dynamic-cast-exact-disabled.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,EXACT +// RUN: %clang_cc1 -I%S %s -triple x86_64-apple-darwin10 -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 -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 -fno-assume-unique-vtables -emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=CHECK,INEXACT + +struct A { virtual ~A(); }; +struct B final : A { }; + +// CHECK-LABEL: @_Z5exactP1A +B *exact(A *a) { + // INEXACT: call {{.*}} @__dynamic_cast + // EXACT-NOT: call {{.*}} @__dynamic_cast + return dynamic_cast(a); +} 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,78 @@ +// 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 Offset { virtual ~Offset(); }; +struct A { virtual ~A(); }; +struct B final : Offset, 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_FAILED:.*]], label %[[LABEL_NOTNULL:.*]] + + // CHECK: [[LABEL_NOTNULL]]: + // CHECK: %[[VPTR:.*]] = load ptr, ptr %[[PTR]] + // CHECK: %[[MATCH:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [4 x ptr], [4 x ptr] }, ptr @_ZTV1B, i32 0, inrange i32 1, i32 2) + // CHECK: %[[RESULT:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 -8 + // CHECK: br i1 %[[MATCH]], label %[[LABEL_END:.*]], label %[[LABEL_FAILED]] + + // CHECK: [[LABEL_FAILED]]: + // CHECK: br label %[[LABEL_END]] + + // CHECK: [[LABEL_END]]: + // CHECK: phi ptr [ %[[RESULT]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_FAILED]] ] + 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_FAILED:.*]], label %[[LABEL_NOTNULL:.*]] + + // CHECK: [[LABEL_NOTNULL]]: + // CHECK: %[[VPTR:.*]] = load ptr, ptr %[[PTR]] + // CHECK: %[[MATCH:.*]] = icmp eq ptr %[[VPTR]], getelementptr inbounds ({ [4 x ptr], [4 x ptr] }, ptr @_ZTV1B, i32 0, inrange i32 1, i32 2) + // CHECK: %[[RESULT:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 -8 + // CHECK: br i1 %[[MATCH]], label %[[LABEL_END:.*]], label %[[LABEL_FAILED]] + + // CHECK: [[LABEL_FAILED]]: + // CHECK: call {{.*}} @__cxa_bad_cast + // CHECK: unreachable + + // CHECK: [[LABEL_END]]: + // CHECK: ret ptr %[[RESULT]] + 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_FAILED:.*]], label %[[LABEL_NOTNULL:.*]] + + // CHECK: [[LABEL_NOTNULL]]: + // CHECK: %[[VPTR:.*]] = load ptr, ptr %[[PTR]] + // CHECK: %[[OFFSET_TO_TOP_SLOT:.*]] = getelementptr inbounds i64, ptr %[[VPTR]], i64 -2 + // CHECK: %[[OFFSET_TO_TOP:.*]] = load i64, ptr %[[OFFSET_TO_TOP_SLOT]] + // CHECK: %[[RESULT:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 %[[OFFSET_TO_TOP]] + // CHECK: %[[DERIVED_VPTR:.*]] = load ptr, ptr %[[RESULT]] + // CHECK: %[[MATCH:.*]] = icmp eq ptr %[[DERIVED_VPTR]], getelementptr inbounds ({ [5 x ptr], [4 x ptr], [4 x ptr], [6 x ptr], [6 x ptr] }, ptr @_ZTV1H, i32 0, inrange i32 0, i32 3) + // CHECK: br i1 %[[MATCH]], label %[[LABEL_END:.*]], label %[[LABEL_FAILED]] + + // CHECK: [[LABEL_FAILED]]: + // CHECK: br label %[[LABEL_END]] + + // CHECK: [[LABEL_END]]: + // CHECK: phi ptr [ %[[RESULT]], %[[LABEL_NOTNULL]] ], [ null, %[[LABEL_FAILED]] ] + return dynamic_cast(a); +} diff --git a/clang/test/Driver/fassume-unique-vtables.cpp b/clang/test/Driver/fassume-unique-vtables.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Driver/fassume-unique-vtables.cpp @@ -0,0 +1,4 @@ +// RUN: %clang -### -fno-assume-unique-vtables %s -S 2>&1 | FileCheck -check-prefix=CHECK-OPT %s +// RUN: %clang -### -fno-assume-unique-vtables -fassume-unique-vtables %s -S 2>&1 | FileCheck -check-prefix=CHECK-NOOPT %s +// CHECK-OPT: "-fno-assume-unique-vtables" +// CHECK-NOOPT-NOT: "-fno-assume-unique-vtables"