[clang] [llvm] [clang][DebugInfo] Add virtual call-site target information in DWARF. (PR #167666)
Carlos Alberto Enciso via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 14 00:14:51 PST 2025
https://github.com/CarlosAlbertoEnciso updated https://github.com/llvm/llvm-project/pull/167666
>From 18994903dbd01e84885e124ea40253b6db52afff Mon Sep 17 00:00:00 2001
From: Carlos Alberto Enciso <Carlos.Enciso at sony.com>
Date: Wed, 12 Nov 2025 09:57:39 +0000
Subject: [PATCH 1/4] [clang][DebugInfo] Add virtual call-site target
information in DWARF.
Given the test case (CBase is a structure or class):
int function(CBase *Input) {
int Output = Input->foo();
return Output;
}
and using '-emit-call-site-info' with llc, the following DWARF
debug information is produced for the indirect call 'Input->foo()':
0x51: DW_TAG_call_site
DW_AT_call_target (DW_OP_reg0 RAX)
DW_AT_call_return_pc (0x0e)
This patch generates an extra 'DW_AT_call_origin' to identify
the target call 'CBase::foo'.
0x51: DW_TAG_call_site
DW_AT_call_target (DW_OP_reg0 RAX)
DW_AT_call_return_pc (0x0e)
-----------------------------------------------
DW_AT_call_origin (0x71 "_ZN5CBase3fooEb")
-----------------------------------------------
0x61: DW_TAG_class_type
DW_AT_name ("CBase")
...
0x71: DW_TAG_subprogram
DW_AT_linkage_name ("_ZN5CBase3fooEb")
DW_AT_name ("foo")
The extra call site information is generated only for the SCE debugger:
'-Xclang -debugger-tuning=sce'
---
clang/lib/CodeGen/CGCall.cpp | 3 +
clang/lib/CodeGen/CGDebugInfo.cpp | 114 +++++++++++++++-
clang/lib/CodeGen/CGDebugInfo.h | 28 ++++
clang/lib/CodeGen/CodeGenFunction.cpp | 2 +
clang/test/DebugInfo/CXX/callsite-base.cpp | 42 ++++++
clang/test/DebugInfo/CXX/callsite-derived.cpp | 85 ++++++++++++
clang/test/DebugInfo/CXX/callsite-edges.cpp | 125 ++++++++++++++++++
.../clang_llvm_roundtrip/callsite-dwarf.cpp | 71 ++++++++++
llvm/include/llvm/CodeGen/MachineFunction.h | 6 +
llvm/include/llvm/IR/FixedMetadataKinds.def | 1 +
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 27 +++-
llvm/lib/CodeGen/MIRPrinter.cpp | 2 +-
llvm/lib/CodeGen/MachineFunction.cpp | 3 +
llvm/lib/Target/X86/X86ISelLoweringCall.cpp | 11 +-
14 files changed, 514 insertions(+), 6 deletions(-)
create mode 100644 clang/test/DebugInfo/CXX/callsite-base.cpp
create mode 100644 clang/test/DebugInfo/CXX/callsite-derived.cpp
create mode 100644 clang/test/DebugInfo/CXX/callsite-edges.cpp
create mode 100644 cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index efacb3cc04c01..5b5ed52e1d554 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5958,6 +5958,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
}
}
+ if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
+ DI->addCallTarget(CI->getCalledFunction(), CalleeDecl, CI);
+
// 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
// Control Flow Guard checks should not be added, even if the call is inlined.
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index bda7b7487f59b..44d2bf5527c7f 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2430,6 +2430,9 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
SPCache[Method->getCanonicalDecl()].reset(SP);
+ // Add the method declaration as a call target.
+ addCallTarget(MethodLinkageName, SP, /*CI=*/nullptr);
+
return SP;
}
@@ -4955,6 +4958,99 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
Fn->setSubprogram(SP);
}
+bool CGDebugInfo::generateCallSiteForPS() const {
+ // The added call target will be available only for SCE targets.
+ if (CGM.getCodeGenOpts().getDebuggerTuning() != llvm::DebuggerKind::SCE)
+ return false;
+
+ // Check general conditions for call site generation.
+ return (getCallSiteRelatedAttrs() != llvm::DINode::FlagZero);
+}
+
+// Set the 'call_target' metadata in the call instruction.
+void CGDebugInfo::addCallTargetMetadata(llvm::MDNode *MD, llvm::CallBase *CI) {
+ if (!MD || !CI)
+ return;
+ CI->setMetadata(llvm::LLVMContext::MD_call_target, MD);
+}
+
+// Finalize call_target generation.
+void CGDebugInfo::finalizeCallTarget() {
+ if (!generateCallSiteForPS())
+ return;
+
+ for (auto &E : CallTargetCache) {
+ for (const auto &WH : E.second.second) {
+ llvm::CallBase *CI = dyn_cast_or_null<llvm::CallBase>(WH);
+ addCallTargetMetadata(E.second.first, CI);
+ }
+ }
+}
+
+void CGDebugInfo::addCallTarget(StringRef Name, llvm::MDNode *MD,
+ llvm::CallBase *CI) {
+ if (!generateCallSiteForPS())
+ return;
+
+ // Record only indirect calls.
+ if (CI && !CI->isIndirectCall())
+ return;
+
+ // Nothing to do.
+ if (Name.empty())
+ return;
+
+ auto It = CallTargetCache.find(Name);
+ if (It == CallTargetCache.end()) {
+ // First time we see 'Name'. Insert record for later finalize.
+ InstrList List;
+ if (CI)
+ List.push_back(CI);
+ CallTargetCache.try_emplace(Name, MD, std::move(List));
+ } else {
+ if (MD)
+ It->second.first.reset(MD);
+ if (CI) {
+ InstrList &List = It->second.second;
+ List.push_back(CI);
+ }
+ }
+}
+
+void CGDebugInfo::addCallTarget(llvm::Function *F, const FunctionDecl *FD,
+ llvm::CallBase *CI) {
+ if (!generateCallSiteForPS())
+ return;
+
+ if (!F && !FD)
+ return;
+
+ // Ignore method types that never can be indirect calls.
+ if (!F && (isa<CXXConstructorDecl>(FD) || isa<CXXDestructorDecl>(FD) ||
+ FD->hasAttr<CUDAGlobalAttr>()))
+ return;
+
+ StringRef Name = (F && F->hasName()) ? F->getName() : CGM.getMangledName(FD);
+ addCallTarget(Name, /*MD=*/nullptr, CI);
+}
+
+void CGDebugInfo::removeCallTarget(StringRef Name) {
+ if (!generateCallSiteForPS())
+ return;
+
+ auto It = CallTargetCache.find(Name);
+ if (It != CallTargetCache.end())
+ CallTargetCache.erase(It);
+}
+
+void CGDebugInfo::removeCallTarget(llvm::Function *F) {
+ if (!generateCallSiteForPS())
+ return;
+
+ if (F && F->hasName())
+ removeCallTarget(F->getName());
+}
+
void CGDebugInfo::EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke,
QualType CalleeType,
GlobalDecl CalleeGlobalDecl) {
@@ -4978,9 +5074,15 @@ void CGDebugInfo::EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke,
// If there is no DISubprogram attached to the function being called,
// create the one describing the function in order to have complete
// call site debug info.
- if (!CalleeDecl->isStatic() && !CalleeDecl->isInlined())
+ if (!CalleeDecl->isStatic() && !CalleeDecl->isInlined()) {
EmitFunctionDecl(CalleeGlobalDecl, CalleeDecl->getLocation(), CalleeType,
Func);
+ if (Func->getSubprogram()) {
+ // For each call instruction emitted, add the call site target metadata.
+ llvm::DISubprogram *SP = Func->getSubprogram();
+ addCallTarget(SP->getLinkageName(), SP, /*CI=*/nullptr);
+ }
+ }
}
void CGDebugInfo::EmitInlineFunctionStart(CGBuilderTy &Builder, GlobalDecl GD) {
@@ -5082,8 +5184,13 @@ void CGDebugInfo::EmitFunctionEnd(CGBuilderTy &Builder, llvm::Function *Fn) {
}
FnBeginRegionCount.pop_back();
- if (Fn && Fn->getSubprogram())
+ if (Fn && Fn->getSubprogram()) {
+ // For each call instruction emitted, add the call site target metadata.
+ llvm::DISubprogram *SP = Fn->getSubprogram();
+ addCallTarget(SP->getLinkageName(), SP, /*CI=*/nullptr);
+
DBuilder.finalizeSubprogram(Fn->getSubprogram());
+ }
}
CGDebugInfo::BlockByRefType
@@ -6498,6 +6605,9 @@ void CGDebugInfo::finalize() {
if (auto MD = TypeCache[RT])
DBuilder.retainType(cast<llvm::DIType>(MD));
+ // Generate call_target information.
+ finalizeCallTarget();
+
DBuilder.finalize();
}
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 2378bdd780b3b..fd71d958bc1f5 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -682,6 +682,15 @@ class CGDebugInfo {
/// that it is supported and enabled.
llvm::DINode::DIFlags getCallSiteRelatedAttrs() const;
+ /// Add call target information.
+ void addCallTarget(StringRef Name, llvm::MDNode *MD, llvm::CallBase *CI);
+ void addCallTarget(llvm::Function *F, const FunctionDecl *FD,
+ llvm::CallBase *CI);
+
+ /// Remove a call target entry for the given name or function.
+ void removeCallTarget(StringRef Name);
+ void removeCallTarget(llvm::Function *F);
+
private:
/// Amend \p I's DebugLoc with \p Group (its source atom group) and \p
/// Rank (lower nonzero rank is higher precedence). Does nothing if \p I
@@ -903,6 +912,25 @@ class CGDebugInfo {
/// If one exists, returns the linkage name of the specified \
/// (non-null) \c Method. Returns empty string otherwise.
llvm::StringRef GetMethodLinkageName(const CXXMethodDecl *Method) const;
+
+ /// For each 'DISuprogram' we store a list of call instructions 'CallBase'
+ /// that indirectly call such 'DISuprogram'. We use its linkage name to
+ /// update such list.
+ /// The 'CallTargetCache' is updated in the following scenarios:
+ /// - Both 'CallBase' and 'MDNode' are ready available.
+ /// - If only the 'CallBase' or 'MDNode' are are available, the partial
+ /// information is added and later is completed when the missing item
+ /// ('CallBase' or 'MDNode') is available.
+ using InstrList = llvm::SmallVector<llvm::WeakVH, 2>;
+ using CallTargetEntry = std::pair<llvm::TrackingMDNodeRef, InstrList>;
+ llvm::SmallDenseMap<StringRef, CallTargetEntry> CallTargetCache;
+
+ /// Generate call target information only for SIE debugger.
+ bool generateCallSiteForPS() const;
+
+ /// Add 'call_target' metadata to the 'call' instruction.
+ void addCallTargetMetadata(llvm::MDNode *MD, llvm::CallBase *CI);
+ void finalizeCallTarget();
};
/// A scoped helper to set the current debug location to the specified
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 88628530cf66b..6d83b5fdd0e1b 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1510,6 +1510,8 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
// Clear non-distinct debug info that was possibly attached to the function
// due to an earlier declaration without the nodebug attribute
Fn->setSubprogram(nullptr);
+ if (CGDebugInfo *DI = getDebugInfo())
+ DI->removeCallTarget(Fn);
// Disable debug info indefinitely for this function
DebugInfo = nullptr;
}
diff --git a/clang/test/DebugInfo/CXX/callsite-base.cpp b/clang/test/DebugInfo/CXX/callsite-base.cpp
new file mode 100644
index 0000000000000..ed7c455ced9d7
--- /dev/null
+++ b/clang/test/DebugInfo/CXX/callsite-base.cpp
@@ -0,0 +1,42 @@
+// Simple class with only virtual methods: inlined and not-inlined
+// We check for a generated 'call_target' for:
+// - 'one', 'two' and 'three'.
+
+class CBase {
+public:
+ virtual void one();
+ virtual void two();
+ virtual void three() {}
+};
+void CBase::one() {}
+
+void bar(CBase *Base) {
+ Base->one();
+ Base->two();
+ Base->three();
+
+ CBase B;
+ B.one();
+}
+
+// RUN: %clang_cc1 -debugger-tuning=sce -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-BASE
+
+// CHECK-BASE: define {{.*}} @_Z3barP5CBase{{.*}} {
+// CHECK-BASE-DAG: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_ONE:![0-9]+]]
+// CHECK-BASE-DAG: call void %3{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_TWO:![0-9]+]]
+// CHECK-BASE-DAG: call void %5{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_THREE:![0-9]+]]
+// CHECK-BASE-DAG: call void @_ZN5CBaseC2Ev{{.*}} !dbg {{![0-9]+}}
+// CHECK-BASE-DAG: call void @_ZN5CBase3oneEv{{.*}} !dbg {{![0-9]+}}
+// CHECK-BASE: }
+
+// CHECK-BASE-DAG: [[BASE_ONE]] = {{.*}}!DISubprogram(name: "one", linkageName: "_ZN5CBase3oneEv"
+// CHECK-BASE-DAG: [[BASE_TWO]] = {{.*}}!DISubprogram(name: "two", linkageName: "_ZN5CBase3twoEv"
+// CHECK-BASE-DAG: [[BASE_THREE]] = {{.*}}!DISubprogram(name: "three", linkageName: "_ZN5CBase5threeEv"
+
+// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-BASE-NON
+
+// CHECK-BASE-NON: define {{.*}} @_Z3barP5CBase{{.*}} {
+// CHECK-BASE-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
+// CHECK-BASE-NON-DAG: call void %3{{.*}} !dbg {{![0-9]+}}
+// CHECK-BASE-NON-DAG: call void %5{{.*}} !dbg {{![0-9]+}}
+// CHECK-BASE-NON: }
diff --git a/clang/test/DebugInfo/CXX/callsite-derived.cpp b/clang/test/DebugInfo/CXX/callsite-derived.cpp
new file mode 100644
index 0000000000000..b1019c4b252a4
--- /dev/null
+++ b/clang/test/DebugInfo/CXX/callsite-derived.cpp
@@ -0,0 +1,85 @@
+// Simple base and derived class with virtual and static methods:
+// We check for a generated 'call_target' for:
+// - 'one', 'two' and 'three'.
+
+class CBase {
+public:
+ virtual void one(bool Flag) {}
+ virtual void two(int P1, char P2) {}
+ static void three();
+};
+
+void CBase::three() {
+}
+void bar(CBase *Base);
+
+void foo(CBase *Base) {
+ CBase::three();
+}
+
+class CDerived : public CBase {
+public:
+ void one(bool Flag) {}
+ void two(int P1, char P2) {}
+};
+void foo(CDerived *Derived);
+
+int main() {
+ CBase B;
+ bar(&B);
+
+ CDerived D;
+ foo(&D);
+
+ return 0;
+}
+
+void bar(CBase *Base) {
+ Base->two(77, 'a');
+}
+
+void foo(CDerived *Derived) {
+ Derived->one(true);
+}
+
+// RUN: %clang_cc1 -debugger-tuning=sce -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-DERIVED
+
+// CHECK-DERIVED: define {{.*}} @_Z3fooP5CBase{{.*}} {
+// CHECK-DERIVED-DAG: call void @_ZN5CBase5threeEv{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED: }
+
+// CHECK-DERIVED: define {{.*}} @main{{.*}} {
+// CHECK-DERIVED-DAG: call void @_ZN5CBaseC1Ev{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED-DAG: call void @_Z3barP5CBase{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED-DAG: call void @_ZN8CDerivedC1Ev{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED-DAG: call void @_Z3fooP8CDerived{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED: }
+
+// CHECK-DERIVED: define {{.*}} @_ZN5CBaseC1Ev{{.*}} {
+// CHECK-DERIVED-DAG: call void @_ZN5CBaseC2Ev{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED: }
+
+// CHECK-DERIVED: define {{.*}} @_Z3barP5CBase{{.*}} {
+// CHECK-DERIVED-DAG: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_TWO:![0-9]+]]
+// CHECK-DERIVED: }
+
+// CHECK-DERIVED: define {{.*}} @_ZN8CDerivedC1Ev{{.*}} {
+// CHECK-DERIVED-DAG: call void @_ZN8CDerivedC2Ev{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED: }
+
+// CHECK-DERIVED: define {{.*}} @_Z3fooP8CDerived{{.*}} {
+// CHECK-DERIVED-DAG: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[DERIVED_ONE:![0-9]+]]
+// CHECK-DERIVED: }
+
+// CHECK-DERIVED-DAG: [[BASE_TWO]] = {{.*}}!DISubprogram(name: "two", linkageName: "_ZN5CBase3twoEic"
+// CHECK-DERIVED-DAG: [[DERIVED_ONE]] = {{.*}}!DISubprogram(name: "one", linkageName: "_ZN8CDerived3oneEb"
+
+// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-DERIVED-NON
+
+// CHECK-DERIVED-NON: define {{.*}} @_Z3barP5CBase{{.*}} {
+// CHECK-DERIVED-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED-NON: }
+
+// CHECK-DERIVED-NON: define {{.*}} @_Z3fooP8CDerived{{.*}} {
+// CHECK-DERIVED-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
+// CHECK-DERIVED-NON: }
diff --git a/clang/test/DebugInfo/CXX/callsite-edges.cpp b/clang/test/DebugInfo/CXX/callsite-edges.cpp
new file mode 100644
index 0000000000000..1d4ef29f4b357
--- /dev/null
+++ b/clang/test/DebugInfo/CXX/callsite-edges.cpp
@@ -0,0 +1,125 @@
+// Check edge cases:
+
+//---------------------------------------------------------------------
+// Method is declared but not defined in current CU - Fail.
+// No debug information entry is generated for 'one'.
+// Generate 'call_target' metadata only for 'two'.
+//---------------------------------------------------------------------
+class CEmpty {
+public:
+ virtual void one(bool Flag);
+ virtual void two(int P1, char P2);
+};
+
+void CEmpty::two(int P1, char P2) {
+}
+
+void edge_a(CEmpty *Empty) {
+ Empty->one(false);
+ Empty->two(77, 'a');
+}
+
+//---------------------------------------------------------------------
+// Pure virtual method but not defined in current CU - Pass.
+// Generate 'call_target' metadata for 'one' and 'two'.
+//---------------------------------------------------------------------
+class CBase {
+public:
+ virtual void one(bool Flag) = 0;
+ virtual void two(int P1, char P2);
+};
+
+void CBase::two(int P1, char P2) {
+}
+
+void edge_b(CBase *Base) {
+ Base->one(false);
+ Base->two(77, 'a');
+}
+
+//---------------------------------------------------------------------
+// Virtual method defined very deeply - Pass.
+// Generate 'call_target' metadata for 'd0', 'd1', 'd2' and 'd3'.
+//---------------------------------------------------------------------
+struct CDeep {
+ struct CD1 {
+ struct CD2 {
+ struct CD3 {
+ virtual void d3(int P3);
+ };
+
+ CD3 D3;
+ virtual void d2(int P2);
+ };
+
+ CD2 D2;
+ virtual void d1(int P1);
+ };
+
+ CD1 D1;
+ virtual void d0(int P);
+};
+
+void CDeep::d0(int P) {}
+void CDeep::CD1::d1(int P1) {}
+void CDeep::CD1::CD2::d2(int P2) {}
+void CDeep::CD1::CD2::CD3::d3(int P3) {}
+
+void edge_c(CDeep *Deep) {
+ Deep->d0(0);
+
+ CDeep::CD1 *D1 = &Deep->D1;
+ D1->d1(1);
+
+ CDeep::CD1::CD2 *D2 = &D1->D2;
+ D2->d2(2);
+
+ CDeep::CD1::CD2::CD3 *D3 = &D2->D3;
+ D3->d3(3);
+}
+
+// RUN: %clang -Xclang -debugger-tuning=sce --target=x86_64-linux -Xclang -disable-llvm-passes -fno-discard-value-names -emit-llvm -S -g -O1 %s -o - | FileCheck %s -check-prefix CHECK-EDGES
+
+// CHECK-EDGES: define {{.*}} @_Z6edge_aP6CEmpty{{.*}} {
+// CHECK-EDGES-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-DAG: call void %3{{.*}} !dbg {{![0-9]+}}, !call_target [[CEMPTY_TWO:![0-9]+]]
+// CHECK-EDGES: }
+
+// CHECK-EDGES: define {{.*}} @_Z6edge_bP5CBase{{.*}} {
+// CHECK-EDGES-DAG: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[CBASE_ONE:![0-9]+]]
+// CHECK-EDGES-DAG: call void %3{{.*}} !dbg {{![0-9]+}}, !call_target [[CBASE_TWO:![0-9]+]]
+// CHECK-EDGES: }
+
+// CHECK-EDGES: define {{.*}} @_Z6edge_cP5CDeep{{.*}} {
+// CHECK-EDGES-DAG: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[CDEEP_D0:![0-9]+]]
+// CHECK-EDGES-DAG: call void %4{{.*}} !dbg {{![0-9]+}}, !call_target [[CDEEP_D1:![0-9]+]]
+// CHECK-EDGES-DAG: call void %7{{.*}} !dbg {{![0-9]+}}, !call_target [[CDEEP_D2:![0-9]+]]
+// CHECK-EDGES-DAG: call void %10{{.*}} !dbg {{![0-9]+}}, !call_target [[CDEEP_D3:![0-9]+]]
+// CHECK-EDGES: }
+
+// CHECK-EDGES-DAG: [[CEMPTY_TWO]] = {{.*}}!DISubprogram(name: "two", linkageName: "_ZN6CEmpty3twoEic"
+// CHECK-EDGES-DAG: [[CBASE_ONE]] = {{.*}}!DISubprogram(name: "one", linkageName: "_ZN5CBase3oneEb"
+// CHECK-EDGES-DAG: [[CBASE_TWO]] = {{.*}}!DISubprogram(name: "two", linkageName: "_ZN5CBase3twoEic"
+
+// CHECK-EDGES-DAG: [[CDEEP_D0]] = {{.*}}!DISubprogram(name: "d0", linkageName: "_ZN5CDeep2d0Ei"
+// CHECK-EDGES-DAG: [[CDEEP_D1]] = {{.*}}!DISubprogram(name: "d1", linkageName: "_ZN5CDeep3CD12d1Ei"
+// CHECK-EDGES-DAG: [[CDEEP_D2]] = {{.*}}!DISubprogram(name: "d2", linkageName: "_ZN5CDeep3CD13CD22d2Ei"
+// CHECK-EDGES-DAG: [[CDEEP_D3]] = {{.*}}!DISubprogram(name: "d3", linkageName: "_ZN5CDeep3CD13CD23CD32d3Ei"
+
+// RUN: %clang --target=x86_64-linux -Xclang -disable-llvm-passes -fno-discard-value-names -emit-llvm -S -g -O1 %s -o - | FileCheck %s -check-prefix CHECK-EDGES-NON
+
+// CHECK-EDGES-NON: define {{.*}} @_Z6edge_aP6CEmpty{{.*}} {
+// CHECK-EDGES-NON-DAG: call void %3{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON: }
+
+// CHECK-EDGES-NON: define {{.*}} @_Z6edge_bP5CBase{{.*}} {
+// CHECK-EDGES-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON-DAG: call void %3{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON: }
+
+// CHECK-EDGES-NON: define {{.*}} @_Z6edge_cP5CDeep{{.*}} {
+// CHECK-EDGES-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON-DAG: call void %4{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON-DAG: call void %7{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON-DAG: call void %10{{.*}} !dbg {{![0-9]+}}
+// CHECK-EDGES-NON: }
diff --git a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
new file mode 100644
index 0000000000000..7374a355da549
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
@@ -0,0 +1,71 @@
+// Simple base and derived class with virtual:
+// We check for a generated 'DW_AT_call_origin' for 'foo', that corresponds
+// to the 'call_target' metadata added to the indirect call instruction.
+
+class CBaseOne {
+ virtual void foo(int &);
+};
+
+struct CDerivedOne : CBaseOne {
+ void foo(int &);
+};
+
+void CDerivedOne::foo(int &) {
+}
+
+struct CBaseTwo {
+ CDerivedOne *DerivedOne;
+};
+
+struct CDerivedTwo : CBaseTwo {
+ void bar(int &);
+};
+
+void CDerivedTwo::bar(int &j) {
+ DerivedOne->foo(j);
+}
+
+// The IR generated looks like:
+//
+// define dso_local void @_ZN11CDerivedTwo3barERi(...) !dbg !40 {
+// entry:
+// ..
+// %vtable = load ptr, ptr %0, align 8
+// %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
+// %2 = load ptr, ptr %vfn, align 8
+// call void %2(...), !dbg !65, !call_target !25
+// ret void
+// }
+//
+// !25 = !DISubprogram(name: "foo", linkageName: "_ZN11CDerivedOne3fooERi", ...)
+// !40 = !DISubprogram(name: "bar", linkageName: "_ZN11CDerivedTwo3barERi", ...)
+// !65 = !DILocation(line: 25, column: 15, scope: !40)
+
+// RUN: %clang --target=x86_64-unknown-linux -c -g -O1 \
+// RUN: -Xclang -debugger-tuning=sce %s -o - | \
+// RUN: llvm-dwarfdump --debug-info - | FileCheck %s --check-prefix=CHECK
+
+// CHECK: DW_TAG_compile_unit
+// CHECK: DW_TAG_structure_type
+// CHECK: DW_AT_name ("CDerivedOne")
+// CHECK: [[FOO_DCL:0x[a-f0-9]+]]: DW_TAG_subprogram
+// CHECK: DW_AT_name ("foo")
+// CHECK: DW_TAG_class_type
+// CHECK: DW_AT_name ("CBaseOne")
+// CHECK: [[FOO_DEF:0x[a-f0-9]+]]: DW_TAG_subprogram
+// CHECK: DW_AT_call_all_calls (true)
+// CHECK: DW_AT_specification ([[FOO_DCL]] "foo")
+// CHECK: DW_TAG_structure_type
+// CHECK: DW_AT_name ("CDerivedTwo")
+// CHECK: DW_TAG_subprogram
+// CHECK: DW_AT_name ("bar")
+// CHECK: DW_TAG_structure_type
+// CHECK: DW_AT_name ("CBaseTwo")
+// CHECK: DW_TAG_subprogram
+// CHECK: DW_AT_call_all_calls (true)
+// CHECK: DW_AT_specification (0x{{.*}} "bar")
+// CHECK: DW_TAG_call_site
+// CHECK: DW_AT_call_target (DW_OP_reg0 RAX)
+// CHECK: DW_AT_call_tail_call (true)
+// CHECK: DW_AT_call_pc (0x{{.*}})
+// CHECK: DW_AT_call_origin ([[FOO_DEF]] "foo")
diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h
index ef783f276b7d4..49aebb850d109 100644
--- a/llvm/include/llvm/CodeGen/MachineFunction.h
+++ b/llvm/include/llvm/CodeGen/MachineFunction.h
@@ -518,11 +518,17 @@ class LLVM_ABI MachineFunction {
/// Callee type ids.
SmallVector<ConstantInt *, 4> CalleeTypeIds;
+ /// 'call_target' metadata for the DISubprogram. It is the declaration
+ /// or definition of the target function and might be indirect.
+ MDNode *MD = nullptr;
+
CallSiteInfo() = default;
/// Extracts the numeric type id from the CallBase's callee_type Metadata,
/// and sets CalleeTypeIds. This is used as type id for the indirect call in
/// the call graph section.
+ /// Extracts the MDNode from the CallBase's call_target Metadata to be used
+ /// during the construction of the debug info call site entries.
LLVM_ABI CallSiteInfo(const CallBase &CB);
};
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index 74746cced6f23..00097e34e8d72 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -57,3 +57,4 @@ LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
+LLVM_FIXED_MD_KIND(MD_call_target, "call_target", 46)
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index fde48a30a8203..d1a3ad417cc0d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -916,6 +916,28 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
return true;
};
+ // Create call-target connections for indirect calls.
+ auto addCallSiteTargetForIndirectCalls = [&](const MachineInstr *MI,
+ DIE &CallSiteDIE) {
+ const MachineFunction *MF = MI->getMF();
+ const auto &CalleesMap = MF->getCallSitesInfo();
+ auto CSInfo = CalleesMap.find(MI);
+ // Get the information for the call instruction.
+ if (CSInfo == CalleesMap.end() || !CSInfo->second.MD)
+ return;
+
+ MDNode *MD = CSInfo->second.MD;
+ // Add DW_AT_call_origin with the 'call_target' metadata.
+ assert(!CallSiteDIE.findAttribute(dwarf::DW_AT_call_origin) &&
+ "DW_AT_call_origin already exists");
+ DIE *CalleeDIE = CU.getOrCreateSubprogramDIE(
+ dyn_cast<DISubprogram>(MD), nullptr);
+ assert(CalleeDIE && "Could not create DIE for call site entry origin");
+ CU.addDIEEntry(CallSiteDIE,
+ CU.getDwarf5OrGNUAttr(dwarf::DW_AT_call_origin),
+ *CalleeDIE);
+ };
+
// Emit call site entries for each call or tail call in the function.
for (const MachineBasicBlock &MBB : MF) {
for (const MachineInstr &MI : MBB.instrs()) {
@@ -1008,6 +1030,9 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP, CalleeDecl, IsTail,
PCAddr, CallAddr, CallReg, AllocSiteTy);
+ if (CallReg)
+ addCallSiteTargetForIndirectCalls(TopLevelCallMI, CallSiteDIE);
+
// Optionally emit call-site-param debug info.
if (emitDebugEntryValues()) {
ParamSet Params;
@@ -1415,7 +1440,7 @@ void DwarfDebug::finalizeModuleInfo() {
TLOF.getDwarfMacinfoSection()->getBeginSymbol());
}
}
- }
+ }
// Emit all frontend-produced Skeleton CUs, i.e., Clang modules.
for (auto *CUNode : MMI->getModule()->debug_compile_units())
diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp
index 1d54d72336860..e51f1d80d550f 100644
--- a/llvm/lib/CodeGen/MIRPrinter.cpp
+++ b/llvm/lib/CodeGen/MIRPrinter.cpp
@@ -537,7 +537,7 @@ static void convertCallSiteObjects(yaml::MachineFunction &YMF,
std::distance(CallI->getParent()->instr_begin(), CallI);
YmlCS.CallLocation = CallLocation;
- auto [ArgRegPairs, CalleeTypeIds] = CallSiteInfo;
+ auto [ArgRegPairs, CalleeTypeIds, MD] = CallSiteInfo;
// Construct call arguments and theirs forwarding register info.
for (auto ArgReg : ArgRegPairs) {
yaml::CallSiteInfo::ArgRegPair YmlArgReg;
diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index bfa5ab274c686..fedfd9be8b50f 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -700,6 +700,9 @@ bool MachineFunction::needsFrameMoves() const {
}
MachineFunction::CallSiteInfo::CallSiteInfo(const CallBase &CB) {
+ if (MDNode *Node = CB.getMetadata(llvm::LLVMContext::MD_call_target))
+ MD = Node;
+
// Numeric callee_type ids are only for indirect calls.
if (!CB.isIndirectCall())
return;
diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index a61bbe56d9c26..67a77a1495e7c 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -2050,8 +2050,15 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
if (CallConv == CallingConv::X86_INTR)
report_fatal_error("X86 interrupts may not be called directly");
- // Set type id for call site info.
- if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
+ // Set type id for call site info and metadata 'call_target'.
+ // We are filtering for:
+ // a) The call-graph-section use case that wants to know about indirect
+ // calls, and
+ // b) the SCE-tuing case where we want to annotate indirect calls.
+ if (CB &&
+ ((MF.getTarget().Options.EmitCallGraphSection && CB->isIndirectCall()) ||
+ (MF.getTarget().Options.EmitCallSiteInfo &&
+ MF.getTarget().Options.DebuggerTuning == DebuggerKind::SCE)))
CSInfo = MachineFunction::CallSiteInfo(*CB);
if (IsIndirectCall && !IsWin64 &&
>From 96678f361d99d68bf63d6cd2fceb49270aca6b54 Mon Sep 17 00:00:00 2001
From: Carlos Alberto Enciso <Carlos.Enciso at sony.com>
Date: Wed, 12 Nov 2025 11:56:27 +0000
Subject: [PATCH 2/4] [clang][DebugInfo] Add virtual call-site target
information in DWARF.
Fix clang-format issues.
---
.../clang_llvm_roundtrip/callsite-dwarf.cpp | 7 ++-----
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 7 +++----
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
index 7374a355da549..237f28e770cb2 100644
--- a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
+++ b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
@@ -10,8 +10,7 @@ struct CDerivedOne : CBaseOne {
void foo(int &);
};
-void CDerivedOne::foo(int &) {
-}
+void CDerivedOne::foo(int &) {}
struct CBaseTwo {
CDerivedOne *DerivedOne;
@@ -21,9 +20,7 @@ struct CDerivedTwo : CBaseTwo {
void bar(int &);
};
-void CDerivedTwo::bar(int &j) {
- DerivedOne->foo(j);
-}
+void CDerivedTwo::bar(int &j) { DerivedOne->foo(j); }
// The IR generated looks like:
//
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index d1a3ad417cc0d..5eb1b555e3005 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -930,11 +930,10 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
// Add DW_AT_call_origin with the 'call_target' metadata.
assert(!CallSiteDIE.findAttribute(dwarf::DW_AT_call_origin) &&
"DW_AT_call_origin already exists");
- DIE *CalleeDIE = CU.getOrCreateSubprogramDIE(
- dyn_cast<DISubprogram>(MD), nullptr);
+ DIE *CalleeDIE =
+ CU.getOrCreateSubprogramDIE(dyn_cast<DISubprogram>(MD), nullptr);
assert(CalleeDIE && "Could not create DIE for call site entry origin");
- CU.addDIEEntry(CallSiteDIE,
- CU.getDwarf5OrGNUAttr(dwarf::DW_AT_call_origin),
+ CU.addDIEEntry(CallSiteDIE, CU.getDwarf5OrGNUAttr(dwarf::DW_AT_call_origin),
*CalleeDIE);
};
>From 4ec56ac5169200fb853d6f351744188477c485d3 Mon Sep 17 00:00:00 2001
From: Carlos Alberto Enciso <Carlos.Enciso at sony.com>
Date: Thu, 13 Nov 2025 14:32:21 +0000
Subject: [PATCH 3/4] [clang][DebugInfo] Add virtual call-site target
information in DWARF.
Make the extra call site information available by default.
---
clang/lib/CodeGen/CGDebugInfo.cpp | 16 +++++---------
clang/lib/CodeGen/CGDebugInfo.h | 4 ++--
clang/test/DebugInfo/CXX/callsite-base.cpp | 12 +++-------
clang/test/DebugInfo/CXX/callsite-derived.cpp | 14 +++---------
clang/test/DebugInfo/CXX/callsite-edges.cpp | 22 +++----------------
.../clang_llvm_roundtrip/callsite-dwarf.cpp | 9 ++++----
llvm/lib/Target/X86/X86ISelLoweringCall.cpp | 11 +++++-----
.../MIR/X86/callsite-emit-calleetypeid.ll | 8 +++----
8 files changed, 29 insertions(+), 67 deletions(-)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 44d2bf5527c7f..15d15ad08fd99 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -4958,11 +4958,7 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
Fn->setSubprogram(SP);
}
-bool CGDebugInfo::generateCallSiteForPS() const {
- // The added call target will be available only for SCE targets.
- if (CGM.getCodeGenOpts().getDebuggerTuning() != llvm::DebuggerKind::SCE)
- return false;
-
+bool CGDebugInfo::generateVirtualCallSite() const {
// Check general conditions for call site generation.
return (getCallSiteRelatedAttrs() != llvm::DINode::FlagZero);
}
@@ -4976,7 +4972,7 @@ void CGDebugInfo::addCallTargetMetadata(llvm::MDNode *MD, llvm::CallBase *CI) {
// Finalize call_target generation.
void CGDebugInfo::finalizeCallTarget() {
- if (!generateCallSiteForPS())
+ if (!generateVirtualCallSite())
return;
for (auto &E : CallTargetCache) {
@@ -4989,7 +4985,7 @@ void CGDebugInfo::finalizeCallTarget() {
void CGDebugInfo::addCallTarget(StringRef Name, llvm::MDNode *MD,
llvm::CallBase *CI) {
- if (!generateCallSiteForPS())
+ if (!generateVirtualCallSite())
return;
// Record only indirect calls.
@@ -5019,7 +5015,7 @@ void CGDebugInfo::addCallTarget(StringRef Name, llvm::MDNode *MD,
void CGDebugInfo::addCallTarget(llvm::Function *F, const FunctionDecl *FD,
llvm::CallBase *CI) {
- if (!generateCallSiteForPS())
+ if (!generateVirtualCallSite())
return;
if (!F && !FD)
@@ -5035,7 +5031,7 @@ void CGDebugInfo::addCallTarget(llvm::Function *F, const FunctionDecl *FD,
}
void CGDebugInfo::removeCallTarget(StringRef Name) {
- if (!generateCallSiteForPS())
+ if (!generateVirtualCallSite())
return;
auto It = CallTargetCache.find(Name);
@@ -5044,7 +5040,7 @@ void CGDebugInfo::removeCallTarget(StringRef Name) {
}
void CGDebugInfo::removeCallTarget(llvm::Function *F) {
- if (!generateCallSiteForPS())
+ if (!generateVirtualCallSite())
return;
if (F && F->hasName())
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index fd71d958bc1f5..55f15a6d2b7ad 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -925,8 +925,8 @@ class CGDebugInfo {
using CallTargetEntry = std::pair<llvm::TrackingMDNodeRef, InstrList>;
llvm::SmallDenseMap<StringRef, CallTargetEntry> CallTargetCache;
- /// Generate call target information only for SIE debugger.
- bool generateCallSiteForPS() const;
+ /// Generate call target information.
+ bool generateVirtualCallSite() const;
/// Add 'call_target' metadata to the 'call' instruction.
void addCallTargetMetadata(llvm::MDNode *MD, llvm::CallBase *CI);
diff --git a/clang/test/DebugInfo/CXX/callsite-base.cpp b/clang/test/DebugInfo/CXX/callsite-base.cpp
index ed7c455ced9d7..7dbcbfe15b5cc 100644
--- a/clang/test/DebugInfo/CXX/callsite-base.cpp
+++ b/clang/test/DebugInfo/CXX/callsite-base.cpp
@@ -19,7 +19,9 @@ void bar(CBase *Base) {
B.one();
}
-// RUN: %clang_cc1 -debugger-tuning=sce -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-BASE
+// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm \
+// RUN: -debug-info-kind=constructor -dwarf-version=5 -O1 %s \
+// RUN: -o - | FileCheck %s -check-prefix CHECK-BASE
// CHECK-BASE: define {{.*}} @_Z3barP5CBase{{.*}} {
// CHECK-BASE-DAG: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_ONE:![0-9]+]]
@@ -32,11 +34,3 @@ void bar(CBase *Base) {
// CHECK-BASE-DAG: [[BASE_ONE]] = {{.*}}!DISubprogram(name: "one", linkageName: "_ZN5CBase3oneEv"
// CHECK-BASE-DAG: [[BASE_TWO]] = {{.*}}!DISubprogram(name: "two", linkageName: "_ZN5CBase3twoEv"
// CHECK-BASE-DAG: [[BASE_THREE]] = {{.*}}!DISubprogram(name: "three", linkageName: "_ZN5CBase5threeEv"
-
-// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-BASE-NON
-
-// CHECK-BASE-NON: define {{.*}} @_Z3barP5CBase{{.*}} {
-// CHECK-BASE-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
-// CHECK-BASE-NON-DAG: call void %3{{.*}} !dbg {{![0-9]+}}
-// CHECK-BASE-NON-DAG: call void %5{{.*}} !dbg {{![0-9]+}}
-// CHECK-BASE-NON: }
diff --git a/clang/test/DebugInfo/CXX/callsite-derived.cpp b/clang/test/DebugInfo/CXX/callsite-derived.cpp
index b1019c4b252a4..da918114de800 100644
--- a/clang/test/DebugInfo/CXX/callsite-derived.cpp
+++ b/clang/test/DebugInfo/CXX/callsite-derived.cpp
@@ -42,7 +42,9 @@ void foo(CDerived *Derived) {
Derived->one(true);
}
-// RUN: %clang_cc1 -debugger-tuning=sce -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-DERIVED
+// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm \
+// RUN: -debug-info-kind=constructor -dwarf-version=5 -O1 %s \
+// RUN: -o - | FileCheck %s -check-prefix CHECK-DERIVED
// CHECK-DERIVED: define {{.*}} @_Z3fooP5CBase{{.*}} {
// CHECK-DERIVED-DAG: call void @_ZN5CBase5threeEv{{.*}} !dbg {{![0-9]+}}
@@ -73,13 +75,3 @@ void foo(CDerived *Derived) {
// CHECK-DERIVED-DAG: [[BASE_TWO]] = {{.*}}!DISubprogram(name: "two", linkageName: "_ZN5CBase3twoEic"
// CHECK-DERIVED-DAG: [[DERIVED_ONE]] = {{.*}}!DISubprogram(name: "one", linkageName: "_ZN8CDerived3oneEb"
-
-// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm -debug-info-kind=constructor -dwarf-version=5 -O1 %s -o - | FileCheck %s -check-prefix CHECK-DERIVED-NON
-
-// CHECK-DERIVED-NON: define {{.*}} @_Z3barP5CBase{{.*}} {
-// CHECK-DERIVED-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
-// CHECK-DERIVED-NON: }
-
-// CHECK-DERIVED-NON: define {{.*}} @_Z3fooP8CDerived{{.*}} {
-// CHECK-DERIVED-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
-// CHECK-DERIVED-NON: }
diff --git a/clang/test/DebugInfo/CXX/callsite-edges.cpp b/clang/test/DebugInfo/CXX/callsite-edges.cpp
index 1d4ef29f4b357..d8d620de245db 100644
--- a/clang/test/DebugInfo/CXX/callsite-edges.cpp
+++ b/clang/test/DebugInfo/CXX/callsite-edges.cpp
@@ -78,7 +78,9 @@ void edge_c(CDeep *Deep) {
D3->d3(3);
}
-// RUN: %clang -Xclang -debugger-tuning=sce --target=x86_64-linux -Xclang -disable-llvm-passes -fno-discard-value-names -emit-llvm -S -g -O1 %s -o - | FileCheck %s -check-prefix CHECK-EDGES
+// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm \
+// RUN: -debug-info-kind=constructor -dwarf-version=5 -O1 %s \
+// RUN: -o - | FileCheck %s -check-prefix CHECK-EDGES
// CHECK-EDGES: define {{.*}} @_Z6edge_aP6CEmpty{{.*}} {
// CHECK-EDGES-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
@@ -105,21 +107,3 @@ void edge_c(CDeep *Deep) {
// CHECK-EDGES-DAG: [[CDEEP_D1]] = {{.*}}!DISubprogram(name: "d1", linkageName: "_ZN5CDeep3CD12d1Ei"
// CHECK-EDGES-DAG: [[CDEEP_D2]] = {{.*}}!DISubprogram(name: "d2", linkageName: "_ZN5CDeep3CD13CD22d2Ei"
// CHECK-EDGES-DAG: [[CDEEP_D3]] = {{.*}}!DISubprogram(name: "d3", linkageName: "_ZN5CDeep3CD13CD23CD32d3Ei"
-
-// RUN: %clang --target=x86_64-linux -Xclang -disable-llvm-passes -fno-discard-value-names -emit-llvm -S -g -O1 %s -o - | FileCheck %s -check-prefix CHECK-EDGES-NON
-
-// CHECK-EDGES-NON: define {{.*}} @_Z6edge_aP6CEmpty{{.*}} {
-// CHECK-EDGES-NON-DAG: call void %3{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON: }
-
-// CHECK-EDGES-NON: define {{.*}} @_Z6edge_bP5CBase{{.*}} {
-// CHECK-EDGES-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON-DAG: call void %3{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON: }
-
-// CHECK-EDGES-NON: define {{.*}} @_Z6edge_cP5CDeep{{.*}} {
-// CHECK-EDGES-NON-DAG: call void %1{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON-DAG: call void %4{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON-DAG: call void %7{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON-DAG: call void %10{{.*}} !dbg {{![0-9]+}}
-// CHECK-EDGES-NON: }
diff --git a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
index 237f28e770cb2..0a7622330083c 100644
--- a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
+++ b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/callsite-dwarf.cpp
@@ -38,8 +38,7 @@ void CDerivedTwo::bar(int &j) { DerivedOne->foo(j); }
// !40 = !DISubprogram(name: "bar", linkageName: "_ZN11CDerivedTwo3barERi", ...)
// !65 = !DILocation(line: 25, column: 15, scope: !40)
-// RUN: %clang --target=x86_64-unknown-linux -c -g -O1 \
-// RUN: -Xclang -debugger-tuning=sce %s -o - | \
+// RUN: %clang --target=x86_64-unknown-linux -c -g -O1 %s -o - | \
// RUN: llvm-dwarfdump --debug-info - | FileCheck %s --check-prefix=CHECK
// CHECK: DW_TAG_compile_unit
@@ -51,7 +50,7 @@ void CDerivedTwo::bar(int &j) { DerivedOne->foo(j); }
// CHECK: DW_AT_name ("CBaseOne")
// CHECK: [[FOO_DEF:0x[a-f0-9]+]]: DW_TAG_subprogram
// CHECK: DW_AT_call_all_calls (true)
-// CHECK: DW_AT_specification ([[FOO_DCL]] "foo")
+// CHECK: DW_AT_specification ([[FOO_DCL]] "{{.*}}foo{{.*}}")
// CHECK: DW_TAG_structure_type
// CHECK: DW_AT_name ("CDerivedTwo")
// CHECK: DW_TAG_subprogram
@@ -60,9 +59,9 @@ void CDerivedTwo::bar(int &j) { DerivedOne->foo(j); }
// CHECK: DW_AT_name ("CBaseTwo")
// CHECK: DW_TAG_subprogram
// CHECK: DW_AT_call_all_calls (true)
-// CHECK: DW_AT_specification (0x{{.*}} "bar")
+// CHECK: DW_AT_specification (0x{{.*}} "{{.*}}bar{{.*}}")
// CHECK: DW_TAG_call_site
// CHECK: DW_AT_call_target (DW_OP_reg0 RAX)
// CHECK: DW_AT_call_tail_call (true)
// CHECK: DW_AT_call_pc (0x{{.*}})
-// CHECK: DW_AT_call_origin ([[FOO_DEF]] "foo")
+// CHECK: DW_AT_call_origin ([[FOO_DEF]] "{{.*}}foo{{.*}}")
diff --git a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
index 67a77a1495e7c..1c56e77b1335f 100644
--- a/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
+++ b/llvm/lib/Target/X86/X86ISelLoweringCall.cpp
@@ -2053,12 +2053,11 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// Set type id for call site info and metadata 'call_target'.
// We are filtering for:
// a) The call-graph-section use case that wants to know about indirect
- // calls, and
- // b) the SCE-tuing case where we want to annotate indirect calls.
- if (CB &&
- ((MF.getTarget().Options.EmitCallGraphSection && CB->isIndirectCall()) ||
- (MF.getTarget().Options.EmitCallSiteInfo &&
- MF.getTarget().Options.DebuggerTuning == DebuggerKind::SCE)))
+ // calls, or
+ // b) We want to annotate indirect calls.
+ if (CB && CB->isIndirectCall() &&
+ (MF.getTarget().Options.EmitCallGraphSection ||
+ MF.getTarget().Options.EmitCallSiteInfo))
CSInfo = MachineFunction::CallSiteInfo(*CB);
if (IsIndirectCall && !IsWin64 &&
diff --git a/llvm/test/CodeGen/MIR/X86/callsite-emit-calleetypeid.ll b/llvm/test/CodeGen/MIR/X86/callsite-emit-calleetypeid.ll
index 3f7590adf9182..fe1980e3f5605 100644
--- a/llvm/test/CodeGen/MIR/X86/callsite-emit-calleetypeid.ll
+++ b/llvm/test/CodeGen/MIR/X86/callsite-emit-calleetypeid.ll
@@ -32,14 +32,13 @@
;; Test printer and parser with -emit-call-site-info only.
;; Test printer.
-;; Verify that fwdArgRegs is set, calleeTypeIds is not set.
+;; Verify that fwdArgRegs and calleeTypeIds are set.
; RUN: llc -mtriple=x86_64 -emit-call-site-info %s -stop-after=finalize-isel -o %t2.mir
; RUN: cat %t2.mir | FileCheck %s --check-prefix=PRINTER_CSI
; PRINTER_CSI: name: main
; PRINTER_CSI: callSites:
; PRINTER_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs:
-; PRINTER_CSI-NEXT: { arg: 0, reg: {{.*}} }
-; PRINTER_CSI-NOT: calleeTypeIds:
+; PRINTER_CSI-NEXT: { arg: 0, reg: {{.*}} }, calleeTypeIds:
;; Test parser.
@@ -49,8 +48,7 @@
; PARSER_CSI: name: main
; PARSER_CSI: callSites:
; PARSER_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs:
-; PARSER_CSI-NEXT: { arg: 0, reg: {{.*}} }
-; PARSER_CSI-NOT: calleeTypeIds:
+; PARSER_CSI-NEXT: { arg: 0, reg: {{.*}} }, calleeTypeIds:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test printer and parser with both -emit-call-site-info and --call-graph-section.
>From 7837b840e76a7d03e012131c797bbb23dcc7c3ce Mon Sep 17 00:00:00 2001
From: Carlos Alberto Enciso <Carlos.Enciso at sony.com>
Date: Fri, 14 Nov 2025 07:34:21 +0000
Subject: [PATCH 4/4] [clang][DebugInfo] Add virtual call-site target
information in DWARF.
As the extra call site information is available by default, update
other targets that support call site debug information.
Move the 'CallSiteInfo' set to the block introduced by:
https://github.com/llvm/llvm-project/pull/166202
---
clang/lib/CodeGen/CGCall.cpp | 5 ++---
clang/lib/CodeGen/CGDebugInfo.h | 2 +-
llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 10 ++++++++--
llvm/lib/Target/ARM/ARMISelLowering.cpp | 10 ++++++++--
llvm/lib/Target/Mips/MipsISelLowering.cpp | 11 +++++++++--
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 10 ++++++++--
6 files changed, 36 insertions(+), 12 deletions(-)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 5b5ed52e1d554..d475cfd9a66a5 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5958,9 +5958,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
}
}
- if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
- DI->addCallTarget(CI->getCalledFunction(), CalleeDecl, CI);
-
// 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
// Control Flow Guard checks should not be added, even if the call is inlined.
@@ -6298,6 +6295,8 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
DI->EmitFuncDeclForCallSite(
CI, DI->getFunctionType(CalleeDecl, ResTy, Args), CalleeGlobalDecl);
}
+ // Collect call site target information.
+ DI->addCallTarget(CI->getCalledFunction(), CalleeDecl, CI);
}
return Ret;
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 55f15a6d2b7ad..40b8db0299f04 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -914,7 +914,7 @@ class CGDebugInfo {
llvm::StringRef GetMethodLinkageName(const CXXMethodDecl *Method) const;
/// For each 'DISuprogram' we store a list of call instructions 'CallBase'
- /// that indirectly call such 'DISuprogram'. We use its linkage name to
+ /// that indirectly call such 'DISuprogram'. We use its linkage name to
/// update such list.
/// The 'CallTargetCache' is updated in the following scenarios:
/// - Both 'CallBase' and 'MDNode' are ready available.
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index eaa10ef031989..4d7a246f2ccad 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -9418,8 +9418,14 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
*DAG.getContext());
RetCCInfo.AnalyzeCallResult(Ins, RetCC);
- // Set type id for call site info.
- if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
+ // Set type id for call site info and metadata 'call_target'.
+ // We are filtering for:
+ // a) The call-graph-section use case that wants to know about indirect
+ // calls, or
+ // b) We want to annotate indirect calls.
+ if (CB && CB->isIndirectCall() &&
+ (MF.getTarget().Options.EmitCallGraphSection ||
+ MF.getTarget().Options.EmitCallSiteInfo))
CSInfo = MachineFunction::CallSiteInfo(*CB);
// Check callee args/returns for SVE registers and set calling convention
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 92fae71121a81..690584facc84a 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -2316,8 +2316,14 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
!Subtarget->noBTIAtReturnTwice())
GuardWithBTI = AFI->branchTargetEnforcement();
- // Set type id for call site info.
- if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
+ // Set type id for call site info and metadata 'call_target'.
+ // We are filtering for:
+ // a) The call-graph-section use case that wants to know about indirect
+ // calls, or
+ // b) We want to annotate indirect calls.
+ if (CB && CB->isIndirectCall() &&
+ (MF.getTarget().Options.EmitCallGraphSection ||
+ MF.getTarget().Options.EmitCallSiteInfo))
CSInfo = MachineFunction::CallSiteInfo(*CB);
// Determine whether this is a non-secure function call.
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index 2fd73275721b1..c7d3247a7e758 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -3400,8 +3400,15 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// Call site info for function parameters tracking and call base type info.
MachineFunction::CallSiteInfo CSInfo;
- // Set type id for call site info.
- if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
+
+ // Set type id for call site info and metadata 'call_target'.
+ // We are filtering for:
+ // a) The call-graph-section use case that wants to know about indirect
+ // calls, or
+ // b) We want to annotate indirect calls.
+ if (CB && CB->isIndirectCall() &&
+ (MF.getTarget().Options.EmitCallGraphSection ||
+ MF.getTarget().Options.EmitCallSiteInfo))
CSInfo = MachineFunction::CallSiteInfo(*CB);
// Check if it's really possible to do a tail call. Restrict it to functions
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 637f1943b8511..0cf1a862db501 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -23601,8 +23601,14 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
MachineFunction &MF = DAG.getMachineFunction();
MachineFunction::CallSiteInfo CSInfo;
- // Set type id for call site info.
- if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
+ // Set type id for call site info and metadata 'call_target'.
+ // We are filtering for:
+ // a) The call-graph-section use case that wants to know about indirect
+ // calls, or
+ // b) We want to annotate indirect calls.
+ if (CB && CB->isIndirectCall() &&
+ (MF.getTarget().Options.EmitCallGraphSection ||
+ MF.getTarget().Options.EmitCallSiteInfo))
CSInfo = MachineFunction::CallSiteInfo(*CB);
// Analyze the operands of the call, assigning locations to each operand.
More information about the llvm-commits
mailing list