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