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

>From eb26a3bdf9bfba33531d9c48f6080946b148f03a Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Wed, 27 Aug 2025 22:11:28 +0000
Subject: [PATCH 01/12] [llvm][AsmPrinter] Add direct calls to callgraph
 section

Extend CallGraphSection to include metadata about direct calls. This
simplifies the design of tools that must parse .callgraph section to not
require dependency on MC layer.
---
 llvm/include/llvm/CodeGen/AsmPrinter.h     |  1 +
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 37 ++++++++++++++++++++--
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h 
b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 91c014236f6cb..e139eb7010dc5 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -214,6 +214,7 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
     /// Map type identifiers to callsite labels. Labels are generated for each
     /// indirect callsite in the function.
     SmallVector<std::pair<CGTypeId, MCSymbol *>> CallSiteLabels;
+    SmallVector<std::pair<MCSymbol *, MCSymbol *>> DirectCallSiteLabels;
   };
 
   enum CallGraphSectionFormatVersion : uint64_t {
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 1641c3eb535a9..4fe082481cbb0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -78,6 +78,7 @@
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
@@ -1733,6 +1734,14 @@ void AsmPrinter::emitCallGraphSection(const 
MachineFunction &MF,
   }
   FuncInfo.CallSiteLabels.clear();
 
+  const auto &DirectCallSiteLabels = FuncInfo.DirectCallSiteLabels;
+  OutStreamer->emitInt64(DirectCallSiteLabels.size());
+  for (const auto &[CallSiteAddrLabel, CalleeSymbol] : DirectCallSiteLabels) {
+    OutStreamer->emitSymbolValue(CallSiteAddrLabel, 
TM.getProgramPointerSize());
+    OutStreamer->emitSymbolValue(CalleeSymbol, TM.getProgramPointerSize());
+  }
+  FuncInfo.DirectCallSiteLabels.clear();
+
   OutStreamer->popSection();
 }
 
@@ -1872,9 +1881,28 @@ void AsmPrinter::emitIndirectCalleeLabels(
     const MachineInstr &MI) {
   // Only indirect calls have type identifiers set.
   const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
-  if (CallSiteInfo == CallSitesInfoMap.end())
+
+  // Handle direct callsite info
+  if (CallSiteInfo == CallSitesInfoMap.end()) {
+    const MachineOperand &CalleeOperand = MI.getOperand(0);
+    MCSymbol *CalleeSymbol = nullptr;
+    switch (CalleeOperand.getType()) {
+    case llvm::MachineOperand::MO_GlobalAddress:
+      CalleeSymbol = getSymbol(CalleeOperand.getGlobal());
+      break;
+    case llvm::MachineOperand::MO_ExternalSymbol:
+      CalleeSymbol = GetExternalSymbolSymbol(CalleeOperand.getSymbolName());
+      break;
+    default:
+      llvm_unreachable("Expect only direct call instructions to be handled.");
+    }
+    MCSymbol *S = MF->getContext().createTempSymbol();
+    OutStreamer->emitLabel(S);
+    FuncInfo.DirectCallSiteLabels.emplace_back(S, CalleeSymbol);
     return;
+  }
 
+  // Handle indirect callsite info.
   for (ConstantInt *CalleeTypeId : CallSiteInfo->second.CalleeTypeIds) {
     MCSymbol *S = MF->getContext().createTempSymbol();
     OutStreamer->emitLabel(S);
@@ -2064,8 +2092,13 @@ void AsmPrinter::emitFunctionBody() {
         break;
       }
 
-      if (TM.Options.EmitCallGraphSection && MI.isCall())
+      if (TM.Options.EmitCallGraphSection && MI.isCall()) {
+        llvm::outs() << "Dump MI for calls \n";
+        MI.dump();
+        llvm::outs() << "CallSitesInfoMap.size() " << CallSitesInfoMap.size()
+                     << "\n";
         emitIndirectCalleeLabels(FuncInfo, CallSitesInfoMap, MI);
+      }
 
       // If there is a post-instruction symbol, emit a label for it here.
       if (MCSymbol *S = MI.getPostInstrSymbol())

>From 2f4443a26926a7f02bff8c5c25831f128506bb15 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Wed, 27 Aug 2025 22:15:59 +0000
Subject: [PATCH 02/12] Remove debug prints

---
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 4fe082481cbb0..d4cbd8ff7e450 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -2093,10 +2093,6 @@ void AsmPrinter::emitFunctionBody() {
       }
 
       if (TM.Options.EmitCallGraphSection && MI.isCall()) {
-        llvm::outs() << "Dump MI for calls \n";
-        MI.dump();
-        llvm::outs() << "CallSitesInfoMap.size() " << CallSitesInfoMap.size()
-                     << "\n";
         emitIndirectCalleeLabels(FuncInfo, CallSitesInfoMap, MI);
       }
 

>From f3abc2f1faf93c65b0316214633131b1e585090d Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Wed, 27 Aug 2025 22:22:04 +0000
Subject: [PATCH 03/12] Rename AsmPrinter method name.

---
 llvm/include/llvm/CodeGen/AsmPrinter.h     | 10 +++++-----
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp |  7 +++----
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h 
b/llvm/include/llvm/CodeGen/AsmPrinter.h
index e139eb7010dc5..2a4655e80a4c8 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -386,12 +386,12 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// are available. Returns empty string otherwise.
   StringRef getConstantSectionSuffix(const Constant *C) const;
 
-  /// Generate and emit labels for callees of the indirect callsites which will
+  /// Generate and emit labels for callees of all callsites which will
   /// be used to populate the .callgraph section.
-  void emitIndirectCalleeLabels(
-      FunctionInfo &FuncInfo,
-      const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
-      const MachineInstr &MI);
+  void
+  emitCalleeLabels(FunctionInfo &FuncInfo,
+                   const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
+                   const MachineInstr &MI);
 
   //===------------------------------------------------------------------===//
   // XRay instrumentation implementation.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index d4cbd8ff7e450..fdf548bb5b33b 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1875,7 +1875,7 @@ static StringRef getMIMnemonic(const MachineInstr &MI, 
MCStreamer &Streamer) {
   return Name;
 }
 
-void AsmPrinter::emitIndirectCalleeLabels(
+void AsmPrinter::emitCalleeLabels(
     FunctionInfo &FuncInfo,
     const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
     const MachineInstr &MI) {
@@ -2092,9 +2092,8 @@ void AsmPrinter::emitFunctionBody() {
         break;
       }
 
-      if (TM.Options.EmitCallGraphSection && MI.isCall()) {
-        emitIndirectCalleeLabels(FuncInfo, CallSitesInfoMap, MI);
-      }
+      if (TM.Options.EmitCallGraphSection && MI.isCall())
+        emitCalleeLabels(FuncInfo, CallSitesInfoMap, MI);
 
       // If there is a post-instruction symbol, emit a label for it here.
       if (MCSymbol *S = MI.getPostInstrSymbol())

>From b76c9adad9804d77c45a5af0f50376c5e7698438 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Thu, 28 Aug 2025 18:21:36 +0000
Subject: [PATCH 04/12] Cleanup callsite label generation function

---
 llvm/include/llvm/CodeGen/AsmPrinter.h     |  8 ++---
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 36 +++++++++++-----------
 2 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h 
b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 2a4655e80a4c8..89333348232b0 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -388,10 +388,10 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
 
   /// Generate and emit labels for callees of all callsites which will
   /// be used to populate the .callgraph section.
-  void
-  emitCalleeLabels(FunctionInfo &FuncInfo,
-                   const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
-                   const MachineInstr &MI);
+  void emitCallsiteLabelsForCallgraph(
+      FunctionInfo &FuncInfo,
+      const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
+      const MachineInstr &MI);
 
   //===------------------------------------------------------------------===//
   // XRay instrumentation implementation.
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index fdf548bb5b33b..4319e8a15c193 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1875,16 +1875,14 @@ static StringRef getMIMnemonic(const MachineInstr &MI, 
MCStreamer &Streamer) {
   return Name;
 }
 
-void AsmPrinter::emitCalleeLabels(
+void AsmPrinter::emitCallsiteLabelsForCallgraph(
     FunctionInfo &FuncInfo,
     const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
     const MachineInstr &MI) {
-  // Only indirect calls have type identifiers set.
-  const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
-
-  // Handle direct callsite info
-  if (CallSiteInfo == CallSitesInfoMap.end()) {
-    const MachineOperand &CalleeOperand = MI.getOperand(0);
+  assert(MI.isCall() && "Callsite labels are meant for call instruction 
only.");
+  const MachineOperand &CalleeOperand = MI.getOperand(0);
+  if (CalleeOperand.isGlobal() || CalleeOperand.isSymbol()) {
+    // Handle direct calls.
     MCSymbol *CalleeSymbol = nullptr;
     switch (CalleeOperand.getType()) {
     case llvm::MachineOperand::MO_GlobalAddress:
@@ -1894,20 +1892,22 @@ void AsmPrinter::emitCalleeLabels(
       CalleeSymbol = GetExternalSymbolSymbol(CalleeOperand.getSymbolName());
       break;
     default:
-      llvm_unreachable("Expect only direct call instructions to be handled.");
+      llvm_unreachable(
+          "Expected to only handle direct call instructions here.");
     }
     MCSymbol *S = MF->getContext().createTempSymbol();
     OutStreamer->emitLabel(S);
     FuncInfo.DirectCallSiteLabels.emplace_back(S, CalleeSymbol);
-    return;
-  }
-
-  // Handle indirect callsite info.
-  for (ConstantInt *CalleeTypeId : CallSiteInfo->second.CalleeTypeIds) {
-    MCSymbol *S = MF->getContext().createTempSymbol();
-    OutStreamer->emitLabel(S);
-    uint64_t CalleeTypeIdVal = CalleeTypeId->getZExtValue();
-    FuncInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
+  } else {
+    // Handle indirect callsite info.
+    // Only indirect calls have type identifiers set.
+    const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
+    for (ConstantInt *CalleeTypeId : CallSiteInfo->second.CalleeTypeIds) {
+      MCSymbol *S = MF->getContext().createTempSymbol();
+      OutStreamer->emitLabel(S);
+      uint64_t CalleeTypeIdVal = CalleeTypeId->getZExtValue();
+      FuncInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
+    }
   }
 }
 
@@ -2093,7 +2093,7 @@ void AsmPrinter::emitFunctionBody() {
       }
 
       if (TM.Options.EmitCallGraphSection && MI.isCall())
-        emitCalleeLabels(FuncInfo, CallSitesInfoMap, MI);
+        emitCallsiteLabelsForCallgraph(FuncInfo, CallSitesInfoMap, MI);
 
       // If there is a post-instruction symbol, emit a label for it here.
       if (MCSymbol *S = MI.getPostInstrSymbol())

>From bffbeb1514e36bcd8df9d1511a46e8cab6950776 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Thu, 28 Aug 2025 20:47:53 +0000
Subject: [PATCH 05/12] Add tests for callgraph direct call sites info

---
 .../X86/call-graph-section-assembly.ll        | 27 ++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll 
b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
index 11362873fb151..950e2f0bdd17a 100644
--- a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
+++ b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
@@ -1,9 +1,16 @@
-;; Test if temporary labels are generated for each indirect callsite with a 
callee_type metadata.
-;; Test if the .callgraph section contains the MD5 hash of callee type ids 
generated from
-;; generalized type id strings.
+;; Test if temporary labels are generated for each callsite.
+;; Test if the .callgraph section contains the MD5 hash of callees' type (type 
id)
+;; is correctly paired with its corresponding temporary label generated for 
indirect
+;; call sites annotated with !callee_type metadata.
+;; Test if the .callgraph section contains direct callsite temporary labels 
paired
+;; correctly with the corresponding callee symbol.
 
 ; RUN: llc -mtriple=x86_64-unknown-linux --call-graph-section -o - < %s | 
FileCheck %s
 
+declare !type !0 void @direct_foo()
+declare !type !1 i32 @direct_bar(i8)
+declare !type !2 ptr @direct_baz(ptr)
+
 ; CHECK: ball:
 ; CHECK-NEXT: [[LABEL_FUNC:\.Lfunc_begin[0-9]+]]:
 define ptr @ball() {
@@ -11,12 +18,18 @@ entry:
   %fp_foo_val = load ptr, ptr null, align 8
    ; CHECK: [[LABEL_TMP0:\.L.*]]:
   call void (...) %fp_foo_val(), !callee_type !0
+   ; CHECK: [[LABEL_TMP_DIRECT0:\.L.*]]:
+  call void @direct_foo()
   %fp_bar_val = load ptr, ptr null, align 8
   ; CHECK: [[LABEL_TMP1:\.L.*]]:
   %call_fp_bar = call i32 %fp_bar_val(i8 0), !callee_type !2
+  ; CHECK: [[LABEL_TMP_DIRECT1:\.L.*]]:
+  %call_fp_bar_direct = call i32 @direct_bar(i8 1)
   %fp_baz_val = load ptr, ptr null, align 8
   ; CHECK: [[LABEL_TMP2:\.L.*]]:
   %call_fp_baz = call ptr %fp_baz_val(ptr null), !callee_type !4
+  ; CHECK: [[LABEL_TMP_DIRECT2:\.L.*]]:
+  %call_fp_baz_direct = call ptr @direct_baz(ptr null)
   ret ptr %call_fp_baz
 }
 
@@ -41,3 +54,11 @@ entry:
 ;; Test for MD5 hash of _ZTSFPvS_E.generalized and the generated temporary 
callsite label.
 ; CHECK-NEXT: .quad   8646233951371320954
 ; CHECK-NEXT: .quad   [[LABEL_TMP2]]
+;; Test for number of direct calls and {callsite_label, callee} pairs.
+; CHECK-NEXT: .quad    3
+; CHECK-NEXT: .quad    [[LABEL_TMP_DIRECT0]]
+; CHECK-NEXT: .quad    direct_foo
+; CHECK-NEXT: .quad    [[LABEL_TMP_DIRECT1]]
+; CHECK-NEXT: .quad    direct_bar
+; CHECK-NEXT: .quad    [[LABEL_TMP_DIRECT2]]
+; CHECK-NEXT: .quad    direct_baz

>From b1a2068d7c0961fd31000701cb922121e3a743ad Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Tue, 11 Mar 2025 20:15:36 +0000
Subject: [PATCH 06/12] [clang] Introduce CallGraphSection codegen option

This patch adds clang CodeGen option to emit callgraph section.
The `-fexperimental-call-graph-section` Driver flag is introduced to
control the `CallGraphSection` CodeGen option.

Pull Request: https://github.com/llvm/llvm-project/pull/117037
---
 clang/include/clang/Basic/CodeGenOptions.def | 2 ++
 clang/include/clang/Driver/Options.td        | 6 ++++++
 clang/lib/CodeGen/BackendUtil.cpp            | 1 +
 clang/lib/Driver/ToolChains/Clang.cpp        | 4 ++++
 clang/lib/Driver/ToolChains/CommonArgs.cpp   | 5 +++++
 clang/test/Driver/call-graph-section.c       | 5 +++++
 6 files changed, 23 insertions(+)
 create mode 100644 clang/test/Driver/call-graph-section.c

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index fda0da99b60c0..043cc55fba63c 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -72,6 +72,8 @@ CODEGENOPT(EnableNoundefAttrs, 1, 0, Benign) ///< Enable 
emitting `noundef` attr
 CODEGENOPT(DebugPassManager, 1, 0, Benign) ///< Prints debug information for 
the new
                                            ///< pass manager.
 CODEGENOPT(DisableRedZone    , 1, 0, Benign) ///< Set when -mno-red-zone is 
enabled.
+CODEGENOPT(CallGraphSection, 1, 0, Benign) ///< Emit a call graph section into 
the
+                                           ///< object file.
 CODEGENOPT(EmitCallSiteInfo, 1, 0, Benign) ///< Emit call site info only in 
the case of
                                            ///< '-g' + 'O>0' level.
 CODEGENOPT(IndirectTlsSegRefs, 1, 0, Benign) ///< Set when 
-mno-tls-direct-seg-refs
diff --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index a7c514e809aa9..7cb50f3ac7677 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4491,6 +4491,12 @@ defm data_sections : BoolFOption<"data-sections",
   PosFlag<SetTrue, [], [ClangOption, CC1Option],
           "Place each data in its own section">,
   NegFlag<SetFalse>>;
+defm experimental_call_graph_section
+    : BoolFOption<"experimental-call-graph-section",
+                  CodeGenOpts<"CallGraphSection">, DefaultFalse,
+                  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+                          "Emit a call graph section">,
+                  NegFlag<SetFalse>>;
 defm stack_size_section : BoolFOption<"stack-size-section",
   CodeGenOpts<"StackSizeSection">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option],
diff --git a/clang/lib/CodeGen/BackendUtil.cpp 
b/clang/lib/CodeGen/BackendUtil.cpp
index 3f095c03397fd..bb295372d537b 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -461,6 +461,7 @@ static bool initTargetOptions(const CompilerInstance &CI,
   Options.StackUsageOutput = CodeGenOpts.StackUsageOutput;
   Options.EmitAddrsig = CodeGenOpts.Addrsig;
   Options.ForceDwarfFrameSection = CodeGenOpts.ForceDwarfFrameSection;
+  Options.EmitCallGraphSection = CodeGenOpts.CallGraphSection;
   Options.EmitCallSiteInfo = CodeGenOpts.EmitCallSiteInfo;
   Options.EnableAIXExtendedAltivecABI = LangOpts.EnableAIXExtendedAltivecABI;
   Options.XRayFunctionIndex = CodeGenOpts.XRayFunctionIndex;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 6aa228826bab2..f6a385a39c561 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6495,6 +6495,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
     CmdArgs.push_back(A->getValue());
   }
 
+  if (Args.hasFlag(options::OPT_fexperimental_call_graph_section,
+                   options::OPT_fno_experimental_call_graph_section, false))
+    CmdArgs.push_back("-fexperimental-call-graph-section");
+
   Args.addOptInFlag(CmdArgs, options::OPT_fstack_size_section,
                     options::OPT_fno_stack_size_section);
 
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 416b4d91b921e..482ae72c3a775 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1272,6 +1272,11 @@ void tools::addLTOOptions(const ToolChain &ToolChain, 
const ArgList &Args,
     CmdArgs.push_back(
         Args.MakeArgString(Twine(PluginOptPrefix) + "-stack-size-section"));
 
+  if (Args.hasFlag(options::OPT_fexperimental_call_graph_section,
+                   options::OPT_fno_experimental_call_graph_section, false))
+    CmdArgs.push_back(
+        Args.MakeArgString(Twine(PluginOptPrefix) + "-call-graph-section"));
+
   // Setup statistics file output.
   SmallString<128> StatsFile = getStatsFileName(Args, Output, *Input, D);
   if (!StatsFile.empty())
diff --git a/clang/test/Driver/call-graph-section.c 
b/clang/test/Driver/call-graph-section.c
new file mode 100644
index 0000000000000..00fa896dffb87
--- /dev/null
+++ b/clang/test/Driver/call-graph-section.c
@@ -0,0 +1,5 @@
+// RUN: %clang -### -fexperimental-call-graph-section %s 2>&1 | FileCheck 
--check-prefix=CALL-GRAPH-SECTION %s
+// RUN: %clang -### -fexperimental-call-graph-section 
-fno-experimental-call-graph-section %s 2>&1 | FileCheck 
--check-prefix=NO-CALL-GRAPH-SECTION %s
+
+// CALL-GRAPH-SECTION: "-fexperimental-call-graph-section"
+// NO-CALL-GRAPH-SECTION-NOT: "-fexperimental-call-graph-section"

>From e40bccf3e9a1ec76b62f168afc23b4a9d12f1bf8 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Tue, 11 Mar 2025 20:39:23 +0000
Subject: [PATCH 07/12] [clang] callee_type metadata for indirect calls

Create and add generalized type identifier metadata to indirect calls,
and to functions which are potential indirect call targets.

The functions carry the !type metadata. The indirect callsites carry a
list of !type metadata values under !callee_type metadata.

Pull Request: https://github.com/llvm/llvm-project/pull/117036
---
 clang/lib/CodeGen/CGCall.cpp                  |  18 ++-
 clang/lib/CodeGen/CodeGenModule.cpp           |  38 ++++-
 clang/lib/CodeGen/CodeGenModule.h             |   3 +
 .../CodeGen/call-graph-section-templates.cpp  | 114 +++++++++++++++
 .../call-graph-section-virtual-methods.cpp    |  55 +++++++
 clang/test/CodeGen/call-graph-section.c       |  83 +++++++++++
 clang/test/CodeGen/call-graph-section.cpp     | 138 ++++++++++++++++++
 7 files changed, 444 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGen/call-graph-section-templates.cpp
 create mode 100644 clang/test/CodeGen/call-graph-section-virtual-methods.cpp
 create mode 100644 clang/test/CodeGen/call-graph-section.c
 create mode 100644 clang/test/CodeGen/call-graph-section.cpp

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 0b2fce4244fb6..b2f27a1c7f3c9 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5919,8 +5919,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo 
&CallInfo,
       CI->getCalledFunction()->getName().starts_with("_Z4sqrt")) {
     SetSqrtFPAccuracy(CI);
   }
-  if (callOrInvoke)
+  if (callOrInvoke) {
     *callOrInvoke = CI;
+    if (CGM.getCodeGenOpts().CallGraphSection) {
+      QualType CST;
+      if (TargetDecl && TargetDecl->getFunctionType())
+        CST = QualType(TargetDecl->getFunctionType(), 0);
+      else if (const auto *FPT =
+                   Callee.getAbstractInfo().getCalleeFunctionProtoType())
+        CST = QualType(FPT, 0);
+      else
+        llvm_unreachable(
+            "Cannot find the callee type to generate callee_type metadata.");
+
+      // Set type identifier metadata of indirect calls for call graph section.
+      if (!CST.isNull())
+        CGM.createCalleeTypeMetadataForIcall(CST, *callOrInvoke);
+    }
+  }
 
   // If this is within a function that has the guard(nocf) attribute and is an
   // indirect call, add the "guard_nocf" attribute to this call to indicate 
that
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 5e96f5bd6e5f8..1168885f4c5a6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2818,8 +2818,9 @@ void 
CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
 
   // In the cross-dso CFI mode with canonical jump tables, we want !type
   // attributes on definitions only.
-  if (CodeGenOpts.SanitizeCfiCrossDso &&
-      CodeGenOpts.SanitizeCfiCanonicalJumpTables) {
+  if ((CodeGenOpts.SanitizeCfiCrossDso &&
+       CodeGenOpts.SanitizeCfiCanonicalJumpTables) ||
+      CodeGenOpts.CallGraphSection) {
     if (auto *FD = dyn_cast<FunctionDecl>(D)) {
       // Skip available_externally functions. They won't be codegen'ed in the
       // current module anyway.
@@ -3031,9 +3032,21 @@ static void setLinkageForGV(llvm::GlobalValue *GV, const 
NamedDecl *ND) {
     GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
 }
 
+static bool hasExistingGeneralizedTypeMD(llvm::Function *F) {
+  llvm::MDNode *MD = F->getMetadata(llvm::LLVMContext::MD_type);
+  return MD && MD->hasGeneralizedMDString();
+}
+
 void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
                                                        llvm::Function *F) {
-  // Only if we are checking indirect calls.
+  if (CodeGenOpts.CallGraphSection && !hasExistingGeneralizedTypeMD(F) &&
+      (!F->hasLocalLinkage() ||
+       F->getFunction().hasAddressTaken(nullptr, /*IgnoreCallbackUses=*/true,
+                                        /*IgnoreAssumeLikeCalls=*/true,
+                                        /*IgnoreLLVMUsed=*/false)))
+    F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType()));
+
+  // Add additional metadata only if we are checking indirect calls with CFI.
   if (!LangOpts.Sanitize.has(SanitizerKind::CFIICall))
     return;
 
@@ -3044,7 +3057,9 @@ void 
CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
 
   llvm::Metadata *MD = CreateMetadataIdentifierForType(FD->getType());
   F->addTypeMetadata(0, MD);
-  F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType()));
+  // Add the generalized identifier if not added already.
+  if (!hasExistingGeneralizedTypeMD(F))
+    F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType()));
 
   // Emit a hash-based bit set entry for cross-DSO calls.
   if (CodeGenOpts.SanitizeCfiCrossDso)
@@ -3052,6 +3067,21 @@ void 
CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
       F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId));
 }
 
+void CodeGenModule::createCalleeTypeMetadataForIcall(const QualType &QT,
+                                                     llvm::CallBase *CB) {
+  // Only if needed for call graph section and only for indirect calls.
+  if (!CodeGenOpts.CallGraphSection || !CB->isIndirectCall())
+    return;
+
+  llvm::Metadata *TypeIdMD = CreateMetadataIdentifierGeneralized(QT);
+  llvm::MDTuple *TypeTuple = llvm::MDTuple::get(
+      getLLVMContext(), {llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+                             llvm::Type::getInt64Ty(getLLVMContext()), 0)),
+                         TypeIdMD});
+  llvm::MDTuple *MDN = llvm::MDNode::get(getLLVMContext(), {TypeTuple});
+  CB->setMetadata(llvm::LLVMContext::MD_callee_type, MDN);
+}
+
 void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
   llvm::LLVMContext &Ctx = F->getContext();
   llvm::MDBuilder MDB(Ctx);
diff --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index f62350fd8d378..19775944e066f 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1641,6 +1641,9 @@ class CodeGenModule : public CodeGenTypeCache {
   void createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
                                           llvm::Function *F);
 
+  /// Create and attach type metadata to the given call.
+  void createCalleeTypeMetadataForIcall(const QualType &QT, llvm::CallBase 
*CB);
+
   /// Set type metadata to the given function.
   void setKCFIType(const FunctionDecl *FD, llvm::Function *F);
 
