diff options
-rw-r--r-- | include/clang/AST/DeclCXX.h | 10 | ||||
-rw-r--r-- | include/clang/AST/Type.h | 12 | ||||
-rw-r--r-- | lib/AST/MicrosoftCXXABI.cpp | 20 | ||||
-rw-r--r-- | lib/CodeGen/MicrosoftCXXABI.cpp | 333 | ||||
-rwxr-xr-x | test/CodeGenCXX/microsoft-abi-member-pointers.cpp | 187 |
5 files changed, 483 insertions, 79 deletions
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 05ff49c64c..7afb45c23a 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -254,6 +254,16 @@ public: TypeSourceInfo *getTypeSourceInfo() const { return BaseTypeInfo; } }; +/// The inheritance model to use for member pointers of a given CXXRecordDecl. +enum MSInheritanceModel { + MSIM_Single, + MSIM_SinglePolymorphic, + MSIM_Multiple, + MSIM_MultiplePolymorphic, + MSIM_Virtual, + MSIM_Unspecified +}; + /// CXXRecordDecl - Represents a C++ struct/union/class. /// FIXME: This class will disappear once we've properly taught RecordDecl /// to deal with C++-specific things. diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 23fa3e8765..e0402148b5 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -2092,14 +2092,6 @@ public: } }; -/// The inheritance model to use for this member pointer. -enum MSInheritanceModel { - MSIM_Single, - MSIM_Multiple, - MSIM_Virtual, - MSIM_Unspecified -}; - /// MemberPointerType - C++ 8.3.3 - Pointers to members /// class MemberPointerType : public Type, public llvm::FoldingSetNode { @@ -2135,10 +2127,6 @@ public: return !PointeeType->isFunctionProtoType(); } - /// Returns the number of pointer and integer slots used to represent this - /// member pointer in the MS C++ ABI. - std::pair<unsigned, unsigned> getMSMemberPointerSlots() const; - const Type *getClass() const { return Class; } bool isSugared() const { return false; } diff --git a/lib/AST/MicrosoftCXXABI.cpp b/lib/AST/MicrosoftCXXABI.cpp index 6553e9d749..fd932f7330 100644 --- a/lib/AST/MicrosoftCXXABI.cpp +++ b/lib/AST/MicrosoftCXXABI.cpp @@ -89,8 +89,8 @@ MSInheritanceModel CXXRecordDecl::getMSInheritanceModel() const { if (this->getNumVBases() > 0) return MSIM_Virtual; if (usesMultipleInheritanceModel(this)) - return MSIM_Multiple; - return MSIM_Single; + return this->isPolymorphic() ? MSIM_MultiplePolymorphic : MSIM_Multiple; + return this->isPolymorphic() ? MSIM_SinglePolymorphic : MSIM_Single; } // Returns the number of pointer and integer slots used to represent a member @@ -119,15 +119,15 @@ MSInheritanceModel CXXRecordDecl::getMSInheritanceModel() const { // // // The offset of the vb-table pointer within the object. Only needed for // // incomplete types. -// int VBTableOffset; +// int VBPtrOffset; // }; -std::pair<unsigned, unsigned> -MemberPointerType::getMSMemberPointerSlots() const { - const CXXRecordDecl *RD = this->getClass()->getAsCXXRecordDecl(); +static std::pair<unsigned, unsigned> +getMSMemberPointerSlots(const MemberPointerType *MPT) { + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); unsigned Ptrs; unsigned Ints = 0; - if (this->isMemberFunctionPointer()) { + if (MPT->isMemberFunctionPointer()) { // Member function pointers are a struct of a function pointer followed by a // variable number of ints depending on the inheritance model used. The // function pointer is a real function if it is non-virtual and a vftable @@ -137,7 +137,9 @@ MemberPointerType::getMSMemberPointerSlots() const { switch (Inheritance) { case MSIM_Unspecified: ++Ints; // VBTableOffset case MSIM_Virtual: ++Ints; // VirtualBaseAdjustmentOffset + case MSIM_MultiplePolymorphic: case MSIM_Multiple: ++Ints; // NonVirtualBaseAdjustment + case MSIM_SinglePolymorphic: case MSIM_Single: break; // Nothing } } else { @@ -147,7 +149,9 @@ MemberPointerType::getMSMemberPointerSlots() const { switch (Inheritance) { case MSIM_Unspecified: ++Ints; // VBTableOffset case MSIM_Virtual: ++Ints; // VirtualBaseAdjustmentOffset + case MSIM_MultiplePolymorphic: case MSIM_Multiple: // Nothing + case MSIM_SinglePolymorphic: case MSIM_Single: ++Ints; // Field offset } } @@ -160,7 +164,7 @@ std::pair<uint64_t, unsigned> MicrosoftCXXABI::getMemberPointerWidthAndAlign( assert(Target.getTriple().getArch() == llvm::Triple::x86 || Target.getTriple().getArch() == llvm::Triple::x86_64); unsigned Ptrs, Ints; - llvm::tie(Ptrs, Ints) = MPT->getMSMemberPointerSlots(); + llvm::tie(Ptrs, Ints) = getMSMemberPointerSlots(MPT); // The nominal struct is laid out with pointers followed by ints and aligned // to a pointer width if any are present and an int width otherwise. unsigned PtrSize = Target.getPointerWidth(0); diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 00b15c9a49..2c7f89aa50 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -111,17 +111,28 @@ public: static bool needThisReturn(GlobalDecl GD); private: - llvm::Constant *getSimpleNullMemberPointer(const MemberPointerType *MPT); - - llvm::Constant *getZeroPtrDiff() { - return llvm::ConstantInt::get(CGM.PtrDiffTy, 0); + llvm::Constant *getZeroInt() { + return llvm::ConstantInt::get(CGM.IntTy, 0); } - llvm::Constant *getAllOnesPtrDiff() { - return llvm::Constant::getAllOnesValue(CGM.PtrDiffTy); + llvm::Constant *getAllOnesInt() { + return llvm::Constant::getAllOnesValue(CGM.IntTy); } + void + GetNullMemberPointerFields(const MemberPointerType *MPT, + llvm::SmallVectorImpl<llvm::Constant *> &fields); + + llvm::Value *AdjustVirtualBase(CodeGenFunction &CGF, const CXXRecordDecl *RD, + llvm::Value *Base, + llvm::Value *VirtualBaseAdjustmentOffset, + llvm::Value *VBPtrOffset /* optional */); + public: + virtual llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); + + virtual bool isZeroInitializable(const MemberPointerType *MPT); + virtual llvm::Constant *EmitNullMemberPointer(const MemberPointerType *MPT); virtual llvm::Constant *EmitMemberDataPointer(const MemberPointerType *MPT, @@ -136,6 +147,12 @@ public: llvm::Value *MemPtr, const MemberPointerType *MPT); + virtual llvm::Value * + EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, + llvm::Value *&This, + llvm::Value *MemPtr, + const MemberPointerType *MPT); + }; } @@ -379,45 +396,125 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit); } -// Returns true for member pointer types that we know how to represent with a -// simple ptrdiff_t. Currently we only know how to emit, test, and load member -// data pointers for complete single inheritance classes. -static bool isSimpleMemberPointer(const MemberPointerType *MPT) { +// Member pointer helpers. +static bool hasVBPtrOffsetField(MSInheritanceModel Inheritance) { + return Inheritance == MSIM_Unspecified; +} + +// Only member pointers to functions need a this adjustment, since it can be +// combined with the field offset for data pointers. +static bool hasNonVirtualBaseAdjustmentField(const MemberPointerType *MPT, + MSInheritanceModel Inheritance) { + return (MPT->isMemberFunctionPointer() && + Inheritance >= MSIM_Multiple); +} + +static bool hasVirtualBaseAdjustmentField(MSInheritanceModel Inheritance) { + return Inheritance >= MSIM_Virtual; +} + +// Use zero for the field offset of a null data member pointer if we can +// guarantee that zero is not a valid field offset, or if the member pointer has +// multiple fields. Polymorphic classes have a vfptr at offset zero, so we can +// use zero for null. If there are multiple fields, we can use zero even if it +// is a valid field offset because null-ness testing will check the other +// fields. +static bool nullFieldOffsetIsZero(MSInheritanceModel Inheritance) { + return Inheritance != MSIM_Multiple && Inheritance != MSIM_Single; +} + +bool MicrosoftCXXABI::isZeroInitializable(const MemberPointerType *MPT) { + // Null-ness for function memptrs only depends on the first field, which is + // the function pointer. The rest don't matter, so we can zero initialize. + if (MPT->isMemberFunctionPointer()) + return true; + + // The virtual base adjustment field is always -1 for null, so if we have one + // we can't zero initialize. The field offset is sometimes also -1 if 0 is a + // valid field offset. const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); - return (MPT->isMemberDataPointer() && - !MPT->getClass()->isIncompleteType() && - RD->getNumVBases() == 0); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + return (!hasVirtualBaseAdjustmentField(Inheritance) && + nullFieldOffsetIsZero(Inheritance)); } -llvm::Constant * -MicrosoftCXXABI::getSimpleNullMemberPointer(const MemberPointerType *MPT) { - if (isSimpleMemberPointer(MPT)) { - const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); - // A null member data pointer is represented as -1 if the class is not - // polymorphic, and 0 otherwise. - if (RD->isPolymorphic()) - return getZeroPtrDiff(); - return getAllOnesPtrDiff(); +llvm::Type * +MicrosoftCXXABI::ConvertMemberPointerType(const MemberPointerType *MPT) { + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + llvm::SmallVector<llvm::Type *, 4> fields; + if (MPT->isMemberFunctionPointer()) + fields.push_back(CGM.VoidPtrTy); // FunctionPointerOrVirtualThunk + else + fields.push_back(CGM.IntTy); // FieldOffset + + if (hasVBPtrOffsetField(Inheritance)) + fields.push_back(CGM.IntTy); + if (hasNonVirtualBaseAdjustmentField(MPT, Inheritance)) + fields.push_back(CGM.IntTy); + if (hasVirtualBaseAdjustmentField(Inheritance)) + fields.push_back(CGM.IntTy); // VirtualBaseAdjustmentOffset + + if (fields.size() == 1) + return fields[0]; + return llvm::StructType::get(CGM.getLLVMContext(), fields); +} + +void MicrosoftCXXABI:: +GetNullMemberPointerFields(const MemberPointerType *MPT, + llvm::SmallVectorImpl<llvm::Constant *> &fields) { + assert(fields.empty()); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + if (MPT->isMemberFunctionPointer()) { + // FunctionPointerOrVirtualThunk + fields.push_back(llvm::Constant::getNullValue(CGM.VoidPtrTy)); + } else { + if (nullFieldOffsetIsZero(Inheritance)) + fields.push_back(getZeroInt()); // FieldOffset + else + fields.push_back(getAllOnesInt()); // FieldOffset } - return GetBogusMemberPointer(QualType(MPT, 0)); + + if (hasVBPtrOffsetField(Inheritance)) + fields.push_back(getZeroInt()); + if (hasNonVirtualBaseAdjustmentField(MPT, Inheritance)) + fields.push_back(getZeroInt()); + if (hasVirtualBaseAdjustmentField(Inheritance)) + fields.push_back(getAllOnesInt()); } llvm::Constant * MicrosoftCXXABI::EmitNullMemberPointer(const MemberPointerType *MPT) { - if (isSimpleMemberPointer(MPT)) - return getSimpleNullMemberPointer(MPT); - // FIXME: Implement function member pointers. - return GetBogusMemberPointer(QualType(MPT, 0)); + llvm::SmallVector<llvm::Constant *, 4> fields; + GetNullMemberPointerFields(MPT, fields); + if (fields.size() == 1) + return fields[0]; + llvm::Constant *Res = llvm::ConstantStruct::getAnon(fields); + assert(Res->getType() == ConvertMemberPointerType(MPT)); + return Res; } llvm::Constant * MicrosoftCXXABI::EmitMemberDataPointer(const MemberPointerType *MPT, CharUnits offset) { - // Member data pointers are plain offsets when no virtual bases are involved. - if (isSimpleMemberPointer(MPT)) - return llvm::ConstantInt::get(CGM.PtrDiffTy, offset.getQuantity()); - // FIXME: Implement member pointers other inheritance models. - return GetBogusMemberPointer(QualType(MPT, 0)); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + llvm::SmallVector<llvm::Constant *, 4> fields; + fields.push_back(llvm::ConstantInt::get(CGM.IntTy, offset.getQuantity())); + if (hasVBPtrOffsetField(Inheritance)) { + int64_t VBPtrOffset = + getContext().getASTRecordLayout(RD).getVBPtrOffset().getQuantity(); + fields.push_back(llvm::ConstantInt::get(CGM.IntTy, VBPtrOffset)); + } + assert(!hasNonVirtualBaseAdjustmentField(MPT, Inheritance)); + // The virtual base field starts out zero. It is adjusted by conversions to + // member pointer types of a more derived class. See http://llvm.org/PR15713 + if (hasVirtualBaseAdjustmentField(Inheritance)) + fields.push_back(getZeroInt()); + if (fields.size() == 1) + return fields[0]; + return llvm::ConstantStruct::getAnon(fields); } llvm::Value * @@ -425,16 +522,90 @@ MicrosoftCXXABI::EmitMemberPointerIsNotNull(CodeGenFunction &CGF, llvm::Value *MemPtr, const MemberPointerType *MPT) { CGBuilderTy &Builder = CGF.Builder; + llvm::SmallVector<llvm::Constant *, 4> fields; + // We only need one field for member functions. + if (MPT->isMemberFunctionPointer()) + fields.push_back(llvm::Constant::getNullValue(CGM.VoidPtrTy)); + else + GetNullMemberPointerFields(MPT, fields); + assert(!fields.empty()); + llvm::Value *FirstField = MemPtr; + if (MemPtr->getType()->isStructTy()) + FirstField = Builder.CreateExtractValue(MemPtr, 0); + llvm::Value *Res = Builder.CreateICmpNE(FirstField, fields[0], "memptr.cmp0"); + + // For function member pointers, we only need to test the function pointer + // field. The other fields if any can be garbage. + if (MPT->isMemberFunctionPointer()) + return Res; + + // Otherwise, emit a series of compares and combine the results. + for (int I = 1, E = fields.size(); I < E; ++I) { + llvm::Value *Field = Builder.CreateExtractValue(MemPtr, I); + llvm::Value *Next = Builder.CreateICmpNE(Field, fields[I], "memptr.cmp"); + Res = Builder.CreateAnd(Res, Next, "memptr.tobool"); + } + return Res; +} - // For member data pointers, this is just a check against -1 or 0. - if (isSimpleMemberPointer(MPT)) { - llvm::Constant *Val = getSimpleNullMemberPointer(MPT); - return Builder.CreateICmpNE(MemPtr, Val, "memptr.tobool"); +// Returns an adjusted base cast to i8*, since we do more address arithmetic on +// it. +llvm::Value * +MicrosoftCXXABI::AdjustVirtualBase(CodeGenFunction &CGF, + const CXXRecordDecl *RD, llvm::Value *Base, + llvm::Value *VirtualBaseAdjustmentOffset, + llvm::Value *VBPtrOffset) { + CGBuilderTy &Builder = CGF.Builder; + Base = Builder.CreateBitCast(Base, CGM.Int8PtrTy); + llvm::BasicBlock *OriginalBB = 0; + llvm::BasicBlock *SkipAdjustBB = 0; + llvm::BasicBlock *VBaseAdjustBB = 0; + + // In the unspecified inheritance model, there might not be a vbtable at all, + // in which case we need to skip the virtual base lookup. If there is a + // vbtable, the first entry is a no-op entry that gives back the original + // base, so look for a virtual base adjustment offset of zero. + if (VBPtrOffset) { + OriginalBB = Builder.GetInsertBlock(); + VBaseAdjustBB = CGF.createBasicBlock("memptr.vadjust"); + SkipAdjustBB = CGF.createBasicBlock("memptr.skip_vadjust"); + llvm::Value *IsVirtual = + Builder.CreateICmpNE(VirtualBaseAdjustmentOffset, getZeroInt(), + "memptr.is_vbase"); + Builder.CreateCondBr(IsVirtual, VBaseAdjustBB, SkipAdjustBB); + CGF.EmitBlock(VBaseAdjustBB); } - // FIXME: Implement member pointers other inheritance models. - ErrorUnsupportedABI(CGF, "function member pointer tests"); - return GetBogusMemberPointer(QualType(MPT, 0)); + // If we weren't given a dynamic vbptr offset, RD should be complete and we'll + // know the vbptr offset. + if (!VBPtrOffset) { + CharUnits offs = getContext().getASTRecordLayout(RD).getVBPtrOffset(); + VBPtrOffset = llvm::ConstantInt::get(CGM.IntTy, offs.getQuantity()); + } + // Load the vbtable pointer from the vbtable offset in the instance. + llvm::Value *VBPtr = + Builder.CreateInBoundsGEP(Base, VBPtrOffset, "memptr.vbptr"); + llvm::Value *VBTable = + Builder.CreateBitCast(VBPtr, CGM.Int8PtrTy->getPointerTo(0)); + VBTable = Builder.CreateLoad(VBTable, "memptr.vbtable"); + // Load an i32 offset from the vb-table. + llvm::Value *VBaseOffs = + Builder.CreateInBoundsGEP(VBTable, VirtualBaseAdjustmentOffset); + VBaseOffs = Builder.CreateBitCast(VBaseOffs, CGM.Int32Ty->getPointerTo(0)); + VBaseOffs = Builder.CreateLoad(VBaseOffs, "memptr.vbase_offs"); + // Add it to VBPtr. GEP will sign extend the i32 value for us. + llvm::Value *AdjustedBase = Builder.CreateInBoundsGEP(VBPtr, VBaseOffs); + + // Merge control flow with the case where we didn't have to adjust. + if (VBaseAdjustBB) { + Builder.CreateBr(SkipAdjustBB); + CGF.EmitBlock(SkipAdjustBB); + llvm::PHINode *Phi = Builder.CreatePHI(CGM.Int8PtrTy, 2, "memptr.base"); + Phi->addIncoming(Base, OriginalBB); + Phi->addIncoming(AdjustedBase, VBaseAdjustBB); + return Phi; + } + return AdjustedBase; } llvm::Value * @@ -442,32 +613,90 @@ MicrosoftCXXABI::EmitMemberDataPointerAddress(CodeGenFunction &CGF, llvm::Value *Base, llvm::Value *MemPtr, const MemberPointerType *MPT) { + assert(MPT->isMemberDataPointer()); unsigned AS = Base->getType()->getPointerAddressSpace(); llvm::Type *PType = CGF.ConvertTypeForMem(MPT->getPointeeType())->getPointerTo(AS); CGBuilderTy &Builder = CGF.Builder; - - if (MPT->isMemberFunctionPointer()) { - ErrorUnsupportedABI(CGF, "function member pointer address"); - return llvm::Constant::getNullValue(PType); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + + // Extract the fields we need, regardless of model. We'll apply them if we + // have them. + llvm::Value *FieldOffset = MemPtr; + llvm::Value *VirtualBaseAdjustmentOffset = 0; + llvm::Value *VBPtrOffset = 0; + if (MemPtr->getType()->isStructTy()) { + // We need to extract values. + unsigned I = 0; + FieldOffset = Builder.CreateExtractValue(MemPtr, I++); + if (hasVBPtrOffsetField(Inheritance)) + VBPtrOffset = Builder.CreateExtractValue(MemPtr, I++); + if (hasVirtualBaseAdjustmentField(Inheritance)) + VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, I++); } - llvm::Value *Addr; - if (isSimpleMemberPointer(MPT)) { - // Add the offset with GEP and i8*. - assert(MemPtr->getType() == CGM.PtrDiffTy); - Base = Builder.CreateBitCast(Base, Builder.getInt8Ty()->getPointerTo(AS)); - Addr = Builder.CreateInBoundsGEP(Base, MemPtr, "memptr.offset"); - } else { - ErrorUnsupportedABI(CGF, "non-scalar member pointers"); - return llvm::Constant::getNullValue(PType); + if (VirtualBaseAdjustmentOffset) { + Base = AdjustVirtualBase(CGF, RD, Base, VirtualBaseAdjustmentOffset, + VBPtrOffset); } + llvm::Value *Addr = + Builder.CreateInBoundsGEP(Base, FieldOffset, "memptr.offset"); // Cast the address to the appropriate pointer type, adopting the address // space of the base pointer. return Builder.CreateBitCast(Addr, PType); } +llvm::Value * +MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, + llvm::Value *&This, + llvm::Value *MemPtr, + const MemberPointerType *MPT) { + assert(MPT->isMemberFunctionPointer()); + const FunctionProtoType *FPT = + MPT->getPointeeType()->castAs<FunctionProtoType>(); + const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl(); + llvm::FunctionType *FTy = + CGM.getTypes().GetFunctionType( + CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + CGBuilderTy &Builder = CGF.Builder; + + MSInheritanceModel Inheritance = RD->getMSInheritanceModel(); + + // Extract the fields we need, regardless of model. We'll apply them if we + // have them. + llvm::Value *FunctionPointer = MemPtr; + llvm::Value *NonVirtualBaseAdjustment = NULL; + llvm::Value *VirtualBaseAdjustmentOffset = NULL; + llvm::Value *VBPtrOffset = NULL; + if (MemPtr->getType()->isStructTy()) { + // We need to extract values. + unsigned I = 0; + FunctionPointer = Builder.CreateExtractValue(MemPtr, I++); + if (hasVBPtrOffsetField(Inheritance)) + VBPtrOffset = Builder.CreateExtractValue(MemPtr, I++); + if (hasNonVirtualBaseAdjustmentField(MPT, Inheritance)) + NonVirtualBaseAdjustment = Builder.CreateExtractValue(MemPtr, I++); + if (hasVirtualBaseAdjustmentField(Inheritance)) + VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, I++); + } + + if (VirtualBaseAdjustmentOffset) { + This = AdjustVirtualBase(CGF, RD, This, VirtualBaseAdjustmentOffset, + VBPtrOffset); + } + + if (NonVirtualBaseAdjustment) { + // Apply the adjustment and cast back to the original struct type. + llvm::Value *Ptr = Builder.CreateBitCast(This, Builder.getInt8PtrTy()); + Ptr = Builder.CreateInBoundsGEP(Ptr, NonVirtualBaseAdjustment); + This = Builder.CreateBitCast(Ptr, This->getType(), "this.adjusted"); + } + + return Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo()); +} + CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) { return new MicrosoftCXXABI(CGM); } diff --git a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp index 997e007086..1206463587 100755 --- a/test/CodeGenCXX/microsoft-abi-member-pointers.cpp +++ b/test/CodeGenCXX/microsoft-abi-member-pointers.cpp @@ -1,10 +1,59 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s + +struct B1 { + int b; +}; +struct B2 { }; +struct Single : B1 { }; +struct Multiple : B1, B2 { }; +struct Virtual : virtual B1 { + int v; +}; struct POD { int a; int b; }; +struct Polymorphic { + virtual void myVirtual(); + int a; + int b; +}; + +// This class uses the virtual inheritance model, yet its vbptr offset is not 0. +// We still use zero for the null field offset, despite it being a valid field +// offset. +struct NonZeroVBPtr : POD, Virtual { + int n; +}; + +struct Unspecified; + +// Check that we can lower the LLVM types and get the initializers right. +int Single ::*s_d_memptr; +int Polymorphic::*p_d_memptr; +int Multiple ::*m_d_memptr; +int Virtual ::*v_d_memptr; +int NonZeroVBPtr::*n_d_memptr; +int Unspecified::*u_d_memptr; +// CHECK: @"\01?s_d_memptr@@3PQSingle@@HA" = global i32 -1, align 4 +// CHECK: @"\01?p_d_memptr@@3PQPolymorphic@@HA" = global i32 0, align 4 +// CHECK: @"\01?m_d_memptr@@3PQMultiple@@HA" = global i32 -1, align 4 +// CHECK: @"\01?v_d_memptr@@3PQVirtual@@HA" = global { i32, i32 } +// CHECK: { i32 0, i32 -1 }, align 4 +// CHECK: @"\01?n_d_memptr@@3PQNonZeroVBPtr@@HA" = global { i32, i32 } +// CHECK: { i32 0, i32 -1 }, align 4 +// CHECK: @"\01?u_d_memptr@@3PQUnspecified@@HA" = global { i32, i32, i32 } +// CHECK: { i32 0, i32 0, i32 -1 }, align 4 + +void (Single ::*s_f_memptr)(); +void (Multiple::*m_f_memptr)(); +void (Virtual ::*v_f_memptr)(); +// CHECK: @"\01?s_f_memptr@@3P8Single@@AEXXZA" = global i8* null, align 4 +// CHECK: @"\01?m_f_memptr@@3P8Multiple@@AEXXZA" = global { i8*, i32 } zeroinitializer, align 4 +// CHECK: @"\01?v_f_memptr@@3P8Virtual@@AEXXZA" = global { i8*, i32, i32 } zeroinitializer, align 4 + void podMemPtrs() { int POD::*memptr; memptr = &POD::a; @@ -24,12 +73,6 @@ void podMemPtrs() { // CHECK: } } -struct Polymorphic { - virtual void myVirtual(); - int a; - int b; -}; - void polymorphicMemPtrs() { int Polymorphic::*memptr; memptr = &Polymorphic::a; @@ -49,3 +92,133 @@ void polymorphicMemPtrs() { // CHECK: ret void // CHECK: } } + +bool nullTestDataUnspecified(int Unspecified::*mp) { + return mp; +// CHECK: define zeroext i1 @"\01?nullTestDataUnspecified@@YA_NPQUnspecified@@H@Z"{{.*}} { +// CHECK: %{{.*}} = load { i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: store { i32, i32, i32 } {{.*}} align 4 +// CHECK: %[[mp:.*]] = load { i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: %[[mp0:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 0 +// CHECK: %[[cmp0:.*]] = icmp ne i32 %[[mp0]], 0 +// CHECK: %[[mp1:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 1 +// CHECK: %[[cmp1:.*]] = icmp ne i32 %[[mp1]], 0 +// CHECK: %[[and0:.*]] = and i1 %[[cmp0]], %[[cmp1]] +// CHECK: %[[mp2:.*]] = extractvalue { i32, i32, i32 } %[[mp]], 2 +// CHECK: %[[cmp2:.*]] = icmp ne i32 %[[mp2]], -1 +// CHECK: %[[and1:.*]] = and i1 %[[and0]], %[[cmp2]] +// CHECK: ret i1 %[[and1]] +// CHECK: } +} + +bool nullTestFunctionUnspecified(void (Unspecified::*mp)()) { + return mp; +// CHECK: define zeroext i1 @"\01?nullTestFunctionUnspecified@@YA_NP8Unspecified@@AEXXZ@Z"{{.*}} { +// CHECK: %{{.*}} = load { i8*, i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: store { i8*, i32, i32, i32 } {{.*}} align 4 +// CHECK: %[[mp:.*]] = load { i8*, i32, i32, i32 }* %{{.*}}, align 4 +// CHECK: %[[mp0:.*]] = extractvalue { i8*, i32, i32, i32 } %[[mp]], 0 +// CHECK: %[[cmp0:.*]] = icmp ne i8* %[[mp0]], null +// CHECK: ret i1 %[[cmp0]] +// CHECK: } +} + +int loadDataMemberPointerVirtual(Virtual *o, int Virtual::*memptr) { + return o->*memptr; +// Test that we can unpack this aggregate member pointer and load the member +// data pointer. +// CHECK: define i32 @"\01?loadDataMemberPointerVirtual@@YAHPAUVirtual@@PQ1@H@Z"{{.*}} { +// CHECK: %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4 +// CHECK: %[[memptr:.*]] = load { i32, i32 }* %memptr.addr, align 4 +// CHECK: %[[memptr0:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i32, i32 } %[[memptr:.*]], 1 +// CHECK: %[[v6:.*]] = bitcast %{{.*}}* %[[o]] to i8* +// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[v6]], i32 0 +// CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** +// CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] +// CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr1]] +// CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* +// CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] +// CHECK: %[[v10:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] +// CHECK: %[[offset:.*]] = getelementptr inbounds i8* %[[v10]], i32 %[[memptr0]] +// CHECK: %[[v11:.*]] = bitcast i8* %[[offset]] to i32* +// CHECK: %[[v12:.*]] = load i32* %[[v11]] +// CHECK: ret i32 %[[v12]] +// CHECK: } +} + +int loadDataMemberPointerUnspecified(Unspecified *o, int Unspecified::*memptr) { + return o->*memptr; +// Test that we can unpack this aggregate member pointer and load the member +// data pointer. +// CHECK: define i32 @"\01?loadDataMemberPointerUnspecified@@YAHPAUUnspecified@@PQ1@H@Z"{{.*}} { +// CHECK: %[[o:.*]] = load %{{.*}}** %{{.*}}, align 4 +// CHECK: %[[memptr:.*]] = load { i32, i32, i32 }* %memptr.addr, align 4 +// CHECK: %[[memptr0:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 1 +// CHECK: %[[memptr2:.*]] = extractvalue { i32, i32, i32 } %[[memptr:.*]], 2 +// CHECK: %[[base:.*]] = bitcast %{{.*}}* %[[o]] to i8* +// CHECK: %[[is_vbase:.*]] = icmp ne i32 %[[memptr2]], 0 +// CHECK: br i1 %[[is_vbase]], label %[[vadjust:.*]], label %[[skip:.*]] +// +// CHECK: [[vadjust]]: +// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[base]], i32 %[[memptr1]] +// CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** +// CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] +// CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr2]] +// CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* +// CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] +// CHECK: %[[base_adj:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] +// +// CHECK: [[skip]]: +// CHECK: %[[new_base:.*]] = phi i8* [ %[[base]], %entry ], [ %[[base_adj]], %[[vadjust]] ] +// CHECK: %[[offset:.*]] = getelementptr inbounds i8* %[[new_base]], i32 %[[memptr0]] +// CHECK: %[[v11:.*]] = bitcast i8* %[[offset]] to i32* +// CHECK: %[[v12:.*]] = load i32* %[[v11]] +// CHECK: ret i32 %[[v12]] +// CHECK: } +} + +void callMemberPointerSingle(Single *o, void (Single::*memptr)()) { + (o->*memptr)(); +// Just look for an indirect thiscall. +// CHECK: define void @"\01?callMemberPointerSingle@@{{.*}} #0 { +// CHECK: call x86_thiscallcc void %{{.*}}(%{{.*}} %{{.*}}) +// CHECK: ret void +// CHECK: } +} + +void callMemberPointerMultiple(Multiple *o, void (Multiple::*memptr)()) { + (o->*memptr)(); +// CHECK: define void @"\01?callMemberPointerMultiple@@{{.*}} #0 { +// CHECK: %[[memptr0:.*]] = extractvalue { i8*, i32 } %{{.*}}, 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32 } %{{.*}}, 1 +// CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %{{.*}}, i32 %[[memptr1]] +// CHECK: %[[this:.*]] = bitcast i8* %[[this_adjusted]] to {{.*}} +// CHECK: %[[fptr:.*]] = bitcast i8* %[[memptr0]] to {{.*}} +// CHECK: call x86_thiscallcc void %[[fptr]](%{{.*}} %[[this]]) +// CHECK: ret void +// CHECK: } +} + +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: %[[memptr0:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 0 +// CHECK: %[[memptr1:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 1 +// CHECK: %[[memptr2:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 2 +// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %{{.*}}, i32 0 +// CHECK: %[[vbptr_a:.*]] = bitcast i8* %[[vbptr]] to i8** +// CHECK: %[[vbtable:.*]] = load i8** %[[vbptr_a:.*]] +// CHECK: %[[v7:.*]] = getelementptr inbounds i8* %[[vbtable]], i32 %[[memptr2]] +// CHECK: %[[v8:.*]] = bitcast i8* %[[v7]] to i32* +// CHECK: %[[vbase_offs:.*]] = load i32* %[[v8]] +// CHECK: %[[v10:.*]] = getelementptr inbounds i8* %[[vbptr]], i32 %[[vbase_offs]] +// CHECK: %[[this_adjusted:.*]] = getelementptr inbounds i8* %[[v10]], i32 %[[memptr1]] +// CHECK: %[[fptr:.*]] = bitcast i8* %[[memptr0]] to void ({{.*}}) +// CHECK: %[[this:.*]] = bitcast i8* %[[this_adjusted]] to {{.*}} +// CHECK: call x86_thiscallcc void %[[fptr]](%{{.*}} %[[this]]) +// CHECK: ret void +// CHECK: } +} |