[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

llvm-svn: 179305
This commit is contained in:
Reid Kleckner 2013-04-11 18:13:19 +00:00
parent 0ff3bf96f2
commit 2341ae3856
5 changed files with 483 additions and 79 deletions

View File

@ -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.

View File

@ -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; }

View File

@ -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);

View File

@ -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) {
const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl();
return (MPT->isMemberDataPointer() &&
!MPT->getClass()->isIncompleteType() &&
RD->getNumVBases() == 0);
// Member pointer helpers.
static bool hasVBPtrOffsetField(MSInheritanceModel Inheritance) {
return Inheritance == MSIM_Unspecified;
}
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();
// 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();
MSInheritanceModel Inheritance = RD->getMSInheritanceModel();
return (!hasVirtualBaseAdjustmentField(Inheritance) &&
nullFieldOffsetIsZero(Inheritance));
}
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 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");
// 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;
}
// 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;
const CXXRecordDecl *RD = MPT->getClass()->getAsCXXRecordDecl();
MSInheritanceModel Inheritance = RD->getMSInheritanceModel();
if (MPT->isMemberFunctionPointer()) {
ErrorUnsupportedABI(CGF, "function member pointer address");
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++);
}
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);
}

View File

@ -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: }
}