Hi rjmccall,
Handles all inheritance models for both data and function member
pointers. This does not require knowing the layout of the vbtable,
because the member pointer itself carries the offset into the vbtable.
Clang still cannot emit multi-field member pointers because it would
need to know the layout of the vbtable to generate the correct offsets.
Also implements isZeroInitializable() and refactors some of the null
member pointer code.
http://llvm-reviews.chandlerc.com/D613
Files:
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/microsoft-abi-member-pointers.cpp
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -111,17 +111,24 @@
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);
}
+ llvm::Value *AdjustVirtualBase(CGBuilderTy &Builder, const CXXRecordDecl *RD,
+ llvm::Value *Base,
+ llvm::Value *VirtualBaseAdjustmentOffset,
+ llvm::Value *VBTableOffset /* 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 +143,12 @@
llvm::Value *MemPtr,
const MemberPointerType *MPT);
+ virtual llvm::Value *
+ EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF,
+ llvm::Value *&This,
+ llvm::Value *MemPtr,
+ const MemberPointerType *MPT);
+
};
}
@@ -379,95 +392,243 @@
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) {
+static bool usesAllOnesForNull(const MemberPointerType *MPT) {
+ // Member function pointers use a null function pointer.
+ if (MPT->isMemberFunctionPointer())
+ return false;
const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl();
- return (MPT->isMemberDataPointer() &&
- !MPT->getClass()->isIncompleteType() &&
- RD->getNumVBases() == 0);
+ MSInheritanceModel Inheritance = RD->getMSInheritanceModel();
+ // Only non-polymorphic single inheritance classes use -1.
+ return Inheritance == MSIM_Single && !RD->isPolymorphic();
}
-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();
- }
- return GetBogusMemberPointer(QualType(MPT, 0));
+bool MicrosoftCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
+ // We only test the first field when doing null checks. MSVC checks all
+ // fields, but short-circuits out after the first field. Therefore we can
+ // zero initialize anything except data member pointers that use -1 for null.
+ return !usesAllOnesForNull(MPT);
+}
+
+llvm::Type *
+MicrosoftCXXABI::ConvertMemberPointerType(const MemberPointerType *MPT) {
+ llvm::SmallVector<llvm::Type *, 4> fields;
+ unsigned Ptrs, Ints;
+ llvm::tie(Ptrs, Ints) = MPT->getMSMemberPointerSlots();
+ for (unsigned i = 0; i < Ptrs; ++i)
+ fields.push_back(CGM.VoidPtrTy);
+ for (unsigned i = 0; i < Ints; ++i)
+ fields.push_back(CGM.IntTy);
+ if (fields.size() == 1)
+ return fields[0];
+ return llvm::StructType::get(CGM.getLLVMContext(), fields);
}
llvm::Constant *
MicrosoftCXXABI::EmitNullMemberPointer(const MemberPointerType *MPT) {
- if (isSimpleMemberPointer(MPT))
- return getSimpleNullMemberPointer(MPT);
- // FIXME: Implement function member pointers.
- return GetBogusMemberPointer(QualType(MPT, 0));
+ if (usesAllOnesForNull(MPT))
+ return getAllOnesInt();
+ llvm::SmallVector<llvm::Constant *, 4> fields;
+ unsigned Ptrs, Ints;
+ llvm::tie(Ptrs, Ints) = MPT->getMSMemberPointerSlots();
+ unsigned i;
+ for (i = 0; i < Ptrs; ++i)
+ fields.push_back(llvm::Constant::getNullValue(CGM.VoidPtrTy));
+ for (i = 0; i < Ints && i < 3; ++i)
+ fields.push_back(getZeroInt());
+ if (i == 3)
+ fields.push_back(getAllOnesInt());
+ if (fields.size() == 1)
+ return fields[0];
+ return llvm::ConstantStruct::getAnon(fields);
}
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::Constant *FieldOffset =
+ llvm::ConstantInt::get(CGM.IntTy, offset.getQuantity());
+ switch (Inheritance) {
+ default:
+ llvm_unreachable("invalid inheritance");
+ case MSIM_Unspecified: {
+ CharUnits offs = getContext().getASTRecordLayout(RD).getVBPtrOffset();
+ llvm::Constant *VBTableOffset =
+ llvm::ConstantInt::get(CGM.IntTy, offs.getQuantity());
+ llvm::Constant *fields[] = {
+ FieldOffset,
+ VBTableOffset,
+ getZeroInt() // FIXME: VirtualBaseAdjustmentOffset
+ };
+ return llvm::ConstantStruct::getAnon(fields);
+ }
+ case MSIM_Virtual: {
+ llvm::Constant *fields[] = {
+ FieldOffset,
+ getZeroInt() // FIXME: VirtualBaseAdjustmentOffset
+ };
+ return llvm::ConstantStruct::getAnon(fields);
+ }
+ case MSIM_Multiple:
+ case MSIM_Single:
+ return FieldOffset;
+ }
}
llvm::Value *
MicrosoftCXXABI::EmitMemberPointerIsNotNull(CodeGenFunction &CGF,
llvm::Value *MemPtr,
const MemberPointerType *MPT) {
+ // We only need to test the first field. Usually it's against zero, and in
+ // one case it's -1.
CGBuilderTy &Builder = CGF.Builder;
+ llvm::Value *FirstField = MemPtr;
+ if (MemPtr->getType()->isStructTy())
+ FirstField = Builder.CreateExtractValue(MemPtr, 0);
+ llvm::Value *Val;
+ if (usesAllOnesForNull(MPT))
+ Val = llvm::ConstantInt::getAllOnesValue(FirstField->getType());
+ else
+ Val = llvm::Constant::getNullValue(FirstField->getType());
+ return Builder.CreateICmpNE(FirstField, Val, "memptr.tobool");
+}
- // 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(CGBuilderTy &Builder,
+ const CXXRecordDecl *RD, llvm::Value *Base,
+ llvm::Value *VirtualBaseAdjustmentOffset,
+ llvm::Value *VBTableOffset) {
+ llvm::Type *Int8Ptr = Builder.getInt8Ty()->getPointerTo(0);
+ Base = Builder.CreateBitCast(Base, Int8Ptr);
+ if (!VBTableOffset) {
+ CharUnits offs = getContext().getASTRecordLayout(RD).getVBPtrOffset();
+ VBTableOffset = llvm::ConstantInt::get(CGM.IntTy, offs.getQuantity());
}
-
- // FIXME: Implement member pointers other inheritance models.
- ErrorUnsupportedABI(CGF, "function member pointer tests");
- return GetBogusMemberPointer(QualType(MPT, 0));
+ // Load the vbtable pointer from the vbtable offset in the instance.
+ llvm::Value *VBPtr =
+ Builder.CreateInBoundsGEP(Base, VBTableOffset, "memptr.vbptr");
+ llvm::Value *VBTable = Builder.CreateBitCast(VBPtr, Int8Ptr->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, Builder.getInt32Ty()->getPointerTo(0));
+ VBaseOffs = Builder.CreateLoad(VBaseOffs, "memptr.vbase_offs");
+ // Add it to VBPtr.
+ return Builder.CreateInBoundsGEP(VBPtr, VBaseOffs);
}
llvm::Value *
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;
+ llvm::Value *VirtualBaseAdjustmentOffset = NULL;
+ llvm::Value *VBTableOffset = NULL;
+ switch (Inheritance) {
+ case MSIM_Unspecified:
+ FieldOffset = Builder.CreateExtractValue(MemPtr, 0);
+ VBTableOffset = Builder.CreateExtractValue(MemPtr, 1);
+ VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, 2);
+ break;
+ case MSIM_Virtual:
+ FieldOffset = Builder.CreateExtractValue(MemPtr, 0);
+ VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, 1);
+ break;
+ case MSIM_Single:
+ case MSIM_Multiple:
+ assert(MemPtr->getType() == CGM.IntTy);
+ FieldOffset = MemPtr;
+ break;
}
- 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(Builder, RD, Base, VirtualBaseAdjustmentOffset,
+ VBTableOffset);
}
+ 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()->getAs<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;
+ llvm::Value *NonVirtualBaseAdjustment = NULL;
+ llvm::Value *VirtualBaseAdjustmentOffset = NULL;
+ llvm::Value *VBTableOffset = NULL;
+ switch (Inheritance) {
+ case MSIM_Unspecified:
+ // FIXME: Verify order.
+ FunctionPointer = Builder.CreateExtractValue(MemPtr, 0);
+ VBTableOffset = Builder.CreateExtractValue(MemPtr, 1);
+ NonVirtualBaseAdjustment = Builder.CreateExtractValue(MemPtr, 2);
+ VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, 3);
+ break;
+ case MSIM_Virtual:
+ FunctionPointer = Builder.CreateExtractValue(MemPtr, 0);
+ NonVirtualBaseAdjustment = Builder.CreateExtractValue(MemPtr, 1);
+ VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(MemPtr, 2);
+ break;
+ case MSIM_Multiple:
+ FunctionPointer = Builder.CreateExtractValue(MemPtr, 0);
+ NonVirtualBaseAdjustment = Builder.CreateExtractValue(MemPtr, 1);
+ break;
+ case MSIM_Single:
+ assert(MemPtr->getType()->isPointerTy());
+ FunctionPointer = MemPtr;
+ break;
+ }
+
+ if (VirtualBaseAdjustmentOffset) {
+ This = AdjustVirtualBase(Builder, RD, This, VirtualBaseAdjustmentOffset,
+ VBTableOffset);
+ }
+
+ 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);
}
Index: test/CodeGenCXX/microsoft-abi-member-pointers.cpp
===================================================================
--- test/CodeGenCXX/microsoft-abi-member-pointers.cpp
+++ test/CodeGenCXX/microsoft-abi-member-pointers.cpp
@@ -1,10 +1,49 @@
// RUN: %clang_cc1 -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;
+};
+
+struct Unspecified;
+
+// Check that we can lower the LLVM types and get the initializers right. cl
+// seems to zero initialize everything here, which is weird considering that
+// null is -1 for some of these. We go ahead and try to handle some -1 cases.
+int Single ::*s_d_memptr;
+int Polymorphic::*p_d_memptr;
+int Multiple ::*m_d_memptr;
+int Virtual ::*v_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 0, align 4
+// CHECK: @"\01?v_d_memptr@@3PQVirtual@@HA" = global { i32, i32 } zeroinitializer, align 4
+// CHECK: @"\01?u_d_memptr@@3PQUnspecified@@HA" = global { i32, i32, i32 } zeroinitializer, 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 +63,6 @@
// CHECK: }
}
-struct Polymorphic {
- virtual void myVirtual();
- int a;
- int b;
-};
-
void polymorphicMemPtrs() {
int Polymorphic::*memptr;
memptr = &Polymorphic::a;
@@ -49,3 +82,96 @@
// CHECK: ret void
// 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: %[[v6:.*]] = bitcast %{{.*}}* %[[o]] to i8*
+// CHECK: %[[vbptr:.*]] = getelementptr inbounds i8* %[[v6]], 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: %[[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: }
+}
+
+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: }
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits