summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Majnemer <david.majnemer@gmail.com>2015-06-23 07:31:11 +0000
committerDavid Majnemer <david.majnemer@gmail.com>2015-06-23 07:31:11 +0000
commitc1709d387e81fd7f18095363f5f6a4af883da3a7 (patch)
tree25103ec9822425c61d836ff955940585114ae995
parent5ca193c333b8ef848eafcfe262d6d3dd5b3c16af (diff)
downloadbcm5719-llvm-c1709d387e81fd7f18095363f5f6a4af883da3a7.tar.gz
bcm5719-llvm-c1709d387e81fd7f18095363f5f6a4af883da3a7.zip
[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
-rw-r--r--clang/include/clang/AST/Mangle.h4
-rw-r--r--clang/lib/AST/MicrosoftMangle.cpp12
-rw-r--r--clang/lib/CodeGen/CGClass.cpp17
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h5
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp314
-rw-r--r--clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp23
6 files changed, 307 insertions, 68 deletions
diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h
index c5a7ea16a7e..735ae11a233 100644
--- a/clang/include/clang/AST/Mangle.h
+++ b/clang/include/clang/AST/Mangle.h
@@ -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;
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 29a95a5103c..26894bb91eb 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -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,
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 4d6a4e288dd..6538351edf2 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -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.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 8e671fa7878..273fe70d52f 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -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 *
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 3c0aecaa161..fabdef3d158 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -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);
+ }
+ }
+
+ // 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);
}
- // FIXME PR15713: Support conversions through virtually derived classes.
+ // 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);
+ }
+
+ // 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);
}
- // FIXME PR15713: Support conversions through virtually derived classes.
+ // 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);
}
diff --git a/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp b/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
index 8cb4a1f8bd2..a509d57194f 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-member-pointers.cpp
@@ -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; }
+}
OpenPOWER on IntegriCloud