Author: rnk Date: Thu May 9 16:01:17 2013 New Revision: 181543 URL: http://llvm.org/viewvc/llvm-project?rev=181543&view=rev Log: [ms-cxxabi] Implement member pointer conversions
Summary: This only supports converting along non-virtual inheritance paths by changing the field offset or the non-virtual base adjustment. This implements three kinds of conversions: - codegen for Value conversions - Constant emission for APValue - Constant folding for CastExprs In almost all constant initialization settings EmitMemberPointer(APValue) is called, except when the expression contains a reinterpret cast. reinterpret casts end up being a big corner case because the null value changes between different kinds of member pointers. Reviewers: rsmith CC: cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D741 Modified: cfe/trunk/lib/CodeGen/CGCXXABI.cpp cfe/trunk/lib/CodeGen/CGCXXABI.h cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp Modified: cfe/trunk/lib/CodeGen/CGCXXABI.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.cpp?rev=181543&r1=181542&r2=181543&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGCXXABI.cpp (original) +++ cfe/trunk/lib/CodeGen/CGCXXABI.cpp Thu May 9 16:01:17 2013 @@ -251,6 +251,28 @@ llvm::Constant *CGCXXABI::getMemberPoint E->path_end()); } +CharUnits CGCXXABI::getMemberPointerPathAdjustment(const APValue &MP) { + // TODO: Store base specifiers in APValue member pointer paths so we can + // easily reuse CGM.GetNonVirtualBaseClassOffset(). + const ValueDecl *MPD = MP.getMemberPointerDecl(); + CharUnits ThisAdjustment = CharUnits::Zero(); + ArrayRef<const CXXRecordDecl*> Path = MP.getMemberPointerPath(); + bool DerivedMember = MP.isMemberPointerToDerivedMember(); + const CXXRecordDecl *RD = cast<CXXRecordDecl>(MPD->getDeclContext()); + for (unsigned I = 0, N = Path.size(); I != N; ++I) { + const CXXRecordDecl *Base = RD; + const CXXRecordDecl *Derived = Path[I]; + if (DerivedMember) + std::swap(Base, Derived); + ThisAdjustment += + getContext().getASTRecordLayout(Derived).getBaseClassOffset(Base); + RD = Path[I]; + } + if (DerivedMember) + ThisAdjustment = -ThisAdjustment; + return ThisAdjustment; +} + llvm::BasicBlock *CGCXXABI::EmitCtorCompleteObjectHandler( CodeGenFunction &CGF) { if (CGM.getTarget().getCXXABI().hasConstructorVariants()) Modified: cfe/trunk/lib/CodeGen/CGCXXABI.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.h?rev=181543&r1=181542&r2=181543&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGCXXABI.h (original) +++ cfe/trunk/lib/CodeGen/CGCXXABI.h Thu May 9 16:01:17 2013 @@ -192,6 +192,12 @@ protected: /// is required. llvm::Constant *getMemberPointerAdjustment(const CastExpr *E); + /// \brief Computes the non-virtual adjustment needed for a member pointer + /// conversion along an inheritance path stored in an APValue. Unlike + /// getMemberPointerAdjustment(), the adjustment can be negative if the path + /// is from a derived type to a base type. + CharUnits getMemberPointerPathAdjustment(const APValue &MP); + public: /// Adjust the given non-null pointer to an object of polymorphic /// type to point to the complete object. Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=181543&r1=181542&r2=181543&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original) +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Thu May 9 16:01:17 2013 @@ -573,22 +573,7 @@ llvm::Constant *ItaniumCXXABI::EmitMembe if (!MPD) return EmitNullMemberPointer(MPT); - // Compute the this-adjustment. - CharUnits ThisAdjustment = CharUnits::Zero(); - ArrayRef<const CXXRecordDecl*> Path = MP.getMemberPointerPath(); - bool DerivedMember = MP.isMemberPointerToDerivedMember(); - const CXXRecordDecl *RD = cast<CXXRecordDecl>(MPD->getDeclContext()); - for (unsigned I = 0, N = Path.size(); I != N; ++I) { - const CXXRecordDecl *Base = RD; - const CXXRecordDecl *Derived = Path[I]; - if (DerivedMember) - std::swap(Base, Derived); - ThisAdjustment += - getContext().getASTRecordLayout(Derived).getBaseClassOffset(Base); - RD = Path[I]; - } - if (DerivedMember) - ThisAdjustment = -ThisAdjustment; + CharUnits ThisAdjustment = getMemberPointerPathAdjustment(MP); if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MPD)) return BuildMemberPointer(MD, ThisAdjustment); Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=181543&r1=181542&r2=181543&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original) +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Thu May 9 16:01:17 2013 @@ -130,6 +130,14 @@ private: return llvm::Constant::getAllOnesValue(CGM.IntTy); } + llvm::Constant *getConstantOrZeroInt(llvm::Constant *C) { + return C ? C : getZeroInt(); + } + + llvm::Value *getValueOrZeroInt(llvm::Value *C) { + return C ? C : getZeroInt(); + } + void GetNullMemberPointerFields(const MemberPointerType *MPT, llvm::SmallVectorImpl<llvm::Constant *> &fields); @@ -143,7 +151,15 @@ private: /// function member pointers. llvm::Constant *EmitFullMemberPointer(llvm::Constant *FirstField, bool IsMemberFunction, - const CXXRecordDecl *RD); + const CXXRecordDecl *RD, + CharUnits NonVirtualBaseAdjustment); + + llvm::Constant *BuildMemberPointer(const CXXRecordDecl *RD, + const CXXMethodDecl *MD, + CharUnits NonVirtualBaseAdjustment); + + bool MemberPointerConstantIsNull(const MemberPointerType *MPT, + llvm::Constant *MP); public: virtual llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); @@ -172,6 +188,13 @@ public: llvm::Value *MemPtr, const MemberPointerType *MPT); + virtual llvm::Value *EmitMemberPointerConversion(CodeGenFunction &CGF, + const CastExpr *E, + llvm::Value *Src); + + virtual llvm::Constant *EmitMemberPointerConversion(const CastExpr *E, + llvm::Constant *Src); + virtual llvm::Value * EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, llvm::Value *&This, @@ -429,8 +452,10 @@ static bool hasVBPtrOffsetField(MSInheri return Inheritance == MSIM_Unspecified; } -static bool hasOnlyOneField(MSInheritanceModel Inheritance) { - return Inheritance <= MSIM_SinglePolymorphic; +static bool hasOnlyOneField(bool IsMemberFunction, + MSInheritanceModel Inheritance) { + return Inheritance <= MSIM_SinglePolymorphic || + (!IsMemberFunction && Inheritance <= MSIM_MultiplePolymorphic); } // Only member pointers to functions need a this adjustment, since it can be @@ -531,22 +556,25 @@ MicrosoftCXXABI::EmitNullMemberPointer(c llvm::Constant * MicrosoftCXXABI::EmitFullMemberPointer(llvm::Constant *FirstField, bool IsMemberFunction, - const CXXRecordDecl *RD) + const CXXRecordDecl *RD, + CharUnits NonVirtualBaseAdjustment) { MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); // Single inheritance class member pointer are represented as scalars instead // of aggregates. - if (hasOnlyOneField(Inheritance)) + if (hasOnlyOneField(IsMemberFunction, Inheritance)) return FirstField; llvm::SmallVector<llvm::Constant *, 4> fields; fields.push_back(FirstField); if (hasNonVirtualBaseAdjustmentField(IsMemberFunction, Inheritance)) - fields.push_back(getZeroInt()); + fields.push_back(llvm::ConstantInt::get( + CGM.IntTy, NonVirtualBaseAdjustment.getQuantity())); if (hasVBPtrOffsetField(Inheritance)) { + // FIXME: We actually need to search non-virtual bases for vbptrs. int64_t VBPtrOffset = getContext().getASTRecordLayout(RD).getVBPtrOffset().getQuantity(); if (VBPtrOffset == -1) @@ -567,14 +595,40 @@ MicrosoftCXXABI::EmitMemberDataPointer(c const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); llvm::Constant *FirstField = llvm::ConstantInt::get(CGM.IntTy, offset.getQuantity()); - return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/false, RD); + return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/false, RD, + CharUnits::Zero()); +} + +llvm::Constant *MicrosoftCXXABI::EmitMemberPointer(const CXXMethodDecl *MD) { + return BuildMemberPointer(MD->getParent(), MD, CharUnits::Zero()); +} + +llvm::Constant *MicrosoftCXXABI::EmitMemberPointer(const APValue &MP, + QualType MPType) { + const MemberPointerType *MPT = MPType->castAs<MemberPointerType>(); + const ValueDecl *MPD = MP.getMemberPointerDecl(); + if (!MPD) + return EmitNullMemberPointer(MPT); + + CharUnits ThisAdjustment = getMemberPointerPathAdjustment(MP); + + // FIXME PR15713: Support virtual inheritance paths. + + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MPD)) + return BuildMemberPointer(MPT->getClass()->getAsCXXRecordDecl(), + MD, ThisAdjustment); + + CharUnits FieldOffset = + getContext().toCharUnitsFromBits(getContext().getFieldOffset(MPD)); + return EmitMemberDataPointer(MPT, ThisAdjustment + FieldOffset); } llvm::Constant * -MicrosoftCXXABI::EmitMemberPointer(const CXXMethodDecl *MD) { +MicrosoftCXXABI::BuildMemberPointer(const CXXRecordDecl *RD, + const CXXMethodDecl *MD, + CharUnits NonVirtualBaseAdjustment) { assert(MD->isInstance() && "Member function must not be static!"); MD = MD->getCanonicalDecl(); - const CXXRecordDecl *RD = MD->getParent(); CodeGenTypes &Types = CGM.getTypes(); llvm::Constant *FirstField; @@ -599,15 +653,8 @@ MicrosoftCXXABI::EmitMemberPointer(const } // The rest of the fields are common with data member pointers. - return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/true, RD); -} - -llvm::Constant * -MicrosoftCXXABI::EmitMemberPointer(const APValue &MP, QualType MPT) { - // FIXME PR15875: Implement member pointer conversions for Constants. - const CXXRecordDecl *RD = MPT->castAs<MemberPointerType>()->getClass()->getAsCXXRecordDecl(); - return EmitFullMemberPointer(llvm::Constant::getNullValue(CGM.VoidPtrTy), - /*IsMemberFunction=*/true, RD); + return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/true, RD, + NonVirtualBaseAdjustment); } /// Member pointers are the same if they're either bitwise identical *or* both @@ -638,7 +685,7 @@ MicrosoftCXXABI::EmitMemberPointerCompar // single icmp. const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); - if (hasOnlyOneField(Inheritance)) + if (hasOnlyOneField(MPT->isMemberFunctionPointer(), Inheritance)) return Builder.CreateICmp(Eq, L, R); // Compare the first field. @@ -703,6 +750,37 @@ MicrosoftCXXABI::EmitMemberPointerIsNotN return Res; } +bool MicrosoftCXXABI::MemberPointerConstantIsNull(const MemberPointerType *MPT, + llvm::Constant *Val) { + // Function pointers are null if the pointer in the first field is null. + if (MPT->isMemberFunctionPointer()) { + llvm::Constant *FirstField = Val->getType()->isStructTy() ? + Val->getAggregateElement(0U) : Val; + return FirstField->isNullValue(); + } + + // If it's not a function pointer and it's zero initializable, we can easily + // check zero. + if (isZeroInitializable(MPT) && Val->isNullValue()) + return true; + + // Otherwise, break down all the fields for comparison. Hopefully these + // little Constants are reused, while a big null struct might not be. + llvm::SmallVector<llvm::Constant *, 4> Fields; + GetNullMemberPointerFields(MPT, Fields); + if (Fields.size() == 1) { + assert(Val->getType()->isIntegerTy()); + return Val == Fields[0]; + } + + unsigned I, E; + for (I = 0, E = Fields.size(); I != E; ++I) { + if (Val->getAggregateElement(I) != Fields[I]) + break; + } + return I == E; +} + // Returns an adjusted base cast to i8*, since we do more address arithmetic on // it. llvm::Value * @@ -803,6 +881,194 @@ MicrosoftCXXABI::EmitMemberDataPointerAd return Builder.CreateBitCast(Addr, PType); } +static MSInheritanceModel +getInheritanceFromMemptr(const MemberPointerType *MPT) { + return MPT->getClass()->getAsCXXRecordDecl()->getMSInheritanceModel(); +} + +llvm::Value * +MicrosoftCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF, + const CastExpr *E, + llvm::Value *Src) { + assert(E->getCastKind() == CK_DerivedToBaseMemberPointer || + E->getCastKind() == CK_BaseToDerivedMemberPointer || + E->getCastKind() == CK_ReinterpretMemberPointer); + + // Use constant emission if we can. + if (isa<llvm::Constant>(Src)) + return EmitMemberPointerConversion(E, cast<llvm::Constant>(Src)); + + // We may be adding or dropping fields from the member pointer, so we need + // both types and the inheritance models of both records. + const MemberPointerType *SrcTy = + E->getSubExpr()->getType()->castAs<MemberPointerType>(); + const MemberPointerType *DstTy = E->getType()->castAs<MemberPointerType>(); + MSInheritanceModel SrcInheritance = getInheritanceFromMemptr(SrcTy); + MSInheritanceModel DstInheritance = getInheritanceFromMemptr(DstTy); + bool IsFunc = SrcTy->isMemberFunctionPointer(); + + // If the classes use the same null representation, reinterpret_cast is a nop. + bool IsReinterpret = E->getCastKind() == CK_ReinterpretMemberPointer; + if (IsReinterpret && (IsFunc || + nullFieldOffsetIsZero(SrcInheritance) == + nullFieldOffsetIsZero(DstInheritance))) + return Src; + + CGBuilderTy &Builder = CGF.Builder; + + // Branch past the conversion if Src is null. + llvm::Value *IsNotNull = EmitMemberPointerIsNotNull(CGF, Src, SrcTy); + llvm::Constant *DstNull = EmitNullMemberPointer(DstTy); + + // C++ 5.2.10p9: The null member pointer value is converted to the null member + // pointer value of the destination type. + if (IsReinterpret) { + // For reinterpret casts, sema ensures that src and dst are both functions + // or data and have the same size, which means the LLVM types should match. + assert(Src->getType() == DstNull->getType()); + return Builder.CreateSelect(IsNotNull, Src, DstNull); + } + + llvm::BasicBlock *OriginalBB = Builder.GetInsertBlock(); + llvm::BasicBlock *ConvertBB = CGF.createBasicBlock("memptr.convert"); + llvm::BasicBlock *ContinueBB = CGF.createBasicBlock("memptr.converted"); + Builder.CreateCondBr(IsNotNull, ConvertBB, ContinueBB); + CGF.EmitBlock(ConvertBB); + + // Decompose src. + llvm::Value *FirstField = Src; + llvm::Value *NonVirtualBaseAdjustment = 0; + llvm::Value *VirtualBaseAdjustmentOffset = 0; + llvm::Value *VBPtrOffset = 0; + if (!hasOnlyOneField(IsFunc, SrcInheritance)) { + // We need to extract values. + unsigned I = 0; + FirstField = Builder.CreateExtractValue(Src, I++); + if (hasNonVirtualBaseAdjustmentField(IsFunc, SrcInheritance)) + NonVirtualBaseAdjustment = Builder.CreateExtractValue(Src, I++); + if (hasVBPtrOffsetField(SrcInheritance)) + VBPtrOffset = Builder.CreateExtractValue(Src, I++); + if (hasVirtualBaseAdjustmentField(SrcInheritance)) + VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(Src, I++); + } + + // For data pointers, we adjust the field offset directly. For functions, we + // have a separate field. + llvm::Constant *Adj = getMemberPointerAdjustment(E); + if (Adj) { + Adj = llvm::ConstantExpr::getTruncOrBitCast(Adj, CGM.IntTy); + llvm::Value *&NVAdjustField = IsFunc ? NonVirtualBaseAdjustment : FirstField; + bool isDerivedToBase = (E->getCastKind() == CK_DerivedToBaseMemberPointer); + if (!NVAdjustField) // If this field didn't exist in src, it's zero. + NVAdjustField = getZeroInt(); + if (isDerivedToBase) + NVAdjustField = Builder.CreateNSWSub(NVAdjustField, Adj, "adj"); + else + NVAdjustField = Builder.CreateNSWAdd(NVAdjustField, Adj, "adj"); + } + + // FIXME PR15713: Support conversions through virtually derived classes. + + // Recompose dst from the null struct and the adjusted fields from src. + llvm::Value *Dst; + if (hasOnlyOneField(IsFunc, DstInheritance)) { + Dst = FirstField; + } else { + Dst = llvm::UndefValue::get(DstNull->getType()); + unsigned Idx = 0; + Dst = Builder.CreateInsertValue(Dst, FirstField, Idx++); + if (hasNonVirtualBaseAdjustmentField(IsFunc, DstInheritance)) + Dst = Builder.CreateInsertValue( + Dst, getValueOrZeroInt(NonVirtualBaseAdjustment), Idx++); + if (hasVBPtrOffsetField(DstInheritance)) + Dst = Builder.CreateInsertValue( + Dst, getValueOrZeroInt(VBPtrOffset), Idx++); + if (hasVirtualBaseAdjustmentField(DstInheritance)) + Dst = Builder.CreateInsertValue( + Dst, getValueOrZeroInt(VirtualBaseAdjustmentOffset), Idx++); + } + Builder.CreateBr(ContinueBB); + + // In the continuation, choose between DstNull and Dst. + CGF.EmitBlock(ContinueBB); + llvm::PHINode *Phi = Builder.CreatePHI(DstNull->getType(), 2, "memptr.converted"); + Phi->addIncoming(DstNull, OriginalBB); + Phi->addIncoming(Dst, ConvertBB); + return Phi; +} + +llvm::Constant * +MicrosoftCXXABI::EmitMemberPointerConversion(const CastExpr *E, + llvm::Constant *Src) { + const MemberPointerType *SrcTy = + E->getSubExpr()->getType()->castAs<MemberPointerType>(); + const MemberPointerType *DstTy = E->getType()->castAs<MemberPointerType>(); + + // If src is null, emit a new null for dst. We can't return src because dst + // might have a new representation. + if (MemberPointerConstantIsNull(SrcTy, Src)) + return EmitNullMemberPointer(DstTy); + + // We don't need to do anything for reinterpret_casts of non-null member + // pointers. We should only get here when the two type representations have + // the same size. + if (E->getCastKind() == CK_ReinterpretMemberPointer) + return Src; + + MSInheritanceModel SrcInheritance = getInheritanceFromMemptr(SrcTy); + MSInheritanceModel DstInheritance = getInheritanceFromMemptr(DstTy); + + // Decompose src. + llvm::Constant *FirstField = Src; + llvm::Constant *NonVirtualBaseAdjustment = 0; + llvm::Constant *VirtualBaseAdjustmentOffset = 0; + llvm::Constant *VBPtrOffset = 0; + bool IsFunc = SrcTy->isMemberFunctionPointer(); + if (!hasOnlyOneField(IsFunc, SrcInheritance)) { + // We need to extract values. + unsigned I = 0; + FirstField = Src->getAggregateElement(I++); + if (hasNonVirtualBaseAdjustmentField(IsFunc, SrcInheritance)) + NonVirtualBaseAdjustment = Src->getAggregateElement(I++); + if (hasVBPtrOffsetField(SrcInheritance)) + VBPtrOffset = Src->getAggregateElement(I++); + if (hasVirtualBaseAdjustmentField(SrcInheritance)) + VirtualBaseAdjustmentOffset = Src->getAggregateElement(I++); + } + + // For data pointers, we adjust the field offset directly. For functions, we + // have a separate field. + llvm::Constant *Adj = getMemberPointerAdjustment(E); + if (Adj) { + Adj = llvm::ConstantExpr::getTruncOrBitCast(Adj, CGM.IntTy); + llvm::Constant *&NVAdjustField = + IsFunc ? NonVirtualBaseAdjustment : FirstField; + bool IsDerivedToBase = (E->getCastKind() == CK_DerivedToBaseMemberPointer); + if (!NVAdjustField) // If this field didn't exist in src, it's zero. + NVAdjustField = getZeroInt(); + if (IsDerivedToBase) + NVAdjustField = llvm::ConstantExpr::getNSWSub(NVAdjustField, Adj); + else + NVAdjustField = llvm::ConstantExpr::getNSWAdd(NVAdjustField, Adj); + } + + // FIXME PR15713: Support conversions through virtually derived classes. + + // Recompose dst from the null struct and the adjusted fields from src. + if (hasOnlyOneField(IsFunc, DstInheritance)) + return FirstField; + + llvm::SmallVector<llvm::Constant *, 4> Fields; + Fields.push_back(FirstField); + if (hasNonVirtualBaseAdjustmentField(IsFunc, DstInheritance)) + Fields.push_back(getConstantOrZeroInt(NonVirtualBaseAdjustment)); + if (hasVBPtrOffsetField(DstInheritance)) + Fields.push_back(getConstantOrZeroInt(VBPtrOffset)); + if (hasVirtualBaseAdjustmentField(DstInheritance)) + Fields.push_back(getConstantOrZeroInt(VirtualBaseAdjustmentOffset)); + return llvm::ConstantStruct::getAnon(Fields); +} + llvm::Value * MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, llvm::Value *&This, Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp?rev=181543&r1=181542&r2=181543&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp (original) +++ cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp Thu May 9 16:01:17 2013 @@ -1,16 +1,20 @@ // RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// FIXME: Test x86_64 member pointers when codegen no longer asserts on records +// with virtual bases. struct B1 { void foo(); int b; }; struct B2 { + int b2; void foo(); }; struct Single : B1 { void foo(); }; struct Multiple : B1, B2 { + int m; void foo(); }; struct Virtual : virtual B1 { @@ -34,6 +38,7 @@ struct Polymorphic { // offset. struct NonZeroVBPtr : POD, Virtual { int n; + void foo(); }; struct Unspecified; @@ -68,6 +73,62 @@ struct Unspecified : Virtual { int u; }; +// Test memptr emission in a constant expression. +namespace Const { +void (Single ::*s_f_mp)() = &Single::foo; +void (Multiple ::*m_f_mp)() = &B2::foo; +void (Virtual ::*v_f_mp)() = &Virtual::foo; +void (Unspecified::*u_f_mp)() = &Unspecified::foo; +// CHECK: @"\01?s_f_mp@Const@@3P8Single@@AEXXZA" = +// CHECK: global i8* bitcast ({{.*}} @"\01?foo@Single@@QAEXXZ" to i8*), align 4 +// CHECK: @"\01?m_f_mp@Const@@3P8Multiple@@AEXXZA" = +// CHECK: global { i8*, i32 } { i8* bitcast ({{.*}} @"\01?foo@B2@@QAEXXZ" to i8*), i32 4 }, align 4 +// CHECK: @"\01?v_f_mp@Const@@3P8Virtual@@AEXXZA" = +// CHECK: global { i8*, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo@Virtual@@QAEXXZ" to i8*), i32 0, i32 0 }, align 4 +// CHECK: @"\01?u_f_mp@Const@@3P8Unspecified@@AEXXZA" = +// CHECK: global { i8*, i32, i32, i32 } { i8* bitcast ({{.*}} @"\01?foo@Unspecified@@QAEXXZ" to i8*), i32 0, i32 0, i32 0 }, align 4 +} + +namespace CastParam { +// This exercises ConstExprEmitter instead of ValueDecl::evaluateValue. The +// extra reinterpret_cast for the parameter type requires more careful folding. +// FIXME: Or does it? If reinterpret_casts are no-ops, we should be able to +// strip them in evaluateValue() and just proceed as normal with an APValue. +struct A { + int a; + void foo(A *p); +}; +struct B { int b; }; +struct C : B, A { int c; }; + +void (A::*ptr1)(void *) = (void (A::*)(void *)) &A::foo; +// CHECK: @"\01?ptr1@CastParam@@3P8A@1@AEXPAX@ZA" = +// CHECK: global i8* bitcast (void ({{.*}})* @"\01?foo@A@CastParam@@QAEXPAU12@@Z" to i8*), align 4 + +// Try a reinterpret_cast followed by a memptr conversion. +void (C::*ptr2)(void *) = (void (C::*)(void *)) (void (A::*)(void *)) &A::foo; +// CHECK: @"\01?ptr2@CastParam@@3P8C@1@AEXPAX@ZA" = +// CHECK: global { i8*, i32 } { i8* bitcast (void ({{.*}})* @"\01?foo@A@CastParam@@QAEXPAU12@@Z" to i8*), i32 4 }, align 4 + +void (C::*ptr3)(void *) = (void (C::*)(void *)) (void (A::*)(void *)) (void (A::*)(A *)) 0; +// CHECK: @"\01?ptr3@CastParam@@3P8C@1@AEXPAX@ZA" = +// CHECK: global { i8*, i32 } zeroinitializer, align 4 + +struct D : C { + virtual void isPolymorphic(); + int d; +}; + +// Try a cast that changes the inheritance model. Null for D is 0, but null for +// C is -1. We need the cast to long in order to hit the non-APValue path. +int C::*ptr4 = (int C::*) (int D::*) (long D::*) 0; +// CHECK: @"\01?ptr4@CastParam@@3PQC@1@HA" = global i32 -1, align 4 + +// MSVC rejects this but we accept it. +int C::*ptr5 = (int C::*) (long D::*) 0; +// CHECK: @"\01?ptr5@CastParam@@3PQC@1@HA" = global i32 -1, align 4 +} + struct UnspecWithVBPtr; int UnspecWithVBPtr::*forceUnspecWithVBPtr; struct UnspecWithVBPtr : B1, virtual B2 { @@ -82,7 +143,7 @@ void EmitNonVirtualMemberPointers() { void (Virtual ::*v_f_memptr)() = &Virtual::foo; void (Unspecified::*u_f_memptr)() = &Unspecified::foo; void (UnspecWithVBPtr::*u2_f_memptr)() = &UnspecWithVBPtr::foo; -// CHECK: define void @"\01?EmitNonVirtualMemberPointers@@YAXXZ"() #0 { +// CHECK: define void @"\01?EmitNonVirtualMemberPointers@@YAXXZ"() {{.*}} { // CHECK: alloca i8*, align 4 // CHECK: alloca { i8*, i32 }, align 4 // CHECK: alloca { i8*, i32, i32 }, align 4 @@ -112,7 +173,7 @@ void podMemPtrs() { if (memptr) memptr = 0; // Check that member pointers use the right offsets and that null is -1. -// CHECK: define void @"\01?podMemPtrs@@YAXXZ"() #0 { +// CHECK: define void @"\01?podMemPtrs@@YAXXZ"() {{.*}} { // CHECK: %[[memptr:.*]] = alloca i32, align 4 // CHECK-NEXT: store i32 0, i32* %[[memptr]], align 4 // CHECK-NEXT: store i32 4, i32* %[[memptr]], align 4 @@ -132,7 +193,7 @@ void polymorphicMemPtrs() { memptr = 0; // Member pointers for polymorphic classes include the vtable slot in their // offset and use 0 to represent null. -// CHECK: define void @"\01?polymorphicMemPtrs@@YAXXZ"() #0 { +// CHECK: define void @"\01?polymorphicMemPtrs@@YAXXZ"() {{.*}} { // CHECK: %[[memptr:.*]] = alloca i32, align 4 // CHECK-NEXT: store i32 4, i32* %[[memptr]], align 4 // CHECK-NEXT: store i32 8, i32* %[[memptr]], align 4 @@ -233,7 +294,7 @@ int loadDataMemberPointerUnspecified(Uns void callMemberPointerSingle(Single *o, void (Single::*memptr)()) { (o->*memptr)(); // Just look for an indirect thiscall. -// CHECK: define void @"\01?callMemberPointerSingle@@{{.*}} #0 { +// CHECK: define void @"\01?callMemberPointerSingle@@{{.*}} {{.*}} { // CHECK: call x86_thiscallcc void %{{.*}}(%{{.*}} %{{.*}}) // CHECK: ret void // CHECK: } @@ -241,7 +302,7 @@ void callMemberPointerSingle(Single *o, void callMemberPointerMultiple(Multiple *o, void (Multiple::*memptr)()) { (o->*memptr)(); -// CHECK: define void @"\01?callMemberPointerMultiple@@{{.*}} #0 { +// CHECK: define void @"\01?callMemberPointerMultiple@@{{.*}} { // CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32 } %{{.*}}, 0 // CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32 } %{{.*}}, 1 // CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %{{.*}}, i32 %[[memptr1]] @@ -255,7 +316,7 @@ void callMemberPointerMultiple(Multiple void callMemberPointerVirtualBase(Virtual *o, void (Virtual::*memptr)()) { (o->*memptr)(); // This shares a lot with virtual data member pointers. -// CHECK: define void @"\01?callMemberPointerVirtualBase@@{{.*}} #0 { +// CHECK: define void @"\01?callMemberPointerVirtualBase@@{{.*}} { // CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 0 // CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 1 // CHECK: %[[memptr2:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 2 @@ -361,3 +422,107 @@ bool unspecDataMemptrEq(int Unspecified: // CHECK: ret i1 // CHECK: } } + +void (Multiple::*convertB2FuncToMultiple(void (B2::*mp)()))() { + return mp; +// CHECK: define i64 @"\01?convertB2FuncToMultiple@@YAP8Multiple@@AEXXZP8B2@@AEXXZ@Z"{{.*}} { +// CHECK: store +// CHECK: %[[mp:.*]] = load i8** %{{.*}}, align 4 +// CHECK: icmp ne i8* %[[mp]], null +// CHECK: br i1 %{{.*}} label %{{.*}}, label %{{.*}} +// +// memptr.convert: ; preds = %entry +// CHECK: insertvalue { i8*, i32 } undef, i8* %[[mp]], 0 +// CHECK: insertvalue { i8*, i32 } %{{.*}}, i32 4, 1 +// CHECK: br label +// +// memptr.converted: ; preds = %memptr.convert, %entry +// CHECK: phi { i8*, i32 } [ zeroinitializer, %{{.*}} ], [ {{.*}} ] +// CHECK: } +} + +void (B2::*convertMultipleFuncToB2(void (Multiple::*mp)()))() { +// FIXME: cl emits warning C4407 on this code because of the representation +// change. We might want to do the same. + return static_cast<void (B2::*)()>(mp); +// FIXME: We should return i8* instead of i32 here. The ptrtoint cast prevents +// LLVM from optimizing away the branch. This is likely a bug in +// lib/CodeGen/TargetInfo.cpp with how we classify memptr types for returns. +// +// CHECK: define i32 @"\01?convertMultipleFuncToB2@@YAP8B2@@AEXXZP8Multiple@@AEXXZ@Z"{{.*}} { +// CHECK: store +// CHECK: %[[src:.*]] = load { i8*, i32 }* %{{.*}}, align 4 +// CHECK: extractvalue { i8*, i32 } %[[src]], 0 +// CHECK: icmp ne i8* %{{.*}}, null +// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} +// +// memptr.convert: ; preds = %entry +// CHECK: %[[fp:.*]] = extractvalue { i8*, i32 } %[[src]], 0 +// CHECK: br label +// +// memptr.converted: ; preds = %memptr.convert, %entry +// CHECK: phi i8* [ null, %{{.*}} ], [ %[[fp]], %{{.*}} ] +// CHECK: } +} + +namespace Test1 { + +struct A { int a; }; +struct B { int b; }; +struct C : virtual A { int c; }; +struct D : B, C { int d; }; + +void (D::*convertCToD(void (C::*mp)()))() { + return mp; +// CHECK: define void @"\01?convertCToD@Test1@@YAP8D@1@AEXXZP8C@1@AEXXZ@Z"{{.*}} { +// CHECK: store +// CHECK: load { i8*, i32, i32 }* %{{.*}}, align 4 +// CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 0 +// CHECK: icmp ne i8* %{{.*}}, null +// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}} +// +// memptr.convert: ; preds = %entry +// CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 0 +// CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 1 +// CHECK: extractvalue { i8*, i32, i32 } %{{.*}}, 2 +// CHECK: %[[adj:.*]] = add nsw i32 %{{.*}}, 4 +// CHECK: insertvalue { i8*, i32, i32 } undef, i8* {{.*}}, 0 +// CHECK: insertvalue { i8*, i32, i32 } {{.*}}, i32 %[[adj]], 1 +// CHECK: insertvalue { i8*, i32, i32 } {{.*}}, i32 {{.*}}, 2 +// CHECK: br label +// +// memptr.converted: ; preds = %memptr.convert, %entry +// CHECK: phi { i8*, i32, i32 } [ { i8* null, i32 0, i32 -1 }, {{.*}} ], [ {{.*}} ] +// CHECK: } +} + +} + +namespace Test2 { +// Test that we dynamically convert between different null reps. + +struct A { int a; }; +struct B : A { int b; }; +struct C : A { + int c; + virtual void hasVfPtr(); +}; + +int A::*reinterpret(int B::*mp) { + return reinterpret_cast<int A::*>(mp); +// CHECK: define i32 @"\01?reinterpret@Test2@@YAPQA@1@HPQB@1@H@Z"{{.*}} { +// CHECK-NOT: select +// CHECK: ret i32 +// CHECK: } +} + +int A::*reinterpret(int C::*mp) { + return reinterpret_cast<int A::*>(mp); +// CHECK: define i32 @"\01?reinterpret@Test2@@YAPQA@1@HPQC@1@H@Z"{{.*}} { +// CHECK: %[[mp:.*]] = load i32* +// CHECK: %[[cmp:.*]] = icmp ne i32 %[[mp]], 0 +// CHECK: select i1 %[[cmp]], i32 %[[mp]], i32 -1 +// CHECK: } +} + +} _______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
