From 0a6c5e2fb555389b25778fce867aa7a3740a916e Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Sun, 22 Jun 2014 12:42:29 +0000 Subject: [PATCH] Revert r211402 (and r211408,r211410), "CodeGen: Refactor dynamic_cast and typeid" It crashes msvc codegen in clang/test/SemaCXX/undefined-internal.cpp. It is reproducible with: $ clang -cc1 -triple i686-win32 -emit-llvm-only clang/test/SemaCXX/undefined-internal.cpp llvm-svn: 211467 --- clang/lib/CodeGen/CGCXXABI.h | 22 - clang/lib/CodeGen/CGExprCXX.cpp | 418 +++++++++++++++--- clang/lib/CodeGen/ItaniumCXXABI.cpp | 209 --------- clang/lib/CodeGen/MicrosoftCXXABI.cpp | 147 ------ .../CodeGenCXX/microsoft-abi-dynamic-cast.cpp | 238 +++++----- .../test/CodeGenCXX/microsoft-abi-typeid.cpp | 73 +-- 6 files changed, 542 insertions(+), 565 deletions(-) diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h index 82b4e883974f..82687746eda4 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -207,28 +207,6 @@ public: llvm::Value *ptr, QualType type) = 0; - virtual bool shouldTypeidBeNullChecked(bool IsDeref, - QualType SrcRecordTy) = 0; - virtual void EmitBadTypeidCall(CodeGenFunction &CGF) = 0; - virtual llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy, - llvm::Value *ThisPtr, - llvm::Type *StdTypeInfoPtrTy) = 0; - - virtual bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) = 0; - - virtual llvm::Value * - EmitDynamicCastCall(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy, QualType DestTy, - QualType DestRecordTy, llvm::BasicBlock *CastEnd) = 0; - - virtual llvm::Value *EmitDynamicCastToVoid(CodeGenFunction &CGF, - llvm::Value *Value, - QualType SrcRecordTy, - QualType DestTy) = 0; - - virtual bool EmitBadCastCall(CodeGenFunction &CGF) = 0; - virtual llvm::Value *GetVirtualBaseClassOffset(CodeGenFunction &CGF, llvm::Value *This, const CXXRecordDecl *ClassDecl, diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 47dc9fbbc730..0b5a415a0ea8 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1615,36 +1615,98 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { EmitBlock(DeleteEnd); } +static llvm::Constant *getBadTypeidFn(CodeGenFunction &CGF) { + // void __cxa_bad_typeid(); + llvm::FunctionType *FTy = llvm::FunctionType::get(CGF.VoidTy, false); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_bad_typeid"); +} + +static void EmitBadTypeidCall(CodeGenFunction &CGF) { + llvm::Value *Fn = getBadTypeidFn(CGF); + CGF.EmitRuntimeCallOrInvoke(Fn).setDoesNotReturn(); + CGF.Builder.CreateUnreachable(); +} + +/// \brief Gets the offset to the virtual base that contains the vfptr for +/// MS-ABI polymorphic types. +static llvm::Value *getPolymorphicOffset(CodeGenFunction &CGF, + const CXXRecordDecl *RD, + llvm::Value *Value) { + const ASTContext &Context = RD->getASTContext(); + for (const CXXBaseSpecifier &Base : RD->vbases()) + if (Context.getASTRecordLayout(Base.getType()->getAsCXXRecordDecl()) + .hasExtendableVFPtr()) + return CGF.CGM.getCXXABI().GetVirtualBaseClassOffset( + CGF, Value, RD, Base.getType()->getAsCXXRecordDecl()); + llvm_unreachable("One of our vbases should be polymorphic."); +} + +static llvm::Value *emitRTtypeidCall(CodeGenFunction &CGF, + llvm::Value *Argument) { + llvm::Type *ArgTypes[] = {CGF.Int8PtrTy}; + llvm::Constant *Function = CGF.CGM.CreateRuntimeFunction( + llvm::FunctionType::get(CGF.Int8PtrTy, ArgTypes, false), "__RTtypeid"); + llvm::Value *Args[] = {Argument}; + return CGF.EmitRuntimeCall(Function, Args); +} + static llvm::Value *EmitTypeidFromVTable(CodeGenFunction &CGF, const Expr *E, llvm::Type *StdTypeInfoPtrTy) { // Get the vtable pointer. llvm::Value *ThisPtr = CGF.EmitLValue(E).getAddress(); + if (CGF.getTarget().getCXXABI().isMicrosoft()) { + llvm::Value *CastPtr = CGF.Builder.CreateBitCast(ThisPtr, CGF.Int8PtrTy); + const CXXRecordDecl *RD = E->getType()->getAsCXXRecordDecl(); + if (CGF.getContext().getASTRecordLayout(RD).hasExtendableVFPtr()) + return CGF.Builder.CreateBitCast(emitRTtypeidCall(CGF, CastPtr), + StdTypeInfoPtrTy); + llvm::BasicBlock *EntryBlock = CGF.Builder.GetInsertBlock(); + llvm::BasicBlock *AdjustBlock = CGF.createBasicBlock("type_id.valid"); + llvm::BasicBlock *ExitBlock = CGF.createBasicBlock("type_id.call"); + CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNull(CastPtr), ExitBlock, + AdjustBlock); + // Emit the call block and code for it. + CGF.EmitBlock(AdjustBlock); + llvm::Value *AdjustedThisPtr = CGF.Builder.CreateInBoundsGEP( + CastPtr, getPolymorphicOffset(CGF, RD, CastPtr)); + // Emit the call block and the phi nodes for it. + CGF.EmitBlock(ExitBlock); + llvm::PHINode *ValuePHI = CGF.Builder.CreatePHI(CGF.Int8PtrTy, 2); + ValuePHI->addIncoming(AdjustedThisPtr, AdjustBlock); + ValuePHI->addIncoming(llvm::Constant::getNullValue(CGF.Int8PtrTy), + EntryBlock); + return CGF.Builder.CreateBitCast(emitRTtypeidCall(CGF, ValuePHI), + StdTypeInfoPtrTy); + } + // C++ [expr.typeid]p2: // If the glvalue expression is obtained by applying the unary * operator to // a pointer and the pointer is a null pointer value, the typeid expression // throws the std::bad_typeid exception. - bool IsDeref = false; - if (const UnaryOperator *UO = dyn_cast(E->IgnoreParens())) - if (UO->getOpcode() == UO_Deref) - IsDeref = true; - - QualType SrcRecordTy = E->getType(); - if (CGF.CGM.getCXXABI().shouldTypeidBeNullChecked(IsDeref, SrcRecordTy)) { - llvm::BasicBlock *BadTypeidBlock = + if (const UnaryOperator *UO = dyn_cast(E->IgnoreParens())) { + if (UO->getOpcode() == UO_Deref) { + llvm::BasicBlock *BadTypeidBlock = CGF.createBasicBlock("typeid.bad_typeid"); - llvm::BasicBlock *EndBlock = CGF.createBasicBlock("typeid.end"); + llvm::BasicBlock *EndBlock = + CGF.createBasicBlock("typeid.end"); - llvm::Value *IsNull = CGF.Builder.CreateIsNull(ThisPtr); - CGF.Builder.CreateCondBr(IsNull, BadTypeidBlock, EndBlock); + llvm::Value *IsNull = CGF.Builder.CreateIsNull(ThisPtr); + CGF.Builder.CreateCondBr(IsNull, BadTypeidBlock, EndBlock); - CGF.EmitBlock(BadTypeidBlock); - CGF.CGM.getCXXABI().EmitBadTypeidCall(CGF); - CGF.EmitBlock(EndBlock); + CGF.EmitBlock(BadTypeidBlock); + EmitBadTypeidCall(CGF); + CGF.EmitBlock(EndBlock); + } } - return CGF.CGM.getCXXABI().EmitTypeid(CGF, SrcRecordTy, ThisPtr, - StdTypeInfoPtrTy); + llvm::Value *Value = CGF.GetVTablePtr(ThisPtr, + StdTypeInfoPtrTy->getPointerTo()); + + // Load the type info. + Value = CGF.Builder.CreateConstInBoundsGEP1_64(Value, -1ULL); + return CGF.Builder.CreateLoad(Value); } llvm::Value *CodeGenFunction::EmitCXXTypeidExpr(const CXXTypeidExpr *E) { @@ -1671,6 +1733,173 @@ llvm::Value *CodeGenFunction::EmitCXXTypeidExpr(const CXXTypeidExpr *E) { StdTypeInfoPtrTy); } +static llvm::Constant *getItaniumDynamicCastFn(CodeGenFunction &CGF) { + // void *__dynamic_cast(const void *sub, + // const abi::__class_type_info *src, + // const abi::__class_type_info *dst, + // std::ptrdiff_t src2dst_offset); + + llvm::Type *Int8PtrTy = CGF.Int8PtrTy; + llvm::Type *PtrDiffTy = + CGF.ConvertType(CGF.getContext().getPointerDiffType()); + + llvm::Type *Args[4] = { Int8PtrTy, Int8PtrTy, Int8PtrTy, PtrDiffTy }; + + llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, Args, false); + + // Mark the function as nounwind readonly. + llvm::Attribute::AttrKind FuncAttrs[] = { llvm::Attribute::NoUnwind, + llvm::Attribute::ReadOnly }; + llvm::AttributeSet Attrs = llvm::AttributeSet::get( + CGF.getLLVMContext(), llvm::AttributeSet::FunctionIndex, FuncAttrs); + + return CGF.CGM.CreateRuntimeFunction(FTy, "__dynamic_cast", Attrs); +} + +static llvm::Constant *getBadCastFn(CodeGenFunction &CGF) { + // void __cxa_bad_cast(); + llvm::FunctionType *FTy = llvm::FunctionType::get(CGF.VoidTy, false); + return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_bad_cast"); +} + +static void EmitBadCastCall(CodeGenFunction &CGF) { + llvm::Value *Fn = getBadCastFn(CGF); + CGF.EmitRuntimeCallOrInvoke(Fn).setDoesNotReturn(); + CGF.Builder.CreateUnreachable(); +} + +/// \brief Compute the src2dst_offset hint as described in the +/// Itanium C++ ABI [2.9.7] +static CharUnits computeOffsetHint(ASTContext &Context, + const CXXRecordDecl *Src, + const CXXRecordDecl *Dst) { + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + + // If Dst is not derived from Src we can skip the whole computation below and + // return that Src is not a public base of Dst. Record all inheritance paths. + if (!Dst->isDerivedFrom(Src, Paths)) + return CharUnits::fromQuantity(-2ULL); + + unsigned NumPublicPaths = 0; + CharUnits Offset; + + // Now walk all possible inheritance paths. + for (CXXBasePaths::paths_iterator I = Paths.begin(), E = Paths.end(); + I != E; ++I) { + if (I->Access != AS_public) // Ignore non-public inheritance. + continue; + + ++NumPublicPaths; + + for (CXXBasePath::iterator J = I->begin(), JE = I->end(); J != JE; ++J) { + // If the path contains a virtual base class we can't give any hint. + // -1: no hint. + if (J->Base->isVirtual()) + return CharUnits::fromQuantity(-1ULL); + + if (NumPublicPaths > 1) // Won't use offsets, skip computation. + continue; + + // Accumulate the base class offsets. + const ASTRecordLayout &L = Context.getASTRecordLayout(J->Class); + Offset += L.getBaseClassOffset(J->Base->getType()->getAsCXXRecordDecl()); + } + } + + // -2: Src is not a public base of Dst. + if (NumPublicPaths == 0) + return CharUnits::fromQuantity(-2ULL); + + // -3: Src is a multiple public base type but never a virtual base type. + if (NumPublicPaths > 1) + return CharUnits::fromQuantity(-3ULL); + + // Otherwise, the Src type is a unique public nonvirtual base type of Dst. + // Return the offset of Src from the origin of Dst. + return Offset; +} + +static llvm::Value * +EmitItaniumDynamicCastCall(CodeGenFunction &CGF, llvm::Value *Value, + QualType SrcTy, QualType DestTy, + llvm::BasicBlock *CastEnd) { + llvm::Type *PtrDiffLTy = + CGF.ConvertType(CGF.getContext().getPointerDiffType()); + llvm::Type *DestLTy = CGF.ConvertType(DestTy); + + if (const PointerType *PTy = DestTy->getAs()) { + if (PTy->getPointeeType()->isVoidType()) { + // 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. + + // Get the vtable pointer. + llvm::Value *VTable = CGF.GetVTablePtr(Value, PtrDiffLTy->getPointerTo()); + + // Get the offset-to-top from the vtable. + llvm::Value *OffsetToTop = + CGF.Builder.CreateConstInBoundsGEP1_64(VTable, -2ULL); + OffsetToTop = CGF.Builder.CreateLoad(OffsetToTop, "offset.to.top"); + + // Finally, add the offset to the pointer. + Value = CGF.EmitCastToVoidPtr(Value); + Value = CGF.Builder.CreateInBoundsGEP(Value, OffsetToTop); + + return CGF.Builder.CreateBitCast(Value, DestLTy); + } + } + + QualType SrcRecordTy; + QualType DestRecordTy; + + if (const PointerType *DestPTy = DestTy->getAs()) { + SrcRecordTy = SrcTy->castAs()->getPointeeType(); + DestRecordTy = DestPTy->getPointeeType(); + } else { + SrcRecordTy = SrcTy; + DestRecordTy = DestTy->castAs()->getPointeeType(); + } + + assert(SrcRecordTy->isRecordType() && "source type must be a record type!"); + assert(DestRecordTy->isRecordType() && "dest type must be a record type!"); + + llvm::Value *SrcRTTI = + CGF.CGM.GetAddrOfRTTIDescriptor(SrcRecordTy.getUnqualifiedType()); + llvm::Value *DestRTTI = + CGF.CGM.GetAddrOfRTTIDescriptor(DestRecordTy.getUnqualifiedType()); + + // Compute the offset hint. + const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); + const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl(); + llvm::Value *OffsetHint = + llvm::ConstantInt::get(PtrDiffLTy, + computeOffsetHint(CGF.getContext(), SrcDecl, + DestDecl).getQuantity()); + + // Emit the call to __dynamic_cast. + Value = CGF.EmitCastToVoidPtr(Value); + + llvm::Value *args[] = { Value, SrcRTTI, DestRTTI, OffsetHint }; + Value = CGF.EmitNounwindRuntimeCall(getItaniumDynamicCastFn(CGF), args); + Value = CGF.Builder.CreateBitCast(Value, DestLTy); + + /// C++ [expr.dynamic.cast]p9: + /// A failed cast to reference type throws std::bad_cast + if (DestTy->isReferenceType()) { + llvm::BasicBlock *BadCastBlock = + CGF.createBasicBlock("dynamic_cast.bad_cast"); + + llvm::Value *IsNull = CGF.Builder.CreateIsNull(Value); + CGF.Builder.CreateCondBr(IsNull, BadCastBlock, CastEnd); + + CGF.EmitBlock(BadCastBlock); + EmitBadCastCall(CGF); + } + + return Value; +} + static llvm::Value *EmitDynamicCastToNull(CodeGenFunction &CGF, QualType DestTy) { llvm::Type *DestLTy = CGF.ConvertType(DestTy); @@ -1679,49 +1908,142 @@ static llvm::Value *EmitDynamicCastToNull(CodeGenFunction &CGF, /// C++ [expr.dynamic.cast]p9: /// A failed cast to reference type throws std::bad_cast - if (!CGF.CGM.getCXXABI().EmitBadCastCall(CGF)) - return nullptr; + EmitBadCastCall(CGF); CGF.EmitBlock(CGF.createBasicBlock("dynamic_cast.end")); return llvm::UndefValue::get(DestLTy); } +namespace { +struct MSDynamicCastBuilder { + MSDynamicCastBuilder(CodeGenFunction &CGF, const CXXDynamicCastExpr *DCE); + llvm::Value *emitDynamicCastCall(llvm::Value *Value); + llvm::Value *emitDynamicCast(llvm::Value *Value); + + CodeGenFunction &CGF; + CGBuilderTy &Builder; + llvm::PointerType *Int8PtrTy; + QualType SrcTy, DstTy; + const CXXRecordDecl *SrcDecl; + bool IsPtrCast, IsCastToVoid, IsCastOfNull; +}; +} // namespace + +MSDynamicCastBuilder::MSDynamicCastBuilder(CodeGenFunction &CGF, + const CXXDynamicCastExpr *DCE) + : CGF(CGF), Builder(CGF.Builder), Int8PtrTy(CGF.Int8PtrTy), + SrcDecl(nullptr) { + DstTy = DCE->getTypeAsWritten(); + IsPtrCast = DstTy->isPointerType(); + // Get the PointeeTypes. After this point the original types are not used. + DstTy = IsPtrCast ? DstTy->castAs()->getPointeeType() + : DstTy->castAs()->getPointeeType(); + IsCastToVoid = DstTy->isVoidType(); + IsCastOfNull = DCE->isAlwaysNull(); + if (IsCastOfNull) + return; + SrcTy = DCE->getSubExpr()->getType(); + SrcTy = IsPtrCast ? SrcTy->castAs()->getPointeeType() : SrcTy; + SrcDecl = SrcTy->getAsCXXRecordDecl(); + // If we don't need a base adjustment, we don't need a SrcDecl so clear it + // here. Later we use the existence of the SrcDecl to determine the need for + // a base adjustment. + if (CGF.getContext().getASTRecordLayout(SrcDecl).hasExtendableVFPtr()) + SrcDecl = nullptr; +} + +llvm::Value *MSDynamicCastBuilder::emitDynamicCastCall(llvm::Value *Value) { + llvm::IntegerType *Int32Ty = CGF.Int32Ty; + llvm::Value *Offset = llvm::ConstantInt::get(Int32Ty, 0); + Value = Builder.CreateBitCast(Value, Int8PtrTy); + // If we need to perform a base adjustment, do it here. + if (SrcDecl) { + Offset = getPolymorphicOffset(CGF, SrcDecl, Value); + Value = Builder.CreateInBoundsGEP(Value, Offset); + Offset = Builder.CreateTrunc(Offset, Int32Ty); + } + if (IsCastToVoid) { + // PVOID __RTCastToVoid( + // PVOID inptr) + llvm::Type *ArgTypes[] = {Int8PtrTy}; + llvm::Constant *Function = CGF.CGM.CreateRuntimeFunction( + llvm::FunctionType::get(Int8PtrTy, ArgTypes, false), "__RTCastToVoid"); + llvm::Value *Args[] = {Value}; + return CGF.EmitRuntimeCall(Function, Args); + } + // PVOID __RTDynamicCast( + // PVOID inptr, + // LONG VfDelta, + // PVOID SrcType, + // PVOID TargetType, + // BOOL isReference) + llvm::Type *ArgTypes[] = {Int8PtrTy, Int32Ty, Int8PtrTy, Int8PtrTy, Int32Ty}; + llvm::Constant *Function = CGF.CGM.CreateRuntimeFunction( + llvm::FunctionType::get(Int8PtrTy, ArgTypes, false), "__RTDynamicCast"); + llvm::Value *Args[] = { + Value, Offset, + CGF.CGM.GetAddrOfRTTIDescriptor(SrcTy.getUnqualifiedType()), + CGF.CGM.GetAddrOfRTTIDescriptor(DstTy.getUnqualifiedType()), + llvm::ConstantInt::get(Int32Ty, IsPtrCast ? 0 : 1)}; + return CGF.EmitRuntimeCall(Function, Args); +} + +llvm::Value *MSDynamicCastBuilder::emitDynamicCast(llvm::Value *Value) { + // Note about undefined behavior: If the dynamic cast is casting to a + // reference type and the input is null, we hit a grey area in the standard. + // Here we're interpreting the behavior as undefined. The effects are the + // following: If the compiler determines that the argument is statically null + // or if the argument is dynamically null but does not require base + // adjustment, __RTDynamicCast will be called with a null argument and the + // isreference bit set. In this case __RTDynamicCast will throw + // std::bad_cast. If the argument is dynamically null and a base adjustment is + // required the resulting code will produce an out of bounds memory reference + // when trying to read VBTblPtr. In Itanium mode clang also emits a vtable + // load that fails at run time. + llvm::PointerType *DstLTy = CGF.ConvertType(DstTy)->getPointerTo(); + if (IsCastOfNull && IsPtrCast) + return Builder.CreateBitCast(Value, DstLTy); + if (IsCastOfNull || !IsPtrCast || !SrcDecl) + return Builder.CreateBitCast(emitDynamicCastCall(Value), DstLTy); + // !IsCastOfNull && IsPtrCast && SrcDecl + // In this case we have a pointer that requires a base adjustment. An + // adjustment is only required if the pointer is actually valid so here we + // perform a null check before doing the base adjustment and calling + // __RTDynamicCast. In the case that the argument is null we simply return + // null without calling __RTDynamicCast. + llvm::BasicBlock *EntryBlock = Builder.GetInsertBlock(); + llvm::BasicBlock *CallBlock = CGF.createBasicBlock("dynamic_cast.valid"); + llvm::BasicBlock *ExitBlock = CGF.createBasicBlock("dynamic_cast.call"); + Builder.CreateCondBr(Builder.CreateIsNull(Value), ExitBlock, CallBlock); + // Emit the call block and code for it. + CGF.EmitBlock(CallBlock); + Value = emitDynamicCastCall(Value); + // Emit the call block and the phi nodes for it. + CGF.EmitBlock(ExitBlock); + llvm::PHINode *ValuePHI = Builder.CreatePHI(Int8PtrTy, 2); + ValuePHI->addIncoming(Value, CallBlock); + ValuePHI->addIncoming(llvm::Constant::getNullValue(Int8PtrTy), EntryBlock); + return Builder.CreateBitCast(ValuePHI, DstLTy); +} + llvm::Value *CodeGenFunction::EmitDynamicCast(llvm::Value *Value, const CXXDynamicCastExpr *DCE) { + if (getTarget().getCXXABI().isMicrosoft()) { + MSDynamicCastBuilder Builder(*this, DCE); + return Builder.emitDynamicCast(Value); + } + QualType DestTy = DCE->getTypeAsWritten(); if (DCE->isAlwaysNull()) - if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy)) - return T; + return EmitDynamicCastToNull(*this, DestTy); QualType SrcTy = DCE->getSubExpr()->getType(); - // 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; - QualType SrcRecordTy; - QualType DestRecordTy; - if (DestPTy) { - isDynamicCastToVoid = DestPTy->getPointeeType()->isVoidType(); - SrcRecordTy = SrcTy->castAs()->getPointeeType(); - DestRecordTy = DestPTy->getPointeeType(); - } else { - isDynamicCastToVoid = false; - SrcRecordTy = SrcTy; - DestRecordTy = DestTy->castAs()->getPointeeType(); - } - - assert(SrcRecordTy->isRecordType() && "source type must be a record type!"); - // 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); + bool ShouldNullCheckSrcValue = SrcTy->isPointerType(); llvm::BasicBlock *CastNull = nullptr; llvm::BasicBlock *CastNotNull = nullptr; @@ -1736,15 +2058,7 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(llvm::Value *Value, EmitBlock(CastNotNull); } - if (isDynamicCastToVoid) { - Value = CGM.getCXXABI().EmitDynamicCastToVoid(*this, Value, SrcRecordTy, - DestTy); - } else { - assert(DestRecordTy->isRecordType() && - "destination type must be a record type!"); - Value = CGM.getCXXABI().EmitDynamicCastCall(*this, Value, SrcRecordTy, - DestTy, DestRecordTy, CastEnd); - } + Value = EmitItaniumDynamicCastCall(*this, Value, SrcTy, DestTy, CastEnd); if (ShouldNullCheckSrcValue) { EmitBranch(CastEnd); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 38d49d134e9d..be8dac140ede 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -25,7 +25,6 @@ #include "CodeGenModule.h" #include "clang/AST/Mangle.h" #include "clang/AST/Type.h" -#include "llvm/IR/CallSite.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Value.h" @@ -109,26 +108,6 @@ public: llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF, llvm::Value *ptr, QualType type) override; - bool shouldTypeidBeNullChecked(bool IsDeref, QualType SrcRecordTy) override; - void EmitBadTypeidCall(CodeGenFunction &CGF) override; - llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy, - llvm::Value *ThisPtr, - llvm::Type *StdTypeInfoPtrTy) override; - - bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) override; - - llvm::Value *EmitDynamicCastCall(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy, QualType DestTy, - QualType DestRecordTy, - llvm::BasicBlock *CastEnd) override; - - llvm::Value *EmitDynamicCastToVoid(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy, - QualType DestTy) override; - - bool EmitBadCastCall(CodeGenFunction &CGF) override; - llvm::Value * GetVirtualBaseClassOffset(CodeGenFunction &CGF, llvm::Value *This, const CXXRecordDecl *ClassDecl, @@ -821,194 +800,6 @@ llvm::Value *ItaniumCXXABI::adjustToCompleteObject(CodeGenFunction &CGF, return CGF.Builder.CreateInBoundsGEP(ptr, offset); } -static llvm::Constant *getItaniumDynamicCastFn(CodeGenFunction &CGF) { - // void *__dynamic_cast(const void *sub, - // const abi::__class_type_info *src, - // const abi::__class_type_info *dst, - // std::ptrdiff_t src2dst_offset); - - llvm::Type *Int8PtrTy = CGF.Int8PtrTy; - llvm::Type *PtrDiffTy = - CGF.ConvertType(CGF.getContext().getPointerDiffType()); - - llvm::Type *Args[4] = { Int8PtrTy, Int8PtrTy, Int8PtrTy, PtrDiffTy }; - - llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, Args, false); - - // Mark the function as nounwind readonly. - llvm::Attribute::AttrKind FuncAttrs[] = { llvm::Attribute::NoUnwind, - llvm::Attribute::ReadOnly }; - llvm::AttributeSet Attrs = llvm::AttributeSet::get( - CGF.getLLVMContext(), llvm::AttributeSet::FunctionIndex, FuncAttrs); - - return CGF.CGM.CreateRuntimeFunction(FTy, "__dynamic_cast", Attrs); -} - -static llvm::Constant *getBadCastFn(CodeGenFunction &CGF) { - // void __cxa_bad_cast(); - llvm::FunctionType *FTy = llvm::FunctionType::get(CGF.VoidTy, false); - return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_bad_cast"); -} - -/// \brief Compute the src2dst_offset hint as described in the -/// Itanium C++ ABI [2.9.7] -static CharUnits computeOffsetHint(ASTContext &Context, - const CXXRecordDecl *Src, - const CXXRecordDecl *Dst) { - CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, - /*DetectVirtual=*/false); - - // If Dst is not derived from Src we can skip the whole computation below and - // return that Src is not a public base of Dst. Record all inheritance paths. - if (!Dst->isDerivedFrom(Src, Paths)) - return CharUnits::fromQuantity(-2ULL); - - unsigned NumPublicPaths = 0; - CharUnits Offset; - - // Now walk all possible inheritance paths. - for (CXXBasePaths::paths_iterator I = Paths.begin(), E = Paths.end(); I != E; - ++I) { - if (I->Access != AS_public) // Ignore non-public inheritance. - continue; - - ++NumPublicPaths; - - for (CXXBasePath::iterator J = I->begin(), JE = I->end(); J != JE; ++J) { - // If the path contains a virtual base class we can't give any hint. - // -1: no hint. - if (J->Base->isVirtual()) - return CharUnits::fromQuantity(-1ULL); - - if (NumPublicPaths > 1) // Won't use offsets, skip computation. - continue; - - // Accumulate the base class offsets. - const ASTRecordLayout &L = Context.getASTRecordLayout(J->Class); - Offset += L.getBaseClassOffset(J->Base->getType()->getAsCXXRecordDecl()); - } - } - - // -2: Src is not a public base of Dst. - if (NumPublicPaths == 0) - return CharUnits::fromQuantity(-2ULL); - - // -3: Src is a multiple public base type but never a virtual base type. - if (NumPublicPaths > 1) - return CharUnits::fromQuantity(-3ULL); - - // Otherwise, the Src type is a unique public nonvirtual base type of Dst. - // Return the offset of Src from the origin of Dst. - return Offset; -} - -static llvm::Constant *getBadTypeidFn(CodeGenFunction &CGF) { - // void __cxa_bad_typeid(); - llvm::FunctionType *FTy = llvm::FunctionType::get(CGF.VoidTy, false); - - return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_bad_typeid"); -} - -bool ItaniumCXXABI::shouldTypeidBeNullChecked(bool IsDeref, - QualType SrcRecordTy) { - return IsDeref; -} - -void ItaniumCXXABI::EmitBadTypeidCall(CodeGenFunction &CGF) { - llvm::Value *Fn = getBadTypeidFn(CGF); - CGF.EmitRuntimeCallOrInvoke(Fn).setDoesNotReturn(); - CGF.Builder.CreateUnreachable(); -} - -llvm::Value *ItaniumCXXABI::EmitTypeid(CodeGenFunction &CGF, - QualType SrcRecordTy, - llvm::Value *ThisPtr, - llvm::Type *StdTypeInfoPtrTy) { - llvm::Value *Value = - CGF.GetVTablePtr(ThisPtr, StdTypeInfoPtrTy->getPointerTo()); - - // Load the type info. - Value = CGF.Builder.CreateConstInBoundsGEP1_64(Value, -1ULL); - return CGF.Builder.CreateLoad(Value); -} - -bool ItaniumCXXABI::shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) { - return SrcIsPtr; -} - -llvm::Value *ItaniumCXXABI::EmitDynamicCastCall( - CodeGenFunction &CGF, llvm::Value *Value, QualType SrcRecordTy, - QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastEnd) { - llvm::Type *PtrDiffLTy = - CGF.ConvertType(CGF.getContext().getPointerDiffType()); - llvm::Type *DestLTy = CGF.ConvertType(DestTy); - - llvm::Value *SrcRTTI = - CGF.CGM.GetAddrOfRTTIDescriptor(SrcRecordTy.getUnqualifiedType()); - llvm::Value *DestRTTI = - CGF.CGM.GetAddrOfRTTIDescriptor(DestRecordTy.getUnqualifiedType()); - - // Compute the offset hint. - const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); - const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl(); - llvm::Value *OffsetHint = llvm::ConstantInt::get( - PtrDiffLTy, - computeOffsetHint(CGF.getContext(), SrcDecl, DestDecl).getQuantity()); - - // Emit the call to __dynamic_cast. - Value = CGF.EmitCastToVoidPtr(Value); - - llvm::Value *args[] = {Value, SrcRTTI, DestRTTI, OffsetHint}; - Value = CGF.EmitNounwindRuntimeCall(getItaniumDynamicCastFn(CGF), args); - Value = CGF.Builder.CreateBitCast(Value, DestLTy); - - /// C++ [expr.dynamic.cast]p9: - /// A failed cast to reference type throws std::bad_cast - if (DestTy->isReferenceType()) { - llvm::BasicBlock *BadCastBlock = - CGF.createBasicBlock("dynamic_cast.bad_cast"); - - llvm::Value *IsNull = CGF.Builder.CreateIsNull(Value); - CGF.Builder.CreateCondBr(IsNull, BadCastBlock, CastEnd); - - CGF.EmitBlock(BadCastBlock); - EmitBadCastCall(CGF); - } - - return Value; -} - -llvm::Value *ItaniumCXXABI::EmitDynamicCastToVoid(CodeGenFunction &CGF, - llvm::Value *Value, - QualType SrcRecordTy, - QualType DestTy) { - llvm::Type *PtrDiffLTy = - CGF.ConvertType(CGF.getContext().getPointerDiffType()); - llvm::Type *DestLTy = CGF.ConvertType(DestTy); - - // Get the vtable pointer. - llvm::Value *VTable = CGF.GetVTablePtr(Value, PtrDiffLTy->getPointerTo()); - - // Get the offset-to-top from the vtable. - llvm::Value *OffsetToTop = - CGF.Builder.CreateConstInBoundsGEP1_64(VTable, -2ULL); - OffsetToTop = CGF.Builder.CreateLoad(OffsetToTop, "offset.to.top"); - - // Finally, add the offset to the pointer. - Value = CGF.EmitCastToVoidPtr(Value); - Value = CGF.Builder.CreateInBoundsGEP(Value, OffsetToTop); - - return CGF.Builder.CreateBitCast(Value, DestLTy); -} - -bool ItaniumCXXABI::EmitBadCastCall(CodeGenFunction &CGF) { - llvm::Value *Fn = getBadCastFn(CGF); - CGF.EmitRuntimeCallOrInvoke(Fn).setDoesNotReturn(); - CGF.Builder.CreateUnreachable(); - return true; -} - llvm::Value * ItaniumCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF, llvm::Value *This, diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index e19904f49f79..ca2aaa2edf1f 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -21,7 +21,6 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/VTableBuilder.h" #include "llvm/ADT/StringSet.h" -#include "llvm/IR/CallSite.h" using namespace clang; using namespace CodeGen; @@ -57,26 +56,6 @@ public: llvm::Value *ptr, QualType type) override; - bool shouldTypeidBeNullChecked(bool IsDeref, QualType SrcRecordTy) override; - void EmitBadTypeidCall(CodeGenFunction &CGF) override; - llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy, - llvm::Value *ThisPtr, - llvm::Type *StdTypeInfoPtrTy) override; - - bool shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) override; - - llvm::Value *EmitDynamicCastCall(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy, QualType DestTy, - QualType DestRecordTy, - llvm::BasicBlock *CastEnd) override; - - llvm::Value *EmitDynamicCastToVoid(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy, - QualType DestTy) override; - - bool EmitBadCastCall(CodeGenFunction &CGF) override; - llvm::Value * GetVirtualBaseClassOffset(CodeGenFunction &CGF, llvm::Value *This, const CXXRecordDecl *ClassDecl, @@ -490,132 +469,6 @@ llvm::Value *MicrosoftCXXABI::adjustToCompleteObject(CodeGenFunction &CGF, return ptr; } -/// \brief Gets the offset to the virtual base that contains the vfptr for -/// MS-ABI polymorphic types. -static llvm::Value *getPolymorphicOffset(CodeGenFunction &CGF, - const CXXRecordDecl *RD, - llvm::Value *Value) { - const ASTContext &Context = RD->getASTContext(); - for (const CXXBaseSpecifier &Base : RD->vbases()) - if (Context.getASTRecordLayout(Base.getType()->getAsCXXRecordDecl()) - .hasExtendableVFPtr()) - return CGF.CGM.getCXXABI().GetVirtualBaseClassOffset( - CGF, Value, RD, Base.getType()->getAsCXXRecordDecl()); - llvm_unreachable("One of our vbases should be polymorphic."); -} - -static std::pair -performBaseAdjustment(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy) { - Value = CGF.Builder.CreateBitCast(Value, CGF.Int8PtrTy); - const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); - - if (CGF.getContext().getASTRecordLayout(SrcDecl).hasExtendableVFPtr()) - return std::make_pair(Value, llvm::ConstantInt::get(CGF.Int32Ty, 0)); - - // Perform a base adjustment. - llvm::Value *Offset = getPolymorphicOffset(CGF, SrcDecl, Value); - Value = CGF.Builder.CreateInBoundsGEP(Value, Offset); - Offset = CGF.Builder.CreateTrunc(Offset, CGF.Int32Ty); - return std::make_pair(Value, Offset); -} - -bool MicrosoftCXXABI::shouldTypeidBeNullChecked(bool IsDeref, - QualType SrcRecordTy) { - const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); - return IsDeref && - !CGM.getContext().getASTRecordLayout(SrcDecl).hasExtendableVFPtr(); -} - -static llvm::CallSite emitRTtypeidCall(CodeGenFunction &CGF, - llvm::Value *Argument) { - llvm::Type *ArgTypes[] = {CGF.Int8PtrTy}; - llvm::FunctionType *FTy = - llvm::FunctionType::get(CGF.Int8PtrTy, ArgTypes, false); - llvm::Value *Args[] = {Argument}; - llvm::Constant *Fn = CGF.CGM.CreateRuntimeFunction(FTy, "__RTtypeid"); - return CGF.EmitRuntimeCallOrInvoke(Fn, Args); -} - -void MicrosoftCXXABI::EmitBadTypeidCall(CodeGenFunction &CGF) { - llvm::CallSite Call = - emitRTtypeidCall(CGF, llvm::Constant::getNullValue(CGM.VoidPtrTy)); - Call.setDoesNotReturn(); - CGF.Builder.CreateUnreachable(); -} - -llvm::Value *MicrosoftCXXABI::EmitTypeid(CodeGenFunction &CGF, - QualType SrcRecordTy, - llvm::Value *ThisPtr, - llvm::Type *StdTypeInfoPtrTy) { - const CXXRecordDecl *RD = SrcRecordTy->getAsCXXRecordDecl(); - llvm::Value *CastPtr = CGF.Builder.CreateBitCast(ThisPtr, CGF.Int8PtrTy); - llvm::Value *AdjustedThisPtr = CGF.Builder.CreateInBoundsGEP( - CastPtr, getPolymorphicOffset(CGF, RD, CastPtr)); - return CGF.Builder.CreateBitCast( - emitRTtypeidCall(CGF, AdjustedThisPtr).getInstruction(), - StdTypeInfoPtrTy); -} - -bool MicrosoftCXXABI::shouldDynamicCastCallBeNullChecked(bool SrcIsPtr, - QualType SrcRecordTy) { - const CXXRecordDecl *SrcDecl = SrcRecordTy->getAsCXXRecordDecl(); - return SrcIsPtr && - !CGM.getContext().getASTRecordLayout(SrcDecl).hasExtendableVFPtr(); -} - -llvm::Value *MicrosoftCXXABI::EmitDynamicCastCall( - CodeGenFunction &CGF, llvm::Value *Value, QualType SrcRecordTy, - QualType DestTy, QualType DestRecordTy, llvm::BasicBlock *CastEnd) { - llvm::Type *DestLTy = CGF.ConvertType(DestTy); - - llvm::Value *SrcRTTI = - CGF.CGM.GetAddrOfRTTIDescriptor(SrcRecordTy.getUnqualifiedType()); - llvm::Value *DestRTTI = - CGF.CGM.GetAddrOfRTTIDescriptor(DestRecordTy.getUnqualifiedType()); - - llvm::Value *Offset; - std::tie(Value, Offset) = performBaseAdjustment(CGF, Value, SrcRecordTy); - - // PVOID __RTDynamicCast( - // PVOID inptr, - // LONG VfDelta, - // PVOID SrcType, - // PVOID TargetType, - // BOOL isReference) - llvm::Type *ArgTypes[] = {CGF.Int8PtrTy, CGF.Int32Ty, CGF.Int8PtrTy, - CGF.Int8PtrTy, CGF.Int32Ty}; - llvm::Constant *Function = CGF.CGM.CreateRuntimeFunction( - llvm::FunctionType::get(CGF.Int8PtrTy, ArgTypes, false), - "__RTDynamicCast"); - llvm::Value *Args[] = { - Value, Offset, SrcRTTI, DestRTTI, - llvm::ConstantInt::get(CGF.Int32Ty, DestTy->isReferenceType())}; - Value = CGF.EmitRuntimeCallOrInvoke(Function, Args).getInstruction(); - return CGF.Builder.CreateBitCast(Value, DestLTy); -} - -llvm::Value * -MicrosoftCXXABI::EmitDynamicCastToVoid(CodeGenFunction &CGF, llvm::Value *Value, - QualType SrcRecordTy, - QualType DestTy) { - llvm::Value *Offset; - std::tie(Value, Offset) = performBaseAdjustment(CGF, Value, SrcRecordTy); - - // PVOID __RTCastToVoid( - // PVOID inptr) - llvm::Type *ArgTypes[] = {CGF.Int8PtrTy}; - llvm::Constant *Function = CGF.CGM.CreateRuntimeFunction( - llvm::FunctionType::get(CGF.Int8PtrTy, ArgTypes, false), - "__RTCastToVoid"); - llvm::Value *Args[] = {Value}; - return CGF.EmitRuntimeCall(Function, Args); -} - -bool MicrosoftCXXABI::EmitBadCastCall(CodeGenFunction &CGF) { - return false; -} - llvm::Value * MicrosoftCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF, llvm::Value *This, diff --git a/clang/test/CodeGenCXX/microsoft-abi-dynamic-cast.cpp b/clang/test/CodeGenCXX/microsoft-abi-dynamic-cast.cpp index 2fc4a622c9a0..3084d0ea1cb2 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-dynamic-cast.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-dynamic-cast.cpp @@ -1,130 +1,158 @@ -// RUN: %clang_cc1 -emit-llvm -O1 -o - -triple=i386-pc-win32 %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -O2 -optzns -o - -triple=i386-pc-win32 2>/dev/null %s | FileCheck %s +// REQUIRES: asserts struct S { char a; }; -struct V { virtual void f(); }; +struct V { virtual void f(){} }; struct A : virtual V {}; struct B : S, virtual V {}; struct T {}; T* test0() { return dynamic_cast((B*)0); } -// CHECK-LABEL: define noalias %struct.T* @"\01?test0@@YAPAUT@@XZ"() -// CHECK: ret %struct.T* null +// CHECK: define noalias %struct.T* @"\01?test0@@YAPAUT@@XZ"() #0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret %struct.T* null +// CHECK-NEXT: } T* test1(V* x) { return &dynamic_cast(*x); } -// CHECK-LABEL: define %struct.T* @"\01?test1@@YAPAUT@@PAUV@@@Z"(%struct.V* %x) -// CHECK: [[CAST:%.*]] = bitcast %struct.V* %x to i8* -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTDynamicCast(i8* [[CAST]], i32 0, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUV@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 1) -// CHECK-NEXT: [[RET:%.*]] = bitcast i8* [[CALL]] to %struct.T* -// CHECK-NEXT: ret %struct.T* [[RET]] +// CHECK: define %struct.T* @"\01?test1@@YAPAUT@@PAUV@@@Z"(%struct.V* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = bitcast %struct.V* %x to i8* +// CHECK-NEXT: %1 = tail call i8* @__RTDynamicCast(i8* %0, i32 0, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUV@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 1) #2 +// CHECK-NEXT: %2 = bitcast i8* %1 to %struct.T* +// CHECK-NEXT: ret %struct.T* %2 +// CHECK-NEXT: } T* test2(A* x) { return &dynamic_cast(*x); } -// CHECK-LABEL: define %struct.T* @"\01?test2@@YAPAUT@@PAUA@@@Z"(%struct.A* %x) -// CHECK: [[CAST:%.*]] = bitcast %struct.A* %x to i8* -// CHECK-NEXT: [[BITCAST:%.*]] = bitcast %struct.A* %x to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[BITCAST]], align 4 -// CHECK-NEXT: [[VBOFFP:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBOFFPCAST:%.*]] = bitcast i8* [[VBOFFP]] to i32* -// CHECK-NEXT: [[VBOFFS:%.*]] = load i32* [[VBOFFPCAST]], align 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[CAST]], i32 [[VBOFFS]] -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTDynamicCast(i8* [[ADJ]], i32 [[VBOFFS]], i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUA@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 1) -// CHECK-NEXT: [[RET:%.*]] = bitcast i8* [[CALL]] to %struct.T* -// CHECK-NEXT: ret %struct.T* [[RET]] +// CHECK: define %struct.T* @"\01?test2@@YAPAUT@@PAUA@@@Z"(%struct.A* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = bitcast %struct.A* %x to i8* +// CHECK-NEXT: %1 = bitcast %struct.A* %x to i8** +// CHECK-NEXT: %vbtable = load i8** %1, align 4 +// CHECK-NEXT: %2 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %3 = bitcast i8* %2 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %3, align 4 +// CHECK-NEXT: %4 = getelementptr inbounds i8* %0, i32 %vbase_offs +// CHECK-NEXT: %5 = tail call i8* @__RTDynamicCast(i8* %4, i32 %vbase_offs, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUA@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 1) #2 +// CHECK-NEXT: %6 = bitcast i8* %5 to %struct.T* +// CHECK-NEXT: ret %struct.T* %6 +// CHECK-NEXT: } T* test3(B* x) { return &dynamic_cast(*x); } -// CHECK-LABEL: define %struct.T* @"\01?test3@@YAPAUT@@PAUB@@@Z"(%struct.B* %x) -// CHECK: [[VOIDP:%.*]] = getelementptr inbounds %struct.B* %x, i32 0, i32 0, i32 0 -// CHECK-NEXT: [[VBPTR:%.*]] = getelementptr inbounds i8* [[VOIDP]], i32 4 -// CHECK-NEXT: [[BITCAST:%.*]] = bitcast i8* [[VBPTR:%.*]] to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[BITCAST]], align 4 -// CHECK-NEXT: [[VBOFFP:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBOFFPCAST:%.*]] = bitcast i8* [[VBOFFP]] to i32* -// CHECK-NEXT: [[VBOFFS:%.*]] = load i32* [[VBOFFPCAST]], align 4 -// CHECK-NEXT: [[DELTA:%.*]] = add nsw i32 [[VBOFFS]], 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[VOIDP]], i32 [[DELTA]] -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTDynamicCast(i8* [[ADJ]], i32 [[DELTA]], i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUB@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 1) -// CHECK-NEXT: [[RET:%.*]] = bitcast i8* [[CALL]] to %struct.T* -// CHECK-NEXT: ret %struct.T* [[RET]] +// CHECK: define %struct.T* @"\01?test3@@YAPAUT@@PAUB@@@Z"(%struct.B* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = getelementptr inbounds %struct.B* %x, i32 0, i32 0, i32 0 +// CHECK-NEXT: %vbptr = getelementptr inbounds i8* %0, i32 4 +// CHECK-NEXT: %1 = bitcast i8* %vbptr to i8** +// CHECK-NEXT: %vbtable = load i8** %1, align 4 +// CHECK-NEXT: %2 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %3 = bitcast i8* %2 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %3, align 4 +// CHECK-NEXT: %4 = add nsw i32 %vbase_offs, 4 +// CHECK-NEXT: %5 = getelementptr inbounds i8* %0, i32 %4 +// CHECK-NEXT: %6 = tail call i8* @__RTDynamicCast(i8* %5, i32 %4, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUB@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 1) #2 +// CHECK-NEXT: %7 = bitcast i8* %6 to %struct.T* +// CHECK-NEXT: ret %struct.T* %7 +// CHECK-NEXT: } T* test4(V* x) { return dynamic_cast(x); } -// CHECK-LABEL: define %struct.T* @"\01?test4@@YAPAUT@@PAUV@@@Z"(%struct.V* %x) -// CHECK: [[CAST:%.*]] = bitcast %struct.V* %x to i8* -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTDynamicCast(i8* [[CAST]], i32 0, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUV@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 0) -// CHECK-NEXT: [[RET:%.*]] = bitcast i8* [[CALL]] to %struct.T* -// CHECK-NEXT: ret %struct.T* [[RET]] +// CHECK: define %struct.T* @"\01?test4@@YAPAUT@@PAUV@@@Z"(%struct.V* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = bitcast %struct.V* %x to i8* +// CHECK-NEXT: %1 = tail call i8* @__RTDynamicCast(i8* %0, i32 0, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUV@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 0) #2 +// CHECK-NEXT: %2 = bitcast i8* %1 to %struct.T* +// CHECK-NEXT: ret %struct.T* %2 +// CHECK-NEXT: } T* test5(A* x) { return dynamic_cast(x); } -// CHECK-LABEL: define %struct.T* @"\01?test5@@YAPAUT@@PAUA@@@Z"(%struct.A* %x) -// CHECK: [[CHECK:%.*]] = icmp eq %struct.A* %x, null -// CHECK-NEXT: br i1 [[CHECK]] -// CHECK: [[VOIDP:%.*]] = bitcast %struct.A* %x to i8* -// CHECK-NEXT: [[BITCAST:%.*]] = bitcast %struct.A* %x to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[BITCAST]], align 4 -// CHECK-NEXT: [[VBOFFP:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBOFFPCAST:%.*]] = bitcast i8* [[VBOFFP]] to i32* -// CHECK-NEXT: [[VBOFFS:%.*]] = load i32* [[VBOFFPCAST:%.*]], align 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[VOIDP]], i32 [[VBOFFS]] -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTDynamicCast(i8* [[ADJ]], i32 [[VBOFFS]], i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUA@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 0) -// CHECK-NEXT: [[RES:%.*]] = bitcast i8* [[CALL]] to %struct.T* -// CHECK-NEXT: br label -// CHECK: [[RET:%.*]] = phi %struct.T* -// CHECK-NEXT: ret %struct.T* [[RET]] +// CHECK: define %struct.T* @"\01?test5@@YAPAUT@@PAUA@@@Z"(%struct.A* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = icmp eq %struct.A* %x, null +// CHECK-NEXT: br i1 %0, label %dynamic_cast.call, label %dynamic_cast.valid +// CHECK: dynamic_cast.valid: ; preds = %entry +// CHECK-NEXT: %1 = bitcast %struct.A* %x to i8* +// CHECK-NEXT: %2 = bitcast %struct.A* %x to i8** +// CHECK-NEXT: %vbtable = load i8** %2, align 4 +// CHECK-NEXT: %3 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %4 = bitcast i8* %3 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %4, align 4 +// CHECK-NEXT: %5 = getelementptr inbounds i8* %1, i32 %vbase_offs +// CHECK-NEXT: %6 = tail call i8* @__RTDynamicCast(i8* %5, i32 %vbase_offs, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUA@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 0) #2 +// CHECK-NEXT: %phitmp = bitcast i8* %6 to %struct.T* +// CHECK-NEXT: br label %dynamic_cast.call +// CHECK: dynamic_cast.call: ; preds = %dynamic_cast.valid, %entry +// CHECK-NEXT: %7 = phi %struct.T* [ %phitmp, %dynamic_cast.valid ], [ null, %entry ] +// CHECK-NEXT: ret %struct.T* %7 +// CHECK-NEXT: } T* test6(B* x) { return dynamic_cast(x); } -// CHECK-LABEL: define %struct.T* @"\01?test6@@YAPAUT@@PAUB@@@Z"(%struct.B* %x) -// CHECK: [[CHECK:%.*]] = icmp eq %struct.B* %x, null -// CHECK-NEXT: br i1 [[CHECK]] -// CHECK: [[CAST:%.*]] = getelementptr inbounds %struct.B* %x, i32 0, i32 0, i32 0 -// CHECK-NEXT: [[VBPTR:%.*]] = getelementptr inbounds i8* [[CAST]], i32 4 -// CHECK-NEXT: [[BITCAST:%.*]] = bitcast i8* [[VBPTR]] to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[BITCAST]], align 4 -// CHECK-NEXT: [[VBOFFP:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBOFFPCAST:%.*]] = bitcast i8* [[VBOFFP]] to i32* -// CHECK-NEXT: [[VBOFFS:%.*]] = load i32* [[VBOFFPCAST:%.*]], align 4 -// CHECK-NEXT: [[DELTA:%.*]] = add nsw i32 [[VBOFFS]], 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[CAST]], i32 [[DELTA]] -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTDynamicCast(i8* [[ADJ]], i32 [[DELTA]], i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUB@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 0) -// CHECK-NEXT: [[RES:%.*]] = bitcast i8* [[CALL]] to %struct.T* -// CHECK-NEXT: br label -// CHECK: [[RET:%.*]] = phi %struct.T* -// CHECK-NEXT: ret %struct.T* [[RET]] +// CHECK: define %struct.T* @"\01?test6@@YAPAUT@@PAUB@@@Z"(%struct.B* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = icmp eq %struct.B* %x, null +// CHECK-NEXT: br i1 %0, label %dynamic_cast.call, label %dynamic_cast.valid +// CHECK: dynamic_cast.valid: ; preds = %entry +// CHECK-NEXT: %1 = getelementptr inbounds %struct.B* %x, i32 0, i32 0, i32 0 +// CHECK-NEXT: %vbptr = getelementptr inbounds i8* %1, i32 4 +// CHECK-NEXT: %2 = bitcast i8* %vbptr to i8** +// CHECK-NEXT: %vbtable = load i8** %2, align 4 +// CHECK-NEXT: %3 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %4 = bitcast i8* %3 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %4, align 4 +// CHECK-NEXT: %5 = add nsw i32 %vbase_offs, 4 +// CHECK-NEXT: %6 = getelementptr inbounds i8* %1, i32 %5 +// CHECK-NEXT: %7 = tail call i8* @__RTDynamicCast(i8* %6, i32 %5, i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUB@@@8" to i8*), i8* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUT@@@8" to i8*), i32 0) #2 +// CHECK-NEXT: %phitmp = bitcast i8* %7 to %struct.T* +// CHECK-NEXT: br label %dynamic_cast.call +// CHECK: dynamic_cast.call: ; preds = %dynamic_cast.valid, %entry +// CHECK-NEXT: %8 = phi %struct.T* [ %phitmp, %dynamic_cast.valid ], [ null, %entry ] +// CHECK-NEXT: ret %struct.T* %8 +// CHECK-NEXT: } void* test7(V* x) { return dynamic_cast(x); } -// CHECK-LABEL: define i8* @"\01?test7@@YAPAXPAUV@@@Z"(%struct.V* %x) -// CHECK: [[CAST:%.*]] = bitcast %struct.V* %x to i8* -// CHECK-NEXT: [[RET:%.*]] = tail call i8* @__RTCastToVoid(i8* [[CAST]]) -// CHECK-NEXT: ret i8* [[RET]] +// CHECK: define i8* @"\01?test7@@YAPAXPAUV@@@Z"(%struct.V* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = bitcast %struct.V* %x to i8* +// CHECK-NEXT: %1 = tail call i8* @__RTCastToVoid(i8* %0) #2 +// CHECK-NEXT: ret i8* %1 +// CHECK-NEXT: } void* test8(A* x) { return dynamic_cast(x); } -// CHECK-LABEL: define i8* @"\01?test8@@YAPAXPAUA@@@Z"(%struct.A* %x) -// CHECK: [[CHECK:%.*]] = icmp eq %struct.A* %x, null -// CHECK-NEXT: br i1 [[CHECK]] -// CHECK: [[VOIDP:%.*]] = bitcast %struct.A* %x to i8* -// CHECK-NEXT: [[BITCAST:%.*]] = bitcast %struct.A* %x to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[BITCAST]], align 4 -// CHECK-NEXT: [[VBOFFP:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBOFFPCAST:%.*]] = bitcast i8* [[VBOFFP]] to i32* -// CHECK-NEXT: [[VBOFFS:%.*]] = load i32* [[VBOFFPCAST:%.*]], align 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[VOIDP]], i32 [[VBOFFS]] -// CHECK-NEXT: [[RES:%.*]] = tail call i8* @__RTCastToVoid(i8* [[ADJ]]) -// CHECK-NEXT: br label -// CHECK: [[RET:%.*]] = phi i8* -// CHECK-NEXT: ret i8* [[RET]] +// CHECK: define i8* @"\01?test8@@YAPAXPAUA@@@Z"(%struct.A* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = icmp eq %struct.A* %x, null +// CHECK-NEXT: br i1 %0, label %dynamic_cast.call, label %dynamic_cast.valid +// CHECK: dynamic_cast.valid: ; preds = %entry +// CHECK-NEXT: %1 = bitcast %struct.A* %x to i8* +// CHECK-NEXT: %2 = bitcast %struct.A* %x to i8** +// CHECK-NEXT: %vbtable = load i8** %2, align 4 +// CHECK-NEXT: %3 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %4 = bitcast i8* %3 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %4, align 4 +// CHECK-NEXT: %5 = getelementptr inbounds i8* %1, i32 %vbase_offs +// CHECK-NEXT: %6 = tail call i8* @__RTCastToVoid(i8* %5) #2 +// CHECK-NEXT: br label %dynamic_cast.call +// CHECK: dynamic_cast.call: ; preds = %dynamic_cast.valid, %entry +// CHECK-NEXT: %7 = phi i8* [ %6, %dynamic_cast.valid ], [ null, %entry ] +// CHECK-NEXT: ret i8* %7 +// CHECK-NEXT: } void* test9(B* x) { return dynamic_cast(x); } -// CHECK-LABEL: define i8* @"\01?test9@@YAPAXPAUB@@@Z"(%struct.B* %x) -// CHECK: [[CHECK:%.*]] = icmp eq %struct.B* %x, null -// CHECK-NEXT: br i1 [[CHECK]] -// CHECK: [[CAST:%.*]] = getelementptr inbounds %struct.B* %x, i32 0, i32 0, i32 0 -// CHECK-NEXT: [[VBPTR:%.*]] = getelementptr inbounds i8* [[CAST]], i32 4 -// CHECK-NEXT: [[BITCAST:%.*]] = bitcast i8* [[VBPTR]] to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[BITCAST]], align 4 -// CHECK-NEXT: [[VBOFFP:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBOFFPCAST:%.*]] = bitcast i8* [[VBOFFP]] to i32* -// CHECK-NEXT: [[VBOFFS:%.*]] = load i32* [[VBOFFPCAST:%.*]], align 4 -// CHECK-NEXT: [[DELTA:%.*]] = add nsw i32 [[VBOFFS]], 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[CAST]], i32 [[DELTA]] -// CHECK-NEXT: [[CALL:%.*]] = tail call i8* @__RTCastToVoid(i8* [[ADJ]]) -// CHECK-NEXT: br label -// CHECK: [[RET:%.*]] = phi i8* -// CHECK-NEXT: ret i8* [[RET]] - +// CHECK: define i8* @"\01?test9@@YAPAXPAUB@@@Z"(%struct.B* %x) #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %0 = icmp eq %struct.B* %x, null +// CHECK-NEXT: br i1 %0, label %dynamic_cast.call, label %dynamic_cast.valid +// CHECK: dynamic_cast.valid: ; preds = %entry +// CHECK-NEXT: %1 = getelementptr inbounds %struct.B* %x, i32 0, i32 0, i32 0 +// CHECK-NEXT: %vbptr = getelementptr inbounds i8* %1, i32 4 +// CHECK-NEXT: %2 = bitcast i8* %vbptr to i8** +// CHECK-NEXT: %vbtable = load i8** %2, align 4 +// CHECK-NEXT: %3 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %4 = bitcast i8* %3 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %4, align 4 +// CHECK-NEXT: %5 = add nsw i32 %vbase_offs, 4 +// CHECK-NEXT: %6 = getelementptr inbounds i8* %1, i32 %5 +// CHECK-NEXT: %7 = tail call i8* @__RTCastToVoid(i8* %6) #2 +// CHECK-NEXT: br label %dynamic_cast.call +// CHECK: dynamic_cast.call: ; preds = %dynamic_cast.valid, %entry +// CHECK-NEXT: %8 = phi i8* [ %7, %dynamic_cast.valid ], [ null, %entry ] +// CHECK-NEXT: ret i8* %8 +// CHECK-NEXT: } diff --git a/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp b/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp index 3875c56ca425..ffbebec1faf6 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-typeid.cpp @@ -1,45 +1,58 @@ -// RUN: %clang_cc1 -emit-llvm -O1 -o - -triple=i386-pc-win32 %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -O2 -optzns -o - -triple=i386-pc-win32 2>/dev/null %s | FileCheck %s +// REQUIRES: asserts -struct type_info; +struct type_info { const char* raw_name() const; }; namespace std { using ::type_info; } -struct V { virtual void f(); }; -struct A : virtual V { A(); }; +struct V { virtual void f() {}; }; +struct A : virtual V {}; -extern A a; -extern int b; +A a; +int b; A* fn(); const std::type_info* test0_typeid() { return &typeid(int); } -// CHECK-LABEL: define %struct.type_info* @"\01?test0_typeid@@YAPBUtype_info@@XZ"() -// CHECK: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\02"* @"\01??_R0H@8" to %struct.type_info*) +// CHECK: define %struct.type_info* @"\01?test0_typeid@@YAPBUtype_info@@XZ"() #0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\02"* @"\01??_R0H@8" to %struct.type_info*) +// CHECK-NEXT: } const std::type_info* test1_typeid() { return &typeid(A); } -// CHECK-LABEL: define %struct.type_info* @"\01?test1_typeid@@YAPBUtype_info@@XZ"() -// CHECK: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUA@@@8" to %struct.type_info*) +// CHECK: define %struct.type_info* @"\01?test1_typeid@@YAPBUtype_info@@XZ"() #0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0?AUA@@@8" to %struct.type_info*) +// CHECK-NEXT: } const std::type_info* test2_typeid() { return &typeid(&a); } -// CHECK-LABEL: define %struct.type_info* @"\01?test2_typeid@@YAPBUtype_info@@XZ"() -// CHECK: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0PAUA@@@8" to %struct.type_info*) +// CHECK: define %struct.type_info* @"\01?test2_typeid@@YAPBUtype_info@@XZ"() #0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\07"* @"\01??_R0PAUA@@@8" to %struct.type_info*) +// CHECK-NEXT: } const std::type_info* test3_typeid() { return &typeid(*fn()); } -// CHECK-LABEL: define %struct.type_info* @"\01?test3_typeid@@YAPBUtype_info@@XZ"() -// CHECK: [[CALL:%.*]] = tail call %struct.A* @"\01?fn@@YAPAUA@@XZ"() -// CHECK-NEXT: [[CMP:%.*]] = icmp eq %struct.A* [[CALL]], null -// CHECK-NEXT: br i1 [[CMP]] -// CHECK: tail call i8* @__RTtypeid(i8* null) -// CHECK-NEXT: unreachable -// CHECK: [[THIS:%.*]] = bitcast %struct.A* [[CALL]] to i8* -// CHECK-NEXT: [[VBTBLP:%.*]] = bitcast %struct.A* [[CALL]] to i8** -// CHECK-NEXT: [[VBTBL:%.*]] = load i8** [[VBTBLP]], align 4 -// CHECK-NEXT: [[VBSLOT:%.*]] = getelementptr inbounds i8* [[VBTBL]], i32 4 -// CHECK-NEXT: [[VBITCST:%.*]] = bitcast i8* [[VBSLOT]] to i32* -// CHECK-NEXT: [[VBASE_OFFS:%.*]] = load i32* [[VBITCST]], align 4 -// CHECK-NEXT: [[ADJ:%.*]] = getelementptr inbounds i8* [[THIS]], i32 [[VBASE_OFFS]] -// CHECK-NEXT: [[RT:%.*]] = tail call i8* @__RTtypeid(i8* [[ADJ]]) -// CHECK-NEXT: [[RET:%.*]] = bitcast i8* [[RT]] to %struct.type_info* -// CHECK-NEXT: ret %struct.type_info* [[RET]] +// CHECK: define %struct.type_info* @"\01?test3_typeid@@YAPBUtype_info@@XZ"() #1 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %call = tail call %struct.A* @"\01?fn@@YAPAUA@@XZ"() #3 +// CHECK-NEXT: %0 = icmp eq %struct.A* %call, null +// CHECK-NEXT: br i1 %0, label %type_id.call, label %type_id.valid +// CHECK: type_id.valid: ; preds = %entry +// CHECK-NEXT: %1 = bitcast %struct.A* %call to i8* +// CHECK-NEXT: %2 = bitcast %struct.A* %call to i8** +// CHECK-NEXT: %vbtable = load i8** %2, align 4 +// CHECK-NEXT: %3 = getelementptr inbounds i8* %vbtable, i32 4 +// CHECK-NEXT: %4 = bitcast i8* %3 to i32* +// CHECK-NEXT: %vbase_offs = load i32* %4, align 4 +// CHECK-NEXT: %5 = getelementptr inbounds i8* %1, i32 %vbase_offs +// CHECK-NEXT: br label %type_id.call +// CHECK: type_id.call: ; preds = %type_id.valid, %entry +// CHECK-NEXT: %6 = phi i8* [ %5, %type_id.valid ], [ null, %entry ] +// CHECK-NEXT: %7 = tail call i8* @__RTtypeid(i8* %6) #3 +// CHECK-NEXT: %8 = bitcast i8* %7 to %struct.type_info* +// CHECK-NEXT: ret %struct.type_info* %8 +// CHECK-NEXT: } const std::type_info* test4_typeid() { return &typeid(b); } -// CHECK: define %struct.type_info* @"\01?test4_typeid@@YAPBUtype_info@@XZ"() -// CHECK: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\02"* @"\01??_R0H@8" to %struct.type_info*) +// CHECK: define %struct.type_info* @"\01?test4_typeid@@YAPBUtype_info@@XZ"() #0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: ret %struct.type_info* bitcast (%"MSRTTITypeDescriptor\02"* @"\01??_R0H@8" to %struct.type_info*) +// CHECK-NEXT: }