Skip to content

Commit 23e9661

Browse files
ojhuntrjmccall
andcommitted
[clang] Record the original declaration for which a v-table slot was created.
Co-Authored-By: John McCall <[email protected]>
1 parent d1f35a5 commit 23e9661

File tree

4 files changed

+128
-4
lines changed

4 files changed

+128
-4
lines changed

clang/include/clang/AST/GlobalDecl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ class GlobalDecl {
145145
LHS.MultiVersionIndex == RHS.MultiVersionIndex;
146146
}
147147

148+
bool operator!=(const GlobalDecl &Other) const {
149+
return !(*this == Other);
150+
}
151+
148152
void *getAsOpaquePtr() const { return Value.getOpaqueValue(); }
149153

150154
explicit operator bool() const { return getAsOpaquePtr(); }

clang/include/clang/AST/VTableBuilder.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ class VTableContextBase {
361361
};
362362

363363
class ItaniumVTableContext : public VTableContextBase {
364+
public:
365+
typedef llvm::DenseMap<const CXXMethodDecl *, const CXXMethodDecl *>
366+
OriginalMethodMapTy;
367+
364368
private:
365369

366370
/// Contains the index (relative to the vtable address point)
@@ -384,6 +388,10 @@ class ItaniumVTableContext : public VTableContextBase {
384388
VirtualBaseClassOffsetOffsetsMapTy;
385389
VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets;
386390

391+
/// Map from a virtual method to the nearest method in the primary base class
392+
/// chain that it overrides.
393+
OriginalMethodMapTy OriginalMethodMap;
394+
387395
void computeVTableRelatedInformation(const CXXRecordDecl *RD) override;
388396

389397
public:
@@ -425,6 +433,27 @@ class ItaniumVTableContext : public VTableContextBase {
425433
CharUnits getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
426434
const CXXRecordDecl *VBase);
427435

436+
/// Return the method that added the v-table slot that will be used to call
437+
/// the given method.
438+
///
439+
/// In the Itanium ABI, where overrides always cause methods to be added to
440+
/// the primary v-table if they're not already there, this will be the first
441+
/// declaration in the primary base class chain for which the return type
442+
/// adjustment is trivial.
443+
GlobalDecl findOriginalMethod(GlobalDecl GD);
444+
445+
const CXXMethodDecl *findOriginalMethodInMap(const CXXMethodDecl *MD) const;
446+
447+
void setOriginalMethod(const CXXMethodDecl *Key, const CXXMethodDecl *Val) {
448+
OriginalMethodMap[Key] = Val;
449+
}
450+
451+
/// This method is reserved for the implementation and shouldn't be used
452+
/// directly.
453+
const OriginalMethodMapTy &getOriginalMethodMap() {
454+
return OriginalMethodMap;
455+
}
456+
428457
static bool classof(const VTableContextBase *VT) {
429458
return !VT->isMicrosoft();
430459
}

clang/include/clang/Basic/Thunk.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,10 @@ struct ThunkInfo {
162162

163163
/// Holds a pointer to the overridden method this thunk is for,
164164
/// if needed by the ABI to distinguish different thunks with equal
165-
/// adjustments. Otherwise, null.
165+
/// adjustments.
166+
/// In the Itanium ABI, this field can hold the method that created the
167+
/// vtable entry for this thunk.
168+
/// Otherwise, null.
166169
/// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using
167170
/// an ABI-specific comparator.
168171
const CXXMethodDecl *Method;

clang/lib/AST/VTableBuilder.cpp

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,11 +1147,38 @@ void ItaniumVTableBuilder::ComputeThisAdjustments() {
11471147
continue;
11481148

11491149
// Add it.
1150-
VTableThunks[VTableIndex].This = ThisAdjustment;
1150+
auto SetThisAdjustmentThunk = [&](uint64_t Idx) {
1151+
// If a this pointer adjustment is required, record the method that
1152+
// created the vtable entry. MD is not necessarily the method that
1153+
// created the entry since derived classes overwrite base class
1154+
// information in MethodInfoMap, hence findOriginalMethodInMap is called
1155+
// here.
1156+
//
1157+
// For example, in the following class hierarchy, if MD = D1::m and
1158+
// Overrider = D2:m, the original method that created the entry is B0:m,
1159+
// which is what findOriginalMethodInMap(MD) returns:
1160+
//
1161+
// struct B0 { int a; virtual void m(); };
1162+
// struct D0 : B0 { int a; void m() override; };
1163+
// struct D1 : B0 { int a; void m() override; };
1164+
// struct D2 : D0, D1 { int a; void m() override; };
1165+
//
1166+
// We need to record the method because we cannot
1167+
// call findOriginalMethod to find the method that created the entry if
1168+
// the method in the entry requires adjustment.
1169+
//
1170+
// Do not set ThunkInfo::Method if Idx is already in VTableThunks. This
1171+
// can happen when covariant return adjustment is required too.
1172+
if (!VTableThunks.count(Idx))
1173+
VTableThunks[Idx].Method = VTables.findOriginalMethodInMap(MD);
1174+
VTableThunks[Idx].This = ThisAdjustment;
1175+
};
1176+
1177+
SetThisAdjustmentThunk(VTableIndex);
11511178

11521179
if (isa<CXXDestructorDecl>(MD)) {
11531180
// Add an adjustment for the deleting destructor as well.
1154-
VTableThunks[VTableIndex + 1].This = ThisAdjustment;
1181+
SetThisAdjustmentThunk(VTableIndex + 1);
11551182
}
11561183
}
11571184

@@ -1509,6 +1536,8 @@ void ItaniumVTableBuilder::AddMethods(
15091536
FindNearestOverriddenMethod(MD, PrimaryBases)) {
15101537
if (ComputeReturnAdjustmentBaseOffset(Context, MD,
15111538
OverriddenMD).isEmpty()) {
1539+
VTables.setOriginalMethod(MD, OverriddenMD);
1540+
15121541
// Replace the method info of the overridden method with our own
15131542
// method.
15141543
assert(MethodInfoMap.count(OverriddenMD) &&
@@ -1615,6 +1644,13 @@ void ItaniumVTableBuilder::AddMethods(
16151644
ReturnAdjustment ReturnAdjustment =
16161645
ComputeReturnAdjustment(ReturnAdjustmentOffset);
16171646

1647+
// If a return adjustment is required, record the method that created the
1648+
// vtable entry. We need to record the method because we cannot call
1649+
// findOriginalMethod to find the method that created the entry if the
1650+
// method in the entry requires adjustment.
1651+
if (!ReturnAdjustment.isEmpty())
1652+
VTableThunks[Components.size()].Method = MD;
1653+
16181654
AddMethod(Overrider.Method, ReturnAdjustment);
16191655
}
16201656
}
@@ -1890,11 +1926,32 @@ void ItaniumVTableBuilder::LayoutVTablesForVirtualBases(
18901926
}
18911927
}
18921928

1929+
static void printThunkMethod(const ThunkInfo &Info, raw_ostream &Out) {
1930+
if (Info.Method) {
1931+
std::string Str =
1932+
PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual,
1933+
Info.Method);
1934+
Out << " method: " << Str;
1935+
}
1936+
}
1937+
18931938
/// dumpLayout - Dump the vtable layout.
18941939
void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
18951940
// FIXME: write more tests that actually use the dumpLayout output to prevent
18961941
// ItaniumVTableBuilder regressions.
18971942

1943+
Out << "Original map\n";
1944+
1945+
for (const auto &P : VTables.getOriginalMethodMap()) {
1946+
std::string Str0 =
1947+
PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual,
1948+
P.first);
1949+
std::string Str1 =
1950+
PredefinedExpr::ComputeName(PredefinedIdentKind::PrettyFunctionNoVirtual,
1951+
P.second);
1952+
Out << " " << Str0 << " -> " << Str1 << "\n";
1953+
}
1954+
18981955
if (isBuildingConstructorVTable()) {
18991956
Out << "Construction vtable for ('";
19001957
MostDerivedClass->printQualifiedName(Out);
@@ -1978,6 +2035,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
19782035
}
19792036

19802037
Out << ']';
2038+
printThunkMethod(Thunk, Out);
19812039
}
19822040

19832041
// If this function pointer has a 'this' pointer adjustment, dump it.
@@ -1991,6 +2049,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
19912049
}
19922050

