[MS ABI] Rework member pointer conversion

Member pointers in the MS ABI are made complicated due to the following:
- Virtual methods in the most derived class (MDC) might live in a
  vftable in a virtual base.
- There are four different representations of member pointer: single
  inheritance, multiple inheritance, virtual inheritance and the "most
  general" representation.
- Bases might have a *more* general representation than classes which
  derived from them, a most surprising result.

We believed that we could treat all member pointers as-if they were a
degenerate case of the multiple inheritance model.  This fell apart once
we realized that implementing standard member pointers using this ABI
requires referencing members with a non-zero vbindex.

On a bright note, all but the virtual inheritance model operate rather
similarly.  The virtual inheritance member pointer representation
awkwardly requires a virtual base adjustment in order to refer to
entities in the MDC.

However, the first virtual base might be quite far from the start of the
virtual base.  This means that we must add a negative non-virtual
displacement.

However, things get even more complicated.  The most general
representation interprets vbindex zero differently from the virtual
inheritance model: it doesn't reference the vbtable at all.

It turns out that this complexity can increase for quite some time:
consider a derived to base conversion from the most general model to the
multiple inheritance model...

To manage this complexity we introduce a concept of "normalized" member
pointer which allows us to treat all three models as the most general
model.  Then we try to figure out how to map this generalized member
pointer onto the destination member pointer model.  I've done my best to
furnish the code with comments explaining why each adjustment is
performed.

This fixes PR23878.

llvm-svn: 240384
This commit is contained in:
David Majnemer 2015-06-23 07:31:11 +00:00
parent 5ca193c333
commit c1709d387e
6 changed files with 307 additions and 68 deletions

View File

@ -204,6 +204,10 @@ public:
virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD,
raw_ostream &) = 0;
virtual void mangleCXXVirtualDisplacementMap(const CXXRecordDecl *SrcRD,
const CXXRecordDecl *DstRD,
raw_ostream &Out) = 0;
virtual void mangleCXXThrowInfo(QualType T, bool IsConst, bool IsVolatile,
uint32_t NumEntries, raw_ostream &Out) = 0;

View File