diff --git a/clang/test/CodeGen/call-graph-section-templates.cpp 
b/clang/test/CodeGen/call-graph-section-templates.cpp
new file mode 100644
index 0000000000000..43be48e94125b
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-templates.cpp
@@ -0,0 +1,114 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// specifically for C++ templates.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck --check-prefix=FT    %s < %t
+// RUN: FileCheck --check-prefix=CST   %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+// Class definitions and template classes (check for indirect target metadata)
+
+class Cls1 {};
+
+// Cls2 is instantiated with T=Cls1 in foo(). Following checks are for this
+// instantiation.
+template <class T>
+class Cls2 {
+public:
+  // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f1Ev({{.*}} !type 
[[F_TCLS2F1:![0-9]+]]
+  void f1() {}
+
+  // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f2ES0_({{.*}} !type 
[[F_TCLS2F2:![0-9]+]]
+  void f2(T a) {}
+
+  // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f3EPS0_({{.*}} !type 
[[F_TCLS2F3:![0-9]+]]
+  void f3(T *a) {}
+
+  // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f4EPKS0_({{.*}} !type 
[[F_TCLS2F4:![0-9]+]]
+  void f4(const T *a) {}
+
+  // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f5ERS0_({{.*}} !type 
[[F_TCLS2F5:![0-9]+]]
+  void f5(T &a) {}
+
+  // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f6ERKS0_({{.*}} !type 
[[F_TCLS2F6:![0-9]+]]
+  void f6(const T &a) {}
+
+  // Mixed type function pointer member
+  T *(*fp)(T a, T *b, const T *c, T &d, const T &e);
+};
+
+// FT-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"}
+// FT-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+// FT-DAG: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvPvE.generalized"}
+// FT-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPKvE.generalized"}
+// FT-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+// FT-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
+
+////////////////////////////////////////////////////////////////////////////////
+// Callsites (check for indirect callsite operand bundles)
+
+template <class T>
+T *T_func(T a, T *b, const T *c, T &d, const T &e) { return b; }
+
+// CST-LABEL: define {{.*}} @_Z3foov
+void foo() {
+  // Methods for Cls2<Cls1> is checked above within the template description.
+  Cls2<Cls1> Obj;
+
+  Obj.fp = T_func<Cls1>;
+  Cls1 Cls1Obj;
+  
+  // CST: call noundef ptr %{{.*}}, !callee_type [[F_TFUNC_CLS1_CT:![0-9]+]]
+  Obj.fp(Cls1Obj, &Cls1Obj, &Cls1Obj, Cls1Obj, Cls1Obj);
+
+  // Make indirect calls to Cls2's member methods
+  auto fp_f1 = &Cls2<Cls1>::f1;
+  auto fp_f2 = &Cls2<Cls1>::f2;
+  auto fp_f3 = &Cls2<Cls1>::f3;
+  auto fp_f4 = &Cls2<Cls1>::f4;
+  auto fp_f5 = &Cls2<Cls1>::f5;
+  auto fp_f6 = &Cls2<Cls1>::f6;
+
+  auto *Obj2Ptr = &Obj;
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F1_CT:![0-9]+]]
+  (Obj2Ptr->*fp_f1)();
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F2_CT:![0-9]+]]
+  (Obj2Ptr->*fp_f2)(Cls1Obj);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F3_CT:![0-9]+]]
+  (Obj2Ptr->*fp_f3)(&Cls1Obj);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F4_CT:![0-9]+]]
+  (Obj2Ptr->*fp_f4)(&Cls1Obj);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F5_CT:![0-9]+]]
+  (Obj2Ptr->*fp_f5)(Cls1Obj);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F6_CT:![0-9]+]]
+  (Obj2Ptr->*fp_f6)(Cls1Obj);
+}
+
+// CST: define {{.*}} @_Z6T_funcI4Cls1EPT_S1_S2_PKS1_RS1_RS3_({{.*}} !type 
[[F_TFUNC_CLS1:![0-9]+]]
+// CST-DAG: [[F_TFUNC_CLS1_CT]] = !{[[F_TFUNC_CLS1:![0-9]+]]}
+// CST-DAG: [[F_TFUNC_CLS1]] = !{i64 0, 
!"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized"}
+
+// CST-DAG: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"}
+
+// CST-DAG: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+
+// CST-DAG: [[F_TCLS2F3_CT]] = !{[[F_TCLS2F3:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvPvE.generalized"}
+
+// CST-DAG: [[F_TCLS2F4_CT]] = !{[[F_TCLS2F4:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPKvE.generalized"}
+
+// CST-DAG: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+
+// CST-DAG: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
diff --git a/clang/test/CodeGen/call-graph-section-virtual-methods.cpp 
b/clang/test/CodeGen/call-graph-section-virtual-methods.cpp
new file mode 100644
index 0000000000000..aacd274dadbed
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-virtual-methods.cpp
@@ -0,0 +1,55 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// specifically for virtual methods.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck --check-prefix=FT %s < %t
+// RUN: FileCheck --check-prefix=CST %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+// Class definitions (check for indirect target metadata)
+
+class Base {
+  public:
+    // FT-DAG: define {{.*}} @_ZN4Base2vfEPc({{.*}} !type [[F_TVF:![0-9]+]]
+    virtual int vf(char *a) { return 0; };
+  };
+  
+  class Derived : public Base {
+  public:
+    // FT-DAG: define {{.*}} @_ZN7Derived2vfEPc({{.*}} !type [[F_TVF]]
+    int vf(char *a) override { return 1; };
+  };
+  
+  // FT-DAG: [[F_TVF]] = !{i64 0, !"_ZTSFiPvE.generalized"}
+  
+  
////////////////////////////////////////////////////////////////////////////////
+  // Callsites (check for indirect callsite operand bundles)
+  
+  // CST-LABEL: define {{.*}} @_Z3foov
+  void foo() {
+    auto B = Base();
+    auto D = Derived();
+  
+    Base *Bptr = &B;
+    Base *BptrToD = &D;
+    Derived *Dptr = &D;
+  
+    auto FpBaseVf = &Base::vf;
+    auto FpDerivedVf = &Derived::vf;
+  
+    // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+    (Bptr->*FpBaseVf)(0);
+  
+    // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+    (BptrToD->*FpBaseVf)(0);
+  
+    // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+    (Dptr->*FpBaseVf)(0);
+  
+    // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+    (Dptr->*FpDerivedVf)(0);
+  }
+
+  // CST-DAG: [[F_TVF_CT]] = !{[[F_TVF:![0-9]+]]}
+  // CST-DAG: [[F_TVF]] = !{i64 0, !"_ZTSFiPvE.generalized"}
diff --git a/clang/test/CodeGen/call-graph-section.c 
b/clang/test/CodeGen/call-graph-section.c
new file mode 100644
index 0000000000000..3579b5551bdf8
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -0,0 +1,83 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ITANIUM %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s
+
+// CHECK-DAG: define {{(dso_local)?}} void @foo({{.*}} !type 
[[F_TVOID:![0-9]+]]
+void foo() {
+}
+
+// CHECK-DAG: define {{(dso_local)?}} void @bar({{.*}} !type [[F_TVOID]]
+void bar() {
+  void (*fp)() = foo;
+  // CHECK: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+  fp();
+}
+
+// CHECK-DAG: define {{(dso_local)?}} i32 @baz({{.*}} !type 
[[F_TPRIMITIVE:![0-9]+]]
+int baz(char a, float b, double c) {
+  return 1;
+}
+
+// CHECK-DAG: define {{(dso_local)?}} ptr @qux({{.*}} !type [[F_TPTR:![0-9]+]]
+int *qux(char *a, float *b, double *c) {
+  return 0;
+}
+
+// CHECK-DAG: define {{(dso_local)?}} void @corge({{.*}} !type [[F_TVOID]]
+void corge() {
+  int (*fp_baz)(char, float, double) = baz;
+  // CHECK: call i32 {{.*}}, !callee_type [[F_TPRIMITIVE_CT:![0-9]+]]  
+  fp_baz('a', .0f, .0);
+
+  int *(*fp_qux)(char *, float *, double *) = qux;
+  // CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]
+  fp_qux(0, 0, 0);
+}
+
+struct st1 {
+  int *(*fp)(char *, float *, double *);
+};
+
+struct st2 {
+  struct st1 m;
+};
+
+// CHECK-DAG: define {{(dso_local)?}} void @stparam({{.*}} !type 
[[F_TSTRUCT:![0-9]+]]
+void stparam(struct st2 a, struct st2 *b) {}
+
+// CHECK-DAG: define {{(dso_local)?}} void @stf({{.*}} !type [[F_TVOID]]
+void stf() {
+  struct st1 St1;
+  St1.fp = qux;
+  // CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]  
+  St1.fp(0, 0, 0);
+
+  struct st2 St2;
+  St2.m.fp = qux;
+  // CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]
+  St2.m.fp(0, 0, 0);
+
+  // CHECK: call void {{.*}}, !callee_type [[F_TSTRUCT_CT:![0-9]+]]
+  void (*fp_stparam)(struct st2, struct st2 *) = stparam;
+  fp_stparam(St2, &St2);
+}
+
+// CHECK-DAG: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
+// ITANIUM-DAG: [[F_TVOID]] = !{i64 0, !"_ZTSFvE.generalized"}
+// MS-DAG:  [[F_TVOID]] = !{i64 0, !"?6AX@Z.generalized"}
+
+// CHECK-DAG: [[F_TPRIMITIVE_CT]] = !{[[F_TPRIMITIVE:![0-9]+]]}
+// ITANIUM-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"_ZTSFicfdE.generalized"}
+// MS-DAG:      [[F_TPRIMITIVE]] = !{i64 0, !"?6AHDMN@Z.generalized"}
+
+// CHECK-DAG: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
+// ITANIUM-DAG: [[F_TPTR]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"}
+// MS-DAG:      [[F_TPTR]] = !{i64 0, !"?6APEAXPEAX00@Z.generalized"}
+
+// CHECK-DAG: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
+// ITANIUM-DAG: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PvE.generalized"}
+// MS-DAG:      [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@PEAX@Z.generalized"}
diff --git a/clang/test/CodeGen/call-graph-section.cpp 
b/clang/test/CodeGen/call-graph-section.cpp
new file mode 100644
index 0000000000000..467fdd111b71d
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section.cpp
@@ -0,0 +1,138 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// specifically for C++ class and instance methods.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux 
-fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck --check-prefix=FT %s < %t
+// RUN: FileCheck --check-prefix=CST %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+// Class definitions (check for indirect target metadata)
+
+class Cls1 {
+public:
+  // FT-DAG: define {{.*}} ptr @_ZN4Cls18receiverEPcPf({{.*}} !type 
[[F_TCLS1RECEIVER:![0-9]+]]
+  static int *receiver(char *a, float *b) { return 0; }
+};
+
+class Cls2 {
+public:
+  int *(*fp)(char *, float *);
+
+  // FT-DAG: define {{.*}} i32 @_ZN4Cls22f1Ecfd({{.*}} !type 
[[F_TCLS2F1:![0-9]+]]
+  int f1(char a, float b, double c) { return 0; }
+
+  // FT-DAG: define {{.*}} ptr @_ZN4Cls22f2EPcPfPd({{.*}} !type 
[[F_TCLS2F2:![0-9]+]]
+  int *f2(char *a, float *b, double *c) { return 0; }
+
+  // FT-DAG: define {{.*}} void @_ZN4Cls22f3E4Cls1({{.*}} !type 
[[F_TCLS2F3F4:![0-9]+]]
+  void f3(Cls1 a) {}
+
+  // FT-DAG: define {{.*}} void @_ZN4Cls22f4E4Cls1({{.*}} !type [[F_TCLS2F3F4]]
+  void f4(const Cls1 a) {}
+
+  // FT-DAG: define {{.*}} void @_ZN4Cls22f5EP4Cls1({{.*}} !type 
[[F_TCLS2F5:![0-9]+]]
+  void f5(Cls1 *a) {}
+
+  // FT-DAG: define {{.*}} void @_ZN4Cls22f6EPK4Cls1({{.*}} !type 
[[F_TCLS2F6:![0-9]+]]
+  void f6(const Cls1 *a) {}
+
+  // FT-DAG: define {{.*}} void @_ZN4Cls22f7ER4Cls1({{.*}} !type 
[[F_TCLS2F7:![0-9]+]]
+  void f7(Cls1 &a) {}
+
+  // FT-DAG: define {{.*}} void @_ZN4Cls22f8ERK4Cls1({{.*}} !type 
[[F_TCLS2F8:![0-9]+]]
+  void f8(const Cls1 &a) {}
+
+  // FT-DAG: define {{.*}} void @_ZNK4Cls22f9Ev({{.*}} !type 
[[F_TCLS2F9:![0-9]+]]
+  void f9() const {}
+};
+
+// FT-DAG: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPvS_S_E.generalized"}
+// FT-DAG: [[F_TCLS2F2]]   = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"}
+// FT-DAG: [[F_TCLS2F1]]   = !{i64 0, !"_ZTSFicfdE.generalized"}
+// FT-DAG: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+// FT-DAG: [[F_TCLS2F5]]   = !{i64 0, !"_ZTSFvPvE.generalized"}
+// FT-DAG: [[F_TCLS2F6]]   = !{i64 0, !"_ZTSFvPKvE.generalized"}
+// FT-DAG: [[F_TCLS2F7]]   = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+// FT-DAG: [[F_TCLS2F8]]   = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
+// FT-DAG: [[F_TCLS2F9]]   = !{i64 0, !"_ZTSKFvvE.generalized"}
+
+////////////////////////////////////////////////////////////////////////////////
+// Callsites (check for indirect callsites' callee_type metadata )
+
+// CST-LABEL: define {{.*}} @_Z3foov
+void foo() {
+  Cls2 ObjCls2;
+  ObjCls2.fp = &Cls1::receiver;
+
+  // CST: call noundef ptr %{{.*}}, !callee_type [[F_TCLS1RECEIVER_CT:![0-9]+]]
+  ObjCls2.fp(0, 0);
+
+  auto fp_f1 = &Cls2::f1;
+  auto fp_f2 = &Cls2::f2;
+  auto fp_f3 = &Cls2::f3;
+  auto fp_f4 = &Cls2::f4;
+  auto fp_f5 = &Cls2::f5;
+  auto fp_f6 = &Cls2::f6;
+  auto fp_f7 = &Cls2::f7;
+  auto fp_f8 = &Cls2::f8;
+  auto fp_f9 = &Cls2::f9;
+
+  Cls2 *ObjCls2Ptr = &ObjCls2;
+  Cls1 Cls1Param;
+
+  // CST: call noundef i32 %{{.*}}, !callee_type [[F_TCLS2F1_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f1)(0, 0, 0);
+
+  // CST: call noundef ptr %{{.*}}, !callee_type [[F_TCLS2F2_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f2)(0, 0, 0);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F3F4_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f3)(Cls1Param);
+
+  // CST: call void  %{{.*}}, !callee_type [[F_TCLS2F3F4_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f4)(Cls1Param);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F5_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f5)(&Cls1Param);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F6_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f6)(&Cls1Param);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F7_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f7)(Cls1Param);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F8_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f8)(Cls1Param);
+
+  // CST: call void %{{.*}}, !callee_type [[F_TCLS2F9_CT:![0-9]+]]
+  (ObjCls2Ptr->*fp_f9)();
+}
+
+
+// CST-DAG: [[F_TCLS1RECEIVER_CT]] = !{[[F_TCLS1RECEIVER:![0-9]+]]}
+// CST-DAG: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPvS_S_E.generalized"}
+
+// CST-DAG: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F2]]   = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"}
+
+// CST-DAG: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F1]]   = !{i64 0, !"_ZTSFicfdE.generalized"}
+
+// CST-DAG: [[F_TCLS2F3F4_CT]] = !{[[F_TCLS2F3F4:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+
+// CST-DAG: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F5]]   = !{i64 0, !"_ZTSFvPvE.generalized"}
+
+// CST-DAG: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F6]]   = !{i64 0, !"_ZTSFvPKvE.generalized"}
+
+// CST-DAG: [[F_TCLS2F7_CT]] = !{[[F_TCLS2F7:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F7]]   = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+
+// CST-DAG: [[F_TCLS2F8_CT]] = !{[[F_TCLS2F8:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F8]]   = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
+
+// CST-DAG: [[F_TCLS2F9_CT]] = !{[[F_TCLS2F9:![0-9]+]]}
+// CST-DAG: [[F_TCLS2F9]]   = !{i64 0, !"_ZTSKFvvE.generalized"}

