https://github.com/Prabhuk updated 
https://github.com/llvm/llvm-project/pull/230831

>From 1e4099a108e91f68993bc9992ff479a9042030c4 Mon Sep 17 00:00:00 2001
From: prabhukr <[email protected]>
Date: Sat, 10 Oct 2026 12:23:16 -0700
Subject: [PATCH 1/2] [clang][CodeGen] Attach a single callgraph type
 identifier to function definitions

A function definition is annotated with its call graph section type
identifier twice, when its declaration is created (SetFunctionAttributes)
and when its body is emitted (SetLLVMFunctionAttributesForDefinition), so
every defined function carries two identical `!callgraph` attachments:

    define void @foo() #0 !callgraph !3 !callgraph !3 {

Replace the attachment instead of adding a second one, so that a function
carries exactly one identifier. The `.llvm.callgraph` section is unaffected,
the AsmPrinter only uses the first identifier of a function.

Assisted-by: Claude
---
 clang/lib/CodeGen/CodeGenModule.cpp           |  9 +++++---
 .../call-graph-section-single-type-id.c       | 21 +++++++++++++++++++
 2 files changed, 27 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGen/call-graph-section-single-type-id.c

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 62ef11a697b1c..3c6e09effe418 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3664,10 +3664,13 @@ void CodeGenModule::createIndirectFunctionTypeMD(const 
FunctionDecl *FD,
       QT = ReconstructCallGraphPrototype(FNPT, ParamTypes);
     }
 
-    F->addMetadata(
+    // A function definition is annotated when its declaration is created and
+    // again when its body is emitted. Replace the attachment instead of adding
+    // a second one, so that a function carries exactly one type identifier.
+    F->setMetadata(
         llvm::LLVMContext::MD_callgraph,
-        *llvm::MDTuple::get(getLLVMContext(),
-                            {CreateMetadataIdentifierForCallGraphType(QT)}));
+        llvm::MDTuple::get(getLLVMContext(),
+                           {CreateMetadataIdentifierForCallGraphType(QT)}));
   }
 }
 
diff --git a/clang/test/CodeGen/call-graph-section-single-type-id.c 
b/clang/test/CodeGen/call-graph-section-single-type-id.c
new file mode 100644
index 0000000000000..93a421fb084c6
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-single-type-id.c
@@ -0,0 +1,21 @@
+// Tests that a function definition carries a single call graph section type
+// identifier: its declaration and its body are annotated separately, and the
+// second annotation must replace the first one.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o - %s | FileCheck %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o - %s | FileCheck %s
+
+void foo(void);
+
+// CHECK: define {{(dso_local )?}}void @bar(){{[^!]*}}!callgraph 
![[TVOID:[0-9]+]] {
+void bar(void) {
+  foo();
+}
+
+// The declaration of foo was created and annotated for the call in bar.
+// CHECK: define {{(dso_local )?}}void @foo(){{[^!]*}}!callgraph ![[TVOID]] {
+void foo(void) {
+}

>From c120a7d06a1d387eb75278de0971fffc7033e4be Mon Sep 17 00:00:00 2001
From: prabhukr <[email protected]>
Date: Sat, 10 Oct 2026 12:24:22 -0700
Subject: [PATCH 2/2] [clang][CodeGen] Identify virtual methods by their
 override chain root in the call graph section

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
---
 clang/lib/CodeGen/CGCall.cpp                  |  4 +-
 clang/lib/CodeGen/CGVTables.cpp               |  9 ++
 clang/lib/CodeGen/CodeGenModule.cpp           | 25 +++++-
 clang/lib/CodeGen/CodeGenModule.h             |  8 ++
 .../call-graph-section-covariant-return.cpp   | 87 +++++++++++++++++++
 5 files changed, 131 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/call-graph-section-covariant-return.cpp

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]]}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to