[clang] [clang] Fix crash on no-prototype indirect calls with -fexperimental-call-graph-section (PR #210846)
Prabhu Rajasekaran via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 21 13:55:49 PDT 2026
https://github.com/Prabhuk updated https://github.com/llvm/llvm-project/pull/210846
>From 6a5c5f34f46f1a83d09d69b2781e7cd65e2728c3 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Mon, 20 Jul 2026 23:30:26 +0000
Subject: [PATCH 1/5] [clang] Handle C11 style function signatures in call
graph section
---
.../clang/Basic/DiagnosticFrontendKinds.td | 4 ++++
clang/lib/CodeGen/CGCall.cpp | 10 +++++++++-
clang/lib/CodeGen/CGCall.h | 19 ++++++++++---------
clang/lib/CodeGen/CGExpr.cpp | 5 +++--
clang/test/CodeGen/call-graph-section.c | 4 ++++
5 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index a688b298f2bbd..b8db966e96ead 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -407,6 +407,10 @@ def warn_profile_data_misexpect : Warning<
BackendInfo, InGroup<MisExpect>;
} // end of instrumentation issue category
+def warn_cgs_no_proto : Warning<
+ "indirect call to a function with no prototype will be treated as a wildcard in the call graph section">,
+ InGroup<DiagGroup<"call-graph-section-no-prototype">>;
+
def warn_ssaf_extract_tu_summary_file_unknown_output_format :
Warning<"unknown output summary file format '%0' "
"specified by '--ssaf-tu-summary-file=%1'">,
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 08cb9860f2f92..50d3bc2747246 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/CodeGenOptions.h"
+#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/CodeGen/SwiftCallingConv.h"
@@ -6296,13 +6297,20 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
else if (const auto *FPT =
Callee.getAbstractInfo().getCalleeFunctionProtoType())
CST = QualType(FPT, 0);
+ else if (const auto *FT =
+ Callee.getAbstractInfo().getCalleeFunctionType())
+ CST = QualType(FT, 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())
+ if (!CST.isNull()) {
CGM.createCalleeTypeMetadataForIcall(CST, *callOrInvoke);
+ if (!CST->isFunctionProtoType() &&
+ CGM.getCodeGenOpts().CallGraphSection)
+ CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto);
+ }
}
}
diff --git a/clang/lib/CodeGen/CGCall.h b/clang/lib/CodeGen/CGCall.h
index 5df7f0d08d070..9c5ffe89367e3 100644
--- a/clang/lib/CodeGen/CGCall.h
+++ b/clang/lib/CodeGen/CGCall.h
@@ -41,22 +41,23 @@ namespace CodeGen {
/// Abstract information about a function or function prototype.
class CGCalleeInfo {
- /// The function prototype of the callee.
- const FunctionProtoType *CalleeProtoTy;
+ /// The function type of the callee.
+ const FunctionType *CalleeFunctionTy;
/// The function declaration of the callee.
GlobalDecl CalleeDecl;
public:
- explicit CGCalleeInfo() : CalleeProtoTy(nullptr) {}
- CGCalleeInfo(const FunctionProtoType *calleeProtoTy, GlobalDecl calleeDecl)
- : CalleeProtoTy(calleeProtoTy), CalleeDecl(calleeDecl) {}
- CGCalleeInfo(const FunctionProtoType *calleeProtoTy)
- : CalleeProtoTy(calleeProtoTy) {}
+ explicit CGCalleeInfo() : CalleeFunctionTy(nullptr) {}
+ CGCalleeInfo(const FunctionType *calleeFunctionTy, GlobalDecl calleeDecl)
+ : CalleeFunctionTy(calleeFunctionTy), CalleeDecl(calleeDecl) {}
+ CGCalleeInfo(const FunctionType *calleeFunctionTy)
+ : CalleeFunctionTy(calleeFunctionTy) {}
CGCalleeInfo(GlobalDecl calleeDecl)
- : CalleeProtoTy(nullptr), CalleeDecl(calleeDecl) {}
+ : CalleeFunctionTy(nullptr), CalleeDecl(calleeDecl) {}
+ const FunctionType *getCalleeFunctionType() const { return CalleeFunctionTy; }
const FunctionProtoType *getCalleeFunctionProtoType() const {
- return CalleeProtoTy;
+ return dyn_cast_or_null<FunctionProtoType>(CalleeFunctionTy);
}
const GlobalDecl getCalleeDecl() const { return CalleeDecl; }
};
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index dd42e9550316d..383a5f50b57a8 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6646,7 +6646,8 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) {
dyn_cast_or_null<VarDecl>(E->getReferencedDeclOfCallee())) {
GD = GlobalDecl(VD);
}
- CGCalleeInfo CalleeInfo(FunctionType->getAs<FunctionProtoType>(), GD);
+ CGCalleeInfo CalleeInfo(FunctionType->castAs<clang::FunctionType>(),
+ GD);
CGCallee Callee(CalleeInfo, ConvertFuncrefToPtr(Result.first),
Result.second);
return Callee;
@@ -6690,7 +6691,7 @@ CGCallee CodeGenFunction::EmitCallee(const Expr *E) {
dyn_cast_or_null<VarDecl>(E->getReferencedDeclOfCallee()))
GD = GlobalDecl(VD);
- CGCalleeInfo calleeInfo(functionType->getAs<FunctionProtoType>(), GD);
+ CGCalleeInfo calleeInfo(functionType->castAs<clang::FunctionType>(), GD);
CGPointerAuthInfo pointerAuth = CGM.getFunctionPointerAuthInfo(functionType);
CGCallee callee(calleeInfo, ConvertFuncrefToPtr(calleePtr), pointerAuth);
return callee;
diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c
index cb2f9015b7ff5..52899ef20b790 100644
--- a/clang/test/CodeGen/call-graph-section.c
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -6,6 +6,9 @@
// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fexperimental-call-graph-section \
// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARNING %s
+
// CHECK-LABEL: define {{(dso_local)?}} void @foo(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
void foo() {
@@ -17,6 +20,7 @@ void bar() {
void (*fp)() = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // WARNING: warning: indirect call to a function with no prototype will be treated as a wildcard in the call graph section [-Wcall-graph-section-no-prototype]
fp();
}
>From fac539dad6f5c6cdef04b3f7864c7f652713556c Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Jul 2026 00:22:30 +0000
Subject: [PATCH 2/5] Improve warning text.
---
.../clang/Basic/DiagnosticFrontendKinds.td | 2 +-
clang/lib/CodeGen/CGCall.cpp | 11 ++++--
clang/test/CodeGen/call-graph-section.c | 36 +++++++++++++++++--
3 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index b8db966e96ead..ee85fd288fc1f 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -408,7 +408,7 @@ def warn_profile_data_misexpect : Warning<
} // end of instrumentation issue category
def warn_cgs_no_proto : Warning<
- "indirect call to a function with no prototype will be treated as a wildcard in the call graph section">,
+ "indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: %0, type string: %1)%select{| even though arguments are passed at this call site}2">,
InGroup<DiagGroup<"call-graph-section-no-prototype">>;
def warn_ssaf_extract_tu_summary_file_unknown_output_format :
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 50d3bc2747246..c54686481fee5 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6308,8 +6308,15 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
if (!CST.isNull()) {
CGM.createCalleeTypeMetadataForIcall(CST, *callOrInvoke);
if (!CST->isFunctionProtoType() &&
- CGM.getCodeGenOpts().CallGraphSection)
- CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto);
+ CGM.getCodeGenOpts().CallGraphSection) {
+ llvm::Metadata *MD =
+ CGM.CreateMetadataIdentifierForCallGraphType(CST);
+ StringRef TypeStr;
+ if (auto *MDS = dyn_cast_or_null<llvm::MDString>(MD))
+ TypeStr = MDS->getString();
+ CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto)
+ << CST << TypeStr << !CallArgs.empty();
+ }
}
}
}
diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c
index 52899ef20b790..60af5ee18fe5d 100644
--- a/clang/test/CodeGen/call-graph-section.c
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -7,7 +7,7 @@
// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
-// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARNING %s
+// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE %s
// CHECK-LABEL: define {{(dso_local)?}} void @foo(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
@@ -20,7 +20,7 @@ void bar() {
void (*fp)() = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARNING: warning: indirect call to a function with no prototype will be treated as a wildcard in the call graph section [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) [-Wcall-graph-section-no-prototype]
fp();
}
@@ -78,6 +78,34 @@ void stf() {
fp_stparam(St2, &St2);
}
+struct my_struct;
+
+// CHECK-LABEL: define {{(dso_local)?}} ptr @create_my_struct(
+// CHECK-SAME: {{.*}} !callgraph [[F_TMY_STRUCT:![0-9]+]]
+struct my_struct *create_my_struct() {
+ return 0;
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @test_struct_ptr_return(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void test_struct_ptr_return() {
+ struct my_struct *(*fp)() = create_my_struct;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
+ // WARN_NO_PROTOTYPE: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'struct my_struct *()', type string: _ZTSFP9my_structE) [-Wcall-graph-section-no-prototype]
+ fp();
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @test_no_proto_with_args(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void test_no_proto_with_args() {
+ void (*fp)() = foo;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // WARN_NO_PROTOTYPE: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) even though arguments are passed at this call site [-Wcall-graph-section-no-prototype]
+ fp(1);
+}
+
// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvE"}
// ITANIUM: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
// ITANIUM: [[F_TPRIMITIVE]] = !{!"_ZTSFicfdE"}
@@ -86,6 +114,8 @@ void stf() {
// ITANIUM: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
// ITANIUM: [[F_TSTRUCT]] = !{!"_ZTSFv3st2PS_E"}
// ITANIUM: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
+// ITANIUM: [[F_TMY_STRUCT]] = !{!"_ZTSFP9my_structE"}
+// ITANIUM: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
// MS: [[F_TVOID]] = !{!"?6AX at Z"}
// MS: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
@@ -95,3 +125,5 @@ void stf() {
// MS: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
// MS: [[F_TSTRUCT]] = !{!"?6AXUst2@@PEAU0@@Z"}
// MS: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
+// MS: [[F_TMY_STRUCT]] = !{!"?6APEAUmy_struct@@@Z"}
+// MS: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
>From 4459945f20f1095fd5d5e4184c4becfec9790af8 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Jul 2026 17:36:02 +0000
Subject: [PATCH 3/5] Include MS ABI warnings too.
---
clang/test/CodeGen/call-graph-section.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c
index 60af5ee18fe5d..7a5f9efe322cb 100644
--- a/clang/test/CodeGen/call-graph-section.c
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -7,7 +7,10 @@
// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
-// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE %s
+// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE_ITANIUM %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE_MS %s
// CHECK-LABEL: define {{(dso_local)?}} void @foo(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
@@ -20,7 +23,8 @@ void bar() {
void (*fp)() = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: ?6AX at Z) [-Wcall-graph-section-no-prototype]
fp();
}
@@ -92,7 +96,8 @@ void test_struct_ptr_return() {
struct my_struct *(*fp)() = create_my_struct;
// ITANIUM: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'struct my_struct *()', type string: _ZTSFP9my_structE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'struct my_struct *()', type string: _ZTSFP9my_structE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'struct my_struct *()', type string: ?6APEAUmy_struct@@@Z) [-Wcall-graph-section-no-prototype]
fp();
}
@@ -102,7 +107,8 @@ void test_no_proto_with_args() {
void (*fp)() = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) even though arguments are passed at this call site [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) even though arguments are passed at this call site [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: ?6AX at Z) even though arguments are passed at this call site [-Wcall-graph-section-no-prototype]
fp(1);
}
>From ef97b722fd89d2829c1ae045445becc5726dcb32 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Jul 2026 20:30:39 +0000
Subject: [PATCH 4/5] Treat function type as variadic.
---
.../clang/Basic/DiagnosticFrontendKinds.td | 2 +-
clang/lib/CodeGen/CGCall.cpp | 3 +-
clang/lib/CodeGen/CodeGenModule.cpp | 8 +++
.../CodeGen/call-graph-section-noprototype.c | 71 +++++++++++++++++++
clang/test/CodeGen/call-graph-section.c | 56 ++-------------
5 files changed, 88 insertions(+), 52 deletions(-)
create mode 100644 clang/test/CodeGen/call-graph-section-noprototype.c
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index ee85fd288fc1f..35e65a02bde09 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -408,7 +408,7 @@ def warn_profile_data_misexpect : Warning<
} // end of instrumentation issue category
def warn_cgs_no_proto : Warning<
- "indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: %0, type string: %1)%select{| even though arguments are passed at this call site}2">,
+ "indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: %0, type string: %1)">,
InGroup<DiagGroup<"call-graph-section-no-prototype">>;
def warn_ssaf_extract_tu_summary_file_unknown_output_format :
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index a5f14bc95a94f..e99d172523cd4 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6368,8 +6368,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
StringRef TypeStr;
if (auto *MDS = dyn_cast_or_null<llvm::MDString>(MD))
TypeStr = MDS->getString();
- CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto)
- << CST << TypeStr << !CallArgs.empty();
+ CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto) << CST << TypeStr;
}
}
}
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 8ce4063a3b780..6c955ace0c9f8 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -8651,6 +8651,14 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierGeneralized(QualType T) {
llvm::Metadata *
CodeGenModule::CreateMetadataIdentifierForCallGraphType(QualType T) {
+ if (auto *FNPT = T->getAs<FunctionNoProtoType>()) {
+ // For no-prototype functions, treat them as if they are variadic functions
+ // as we cannot determine the arguments for calls to these functions from
+ // call sites.
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.Variadic = true;
+ T = getContext().getFunctionType(FNPT->getReturnType(), {}, EPI);
+ }
return CreateMetadataIdentifierImpl(T, CallGraphMetadataIdMap, "",
/*ForceString=*/true);
}
diff --git a/clang/test/CodeGen/call-graph-section-noprototype.c b/clang/test/CodeGen/call-graph-section-noprototype.c
new file mode 100644
index 0000000000000..8847481be9e4c
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-noprototype.c
@@ -0,0 +1,71 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// for no-prototype functions which are treated as if they are variadic functions
+// in generating their type strings for call graph section.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE_ITANIUM %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE_MS %s
+
+// 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-LABEL: define {{(dso_local)?}} void @foo(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
+void foo() {
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @bar(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void bar() {
+ void (*fp)() = foo;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: _ZTSFvzE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: ?6AXZZ) [-Wcall-graph-section-no-prototype]
+ fp();
+}
+
+struct my_struct;
+
+// CHECK-LABEL: define {{(dso_local)?}} ptr @create_my_struct(
+// CHECK-SAME: {{.*}} !callgraph [[F_TMY_STRUCT:![0-9]+]]
+struct my_struct *create_my_struct() {
+ return 0;
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @test_struct_ptr_return(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void test_struct_ptr_return() {
+ struct my_struct *(*fp)() = create_my_struct;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'struct my_struct *()', type string: _ZTSFP9my_structzE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'struct my_struct *()', type string: ?6APEAUmy_struct@@ZZ) [-Wcall-graph-section-no-prototype]
+ fp();
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @test_no_proto_with_args(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void test_no_proto_with_args() {
+ void (*fp)() = foo;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: _ZTSFvzE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: ?6AXZZ) [-Wcall-graph-section-no-prototype]
+ fp(1);
+}
+
+// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvzE"}
+// ITANIUM: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
+// ITANIUM: [[F_TMY_STRUCT]] = !{!"_ZTSFP9my_structzE"}
+// ITANIUM: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
+
+// MS: [[F_TVOID]] = !{!"?6AXZZ"}
+// MS: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
+// MS: [[F_TMY_STRUCT]] = !{!"?6APEAUmy_struct@@ZZ"}
+// MS: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c
index 7a5f9efe322cb..5019d18425ec6 100644
--- a/clang/test/CodeGen/call-graph-section.c
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -6,25 +6,17 @@
// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fexperimental-call-graph-section \
// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
-// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE_ITANIUM %s
-
-// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fexperimental-call-graph-section \
-// RUN: -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefixes=WARN_NO_PROTOTYPE_MS %s
-
// CHECK-LABEL: define {{(dso_local)?}} void @foo(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
-void foo() {
+void foo(void) {
}
// CHECK-LABEL: define {{(dso_local)?}} void @bar(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
-void bar() {
- void (*fp)() = foo;
+void bar(void) {
+ void (*fp)(void) = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) [-Wcall-graph-section-no-prototype]
- // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: ?6AX at Z) [-Wcall-graph-section-no-prototype]
fp();
}
@@ -42,7 +34,7 @@ int *qux(char *a, float *b, double *c) {
// CHECK-LABEL: define {{(dso_local)?}} void @corge(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
-void corge() {
+void corge(void) {
int (*fp_baz)(char, float, double) = baz;
// CHECK: call i32 {{.*}}, !callee_type [[F_TPRIMITIVE_CT:![0-9]+]]
fp_baz('a', .0f, .0);
@@ -66,7 +58,7 @@ void stparam(struct st2 a, struct st2 *b) {}
// CHECK-LABEL: define {{(dso_local)?}} void @stf(
// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
-void stf() {
+void stf(void) {
struct st1 St1;
St1.fp = qux;
// CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]
@@ -82,37 +74,7 @@ void stf() {
fp_stparam(St2, &St2);
}
-struct my_struct;
-
-// CHECK-LABEL: define {{(dso_local)?}} ptr @create_my_struct(
-// CHECK-SAME: {{.*}} !callgraph [[F_TMY_STRUCT:![0-9]+]]
-struct my_struct *create_my_struct() {
- return 0;
-}
-
-// CHECK-LABEL: define {{(dso_local)?}} void @test_struct_ptr_return(
-// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
-void test_struct_ptr_return() {
- struct my_struct *(*fp)() = create_my_struct;
- // ITANIUM: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
- // MS: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'struct my_struct *()', type string: _ZTSFP9my_structE) [-Wcall-graph-section-no-prototype]
- // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'struct my_struct *()', type string: ?6APEAUmy_struct@@@Z) [-Wcall-graph-section-no-prototype]
- fp();
-}
-
-// CHECK-LABEL: define {{(dso_local)?}} void @test_no_proto_with_args(
-// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
-void test_no_proto_with_args() {
- void (*fp)() = foo;
- // ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: _ZTSFvE) even though arguments are passed at this call site [-Wcall-graph-section-no-prototype]
- // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating type metadata as if it took no arguments (type: 'void ()', type string: ?6AX at Z) even though arguments are passed at this call site [-Wcall-graph-section-no-prototype]
- fp(1);
-}
-
-// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvE"}
+// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvvE"}
// ITANIUM: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
// ITANIUM: [[F_TPRIMITIVE]] = !{!"_ZTSFicfdE"}
// ITANIUM: [[F_TPTR]] = !{!"_ZTSFPiPcPfPdE"}
@@ -120,10 +82,8 @@ void test_no_proto_with_args() {
// ITANIUM: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
// ITANIUM: [[F_TSTRUCT]] = !{!"_ZTSFv3st2PS_E"}
// ITANIUM: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
-// ITANIUM: [[F_TMY_STRUCT]] = !{!"_ZTSFP9my_structE"}
-// ITANIUM: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
-// MS: [[F_TVOID]] = !{!"?6AX at Z"}
+// MS: [[F_TVOID]] = !{!"?6AXXZ"}
// MS: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
// MS: [[F_TPRIMITIVE]] = !{!"?6AHDMN at Z"}
// MS: [[F_TPTR]] = !{!"?6APEAHPEADPEAMPEAN at Z"}
@@ -131,5 +91,3 @@ void test_no_proto_with_args() {
// MS: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
// MS: [[F_TSTRUCT]] = !{!"?6AXUst2@@PEAU0@@Z"}
// MS: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
-// MS: [[F_TMY_STRUCT]] = !{!"?6APEAUmy_struct@@@Z"}
-// MS: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
>From 2a1c847879181180879786d4ae472307a861327d Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Tue, 21 Jul 2026 20:55:13 +0000
Subject: [PATCH 5/5] Print assumed type in the warning. Add a test case with
non-void return type.
---
.../clang/Basic/DiagnosticFrontendKinds.td | 2 +-
clang/lib/CodeGen/CGCall.cpp | 12 ++++++-
.../CodeGen/call-graph-section-noprototype.c | 33 +++++++++++++++----
3 files changed, 39 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 35e65a02bde09..3abf39aed704d 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -408,7 +408,7 @@ def warn_profile_data_misexpect : Warning<
} // end of instrumentation issue category
def warn_cgs_no_proto : Warning<
- "indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: %0, type string: %1)">,
+ "indirect call to a function with no prototype %0; generating callee_type metadata as if calling a variadic function %1 (type string: %2)">,
InGroup<DiagGroup<"call-graph-section-no-prototype">>;
def warn_ssaf_extract_tu_summary_file_unknown_output_format :
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index e99d172523cd4..cb630864c5eeb 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6368,7 +6368,17 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
StringRef TypeStr;
if (auto *MDS = dyn_cast_or_null<llvm::MDString>(MD))
TypeStr = MDS->getString();
- CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto) << CST << TypeStr;
+
+ QualType AssumedType = CST;
+ if (auto *FNPT = CST->getAs<FunctionNoProtoType>()) {
+ FunctionProtoType::ExtProtoInfo EPI;
+ EPI.Variadic = true;
+ AssumedType = CGM.getContext().getFunctionType(
+ FNPT->getReturnType(), {}, EPI);
+ }
+
+ CGM.getDiags().Report(Loc, diag::warn_cgs_no_proto)
+ << CST << AssumedType << TypeStr;
}
}
}
diff --git a/clang/test/CodeGen/call-graph-section-noprototype.c b/clang/test/CodeGen/call-graph-section-noprototype.c
index 8847481be9e4c..dc89daf956bc6 100644
--- a/clang/test/CodeGen/call-graph-section-noprototype.c
+++ b/clang/test/CodeGen/call-graph-section-noprototype.c
@@ -25,8 +25,8 @@ void bar() {
void (*fp)() = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: _ZTSFvzE) [-Wcall-graph-section-no-prototype]
- // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: ?6AXZZ) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype 'void ()'; generating callee_type metadata as if calling a variadic function 'void (...)' (type string: _ZTSFvzE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype 'void ()'; generating callee_type metadata as if calling a variadic function 'void (...)' (type string: ?6AXZZ) [-Wcall-graph-section-no-prototype]
fp();
}
@@ -44,8 +44,25 @@ void test_struct_ptr_return() {
struct my_struct *(*fp)() = create_my_struct;
// ITANIUM: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TMY_STRUCT_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'struct my_struct *()', type string: _ZTSFP9my_structzE) [-Wcall-graph-section-no-prototype]
- // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'struct my_struct *()', type string: ?6APEAUmy_struct@@ZZ) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype 'struct my_struct *()'; generating callee_type metadata as if calling a variadic function 'struct my_struct *(...)' (type string: _ZTSFP9my_structzE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype 'struct my_struct *()'; generating callee_type metadata as if calling a variadic function 'struct my_struct *(...)' (type string: ?6APEAUmy_struct@@ZZ) [-Wcall-graph-section-no-prototype]
+ fp();
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} i32 @baz(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT:![0-9]+]]
+int baz() {
+ return 1;
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @test_int_return(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void test_int_return() {
+ int (*fp)() = baz;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TINT_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TINT_CT:![0-9]+]]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype 'int ()'; generating callee_type metadata as if calling a variadic function 'int (...)' (type string: _ZTSFizE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype 'int ()'; generating callee_type metadata as if calling a variadic function 'int (...)' (type string: ?6AHZZ) [-Wcall-graph-section-no-prototype]
fp();
}
@@ -55,8 +72,8 @@ void test_no_proto_with_args() {
void (*fp)() = foo;
// ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
// MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
- // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: _ZTSFvzE) [-Wcall-graph-section-no-prototype]
- // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype; generating callee_type metadata as if calling a variadic function (type: 'void ()', type string: ?6AXZZ) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_ITANIUM: warning: indirect call to a function with no prototype 'void ()'; generating callee_type metadata as if calling a variadic function 'void (...)' (type string: _ZTSFvzE) [-Wcall-graph-section-no-prototype]
+ // WARN_NO_PROTOTYPE_MS: warning: indirect call to a function with no prototype 'void ()'; generating callee_type metadata as if calling a variadic function 'void (...)' (type string: ?6AXZZ) [-Wcall-graph-section-no-prototype]
fp(1);
}
@@ -64,8 +81,12 @@ void test_no_proto_with_args() {
// ITANIUM: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
// ITANIUM: [[F_TMY_STRUCT]] = !{!"_ZTSFP9my_structzE"}
// ITANIUM: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
+// ITANIUM: [[F_TINT]] = !{!"_ZTSFizE"}
+// ITANIUM: [[F_TINT_CT]] = !{[[F_TINT:![0-9]+]]}
// MS: [[F_TVOID]] = !{!"?6AXZZ"}
// MS: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
// MS: [[F_TMY_STRUCT]] = !{!"?6APEAUmy_struct@@ZZ"}
// MS: [[F_TMY_STRUCT_CT]] = !{[[F_TMY_STRUCT:![0-9]+]]}
+// MS: [[F_TINT]] = !{!"?6AHZZ"}
+// MS: [[F_TINT_CT]] = !{[[F_TINT:![0-9]+]]}
More information about the cfe-commits
mailing list