[llvm] [profcheck] Add indirect call metadata (PR #154657)

Mircea Trofin via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 20 21:26:12 PDT 2025


https://github.com/mtrofin updated https://github.com/llvm/llvm-project/pull/154657

>From 0a12548b08d6f89704f4e988f0b4364ccc076263 Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Wed, 20 Aug 2025 18:29:05 -0700
Subject: [PATCH] [profcheck] Add indirect call metadata

---
 llvm/lib/Transforms/Utils/ProfileVerify.cpp   | 44 ++++++++++++++-----
 .../JumpTableToSwitch/lit.local.cfg           |  5 +++
 .../PGOProfile/profcheck-indirect-calls.ll    | 34 ++++++++++++++
 3 files changed, 72 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/Transforms/JumpTableToSwitch/lit.local.cfg
 create mode 100644 llvm/test/Transforms/PGOProfile/profcheck-indirect-calls.ll

diff --git a/llvm/lib/Transforms/Utils/ProfileVerify.cpp b/llvm/lib/Transforms/Utils/ProfileVerify.cpp
index 41647f7717a43..cf8d6c043e0cd 100644
--- a/llvm/lib/Transforms/Utils/ProfileVerify.cpp
+++ b/llvm/lib/Transforms/Utils/ProfileVerify.cpp
@@ -17,6 +17,7 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/MDBuilder.h"
 #include "llvm/IR/ProfDataUtils.h"
+#include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Support/BranchProbability.h"
 #include "llvm/Support/CommandLine.h"
 
@@ -36,6 +37,10 @@ static cl::opt<uint32_t> SelectFalseWeight(
     "profcheck-default-select-false-weight", cl::init(3U),
     cl::desc("When annotating `select` instructions, this value will be used "
              "for the second ('false') case."));
+static cl::opt<bool> AnnotateIndirectCalls(
+    "profcheck-annotate-indirect-calls", cl::init(true),
+    cl::desc("Also inject (if missing) MD_prof for indirect calls"));
+
 namespace {
 class ProfileInjector {
   Function &F;
@@ -92,12 +97,24 @@ bool ProfileInjector::inject() {
     return false;
   bool Changed = false;
   for (auto &BB : F) {
-    if (AnnotateSelect) {
-      for (auto &I : BB) {
-        if (isa<SelectInst>(I) && !I.getMetadata(LLVMContext::MD_prof))
-          setBranchWeights(I, {SelectTrueWeight, SelectFalseWeight},
-                           /*IsExpected=*/false);
-      }
+    for (auto &I : BB) {
+      // Annotate instructions that support MD_prof metadata, such as `select`
+      // and indirect calls - *if* they don't already have that metadata.
+      if (AnnotateSelect && isa<SelectInst>(I) &&
+          !I.getMetadata(LLVMContext::MD_prof))
+        setBranchWeights(I, {SelectTrueWeight, SelectFalseWeight},
+                         /*IsExpected=*/false);
+      if (AnnotateIndirectCalls)
+        if (auto *CB = dyn_cast<CallBase>(&I))
+          if (CB->isIndirectCall() && !CB->getMetadata(LLVMContext::MD_prof))
+            // add a valid-format but bogus indirect call profile. Neither the
+            // GUIDs nor the counts are meant to matter.
+            annotateValueSite(*F.getParent(), *CB,
+                              {{/*.Value=*/2345, /*.Count=*/10},
+                               {/*.Value=*/5678, /*.Count=*/20}},
+                              /*Sum=*/30,
+                              InstrProfValueKind::IPVK_IndirectCallTarget,
+                              /*MaxMDCount=*/30);
     }
     auto *Term = getTerminatorBenefitingFromMDProf(BB);
     if (!Term || Term->getMetadata(LLVMContext::MD_prof))
@@ -162,11 +179,16 @@ PreservedAnalyses ProfileVerifierPass::run(Function &F,
   if (EntryCount->getCount() == 0)
     return PreservedAnalyses::all();
   for (const auto &BB : F) {
-    if (AnnotateSelect) {
-      for (const auto &I : BB)
-        if (isa<SelectInst>(I) && !I.getMetadata(LLVMContext::MD_prof))
-          F.getContext().emitError(
-              "Profile verification failed: select annotation missing");
+    for (const auto &I : BB) {
+      if (AnnotateSelect && isa<SelectInst>(I) &&
+          !I.getMetadata(LLVMContext::MD_prof))
+        F.getContext().emitError(
+            "Profile verification failed: select annotation missing");
+      if (AnnotateIndirectCalls)
+        if (const auto *CB = dyn_cast<CallBase>(&I))
+          if (CB->isIndirectCall() && !I.getMetadata(LLVMContext::MD_prof))
+            F.getContext().emitError("Profile verification failed: indirect "
+                                     "call annotation missing");
     }
     if (const auto *Term =
             ProfileInjector::getTerminatorBenefitingFromMDProf(BB))
diff --git a/llvm/test/Transforms/JumpTableToSwitch/lit.local.cfg b/llvm/test/Transforms/JumpTableToSwitch/lit.local.cfg
new file mode 100644
index 0000000000000..de5b8ebb5d427
--- /dev/null
+++ b/llvm/test/Transforms/JumpTableToSwitch/lit.local.cfg
@@ -0,0 +1,5 @@
+if bool(config.enable_profcheck):
+    # disable indirect call annotations here. The targets for JumpTableToSwitch
+    # are specific, and the profile injector is (intentionally) naive in what
+    # VP metadata it inserts.
+    config.substitutions.append(("opt", "opt -profcheck-annotate-indirect-calls=0"))
\ No newline at end of file
diff --git a/llvm/test/Transforms/PGOProfile/profcheck-indirect-calls.ll b/llvm/test/Transforms/PGOProfile/profcheck-indirect-calls.ll
new file mode 100644
index 0000000000000..027395eff7515
--- /dev/null
+++ b/llvm/test/Transforms/PGOProfile/profcheck-indirect-calls.ll
@@ -0,0 +1,34 @@
+; Check insertion and verification of indirect calls
+; RUN: split-file %s %t
+; RUN: opt -passes=prof-inject %t/inject.ll -S -o - | FileCheck %t/inject.ll
+; RUN: opt -passes=prof-verify %t/verify-ok.ll -S -o - | FileCheck %t/verify-ok.ll
+; RUN: not opt -passes=prof-verify %t/verify-bad.ll -S -o - 2>&1 | FileCheck %t/verify-bad.ll
+
+;--- inject.ll
+define void @foo(ptr %f) {
+  call void %f()
+  ret void
+}
+
+; CHECK: call void %f(), !prof !1
+; CHECK: !0 = !{!"function_entry_count", i64 1000}
+; CHECK: !1 = !{!"VP", i32 0, i64 30, i64 2345, i64 10, i64 5678, i64 20}
+
+;--- verify-ok.ll
+define void @foo(ptr %f) !prof !0 {
+  call void %f(), !prof !1
+  ret void
+}
+!0 = !{!"function_entry_count", i64 10}
+!1 = !{!"VP", i32 0, i64 100, i64 123, i64 50, i64 456, i64 50}
+
+; CHECK: call void %f(), !prof !1
+; CHECK: !1 = !{!"VP", i32 0, i64 100, i64 123, i64 50, i64 456, i64 50}
+
+;--- verify-bad.ll
+define void @foo(ptr %f) !prof !0  {
+  call void %f()
+  ret void
+}
+!0 = !{!"function_entry_count", i64 10}
+; CHECK: Profile verification failed: indirect call annotation missing



More information about the llvm-commits mailing list