>From b9943231b9aa5d2240c0c28db00a13b2164d770d Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Thu, 28 Aug 2025 22:16:13 +0000
Subject: [PATCH 08/12] Adapted objdump changes without disassembly

---
 llvm/tools/llvm-objdump/ObjdumpOpts.td   |   6 +
 llvm/tools/llvm-objdump/llvm-objdump.cpp | 229 ++++++++++++++++++++++-
 2 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td 
b/llvm/tools/llvm-objdump/ObjdumpOpts.td
index c97e06f3ed173..cb9e0d15fca65 100644
--- a/llvm/tools/llvm-objdump/ObjdumpOpts.td
+++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td
@@ -45,6 +45,12 @@ defm build_id :
 def : Flag<["-"], "a">, Alias<archive_headers>,
   HelpText<"Alias for --archive-headers">;
 
+def call_graph_info
+    : Flag<["--"], "call-graph-info">,
+      HelpText<
+          "Dump call graph information including indirect call and target IDs "
+          "from call graph section, if available.">;
+
 def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">;
 def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
 
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp 
b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 46be539d6f9e6..bd14241970593 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -302,6 +302,7 @@ static uint64_t AdjustVMA;
 static bool AllHeaders;
 static std::string ArchName;
 bool objdump::ArchiveHeaders;
+static bool CallGraphInfo;
 bool objdump::Demangle;
 bool objdump::Disassemble;
 bool objdump::DisassembleAll;
@@ -349,6 +350,33 @@ static bool Wide;
 std::string objdump::Prefix;
 uint32_t objdump::PrefixStrip;
 
+// Enumeration of function kinds, and their mapping to function kind values
+// from call graph section (.callgraph).
+// Must stay in sync with enum from llvm/include/llvm/CodeGen/AsmPrinter.h.
+enum class FunctionKind : uint64_t {
+  // Function cannot be target to indirect calls.
+  NOT_INDIRECT_TARGET = 0,
+  // Function may be target to indirect calls but its type id is unknown.
+  INDIRECT_TARGET_UNKNOWN_TID = 1,
+  // Function may be target to indirect calls and its type id is known.
+  INDIRECT_TARGET_KNOWN_TID = 2,
+
+  // Available in the binary but not listed in the call graph section.
+  NOT_LISTED = 3,
+};
+
+struct FunctionInfo {
+  FunctionKind Kind;
+  struct DirectCallSite {
+    uint64_t CallSite;
+    uint64_t Callee;
+    DirectCallSite(uint64_t CallSite, uint64_t Callee)
+        : CallSite(CallSite), Callee(Callee) {}
+  };
+  SmallVector<DirectCallSite> DirectCallSites;
+  SmallVector<uint64_t> IndirectCallSites;
+};
+
 DebugFormat objdump::DbgVariables = DFDisabled;
 DebugFormat objdump::DbgInlinedFunctions = DFDisabled;
 
