[clang] [llvm] [clang][CodeGen] Construct function type for callgraph from function definition (PR #212863)

Prabhu Rajasekaran via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 9 10:52:05 PDT 2026


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

>From cde7374ccfeae9003d0d81228b3fbf47ed61c01d Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 29 Jul 2026 13:11:36 -0700
Subject: [PATCH 1/4] [clang][CodeGen] Construct function type for callgraph
 from function definition

When -fexperimental-call-graph-section is enabled, for unprototyped function
definitions (such as C89 parameterless declarations or K&R definitions)
reconstruct their prototype from the parameter declarations in the
definition AST (applying default argument promotions to parameters).
---
 clang/lib/CodeGen/CodeGenModule.cpp           | 37 +++++++++++-
 ...all-graph-section-definition-noprototype.c | 57 +++++++++++++++++++
 clang/test/CodeGen/call-graph-section.c       |  4 +-
 .../Linker/callgraph-section-noprototype.ll   | 42 ++++++++++++++
 4 files changed, 135 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGen/call-graph-section-definition-noprototype.c
 create mode 100644 llvm/test/Linker/callgraph-section-noprototype.ll

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e7c1d182fd20d..f19cfd6f07157 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3464,11 +3464,38 @@ void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
       F->getFunction().hasAddressTaken(nullptr, /*IgnoreCallbackUses=*/true,
                                        /*IgnoreAssumeLikeCalls=*/true,
                                        /*IgnoreLLVMUsed=*/false)) {
+    const FunctionDecl *Def = nullptr;
+    bool HasBody = FD->hasBody(Def);
+    if (!HasBody || !Def)
+      Def = FD;
+
+    QualType QT = Def->getType();
+    if (const auto *FNPT = QT->getAs<FunctionNoProtoType>()) {
+      // If there is no definition available in this TU for an unprototyped
+      // function declaration, skip generating incomplete callgraph metadata.
+      if (!HasBody && !Def->isThisDeclarationADefinition())
+        return;
+
+      SmallVector<QualType, 8> ParamTypes;
+      for (const ParmVarDecl *P : Def->parameters()) {
+        QualType ParamTy = P->getType();
+        if (Context.isPromotableIntegerType(ParamTy))
+          ParamTy = Context.getPromotedIntegerType(ParamTy);
+        else if (const auto *BT = ParamTy->getAs<BuiltinType>()) {
+          if (BT->getKind() == BuiltinType::Float ||
+              BT->getKind() == BuiltinType::Half)
+            ParamTy = Context.DoubleTy;
+        }
+        ParamTypes.push_back(ParamTy);
+      }
+      FunctionProtoType::ExtProtoInfo EPI;
+      QT = Context.getFunctionType(FNPT->getReturnType(), ParamTypes, EPI);
+    }
+
     F->addMetadata(
         llvm::LLVMContext::MD_callgraph,
-        *llvm::MDTuple::get(
-            getLLVMContext(),
-            {CreateMetadataIdentifierForCallGraphType(FD->getType())}));
+        *llvm::MDTuple::get(getLLVMContext(),
+                            {CreateMetadataIdentifierForCallGraphType(QT)}));
   }
 }
 
