[llvm-branch-commits] [clang] [clang][CodeGen] Identify virtual methods by their override chain root in the call graph section (PR #230831)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Oct 10 12:38:09 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-codegen
Author: Prabhu Rajasekaran (Prabhuk)
<details>
<summary>Changes</summary>
A virtual call site is typed after the method named at the call site, but it
can reach any overrider of that method, and overriders may differ from it in
their covariant return type. In that case the `!callee_type` identifier of a
call through a base class pointer did not match the `!callgraph` identifier
of the overrider, and the call graph section missed the edge.
Identify every method of an override chain, at definitions, call sites and
thunks, by the type of the method that introduced the virtual function, so
that virtual calls through a pointer to any class of the hierarchy match the
overriders they can reach.
Stacked on #<!-- -->230838.
Assisted-by: Claude
---
Full diff: https://github.com/llvm/llvm-project/pull/230831.diff
5 Files Affected:
- (modified) clang/lib/CodeGen/CGCall.cpp (+3-1)
- (modified) clang/lib/CodeGen/CGVTables.cpp (+9)
- (modified) clang/lib/CodeGen/CodeGenModule.cpp (+24-1)
- (modified) clang/lib/CodeGen/CodeGenModule.h (+8)
- (added) clang/test/CodeGen/call-graph-section-covariant-return.cpp (+87)
``````````diff
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 1f4fd55060d39..8c8aa079b53a4 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6406,7 +6406,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
*callOrInvoke = CI;
if (CGM.getCodeGenOpts().CallGraphSection) {
QualType CST;
- if (TargetDecl && TargetDecl->getFunctionType())
+ if (const auto *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl))
+ CST = CGM.GetCallGraphFunctionType(FD);
+ else if (TargetDecl && TargetDecl->getFunctionType())
CST = QualType(TargetDecl->getFunctionType(), 0);
else if (const auto *FPT =
Callee.getAbstractInfo().getCalleeFunctionProtoType())
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index f0c48eba1f5c3..37b2250f6e313 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -646,6 +646,15 @@ llvm::Constant *CodeGenVTables::maybeEmitThunk(GlobalDecl GD,
CodeGenFunction(CGM).generateThunk(ThunkFn, FnInfo, GD, TI, IsUnprototyped);
}
+ // A return adjusting thunk fills the vtable slot created by the overridden
+ // method recorded in the thunk info, so it is reached by virtual calls typed
+ // after that method rather than after the thunk's target. Identify it in the
+ // call graph section accordingly, replacing the identifier of its target
+ // attached when the thunk was created (see
+ // CodeGenModule::GetCallGraphFunctionType).
+ if (CGM.getCodeGenOpts().CallGraphSection && TI.Method)
+ CGM.createIndirectFunctionTypeMD(TI.Method, ThunkFn);
+
setThunkProperties(CGM, TI, ThunkFn, ForVTable, GD);
return ThunkFn;
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3c6e09effe418..13e89043a8ec3 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3633,6 +3633,29 @@ static void setLinkageForGV(llvm::GlobalValue *GV, const NamedDecl *ND) {
GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
}
+/// Returns the method that introduced the virtual function that \p MD
+/// overrides, i.e., the root of its (first) override chain.
+static const CXXMethodDecl *GetRootOverriddenMethod(const CXXMethodDecl *MD) {
+ while (MD->size_overridden_methods() > 0)
+ MD = *MD->begin_overridden_methods();
+ return MD;
+}
+
+QualType CodeGenModule::GetCallGraphFunctionType(const FunctionDecl *FD) const {
+ // A virtual call is typed after the method named at the call site, but it
+ // can reach any overrider of that method, whose type may differ from it only
+ // in a covariant return type. Identify every method of an override chain by
+ // the type of the method that introduced the virtual function, so that calls
+ // through a pointer to any class of the hierarchy match the overriders they
+ // can reach. Methods that override several virtual functions with different
+ // (covariant) return types are identified after the first one; the thunks
+ // that fill the vtable slots of the others are identified after those (see
+ // CodeGenVTables::maybeEmitThunk).
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); MD && MD->isVirtual())
+ return GetRootOverriddenMethod(MD)->getType();
+ return FD->getType();
+}
+
void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
llvm::Function *F) {
// All functions which are not internal linkage could be indirect targets.
@@ -3646,7 +3669,7 @@ void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
if (!HasBody || !Def)
Def = FD;
- QualType QT = Def->getType();
+ QualType QT = GetCallGraphFunctionType(Def);
if (const auto *FNPT = QT->getAs<FunctionNoProtoType>()) {
// If there is no definition available in this TU for an unprototyped
// function declaration, skip generating incomplete callgraph metadata.
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index cb1f910366829..bed378e3ebe91 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1793,6 +1793,14 @@ class CodeGenModule : public CodeGenTypeCache {
void createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
llvm::Function *F);
+ /// Returns the function type that identifies \p FD as an indirect call
+ /// target or callee in the call graph section. Virtual methods are
+ /// identified by the type of the method that introduced the virtual function
+ /// (the root of their override chain) so that calls through pointers to any
+ /// class of the hierarchy match the overriders they can reach, even if those
+ /// have a covariant return type.
+ QualType GetCallGraphFunctionType(const FunctionDecl *FD) const;
+
/// Create and attach callgraph metadata if the function is a potential
/// indirect call target to support call graph section.
void createIndirectFunctionTypeMD(const FunctionDecl *FD, llvm::Function *F);
diff --git a/clang/test/CodeGen/call-graph-section-covariant-return.cpp b/clang/test/CodeGen/call-graph-section-covariant-return.cpp
new file mode 100644
index 0000000000000..27ebac8a71b14
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-covariant-return.cpp
@@ -0,0 +1,87 @@
+// Tests that virtual calls through a pointer to any class of a hierarchy get
+// the same call graph section type identifier as the overriders they can reach,
+// even when the overriders have a covariant return type.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -std=c++17 -emit-llvm -o - %s | FileCheck %s
+
+////////////////////////////////////////////////////////////////////////////////
+// A chain of covariant overrides: all methods are identified by the type of
+// Base::clone, the method that introduced the virtual function.
+
+struct Base {
+ virtual ~Base() {}
+ virtual Base *clone() { return this; }
+};
+
+struct Mid : Base {
+ Mid *clone() override { return this; }
+};
+
+struct Derived : Mid {
+ Derived *clone() override { return this; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// A method overriding two virtual functions with different covariant return
+// types: the method is identified after the first one (A::f), and the return
+// adjusting thunk that fills the vtable slot of B::f after the second one.
+
+struct A {
+ virtual A *f() { return this; }
+};
+struct B {
+ virtual B *f() { return this; }
+};
+struct C : A, B {
+ C *f() override { return this; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Call sites.
+
+// CHECK-LABEL: define {{.*}} @_Z12call_clone_bP4Base(
+// CHECK: call noundef ptr %{{.*}}, !callee_type [[CLONE_CT:![0-9]+]]
+Base *call_clone_b(Base *b) { return b->clone(); }
+
+// CHECK-LABEL: define {{.*}} @_Z12call_clone_mP3Mid(
+// CHECK: call noundef ptr %{{.*}}, !callee_type [[CLONE_CT]]
+Mid *call_clone_m(Mid *m) { return m->clone(); }
+
+// CHECK-LABEL: define {{.*}} @_Z12call_clone_dP7Derived(
+// CHECK: call noundef ptr %{{.*}}, !callee_type [[CLONE_CT]]
+Derived *call_clone_d(Derived *d) { return d->clone(); }
+
+// CHECK-LABEL: define {{.*}} @_Z8call_f_aP1A(
+// CHECK: call noundef ptr %{{.*}}, !callee_type [[A_F_CT:![0-9]+]]
+A *call_f_a(A *a) { return a->f(); }
+
+// CHECK-LABEL: define {{.*}} @_Z8call_f_bP1B(
+// CHECK: call noundef ptr %{{.*}}, !callee_type [[B_F_CT:![0-9]+]]
+B *call_f_b(B *b) { return b->f(); }
+
+// CHECK-LABEL: define {{.*}} @_Z8call_f_cP1C(
+// CHECK: call noundef ptr %{{.*}}, !callee_type [[A_F_CT]]
+C *call_f_c(C *c) { return c->f(); }
+
+// Instantiate the vtables (and thereby the methods and thunks below).
+Base *make() { return new Derived(); }
+C *make_c() { return new C(); }
+
+////////////////////////////////////////////////////////////////////////////////
+// Indirect call targets (emitted after the functions above).
+
+// CHECK-DAG: define {{.*}} @_ZN7Derived5cloneEv({{.*}} !callgraph [[CLONE:![0-9]+]] {
+// CHECK-DAG: define {{.*}} @_ZN3Mid5cloneEv({{.*}} !callgraph [[CLONE]] {
+// CHECK-DAG: define {{.*}} @_ZN4Base5cloneEv({{.*}} !callgraph [[CLONE]] {
+// CHECK-DAG: define {{.*}} @_ZN1C1fEv({{.*}} !callgraph [[A_F:![0-9]+]] {
+// CHECK-DAG: define {{.*}} @_ZTchn8_h8_N1C1fEv({{.*}} !callgraph [[B_F:![0-9]+]] {
+// CHECK-DAG: define {{.*}} @_ZN1A1fEv({{.*}} !callgraph [[A_F]] {
+// CHECK-DAG: define {{.*}} @_ZN1B1fEv({{.*}} !callgraph [[B_F]] {
+
+// CHECK-DAG: [[CLONE]] = !{!"_ZTSFP4BasevE"}
+// CHECK-DAG: [[CLONE_CT]] = !{[[CLONE]]}
+// CHECK-DAG: [[A_F]] = !{!"_ZTSFP1AvE"}
+// CHECK-DAG: [[A_F_CT]] = !{[[A_F]]}
+// CHECK-DAG: [[B_F]] = !{!"_ZTSFP1BvE"}
+// CHECK-DAG: [[B_F_CT]] = !{[[B_F]]}
``````````
</details>
https://github.com/llvm/llvm-project/pull/230831
More information about the llvm-branch-commits
mailing list