forked from OSchip/llvm-project
Fix PR17738 - add support for vtordisp thunks when using -cxx-abi microsoft
llvm-svn: 194132
This commit is contained in:
parent
67b277c34f
commit
053142a90d
|
@ -106,18 +106,53 @@ struct ThisAdjustment {
|
|||
/// nearest virtual base.
|
||||
int64_t NonVirtual;
|
||||
|
||||
/// \brief The offset (in bytes), relative to the address point,
|
||||
/// of the virtual call offset.
|
||||
int64_t VCallOffsetOffset;
|
||||
|
||||
ThisAdjustment() : NonVirtual(0), VCallOffsetOffset(0) { }
|
||||
/// \brief Holds the ABI-specific information about the virtual this
|
||||
/// adjustment, if needed.
|
||||
union VirtualAdjustment {
|
||||
// Itanium ABI
|
||||
struct {
|
||||
/// \brief The offset (in bytes), relative to the address point,
|
||||
/// of the virtual call offset.
|
||||
int64_t VCallOffsetOffset;
|
||||
} Itanium;
|
||||
|
||||
bool isEmpty() const { return !NonVirtual && !VCallOffsetOffset; }
|
||||
struct {
|
||||
/// \brief The offset of the vtordisp (in bytes), relative to the ECX.
|
||||
int32_t VtordispOffset;
|
||||
|
||||
/// \brief The offset of the vbptr of the derived class (in bytes),
|
||||
/// relative to the ECX after vtordisp adjustment.
|
||||
int32_t VBPtrOffset;
|
||||
|
||||
/// \brief The offset (in bytes) of the vbase offset in the vbtable.
|
||||
int32_t VBOffsetOffset;
|
||||
} Microsoft;
|
||||
|
||||
VirtualAdjustment() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
bool Equals(const VirtualAdjustment &Other) const {
|
||||
return memcmp(this, &Other, sizeof(Other)) == 0;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
VirtualAdjustment Zero;
|
||||
return Equals(Zero);
|
||||
}
|
||||
|
||||
bool Less(const VirtualAdjustment &RHS) const {
|
||||
return memcmp(this, &RHS, sizeof(RHS)) < 0;
|
||||
}
|
||||
} Virtual;
|
||||
|
||||
ThisAdjustment() : NonVirtual(0) { }
|
||||
|
||||
bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); }
|
||||
|
||||
friend bool operator==(const ThisAdjustment &LHS,
|
||||
const ThisAdjustment &RHS) {
|
||||
return LHS.NonVirtual == RHS.NonVirtual &&
|
||||
LHS.VCallOffsetOffset == RHS.VCallOffsetOffset;
|
||||
return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual);
|
||||
}
|
||||
|
||||
friend bool operator!=(const ThisAdjustment &LHS, const ThisAdjustment &RHS) {
|
||||
|
@ -129,8 +164,7 @@ struct ThisAdjustment {
|
|||
if (LHS.NonVirtual < RHS.NonVirtual)
|
||||
return true;
|
||||
|
||||
return LHS.NonVirtual == RHS.NonVirtual &&
|
||||
LHS.VCallOffsetOffset < RHS.VCallOffsetOffset;
|
||||
return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3657,8 +3657,9 @@ void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
|
|||
Mangler.getStream() << 'c';
|
||||
|
||||
// Mangle the 'this' pointer adjustment.
|
||||
Mangler.mangleCallOffset(Thunk.This.NonVirtual, Thunk.This.VCallOffsetOffset);
|
||||
|
||||
Mangler.mangleCallOffset(Thunk.This.NonVirtual,
|
||||
Thunk.This.Virtual.Itanium.VCallOffsetOffset);
|
||||
|
||||
// Mangle the return pointer adjustment if there is one.
|
||||
if (!Thunk.Return.isEmpty())
|
||||
Mangler.mangleCallOffset(Thunk.Return.NonVirtual,
|
||||
|
@ -3677,7 +3678,7 @@ void ItaniumMangleContextImpl::mangleCXXDtorThunk(
|
|||
|
||||
// Mangle the 'this' pointer adjustment.
|
||||
Mangler.mangleCallOffset(ThisAdjustment.NonVirtual,
|
||||
ThisAdjustment.VCallOffsetOffset);
|
||||
ThisAdjustment.Virtual.Itanium.VCallOffsetOffset);
|
||||
|
||||
Mangler.mangleFunctionEncoding(DD);
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ public:
|
|||
void mangleDeclaration(const NamedDecl *ND);
|
||||
void mangleFunctionEncoding(const FunctionDecl *FD);
|
||||
void mangleVariableEncoding(const VarDecl *VD);
|
||||
void mangleNumber(int64_t Number);
|
||||
void mangleNumber(uint32_t Number);
|
||||
void mangleNumber(const llvm::APSInt &Value);
|
||||
void mangleType(QualType T, SourceRange Range,
|
||||
QualifierMangleMode QMM = QMM_Mangle);
|
||||
|
@ -387,8 +387,8 @@ void MicrosoftCXXNameMangler::mangleName(const NamedDecl *ND) {
|
|||
Out << '@';
|
||||
}
|
||||
|
||||
void MicrosoftCXXNameMangler::mangleNumber(int64_t Number) {
|
||||
llvm::APSInt APSNumber(/*BitWidth=*/64, /*isUnsigned=*/false);
|
||||
void MicrosoftCXXNameMangler::mangleNumber(uint32_t Number) {
|
||||
llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true);
|
||||
APSNumber = Number;
|
||||
mangleNumber(APSNumber);
|
||||
}
|
||||
|
@ -836,7 +836,7 @@ void MicrosoftCXXNameMangler::mangleLocalName(const FunctionDecl *FD) {
|
|||
// functions. You could have a method baz of class C inside a function bar
|
||||
// inside a function foo, like so:
|
||||
// ?baz@C@?3??bar@?1??foo@@YAXXZ@YAXXZ@QAEXXZ
|
||||
int NestLevel = getLocalNestingLevel(FD);
|
||||
unsigned NestLevel = getLocalNestingLevel(FD);
|
||||
Out << '?';
|
||||
mangleNumber(NestLevel);
|
||||
Out << '?';
|
||||
|
@ -1367,24 +1367,18 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
|
|||
// ::= D # private: static far
|
||||
// ::= E # private: virtual near
|
||||
// ::= F # private: virtual far
|
||||
// ::= G # private: thunk near
|
||||
// ::= H # private: thunk far
|
||||
// ::= I # protected: near
|
||||
// ::= J # protected: far
|
||||
// ::= K # protected: static near
|
||||
// ::= L # protected: static far
|
||||
// ::= M # protected: virtual near
|
||||
// ::= N # protected: virtual far
|
||||
// ::= O # protected: thunk near
|
||||
// ::= P # protected: thunk far
|
||||
// ::= Q # public: near
|
||||
// ::= R # public: far
|
||||
// ::= S # public: static near
|
||||
// ::= T # public: static far
|
||||
// ::= U # public: virtual near
|
||||
// ::= V # public: virtual far
|
||||
// ::= W # public: thunk near
|
||||
// ::= X # public: thunk far
|
||||
// <global-function> ::= Y # global near
|
||||
// ::= Z # global far
|
||||
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
|
||||
|
@ -1843,12 +1837,61 @@ void MicrosoftMangleContextImpl::mangleCXXName(const NamedDecl *D,
|
|||
return Mangler.mangle(D);
|
||||
}
|
||||
|
||||
// <this-adjustment> ::= <no-adjustment> | <static-adjustment> |
|
||||
// <virtual-adjustment>
|
||||
// <no-adjustment> ::= A # private near
|
||||
// ::= B # private far
|
||||
// ::= I # protected near
|
||||
// ::= J # protected far
|
||||
// ::= Q # public near
|
||||
// ::= R # public far
|
||||
// <static-adjustment> ::= G <static-offset> # private near
|
||||
// ::= H <static-offset> # private far
|
||||
// ::= O <static-offset> # protected near
|
||||
// ::= P <static-offset> # protected far
|
||||
// ::= W <static-offset> # public near
|
||||
// ::= X <static-offset> # public far
|
||||
// <virtual-adjustment> ::= $0 <virtual-shift> <static-offset> # private near
|
||||
// ::= $1 <virtual-shift> <static-offset> # private far
|
||||
// ::= $2 <virtual-shift> <static-offset> # protected near
|
||||
// ::= $3 <virtual-shift> <static-offset> # protected far
|
||||
// ::= $4 <virtual-shift> <static-offset> # public near
|
||||
// ::= $5 <virtual-shift> <static-offset> # public far
|
||||
// <virtual-shift> ::= <vtordisp-shift> | <vtordispex-shift>
|
||||
// <vtordisp-shift> ::= <offset-to-vtordisp>
|
||||
// <vtordispex-shift> ::= <offset-to-vbptr> <vbase-offset-offset>
|
||||
// <offset-to-vtordisp>
|
||||
static void mangleThunkThisAdjustment(const CXXMethodDecl *MD,
|
||||
const ThisAdjustment &Adjustment,
|
||||
MicrosoftCXXNameMangler &Mangler,
|
||||
raw_ostream &Out) {
|
||||
// FIXME: add support for vtordisp thunks.
|
||||
if (Adjustment.NonVirtual != 0) {
|
||||
if (!Adjustment.Virtual.isEmpty()) {
|
||||
Out << '$';
|
||||
char AccessSpec;
|
||||
switch (MD->getAccess()) {
|
||||
case AS_none:
|
||||
llvm_unreachable("Unsupported access specifier");
|
||||
case AS_private:
|
||||
AccessSpec = '0';
|
||||
break;
|
||||
case AS_protected:
|
||||
AccessSpec = '2';
|
||||
break;
|
||||
case AS_public:
|
||||
AccessSpec = '4';
|
||||
}
|
||||
if (Adjustment.Virtual.Microsoft.VBPtrOffset) {
|
||||
Out << 'R' << AccessSpec;
|
||||
Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VBPtrOffset);
|
||||
Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VBOffsetOffset);
|
||||
Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VtordispOffset);
|
||||
Mangler.mangleNumber(Adjustment.NonVirtual);
|
||||
} else {
|
||||
Out << AccessSpec;
|
||||
Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VtordispOffset);
|
||||
Mangler.mangleNumber(-Adjustment.NonVirtual);
|
||||
}
|
||||
} else if (Adjustment.NonVirtual != 0) {
|
||||
switch (MD->getAccess()) {
|
||||
case AS_none:
|
||||
llvm_unreachable("Unsupported access specifier");
|
||||
|
@ -1861,9 +1904,7 @@ static void mangleThunkThisAdjustment(const CXXMethodDecl *MD,
|
|||
case AS_public:
|
||||
Out << 'W';
|
||||
}
|
||||
llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true);
|
||||
APSNumber = -Adjustment.NonVirtual;
|
||||
Mangler.mangleNumber(APSNumber);
|
||||
Mangler.mangleNumber(-Adjustment.NonVirtual);
|
||||
} else {
|
||||
switch (MD->getAccess()) {
|
||||
case AS_none:
|
||||
|
|
|
@ -1304,7 +1304,7 @@ ThisAdjustment ItaniumVTableBuilder::ComputeThisAdjustment(
|
|||
VCallOffsets = Builder.getVCallOffsets();
|
||||
}
|
||||
|
||||
Adjustment.VCallOffsetOffset =
|
||||
Adjustment.Virtual.Itanium.VCallOffsetOffset =
|
||||
VCallOffsets.getVCallOffsetOffset(MD).getQuantity();
|
||||
}
|
||||
|
||||
|
@ -1552,7 +1552,7 @@ void ItaniumVTableBuilder::AddMethods(
|
|||
ComputeThisAdjustment(OverriddenMD, BaseOffsetInLayoutClass,
|
||||
Overrider);
|
||||
|
||||
if (ThisAdjustment.VCallOffsetOffset &&
|
||||
if (ThisAdjustment.Virtual.Itanium.VCallOffsetOffset &&
|
||||
Overrider.Method->getParent() == MostDerivedClass) {
|
||||
|
||||
// There's no return adjustment from OverriddenMD and MD,
|
||||
|
@ -2009,8 +2009,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
|
|||
Out << "\n [this adjustment: ";
|
||||
Out << Thunk.This.NonVirtual << " non-virtual";
|
||||
|
||||
if (Thunk.This.VCallOffsetOffset) {
|
||||
Out << ", " << Thunk.This.VCallOffsetOffset;
|
||||
if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) {
|
||||
Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset;
|
||||
Out << " vcall offset offset";
|
||||
}
|
||||
|
||||
|
@ -2044,8 +2044,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
|
|||
Out << "\n [this adjustment: ";
|
||||
Out << Thunk.This.NonVirtual << " non-virtual";
|
||||
|
||||
if (Thunk.This.VCallOffsetOffset) {
|
||||
Out << ", " << Thunk.This.VCallOffsetOffset;
|
||||
if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) {
|
||||
Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset;
|
||||
Out << " vcall offset offset";
|
||||
}
|
||||
|
||||
|
@ -2186,8 +2186,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
|
|||
Out << "this adjustment: ";
|
||||
Out << Thunk.This.NonVirtual << " non-virtual";
|
||||
|
||||
if (Thunk.This.VCallOffsetOffset) {
|
||||
Out << ", " << Thunk.This.VCallOffsetOffset;
|
||||
if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) {
|
||||
Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset;
|
||||
Out << " vcall offset offset";
|
||||
}
|
||||
}
|
||||
|
@ -2527,6 +2527,9 @@ private:
|
|||
BaseSubobject Base,
|
||||
FinalOverriders::OverriderInfo Overrider);
|
||||
|
||||
void CalculateVtordispAdjustment(FinalOverriders::OverriderInfo Overrider,
|
||||
CharUnits ThisOffset, ThisAdjustment &TA);
|
||||
|
||||
/// AddMethod - Add a single virtual member function to the vftable
|
||||
/// components vector.
|
||||
void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) {
|
||||
|
@ -2672,7 +2675,7 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD,
|
|||
I != E; ++I) {
|
||||
const CXXBasePath &Path = (*I);
|
||||
CharUnits ThisOffset = Base.getBaseOffset();
|
||||
bool SeenVBase = false;
|
||||
CharUnits LastVBaseOffset;
|
||||
|
||||
// For each path from the overrider to the parents of the overridden methods,
|
||||
// traverse the path, calculating the this offset in the most derived class.
|
||||
|
@ -2684,7 +2687,7 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD,
|
|||
const ASTRecordLayout &Layout = Context.getASTRecordLayout(PrevRD);
|
||||
|
||||
if (Element.Base->isVirtual()) {
|
||||
SeenVBase = true;
|
||||
LastVBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD);
|
||||
if (Overrider.Method->getParent() == PrevRD) {
|
||||
// This one's interesting. If the final overrider is in a vbase B of the
|
||||
// most derived class and it overrides a method of the B's own vbase A,
|
||||
|
@ -2695,23 +2698,25 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD,
|
|||
// differently in the most derived class.
|
||||
ThisOffset += Layout.getVBaseClassOffset(CurRD);
|
||||
} else {
|
||||
ThisOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD);
|
||||
ThisOffset = LastVBaseOffset;
|
||||
}
|
||||
|
||||
// A virtual destructor of a virtual base takes the address of the
|
||||
// virtual base subobject as the "this" argument.
|
||||
if (isa<CXXDestructorDecl>(MD))
|
||||
break;
|
||||
} else {
|
||||
ThisOffset += Layout.getBaseClassOffset(CurRD);
|
||||
}
|
||||
}
|
||||
|
||||
// If a "Base" class has at least one non-virtual base with a virtual
|
||||
// destructor, the "Base" virtual destructor will take the address of the
|
||||
// "Base" subobject as the "this" argument.
|
||||
if (!SeenVBase && isa<CXXDestructorDecl>(MD))
|
||||
return Base.getBaseOffset();
|
||||
if (isa<CXXDestructorDecl>(MD)) {
|
||||
if (LastVBaseOffset.isZero()) {
|
||||
// If a "Base" class has at least one non-virtual base with a virtual
|
||||
// destructor, the "Base" virtual destructor will take the address
|
||||
// of the "Base" subobject as the "this" argument.
|
||||
return Base.getBaseOffset();
|
||||
} else {
|
||||
// A virtual destructor of a virtual base takes the address of the
|
||||
// virtual base subobject as the "this" argument.
|
||||
return LastVBaseOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (Ret > ThisOffset || First) {
|
||||
First = false;
|
||||
|
@ -2723,6 +2728,49 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD,
|
|||
return Ret;
|
||||
}
|
||||
|
||||
void VFTableBuilder::CalculateVtordispAdjustment(
|
||||
FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset,
|
||||
ThisAdjustment &TA) {
|
||||
const ASTRecordLayout::VBaseOffsetsMapTy &VBaseMap =
|
||||
MostDerivedClassLayout.getVBaseOffsetsMap();
|
||||
const ASTRecordLayout::VBaseOffsetsMapTy::const_iterator &VBaseMapEntry =
|
||||
VBaseMap.find(WhichVFPtr.LastVBase);
|
||||
assert(VBaseMapEntry != VBaseMap.end());
|
||||
|
||||
// Check if we need a vtordisp adjustment at all.
|
||||
if (!VBaseMapEntry->second.hasVtorDisp())
|
||||
return;
|
||||
|
||||
CharUnits VFPtrVBaseOffset = VBaseMapEntry->second.VBaseOffset;
|
||||
// The implicit vtordisp field is located right before the vbase.
|
||||
TA.Virtual.Microsoft.VtordispOffset =
|
||||
(VFPtrVBaseOffset - WhichVFPtr.VFPtrFullOffset).getQuantity() - 4;
|
||||
|
||||
// If the final overrider is defined in either:
|
||||
// - the most derived class or its non-virtual base or
|
||||
// - the same vbase as the initial declaration,
|
||||
// a simple vtordisp thunk will suffice.
|
||||
const CXXRecordDecl *OverriderRD = Overrider.Method->getParent();
|
||||
if (OverriderRD == MostDerivedClass)
|
||||
return;
|
||||
|
||||
const CXXRecordDecl *OverriderVBase =
|
||||
ComputeBaseOffset(Context, OverriderRD, MostDerivedClass).VirtualBase;
|
||||
if (!OverriderVBase || OverriderVBase == WhichVFPtr.LastVBase)
|
||||
return;
|
||||
|
||||
// Otherwise, we need to do use the dynamic offset of the final overrider
|
||||
// in order to get "this" adjustment right.
|
||||
TA.Virtual.Microsoft.VBPtrOffset =
|
||||
(VFPtrVBaseOffset + WhichVFPtr.VFPtrOffset -
|
||||
MostDerivedClassLayout.getVBPtrOffset()).getQuantity();
|
||||
TA.Virtual.Microsoft.VBOffsetOffset =
|
||||
Context.getTypeSizeInChars(Context.IntTy).getQuantity() *
|
||||
VTables.getVBTableIndex(MostDerivedClass, OverriderVBase);
|
||||
|
||||
TA.NonVirtual = (ThisOffset - Overrider.Offset).getQuantity();
|
||||
}
|
||||
|
||||
static void GroupNewVirtualOverloads(
|
||||
const CXXRecordDecl *RD,
|
||||
SmallVector<const CXXMethodDecl *, 10> &VirtualMethods) {
|
||||
|
@ -2829,6 +2877,12 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
if (TI != WhichVFPtr.VFPtrFullOffset) {
|
||||
ThisAdjustmentOffset.NonVirtual =
|
||||
(TI - WhichVFPtr.VFPtrFullOffset).getQuantity();
|
||||
}
|
||||
|
||||
if (WhichVFPtr.LastVBase)
|
||||
CalculateVtordispAdjustment(Overrider, TI, ThisAdjustmentOffset);
|
||||
|
||||
if (!ThisAdjustmentOffset.isEmpty()) {
|
||||
VTableThunks[OverriddenMethodInfo.VFTableIndex].This =
|
||||
ThisAdjustmentOffset;
|
||||
AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
|
||||
|
@ -2962,8 +3016,17 @@ static void dumpMicrosoftThunkAdjustment(const ThunkInfo &TI, raw_ostream &Out,
|
|||
if (Multiline || !ContinueFirstLine)
|
||||
Out << LinePrefix;
|
||||
Out << "[this adjustment: ";
|
||||
assert(TI.This.VCallOffsetOffset == 0 &&
|
||||
"VtorDisp adjustment is not supported yet");
|
||||
if (!TI.This.Virtual.isEmpty()) {
|
||||
assert(T.Virtual.Microsoft.VtordispOffset < 0);
|
||||
Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", ";
|
||||
if (T.Virtual.Microsoft.VBPtrOffset) {
|
||||
Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset
|
||||
<< " to the left, ";
|
||||
assert(T.Virtual.Microsoft.VBOffsetOffset > 0);
|
||||
Out << LinePrefix << " vboffset at "
|
||||
<< T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, ";
|
||||
}
|
||||
}
|
||||
Out << T.NonVirtual << " non-virtual]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1118,7 +1118,8 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
|
|||
llvm::Value *ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF,
|
||||
llvm::Value *This,
|
||||
const ThisAdjustment &TA) {
|
||||
return performTypeAdjustment(CGF, This, TA.NonVirtual, TA.VCallOffsetOffset,
|
||||
return performTypeAdjustment(CGF, This, TA.NonVirtual,
|
||||
TA.Virtual.Itanium.VCallOffsetOffset,
|
||||
/*IsReturnAdjustment=*/false);
|
||||
}
|
||||
|
||||
|
|
|
@ -988,8 +988,29 @@ llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
|
|||
|
||||
llvm::Value *V = CGF.Builder.CreateBitCast(This, CGF.Int8PtrTy);
|
||||
|
||||
assert(TA.VCallOffsetOffset == 0 &&
|
||||
"VtorDisp adjustment is not supported yet");
|
||||
if (!TA.Virtual.isEmpty()) {
|
||||
assert(TA.Virtual.Microsoft.VtordispOffset < 0);
|
||||
// Adjust the this argument based on the vtordisp value.
|
||||
llvm::Value *VtorDispPtr =
|
||||
CGF.Builder.CreateConstGEP1_32(V, TA.Virtual.Microsoft.VtordispOffset);
|
||||
VtorDispPtr =
|
||||
CGF.Builder.CreateBitCast(VtorDispPtr, CGF.Int32Ty->getPointerTo());
|
||||
llvm::Value *VtorDisp = CGF.Builder.CreateLoad(VtorDispPtr, "vtordisp");
|
||||
V = CGF.Builder.CreateGEP(V, CGF.Builder.CreateNeg(VtorDisp));
|
||||
|
||||
if (TA.Virtual.Microsoft.VBPtrOffset) {
|
||||
// If the final overrider is defined in a virtual base other than the one
|
||||
// that holds the vfptr, we have to use a vtordispex thunk which looks up
|
||||
// the vbtable of the derived class.
|
||||
assert(TA.Virtual.Microsoft.VBPtrOffset > 0);
|
||||
assert(TA.Virtual.Microsoft.VBOffsetOffset >= 0);
|
||||
llvm::Value *VBPtr;
|
||||
llvm::Value *VBaseOffset =
|
||||
GetVBaseOffsetFromVBPtr(CGF, V, -TA.Virtual.Microsoft.VBPtrOffset,
|
||||
TA.Virtual.Microsoft.VBOffsetOffset, &VBPtr);
|
||||
V = CGF.Builder.CreateInBoundsGEP(VBPtr, VBaseOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (TA.NonVirtual) {
|
||||
// Non-virtual adjustment might result in a pointer outside the allocated
|
||||
|
|
|
@ -138,8 +138,6 @@ I::I() {} // Emits vftable and forces thunk generation.
|
|||
// CODEGEN: phi %struct.F* {{.*}} %[[RES]]
|
||||
// CODEGEN: ret %struct.{{[BF]}}*
|
||||
|
||||
// FIXME: Write vtordisp adjusting thunk tests
|
||||
|
||||
namespace CrashOnThunksForAttributedType {
|
||||
// We used to crash on this because the type of foo is an AttributedType, not
|
||||
// FunctionType, and we had to look through the sugar.
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -o - | FileCheck %s
|
||||
|
||||
// For now, just make sure x86_64 doesn't crash.
|
||||
// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=x86_64-pc-win32 -emit-llvm -o %t
|
||||
|
||||
struct A {
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
struct B {
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
struct C : A, B {};
|
||||
|
||||
struct D : virtual C {
|
||||
D();
|
||||
~D();
|
||||
virtual void f();
|
||||
void g();
|
||||
int xxx;
|
||||
};
|
||||
|
||||
D::D() {} // Forces vftable emission.
|
||||
|
||||
// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f@D@@$4PPPPPPPM@A@AEXXZ"
|
||||
// CHECK: %[[ECX:.*]] = load %struct.D** %{{.*}}
|
||||
// CHECK: %[[ECX_i8:.*]] = bitcast %struct.D* %[[ECX]] to i8*
|
||||
// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -4
|
||||
// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32*
|
||||
// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]]
|
||||
// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]]
|
||||
// CHECK: %[[ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]]
|
||||
// CHECK: call x86_thiscallcc void @"\01?f@D@@UAEXXZ"(i8* %[[ADJUSTED_i8]])
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f@D@@$4PPPPPPPI@3AEXXZ"
|
||||
// CHECK: %[[ECX:.*]] = load %struct.D** %{{.*}}
|
||||
// CHECK: %[[ECX_i8:.*]] = bitcast %struct.D* %[[ECX]] to i8*
|
||||
// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -8
|
||||
// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32*
|
||||
// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]]
|
||||
// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]]
|
||||
// CHECK: %[[VTORDISP_ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]]
|
||||
// CHECK: %[[ADJUSTED_i8:.*]] = getelementptr i8* %[[VTORDISP_ADJUSTED_i8]], i32 -4
|
||||
// CHECK: call x86_thiscallcc void @"\01?f@D@@UAEXXZ"(i8* %[[ADJUSTED_i8]])
|
||||
// CHECK: ret void
|
||||
|
||||
struct E : virtual A {
|
||||
virtual void f();
|
||||
~E();
|
||||
};
|
||||
|
||||
struct F {
|
||||
virtual void z();
|
||||
};
|
||||
|
||||
struct G : virtual F, virtual E {
|
||||
int ggg;
|
||||
G();
|
||||
~G();
|
||||
};
|
||||
|
||||
G::G() {} // Forces vftable emission.
|
||||
|
||||
// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f@E@@$R4BA@M@PPPPPPPM@7AEXXZ"(i8*)
|
||||
// CHECK: %[[ECX:.*]] = load %struct.E** %{{.*}}
|
||||
// CHECK: %[[ECX_i8:.*]] = bitcast %struct.E* %[[ECX]] to i8*
|
||||
// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -4
|
||||
// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32*
|
||||
// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]]
|
||||
// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]]
|
||||
// CHECK: %[[VTORDISP_ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]]
|
||||
// CHECK: %[[VBPTR_i8:.*]] = getelementptr inbounds i8* %[[VTORDISP_ADJUSTED_i8]], i32 -16
|
||||
// CHECK: %[[VBPTR:.*]] = bitcast i8* %[[VBPTR_i8]] to i8**
|
||||
// CHECK: %[[VBTABLE:.*]] = load i8** %[[VBPTR]]
|
||||
// CHECK: %[[VBOFFSET_PTR_i8:.*]] = getelementptr inbounds i8* %[[VBTABLE]], i32 12
|
||||
// CHECK: %[[VBOFFSET_PTR:.*]] = bitcast i8* %[[VBOFFSET_PTR_i8]] to i32*
|
||||
// CHECK: %[[VBASE_OFFSET:.*]] = load i32* %[[VBOFFSET_PTR]]
|
||||
// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8* %[[VBPTR_i8]], i32 %[[VBASE_OFFSET]]
|
||||
// CHECK: %[[ARG_i8:.*]] = getelementptr i8* %[[VBASE]], i32 8
|
||||
// CHECK: call x86_thiscallcc void @"\01?f@E@@UAEXXZ"(i8* %[[ARG_i8]])
|
||||
// CHECK: ret void
|
|
@ -0,0 +1,324 @@
|
|||
// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1
|
||||
// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-A %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-B %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-C %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-A %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-B %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-C %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-E %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-F %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-G %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-H %s < %t
|
||||
// RUN: FileCheck --check-prefix=VTABLE-PR17738-A %s < %t
|
||||
// RUN: FileCheck --check-prefix=MANGLING %s < %t
|
||||
|
||||
// For now, just make sure x86_64 doesn't crash.
|
||||
// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 >%t 2>&1
|
||||
|
||||
struct V1 {
|
||||
virtual void f();
|
||||
virtual ~V1();
|
||||
};
|
||||
|
||||
struct V2 {
|
||||
virtual void f();
|
||||
virtual ~V2();
|
||||
int v;
|
||||
};
|
||||
|
||||
struct Z {
|
||||
virtual void g();
|
||||
virtual ~Z();
|
||||
int x;
|
||||
};
|
||||
|
||||
struct V3 : Z, V2 {
|
||||
};
|
||||
|
||||
struct V4 : Z, V1, V2 {
|
||||
int y;
|
||||
};
|
||||
|
||||
void use_somewhere_else(void*);
|
||||
|
||||
namespace simple {
|
||||
// In case of a single-layer virtual inheritance, the "this" adjustment is done
|
||||
// staically:
|
||||
// struct A {
|
||||
// virtual void f(); // Expects "(A*)this" in ECX
|
||||
// };
|
||||
// struct B : virtual A {
|
||||
// virtual void f(); // Expects "(char*)(B*)this + 12" in ECX
|
||||
// virtual ~B(); // Might call f()
|
||||
// };
|
||||
//
|
||||
// If a class overrides a virtual function of its base and has a non-trivial
|
||||
// ctor/dtor that call(s) the virtual function (or may escape "this" to some
|
||||
// code that might call it), a virtual adjustment might be needed in case the
|
||||
// current class layout and the most derived class layout are different.
|
||||
// This is done using vtordisp thunks.
|
||||
//
|
||||
// A simple vtordisp{A,B} thunk for Method@Class is something like:
|
||||
// sub ecx, [ecx+A] // apply the vtordisp adjustment
|
||||
// sub ecx, B // apply the subobject adjustment, if needed.
|
||||
// jmp Method@Class
|
||||
|
||||
struct A : virtual V1 {
|
||||
// VTABLE-SIMPLE-A: VFTable for 'V1' in 'simple::A' (2 entries).
|
||||
// VTABLE-SIMPLE-A-NEXT: 0 | void simple::A::f()
|
||||
// VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
// VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting]
|
||||
// VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
virtual void f();
|
||||
// MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
|
||||
|
||||
virtual ~A();
|
||||
// MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
A a;
|
||||
|
||||
struct B : virtual V3 {
|
||||
// VTABLE-SIMPLE-B: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
|
||||
// VTABLE-SIMPLE-B-NEXT: 0 | void Z::g()
|
||||
// VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
|
||||
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
|
||||
// VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f()
|
||||
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
|
||||
// VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
|
||||
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
|
||||
// FIXME: The vtordisp thunk should only get emitted for a constructor
|
||||
// if "this" leaves scope.
|
||||
B() { use_somewhere_else(this); }
|
||||
|
||||
virtual void f();
|
||||
// MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
|
||||
|
||||
// Has an implicit destructor.
|
||||
// MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
|
||||
// MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
B b;
|
||||
|
||||
struct C : virtual V4 {
|
||||
// VTABLE-SIMPLE-C: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
|
||||
// VTABLE-SIMPLE-C-NEXT: 0 | void Z::g()
|
||||
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
|
||||
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
|
||||
// VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
|
||||
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
|
||||
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
|
||||
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
|
||||
// VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
|
||||
// VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
|
||||
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]
|
||||
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
|
||||
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]
|
||||
|
||||
int x;
|
||||
virtual void f();
|
||||
// MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ"
|
||||
// MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
|
||||
virtual ~C();
|
||||
// MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
|
||||
// MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
|
||||
// MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
C c;
|
||||
}
|
||||
|
||||
namespace extended {
|
||||
// If a virtual function requires vtordisp adjustment and the final overrider
|
||||
// is defined in another vitual base of the most derived class,
|
||||
// we need to know two vbase offsets.
|
||||
// In this case, we should use the extended form of vtordisp thunks, called
|
||||
// vtordispex thunks.
|
||||
//
|
||||
// vtordispex{A,B,C,D} thunk for Method@Class is something like:
|
||||
// sub ecx, [ecx+C] // apply the vtordisp adjustment
|
||||
// sub ecx, A // jump to the vbtable of the most derived class
|
||||
// mov eax, [ecx] // load the vbtable address
|
||||
// add ecx, [eax+B] // lookup the final overrider's vbase offset
|
||||
// add ecx, D // apphy the subobject offset if needed
|
||||
// jmp Method@Class
|
||||
|
||||
struct A : virtual simple::A {
|
||||
// VTABLE-EXTENDED-A: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).
|
||||
// VTABLE-EXTENDED-A-NEXT: 0 | void simple::A::f()
|
||||
// VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
||||
// VTABLE-EXTENDED-A-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
||||
// VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting]
|
||||
// VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// `vtordispex{8,8,4294967292,8}'
|
||||
// MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
|
||||
|
||||
virtual ~A();
|
||||
// vtordisp{4294967292,0}
|
||||
// MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
A a;
|
||||
|
||||
struct B : virtual simple::A {
|
||||
// This class has an implicit dtor. Vdtors don't require vtordispex thunks
|
||||
// as the most derived class always has an implicit dtor,
|
||||
// which is a final overrider.
|
||||
|
||||
// VTABLE-EXTENDED-B: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
|
||||
// ...
|
||||
// VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting]
|
||||
// VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// vtordisp{4294967292,0}
|
||||
// MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
B b;
|
||||
|
||||
struct C : virtual simple::A {
|
||||
// VTABLE-EXTENDED-C: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).
|
||||
// VTABLE-EXTENDED-C-NEXT: 0 | void simple::A::f()
|
||||
// VTABLE-EXTENDED-C-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left,
|
||||
// VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
||||
|
||||
// `vtordispex{12,8,4294967292,8}'
|
||||
// MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
|
||||
int x;
|
||||
virtual ~C();
|
||||
// MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
C c;
|
||||
|
||||
struct D : virtual V2 {
|
||||
virtual void f();
|
||||
virtual ~D();
|
||||
int x;
|
||||
};
|
||||
|
||||
struct E : virtual D {
|
||||
// VTABLE-EXTENDED-E: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).
|
||||
// VTABLE-EXTENDED-E-NEXT: 0 | void extended::D::f()
|
||||
// VTABLE-EXTENDED-E-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
||||
// VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]
|
||||
|
||||
// `vtordispex{8,8,4294967292,12}'
|
||||
// MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
|
||||
|
||||
virtual ~E();
|
||||
// MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
E e;
|
||||
|
||||
struct F : virtual Z, virtual D {
|
||||
// VTABLE-EXTENDED-F: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).
|
||||
// VTABLE-EXTENDED-F-NEXT: 0 | void extended::D::f()
|
||||
// VTABLE-EXTENDED-F-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left,
|
||||
// VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]
|
||||
|
||||
// `vtordispex{20,12,4294967292,12}'
|
||||
// MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
|
||||
int x;
|
||||
virtual ~F();
|
||||
// MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
|
||||
};
|
||||
|
||||
F f;
|
||||
|
||||
struct G : virtual simple::A {
|
||||
// VTABLE-EXTENDED-G: VFTable for 'extended::G' (1 entries).
|
||||
// VTABLE-EXTENDED-G-NEXT: 0 | void extended::G::g()
|
||||
|
||||
// VTABLE-EXTENDED-G: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).
|
||||
// VTABLE-EXTENDED-G-NEXT: 0 | void simple::A::f()
|
||||
// VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
||||
// VTABLE-EXTENDED-G-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
||||
// VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting]
|
||||
// VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// Emits a G's own vfptr, thus moving the vbptr in the layout.
|
||||
virtual void g();
|
||||
|
||||
virtual ~G();
|
||||
// vtordisp{4294967292,0}
|
||||
// MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
||||
};
|
||||
|
||||
G g;
|
||||
|
||||
struct H : Z, A {
|
||||
// VTABLE-EXTENDED-H: VFTable for 'Z' in 'extended::H' (2 entries).
|
||||
// VTABLE-EXTENDED-H-NEXT: 0 | void Z::g()
|
||||
// VTABLE-EXTENDED-H-NEXT: 1 | extended::H::~H() [scalar deleting]
|
||||
|
||||
// VTABLE-EXTENDED-H: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
|
||||
// VTABLE-EXTENDED-H-NEXT: 0 | void simple::A::f()
|
||||
// VTABLE-EXTENDED-H-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
||||
// VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
||||
|
||||
// MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
|
||||
// MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
|
||||
};
|
||||
|
||||
H h;
|
||||
}
|
||||
|
||||
namespace pr17738 {
|
||||
// These classes should have vtordispex thunks but MSVS CL miscompiles them.
|
||||
// Just do the right thing.
|
||||
|
||||
struct A : virtual simple::B {
|
||||
// VTABLE-PR17738-A: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).
|
||||
// VTABLE-PR17738-A-NEXT: 0 | void simple::B::f()
|
||||
// VTABLE-PR17738-A-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left,
|
||||
// VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]
|
||||
|
||||
// MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
|
||||
int a;
|
||||
virtual ~A();
|
||||
};
|
||||
|
||||
A a;
|
||||
}
|
||||
|
||||
namespace access {
|
||||
struct A {
|
||||
virtual ~A();
|
||||
protected:
|
||||
virtual void prot();
|
||||
private:
|
||||
virtual void priv();
|
||||
};
|
||||
|
||||
struct B : virtual A {
|
||||
virtual ~B();
|
||||
protected:
|
||||
virtual void prot();
|
||||
// MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
|
||||
private:
|
||||
virtual void priv();
|
||||
// MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
|
||||
};
|
||||
|
||||
B b;
|
||||
|
||||
struct C : virtual B {
|
||||
virtual ~C();
|
||||
|
||||
// MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
|
||||
// MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
|
||||
};
|
||||
|
||||
C c;
|
||||
}
|
Loading…
Reference in New Issue