r179305 - [ms-cxxabi] Implement member pointer emission and dereferencing

Reid Kleckner reid at kleckner.net
Thu Apr 11 11:13:20 PDT 2013


Author: rnk
Date: Thu Apr 11 13:13:19 2013
New Revision: 179305

URL: http://llvm.org/viewvc/llvm-project?rev=179305&view=rev
Log:
[ms-cxxabi] Implement member pointer emission and dereferencing

Summary:
Handles all inheritance models for both data and function member
pointers.

Also implements isZeroInitializable() and refactors some of the null
member pointer code.

MSVC supports converting member pointers through virtual bases, which
clang does not (yet?) support.  Implementing that extension is covered
by http://llvm.org/15713

Reviewers: rjmccall

CC: cfe-commits

Differential Revision: http://llvm-reviews.chandlerc.com/D613

Modified:
    cfe/trunk/include/clang/AST/DeclCXX.h
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/lib/AST/MicrosoftCXXABI.cpp
    cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp

Modified: cfe/trunk/include/clang/AST/DeclCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclCXX.h?rev=179305&r1=179304&r2=179305&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclCXX.h (original)
+++ cfe/trunk/include/clang/AST/DeclCXX.h Thu Apr 11 13:13:19 2013
@@ -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.

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=179305&r1=179304&r2=179305&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Thu Apr 11 13:13:19 2013
@@ -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; }

Modified: cfe/trunk/lib/AST/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftCXXABI.cpp?rev=179305&r1=179304&r2=179305&view=diff
==============================================================================
--- cfe/trunk/lib/AST/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/AST/MicrosoftCXXABI.cpp Thu Apr 11 13:13:19 2013
@@ -89,8 +89,8 @@ MSInheritanceModel CXXRecordDecl::getMSI
   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::getMSI
 //
 //     // 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::getMSMemberPointerSlo
     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::getMSMemberPointerSlo
     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> MicrosoftC
   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);

Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=179305&r1=179304&r2=179305&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Thu Apr 11 13:13:19 2013
@@ -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(Co
   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::EmitMemberPointerIsNotN
                                             llvm::Value *MemPtr,
                                             const MemberPointerType *MPT) {
   CGBuilderTy &Builder = CGF.Builder;
-
-  // 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");
+  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;
+}
 
-  // FIXME: Implement member pointers other inheritance models.
-  ErrorUnsupportedABI(CGF, "function member pointer tests");
-  return GetBogusMemberPointer(QualType(MPT, 0));
+// 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);
+  }
+
+  // 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::EmitMemberDataPointerAd
                                               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;
+  const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl();
+  MSInheritanceModel Inheritance = RD->getMSInheritanceModel();
 
-  if (MPT->isMemberFunctionPointer()) {
-    ErrorUnsupportedABI(CGF, "function member pointer address");
-    return llvm::Constant::getNullValue(PType);
-  }
-
-  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);
+  // 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++);
+  }
+
+  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);
 }

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=179305&r1=179304&r2=179305&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-member-pointers.cpp Thu Apr 11 13:13:19 2013
@@ -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 at 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 at 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 at 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 at 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: }
+}





More information about the cfe-commits mailing list