@@ -1948,7 +1976,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile 
&DbgObj,
   }
 
   for (const SectionRef &Section : ToolSectionFilter(Obj)) {
-    if (FilterSections.empty() && !DisassembleAll &&
+    if (((FilterSections.empty() && !DisassembleAll) || CallGraphInfo) &&
         (!Section.isText() || Section.isVirtual()))
       continue;
 
@@ -2383,7 +2411,6 @@ disassembleObject(ObjectFile &Obj, const ObjectFile 
&DbgObj,
           LEP.update({ThisAddr, Section.getIndex()},
                      {ThisAddr + Size, Section.getIndex()},
                      Index + Size != End);
-
           DT->InstPrinter->setCommentStream(CommentStream);
 
           DT->Printer->printInst(
@@ -3141,6 +3168,200 @@ void Dumper::printSymbol(const SymbolRef &Symbol,
   outs() << ' ' << SymName << '\n';
 }
 
+static void printCallGraphInfo(ObjectFile *Obj) {
+  // Get function info through disassembly. Suppress disassembler outputs to
+  // console.
+  // disassembleObject(Obj, /*InlineRelocs=*/false, nulls());
+
+  // Get the .callgraph section.
+  StringRef CallGraphSectionName(".callgraph");
+  std::optional<object::SectionRef> CallGraphSection;
+  for (auto Sec : ToolSectionFilter(*Obj)) {
+    StringRef Name;
+    if (Expected<StringRef> NameOrErr = Sec.getName())
+      Name = *NameOrErr;
+    else
+      consumeError(NameOrErr.takeError());
+
+    if (Name == CallGraphSectionName) {
+      CallGraphSection = Sec;
+      break;
+    }
+  }
+  if (!CallGraphSection)
+    reportWarning("there is no .callgraph section", Obj->getFileName());
+
+  // Map type id to indirect call sites.
+  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirCallSites;
+  // Map type id to indirect targets.
+  MapVector<uint64_t, SmallVector<uint64_t>> TypeIdToIndirTargets;
+  // Map function entry pc to function info.
+  MapVector<uint64_t, FunctionInfo> FuncInfo;
+  // Instructions that are not indirect calls but have a type id are ignored.
+  // uint64_t IgnoredICallIdCount = 0;
+  // Number of valid indirect calls with type ids.
+  // uint64_t ICallWithTypeIdCount = 0;
+  if (CallGraphSection) {
+    StringRef CGSecContents = unwrapOrError(
+        CallGraphSection.value().getContents(), Obj->getFileName());
+    // TODO: some entries are written in pointer size. are they always 64-bit?
+    if (CGSecContents.size() % sizeof(uint64_t))
+      reportError(Obj->getFileName(),
+                  "Malformed .callgraph section. Unexpected size.");
+
+    size_t Size = CGSecContents.size() / sizeof(uint64_t);
+    auto *It = reinterpret_cast<const uint64_t *>(CGSecContents.data());
+    const auto *const End = It + Size;
+
+    auto CGHasNext = [&]() { return It < End; };
+    auto CGNext = [&]() -> uint64_t {
+      if (!CGHasNext())
+        reportError(Obj->getFileName(),
+                    "Malformed .callgraph section. Parsing error.");
+      return *It++;
+    };
+
+    // Parse the content
+    while (CGHasNext()) {
+      // Format version number.
+      uint64_t FormatVersionNumber = CGNext();
+      if (FormatVersionNumber != 0)
+        reportError(Obj->getFileName(),
+                    "Unknown format version in .callgraph section.");
+      // Function entry pc.
+      uint64_t FuncEntryPc = CGNext();
+      // Function kind.
+      uint64_t Kind = CGNext();
+      switch (Kind) {
+      case 0: // not an indirect target
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET;
+        break;
+      case 1: // indirect target with unknown type id
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+        break;
+      case 2: // indirect target with known type id
+        FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID;
+        TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc);
+        break;
+      default:
+        reportError(Obj->getFileName(),
+                    "Unknown function kind in .callgraph section.");
+      }
+
+      // Read indirect call sites info.
+      uint64_t IndirectCallSiteCount = CGNext();
+      for (unsigned long I = 0; I < IndirectCallSiteCount; I++) {
+        uint64_t TypeId = CGNext();
+        uint64_t CallSitePc = CGNext();
+        TypeIdToIndirCallSites[TypeId].push_back(CallSitePc);
+        FuncInfo[FuncEntryPc].IndirectCallSites.push_back(CallSitePc);
+      }
+
+      // Read call sites.
+      uint64_t DirectCallSiteCount = CGNext();
+      for (unsigned long I = 0; I < DirectCallSiteCount; I++) {
+        uint64_t CallSitePc = CGNext();
+        uint64_t CalleePc = CGNext();
+        FuncInfo[FuncEntryPc].DirectCallSites.emplace_back(CallSitePc,
+                                                           CalleePc);
+      }
+    }
+
+    uint64_t NotListedCount = 0;
+    uint64_t UnknownCount = 0;
+
+    llvm::sort(FuncInfo,
+               [](const auto &A, const auto &B) { return A.first < B.first; });
+
+    for (const auto &El : FuncInfo) {
+      NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED;
+      UnknownCount +=
+          El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID;
+    }
+    if (NotListedCount)
+      reportWarning("callgraph section does not have information for " +
+                        std::to_string(NotListedCount) + " functions",
+                    Obj->getFileName());
+    if (UnknownCount)
+      reportWarning("callgraph section has unknown type id for " +
+                        std::to_string(UnknownCount) + " indirect targets",
+                    Obj->getFileName());
+
+    // Print indirect targets
+    outs() << "\nINDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])";
+
+    // Print indirect targets with unknown type.
+    // For completeness, functions for which the call graph section does not
+    // provide information are included.
+    if (NotListedCount || UnknownCount) {
+      outs() << "\nUNKNOWN";
+      for (const auto &El : FuncInfo) {
+        uint64_t FuncEntryPc = El.first;
+        FunctionKind FuncKind = El.second.Kind;
+        if (FuncKind == FunctionKind::NOT_LISTED ||
+            FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID)
+          outs() << " " << format("%lx", FuncEntryPc);
+      }
+    }
+
+    // Print indirect targets to type id mapping.
+    for (const auto &El : TypeIdToIndirTargets) {
+      uint64_t TypeId = El.first;
+      outs() << "\n" << format("%lx", TypeId);
+      for (uint64_t IndirTargetPc : El.second)
+        outs() << " " << format("%lx", IndirTargetPc);
+    }
+
+    // Print indirect calls to type id mapping. Any indirect call without a
+    // type id can be deduced by comparing this list to indirect call sites
+    // list.
+    outs() << "\n\nINDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])";
+    for (const auto &El : TypeIdToIndirCallSites) {
+      uint64_t TypeId = El.first;
+      outs() << "\n" << format("%lx", TypeId);
+      for (uint64_t IndirCallSitePc : El.second)
+        outs() << " " << format("%lx", IndirCallSitePc);
+    }
+  }
+
+  // Print function entry to indirect call site addresses mapping from disasm.
+  outs() << "\n\nINDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])";
+  for (const auto &El : FuncInfo) {
+    auto CallerPc = El.first;
+    auto FuncIndirCallSites = El.second.IndirectCallSites;
+    if (!FuncIndirCallSites.empty()) {
+      outs() << "\n" << format("%lx", CallerPc);
+      for (auto IndirCallSitePc : FuncIndirCallSites)
+        outs() << " " << format("%lx", IndirCallSitePc);
+    }
+  }
+
+  // Print function entry to direct call site and target function entry
+  // addresses mapping from disasm.
+  outs()
+      << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, 
TARGET_ADDR),])";
+  for (const auto &El : FuncInfo) {
+    auto CallerPc = El.first;
+    auto FuncDirCallSites = El.second.DirectCallSites;
+    if (!FuncDirCallSites.empty()) {
+      outs() << "\n" << format("%lx", CallerPc);
+      for (const FunctionInfo::DirectCallSite &DCS : FuncDirCallSites) {
+        outs() << " " << format("%lx", DCS.CallSite) << " "
+               << format("%lx", DCS.Callee);
+      }
+    }
+  }
+
+  // Print function entry pc to function name mapping.
+  // outs() << "\n\nFUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)";
+  // for (const auto &El : FuncInfo) {
+  //   uint64_t EntryPc = El.first;
+  //   const auto &Name = El.second.Name;
+  //   outs() << "\n" << format("%lx", EntryPc) << " " << Name;
+  // }
+  outs() << "\n";
+}
+
 static void printUnwindInfo(const ObjectFile *O) {
   outs() << "Unwind info:\n\n";
 
@@ -3415,6 +3636,8 @@ static void dumpObject(ObjectFile *O, const Archive *A = 
nullptr,
     printRawClangAST(O);
   if (FaultMapSection)
     printFaultMaps(O);
+  if (CallGraphInfo)
+    printCallGraphInfo(O);
   if (Offloading)
     dumpOffloadBinary(*O, StringRef(ArchName));
 }
@@ -3581,6 +3804,7 @@ static void parseObjdumpOptions(const 
llvm::opt::InputArgList &InputArgs) {
   AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers);
   ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str();
   ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers);
+  CallGraphInfo = InputArgs.hasArg(OBJDUMP_call_graph_info);
   Demangle = InputArgs.hasArg(OBJDUMP_demangle);
   Disassemble = InputArgs.hasArg(OBJDUMP_disassemble);
   DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all);