@@ -8636,6 +8663,10 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierGeneralized(QualType T) {
 
 llvm::Metadata *
 CodeGenModule::CreateMetadataIdentifierForCallGraphType(QualType T) {
+  if (auto *FNPT = T->getAs<FunctionNoProtoType>()) {
+    FunctionProtoType::ExtProtoInfo EPI;
+    T = getContext().getFunctionType(FNPT->getReturnType(), {}, EPI);
+  }
   return CreateMetadataIdentifierImpl(T, CallGraphMetadataIdMap, "",
                                       /*ForceString=*/true);
 }
diff --git a/clang/test/CodeGen/call-graph-section-definition-noprototype.c b/clang/test/CodeGen/call-graph-section-definition-noprototype.c
new file mode 100644
index 0000000000000..21193ff8aca92
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-definition-noprototype.c
@@ -0,0 +1,57 @@
+// 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
+
+// Tests that function definitions specified without a prototype (C89 empty parameter list or K&R declarations)
+// generate !callgraph metadata based on reconstructed parameter types (with default argument promotions).
+
+// Forward declaration without prototype (pure declaration in this TU).
+// Because there is no definition or prototype in this TU, no !callgraph metadata is attached to @decl_only declaration.
+// CHECK-LABEL: declare {{.*}}void @decl_only(...)
+void decl_only();
+
+void use_decl() {
+  void (*fp)() = decl_only;
+}
+
+// C89 definition with no parameters: reconstructed prototype void (void).
+// CHECK-LABEL: define {{(dso_local)?}} void @foo(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
+void foo() {
+}
+
+// Function returning struct pointer with no parameters: reconstructed prototype struct my_struct *(void).
+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;
+}
+
+// Function returning int with no parameters: reconstructed prototype int (void).
+// CHECK-LABEL: define {{(dso_local)?}} i32 @baz(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT:![0-9]+]]
+int baz() {
+  return 1;
+}
+
+// K&R function definition: reconstructed prototype void (int, int) with promoted short -> int.
+// CHECK-LABEL: define {{(dso_local)?}} void @knr_func(
+// CHECK-SAME: {{.*}} !callgraph [[F_TKNR:![0-9]+]]
+void knr_func(a, b)
+  int a;
+  short b;
+{
+}
+
+// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvvE"}
+// ITANIUM: [[F_TMY_STRUCT]] = !{!"_ZTSFP9my_structvE"}
+// ITANIUM: [[F_TINT]] = !{!"_ZTSFivE"}
+// ITANIUM: [[F_TKNR]] = !{!"_ZTSFviiE"}
+
+// MS: [[F_TVOID]] = !{!"?6AXXZ"}
+// MS: [[F_TMY_STRUCT]] = !{!"?6APEAUmy_struct@@XZ"}
+// MS: [[F_TINT]] = !{!"?6AHXZ"}
+// MS: [[F_TKNR]] = !{!"?6AXHH at Z"}
diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c
index cb2f9015b7ff5..1543f30aa2b40 100644
--- a/clang/test/CodeGen/call-graph-section.c
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -74,7 +74,7 @@ void stf() {
   fp_stparam(St2, &St2);
 }
 
-// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvE"}
+// ITANIUM: [[F_TVOID]] = !{!"_ZTSFvvE"}
 // ITANIUM: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
 // ITANIUM: [[F_TPRIMITIVE]] = !{!"_ZTSFicfdE"}
 // ITANIUM: [[F_TPTR]] = !{!"_ZTSFPiPcPfPdE"}