@ -115,6 +115,9 @@ public:
void mangleCXXVBTable(const CXXRecordDecl *Derived,
ArrayRef<const CXXRecordDecl *> BasePath,
raw_ostream &Out) override;
void mangleCXXVirtualDisplacementMap(const CXXRecordDecl *SrcRD,
const CXXRecordDecl *DstRD,
raw_ostream &Out) override;
void mangleCXXThrowInfo(QualType T, bool IsConst, bool IsVolatile,
uint32_t NumEntries, raw_ostream &Out) override;
void mangleCXXCatchableTypeArray(QualType T, uint32_t NumEntries,
@ -2395,6 +2398,15 @@ void MicrosoftMangleContextImpl::mangleCXXCatchHandlerType(QualType T,
Mangler.getStream() << '.' << Flags;
}
void MicrosoftMangleContextImpl::mangleCXXVirtualDisplacementMap(
const CXXRecordDecl *SrcRD, const CXXRecordDecl *DstRD, raw_ostream &Out) {
MicrosoftCXXNameMangler Mangler(*this, Out);
Mangler.getStream() << "\01??_K";
Mangler.mangleName(SrcRD);
Mangler.getStream() << "$C";
Mangler.mangleName(DstRD);
}
void MicrosoftMangleContextImpl::mangleCXXThrowInfo(QualType T,
bool IsConst,
bool IsVolatile,

View File

@ -29,13 +29,12 @@
using namespace clang;
using namespace CodeGen;
static CharUnits
ComputeNonVirtualBaseClassOffset(ASTContext &Context,
const CXXRecordDecl *DerivedClass,
CastExpr::path_const_iterator Start,
CastExpr::path_const_iterator End) {
CharUnits CodeGenModule::computeNonVirtualBaseClassOffset(
const CXXRecordDecl *DerivedClass, CastExpr::path_const_iterator Start,
CastExpr::path_const_iterator End) {
CharUnits Offset = CharUnits::Zero();
const ASTContext &Context = getContext();
const CXXRecordDecl *RD = DerivedClass;
for (CastExpr::path_const_iterator I = Start; I != End; ++I) {
@ -64,8 +63,7 @@ CodeGenModule::GetNonVirtualBaseClassOffset(const CXXRecordDecl *ClassDecl,
assert(PathBegin != PathEnd && "Base path should not be empty!");
CharUnits Offset =
ComputeNonVirtualBaseClassOffset(getContext(), ClassDecl,
PathBegin, PathEnd);
computeNonVirtualBaseClassOffset(ClassDecl, PathBegin, PathEnd);
if (Offset.isZero())
return nullptr;
@ -158,9 +156,8 @@ llvm::Value *CodeGenFunction::GetAddressOfBaseClass(
// Compute the static offset of the ultimate destination within its
// allocating subobject (the virtual base, if there is one, or else
// the "complete" object that we see).
CharUnits NonVirtualOffset =
ComputeNonVirtualBaseClassOffset(getContext(), VBase ? VBase : Derived,
Start, PathEnd);
CharUnits NonVirtualOffset = CGM.computeNonVirtualBaseClassOffset(
VBase ? VBase : Derived, Start, PathEnd);
// If there's a virtual step, we can sometimes "devirtualize" it.
// For now, that's limited to when the derived type is final.

View File

@ -732,6 +732,11 @@ public:
/// Get a reference to the target of VD.
llvm::Constant *GetWeakRefReference(const ValueDecl *VD);
CharUnits
computeNonVirtualBaseClassOffset(const CXXRecordDecl *DerivedClass,
CastExpr::path_const_iterator Start,
CastExpr::path_const_iterator End);
/// Returns the offset from a derived class to a class. Returns null if the
/// offset is 0.
llvm::Constant *

View File

@ -247,6 +247,50 @@ public:
getAddrOfVBTable(const VPtrInfo &VBT, const CXXRecordDecl *RD,
llvm::GlobalVariable::LinkageTypes Linkage);
llvm::GlobalVariable *
getAddrOfVirtualDisplacementMap(const CXXRecordDecl *SrcRD,
const CXXRecordDecl *DstRD) {
SmallString<256> OutName;
llvm::raw_svector_ostream Out(OutName);
getMangleContext().mangleCXXVirtualDisplacementMap(SrcRD, DstRD, Out);
Out.flush();
StringRef MangledName = OutName.str();
if (auto *VDispMap = CGM.getModule().getNamedGlobal(MangledName))
return VDispMap;
MicrosoftVTableContext &VTContext = CGM.getMicrosoftVTableContext();
unsigned NumEntries = 1 + SrcRD->getNumVBases();
SmallVector<llvm::Constant *, 4> Map(NumEntries,
llvm::UndefValue::get(CGM.IntTy));
Map[0] = llvm::ConstantInt::get(CGM.IntTy, 0);
bool AnyDifferent = false;
for (const auto &I : SrcRD->vbases()) {
const CXXRecordDecl *VBase = I.getType()->getAsCXXRecordDecl();
if (!DstRD->isVirtuallyDerivedFrom(VBase))
continue;
unsigned SrcVBIndex = VTContext.getVBTableIndex(SrcRD, VBase);
unsigned DstVBIndex = VTContext.getVBTableIndex(DstRD, VBase);
Map[SrcVBIndex] = llvm::ConstantInt::get(CGM.IntTy, DstVBIndex * 4);
AnyDifferent |= SrcVBIndex != DstVBIndex;
}
// This map would be useless, don't use it.
if (!AnyDifferent)
return nullptr;
llvm::ArrayType *VDispMapTy = llvm::ArrayType::get(CGM.IntTy, Map.size());
llvm::Constant *Init = llvm::ConstantArray::get(VDispMapTy, Map);
llvm::GlobalValue::LinkageTypes Linkage =
SrcRD->isExternallyVisible() && DstRD->isExternallyVisible()
? llvm::GlobalValue::LinkOnceODRLinkage
: llvm::GlobalValue::InternalLinkage;
auto *VDispMap = new llvm::GlobalVariable(
CGM.getModule(), VDispMapTy, /*Constant=*/true, Linkage,
/*Initializer=*/Init, MangledName);
return VDispMap;
}
void emitVBTableDefinition(const VPtrInfo &VBT, const CXXRecordDecl *RD,
llvm::GlobalVariable *GV) const;
@ -2458,7 +2502,7 @@ MicrosoftCXXABI::EmitFullMemberPointer(llvm::Constant *FirstField,
if (MSInheritanceAttr::hasVBPtrOffsetField(Inheritance)) {
CharUnits Offs = CharUnits::Zero();
if (VBTableIndex && RD->getNumVBases())
if (VBTableIndex)
Offs = getContext().getASTRecordLayout(RD).getVBPtrOffset();
fields.push_back(llvm::ConstantInt::get(CGM.IntTy, Offs.getQuantity()));
}
@ -2470,10 +2514,33 @@ MicrosoftCXXABI::EmitFullMemberPointer(llvm::Constant *FirstField,
return llvm::ConstantStruct::getAnon(fields);
}
// Loading virtual member pointers using the virtual inheritance model
// always results in an adjustment using the vbtable even if the index is
// zero.
//
// This is usually OK because the first slot in the vbtable points
// backwards to the top of the MDC. However, the MDC might be reusing a
// vbptr from an nv-base. In this case, the first slot in the vbtable
// points to the start of the nv-base which introduced the vbptr and *not*
// the MDC. Modify the NonVirtualBaseAdjustment to account for this.
static CharUnits computeOffsetOfBaseWithVBPtr(const ASTContext &Ctx,
const CXXRecordDecl *RD) {
CharUnits Offset = CharUnits::Zero();
const ASTRecordLayout *Layout = &Ctx.getASTRecordLayout(RD);
while (const CXXRecordDecl *Base = Layout->getBaseSharingVBPtr()) {
Offset += Layout->getBaseClassOffset(Base);
Layout = &Ctx.getASTRecordLayout(Base);
}
return Offset;
}
llvm::Constant *
MicrosoftCXXABI::EmitMemberDataPointer(const MemberPointerType *MPT,
CharUnits offset) {
const CXXRecordDecl *RD = MPT->getMostRecentCXXRecordDecl();
if (RD->getMSInheritanceModel() ==
MSInheritanceAttr::Keyword_virtual_inheritance)
offset -= computeOffsetOfBaseWithVBPtr(getContext(), RD);
llvm::Constant *FirstField =
llvm::ConstantInt::get(CGM.IntTy, offset.getQuantity());
return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/false, RD,
@ -2558,20 +2625,24 @@ MicrosoftCXXABI::EmitMemberFunctionPointer(const CXXMethodDecl *MD) {
Ty = CGM.PtrDiffTy;
}
FirstField = CGM.GetAddrOfFunction(MD, Ty);
FirstField = llvm::ConstantExpr::getBitCast(FirstField, CGM.VoidPtrTy);
} else {
auto &VTableContext = CGM.getMicrosoftVTableContext();
MicrosoftVTableContext::MethodVFTableLocation ML =
VTableContext.getMethodVFTableLocation(MD);
llvm::Function *Thunk = EmitVirtualMemPtrThunk(MD, ML);
FirstField = llvm::ConstantExpr::getBitCast(Thunk, CGM.VoidPtrTy);
FirstField = EmitVirtualMemPtrThunk(MD, ML);
// Include the vfptr adjustment if the method is in a non-primary vftable.
NonVirtualBaseAdjustment += ML.VFPtrOffset;
if (ML.VBase)
VBTableIndex = VTableContext.getVBTableIndex(RD, ML.VBase) * 4;
}
if (VBTableIndex == 0 &&
RD->getMSInheritanceModel() ==
MSInheritanceAttr::Keyword_virtual_inheritance)
NonVirtualBaseAdjustment -= computeOffsetOfBaseWithVBPtr(getContext(), RD);
// The rest of the fields are common with data member pointers.
FirstField = llvm::ConstantExpr::getBitCast(FirstField, CGM.VoidPtrTy);
return EmitFullMemberPointer(FirstField, /*IsMemberFunction=*/true, RD,
NonVirtualBaseAdjustment, VBTableIndex);
}
@ -2829,11 +2900,6 @@ llvm::Value *MicrosoftCXXABI::EmitMemberDataPointerAddress(
return Builder.CreateBitCast(Addr, PType);
}
static MSInheritanceAttr::Spelling
getInheritanceFromMemptr(const MemberPointerType *MPT) {
return MPT->getMostRecentCXXRecordDecl()->getMSInheritanceModel();
}
llvm::Value *
MicrosoftCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF,
const CastExpr *E,
@ -2887,10 +2953,11 @@ MicrosoftCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF,
// Decompose src.
llvm::Value *FirstField = Src;
llvm::Value *NonVirtualBaseAdjustment = nullptr;
llvm::Value *VirtualBaseAdjustmentOffset = nullptr;
llvm::Value *VBPtrOffset = nullptr;
llvm::Value *NonVirtualBaseAdjustment = getZeroInt();
llvm::Value *VirtualBaseAdjustmentOffset = getZeroInt();
llvm::Value *VBPtrOffset = getZeroInt();
MSInheritanceAttr::Spelling SrcInheritance = SrcRD->getMSInheritanceModel();
MSInheritanceAttr::Spelling DstInheritance = DstRD->getMSInheritanceModel();
if (!MSInheritanceAttr::hasOnlyOneField(IsFunc, SrcInheritance)) {
// We need to extract values.
unsigned I = 0;
@ -2903,25 +2970,95 @@ MicrosoftCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF,
VirtualBaseAdjustmentOffset = Builder.CreateExtractValue(Src, I++);
}
bool IsDerivedToBase = (E->getCastKind() == CK_DerivedToBaseMemberPointer);
const MemberPointerType *DerivedTy = IsDerivedToBase ? SrcTy : DstTy;
const CXXRecordDecl *DerivedClass = DerivedTy->getMostRecentCXXRecordDecl();
// 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");
llvm::Value *&NVAdjustField = IsFunc ? NonVirtualBaseAdjustment : FirstField;
// The virtual inheritance model has a quirk: the virtual base table is always
// referenced when dereferencing a member pointer even if the member pointer
// is non-virtual. This is accounted for by adjusting the non-virtual offset
// to point backwards to the top of the MDC from the first VBase. Undo this
// adjustment to normalize the member pointer.
llvm::Value *SrcVBIndexEqZero =
Builder.CreateICmpEQ(VirtualBaseAdjustmentOffset, getZeroInt());
if (SrcInheritance == MSInheritanceAttr::Keyword_virtual_inheritance) {
if (int64_t SrcOffsetToFirstVBase =
computeOffsetOfBaseWithVBPtr(getContext(), SrcRD).getQuantity()) {
llvm::Value *UndoSrcAdjustment = Builder.CreateSelect(
SrcVBIndexEqZero,
llvm::ConstantInt::get(CGM.IntTy, SrcOffsetToFirstVBase),
getZeroInt());
NVAdjustField = Builder.CreateNSWAdd(NVAdjustField, UndoSrcAdjustment);
}
}
// FIXME PR15713: Support conversions through virtually derived classes.
// A non-zero vbindex implies that we are dealing with a source member in a
// floating virtual base in addition to some non-virtual offset. If the
// vbindex is zero, we are dealing with a source that exists in a non-virtual,
// fixed, base. The difference between these two cases is that the vbindex +
// nvoffset *always* point to the member regardless of what context they are
// evaluated in so long as the vbindex is adjusted. A member inside a fixed
// base requires explicit nv adjustment.
llvm::Constant *BaseClassOffset = llvm::ConstantInt::get(
CGM.IntTy, CGM.computeNonVirtualBaseClassOffset(
DerivedClass, E->path_begin(), E->path_end())
.getQuantity());
llvm::Value *NVDisp;
if (IsDerivedToBase)
NVDisp = Builder.CreateNSWSub(NVAdjustField, BaseClassOffset, "adj");
else
NVDisp = Builder.CreateNSWAdd(NVAdjustField, BaseClassOffset, "adj");
NVAdjustField = Builder.CreateSelect(SrcVBIndexEqZero, NVDisp, getZeroInt());
// Update the vbindex to an appropriate value in the destination because
// SrcRD's vbtable might not be a strict prefix of the one in DstRD.
llvm::Value *DstVBIndexEqZero = SrcVBIndexEqZero;
if (MSInheritanceAttr::hasVBTableOffsetField(DstInheritance) &&
MSInheritanceAttr::hasVBTableOffsetField(SrcInheritance)) {
if (llvm::GlobalVariable *VDispMap =
getAddrOfVirtualDisplacementMap(SrcRD, DstRD)) {
llvm::Value *VBIndex = Builder.CreateExactUDiv(
VirtualBaseAdjustmentOffset, llvm::ConstantInt::get(CGM.IntTy, 4));
llvm::Value *Idxs[] = {getZeroInt(), VBIndex};
VirtualBaseAdjustmentOffset =
Builder.CreateLoad(Builder.CreateInBoundsGEP(VDispMap, Idxs));
DstVBIndexEqZero =
Builder.CreateICmpEQ(VirtualBaseAdjustmentOffset, getZeroInt());
}
}
// Set the VBPtrOffset to zero if the vbindex is zero. Otherwise, initialize
// it to the offset of the vbptr.
if (MSInheritanceAttr::hasVBPtrOffsetField(DstInheritance)) {
llvm::Value *DstVBPtrOffset = llvm::ConstantInt::get(
CGM.IntTy,
getContext().getASTRecordLayout(DstRD).getVBPtrOffset().getQuantity());
VBPtrOffset =
Builder.CreateSelect(DstVBIndexEqZero, getZeroInt(), DstVBPtrOffset);
}
// Likewise, apply a similar adjustment so that dereferencing the member
// pointer correctly accounts for the distance between the start of the first
// virtual base and the top of the MDC.
if (DstInheritance == MSInheritanceAttr::Keyword_virtual_inheritance) {
if (int64_t DstOffsetToFirstVBase =
computeOffsetOfBaseWithVBPtr(getContext(), DstRD).getQuantity()) {
llvm::Value *DoDstAdjustment = Builder.CreateSelect(
DstVBIndexEqZero,
llvm::ConstantInt::get(CGM.IntTy, DstOffsetToFirstVBase),
getZeroInt());
NVAdjustField = Builder.CreateNSWSub(NVAdjustField, DoDstAdjustment);
}
}
// Recompose dst from the null struct and the adjusted fields from src.
MSInheritanceAttr::Spelling DstInheritance = DstRD->getMSInheritanceModel();
llvm::Value *Dst;
if (MSInheritanceAttr::hasOnlyOneField(IsFunc, DstInheritance)) {
Dst = FirstField;
@ -2930,14 +3067,11 @@ MicrosoftCXXABI::EmitMemberPointerConversion(CodeGenFunction &CGF,
unsigned Idx = 0;
Dst = Builder.CreateInsertValue(Dst, FirstField, Idx++);
if (MSInheritanceAttr::hasNVOffsetField(IsFunc, DstInheritance))
Dst = Builder.CreateInsertValue(
Dst, getValueOrZeroInt(NonVirtualBaseAdjustment), Idx++);
Dst = Builder.CreateInsertValue(Dst, NonVirtualBaseAdjustment, Idx++);
if (MSInheritanceAttr::hasVBPtrOffsetField(DstInheritance))
Dst = Builder.CreateInsertValue(
Dst, getValueOrZeroInt(VBPtrOffset), Idx++);
Dst = Builder.CreateInsertValue(Dst, VBPtrOffset, Idx++);
if (MSInheritanceAttr::hasVBTableOffsetField(DstInheritance))
Dst = Builder.CreateInsertValue(
Dst, getValueOrZeroInt(VirtualBaseAdjustmentOffset), Idx++);
Dst = Builder.CreateInsertValue(Dst, VirtualBaseAdjustmentOffset, Idx++);
}
Builder.CreateBr(ContinueBB);
@ -2980,14 +3114,16 @@ llvm::Constant *MicrosoftCXXABI::EmitMemberPointerConversion(
if (CK == CK_ReinterpretMemberPointer)
return Src;
MSInheritanceAttr::Spelling SrcInheritance = getInheritanceFromMemptr(SrcTy);
MSInheritanceAttr::Spelling DstInheritance = getInheritanceFromMemptr(DstTy);
const CXXRecordDecl *SrcRD = SrcTy->getMostRecentCXXRecordDecl();
const CXXRecordDecl *DstRD = DstTy->getMostRecentCXXRecordDecl();
MSInheritanceAttr::Spelling SrcInheritance = SrcRD->getMSInheritanceModel();
MSInheritanceAttr::Spelling DstInheritance = DstRD->getMSInheritanceModel();
// Decompose src.
llvm::Constant *FirstField = Src;
llvm::Constant *NonVirtualBaseAdjustment = nullptr;
llvm::Constant *VirtualBaseAdjustmentOffset = nullptr;
llvm::Constant *VBPtrOffset = nullptr;
llvm::Constant *NonVirtualBaseAdjustment = getZeroInt();
llvm::Constant *VirtualBaseAdjustmentOffset = getZeroInt();
llvm::Constant *VBPtrOffset = getZeroInt();
bool IsFunc = SrcTy->isMemberFunctionPointer();
if (!MSInheritanceAttr::hasOnlyOneField(IsFunc, SrcInheritance)) {
// We need to extract values.
@ -3001,27 +3137,95 @@ llvm::Constant *MicrosoftCXXABI::EmitMemberPointerConversion(
VirtualBaseAdjustmentOffset = Src->getAggregateElement(I++);
}
bool IsDerivedToBase = (CK == CK_DerivedToBaseMemberPointer);
const MemberPointerType *DerivedTy = IsDerivedToBase ? SrcTy : DstTy;
const CXXRecordDecl *DerivedClass = DerivedTy->getMostRecentCXXRecordDecl();
// For data pointers, we adjust the field offset directly. For functions, we
// have a separate field.
const MemberPointerType *DerivedTy =
CK == CK_DerivedToBaseMemberPointer ? SrcTy : DstTy;
const CXXRecordDecl *DerivedClass = DerivedTy->getMostRecentCXXRecordDecl();
llvm::Constant *Adj =
CGM.GetNonVirtualBaseClassOffset(DerivedClass, PathBegin, PathEnd);
if (Adj) {
Adj = llvm::ConstantExpr::getTruncOrBitCast(Adj, CGM.IntTy);
llvm::Constant *&NVAdjustField =
llvm::Constant *&NVAdjustField =
IsFunc ? NonVirtualBaseAdjustment : FirstField;
bool IsDerivedToBase = (CK == 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);
// The virtual inheritance model has a quirk: the virtual base table is always
// referenced when dereferencing a member pointer even if the member pointer
// is non-virtual. This is accounted for by adjusting the non-virtual offset
// to point backwards to the top of the MDC from the first VBase. Undo this
// adjustment to normalize the member pointer.
llvm::Constant *SrcVBIndexEqZero = llvm::ConstantExpr::getICmp(
llvm::ICmpInst::ICMP_EQ, VirtualBaseAdjustmentOffset, getZeroInt());
if (SrcInheritance == MSInheritanceAttr::Keyword_virtual_inheritance) {
llvm::Constant *SrcOffsetToFirstVBase = llvm::ConstantInt::get(
CGM.IntTy,
computeOffsetOfBaseWithVBPtr(getContext(), SrcRD).getQuantity());
llvm::Constant *UndoSrcAdjustment = llvm::ConstantExpr::getSelect(
SrcVBIndexEqZero, SrcOffsetToFirstVBase, getZeroInt());
NVAdjustField =
llvm::ConstantExpr::getNSWAdd(NVAdjustField, UndoSrcAdjustment);
}
// FIXME PR15713: Support conversions through virtually derived classes.
// A non-zero vbindex implies that we are dealing with a source member in a
// floating virtual base in addition to some non-virtual offset. If the
// vbindex is zero, we are dealing with a source that exists in a non-virtual,
// fixed, base. The difference between these two cases is that the vbindex +
// nvoffset *always* point to the member regardless of what context they are
// evaluated in so long as the vbindex is adjusted. A member inside a fixed
// base requires explicit nv adjustment.
llvm::Constant *BaseClassOffset = llvm::ConstantInt::get(
CGM.IntTy,
CGM.computeNonVirtualBaseClassOffset(DerivedClass, PathBegin, PathEnd)
.getQuantity());
llvm::Constant *NVDisp;
if (IsDerivedToBase)
NVDisp = llvm::ConstantExpr::getNSWSub(NVAdjustField, BaseClassOffset);
else
NVDisp = llvm::ConstantExpr::getNSWAdd(NVAdjustField, BaseClassOffset);
// An nv-base adjustment must only be made if the vbindex is zero (or does not
// exist).
NVAdjustField =
llvm::ConstantExpr::getSelect(SrcVBIndexEqZero, NVDisp, getZeroInt());
// Update the vbindex to an appropriate value in the destination because
// SrcRD's vbtable might not be a strict prefix of the one in DstRD.
llvm::Constant *DstVBIndexEqZero = SrcVBIndexEqZero;
if (MSInheritanceAttr::hasVBTableOffsetField(DstInheritance) &&
MSInheritanceAttr::hasVBTableOffsetField(SrcInheritance)) {
if (llvm::GlobalVariable *VDispMap =
getAddrOfVirtualDisplacementMap(SrcRD, DstRD)) {
llvm::Constant *Mapping = VDispMap->getInitializer();
llvm::Constant *VBIndex = llvm::ConstantExpr::getUDiv(
VirtualBaseAdjustmentOffset, llvm::ConstantInt::get(CGM.IntTy, 4),
/*IsExact=*/true);
VirtualBaseAdjustmentOffset = Mapping->getAggregateElement(VBIndex);
DstVBIndexEqZero = llvm::ConstantExpr::getICmp(
llvm::ICmpInst::ICMP_EQ, VirtualBaseAdjustmentOffset, getZeroInt());
}
}
// Set the VBPtrOffset to zero if the vbindex is zero. Otherwise, initialize
// it to the offset of the vbptr.
if (MSInheritanceAttr::hasVBPtrOffsetField(DstInheritance)) {
llvm::Constant *DstVBPtrOffset = llvm::ConstantInt::get(
CGM.IntTy,
getContext().getASTRecordLayout(DstRD).getVBPtrOffset().getQuantity());
VBPtrOffset = llvm::ConstantExpr::getSelect(DstVBIndexEqZero, getZeroInt(),
DstVBPtrOffset);
}
// Likewise, apply a similar adjustment so that dereferencing the member
// pointer correctly accounts for the distance between the start of the first
// virtual base and the top of the MDC.
if (DstInheritance == MSInheritanceAttr::Keyword_virtual_inheritance) {
llvm::Constant *DstOffsetToFirstVBase = llvm::ConstantInt::get(
CGM.IntTy,
computeOffsetOfBaseWithVBPtr(getContext(), DstRD).getQuantity());
llvm::Constant *DoDstAdjustment = llvm::ConstantExpr::getSelect(
DstVBIndexEqZero, DstOffsetToFirstVBase, getZeroInt());
NVAdjustField =
llvm::ConstantExpr::getNSWSub(NVAdjustField, DoDstAdjustment);
}
// Recompose dst from the null struct and the adjusted fields from src.
if (MSInheritanceAttr::hasOnlyOneField(IsFunc, DstInheritance))
@ -3030,11 +3234,11 @@ llvm::Constant *MicrosoftCXXABI::EmitMemberPointerConversion(
llvm::SmallVector<llvm::Constant *, 4> Fields;
Fields.push_back(FirstField);
if (MSInheritanceAttr::hasNVOffsetField(IsFunc, DstInheritance))
Fields.push_back(getConstantOrZeroInt(NonVirtualBaseAdjustment));
Fields.push_back(NonVirtualBaseAdjustment);
if (MSInheritanceAttr::hasVBPtrOffsetField(DstInheritance))
Fields.push_back(getConstantOrZeroInt(VBPtrOffset));
Fields.push_back(VBPtrOffset);
if (MSInheritanceAttr::hasVBTableOffsetField(DstInheritance))
Fields.push_back(getConstantOrZeroInt(VirtualBaseAdjustmentOffset));
Fields.push_back(VirtualBaseAdjustmentOffset);
return llvm::ConstantStruct::getAnon(Fields);
}

View File

@ -541,9 +541,13 @@ void (D::*convertCToD(void (C::*mp)()))() {
//
// 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: %[[nvoff:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 1
// CHECK: %[[vbidx:.*]] = extractvalue { i8*, i32, i32 } %{{.*}}, 2
// CHECK: %[[is_nvbase:.*]] = icmp eq i32 %[[vbidx]], 0
// CHECK: %[[nv_disp:.*]] = add nsw i32 %[[nvoff]], 4
// CHECK: %[[nv_adj:.*]] = select i1 %[[is_nvbase]], i32 %[[nv_disp]], i32 0
// CHECK: %[[dst_adj:.*]] = select i1 %[[is_nvbase]], i32 4, i32 0
// CHECK: %[[adj:.*]] = sub nsw i32 %[[nv_adj]], %[[dst_adj]]
// CHECK: insertvalue { i8*, i32, i32 } undef, i8* {{.*}}, 0
// CHECK: insertvalue { i8*, i32, i32 } {{.*}}, i32 %[[adj]], 1
// CHECK: insertvalue { i8*, i32, i32 } {{.*}}, i32 {{.*}}, 2
@ -712,3 +716,16 @@ int foo(A *a, int A::*mp) {
}
#endif
#endif
namespace pr23878 {
struct A { virtual void g(); };
struct B { virtual void f(); };
struct C : virtual B { void f(); };
struct D : A, C {};
typedef void (D::*DMemPtrTy)();
// CHECK-LABEL: define void @"\01?get_memptr@pr23878@@YAP8D@1@AEXXZXZ"
// CHECK: @"\01??_9C@pr23878@@$BA@AE" to i8*), i32 0, i32 4
DMemPtrTy get_memptr() { return &D::f; }
}