@@ -3822,6 +4046,7 @@ int llvm_objdump_main(int argc, char **argv, const 
llvm::ToolContext &) {
       !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
       !Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
       !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading &&
+      !CallGraphInfo &&
       !(MachOOpt &&
         (Bind || DataInCode || ChainedFixups || DyldInfo || DylibId ||
          DylibsUsed || ExportsTrie || FirstPrivateHeader ||

>From f0f1da44dbc1baded75e445430df04af0788078d Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Tue, 16 Sep 2025 00:07:34 +0000
Subject: [PATCH 09/12] Address review comments.

---
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp | 25 ++++++++++++----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index d5ecc4c5e835d..55eaeeea2de1e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1879,7 +1879,8 @@ void AsmPrinter::emitCallsiteLabelsForCallgraph(
     FunctionInfo &FuncInfo,
     const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
     const MachineInstr &MI) {
-  assert(MI.isCall() && "Callsite labels are meant for call instruction 
only.");
+  assert(MI.isCall() &&
+         "Callsite labels are meant for call instructions only.");
   const MachineOperand &CalleeOperand = MI.getOperand(0);
   if (CalleeOperand.isGlobal() || CalleeOperand.isSymbol()) {
     // Handle direct calls.
@@ -1898,16 +1899,18 @@ void AsmPrinter::emitCallsiteLabelsForCallgraph(
     MCSymbol *S = MF->getContext().createTempSymbol();
     OutStreamer->emitLabel(S);
     FuncInfo.DirectCallSiteLabels.emplace_back(S, CalleeSymbol);
-  } else {
-    // Handle indirect callsite info.
-    // Only indirect calls have type identifiers set.
-    const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
-    for (ConstantInt *CalleeTypeId : CallSiteInfo->second.CalleeTypeIds) {
-      MCSymbol *S = MF->getContext().createTempSymbol();
-      OutStreamer->emitLabel(S);
-      uint64_t CalleeTypeIdVal = CalleeTypeId->getZExtValue();
-      FuncInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
-    }
+    return; // Early exit after handling the direct call instruction.
+  }
+  const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
+  if (CallSiteInfo == CallSitesInfoMap.end())
+    return;
+  // Handle indirect callsite info.
+  // Only indirect calls have type identifiers set.
+  for (ConstantInt *CalleeTypeId : CallSiteInfo->second.CalleeTypeIds) {
+    MCSymbol *S = MF->getContext().createTempSymbol();
+    OutStreamer->emitLabel(S);
+    uint64_t CalleeTypeIdVal = CalleeTypeId->getZExtValue();
+    FuncInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
   }
 }
 

>From 2f526631609b71f10bd08d898ad8dcab5d35d4fe Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Thu, 18 Sep 2025 01:19:02 +0000
Subject: [PATCH 10/12] Make callees per function unique for efficiency.

---
 llvm/include/llvm/CodeGen/AsmPrinter.h        | 43 ++++++++++---------
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp    | 37 ++++++++--------
 .../X86/call-graph-section-assembly.ll        | 13 +++---
 3 files changed, 44 insertions(+), 49 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h 
b/llvm/include/llvm/CodeGen/AsmPrinter.h
index 4803f852df518..dde351abb0e5f 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -18,6 +18,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
 #include "llvm/Analysis/StaticDataProfileInfo.h"
 #include "llvm/BinaryFormat/Dwarf.h"
@@ -192,29 +193,29 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
 
   /// Store symbols and type identifiers used to create callgraph section
   /// entries related to a function.
-  struct FunctionInfo {
+  struct FunctionCallGraphInfo {
     /// Numeric type identifier used in callgraph section for indirect calls
     /// and targets.
     using CGTypeId = uint64_t;
 
-    /// Enumeration of function kinds, and their mapping to function kind 
values
-    /// stored in callgraph section entries.
-    /// Must match the enum in llvm/tools/llvm-objdump/llvm-objdump.cpp.
-    enum class FunctionKind : uint64_t {
-      /// Function cannot be target to indirect calls.
-      NOT_INDIRECT_TARGET = 0,
-
-      /// Function may be target to indirect calls but its type id is unknown.
-      INDIRECT_TARGET_UNKNOWN_TID = 1,
-
-      /// Function may be target to indirect calls and its type id is known.
-      INDIRECT_TARGET_KNOWN_TID = 2,
-    };
-
     /// Map type identifiers to callsite labels. Labels are generated for each
     /// indirect callsite in the function.
     SmallVector<std::pair<CGTypeId, MCSymbol *>> CallSiteLabels;
-    SmallVector<std::pair<MCSymbol *, MCSymbol *>> DirectCallSiteLabels;
+    SmallSet<MCSymbol *, 4> DirectCallees;
+  };
+
+  /// Enumeration of function kinds, and their mapping to function kind values
+  /// stored in callgraph section entries.
+  /// Must match the enum in llvm/tools/llvm-objdump/llvm-objdump.cpp.
+  enum class FunctionKind : uint64_t {
+    /// Function cannot be target to indirect calls.
+    NOT_INDIRECT_TARGET = 0,
+
+    /// Function may be target to indirect calls but its type id is unknown.
+    INDIRECT_TARGET_UNKNOWN_TID = 1,
+
+    /// Function may be target to indirect calls and its type id is known.
+    INDIRECT_TARGET_KNOWN_TID = 2,
   };
 
   enum CallGraphSectionFormatVersion : uint64_t {
@@ -386,10 +387,10 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// are available. Returns empty string otherwise.
   StringRef getConstantSectionSuffix(const Constant *C) const;
 
-  /// Generate and emit labels for callees of all callsites which will
-  /// be used to populate the .callgraph section.
-  void emitCallsiteLabelsForCallgraph(
-      FunctionInfo &FuncInfo,
+  /// Iff MI is an indirect call, generate and emit a label after the 
callsites which will
+  /// be used to populate the .callgraph section. For direct callsites add the 
callee symbol to direct callsites list of FuncCGInfo.
+  void handleCallsiteForCallgraph(
+      FunctionCallGraphInfo &FuncCGInfo,
       const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
       const MachineInstr &MI);
 
@@ -480,7 +481,7 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitKCFITrapEntry(const MachineFunction &MF, const MCSymbol *Symbol);
   virtual void emitKCFITypeId(const MachineFunction &MF);
 
-  void emitCallGraphSection(const MachineFunction &MF, FunctionInfo &FuncInfo);
+  void emitCallGraphSection(const MachineFunction &MF, FunctionCallGraphInfo 
&FuncCGInfo);
 
   void emitPseudoProbe(const MachineInstr &MI);
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 55eaeeea2de1e..010e243cf0e62 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1673,7 +1673,7 @@ static ConstantInt *extractNumericCGTypeId(const Function 
&F) {
 
 /// Emits .callgraph section.
 void AsmPrinter::emitCallGraphSection(const MachineFunction &MF,
-                                      FunctionInfo &FuncInfo) {
+                                      FunctionCallGraphInfo &FuncCGInfo) {
   if (!MF.getTarget().Options.EmitCallGraphSection)
     return;
 
@@ -1712,35 +1712,34 @@ void AsmPrinter::emitCallGraphSection(const 
MachineFunction &MF,
   // Emit function kind, and type id if available.
   if (!IsIndirectTarget) {
     OutStreamer->emitInt64(
-        
static_cast<uint64_t>(FunctionInfo::FunctionKind::NOT_INDIRECT_TARGET));
+        static_cast<uint64_t>(FunctionKind::NOT_INDIRECT_TARGET));
   } else {
     if (const auto *TypeId = extractNumericCGTypeId(F)) {
       OutStreamer->emitInt64(static_cast<uint64_t>(
-          FunctionInfo::FunctionKind::INDIRECT_TARGET_KNOWN_TID));
+          FunctionKind::INDIRECT_TARGET_KNOWN_TID));
       OutStreamer->emitInt64(TypeId->getZExtValue());
     } else {
       OutStreamer->emitInt64(static_cast<uint64_t>(
-          FunctionInfo::FunctionKind::INDIRECT_TARGET_UNKNOWN_TID));
+          FunctionKind::INDIRECT_TARGET_UNKNOWN_TID));
     }
   }
 
   // Emit callsite labels, where each element is a pair of type id and
   // indirect callsite pc.
-  const auto &CallSiteLabels = FuncInfo.CallSiteLabels;
+  const auto &CallSiteLabels = FuncCGInfo.CallSiteLabels;
   OutStreamer->emitInt64(CallSiteLabels.size());
   for (const auto &[TypeId, Label] : CallSiteLabels) {
     OutStreamer->emitInt64(TypeId);
     OutStreamer->emitSymbolValue(Label, TM.getProgramPointerSize());
   }
-  FuncInfo.CallSiteLabels.clear();
+  FuncCGInfo.CallSiteLabels.clear();
 
-  const auto &DirectCallSiteLabels = FuncInfo.DirectCallSiteLabels;
-  OutStreamer->emitInt64(DirectCallSiteLabels.size());
-  for (const auto &[CallSiteAddrLabel, CalleeSymbol] : DirectCallSiteLabels) {
-    OutStreamer->emitSymbolValue(CallSiteAddrLabel, 
TM.getProgramPointerSize());
+  const auto &DirectCallees = FuncCGInfo.DirectCallees;
+  OutStreamer->emitInt64(DirectCallees.size());
+  for (const auto &CalleeSymbol : DirectCallees) {
     OutStreamer->emitSymbolValue(CalleeSymbol, TM.getProgramPointerSize());
   }
-  FuncInfo.DirectCallSiteLabels.clear();
+  FuncCGInfo.DirectCallees.clear();
 
   OutStreamer->popSection();
 }
@@ -1875,8 +1874,8 @@ static StringRef getMIMnemonic(const MachineInstr &MI, 
MCStreamer &Streamer) {
   return Name;
 }
 
-void AsmPrinter::emitCallsiteLabelsForCallgraph(
-    FunctionInfo &FuncInfo,
+void AsmPrinter::handleCallsiteForCallgraph(
+    FunctionCallGraphInfo &FuncCGInfo,
     const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
     const MachineInstr &MI) {
   assert(MI.isCall() &&
@@ -1896,9 +1895,7 @@ void AsmPrinter::emitCallsiteLabelsForCallgraph(
       llvm_unreachable(
           "Expected to only handle direct call instructions here.");
     }
-    MCSymbol *S = MF->getContext().createTempSymbol();
-    OutStreamer->emitLabel(S);
-    FuncInfo.DirectCallSiteLabels.emplace_back(S, CalleeSymbol);
+    FuncCGInfo.DirectCallees.insert(CalleeSymbol);
     return; // Early exit after handling the direct call instruction.
   }
   const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
@@ -1910,7 +1907,7 @@ void AsmPrinter::emitCallsiteLabelsForCallgraph(
     MCSymbol *S = MF->getContext().createTempSymbol();
     OutStreamer->emitLabel(S);
     uint64_t CalleeTypeIdVal = CalleeTypeId->getZExtValue();
-    FuncInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
+    FuncCGInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
   }
 }
 
@@ -1960,7 +1957,7 @@ void AsmPrinter::emitFunctionBody() {
     MBBSectionRanges[MF->front().getSectionID()] =
         MBBSectionRange{CurrentFnBegin, nullptr};
 
-  FunctionInfo FuncInfo;
+  FunctionCallGraphInfo FuncCGInfo;
   const auto &CallSitesInfoMap = MF->getCallSitesInfo();
   for (auto &MBB : *MF) {
     // Print a label for the basic block.
@@ -2097,7 +2094,7 @@ void AsmPrinter::emitFunctionBody() {
         OutStreamer->emitLabel(createCallsiteEndSymbol(MBB));
 
       if (TM.Options.EmitCallGraphSection && MI.isCall())
-        emitCallsiteLabelsForCallgraph(FuncInfo, CallSitesInfoMap, MI);
+        handleCallsiteForCallgraph(FuncCGInfo, CallSitesInfoMap, MI);
 
       // If there is a post-instruction symbol, emit a label for it here.
       if (MCSymbol *S = MI.getPostInstrSymbol())
@@ -2279,7 +2276,7 @@ void AsmPrinter::emitFunctionBody() {
   emitStackSizeSection(*MF);
 
   // Emit section containing call graph metadata.
-  emitCallGraphSection(*MF, FuncInfo);
+  emitCallGraphSection(*MF, FuncCGInfo);
 
   // Emit .su file containing function stack size information.
   emitStackUsage(*MF);
diff --git a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll 
b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
index 950e2f0bdd17a..6a22ee505b6c3 100644
--- a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
+++ b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
@@ -15,21 +15,21 @@ declare !type !2 ptr @direct_baz(ptr)
 ; CHECK-NEXT: [[LABEL_FUNC:\.Lfunc_begin[0-9]+]]:
 define ptr @ball() {
 entry:
+  call void @direct_foo()
   %fp_foo_val = load ptr, ptr null, align 8
    ; CHECK: [[LABEL_TMP0:\.L.*]]:
-  call void (...) %fp_foo_val(), !callee_type !0
-   ; CHECK: [[LABEL_TMP_DIRECT0:\.L.*]]:
+  call void (...) %fp_foo_val(), !callee_type !0   
   call void @direct_foo()
   %fp_bar_val = load ptr, ptr null, align 8
   ; CHECK: [[LABEL_TMP1:\.L.*]]:
-  %call_fp_bar = call i32 %fp_bar_val(i8 0), !callee_type !2
-  ; CHECK: [[LABEL_TMP_DIRECT1:\.L.*]]:
+  %call_fp_bar = call i32 %fp_bar_val(i8 0), !callee_type !2  
   %call_fp_bar_direct = call i32 @direct_bar(i8 1)
   %fp_baz_val = load ptr, ptr null, align 8
   ; CHECK: [[LABEL_TMP2:\.L.*]]:
   %call_fp_baz = call ptr %fp_baz_val(ptr null), !callee_type !4
-  ; CHECK: [[LABEL_TMP_DIRECT2:\.L.*]]:
+  call void @direct_foo()
   %call_fp_baz_direct = call ptr @direct_baz(ptr null)
+  call void @direct_foo()
   ret ptr %call_fp_baz
 }
 
@@ -56,9 +56,6 @@ entry:
 ; CHECK-NEXT: .quad   [[LABEL_TMP2]]
 ;; Test for number of direct calls and {callsite_label, callee} pairs.
 ; CHECK-NEXT: .quad    3
-; CHECK-NEXT: .quad    [[LABEL_TMP_DIRECT0]]
 ; CHECK-NEXT: .quad    direct_foo
-; CHECK-NEXT: .quad    [[LABEL_TMP_DIRECT1]]
 ; CHECK-NEXT: .quad    direct_bar
-; CHECK-NEXT: .quad    [[LABEL_TMP_DIRECT2]]
 ; CHECK-NEXT: .quad    direct_baz

>From 5c718d6574e00a171bffc1fa5e0532af3550c0d6 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Thu, 18 Sep 2025 01:19:27 +0000
Subject: [PATCH 11/12] Formatting changes

---
 llvm/include/llvm/CodeGen/AsmPrinter.h     | 10 ++++++----
 llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp |  8 ++++----
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h 
b/llvm/include/llvm/CodeGen/AsmPrinter.h
index dde351abb0e5f..2383227048891 100644
--- a/llvm/include/llvm/CodeGen/AsmPrinter.h
+++ b/llvm/include/llvm/CodeGen/AsmPrinter.h
@@ -17,8 +17,8 @@
 
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
 #include "llvm/Analysis/StaticDataProfileInfo.h"
 #include "llvm/BinaryFormat/Dwarf.h"
@@ -387,8 +387,9 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   /// are available. Returns empty string otherwise.
   StringRef getConstantSectionSuffix(const Constant *C) const;
 
-  /// Iff MI is an indirect call, generate and emit a label after the 
callsites which will
-  /// be used to populate the .callgraph section. For direct callsites add the 
callee symbol to direct callsites list of FuncCGInfo.
+  /// Iff MI is an indirect call, generate and emit a label after the callsites
+  /// which will be used to populate the .callgraph section. For direct
+  /// callsites add the callee symbol to direct callsites list of FuncCGInfo.
   void handleCallsiteForCallgraph(
       FunctionCallGraphInfo &FuncCGInfo,
       const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
@@ -481,7 +482,8 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
   void emitKCFITrapEntry(const MachineFunction &MF, const MCSymbol *Symbol);
   virtual void emitKCFITypeId(const MachineFunction &MF);
 
-  void emitCallGraphSection(const MachineFunction &MF, FunctionCallGraphInfo 
&FuncCGInfo);
+  void emitCallGraphSection(const MachineFunction &MF,
+                            FunctionCallGraphInfo &FuncCGInfo);
 
   void emitPseudoProbe(const MachineInstr &MI);
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp 
b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index 010e243cf0e62..ece4878785ebd 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -1715,12 +1715,12 @@ void AsmPrinter::emitCallGraphSection(const 
MachineFunction &MF,
         static_cast<uint64_t>(FunctionKind::NOT_INDIRECT_TARGET));
   } else {
     if (const auto *TypeId = extractNumericCGTypeId(F)) {
-      OutStreamer->emitInt64(static_cast<uint64_t>(
-          FunctionKind::INDIRECT_TARGET_KNOWN_TID));
+      OutStreamer->emitInt64(
+          static_cast<uint64_t>(FunctionKind::INDIRECT_TARGET_KNOWN_TID));
       OutStreamer->emitInt64(TypeId->getZExtValue());
     } else {
-      OutStreamer->emitInt64(static_cast<uint64_t>(
-          FunctionKind::INDIRECT_TARGET_UNKNOWN_TID));
+      OutStreamer->emitInt64(
+          static_cast<uint64_t>(FunctionKind::INDIRECT_TARGET_UNKNOWN_TID));
     }
   }
 

>From 18e0ad8f3a370bb6f3fae7c3e7ef85e98947cf81 Mon Sep 17 00:00:00 2001
From: prabhukr <prabh...@google.com>
Date: Thu, 18 Sep 2025 01:23:04 +0000
Subject: [PATCH 12/12] Update test comment

---
 llvm/test/CodeGen/X86/call-graph-section-assembly.ll | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll 
b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
index 6a22ee505b6c3..f0dbc31222c89 100644
--- a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
+++ b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
@@ -1,9 +1,8 @@
-;; Test if temporary labels are generated for each callsite.
+;; Test if temporary labels are generated for each indirect callsite.
 ;; Test if the .callgraph section contains the MD5 hash of callees' type (type 
id)
 ;; is correctly paired with its corresponding temporary label generated for 
indirect
 ;; call sites annotated with !callee_type metadata.
-;; Test if the .callgraph section contains direct callsite temporary labels 
paired
-;; correctly with the corresponding callee symbol.
+;; Test if the .callgraph section contains unique direct callees.
 
 ; RUN: llc -mtriple=x86_64-unknown-linux --call-graph-section -o - < %s | 
FileCheck %s
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to