19932051
Out << ']';
2052+
printThunkMethod(Thunk, Out);
19942053
}
19952054
}
19962055

@@ -2027,6 +2086,7 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
20272086

20282087
Out << ']';
20292088
}
2089+
printThunkMethod(Thunk, Out);
20302090
}
20312091

20322092
break;
@@ -2125,7 +2185,6 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) {
21252185

21262186
ThunkInfoVectorTy ThunksVector = Thunks[MD];
21272187
llvm::sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) {
2128-
assert(LHS.Method == nullptr && RHS.Method == nullptr);
21292188
return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return);
21302189
});
21312190

@@ -2314,6 +2373,35 @@ ItaniumVTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
23142373
return I->second;
23152374
}
23162375

2376+
GlobalDecl ItaniumVTableContext::findOriginalMethod(GlobalDecl GD) {
2377+
const auto *MD = cast<CXXMethodDecl>(GD.getDecl());
2378+
computeVTableRelatedInformation(MD->getParent());
2379+
const auto *OriginalMD = findOriginalMethodInMap(MD);
2380+
2381+
if (const auto *DD = dyn_cast<CXXDestructorDecl>(OriginalMD))
2382+
return GlobalDecl(DD, GD.getDtorType());
2383+
return OriginalMD;
2384+
}
2385+
2386+
const CXXMethodDecl *
2387+
ItaniumVTableContext::findOriginalMethodInMap(const CXXMethodDecl *MD) const {
2388+
// Traverse the chain of virtual methods until we find the method that added
2389+
// the v-table slot.
2390+
while (true) {
2391+
auto I = OriginalMethodMap.find(MD);
2392+
2393+
// MD doesn't exist in OriginalMethodMap, so it must be the method we are
2394+
// looking for.
2395+
if (I == OriginalMethodMap.end())
2396+
break;
2397+
2398+
// Set MD to the overridden method.
2399+
MD = I->second;
2400+
}
2401+
2402+
return MD;
2403+
}
2404+
23172405
static std::unique_ptr<VTableLayout>
23182406
CreateVTableLayout(const ItaniumVTableBuilder &Builder) {
23192407
SmallVector<VTableLayout::VTableThunkTy, 1>

0 commit comments

Comments
 (0)