@@ -83,7 +83,7 @@ void stf() {
 // ITANIUM: [[F_TSTRUCT]] = !{!"_ZTSFv3st2PS_E"}
 // ITANIUM: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![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"}
diff --git a/llvm/test/Linker/callgraph-section-noprototype.ll b/llvm/test/Linker/callgraph-section-noprototype.ll
new file mode 100644
index 0000000000000..1b2e77f121ffe
--- /dev/null
+++ b/llvm/test/Linker/callgraph-section-noprototype.ll
@@ -0,0 +1,42 @@
+; RUN: rm -rf %t && split-file %s %t
+; RUN: llvm-link %t/decl.ll %t/def.ll -S | FileCheck %s
+
+; Tests that when linking a declaration module (without !callgraph metadata for unprototyped decl)
+; and a definition module (with full reconstructed !callgraph metadata),
+; the merged definition replaces the declaration and retains the definition's !callgraph metadata.
+
+; CHECK: define dso_local void @bar()
+; CHECK: call void (i32, i32, ...) %0(i32 noundef 1, i32 noundef 2), !callee_type [[F_CT:![0-9]+]]
+; CHECK: define dso_local void @foo(i32 noundef %a, i32 noundef %0) !callgraph [[F_DEF:![0-9]+]]
+
+; CHECK: [[F_CT]] = !{[[F_DEF]]}
+; CHECK: [[F_DEF]] = !{!"_ZTSFviiE"}
+
+;--- decl.ll
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux"
+
+define dso_local void @bar() {
+entry:
+  %fp = alloca ptr, align 8
+  store ptr @foo, ptr %fp, align 8
+  %0 = load ptr, ptr %fp, align 8
+  call void (i32, i32, ...) %0(i32 noundef 1, i32 noundef 2), !callee_type !1
+  ret void
+}
+
+declare void @foo(...)
+
+!1 = !{!2}
+!2 = !{!"_ZTSFviiE"}
+
+;--- def.ll
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux"
+
+define dso_local void @foo(i32 noundef %a, i32 noundef %0) !callgraph !1 {
+entry:
+  ret void
+}
+
+!1 = !{!"_ZTSFviiE"}

>From 390d54aefe5e1e24574347acfa3c242ccf2af88d Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 19 Aug 2026 22:41:09 +0000
Subject: [PATCH 2/4] Fix the test. Test for promotion normalization
 expectations. Test for call site and definitions side reconstructed prototype
 matching.

---
 ...all-graph-section-definition-noprototype.c | 107 ++++++++++++------
 1 file changed, 75 insertions(+), 32 deletions(-)

diff --git a/clang/test/CodeGen/call-graph-section-definition-noprototype.c b/clang/test/CodeGen/call-graph-section-definition-noprototype.c
index 21193ff8aca92..9cf93e4892199 100644
--- a/clang/test/CodeGen/call-graph-section-definition-noprototype.c
+++ b/clang/test/CodeGen/call-graph-section-definition-noprototype.c
@@ -1,57 +1,100 @@
+/// Tests that function definitions without a prototype (C89 empty parameter list or K&R declarations)
+/// reconstruct parameter types with default argument promotions and produce type identifiers that
+/// match both:
+/// - Their prototyped equivalents on the definition side.
+/// - Indirect callsites using unprototyped function pointers.
+
 // 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
 
-// Tests that function definitions specified without a prototype (C89 empty parameter list or K&R declarations)
-// generate !callgraph metadata based on reconstructed parameter types (with default argument promotions).
-
-// Forward declaration without prototype (pure declaration in this TU).
-// Because there is no definition or prototype in this TU, no !callgraph metadata is attached to @decl_only declaration.
+/// Forward declaration without prototype (pure declaration in this TU).
+/// Because there is no definition or prototype in this TU, no !callgraph metadata is attached.
 // CHECK-LABEL: declare {{.*}}void @decl_only(...)
 void decl_only();
 
-void use_decl() {
+void use_decl(void) {
   void (*fp)() = decl_only;
 }
 
-// C89 definition with no parameters: reconstructed prototype void (void).
-// CHECK-LABEL: define {{(dso_local)?}} void @foo(
+/// Void parameter list: C89 parameterless definition and C prototyped (void) definition
+/// must produce the same type identifier.
+// CHECK-LABEL: define {{(dso_local)?}} void @proto_void(
 // CHECK-SAME: {{.*}} !callgraph [[F_TVOID:![0-9]+]]
-void foo() {
-}
+void proto_void(void) {}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @c89_void(
+// CHECK-SAME: {{.*}} !callgraph [[F_TVOID]]
+void c89_void() {}
+
+/// Single argument promotion: K&R int definition and K&R short definition (promoted to int)
+/// must produce the same type identifier.
+// CHECK-LABEL: define {{(dso_local)?}} void @knr_int(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT_ONE:![0-9]+]]
+void knr_int(i)
+  int i;
+{}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @knr_promoted_int(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT_ONE]]
+void knr_promoted_int(i)
+  short i;
+{}
+
+/// Multi-argument promotions: prototyped (int, double), K&R (int, double), and K&R (short, float)
+/// (promoted to int, double) must all produce the same type identifier.
+// CHECK-LABEL: define {{(dso_local)?}} void @proto_int_double(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT_DOUBLE:![0-9]+]]
+void proto_int_double(int a, double b) {}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @knr_int_double(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT_DOUBLE]]
+void knr_int_double(a, b)
+  int a;
+  double b;
+{}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @knr_promoted_multi(
+// CHECK-SAME: {{.*}} !callgraph [[F_TINT_DOUBLE]]
+void knr_promoted_multi(a, b)
+  short a;
+  float b;
+{}
 
-// Function returning struct pointer with no parameters: reconstructed prototype struct my_struct *(void).
+/// Struct pointer return: C89 parameterless and prototyped (void) return types must match.
 struct my_struct;
-// CHECK-LABEL: define {{(dso_local)?}} ptr @create_my_struct(
+
+// CHECK-LABEL: define {{(dso_local)?}} ptr @proto_my_struct(
 // CHECK-SAME: {{.*}} !callgraph [[F_TMY_STRUCT:![0-9]+]]
-struct my_struct *create_my_struct() {
-  return 0;
-}
+struct my_struct *proto_my_struct(void) { return 0; }
 
-// Function returning int with no parameters: reconstructed prototype int (void).
-// CHECK-LABEL: define {{(dso_local)?}} i32 @baz(
-// CHECK-SAME: {{.*}} !callgraph [[F_TINT:![0-9]+]]
-int baz() {
-  return 1;
-}
+// CHECK-LABEL: define {{(dso_local)?}} ptr @c89_my_struct(
+// CHECK-SAME: {{.*}} !callgraph [[F_TMY_STRUCT]]
+struct my_struct *c89_my_struct() { return 0; }
 
-// K&R function definition: reconstructed prototype void (int, int) with promoted short -> int.
-// CHECK-LABEL: define {{(dso_local)?}} void @knr_func(
-// CHECK-SAME: {{.*}} !callgraph [[F_TKNR:![0-9]+]]
-void knr_func(a, b)
-  int a;
-  short b;
-{
+/// Callsite-to-definition equivalence: indirect calls using unprototyped function pointers
+/// must generate !callee_type metadata referencing the normalized definition type identifiers.
+void test_indirect_calls(void) {
+  // CHECK: call void {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+  void (*fp_void)() = c89_void;
+  fp_void();
+
+  // CHECK: call void {{.*}}, !callee_type [[F_TINT_DOUBLE_CT:![0-9]+]]
+  void (*fp_multi)() = knr_promoted_multi;
+  fp_multi((short)1, (float)2.0);
 }
 
 // ITANIUM: [[F_TVOID]] = !{!"_ZTSFvvE"}
+// ITANIUM: [[F_TINT_ONE]] = !{!"_ZTSFviE"}
+// ITANIUM: [[F_TINT_DOUBLE]] = !{!"_ZTSFvidE"}
 // ITANIUM: [[F_TMY_STRUCT]] = !{!"_ZTSFP9my_structvE"}
-// ITANIUM: [[F_TINT]] = !{!"_ZTSFivE"}
-// ITANIUM: [[F_TKNR]] = !{!"_ZTSFviiE"}
 
 // MS: [[F_TVOID]] = !{!"?6AXXZ"}
+// MS: [[F_TINT_ONE]] = !{!"?6AXH at Z"}
+// MS: [[F_TINT_DOUBLE]] = !{!"?6AXHN at Z"}
 // MS: [[F_TMY_STRUCT]] = !{!"?6APEAUmy_struct@@XZ"}
-// MS: [[F_TINT]] = !{!"?6AHXZ"}
-// MS: [[F_TKNR]] = !{!"?6AXHH at Z"}
+
+// CHECK: [[F_TVOID_CT]] = !{[[F_TVOID]]}
+// CHECK: [[F_TINT_DOUBLE_CT]] = !{[[F_TINT_DOUBLE]]}

>From 221348a5023a9be35814af8376012ceb1b36c914 Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 19 Aug 2026 23:14:03 +0000
Subject: [PATCH 3/4] Add inline comments regarding protoype construction.

---
 clang/lib/CodeGen/CodeGenModule.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index ac6be405979f6..283e3d9821733 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3566,6 +3566,11 @@ void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
       if (!HasBody && !Def->isThisDeclarationADefinition())
         return;
 
+      // Reconstruct a prototype from the parameter declarations in the
+      // definition AST, applying C default argument promotions (e.g.,
+      // short -> int, float -> double). This ensures definition-side
+      // type metadata matches the promoted signatures computed at indirect
+      // call sites in CGCall.cpp (see CodeGenFunction::EmitCall).
       SmallVector<QualType, 8> ParamTypes;
       for (const ParmVarDecl *P : Def->parameters()) {
         QualType ParamTy = P->getType();

>From a2629dd413cec32e8348b84daf2644782926976c Mon Sep 17 00:00:00 2001
From: prabhukr <prabhukr at google.com>
Date: Wed, 9 Sep 2026 10:51:27 -0700
Subject: [PATCH 4/4] Refactor out type reconstruction so the call sites and
 function definition sides stay consistent.

---
 clang/lib/CodeGen/CGCall.cpp        |  4 +--
 clang/lib/CodeGen/CodeGenModule.cpp | 53 ++++++++++++++++++++---------
 clang/lib/CodeGen/CodeGenModule.h   | 11 ++++++
 3 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 872229cd30219..7e77c53be6f86 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6437,9 +6437,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
             // unprototyped calls.
             for (const CallArg &Arg : CallArgs)
               ParamTypes.push_back(Arg.getType());
-            FunctionProtoType::ExtProtoInfo EPI;
-            CST = getContext().getFunctionType(FNPT->getReturnType(),
-                                               ParamTypes, EPI);
+            CST = CGM.reconstructCallGraphPrototype(FNPT, ParamTypes);
           }
 
           llvm::Metadata *MD =
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 283e3d9821733..5bf031a172bea 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -3572,19 +3572,9 @@ void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
       // type metadata matches the promoted signatures computed at indirect
       // call sites in CGCall.cpp (see CodeGenFunction::EmitCall).
       SmallVector<QualType, 8> ParamTypes;
-      for (const ParmVarDecl *P : Def->parameters()) {
-        QualType ParamTy = P->getType();
-        if (Context.isPromotableIntegerType(ParamTy))
-          ParamTy = Context.getPromotedIntegerType(ParamTy);
-        else if (const auto *BT = ParamTy->getAs<BuiltinType>()) {
-          if (BT->getKind() == BuiltinType::Float ||
-              BT->getKind() == BuiltinType::Half)
-            ParamTy = Context.DoubleTy;
-        }
-        ParamTypes.push_back(ParamTy);
-      }
-      FunctionProtoType::ExtProtoInfo EPI;
-      QT = Context.getFunctionType(FNPT->getReturnType(), ParamTypes, EPI);
+      for (const ParmVarDecl *P : Def->parameters())
+        ParamTypes.push_back(P->getType());
+      QT = reconstructCallGraphPrototype(FNPT, ParamTypes);
     }
 
     F->addMetadata(
@@ -8759,12 +8749,41 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierGeneralized(QualType T) {
                                       ".generalized", /*ForceString=*/false);
 }
 
+// Applies C default argument promotions to a parameter type.
+//
+// FIXME: The canonical source of truth for C default argument promotion is
+// Sema::DefaultArgumentPromotion (SemaExpr.cpp), which operates on Expr*.
+// Because CodeGen only has type information (QualType from ParmVarDecl or
+// CallArg) and cannot invoke Sema without dummy expressions, we mirror the
+// promotion rules here. In the long term, this type-based logic should be
+// unified with Sema (for example, by extracting a shared type-level promotion
+// helper in ASTContext).
+QualType CodeGenModule::getCallGraphPromotedType(QualType Ty) const {
+  if (Context.isPromotableIntegerType(Ty))
+    return Context.getPromotedIntegerType(Ty);
+  if (const auto *BT = Ty->getAs<BuiltinType>()) {
+    if (BT->getKind() == BuiltinType::Float ||
+        BT->getKind() == BuiltinType::Half)
+      return Context.DoubleTy;
+  }
+  return Ty;
+}
+
+QualType CodeGenModule::reconstructCallGraphPrototype(
+    const FunctionNoProtoType *FNPT, ArrayRef<QualType> ParamTypes) const {
+  SmallVector<QualType, 8> PromotedParamTypes;
+  PromotedParamTypes.reserve(ParamTypes.size());
+  for (QualType PT : ParamTypes)
+    PromotedParamTypes.push_back(getCallGraphPromotedType(PT));
+  FunctionProtoType::ExtProtoInfo EPI;
+  return Context.getFunctionType(FNPT->getReturnType(), PromotedParamTypes,
+                                 EPI);
+}
+
 llvm::Metadata *
 CodeGenModule::CreateMetadataIdentifierForCallGraphType(QualType T) {
-  if (auto *FNPT = T->getAs<FunctionNoProtoType>()) {
-    FunctionProtoType::ExtProtoInfo EPI;
-    T = getContext().getFunctionType(FNPT->getReturnType(), {}, EPI);
-  }
+  if (auto *FNPT = T->getAs<FunctionNoProtoType>())
+    T = reconstructCallGraphPrototype(FNPT, {});
   return CreateMetadataIdentifierImpl(T, CallGraphMetadataIdMap, "",
                                       /*ForceString=*/true);
 }
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index f3e0541636638..22a1517c232d4 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1768,6 +1768,17 @@ class CodeGenModule : public CodeGenTypeCache {
   /// MDString.
   llvm::Metadata *CreateMetadataIdentifierForCallGraphType(QualType T);
 
+  /// Applies C default argument promotions to a parameter type for Call Graph
+  /// Section type reconstruction.
+  QualType getCallGraphPromotedType(QualType Ty) const;
+
+  /// Reconstructs a FunctionProtoType for an unprototyped function type
+  /// (FunctionNoProtoType) using the given parameter/argument types, applying
+  /// default argument promotions to ensure call-site and definition-site type
+  /// signatures match.
+  QualType reconstructCallGraphPrototype(const FunctionNoProtoType *FNPT,
+                                         ArrayRef<QualType> ParamTypes) const;
+
   /// Create a metadata identifier that is intended to be used to check virtual
   /// calls via a member function pointer.
   llvm::Metadata *CreateMetadataIdentifierForVirtualMemPtrType(QualType T);



More information about the cfe-commits mailing list